diff options
Diffstat (limited to 'src')
305 files changed, 9891 insertions, 11185 deletions
diff --git a/src/3rdparty/double-conversion/LICENSE b/src/3rdparty/double-conversion/LICENSE new file mode 100644 index 0000000000..933718a9ef --- /dev/null +++ b/src/3rdparty/double-conversion/LICENSE @@ -0,0 +1,26 @@ +Copyright 2006-2011, the V8 project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/3rdparty/double-conversion/README b/src/3rdparty/double-conversion/README new file mode 100644 index 0000000000..75d08df5fe --- /dev/null +++ b/src/3rdparty/double-conversion/README @@ -0,0 +1,8 @@ +This is a copy of the library for binary-decimal and decimal-binary +conversion routines for IEEE doubles, available from + + https://github.com/google/double-conversion + +commit 2fb03de56faa32bbba5e02222528e7b760f71d77 + +See the LICENSE file for license information. diff --git a/src/3rdparty/double-conversion/bignum-dtoa.cc b/src/3rdparty/double-conversion/bignum-dtoa.cc new file mode 100644 index 0000000000..f1ad7a5ae8 --- /dev/null +++ b/src/3rdparty/double-conversion/bignum-dtoa.cc @@ -0,0 +1,641 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <math.h> + +#include "bignum-dtoa.h" + +#include "bignum.h" +#include "ieee.h" + +namespace double_conversion { + +static int NormalizedExponent(uint64_t significand, int exponent) { + ASSERT(significand != 0); + while ((significand & Double::kHiddenBit) == 0) { + significand = significand << 1; + exponent = exponent - 1; + } + return exponent; +} + + +// Forward declarations: +// Returns an estimation of k such that 10^(k-1) <= v < 10^k. +static int EstimatePower(int exponent); +// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator +// and denominator. +static void InitialScaledStartValues(uint64_t significand, + int exponent, + bool lower_boundary_is_closer, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus); +// Multiplies numerator/denominator so that its values lies in the range 1-10. +// Returns decimal_point s.t. +// v = numerator'/denominator' * 10^(decimal_point-1) +// where numerator' and denominator' are the values of numerator and +// denominator after the call to this function. +static void FixupMultiply10(int estimated_power, bool is_even, + int* decimal_point, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus); +// Generates digits from the left to the right and stops when the generated +// digits yield the shortest decimal representation of v. +static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus, + bool is_even, + Vector<char> buffer, int* length); +// Generates 'requested_digits' after the decimal point. +static void BignumToFixed(int requested_digits, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length); +// Generates 'count' digits of numerator/denominator. +// Once 'count' digits have been produced rounds the result depending on the +// remainder (remainders of exactly .5 round upwards). Might update the +// decimal_point when rounding up (for example for 0.9999). +static void GenerateCountedDigits(int count, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length); + + +void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, + Vector<char> buffer, int* length, int* decimal_point) { + ASSERT(v > 0); + ASSERT(!Double(v).IsSpecial()); + uint64_t significand; + int exponent; + bool lower_boundary_is_closer; + if (mode == BIGNUM_DTOA_SHORTEST_SINGLE) { + float f = static_cast<float>(v); + ASSERT(f == v); + significand = Single(f).Significand(); + exponent = Single(f).Exponent(); + lower_boundary_is_closer = Single(f).LowerBoundaryIsCloser(); + } else { + significand = Double(v).Significand(); + exponent = Double(v).Exponent(); + lower_boundary_is_closer = Double(v).LowerBoundaryIsCloser(); + } + bool need_boundary_deltas = + (mode == BIGNUM_DTOA_SHORTEST || mode == BIGNUM_DTOA_SHORTEST_SINGLE); + + bool is_even = (significand & 1) == 0; + int normalized_exponent = NormalizedExponent(significand, exponent); + // estimated_power might be too low by 1. + int estimated_power = EstimatePower(normalized_exponent); + + // Shortcut for Fixed. + // The requested digits correspond to the digits after the point. If the + // number is much too small, then there is no need in trying to get any + // digits. + if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) { + buffer[0] = '\0'; + *length = 0; + // Set decimal-point to -requested_digits. This is what Gay does. + // Note that it should not have any effect anyways since the string is + // empty. + *decimal_point = -requested_digits; + return; + } + + Bignum numerator; + Bignum denominator; + Bignum delta_minus; + Bignum delta_plus; + // Make sure the bignum can grow large enough. The smallest double equals + // 4e-324. In this case the denominator needs fewer than 324*4 binary digits. + // The maximum double is 1.7976931348623157e308 which needs fewer than + // 308*4 binary digits. + ASSERT(Bignum::kMaxSignificantBits >= 324*4); + InitialScaledStartValues(significand, exponent, lower_boundary_is_closer, + estimated_power, need_boundary_deltas, + &numerator, &denominator, + &delta_minus, &delta_plus); + // We now have v = (numerator / denominator) * 10^estimated_power. + FixupMultiply10(estimated_power, is_even, decimal_point, + &numerator, &denominator, + &delta_minus, &delta_plus); + // We now have v = (numerator / denominator) * 10^(decimal_point-1), and + // 1 <= (numerator + delta_plus) / denominator < 10 + switch (mode) { + case BIGNUM_DTOA_SHORTEST: + case BIGNUM_DTOA_SHORTEST_SINGLE: + GenerateShortestDigits(&numerator, &denominator, + &delta_minus, &delta_plus, + is_even, buffer, length); + break; + case BIGNUM_DTOA_FIXED: + BignumToFixed(requested_digits, decimal_point, + &numerator, &denominator, + buffer, length); + break; + case BIGNUM_DTOA_PRECISION: + GenerateCountedDigits(requested_digits, decimal_point, + &numerator, &denominator, + buffer, length); + break; + default: + UNREACHABLE(); + } + buffer[*length] = '\0'; +} + + +// The procedure starts generating digits from the left to the right and stops +// when the generated digits yield the shortest decimal representation of v. A +// decimal representation of v is a number lying closer to v than to any other +// double, so it converts to v when read. +// +// This is true if d, the decimal representation, is between m- and m+, the +// upper and lower boundaries. d must be strictly between them if !is_even. +// m- := (numerator - delta_minus) / denominator +// m+ := (numerator + delta_plus) / denominator +// +// Precondition: 0 <= (numerator+delta_plus) / denominator < 10. +// If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit +// will be produced. This should be the standard precondition. +static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus, + bool is_even, + Vector<char> buffer, int* length) { + // Small optimization: if delta_minus and delta_plus are the same just reuse + // one of the two bignums. + if (Bignum::Equal(*delta_minus, *delta_plus)) { + delta_plus = delta_minus; + } + *length = 0; + for (;;) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + // digit = numerator / denominator (integer division). + // numerator = numerator % denominator. + buffer[(*length)++] = static_cast<char>(digit + '0'); + + // Can we stop already? + // If the remainder of the division is less than the distance to the lower + // boundary we can stop. In this case we simply round down (discarding the + // remainder). + // Similarly we test if we can round up (using the upper boundary). + bool in_delta_room_minus; + bool in_delta_room_plus; + if (is_even) { + in_delta_room_minus = Bignum::LessEqual(*numerator, *delta_minus); + } else { + in_delta_room_minus = Bignum::Less(*numerator, *delta_minus); + } + if (is_even) { + in_delta_room_plus = + Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; + } else { + in_delta_room_plus = + Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; + } + if (!in_delta_room_minus && !in_delta_room_plus) { + // Prepare for next iteration. + numerator->Times10(); + delta_minus->Times10(); + // We optimized delta_plus to be equal to delta_minus (if they share the + // same value). So don't multiply delta_plus if they point to the same + // object. + if (delta_minus != delta_plus) { + delta_plus->Times10(); + } + } else if (in_delta_room_minus && in_delta_room_plus) { + // Let's see if 2*numerator < denominator. + // If yes, then the next digit would be < 5 and we can round down. + int compare = Bignum::PlusCompare(*numerator, *numerator, *denominator); + if (compare < 0) { + // Remaining digits are less than .5. -> Round down (== do nothing). + } else if (compare > 0) { + // Remaining digits are more than .5 of denominator. -> Round up. + // Note that the last digit could not be a '9' as otherwise the whole + // loop would have stopped earlier. + // We still have an assert here in case the preconditions were not + // satisfied. + ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; + } else { + // Halfway case. + // TODO(floitsch): need a way to solve half-way cases. + // For now let's round towards even (since this is what Gay seems to + // do). + + if ((buffer[(*length) - 1] - '0') % 2 == 0) { + // Round down => Do nothing. + } else { + ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; + } + } + return; + } else if (in_delta_room_minus) { + // Round down (== do nothing). + return; + } else { // in_delta_room_plus + // Round up. + // Note again that the last digit could not be '9' since this would have + // stopped the loop earlier. + // We still have an ASSERT here, in case the preconditions were not + // satisfied. + ASSERT(buffer[(*length) -1] != '9'); + buffer[(*length) - 1]++; + return; + } + } +} + + +// Let v = numerator / denominator < 10. +// Then we generate 'count' digits of d = x.xxxxx... (without the decimal point) +// from left to right. Once 'count' digits have been produced we decide wether +// to round up or down. Remainders of exactly .5 round upwards. Numbers such +// as 9.999999 propagate a carry all the way, and change the +// exponent (decimal_point), when rounding upwards. +static void GenerateCountedDigits(int count, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char> buffer, int* length) { + ASSERT(count >= 0); + for (int i = 0; i < count - 1; ++i) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + // digit = numerator / denominator (integer division). + // numerator = numerator % denominator. + buffer[i] = static_cast<char>(digit + '0'); + // Prepare for next iteration. + numerator->Times10(); + } + // Generate the last digit. + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + digit++; + } + ASSERT(digit <= 10); + buffer[count - 1] = static_cast<char>(digit + '0'); + // Correct bad digits (in case we had a sequence of '9's). Propagate the + // carry until we hat a non-'9' or til we reach the first digit. + for (int i = count - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) break; + buffer[i] = '0'; + buffer[i - 1]++; + } + if (buffer[0] == '0' + 10) { + // Propagate a carry past the top place. + buffer[0] = '1'; + (*decimal_point)++; + } + *length = count; +} + + +// Generates 'requested_digits' after the decimal point. It might omit +// trailing '0's. If the input number is too small then no digits at all are +// generated (ex.: 2 fixed digits for 0.00001). +// +// Input verifies: 1 <= (numerator + delta) / denominator < 10. +static void BignumToFixed(int requested_digits, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length) { + // Note that we have to look at more than just the requested_digits, since + // a number could be rounded up. Example: v=0.5 with requested_digits=0. + // Even though the power of v equals 0 we can't just stop here. + if (-(*decimal_point) > requested_digits) { + // The number is definitively too small. + // Ex: 0.001 with requested_digits == 1. + // Set decimal-point to -requested_digits. This is what Gay does. + // Note that it should not have any effect anyways since the string is + // empty. + *decimal_point = -requested_digits; + *length = 0; + return; + } else if (-(*decimal_point) == requested_digits) { + // We only need to verify if the number rounds down or up. + // Ex: 0.04 and 0.06 with requested_digits == 1. + ASSERT(*decimal_point == -requested_digits); + // Initially the fraction lies in range (1, 10]. Multiply the denominator + // by 10 so that we can compare more easily. + denominator->Times10(); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + // If the fraction is >= 0.5 then we have to include the rounded + // digit. + buffer[0] = '1'; + *length = 1; + (*decimal_point)++; + } else { + // Note that we caught most of similar cases earlier. + *length = 0; + } + return; + } else { + // The requested digits correspond to the digits after the point. + // The variable 'needed_digits' includes the digits before the point. + int needed_digits = (*decimal_point) + requested_digits; + GenerateCountedDigits(needed_digits, decimal_point, + numerator, denominator, + buffer, length); + } +} + + +// Returns an estimation of k such that 10^(k-1) <= v < 10^k where +// v = f * 2^exponent and 2^52 <= f < 2^53. +// v is hence a normalized double with the given exponent. The output is an +// approximation for the exponent of the decimal approimation .digits * 10^k. +// +// The result might undershoot by 1 in which case 10^k <= v < 10^k+1. +// Note: this property holds for v's upper boundary m+ too. +// 10^k <= m+ < 10^k+1. +// (see explanation below). +// +// Examples: +// EstimatePower(0) => 16 +// EstimatePower(-52) => 0 +// +// Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0. +static int EstimatePower(int exponent) { + // This function estimates log10 of v where v = f*2^e (with e == exponent). + // Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)). + // Note that f is bounded by its container size. Let p = 53 (the double's + // significand size). Then 2^(p-1) <= f < 2^p. + // + // Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close + // to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)). + // The computed number undershoots by less than 0.631 (when we compute log3 + // and not log10). + // + // Optimization: since we only need an approximated result this computation + // can be performed on 64 bit integers. On x86/x64 architecture the speedup is + // not really measurable, though. + // + // Since we want to avoid overshooting we decrement by 1e10 so that + // floating-point imprecisions don't affect us. + // + // Explanation for v's boundary m+: the computation takes advantage of + // the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement + // (even for denormals where the delta can be much more important). + + const double k1Log10 = 0.30102999566398114; // 1/lg(10) + + // For doubles len(f) == 53 (don't forget the hidden bit). + const int kSignificandSize = Double::kSignificandSize; + double estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10); + return static_cast<int>(estimate); +} + + +// See comments for InitialScaledStartValues. +static void InitialScaledStartValuesPositiveExponent( + uint64_t significand, int exponent, + int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + // A positive exponent implies a positive power. + ASSERT(estimated_power >= 0); + // Since the estimated_power is positive we simply multiply the denominator + // by 10^estimated_power. + + // numerator = v. + numerator->AssignUInt64(significand); + numerator->ShiftLeft(exponent); + // denominator = 10^estimated_power. + denominator->AssignPowerUInt16(10, estimated_power); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + denominator->ShiftLeft(1); + numerator->ShiftLeft(1); + // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common + // denominator (of 2) delta_plus equals 2^e. + delta_plus->AssignUInt16(1); + delta_plus->ShiftLeft(exponent); + // Same for delta_minus. The adjustments if f == 2^p-1 are done later. + delta_minus->AssignUInt16(1); + delta_minus->ShiftLeft(exponent); + } +} + + +// See comments for InitialScaledStartValues +static void InitialScaledStartValuesNegativeExponentPositivePower( + uint64_t significand, int exponent, + int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + // v = f * 2^e with e < 0, and with estimated_power >= 0. + // This means that e is close to 0 (have a look at how estimated_power is + // computed). + + // numerator = significand + // since v = significand * 2^exponent this is equivalent to + // numerator = v * / 2^-exponent + numerator->AssignUInt64(significand); + // denominator = 10^estimated_power * 2^-exponent (with exponent < 0) + denominator->AssignPowerUInt16(10, estimated_power); + denominator->ShiftLeft(-exponent); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + denominator->ShiftLeft(1); + numerator->ShiftLeft(1); + // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common + // denominator (of 2) delta_plus equals 2^e. + // Given that the denominator already includes v's exponent the distance + // to the boundaries is simply 1. + delta_plus->AssignUInt16(1); + // Same for delta_minus. The adjustments if f == 2^p-1 are done later. + delta_minus->AssignUInt16(1); + } +} + + +// See comments for InitialScaledStartValues +static void InitialScaledStartValuesNegativeExponentNegativePower( + uint64_t significand, int exponent, + int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + // Instead of multiplying the denominator with 10^estimated_power we + // multiply all values (numerator and deltas) by 10^-estimated_power. + + // Use numerator as temporary container for power_ten. + Bignum* power_ten = numerator; + power_ten->AssignPowerUInt16(10, -estimated_power); + + if (need_boundary_deltas) { + // Since power_ten == numerator we must make a copy of 10^estimated_power + // before we complete the computation of the numerator. + // delta_plus = delta_minus = 10^estimated_power + delta_plus->AssignBignum(*power_ten); + delta_minus->AssignBignum(*power_ten); + } + + // numerator = significand * 2 * 10^-estimated_power + // since v = significand * 2^exponent this is equivalent to + // numerator = v * 10^-estimated_power * 2 * 2^-exponent. + // Remember: numerator has been abused as power_ten. So no need to assign it + // to itself. + ASSERT(numerator == power_ten); + numerator->MultiplyByUInt64(significand); + + // denominator = 2 * 2^-exponent with exponent < 0. + denominator->AssignUInt16(1); + denominator->ShiftLeft(-exponent); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + numerator->ShiftLeft(1); + denominator->ShiftLeft(1); + // With this shift the boundaries have their correct value, since + // delta_plus = 10^-estimated_power, and + // delta_minus = 10^-estimated_power. + // These assignments have been done earlier. + // The adjustments if f == 2^p-1 (lower boundary is closer) are done later. + } +} + + +// Let v = significand * 2^exponent. +// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator +// and denominator. The functions GenerateShortestDigits and +// GenerateCountedDigits will then convert this ratio to its decimal +// representation d, with the required accuracy. +// Then d * 10^estimated_power is the representation of v. +// (Note: the fraction and the estimated_power might get adjusted before +// generating the decimal representation.) +// +// The initial start values consist of: +// - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power. +// - a scaled (common) denominator. +// optionally (used by GenerateShortestDigits to decide if it has the shortest +// decimal converting back to v): +// - v - m-: the distance to the lower boundary. +// - m+ - v: the distance to the upper boundary. +// +// v, m+, m-, and therefore v - m- and m+ - v all share the same denominator. +// +// Let ep == estimated_power, then the returned values will satisfy: +// v / 10^ep = numerator / denominator. +// v's boundarys m- and m+: +// m- / 10^ep == v / 10^ep - delta_minus / denominator +// m+ / 10^ep == v / 10^ep + delta_plus / denominator +// Or in other words: +// m- == v - delta_minus * 10^ep / denominator; +// m+ == v + delta_plus * 10^ep / denominator; +// +// Since 10^(k-1) <= v < 10^k (with k == estimated_power) +// or 10^k <= v < 10^(k+1) +// we then have 0.1 <= numerator/denominator < 1 +// or 1 <= numerator/denominator < 10 +// +// It is then easy to kickstart the digit-generation routine. +// +// The boundary-deltas are only filled if the mode equals BIGNUM_DTOA_SHORTEST +// or BIGNUM_DTOA_SHORTEST_SINGLE. + +static void InitialScaledStartValues(uint64_t significand, + int exponent, + bool lower_boundary_is_closer, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus) { + if (exponent >= 0) { + InitialScaledStartValuesPositiveExponent( + significand, exponent, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } else if (estimated_power >= 0) { + InitialScaledStartValuesNegativeExponentPositivePower( + significand, exponent, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } else { + InitialScaledStartValuesNegativeExponentNegativePower( + significand, exponent, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } + + if (need_boundary_deltas && lower_boundary_is_closer) { + // The lower boundary is closer at half the distance of "normal" numbers. + // Increase the common denominator and adapt all but the delta_minus. + denominator->ShiftLeft(1); // *2 + numerator->ShiftLeft(1); // *2 + delta_plus->ShiftLeft(1); // *2 + } +} + + +// This routine multiplies numerator/denominator so that its values lies in the +// range 1-10. That is after a call to this function we have: +// 1 <= (numerator + delta_plus) /denominator < 10. +// Let numerator the input before modification and numerator' the argument +// after modification, then the output-parameter decimal_point is such that +// numerator / denominator * 10^estimated_power == +// numerator' / denominator' * 10^(decimal_point - 1) +// In some cases estimated_power was too low, and this is already the case. We +// then simply adjust the power so that 10^(k-1) <= v < 10^k (with k == +// estimated_power) but do not touch the numerator or denominator. +// Otherwise the routine multiplies the numerator and the deltas by 10. +static void FixupMultiply10(int estimated_power, bool is_even, + int* decimal_point, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + bool in_range; + if (is_even) { + // For IEEE doubles half-way cases (in decimal system numbers ending with 5) + // are rounded to the closest floating-point number with even significand. + in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; + } else { + in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; + } + if (in_range) { + // Since numerator + delta_plus >= denominator we already have + // 1 <= numerator/denominator < 10. Simply update the estimated_power. + *decimal_point = estimated_power + 1; + } else { + *decimal_point = estimated_power; + numerator->Times10(); + if (Bignum::Equal(*delta_minus, *delta_plus)) { + delta_minus->Times10(); + delta_plus->AssignBignum(*delta_minus); + } else { + delta_minus->Times10(); + delta_plus->Times10(); + } + } +} + +} // namespace double_conversion diff --git a/src/3rdparty/double-conversion/bignum-dtoa.h b/src/3rdparty/double-conversion/bignum-dtoa.h new file mode 100644 index 0000000000..34b961992d --- /dev/null +++ b/src/3rdparty/double-conversion/bignum-dtoa.h @@ -0,0 +1,84 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_BIGNUM_DTOA_H_ +#define DOUBLE_CONVERSION_BIGNUM_DTOA_H_ + +#include "utils.h" + +namespace double_conversion { + +enum BignumDtoaMode { + // Return the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate but + // correct) 0.3. + BIGNUM_DTOA_SHORTEST, + // Same as BIGNUM_DTOA_SHORTEST but for single-precision floats. + BIGNUM_DTOA_SHORTEST_SINGLE, + // Return a fixed number of digits after the decimal point. + // For instance fixed(0.1, 4) becomes 0.1000 + // If the input number is big, the output will be big. + BIGNUM_DTOA_FIXED, + // Return a fixed number of digits, no matter what the exponent is. + BIGNUM_DTOA_PRECISION +}; + +// Converts the given double 'v' to ascii. +// The result should be interpreted as buffer * 10^(point-length). +// The buffer will be null-terminated. +// +// The input v must be > 0 and different from NaN, and Infinity. +// +// The output depends on the given mode: +// - SHORTEST: produce the least amount of digits for which the internal +// identity requirement is still satisfied. If the digits are printed +// (together with the correct exponent) then reading this number will give +// 'v' again. The buffer will choose the representation that is closest to +// 'v'. If there are two at the same distance, than the number is round up. +// In this mode the 'requested_digits' parameter is ignored. +// - FIXED: produces digits necessary to print a given number with +// 'requested_digits' digits after the decimal point. The produced digits +// might be too short in which case the caller has to fill the gaps with '0's. +// Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. +// Halfway cases are rounded up. The call toFixed(0.15, 2) thus returns +// buffer="2", point=0. +// Note: the length of the returned buffer has no meaning wrt the significance +// of its digits. That is, just because it contains '0's does not mean that +// any other digit would not satisfy the internal identity requirement. +// - PRECISION: produces 'requested_digits' where the first digit is not '0'. +// Even though the length of produced digits usually equals +// 'requested_digits', the function is allowed to return fewer digits, in +// which case the caller has to fill the missing digits with '0's. +// Halfway cases are again rounded up. +// 'BignumDtoa' expects the given buffer to be big enough to hold all digits +// and a terminating null-character. +void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, + Vector<char> buffer, int* length, int* point); + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_BIGNUM_DTOA_H_ diff --git a/src/3rdparty/double-conversion/bignum.cc b/src/3rdparty/double-conversion/bignum.cc new file mode 100644 index 0000000000..2743d67e8d --- /dev/null +++ b/src/3rdparty/double-conversion/bignum.cc @@ -0,0 +1,766 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "bignum.h" +#include "utils.h" + +namespace double_conversion { + +Bignum::Bignum() + : bigits_(bigits_buffer_, kBigitCapacity), used_digits_(0), exponent_(0) { + for (int i = 0; i < kBigitCapacity; ++i) { + bigits_[i] = 0; + } +} + + +template<typename S> +static int BitSize(S value) { + (void) value; // Mark variable as used. + return 8 * sizeof(value); +} + +// Guaranteed to lie in one Bigit. +void Bignum::AssignUInt16(uint16_t value) { + ASSERT(kBigitSize >= BitSize(value)); + Zero(); + if (value == 0) return; + + EnsureCapacity(1); + bigits_[0] = value; + used_digits_ = 1; +} + + +void Bignum::AssignUInt64(uint64_t value) { + const int kUInt64Size = 64; + + Zero(); + if (value == 0) return; + + int needed_bigits = kUInt64Size / kBigitSize + 1; + EnsureCapacity(needed_bigits); + for (int i = 0; i < needed_bigits; ++i) { + bigits_[i] = value & kBigitMask; + value = value >> kBigitSize; + } + used_digits_ = needed_bigits; + Clamp(); +} + + +void Bignum::AssignBignum(const Bignum& other) { + exponent_ = other.exponent_; + for (int i = 0; i < other.used_digits_; ++i) { + bigits_[i] = other.bigits_[i]; + } + // Clear the excess digits (if there were any). + for (int i = other.used_digits_; i < used_digits_; ++i) { + bigits_[i] = 0; + } + used_digits_ = other.used_digits_; +} + + +static uint64_t ReadUInt64(Vector<const char> buffer, + int from, + int digits_to_read) { + uint64_t result = 0; + for (int i = from; i < from + digits_to_read; ++i) { + int digit = buffer[i] - '0'; + ASSERT(0 <= digit && digit <= 9); + result = result * 10 + digit; + } + return result; +} + + +void Bignum::AssignDecimalString(Vector<const char> value) { + // 2^64 = 18446744073709551616 > 10^19 + const int kMaxUint64DecimalDigits = 19; + Zero(); + int length = value.length(); + int pos = 0; + // Let's just say that each digit needs 4 bits. + while (length >= kMaxUint64DecimalDigits) { + uint64_t digits = ReadUInt64(value, pos, kMaxUint64DecimalDigits); + pos += kMaxUint64DecimalDigits; + length -= kMaxUint64DecimalDigits; + MultiplyByPowerOfTen(kMaxUint64DecimalDigits); + AddUInt64(digits); + } + uint64_t digits = ReadUInt64(value, pos, length); + MultiplyByPowerOfTen(length); + AddUInt64(digits); + Clamp(); +} + + +static int HexCharValue(char c) { + if ('0' <= c && c <= '9') return c - '0'; + if ('a' <= c && c <= 'f') return 10 + c - 'a'; + ASSERT('A' <= c && c <= 'F'); + return 10 + c - 'A'; +} + + +void Bignum::AssignHexString(Vector<const char> value) { + Zero(); + int length = value.length(); + + int needed_bigits = length * 4 / kBigitSize + 1; + EnsureCapacity(needed_bigits); + int string_index = length - 1; + for (int i = 0; i < needed_bigits - 1; ++i) { + // These bigits are guaranteed to be "full". + Chunk current_bigit = 0; + for (int j = 0; j < kBigitSize / 4; j++) { + current_bigit += HexCharValue(value[string_index--]) << (j * 4); + } + bigits_[i] = current_bigit; + } + used_digits_ = needed_bigits - 1; + + Chunk most_significant_bigit = 0; // Could be = 0; + for (int j = 0; j <= string_index; ++j) { + most_significant_bigit <<= 4; + most_significant_bigit += HexCharValue(value[j]); + } + if (most_significant_bigit != 0) { + bigits_[used_digits_] = most_significant_bigit; + used_digits_++; + } + Clamp(); +} + + +void Bignum::AddUInt64(uint64_t operand) { + if (operand == 0) return; + Bignum other; + other.AssignUInt64(operand); + AddBignum(other); +} + + +void Bignum::AddBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + + // If this has a greater exponent than other append zero-bigits to this. + // After this call exponent_ <= other.exponent_. + Align(other); + + // There are two possibilities: + // aaaaaaaaaaa 0000 (where the 0s represent a's exponent) + // bbbbb 00000000 + // ---------------- + // ccccccccccc 0000 + // or + // aaaaaaaaaa 0000 + // bbbbbbbbb 0000000 + // ----------------- + // cccccccccccc 0000 + // In both cases we might need a carry bigit. + + EnsureCapacity(1 + Max(BigitLength(), other.BigitLength()) - exponent_); + Chunk carry = 0; + int bigit_pos = other.exponent_ - exponent_; + ASSERT(bigit_pos >= 0); + for (int i = 0; i < other.used_digits_; ++i) { + Chunk sum = bigits_[bigit_pos] + other.bigits_[i] + carry; + bigits_[bigit_pos] = sum & kBigitMask; + carry = sum >> kBigitSize; + bigit_pos++; + } + + while (carry != 0) { + Chunk sum = bigits_[bigit_pos] + carry; + bigits_[bigit_pos] = sum & kBigitMask; + carry = sum >> kBigitSize; + bigit_pos++; + } + used_digits_ = Max(bigit_pos, used_digits_); + ASSERT(IsClamped()); +} + + +void Bignum::SubtractBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + // We require this to be bigger than other. + ASSERT(LessEqual(other, *this)); + + Align(other); + + int offset = other.exponent_ - exponent_; + Chunk borrow = 0; + int i; + for (i = 0; i < other.used_digits_; ++i) { + ASSERT((borrow == 0) || (borrow == 1)); + Chunk difference = bigits_[i + offset] - other.bigits_[i] - borrow; + bigits_[i + offset] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + } + while (borrow != 0) { + Chunk difference = bigits_[i + offset] - borrow; + bigits_[i + offset] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + ++i; + } + Clamp(); +} + + +void Bignum::ShiftLeft(int shift_amount) { + if (used_digits_ == 0) return; + exponent_ += shift_amount / kBigitSize; + int local_shift = shift_amount % kBigitSize; + EnsureCapacity(used_digits_ + 1); + BigitsShiftLeft(local_shift); +} + + +void Bignum::MultiplyByUInt32(uint32_t factor) { + if (factor == 1) return; + if (factor == 0) { + Zero(); + return; + } + if (used_digits_ == 0) return; + + // The product of a bigit with the factor is of size kBigitSize + 32. + // Assert that this number + 1 (for the carry) fits into double chunk. + ASSERT(kDoubleChunkSize >= kBigitSize + 32 + 1); + DoubleChunk carry = 0; + for (int i = 0; i < used_digits_; ++i) { + DoubleChunk product = static_cast<DoubleChunk>(factor) * bigits_[i] + carry; + bigits_[i] = static_cast<Chunk>(product & kBigitMask); + carry = (product >> kBigitSize); + } + while (carry != 0) { + EnsureCapacity(used_digits_ + 1); + bigits_[used_digits_] = carry & kBigitMask; + used_digits_++; + carry >>= kBigitSize; + } +} + + +void Bignum::MultiplyByUInt64(uint64_t factor) { + if (factor == 1) return; + if (factor == 0) { + Zero(); + return; + } + ASSERT(kBigitSize < 32); + uint64_t carry = 0; + uint64_t low = factor & 0xFFFFFFFF; + uint64_t high = factor >> 32; + for (int i = 0; i < used_digits_; ++i) { + uint64_t product_low = low * bigits_[i]; + uint64_t product_high = high * bigits_[i]; + uint64_t tmp = (carry & kBigitMask) + product_low; + bigits_[i] = tmp & kBigitMask; + carry = (carry >> kBigitSize) + (tmp >> kBigitSize) + + (product_high << (32 - kBigitSize)); + } + while (carry != 0) { + EnsureCapacity(used_digits_ + 1); + bigits_[used_digits_] = carry & kBigitMask; + used_digits_++; + carry >>= kBigitSize; + } +} + + +void Bignum::MultiplyByPowerOfTen(int exponent) { + const uint64_t kFive27 = UINT64_2PART_C(0x6765c793, fa10079d); + const uint16_t kFive1 = 5; + const uint16_t kFive2 = kFive1 * 5; + const uint16_t kFive3 = kFive2 * 5; + const uint16_t kFive4 = kFive3 * 5; + const uint16_t kFive5 = kFive4 * 5; + const uint16_t kFive6 = kFive5 * 5; + const uint32_t kFive7 = kFive6 * 5; + const uint32_t kFive8 = kFive7 * 5; + const uint32_t kFive9 = kFive8 * 5; + const uint32_t kFive10 = kFive9 * 5; + const uint32_t kFive11 = kFive10 * 5; + const uint32_t kFive12 = kFive11 * 5; + const uint32_t kFive13 = kFive12 * 5; + const uint32_t kFive1_to_12[] = + { kFive1, kFive2, kFive3, kFive4, kFive5, kFive6, + kFive7, kFive8, kFive9, kFive10, kFive11, kFive12 }; + + ASSERT(exponent >= 0); + if (exponent == 0) return; + if (used_digits_ == 0) return; + + // We shift by exponent at the end just before returning. + int remaining_exponent = exponent; + while (remaining_exponent >= 27) { + MultiplyByUInt64(kFive27); + remaining_exponent -= 27; + } + while (remaining_exponent >= 13) { + MultiplyByUInt32(kFive13); + remaining_exponent -= 13; + } + if (remaining_exponent > 0) { + MultiplyByUInt32(kFive1_to_12[remaining_exponent - 1]); + } + ShiftLeft(exponent); +} + + +void Bignum::Square() { + ASSERT(IsClamped()); + int product_length = 2 * used_digits_; + EnsureCapacity(product_length); + + // Comba multiplication: compute each column separately. + // Example: r = a2a1a0 * b2b1b0. + // r = 1 * a0b0 + + // 10 * (a1b0 + a0b1) + + // 100 * (a2b0 + a1b1 + a0b2) + + // 1000 * (a2b1 + a1b2) + + // 10000 * a2b2 + // + // In the worst case we have to accumulate nb-digits products of digit*digit. + // + // Assert that the additional number of bits in a DoubleChunk are enough to + // sum up used_digits of Bigit*Bigit. + if ((1 << (2 * (kChunkSize - kBigitSize))) <= used_digits_) { + UNIMPLEMENTED(); + } + DoubleChunk accumulator = 0; + // First shift the digits so we don't overwrite them. + int copy_offset = used_digits_; + for (int i = 0; i < used_digits_; ++i) { + bigits_[copy_offset + i] = bigits_[i]; + } + // We have two loops to avoid some 'if's in the loop. + for (int i = 0; i < used_digits_; ++i) { + // Process temporary digit i with power i. + // The sum of the two indices must be equal to i. + int bigit_index1 = i; + int bigit_index2 = 0; + // Sum all of the sub-products. + while (bigit_index1 >= 0) { + Chunk chunk1 = bigits_[copy_offset + bigit_index1]; + Chunk chunk2 = bigits_[copy_offset + bigit_index2]; + accumulator += static_cast<DoubleChunk>(chunk1) * chunk2; + bigit_index1--; + bigit_index2++; + } + bigits_[i] = static_cast<Chunk>(accumulator) & kBigitMask; + accumulator >>= kBigitSize; + } + for (int i = used_digits_; i < product_length; ++i) { + int bigit_index1 = used_digits_ - 1; + int bigit_index2 = i - bigit_index1; + // Invariant: sum of both indices is again equal to i. + // Inner loop runs 0 times on last iteration, emptying accumulator. + while (bigit_index2 < used_digits_) { + Chunk chunk1 = bigits_[copy_offset + bigit_index1]; + Chunk chunk2 = bigits_[copy_offset + bigit_index2]; + accumulator += static_cast<DoubleChunk>(chunk1) * chunk2; + bigit_index1--; + bigit_index2++; + } + // The overwritten bigits_[i] will never be read in further loop iterations, + // because bigit_index1 and bigit_index2 are always greater + // than i - used_digits_. + bigits_[i] = static_cast<Chunk>(accumulator) & kBigitMask; + accumulator >>= kBigitSize; + } + // Since the result was guaranteed to lie inside the number the + // accumulator must be 0 now. + ASSERT(accumulator == 0); + + // Don't forget to update the used_digits and the exponent. + used_digits_ = product_length; + exponent_ *= 2; + Clamp(); +} + + +void Bignum::AssignPowerUInt16(uint16_t base, int power_exponent) { + ASSERT(base != 0); + ASSERT(power_exponent >= 0); + if (power_exponent == 0) { + AssignUInt16(1); + return; + } + Zero(); + int shifts = 0; + // We expect base to be in range 2-32, and most often to be 10. + // It does not make much sense to implement different algorithms for counting + // the bits. + while ((base & 1) == 0) { + base >>= 1; + shifts++; + } + int bit_size = 0; + int tmp_base = base; + while (tmp_base != 0) { + tmp_base >>= 1; + bit_size++; + } + int final_size = bit_size * power_exponent; + // 1 extra bigit for the shifting, and one for rounded final_size. + EnsureCapacity(final_size / kBigitSize + 2); + + // Left to Right exponentiation. + int mask = 1; + while (power_exponent >= mask) mask <<= 1; + + // The mask is now pointing to the bit above the most significant 1-bit of + // power_exponent. + // Get rid of first 1-bit; + mask >>= 2; + uint64_t this_value = base; + + bool delayed_multipliciation = false; + const uint64_t max_32bits = 0xFFFFFFFF; + while (mask != 0 && this_value <= max_32bits) { + this_value = this_value * this_value; + // Verify that there is enough space in this_value to perform the + // multiplication. The first bit_size bits must be 0. + if ((power_exponent & mask) != 0) { + uint64_t base_bits_mask = + ~((static_cast<uint64_t>(1) << (64 - bit_size)) - 1); + bool high_bits_zero = (this_value & base_bits_mask) == 0; + if (high_bits_zero) { + this_value *= base; + } else { + delayed_multipliciation = true; + } + } + mask >>= 1; + } + AssignUInt64(this_value); + if (delayed_multipliciation) { + MultiplyByUInt32(base); + } + + // Now do the same thing as a bignum. + while (mask != 0) { + Square(); + if ((power_exponent & mask) != 0) { + MultiplyByUInt32(base); + } + mask >>= 1; + } + + // And finally add the saved shifts. + ShiftLeft(shifts * power_exponent); +} + + +// Precondition: this/other < 16bit. +uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + ASSERT(other.used_digits_ > 0); + + // Easy case: if we have less digits than the divisor than the result is 0. + // Note: this handles the case where this == 0, too. + if (BigitLength() < other.BigitLength()) { + return 0; + } + + Align(other); + + uint16_t result = 0; + + // Start by removing multiples of 'other' until both numbers have the same + // number of digits. + while (BigitLength() > other.BigitLength()) { + // This naive approach is extremely inefficient if `this` divided by other + // is big. This function is implemented for doubleToString where + // the result should be small (less than 10). + ASSERT(other.bigits_[other.used_digits_ - 1] >= ((1 << kBigitSize) / 16)); + ASSERT(bigits_[used_digits_ - 1] < 0x10000); + // Remove the multiples of the first digit. + // Example this = 23 and other equals 9. -> Remove 2 multiples. + result += static_cast<uint16_t>(bigits_[used_digits_ - 1]); + SubtractTimes(other, bigits_[used_digits_ - 1]); + } + + ASSERT(BigitLength() == other.BigitLength()); + + // Both bignums are at the same length now. + // Since other has more than 0 digits we know that the access to + // bigits_[used_digits_ - 1] is safe. + Chunk this_bigit = bigits_[used_digits_ - 1]; + Chunk other_bigit = other.bigits_[other.used_digits_ - 1]; + + if (other.used_digits_ == 1) { + // Shortcut for easy (and common) case. + int quotient = this_bigit / other_bigit; + bigits_[used_digits_ - 1] = this_bigit - other_bigit * quotient; + ASSERT(quotient < 0x10000); + result += static_cast<uint16_t>(quotient); + Clamp(); + return result; + } + + int division_estimate = this_bigit / (other_bigit + 1); + ASSERT(division_estimate < 0x10000); + result += static_cast<uint16_t>(division_estimate); + SubtractTimes(other, division_estimate); + + if (other_bigit * (division_estimate + 1) > this_bigit) { + // No need to even try to subtract. Even if other's remaining digits were 0 + // another subtraction would be too much. + return result; + } + + while (LessEqual(other, *this)) { + SubtractBignum(other); + result++; + } + return result; +} + + +template<typename S> +static int SizeInHexChars(S number) { + ASSERT(number > 0); + int result = 0; + while (number != 0) { + number >>= 4; + result++; + } + return result; +} + + +static char HexCharOfValue(int value) { + ASSERT(0 <= value && value <= 16); + if (value < 10) return static_cast<char>(value + '0'); + return static_cast<char>(value - 10 + 'A'); +} + + +bool Bignum::ToHexString(char* buffer, int buffer_size) const { + ASSERT(IsClamped()); + // Each bigit must be printable as separate hex-character. + ASSERT(kBigitSize % 4 == 0); + const int kHexCharsPerBigit = kBigitSize / 4; + + if (used_digits_ == 0) { + if (buffer_size < 2) return false; + buffer[0] = '0'; + buffer[1] = '\0'; + return true; + } + // We add 1 for the terminating '\0' character. + int needed_chars = (BigitLength() - 1) * kHexCharsPerBigit + + SizeInHexChars(bigits_[used_digits_ - 1]) + 1; + if (needed_chars > buffer_size) return false; + int string_index = needed_chars - 1; + buffer[string_index--] = '\0'; + for (int i = 0; i < exponent_; ++i) { + for (int j = 0; j < kHexCharsPerBigit; ++j) { + buffer[string_index--] = '0'; + } + } + for (int i = 0; i < used_digits_ - 1; ++i) { + Chunk current_bigit = bigits_[i]; + for (int j = 0; j < kHexCharsPerBigit; ++j) { + buffer[string_index--] = HexCharOfValue(current_bigit & 0xF); + current_bigit >>= 4; + } + } + // And finally the last bigit. + Chunk most_significant_bigit = bigits_[used_digits_ - 1]; + while (most_significant_bigit != 0) { + buffer[string_index--] = HexCharOfValue(most_significant_bigit & 0xF); + most_significant_bigit >>= 4; + } + return true; +} + + +Bignum::Chunk Bignum::BigitAt(int index) const { + if (index >= BigitLength()) return 0; + if (index < exponent_) return 0; + return bigits_[index - exponent_]; +} + + +int Bignum::Compare(const Bignum& a, const Bignum& b) { + ASSERT(a.IsClamped()); + ASSERT(b.IsClamped()); + int bigit_length_a = a.BigitLength(); + int bigit_length_b = b.BigitLength(); + if (bigit_length_a < bigit_length_b) return -1; + if (bigit_length_a > bigit_length_b) return +1; + for (int i = bigit_length_a - 1; i >= Min(a.exponent_, b.exponent_); --i) { + Chunk bigit_a = a.BigitAt(i); + Chunk bigit_b = b.BigitAt(i); + if (bigit_a < bigit_b) return -1; + if (bigit_a > bigit_b) return +1; + // Otherwise they are equal up to this digit. Try the next digit. + } + return 0; +} + + +int Bignum::PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c) { + ASSERT(a.IsClamped()); + ASSERT(b.IsClamped()); + ASSERT(c.IsClamped()); + if (a.BigitLength() < b.BigitLength()) { + return PlusCompare(b, a, c); + } + if (a.BigitLength() + 1 < c.BigitLength()) return -1; + if (a.BigitLength() > c.BigitLength()) return +1; + // The exponent encodes 0-bigits. So if there are more 0-digits in 'a' than + // 'b' has digits, then the bigit-length of 'a'+'b' must be equal to the one + // of 'a'. + if (a.exponent_ >= b.BigitLength() && a.BigitLength() < c.BigitLength()) { + return -1; + } + + Chunk borrow = 0; + // Starting at min_exponent all digits are == 0. So no need to compare them. + int min_exponent = Min(Min(a.exponent_, b.exponent_), c.exponent_); + for (int i = c.BigitLength() - 1; i >= min_exponent; --i) { + Chunk chunk_a = a.BigitAt(i); + Chunk chunk_b = b.BigitAt(i); + Chunk chunk_c = c.BigitAt(i); + Chunk sum = chunk_a + chunk_b; + if (sum > chunk_c + borrow) { + return +1; + } else { + borrow = chunk_c + borrow - sum; + if (borrow > 1) return -1; + borrow <<= kBigitSize; + } + } + if (borrow == 0) return 0; + return -1; +} + + +void Bignum::Clamp() { + while (used_digits_ > 0 && bigits_[used_digits_ - 1] == 0) { + used_digits_--; + } + if (used_digits_ == 0) { + // Zero. + exponent_ = 0; + } +} + + +bool Bignum::IsClamped() const { + return used_digits_ == 0 || bigits_[used_digits_ - 1] != 0; +} + + +void Bignum::Zero() { + for (int i = 0; i < used_digits_; ++i) { + bigits_[i] = 0; + } + used_digits_ = 0; + exponent_ = 0; +} + + +void Bignum::Align(const Bignum& other) { + if (exponent_ > other.exponent_) { + // If "X" represents a "hidden" digit (by the exponent) then we are in the + // following case (a == this, b == other): + // a: aaaaaaXXXX or a: aaaaaXXX + // b: bbbbbbX b: bbbbbbbbXX + // We replace some of the hidden digits (X) of a with 0 digits. + // a: aaaaaa000X or a: aaaaa0XX + int zero_digits = exponent_ - other.exponent_; + EnsureCapacity(used_digits_ + zero_digits); + for (int i = used_digits_ - 1; i >= 0; --i) { + bigits_[i + zero_digits] = bigits_[i]; + } + for (int i = 0; i < zero_digits; ++i) { + bigits_[i] = 0; + } + used_digits_ += zero_digits; + exponent_ -= zero_digits; + ASSERT(used_digits_ >= 0); + ASSERT(exponent_ >= 0); + } +} + + +void Bignum::BigitsShiftLeft(int shift_amount) { + ASSERT(shift_amount < kBigitSize); + ASSERT(shift_amount >= 0); + Chunk carry = 0; + for (int i = 0; i < used_digits_; ++i) { + Chunk new_carry = bigits_[i] >> (kBigitSize - shift_amount); + bigits_[i] = ((bigits_[i] << shift_amount) + carry) & kBigitMask; + carry = new_carry; + } + if (carry != 0) { + bigits_[used_digits_] = carry; + used_digits_++; + } +} + + +void Bignum::SubtractTimes(const Bignum& other, int factor) { + ASSERT(exponent_ <= other.exponent_); + if (factor < 3) { + for (int i = 0; i < factor; ++i) { + SubtractBignum(other); + } + return; + } + Chunk borrow = 0; + int exponent_diff = other.exponent_ - exponent_; + for (int i = 0; i < other.used_digits_; ++i) { + DoubleChunk product = static_cast<DoubleChunk>(factor) * other.bigits_[i]; + DoubleChunk remove = borrow + product; + Chunk difference = bigits_[i + exponent_diff] - (remove & kBigitMask); + bigits_[i + exponent_diff] = difference & kBigitMask; + borrow = static_cast<Chunk>((difference >> (kChunkSize - 1)) + + (remove >> kBigitSize)); + } + for (int i = other.used_digits_ + exponent_diff; i < used_digits_; ++i) { + if (borrow == 0) return; + Chunk difference = bigits_[i] - borrow; + bigits_[i] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + } + Clamp(); +} + + +} // namespace double_conversion diff --git a/src/3rdparty/double-conversion/bignum.h b/src/3rdparty/double-conversion/bignum.h new file mode 100644 index 0000000000..5ec3544f57 --- /dev/null +++ b/src/3rdparty/double-conversion/bignum.h @@ -0,0 +1,145 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_BIGNUM_H_ +#define DOUBLE_CONVERSION_BIGNUM_H_ + +#include "utils.h" + +namespace double_conversion { + +class Bignum { + public: + // 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately. + // This bignum can encode much bigger numbers, since it contains an + // exponent. + static const int kMaxSignificantBits = 3584; + + Bignum(); + void AssignUInt16(uint16_t value); + void AssignUInt64(uint64_t value); + void AssignBignum(const Bignum& other); + + void AssignDecimalString(Vector<const char> value); + void AssignHexString(Vector<const char> value); + + void AssignPowerUInt16(uint16_t base, int exponent); + + void AddUInt16(uint16_t operand); + void AddUInt64(uint64_t operand); + void AddBignum(const Bignum& other); + // Precondition: this >= other. + void SubtractBignum(const Bignum& other); + + void Square(); + void ShiftLeft(int shift_amount); + void MultiplyByUInt32(uint32_t factor); + void MultiplyByUInt64(uint64_t factor); + void MultiplyByPowerOfTen(int exponent); + void Times10() { return MultiplyByUInt32(10); } + // Pseudocode: + // int result = this / other; + // this = this % other; + // In the worst case this function is in O(this/other). + uint16_t DivideModuloIntBignum(const Bignum& other); + + bool ToHexString(char* buffer, int buffer_size) const; + + // Returns + // -1 if a < b, + // 0 if a == b, and + // +1 if a > b. + static int Compare(const Bignum& a, const Bignum& b); + static bool Equal(const Bignum& a, const Bignum& b) { + return Compare(a, b) == 0; + } + static bool LessEqual(const Bignum& a, const Bignum& b) { + return Compare(a, b) <= 0; + } + static bool Less(const Bignum& a, const Bignum& b) { + return Compare(a, b) < 0; + } + // Returns Compare(a + b, c); + static int PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c); + // Returns a + b == c + static bool PlusEqual(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) == 0; + } + // Returns a + b <= c + static bool PlusLessEqual(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) <= 0; + } + // Returns a + b < c + static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) < 0; + } + private: + typedef uint32_t Chunk; + typedef uint64_t DoubleChunk; + + static const int kChunkSize = sizeof(Chunk) * 8; + static const int kDoubleChunkSize = sizeof(DoubleChunk) * 8; + // With bigit size of 28 we loose some bits, but a double still fits easily + // into two chunks, and more importantly we can use the Comba multiplication. + static const int kBigitSize = 28; + static const Chunk kBigitMask = (1 << kBigitSize) - 1; + // Every instance allocates kBigitLength chunks on the stack. Bignums cannot + // grow. There are no checks if the stack-allocated space is sufficient. + static const int kBigitCapacity = kMaxSignificantBits / kBigitSize; + + void EnsureCapacity(int size) { + if (size > kBigitCapacity) { + UNREACHABLE(); + } + } + void Align(const Bignum& other); + void Clamp(); + bool IsClamped() const; + void Zero(); + // Requires this to have enough capacity (no tests done). + // Updates used_digits_ if necessary. + // shift_amount must be < kBigitSize. + void BigitsShiftLeft(int shift_amount); + // BigitLength includes the "hidden" digits encoded in the exponent. + int BigitLength() const { return used_digits_ + exponent_; } + Chunk BigitAt(int index) const; + void SubtractTimes(const Bignum& other, int factor); + + Chunk bigits_buffer_[kBigitCapacity]; + // A vector backed by bigits_buffer_. This way accesses to the array are + // checked for out-of-bounds errors. + Vector<Chunk> bigits_; + int used_digits_; + // The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize). + int exponent_; + + DISALLOW_COPY_AND_ASSIGN(Bignum); +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_BIGNUM_H_ diff --git a/src/3rdparty/double-conversion/cached-powers.cc b/src/3rdparty/double-conversion/cached-powers.cc new file mode 100644 index 0000000000..9536f26927 --- /dev/null +++ b/src/3rdparty/double-conversion/cached-powers.cc @@ -0,0 +1,178 @@ +// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <stdarg.h> +#include <limits.h> +#include <math.h> + +#include "utils.h" + +#include "cached-powers.h" + +namespace double_conversion { + +struct CachedPower { + uint64_t significand; + int16_t binary_exponent; + int16_t decimal_exponent; +}; + +static const CachedPower kCachedPowers[] = { + {UINT64_2PART_C(0xfa8fd5a0, 081c0288), -1220, -348}, + {UINT64_2PART_C(0xbaaee17f, a23ebf76), -1193, -340}, + {UINT64_2PART_C(0x8b16fb20, 3055ac76), -1166, -332}, + {UINT64_2PART_C(0xcf42894a, 5dce35ea), -1140, -324}, + {UINT64_2PART_C(0x9a6bb0aa, 55653b2d), -1113, -316}, + {UINT64_2PART_C(0xe61acf03, 3d1a45df), -1087, -308}, + {UINT64_2PART_C(0xab70fe17, c79ac6ca), -1060, -300}, + {UINT64_2PART_C(0xff77b1fc, bebcdc4f), -1034, -292}, + {UINT64_2PART_C(0xbe5691ef, 416bd60c), -1007, -284}, + {UINT64_2PART_C(0x8dd01fad, 907ffc3c), -980, -276}, + {UINT64_2PART_C(0xd3515c28, 31559a83), -954, -268}, + {UINT64_2PART_C(0x9d71ac8f, ada6c9b5), -927, -260}, + {UINT64_2PART_C(0xea9c2277, 23ee8bcb), -901, -252}, + {UINT64_2PART_C(0xaecc4991, 4078536d), -874, -244}, + {UINT64_2PART_C(0x823c1279, 5db6ce57), -847, -236}, + {UINT64_2PART_C(0xc2109436, 4dfb5637), -821, -228}, + {UINT64_2PART_C(0x9096ea6f, 3848984f), -794, -220}, + {UINT64_2PART_C(0xd77485cb, 25823ac7), -768, -212}, + {UINT64_2PART_C(0xa086cfcd, 97bf97f4), -741, -204}, + {UINT64_2PART_C(0xef340a98, 172aace5), -715, -196}, + {UINT64_2PART_C(0xb23867fb, 2a35b28e), -688, -188}, + {UINT64_2PART_C(0x84c8d4df, d2c63f3b), -661, -180}, + {UINT64_2PART_C(0xc5dd4427, 1ad3cdba), -635, -172}, + {UINT64_2PART_C(0x936b9fce, bb25c996), -608, -164}, + {UINT64_2PART_C(0xdbac6c24, 7d62a584), -582, -156}, + {UINT64_2PART_C(0xa3ab6658, 0d5fdaf6), -555, -148}, + {UINT64_2PART_C(0xf3e2f893, dec3f126), -529, -140}, + {UINT64_2PART_C(0xb5b5ada8, aaff80b8), -502, -132}, + {UINT64_2PART_C(0x87625f05, 6c7c4a8b), -475, -124}, + {UINT64_2PART_C(0xc9bcff60, 34c13053), -449, -116}, + {UINT64_2PART_C(0x964e858c, 91ba2655), -422, -108}, + {UINT64_2PART_C(0xdff97724, 70297ebd), -396, -100}, + {UINT64_2PART_C(0xa6dfbd9f, b8e5b88f), -369, -92}, + {UINT64_2PART_C(0xf8a95fcf, 88747d94), -343, -84}, + {UINT64_2PART_C(0xb9447093, 8fa89bcf), -316, -76}, + {UINT64_2PART_C(0x8a08f0f8, bf0f156b), -289, -68}, + {UINT64_2PART_C(0xcdb02555, 653131b6), -263, -60}, + {UINT64_2PART_C(0x993fe2c6, d07b7fac), -236, -52}, + {UINT64_2PART_C(0xe45c10c4, 2a2b3b06), -210, -44}, + {UINT64_2PART_C(0xaa242499, 697392d3), -183, -36}, + {UINT64_2PART_C(0xfd87b5f2, 8300ca0e), -157, -28}, + {UINT64_2PART_C(0xbce50864, 92111aeb), -130, -20}, + {UINT64_2PART_C(0x8cbccc09, 6f5088cc), -103, -12}, + {UINT64_2PART_C(0xd1b71758, e219652c), -77, -4}, + {UINT64_2PART_C(0x9c400000, 00000000), -50, 4}, + {UINT64_2PART_C(0xe8d4a510, 00000000), -24, 12}, + {UINT64_2PART_C(0xad78ebc5, ac620000), 3, 20}, + {UINT64_2PART_C(0x813f3978, f8940984), 30, 28}, + {UINT64_2PART_C(0xc097ce7b, c90715b3), 56, 36}, + {UINT64_2PART_C(0x8f7e32ce, 7bea5c70), 83, 44}, + {UINT64_2PART_C(0xd5d238a4, abe98068), 109, 52}, + {UINT64_2PART_C(0x9f4f2726, 179a2245), 136, 60}, + {UINT64_2PART_C(0xed63a231, d4c4fb27), 162, 68}, + {UINT64_2PART_C(0xb0de6538, 8cc8ada8), 189, 76}, + {UINT64_2PART_C(0x83c7088e, 1aab65db), 216, 84}, + {UINT64_2PART_C(0xc45d1df9, 42711d9a), 242, 92}, + {UINT64_2PART_C(0x924d692c, a61be758), 269, 100}, + {UINT64_2PART_C(0xda01ee64, 1a708dea), 295, 108}, + {UINT64_2PART_C(0xa26da399, 9aef774a), 322, 116}, + {UINT64_2PART_C(0xf209787b, b47d6b85), 348, 124}, + {UINT64_2PART_C(0xb454e4a1, 79dd1877), 375, 132}, + {UINT64_2PART_C(0x865b8692, 5b9bc5c2), 402, 140}, + {UINT64_2PART_C(0xc83553c5, c8965d3d), 428, 148}, + {UINT64_2PART_C(0x952ab45c, fa97a0b3), 455, 156}, + {UINT64_2PART_C(0xde469fbd, 99a05fe3), 481, 164}, + {UINT64_2PART_C(0xa59bc234, db398c25), 508, 172}, + {UINT64_2PART_C(0xf6c69a72, a3989f5c), 534, 180}, + {UINT64_2PART_C(0xb7dcbf53, 54e9bece), 561, 188}, + {UINT64_2PART_C(0x88fcf317, f22241e2), 588, 196}, + {UINT64_2PART_C(0xcc20ce9b, d35c78a5), 614, 204}, + {UINT64_2PART_C(0x98165af3, 7b2153df), 641, 212}, + {UINT64_2PART_C(0xe2a0b5dc, 971f303a), 667, 220}, + {UINT64_2PART_C(0xa8d9d153, 5ce3b396), 694, 228}, + {UINT64_2PART_C(0xfb9b7cd9, a4a7443c), 720, 236}, + {UINT64_2PART_C(0xbb764c4c, a7a44410), 747, 244}, + {UINT64_2PART_C(0x8bab8eef, b6409c1a), 774, 252}, + {UINT64_2PART_C(0xd01fef10, a657842c), 800, 260}, + {UINT64_2PART_C(0x9b10a4e5, e9913129), 827, 268}, + {UINT64_2PART_C(0xe7109bfb, a19c0c9d), 853, 276}, + {UINT64_2PART_C(0xac2820d9, 623bf429), 880, 284}, + {UINT64_2PART_C(0x80444b5e, 7aa7cf85), 907, 292}, + {UINT64_2PART_C(0xbf21e440, 03acdd2d), 933, 300}, + {UINT64_2PART_C(0x8e679c2f, 5e44ff8f), 960, 308}, + {UINT64_2PART_C(0xd433179d, 9c8cb841), 986, 316}, + {UINT64_2PART_C(0x9e19db92, b4e31ba9), 1013, 324}, + {UINT64_2PART_C(0xeb96bf6e, badf77d9), 1039, 332}, + {UINT64_2PART_C(0xaf87023b, 9bf0ee6b), 1066, 340}, +}; + +static const int kCachedPowersLength = ARRAY_SIZE(kCachedPowers); +static const int kCachedPowersOffset = 348; // -1 * the first decimal_exponent. +static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10) +// Difference between the decimal exponents in the table above. +const int PowersOfTenCache::kDecimalExponentDistance = 8; +const int PowersOfTenCache::kMinDecimalExponent = -348; +const int PowersOfTenCache::kMaxDecimalExponent = 340; + +void PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + int min_exponent, + int max_exponent, + DiyFp* power, + int* decimal_exponent) { + (void)max_exponent; // Silence unused parameter warning in release builds + (void)kCachedPowersLength; // Silence unused parameter warning in release builds + int kQ = DiyFp::kSignificandSize; + double k = ceil((min_exponent + kQ - 1) * kD_1_LOG2_10); + int foo = kCachedPowersOffset; + int index = + (foo + static_cast<int>(k) - 1) / kDecimalExponentDistance + 1; + ASSERT(0 <= index && index < kCachedPowersLength); + CachedPower cached_power = kCachedPowers[index]; + ASSERT(min_exponent <= cached_power.binary_exponent); + (void) max_exponent; // Mark variable as used. + ASSERT(cached_power.binary_exponent <= max_exponent); + *decimal_exponent = cached_power.decimal_exponent; + *power = DiyFp(cached_power.significand, cached_power.binary_exponent); +} + + +void PowersOfTenCache::GetCachedPowerForDecimalExponent(int requested_exponent, + DiyFp* power, + int* found_exponent) { + ASSERT(kMinDecimalExponent <= requested_exponent); + ASSERT(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance); + int index = + (requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance; + CachedPower cached_power = kCachedPowers[index]; + *power = DiyFp(cached_power.significand, cached_power.binary_exponent); + *found_exponent = cached_power.decimal_exponent; + ASSERT(*found_exponent <= requested_exponent); + ASSERT(requested_exponent < *found_exponent + kDecimalExponentDistance); +} + +} // namespace double_conversion diff --git a/src/3rdparty/double-conversion/cached-powers.h b/src/3rdparty/double-conversion/cached-powers.h new file mode 100644 index 0000000000..61a50614cf --- /dev/null +++ b/src/3rdparty/double-conversion/cached-powers.h @@ -0,0 +1,64 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_CACHED_POWERS_H_ +#define DOUBLE_CONVERSION_CACHED_POWERS_H_ + +#include "diy-fp.h" + +namespace double_conversion { + +class PowersOfTenCache { + public: + + // Not all powers of ten are cached. The decimal exponent of two neighboring + // cached numbers will differ by kDecimalExponentDistance. + static const int kDecimalExponentDistance; + + static const int kMinDecimalExponent; + static const int kMaxDecimalExponent; + + // Returns a cached power-of-ten with a binary exponent in the range + // [min_exponent; max_exponent] (boundaries included). + static void GetCachedPowerForBinaryExponentRange(int min_exponent, + int max_exponent, + DiyFp* power, + int* decimal_exponent); + + // Returns a cached power of ten x ~= 10^k such that + // k <= decimal_exponent < k + kCachedPowersDecimalDistance. + // The given decimal_exponent must satisfy + // kMinDecimalExponent <= requested_exponent, and + // requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance. + static void GetCachedPowerForDecimalExponent(int requested_exponent, + DiyFp* power, + int* found_exponent); +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_CACHED_POWERS_H_ diff --git a/src/3rdparty/double-conversion/diy-fp.cc b/src/3rdparty/double-conversion/diy-fp.cc new file mode 100644 index 0000000000..ddd1891b16 --- /dev/null +++ b/src/3rdparty/double-conversion/diy-fp.cc @@ -0,0 +1,57 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +#include "diy-fp.h" +#include "utils.h" + +namespace double_conversion { + +void DiyFp::Multiply(const DiyFp& other) { + // Simply "emulates" a 128 bit multiplication. + // However: the resulting number only contains 64 bits. The least + // significant 64 bits are only used for rounding the most significant 64 + // bits. + const uint64_t kM32 = 0xFFFFFFFFU; + uint64_t a = f_ >> 32; + uint64_t b = f_ & kM32; + uint64_t c = other.f_ >> 32; + uint64_t d = other.f_ & kM32; + uint64_t ac = a * c; + uint64_t bc = b * c; + uint64_t ad = a * d; + uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32); + // By adding 1U << 31 to tmp we round the final result. + // Halfway cases will be round up. + tmp += 1U << 31; + uint64_t result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); + e_ += other.e_ + 64; + f_ = result_f; +} + +} // namespace double_conversion diff --git a/src/3rdparty/double-conversion/diy-fp.h b/src/3rdparty/double-conversion/diy-fp.h new file mode 100644 index 0000000000..9dcf8fbdba --- /dev/null +++ b/src/3rdparty/double-conversion/diy-fp.h @@ -0,0 +1,118 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_DIY_FP_H_ +#define DOUBLE_CONVERSION_DIY_FP_H_ + +#include "utils.h" + +namespace double_conversion { + +// This "Do It Yourself Floating Point" class implements a floating-point number +// with a uint64 significand and an int exponent. Normalized DiyFp numbers will +// have the most significant bit of the significand set. +// Multiplication and Subtraction do not normalize their results. +// DiyFp are not designed to contain special doubles (NaN and Infinity). +class DiyFp { + public: + static const int kSignificandSize = 64; + + DiyFp() : f_(0), e_(0) {} + DiyFp(uint64_t f, int e) : f_(f), e_(e) {} + + // this = this - other. + // The exponents of both numbers must be the same and the significand of this + // must be bigger than the significand of other. + // The result will not be normalized. + void Subtract(const DiyFp& other) { + ASSERT(e_ == other.e_); + ASSERT(f_ >= other.f_); + f_ -= other.f_; + } + + // Returns a - b. + // The exponents of both numbers must be the same and this must be bigger + // than other. The result will not be normalized. + static DiyFp Minus(const DiyFp& a, const DiyFp& b) { + DiyFp result = a; + result.Subtract(b); + return result; + } + + + // this = this * other. + void Multiply(const DiyFp& other); + + // returns a * b; + static DiyFp Times(const DiyFp& a, const DiyFp& b) { + DiyFp result = a; + result.Multiply(b); + return result; + } + + void Normalize() { + ASSERT(f_ != 0); + uint64_t f = f_; + int e = e_; + + // This method is mainly called for normalizing boundaries. In general + // boundaries need to be shifted by 10 bits. We thus optimize for this case. + const uint64_t k10MSBits = UINT64_2PART_C(0xFFC00000, 00000000); + while ((f & k10MSBits) == 0) { + f <<= 10; + e -= 10; + } + while ((f & kUint64MSB) == 0) { + f <<= 1; + e--; + } + f_ = f; + e_ = e; + } + + static DiyFp Normalize(const DiyFp& a) { + DiyFp result = a; + result.Normalize(); + return result; + } + + uint64_t f() const { return f_; } + int e() const { return e_; } + + void set_f(uint64_t new_value) { f_ = new_value; } + void set_e(int new_value) { e_ = new_value; } + + private: + static const uint64_t kUint64MSB = UINT64_2PART_C(0x80000000, 00000000); + + uint64_t f_; + int e_; +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_DIY_FP_H_ diff --git a/src/3rdparty/double-conversion/double-conversion.cc b/src/3rdparty/double-conversion/double-conversion.cc new file mode 100644 index 0000000000..909985be82 --- /dev/null +++ b/src/3rdparty/double-conversion/double-conversion.cc @@ -0,0 +1,975 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <limits.h> +#include <math.h> + +#include "double-conversion.h" + +#include "bignum-dtoa.h" +#include "fast-dtoa.h" +#include "fixed-dtoa.h" +#include "ieee.h" +#include "strtod.h" +#include "utils.h" + +namespace double_conversion { + +const DoubleToStringConverter& DoubleToStringConverter::EcmaScriptConverter() { + int flags = UNIQUE_ZERO | EMIT_POSITIVE_EXPONENT_SIGN; + static DoubleToStringConverter converter(flags, + "Infinity", + "NaN", + 'e', + -6, 21, + 6, 0); + return converter; +} + + +bool DoubleToStringConverter::HandleSpecialValues( + double value, + StringBuilder* result_builder) const { + Double double_inspect(value); + if (double_inspect.IsInfinite()) { + if (infinity_symbol_ == NULL) return false; + if (value < 0) { + result_builder->AddCharacter('-'); + } + result_builder->AddString(infinity_symbol_); + return true; + } + if (double_inspect.IsNan()) { + if (nan_symbol_ == NULL) return false; + result_builder->AddString(nan_symbol_); + return true; + } + return false; +} + + +void DoubleToStringConverter::CreateExponentialRepresentation( + const char* decimal_digits, + int length, + int exponent, + StringBuilder* result_builder) const { + ASSERT(length != 0); + result_builder->AddCharacter(decimal_digits[0]); + if (length != 1) { + result_builder->AddCharacter('.'); + result_builder->AddSubstring(&decimal_digits[1], length-1); + } + result_builder->AddCharacter(exponent_character_); + if (exponent < 0) { + result_builder->AddCharacter('-'); + exponent = -exponent; + } else { + if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) { + result_builder->AddCharacter('+'); + } + } + if (exponent == 0) { + result_builder->AddCharacter('0'); + return; + } + ASSERT(exponent < 1e4); + const int kMaxExponentLength = 5; + char buffer[kMaxExponentLength + 1]; + buffer[kMaxExponentLength] = '\0'; + int first_char_pos = kMaxExponentLength; + while (exponent > 0) { + buffer[--first_char_pos] = '0' + (exponent % 10); + exponent /= 10; + } + result_builder->AddSubstring(&buffer[first_char_pos], + kMaxExponentLength - first_char_pos); +} + + +void DoubleToStringConverter::CreateDecimalRepresentation( + const char* decimal_digits, + int length, + int decimal_point, + int digits_after_point, + StringBuilder* result_builder) const { + // Create a representation that is padded with zeros if needed. + if (decimal_point <= 0) { + // "0.00000decimal_rep". + result_builder->AddCharacter('0'); + if (digits_after_point > 0) { + result_builder->AddCharacter('.'); + result_builder->AddPadding('0', -decimal_point); + ASSERT(length <= digits_after_point - (-decimal_point)); + result_builder->AddSubstring(decimal_digits, length); + int remaining_digits = digits_after_point - (-decimal_point) - length; + result_builder->AddPadding('0', remaining_digits); + } + } else if (decimal_point >= length) { + // "decimal_rep0000.00000" or "decimal_rep.0000" + result_builder->AddSubstring(decimal_digits, length); + result_builder->AddPadding('0', decimal_point - length); + if (digits_after_point > 0) { + result_builder->AddCharacter('.'); + result_builder->AddPadding('0', digits_after_point); + } + } else { + // "decima.l_rep000" + ASSERT(digits_after_point > 0); + result_builder->AddSubstring(decimal_digits, decimal_point); + result_builder->AddCharacter('.'); + ASSERT(length - decimal_point <= digits_after_point); + result_builder->AddSubstring(&decimal_digits[decimal_point], + length - decimal_point); + int remaining_digits = digits_after_point - (length - decimal_point); + result_builder->AddPadding('0', remaining_digits); + } + if (digits_after_point == 0) { + if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) { + result_builder->AddCharacter('.'); + } + if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) { + result_builder->AddCharacter('0'); + } + } +} + + +bool DoubleToStringConverter::ToShortestIeeeNumber( + double value, + StringBuilder* result_builder, + DoubleToStringConverter::DtoaMode mode) const { + ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE); + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + int decimal_point; + bool sign; + const int kDecimalRepCapacity = kBase10MaximalLength + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + DoubleToAscii(value, mode, 0, decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + + bool unique_zero = (flags_ & UNIQUE_ZERO) != 0; + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + int exponent = decimal_point - 1; + if ((decimal_in_shortest_low_ <= exponent) && + (exponent < decimal_in_shortest_high_)) { + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, + decimal_point, + Max(0, decimal_rep_length - decimal_point), + result_builder); + } else { + CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, + result_builder); + } + return true; +} + + +bool DoubleToStringConverter::ToFixed(double value, + int requested_digits, + StringBuilder* result_builder) const { + ASSERT(kMaxFixedDigitsBeforePoint == 60); + const double kFirstNonFixed = 1e60; + + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (requested_digits > kMaxFixedDigitsAfterPoint) return false; + if (value >= kFirstNonFixed || value <= -kFirstNonFixed) return false; + + // Find a sufficiently precise decimal representation of n. + int decimal_point; + bool sign; + // Add space for the '\0' byte. + const int kDecimalRepCapacity = + kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + DoubleToAscii(value, FIXED, requested_digits, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + requested_digits, result_builder); + return true; +} + + +bool DoubleToStringConverter::ToExponential( + double value, + int requested_digits, + StringBuilder* result_builder) const { + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (requested_digits < -1) return false; + if (requested_digits > kMaxExponentialDigits) return false; + + int decimal_point; + bool sign; + // Add space for digit before the decimal point and the '\0' character. + const int kDecimalRepCapacity = kMaxExponentialDigits + 2; + ASSERT(kDecimalRepCapacity > kBase10MaximalLength); + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + if (requested_digits == -1) { + DoubleToAscii(value, SHORTEST, 0, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + } else { + DoubleToAscii(value, PRECISION, requested_digits + 1, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + ASSERT(decimal_rep_length <= requested_digits + 1); + + for (int i = decimal_rep_length; i < requested_digits + 1; ++i) { + decimal_rep[i] = '0'; + } + decimal_rep_length = requested_digits + 1; + } + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + int exponent = decimal_point - 1; + CreateExponentialRepresentation(decimal_rep, + decimal_rep_length, + exponent, + result_builder); + return true; +} + + +bool DoubleToStringConverter::ToPrecision(double value, + int precision, + StringBuilder* result_builder) const { + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) { + return false; + } + + // Find a sufficiently precise decimal representation of n. + int decimal_point; + bool sign; + // Add one for the terminating null character. + const int kDecimalRepCapacity = kMaxPrecisionDigits + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + DoubleToAscii(value, PRECISION, precision, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + ASSERT(decimal_rep_length <= precision); + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + // The exponent if we print the number as x.xxeyyy. That is with the + // decimal point after the first digit. + int exponent = decimal_point - 1; + + int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0; + if ((-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) || + (decimal_point - precision + extra_zero > + max_trailing_padding_zeroes_in_precision_mode_)) { + // Fill buffer to contain 'precision' digits. + // Usually the buffer is already at the correct length, but 'DoubleToAscii' + // is allowed to return less characters. + for (int i = decimal_rep_length; i < precision; ++i) { + decimal_rep[i] = '0'; + } + + CreateExponentialRepresentation(decimal_rep, + precision, + exponent, + result_builder); + } else { + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + Max(0, precision - decimal_point), + result_builder); + } + return true; +} + + +static BignumDtoaMode DtoaToBignumDtoaMode( + DoubleToStringConverter::DtoaMode dtoa_mode) { + switch (dtoa_mode) { + case DoubleToStringConverter::SHORTEST: return BIGNUM_DTOA_SHORTEST; + case DoubleToStringConverter::SHORTEST_SINGLE: + return BIGNUM_DTOA_SHORTEST_SINGLE; + case DoubleToStringConverter::FIXED: return BIGNUM_DTOA_FIXED; + case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION; + default: + UNREACHABLE(); + } +} + + +void DoubleToStringConverter::DoubleToAscii(double v, + DtoaMode mode, + int requested_digits, + char* buffer, + int buffer_length, + bool* sign, + int* length, + int* point) { + Vector<char> vector(buffer, buffer_length); + ASSERT(!Double(v).IsSpecial()); + ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE || requested_digits >= 0); + + if (Double(v).Sign() < 0) { + *sign = true; + v = -v; + } else { + *sign = false; + } + + if (mode == PRECISION && requested_digits == 0) { + vector[0] = '\0'; + *length = 0; + return; + } + + if (v == 0) { + vector[0] = '0'; + vector[1] = '\0'; + *length = 1; + *point = 1; + return; + } + + bool fast_worked; + switch (mode) { + case SHORTEST: + fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point); + break; + case SHORTEST_SINGLE: + fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST_SINGLE, 0, + vector, length, point); + break; + case FIXED: + fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point); + break; + case PRECISION: + fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, + vector, length, point); + break; + default: + fast_worked = false; + UNREACHABLE(); + } + if (fast_worked) return; + + // If the fast dtoa didn't succeed use the slower bignum version. + BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode); + BignumDtoa(v, bignum_mode, requested_digits, vector, length, point); + vector[*length] = '\0'; +} + + +// Consumes the given substring from the iterator. +// Returns false, if the substring does not match. +template <class Iterator> +static bool ConsumeSubString(Iterator* current, + Iterator end, + const char* substring) { + ASSERT(**current == *substring); + for (substring++; *substring != '\0'; substring++) { + ++*current; + if (*current == end || **current != *substring) return false; + } + ++*current; + return true; +} + + +// Maximum number of significant digits in decimal representation. +// The longest possible double in decimal representation is +// (2^53 - 1) * 2 ^ -1074 that is (2 ^ 53 - 1) * 5 ^ 1074 / 10 ^ 1074 +// (768 digits). If we parse a number whose first digits are equal to a +// mean of 2 adjacent doubles (that could have up to 769 digits) the result +// must be rounded to the bigger one unless the tail consists of zeros, so +// we don't need to preserve all the digits. +const int kMaxSignificantDigits = 772; + + +static const char kWhitespaceTable7[] = { 32, 13, 10, 9, 11, 12 }; +static const int kWhitespaceTable7Length = ARRAY_SIZE(kWhitespaceTable7); + + +static const uc16 kWhitespaceTable16[] = { + 160, 8232, 8233, 5760, 6158, 8192, 8193, 8194, 8195, + 8196, 8197, 8198, 8199, 8200, 8201, 8202, 8239, 8287, 12288, 65279 +}; +static const int kWhitespaceTable16Length = ARRAY_SIZE(kWhitespaceTable16); + + +static bool isWhitespace(int x) { + if (x < 128) { + for (int i = 0; i < kWhitespaceTable7Length; i++) { + if (kWhitespaceTable7[i] == x) return true; + } + } else { + for (int i = 0; i < kWhitespaceTable16Length; i++) { + if (kWhitespaceTable16[i] == x) return true; + } + } + return false; +} + + +// Returns true if a nonspace found and false if the end has reached. +template <class Iterator> +static inline bool AdvanceToNonspace(Iterator* current, Iterator end) { + while (*current != end) { + if (!isWhitespace(**current)) return true; + ++*current; + } + return false; +} + + +static bool isDigit(int x, int radix) { + return (x >= '0' && x <= '9' && x < '0' + radix) + || (radix > 10 && x >= 'a' && x < 'a' + radix - 10) + || (radix > 10 && x >= 'A' && x < 'A' + radix - 10); +} + + +static double SignedZero(bool sign) { + return sign ? -0.0 : 0.0; +} + + +// Returns true if 'c' is a decimal digit that is valid for the given radix. +// +// The function is small and could be inlined, but VS2012 emitted a warning +// because it constant-propagated the radix and concluded that the last +// condition was always true. By moving it into a separate function the +// compiler wouldn't warn anymore. +static bool IsDecimalDigitForRadix(int c, int radix) { + return '0' <= c && c <= '9' && (c - '0') < radix; +} + +// Returns true if 'c' is a character digit that is valid for the given radix. +// The 'a_character' should be 'a' or 'A'. +// +// The function is small and could be inlined, but VS2012 emitted a warning +// because it constant-propagated the radix and concluded that the first +// condition was always false. By moving it into a separate function the +// compiler wouldn't warn anymore. +static bool IsCharacterDigitForRadix(int c, int radix, char a_character) { + return radix > 10 && c >= a_character && c < a_character + radix - 10; +} + + +// Parsing integers with radix 2, 4, 8, 16, 32. Assumes current != end. +template <int radix_log_2, class Iterator> +static double RadixStringToIeee(Iterator* current, + Iterator end, + bool sign, + bool allow_trailing_junk, + double junk_string_value, + bool read_as_double, + bool* result_is_junk) { + ASSERT(*current != end); + + const int kDoubleSize = Double::kSignificandSize; + const int kSingleSize = Single::kSignificandSize; + const int kSignificandSize = read_as_double? kDoubleSize: kSingleSize; + + *result_is_junk = true; + + // Skip leading 0s. + while (**current == '0') { + ++(*current); + if (*current == end) { + *result_is_junk = false; + return SignedZero(sign); + } + } + + int64_t number = 0; + int exponent = 0; + const int radix = (1 << radix_log_2); + + do { + int digit; + if (IsDecimalDigitForRadix(**current, radix)) { + digit = static_cast<char>(**current) - '0'; + } else if (IsCharacterDigitForRadix(**current, radix, 'a')) { + digit = static_cast<char>(**current) - 'a' + 10; + } else if (IsCharacterDigitForRadix(**current, radix, 'A')) { + digit = static_cast<char>(**current) - 'A' + 10; + } else { + if (allow_trailing_junk || !AdvanceToNonspace(current, end)) { + break; + } else { + return junk_string_value; + } + } + + number = number * radix + digit; + int overflow = static_cast<int>(number >> kSignificandSize); + if (overflow != 0) { + // Overflow occurred. Need to determine which direction to round the + // result. + int overflow_bits_count = 1; + while (overflow > 1) { + overflow_bits_count++; + overflow >>= 1; + } + + int dropped_bits_mask = ((1 << overflow_bits_count) - 1); + int dropped_bits = static_cast<int>(number) & dropped_bits_mask; + number >>= overflow_bits_count; + exponent = overflow_bits_count; + + bool zero_tail = true; + for (;;) { + ++(*current); + if (*current == end || !isDigit(**current, radix)) break; + zero_tail = zero_tail && **current == '0'; + exponent += radix_log_2; + } + + if (!allow_trailing_junk && AdvanceToNonspace(current, end)) { + return junk_string_value; + } + + int middle_value = (1 << (overflow_bits_count - 1)); + if (dropped_bits > middle_value) { + number++; // Rounding up. + } else if (dropped_bits == middle_value) { + // Rounding to even to consistency with decimals: half-way case rounds + // up if significant part is odd and down otherwise. + if ((number & 1) != 0 || !zero_tail) { + number++; // Rounding up. + } + } + + // Rounding up may cause overflow. + if ((number & ((int64_t)1 << kSignificandSize)) != 0) { + exponent++; + number >>= 1; + } + break; + } + ++(*current); + } while (*current != end); + + ASSERT(number < ((int64_t)1 << kSignificandSize)); + ASSERT(static_cast<int64_t>(static_cast<double>(number)) == number); + + *result_is_junk = false; + + if (exponent == 0) { + if (sign) { + if (number == 0) return -0.0; + number = -number; + } + return static_cast<double>(number); + } + + ASSERT(number != 0); + return Double(DiyFp(number, exponent)).value(); +} + + +template <class Iterator> +double StringToDoubleConverter::StringToIeee( + Iterator input, + int length, + bool read_as_double, + int* processed_characters_count) const { + Iterator current = input; + Iterator end = input + length; + + *processed_characters_count = 0; + + const bool allow_trailing_junk = (flags_ & ALLOW_TRAILING_JUNK) != 0; + const bool allow_leading_spaces = (flags_ & ALLOW_LEADING_SPACES) != 0; + const bool allow_trailing_spaces = (flags_ & ALLOW_TRAILING_SPACES) != 0; + const bool allow_spaces_after_sign = (flags_ & ALLOW_SPACES_AFTER_SIGN) != 0; + + // To make sure that iterator dereferencing is valid the following + // convention is used: + // 1. Each '++current' statement is followed by check for equality to 'end'. + // 2. If AdvanceToNonspace returned false then current == end. + // 3. If 'current' becomes equal to 'end' the function returns or goes to + // 'parsing_done'. + // 4. 'current' is not dereferenced after the 'parsing_done' label. + // 5. Code before 'parsing_done' may rely on 'current != end'. + if (current == end) return empty_string_value_; + + if (allow_leading_spaces || allow_trailing_spaces) { + if (!AdvanceToNonspace(¤t, end)) { + *processed_characters_count = static_cast<int>(current - input); + return empty_string_value_; + } + if (!allow_leading_spaces && (input != current)) { + // No leading spaces allowed, but AdvanceToNonspace moved forward. + return junk_string_value_; + } + } + + // The longest form of simplified number is: "-<significant digits>.1eXXX\0". + const int kBufferSize = kMaxSignificantDigits + 10; + char buffer[kBufferSize]; // NOLINT: size is known at compile time. + int buffer_pos = 0; + + // Exponent will be adjusted if insignificant digits of the integer part + // or insignificant leading zeros of the fractional part are dropped. + int exponent = 0; + int significant_digits = 0; + int insignificant_digits = 0; + bool nonzero_digit_dropped = false; + + bool sign = false; + + if (*current == '+' || *current == '-') { + sign = (*current == '-'); + ++current; + Iterator next_non_space = current; + // Skip following spaces (if allowed). + if (!AdvanceToNonspace(&next_non_space, end)) return junk_string_value_; + if (!allow_spaces_after_sign && (current != next_non_space)) { + return junk_string_value_; + } + current = next_non_space; + } + + if (infinity_symbol_ != NULL) { + if (*current == infinity_symbol_[0]) { + if (!ConsumeSubString(¤t, end, infinity_symbol_)) { + return junk_string_value_; + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + + ASSERT(buffer_pos == 0); + *processed_characters_count = static_cast<int>(current - input); + return sign ? -Double::Infinity() : Double::Infinity(); + } + } + + if (nan_symbol_ != NULL) { + if (*current == nan_symbol_[0]) { + if (!ConsumeSubString(¤t, end, nan_symbol_)) { + return junk_string_value_; + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + + ASSERT(buffer_pos == 0); + *processed_characters_count = static_cast<int>(current - input); + return sign ? -Double::NaN() : Double::NaN(); + } + } + + bool leading_zero = false; + if (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = static_cast<int>(current - input); + return SignedZero(sign); + } + + leading_zero = true; + + // It could be hexadecimal value. + if ((flags_ & ALLOW_HEX) && (*current == 'x' || *current == 'X')) { + ++current; + if (current == end || !isDigit(*current, 16)) { + return junk_string_value_; // "0x". + } + + bool result_is_junk; + double result = RadixStringToIeee<4>(¤t, + end, + sign, + allow_trailing_junk, + junk_string_value_, + read_as_double, + &result_is_junk); + if (!result_is_junk) { + if (allow_trailing_spaces) AdvanceToNonspace(¤t, end); + *processed_characters_count = static_cast<int>(current - input); + } + return result; + } + + // Ignore leading zeros in the integer part. + while (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = static_cast<int>(current - input); + return SignedZero(sign); + } + } + } + + bool octal = leading_zero && (flags_ & ALLOW_OCTALS) != 0; + + // Copy significant digits of the integer part (if any) to the buffer. + while (*current >= '0' && *current <= '9') { + if (significant_digits < kMaxSignificantDigits) { + ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos++] = static_cast<char>(*current); + significant_digits++; + // Will later check if it's an octal in the buffer. + } else { + insignificant_digits++; // Move the digit into the exponential part. + nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; + } + octal = octal && *current < '8'; + ++current; + if (current == end) goto parsing_done; + } + + if (significant_digits == 0) { + octal = false; + } + + if (*current == '.') { + if (octal && !allow_trailing_junk) return junk_string_value_; + if (octal) goto parsing_done; + + ++current; + if (current == end) { + if (significant_digits == 0 && !leading_zero) { + return junk_string_value_; + } else { + goto parsing_done; + } + } + + if (significant_digits == 0) { + // octal = false; + // Integer part consists of 0 or is absent. Significant digits start after + // leading zeros (if any). + while (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = static_cast<int>(current - input); + return SignedZero(sign); + } + exponent--; // Move this 0 into the exponent. + } + } + + // There is a fractional part. + // We don't emit a '.', but adjust the exponent instead. + while (*current >= '0' && *current <= '9') { + if (significant_digits < kMaxSignificantDigits) { + ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos++] = static_cast<char>(*current); + significant_digits++; + exponent--; + } else { + // Ignore insignificant digits in the fractional part. + nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; + } + ++current; + if (current == end) goto parsing_done; + } + } + + if (!leading_zero && exponent == 0 && significant_digits == 0) { + // If leading_zeros is true then the string contains zeros. + // If exponent < 0 then string was [+-]\.0*... + // If significant_digits != 0 the string is not equal to 0. + // Otherwise there are no digits in the string. + return junk_string_value_; + } + + // Parse exponential part. + if (*current == 'e' || *current == 'E') { + if (octal && !allow_trailing_junk) return junk_string_value_; + if (octal) goto parsing_done; + ++current; + if (current == end) { + if (allow_trailing_junk) { + goto parsing_done; + } else { + return junk_string_value_; + } + } + char sign = '+'; + if (*current == '+' || *current == '-') { + sign = static_cast<char>(*current); + ++current; + if (current == end) { + if (allow_trailing_junk) { + goto parsing_done; + } else { + return junk_string_value_; + } + } + } + + if (current == end || *current < '0' || *current > '9') { + if (allow_trailing_junk) { + goto parsing_done; + } else { + return junk_string_value_; + } + } + + const int max_exponent = INT_MAX / 2; + ASSERT(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2); + int num = 0; + do { + // Check overflow. + int digit = *current - '0'; + if (num >= max_exponent / 10 + && !(num == max_exponent / 10 && digit <= max_exponent % 10)) { + num = max_exponent; + } else { + num = num * 10 + digit; + } + ++current; + } while (current != end && *current >= '0' && *current <= '9'); + + exponent += (sign == '-' ? -num : num); + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + if (allow_trailing_spaces) { + AdvanceToNonspace(¤t, end); + } + + parsing_done: + exponent += insignificant_digits; + + if (octal) { + double result; + bool result_is_junk; + char* start = buffer; + result = RadixStringToIeee<3>(&start, + buffer + buffer_pos, + sign, + allow_trailing_junk, + junk_string_value_, + read_as_double, + &result_is_junk); + ASSERT(!result_is_junk); + *processed_characters_count = static_cast<int>(current - input); + return result; + } + + if (nonzero_digit_dropped) { + buffer[buffer_pos++] = '1'; + exponent--; + } + + ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos] = '\0'; + + double converted; + if (read_as_double) { + converted = Strtod(Vector<const char>(buffer, buffer_pos), exponent); + } else { + converted = Strtof(Vector<const char>(buffer, buffer_pos), exponent); + } + *processed_characters_count = static_cast<int>(current - input); + return sign? -converted: converted; +} + + +double StringToDoubleConverter::StringToDouble( + const char* buffer, + int length, + int* processed_characters_count) const { + return StringToIeee(buffer, length, true, processed_characters_count); +} + + +double StringToDoubleConverter::StringToDouble( + const uc16* buffer, + int length, + int* processed_characters_count) const { + return StringToIeee(buffer, length, true, processed_characters_count); +} + + +float StringToDoubleConverter::StringToFloat( + const char* buffer, + int length, + int* processed_characters_count) const { + return static_cast<float>(StringToIeee(buffer, length, false, + processed_characters_count)); +} + + +float StringToDoubleConverter::StringToFloat( + const uc16* buffer, + int length, + int* processed_characters_count) const { + return static_cast<float>(StringToIeee(buffer, length, false, + processed_characters_count)); +} + +} // namespace double_conversion diff --git a/src/3rdparty/double-conversion/double-conversion.pri b/src/3rdparty/double-conversion/double-conversion.pri new file mode 100644 index 0000000000..3207169689 --- /dev/null +++ b/src/3rdparty/double-conversion/double-conversion.pri @@ -0,0 +1,24 @@ +INCLUDEPATH += $$PWD/include $$PWD/include/double-conversion +SOURCES += \ + $$PWD/bignum.cc \ + $$PWD/bignum-dtoa.cc \ + $$PWD/cached-powers.cc \ + $$PWD/diy-fp.cc \ + $$PWD/double-conversion.cc \ + $$PWD/fast-dtoa.cc \ + $$PWD/fixed-dtoa.cc \ + $$PWD/strtod.cc + +HEADERS += \ + $$PWD/bignum-dtoa.h \ + $$PWD/bignum.h \ + $$PWD/cached-powers.h \ + $$PWD/diy-fp.h \ + $$PWD/include/double-conversion/double-conversion.h \ + $$PWD/fast-dtoa.h \ + $$PWD/fixed-dtoa.h \ + $$PWD/ieee.h \ + $$PWD/strtod.h \ + $$PWD/include/double-conversion/utils.h + +OTHER_FILES += README diff --git a/src/3rdparty/double-conversion/fast-dtoa.cc b/src/3rdparty/double-conversion/fast-dtoa.cc new file mode 100644 index 0000000000..61350383a9 --- /dev/null +++ b/src/3rdparty/double-conversion/fast-dtoa.cc @@ -0,0 +1,665 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "fast-dtoa.h" + +#include "cached-powers.h" +#include "diy-fp.h" +#include "ieee.h" + +namespace double_conversion { + +// The minimal and maximal target exponent define the range of w's binary +// exponent, where 'w' is the result of multiplying the input by a cached power +// of ten. +// +// A different range might be chosen on a different platform, to optimize digit +// generation, but a smaller range requires more powers of ten to be cached. +static const int kMinimalTargetExponent = -60; +static const int kMaximalTargetExponent = -32; + + +// Adjusts the last digit of the generated number, and screens out generated +// solutions that may be inaccurate. A solution may be inaccurate if it is +// outside the safe interval, or if we cannot prove that it is closer to the +// input than a neighboring representation of the same length. +// +// Input: * buffer containing the digits of too_high / 10^kappa +// * the buffer's length +// * distance_too_high_w == (too_high - w).f() * unit +// * unsafe_interval == (too_high - too_low).f() * unit +// * rest = (too_high - buffer * 10^kappa).f() * unit +// * ten_kappa = 10^kappa * unit +// * unit = the common multiplier +// Output: returns true if the buffer is guaranteed to contain the closest +// representable number to the input. +// Modifies the generated digits in the buffer to approach (round towards) w. +static bool RoundWeed(Vector<char> buffer, + int length, + uint64_t distance_too_high_w, + uint64_t unsafe_interval, + uint64_t rest, + uint64_t ten_kappa, + uint64_t unit) { + uint64_t small_distance = distance_too_high_w - unit; + uint64_t big_distance = distance_too_high_w + unit; + // Let w_low = too_high - big_distance, and + // w_high = too_high - small_distance. + // Note: w_low < w < w_high + // + // The real w (* unit) must lie somewhere inside the interval + // ]w_low; w_high[ (often written as "(w_low; w_high)") + + // Basically the buffer currently contains a number in the unsafe interval + // ]too_low; too_high[ with too_low < w < too_high + // + // too_high - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // ^v 1 unit ^ ^ ^ ^ + // boundary_high --------------------- . . . . + // ^v 1 unit . . . . + // - - - - - - - - - - - - - - - - - - - + - - + - - - - - - . . + // . . ^ . . + // . big_distance . . . + // . . . . rest + // small_distance . . . . + // v . . . . + // w_high - - - - - - - - - - - - - - - - - - . . . . + // ^v 1 unit . . . . + // w ---------------------------------------- . . . . + // ^v 1 unit v . . . + // w_low - - - - - - - - - - - - - - - - - - - - - . . . + // . . v + // buffer --------------------------------------------------+-------+-------- + // . . + // safe_interval . + // v . + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . + // ^v 1 unit . + // boundary_low ------------------------- unsafe_interval + // ^v 1 unit v + // too_low - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // + // + // Note that the value of buffer could lie anywhere inside the range too_low + // to too_high. + // + // boundary_low, boundary_high and w are approximations of the real boundaries + // and v (the input number). They are guaranteed to be precise up to one unit. + // In fact the error is guaranteed to be strictly less than one unit. + // + // Anything that lies outside the unsafe interval is guaranteed not to round + // to v when read again. + // Anything that lies inside the safe interval is guaranteed to round to v + // when read again. + // If the number inside the buffer lies inside the unsafe interval but not + // inside the safe interval then we simply do not know and bail out (returning + // false). + // + // Similarly we have to take into account the imprecision of 'w' when finding + // the closest representation of 'w'. If we have two potential + // representations, and one is closer to both w_low and w_high, then we know + // it is closer to the actual value v. + // + // By generating the digits of too_high we got the largest (closest to + // too_high) buffer that is still in the unsafe interval. In the case where + // w_high < buffer < too_high we try to decrement the buffer. + // This way the buffer approaches (rounds towards) w. + // There are 3 conditions that stop the decrementation process: + // 1) the buffer is already below w_high + // 2) decrementing the buffer would make it leave the unsafe interval + // 3) decrementing the buffer would yield a number below w_high and farther + // away than the current number. In other words: + // (buffer{-1} < w_high) && w_high - buffer{-1} > buffer - w_high + // Instead of using the buffer directly we use its distance to too_high. + // Conceptually rest ~= too_high - buffer + // We need to do the following tests in this order to avoid over- and + // underflows. + ASSERT(rest <= unsafe_interval); + while (rest < small_distance && // Negated condition 1 + unsafe_interval - rest >= ten_kappa && // Negated condition 2 + (rest + ten_kappa < small_distance || // buffer{-1} > w_high + small_distance - rest >= rest + ten_kappa - small_distance)) { + buffer[length - 1]--; + rest += ten_kappa; + } + + // We have approached w+ as much as possible. We now test if approaching w- + // would require changing the buffer. If yes, then we have two possible + // representations close to w, but we cannot decide which one is closer. + if (rest < big_distance && + unsafe_interval - rest >= ten_kappa && + (rest + ten_kappa < big_distance || + big_distance - rest > rest + ten_kappa - big_distance)) { + return false; + } + + // Weeding test. + // The safe interval is [too_low + 2 ulp; too_high - 2 ulp] + // Since too_low = too_high - unsafe_interval this is equivalent to + // [too_high - unsafe_interval + 4 ulp; too_high - 2 ulp] + // Conceptually we have: rest ~= too_high - buffer + return (2 * unit <= rest) && (rest <= unsafe_interval - 4 * unit); +} + + +// Rounds the buffer upwards if the result is closer to v by possibly adding +// 1 to the buffer. If the precision of the calculation is not sufficient to +// round correctly, return false. +// The rounding might shift the whole buffer in which case the kappa is +// adjusted. For example "99", kappa = 3 might become "10", kappa = 4. +// +// If 2*rest > ten_kappa then the buffer needs to be round up. +// rest can have an error of +/- 1 unit. This function accounts for the +// imprecision and returns false, if the rounding direction cannot be +// unambiguously determined. +// +// Precondition: rest < ten_kappa. +static bool RoundWeedCounted(Vector<char> buffer, + int length, + uint64_t rest, + uint64_t ten_kappa, + uint64_t unit, + int* kappa) { + ASSERT(rest < ten_kappa); + // The following tests are done in a specific order to avoid overflows. They + // will work correctly with any uint64 values of rest < ten_kappa and unit. + // + // If the unit is too big, then we don't know which way to round. For example + // a unit of 50 means that the real number lies within rest +/- 50. If + // 10^kappa == 40 then there is no way to tell which way to round. + if (unit >= ten_kappa) return false; + // Even if unit is just half the size of 10^kappa we are already completely + // lost. (And after the previous test we know that the expression will not + // over/underflow.) + if (ten_kappa - unit <= unit) return false; + // If 2 * (rest + unit) <= 10^kappa we can safely round down. + if ((ten_kappa - rest > rest) && (ten_kappa - 2 * rest >= 2 * unit)) { + return true; + } + // If 2 * (rest - unit) >= 10^kappa, then we can safely round up. + if ((rest > unit) && (ten_kappa - (rest - unit) <= (rest - unit))) { + // Increment the last digit recursively until we find a non '9' digit. + buffer[length - 1]++; + for (int i = length - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) break; + buffer[i] = '0'; + buffer[i - 1]++; + } + // If the first digit is now '0'+ 10 we had a buffer with all '9's. With the + // exception of the first digit all digits are now '0'. Simply switch the + // first digit to '1' and adjust the kappa. Example: "99" becomes "10" and + // the power (the kappa) is increased. + if (buffer[0] == '0' + 10) { + buffer[0] = '1'; + (*kappa) += 1; + } + return true; + } + return false; +} + +// Returns the biggest power of ten that is less than or equal to the given +// number. We furthermore receive the maximum number of bits 'number' has. +// +// Returns power == 10^(exponent_plus_one-1) such that +// power <= number < power * 10. +// If number_bits == 0 then 0^(0-1) is returned. +// The number of bits must be <= 32. +// Precondition: number < (1 << (number_bits + 1)). + +// Inspired by the method for finding an integer log base 10 from here: +// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 +static unsigned int const kSmallPowersOfTen[] = + {0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, + 1000000000}; + +static void BiggestPowerTen(uint32_t number, + int number_bits, + uint32_t* power, + int* exponent_plus_one) { + ASSERT(number < (1u << (number_bits + 1))); + // 1233/4096 is approximately 1/lg(10). + int exponent_plus_one_guess = ((number_bits + 1) * 1233 >> 12); + // We increment to skip over the first entry in the kPowersOf10 table. + // Note: kPowersOf10[i] == 10^(i-1). + exponent_plus_one_guess++; + // We don't have any guarantees that 2^number_bits <= number. + if (number < kSmallPowersOfTen[exponent_plus_one_guess]) { + exponent_plus_one_guess--; + } + *power = kSmallPowersOfTen[exponent_plus_one_guess]; + *exponent_plus_one = exponent_plus_one_guess; +} + +// Generates the digits of input number w. +// w is a floating-point number (DiyFp), consisting of a significand and an +// exponent. Its exponent is bounded by kMinimalTargetExponent and +// kMaximalTargetExponent. +// Hence -60 <= w.e() <= -32. +// +// Returns false if it fails, in which case the generated digits in the buffer +// should not be used. +// Preconditions: +// * low, w and high are correct up to 1 ulp (unit in the last place). That +// is, their error must be less than a unit of their last digits. +// * low.e() == w.e() == high.e() +// * low < w < high, and taking into account their error: low~ <= high~ +// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent +// Postconditions: returns false if procedure fails. +// otherwise: +// * buffer is not null-terminated, but len contains the number of digits. +// * buffer contains the shortest possible decimal digit-sequence +// such that LOW < buffer * 10^kappa < HIGH, where LOW and HIGH are the +// correct values of low and high (without their error). +// * if more than one decimal representation gives the minimal number of +// decimal digits then the one closest to W (where W is the correct value +// of w) is chosen. +// Remark: this procedure takes into account the imprecision of its input +// numbers. If the precision is not enough to guarantee all the postconditions +// then false is returned. This usually happens rarely (~0.5%). +// +// Say, for the sake of example, that +// w.e() == -48, and w.f() == 0x1234567890abcdef +// w's value can be computed by w.f() * 2^w.e() +// We can obtain w's integral digits by simply shifting w.f() by -w.e(). +// -> w's integral part is 0x1234 +// w's fractional part is therefore 0x567890abcdef. +// Printing w's integral part is easy (simply print 0x1234 in decimal). +// In order to print its fraction we repeatedly multiply the fraction by 10 and +// get each digit. Example the first digit after the point would be computed by +// (0x567890abcdef * 10) >> 48. -> 3 +// The whole thing becomes slightly more complicated because we want to stop +// once we have enough digits. That is, once the digits inside the buffer +// represent 'w' we can stop. Everything inside the interval low - high +// represents w. However we have to pay attention to low, high and w's +// imprecision. +static bool DigitGen(DiyFp low, + DiyFp w, + DiyFp high, + Vector<char> buffer, + int* length, + int* kappa) { + ASSERT(low.e() == w.e() && w.e() == high.e()); + ASSERT(low.f() + 1 <= high.f() - 1); + ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); + // low, w and high are imprecise, but by less than one ulp (unit in the last + // place). + // If we remove (resp. add) 1 ulp from low (resp. high) we are certain that + // the new numbers are outside of the interval we want the final + // representation to lie in. + // Inversely adding (resp. removing) 1 ulp from low (resp. high) would yield + // numbers that are certain to lie in the interval. We will use this fact + // later on. + // We will now start by generating the digits within the uncertain + // interval. Later we will weed out representations that lie outside the safe + // interval and thus _might_ lie outside the correct interval. + uint64_t unit = 1; + DiyFp too_low = DiyFp(low.f() - unit, low.e()); + DiyFp too_high = DiyFp(high.f() + unit, high.e()); + // too_low and too_high are guaranteed to lie outside the interval we want the + // generated number in. + DiyFp unsafe_interval = DiyFp::Minus(too_high, too_low); + // We now cut the input number into two parts: the integral digits and the + // fractionals. We will not write any decimal separator though, but adapt + // kappa instead. + // Reminder: we are currently computing the digits (stored inside the buffer) + // such that: too_low < buffer * 10^kappa < too_high + // We use too_high for the digit_generation and stop as soon as possible. + // If we stop early we effectively round down. + DiyFp one = DiyFp(static_cast<uint64_t>(1) << -w.e(), w.e()); + // Division by one is a shift. + uint32_t integrals = static_cast<uint32_t>(too_high.f() >> -one.e()); + // Modulo by one is an and. + uint64_t fractionals = too_high.f() & (one.f() - 1); + uint32_t divisor; + int divisor_exponent_plus_one; + BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), + &divisor, &divisor_exponent_plus_one); + *kappa = divisor_exponent_plus_one; + *length = 0; + // Loop invariant: buffer = too_high / 10^kappa (integer division) + // The invariant holds for the first iteration: kappa has been initialized + // with the divisor exponent + 1. And the divisor is the biggest power of ten + // that is smaller than integrals. + while (*kappa > 0) { + int digit = integrals / divisor; + ASSERT(digit <= 9); + buffer[*length] = static_cast<char>('0' + digit); + (*length)++; + integrals %= divisor; + (*kappa)--; + // Note that kappa now equals the exponent of the divisor and that the + // invariant thus holds again. + uint64_t rest = + (static_cast<uint64_t>(integrals) << -one.e()) + fractionals; + // Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e()) + // Reminder: unsafe_interval.e() == one.e() + if (rest < unsafe_interval.f()) { + // Rounding down (by not emitting the remaining digits) yields a number + // that lies within the unsafe interval. + return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f(), + unsafe_interval.f(), rest, + static_cast<uint64_t>(divisor) << -one.e(), unit); + } + divisor /= 10; + } + + // The integrals have been generated. We are at the point of the decimal + // separator. In the following loop we simply multiply the remaining digits by + // 10 and divide by one. We just need to pay attention to multiply associated + // data (like the interval or 'unit'), too. + // Note that the multiplication by 10 does not overflow, because w.e >= -60 + // and thus one.e >= -60. + ASSERT(one.e() >= -60); + ASSERT(fractionals < one.f()); + ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); + for (;;) { + fractionals *= 10; + unit *= 10; + unsafe_interval.set_f(unsafe_interval.f() * 10); + // Integer division by one. + int digit = static_cast<int>(fractionals >> -one.e()); + ASSERT(digit <= 9); + buffer[*length] = static_cast<char>('0' + digit); + (*length)++; + fractionals &= one.f() - 1; // Modulo by one. + (*kappa)--; + if (fractionals < unsafe_interval.f()) { + return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f() * unit, + unsafe_interval.f(), fractionals, one.f(), unit); + } + } +} + + + +// Generates (at most) requested_digits digits of input number w. +// w is a floating-point number (DiyFp), consisting of a significand and an +// exponent. Its exponent is bounded by kMinimalTargetExponent and +// kMaximalTargetExponent. +// Hence -60 <= w.e() <= -32. +// +// Returns false if it fails, in which case the generated digits in the buffer +// should not be used. +// Preconditions: +// * w is correct up to 1 ulp (unit in the last place). That +// is, its error must be strictly less than a unit of its last digit. +// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent +// +// Postconditions: returns false if procedure fails. +// otherwise: +// * buffer is not null-terminated, but length contains the number of +// digits. +// * the representation in buffer is the most precise representation of +// requested_digits digits. +// * buffer contains at most requested_digits digits of w. If there are less +// than requested_digits digits then some trailing '0's have been removed. +// * kappa is such that +// w = buffer * 10^kappa + eps with |eps| < 10^kappa / 2. +// +// Remark: This procedure takes into account the imprecision of its input +// numbers. If the precision is not enough to guarantee all the postconditions +// then false is returned. This usually happens rarely, but the failure-rate +// increases with higher requested_digits. +static bool DigitGenCounted(DiyFp w, + int requested_digits, + Vector<char> buffer, + int* length, + int* kappa) { + ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); + ASSERT(kMinimalTargetExponent >= -60); + ASSERT(kMaximalTargetExponent <= -32); + // w is assumed to have an error less than 1 unit. Whenever w is scaled we + // also scale its error. + uint64_t w_error = 1; + // We cut the input number into two parts: the integral digits and the + // fractional digits. We don't emit any decimal separator, but adapt kappa + // instead. Example: instead of writing "1.2" we put "12" into the buffer and + // increase kappa by 1. + DiyFp one = DiyFp(static_cast<uint64_t>(1) << -w.e(), w.e()); + // Division by one is a shift. + uint32_t integrals = static_cast<uint32_t>(w.f() >> -one.e()); + // Modulo by one is an and. + uint64_t fractionals = w.f() & (one.f() - 1); + uint32_t divisor; + int divisor_exponent_plus_one; + BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), + &divisor, &divisor_exponent_plus_one); + *kappa = divisor_exponent_plus_one; + *length = 0; + + // Loop invariant: buffer = w / 10^kappa (integer division) + // The invariant holds for the first iteration: kappa has been initialized + // with the divisor exponent + 1. And the divisor is the biggest power of ten + // that is smaller than 'integrals'. + while (*kappa > 0) { + int digit = integrals / divisor; + ASSERT(digit <= 9); + buffer[*length] = static_cast<char>('0' + digit); + (*length)++; + requested_digits--; + integrals %= divisor; + (*kappa)--; + // Note that kappa now equals the exponent of the divisor and that the + // invariant thus holds again. + if (requested_digits == 0) break; + divisor /= 10; + } + + if (requested_digits == 0) { + uint64_t rest = + (static_cast<uint64_t>(integrals) << -one.e()) + fractionals; + return RoundWeedCounted(buffer, *length, rest, + static_cast<uint64_t>(divisor) << -one.e(), w_error, + kappa); + } + + // The integrals have been generated. We are at the point of the decimal + // separator. In the following loop we simply multiply the remaining digits by + // 10 and divide by one. We just need to pay attention to multiply associated + // data (the 'unit'), too. + // Note that the multiplication by 10 does not overflow, because w.e >= -60 + // and thus one.e >= -60. + ASSERT(one.e() >= -60); + ASSERT(fractionals < one.f()); + ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); + while (requested_digits > 0 && fractionals > w_error) { + fractionals *= 10; + w_error *= 10; + // Integer division by one. + int digit = static_cast<int>(fractionals >> -one.e()); + ASSERT(digit <= 9); + buffer[*length] = static_cast<char>('0' + digit); + (*length)++; + requested_digits--; + fractionals &= one.f() - 1; // Modulo by one. + (*kappa)--; + } + if (requested_digits != 0) return false; + return RoundWeedCounted(buffer, *length, fractionals, one.f(), w_error, + kappa); +} + + +// Provides a decimal representation of v. +// Returns true if it succeeds, otherwise the result cannot be trusted. +// There will be *length digits inside the buffer (not null-terminated). +// If the function returns true then +// v == (double) (buffer * 10^decimal_exponent). +// The digits in the buffer are the shortest representation possible: no +// 0.09999999999999999 instead of 0.1. The shorter representation will even be +// chosen even if the longer one would be closer to v. +// The last digit will be closest to the actual v. That is, even if several +// digits might correctly yield 'v' when read again, the closest will be +// computed. +static bool Grisu3(double v, + FastDtoaMode mode, + Vector<char> buffer, + int* length, + int* decimal_exponent) { + DiyFp w = Double(v).AsNormalizedDiyFp(); + // boundary_minus and boundary_plus are the boundaries between v and its + // closest floating-point neighbors. Any number strictly between + // boundary_minus and boundary_plus will round to v when convert to a double. + // Grisu3 will never output representations that lie exactly on a boundary. + DiyFp boundary_minus, boundary_plus; + if (mode == FAST_DTOA_SHORTEST) { + Double(v).NormalizedBoundaries(&boundary_minus, &boundary_plus); + } else { + ASSERT(mode == FAST_DTOA_SHORTEST_SINGLE); + float single_v = static_cast<float>(v); + Single(single_v).NormalizedBoundaries(&boundary_minus, &boundary_plus); + } + ASSERT(boundary_plus.e() == w.e()); + DiyFp ten_mk; // Cached power of ten: 10^-k + int mk; // -k + int ten_mk_minimal_binary_exponent = + kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); + int ten_mk_maximal_binary_exponent = + kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); + PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + ten_mk_minimal_binary_exponent, + ten_mk_maximal_binary_exponent, + &ten_mk, &mk); + ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + + DiyFp::kSignificandSize) && + (kMaximalTargetExponent >= w.e() + ten_mk.e() + + DiyFp::kSignificandSize)); + // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a + // 64 bit significand and ten_mk is thus only precise up to 64 bits. + + // The DiyFp::Times procedure rounds its result, and ten_mk is approximated + // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now + // off by a small amount. + // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. + // In other words: let f = scaled_w.f() and e = scaled_w.e(), then + // (f-1) * 2^e < w*10^k < (f+1) * 2^e + DiyFp scaled_w = DiyFp::Times(w, ten_mk); + ASSERT(scaled_w.e() == + boundary_plus.e() + ten_mk.e() + DiyFp::kSignificandSize); + // In theory it would be possible to avoid some recomputations by computing + // the difference between w and boundary_minus/plus (a power of 2) and to + // compute scaled_boundary_minus/plus by subtracting/adding from + // scaled_w. However the code becomes much less readable and the speed + // enhancements are not terriffic. + DiyFp scaled_boundary_minus = DiyFp::Times(boundary_minus, ten_mk); + DiyFp scaled_boundary_plus = DiyFp::Times(boundary_plus, ten_mk); + + // DigitGen will generate the digits of scaled_w. Therefore we have + // v == (double) (scaled_w * 10^-mk). + // Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an + // integer than it will be updated. For instance if scaled_w == 1.23 then + // the buffer will be filled with "123" und the decimal_exponent will be + // decreased by 2. + int kappa; + bool result = DigitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus, + buffer, length, &kappa); + *decimal_exponent = -mk + kappa; + return result; +} + + +// The "counted" version of grisu3 (see above) only generates requested_digits +// number of digits. This version does not generate the shortest representation, +// and with enough requested digits 0.1 will at some point print as 0.9999999... +// Grisu3 is too imprecise for real halfway cases (1.5 will not work) and +// therefore the rounding strategy for halfway cases is irrelevant. +static bool Grisu3Counted(double v, + int requested_digits, + Vector<char> buffer, + int* length, + int* decimal_exponent) { + DiyFp w = Double(v).AsNormalizedDiyFp(); + DiyFp ten_mk; // Cached power of ten: 10^-k + int mk; // -k + int ten_mk_minimal_binary_exponent = + kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); + int ten_mk_maximal_binary_exponent = + kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); + PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + ten_mk_minimal_binary_exponent, + ten_mk_maximal_binary_exponent, + &ten_mk, &mk); + ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + + DiyFp::kSignificandSize) && + (kMaximalTargetExponent >= w.e() + ten_mk.e() + + DiyFp::kSignificandSize)); + // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a + // 64 bit significand and ten_mk is thus only precise up to 64 bits. + + // The DiyFp::Times procedure rounds its result, and ten_mk is approximated + // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now + // off by a small amount. + // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. + // In other words: let f = scaled_w.f() and e = scaled_w.e(), then + // (f-1) * 2^e < w*10^k < (f+1) * 2^e + DiyFp scaled_w = DiyFp::Times(w, ten_mk); + + // We now have (double) (scaled_w * 10^-mk). + // DigitGen will generate the first requested_digits digits of scaled_w and + // return together with a kappa such that scaled_w ~= buffer * 10^kappa. (It + // will not always be exactly the same since DigitGenCounted only produces a + // limited number of digits.) + int kappa; + bool result = DigitGenCounted(scaled_w, requested_digits, + buffer, length, &kappa); + *decimal_exponent = -mk + kappa; + return result; +} + + +bool FastDtoa(double v, + FastDtoaMode mode, + int requested_digits, + Vector<char> buffer, + int* length, + int* decimal_point) { + ASSERT(v > 0); + ASSERT(!Double(v).IsSpecial()); + + bool result = false; + int decimal_exponent = 0; + switch (mode) { + case FAST_DTOA_SHORTEST: + case FAST_DTOA_SHORTEST_SINGLE: + result = Grisu3(v, mode, buffer, length, &decimal_exponent); + break; + case FAST_DTOA_PRECISION: + result = Grisu3Counted(v, requested_digits, + buffer, length, &decimal_exponent); + break; + default: + UNREACHABLE(); + } + if (result) { + *decimal_point = *length + decimal_exponent; + buffer[*length] = '\0'; + } + return result; +} + +} // namespace double_conversion diff --git a/src/3rdparty/double-conversion/fast-dtoa.h b/src/3rdparty/double-conversion/fast-dtoa.h new file mode 100644 index 0000000000..5f1e8eee5e --- /dev/null +++ b/src/3rdparty/double-conversion/fast-dtoa.h @@ -0,0 +1,88 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_FAST_DTOA_H_ +#define DOUBLE_CONVERSION_FAST_DTOA_H_ + +#include "utils.h" + +namespace double_conversion { + +enum FastDtoaMode { + // Computes the shortest representation of the given input. The returned + // result will be the most accurate number of this length. Longer + // representations might be more accurate. + FAST_DTOA_SHORTEST, + // Same as FAST_DTOA_SHORTEST but for single-precision floats. + FAST_DTOA_SHORTEST_SINGLE, + // Computes a representation where the precision (number of digits) is + // given as input. The precision is independent of the decimal point. + FAST_DTOA_PRECISION +}; + +// FastDtoa will produce at most kFastDtoaMaximalLength digits. This does not +// include the terminating '\0' character. +static const int kFastDtoaMaximalLength = 17; +// Same for single-precision numbers. +static const int kFastDtoaMaximalSingleLength = 9; + +// Provides a decimal representation of v. +// The result should be interpreted as buffer * 10^(point - length). +// +// Precondition: +// * v must be a strictly positive finite double. +// +// Returns true if it succeeds, otherwise the result can not be trusted. +// There will be *length digits inside the buffer followed by a null terminator. +// If the function returns true and mode equals +// - FAST_DTOA_SHORTEST, then +// the parameter requested_digits is ignored. +// The result satisfies +// v == (double) (buffer * 10^(point - length)). +// The digits in the buffer are the shortest representation possible. E.g. +// if 0.099999999999 and 0.1 represent the same double then "1" is returned +// with point = 0. +// The last digit will be closest to the actual v. That is, even if several +// digits might correctly yield 'v' when read again, the buffer will contain +// the one closest to v. +// - FAST_DTOA_PRECISION, then +// the buffer contains requested_digits digits. +// the difference v - (buffer * 10^(point-length)) is closest to zero for +// all possible representations of requested_digits digits. +// If there are two values that are equally close, then FastDtoa returns +// false. +// For both modes the buffer must be large enough to hold the result. +bool FastDtoa(double d, + FastDtoaMode mode, + int requested_digits, + Vector<char> buffer, + int* length, + int* decimal_point); + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_FAST_DTOA_H_ diff --git a/src/3rdparty/double-conversion/fixed-dtoa.cc b/src/3rdparty/double-conversion/fixed-dtoa.cc new file mode 100644 index 0000000000..aef65fdc21 --- /dev/null +++ b/src/3rdparty/double-conversion/fixed-dtoa.cc @@ -0,0 +1,404 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <math.h> + +#include "fixed-dtoa.h" +#include "ieee.h" + +namespace double_conversion { + +// Represents a 128bit type. This class should be replaced by a native type on +// platforms that support 128bit integers. +class UInt128 { + public: + UInt128() : high_bits_(0), low_bits_(0) { } + UInt128(uint64_t high, uint64_t low) : high_bits_(high), low_bits_(low) { } + + void Multiply(uint32_t multiplicand) { + uint64_t accumulator; + + accumulator = (low_bits_ & kMask32) * multiplicand; + uint32_t part = static_cast<uint32_t>(accumulator & kMask32); + accumulator >>= 32; + accumulator = accumulator + (low_bits_ >> 32) * multiplicand; + low_bits_ = (accumulator << 32) + part; + accumulator >>= 32; + accumulator = accumulator + (high_bits_ & kMask32) * multiplicand; + part = static_cast<uint32_t>(accumulator & kMask32); + accumulator >>= 32; + accumulator = accumulator + (high_bits_ >> 32) * multiplicand; + high_bits_ = (accumulator << 32) + part; + ASSERT((accumulator >> 32) == 0); + } + + void Shift(int shift_amount) { + ASSERT(-64 <= shift_amount && shift_amount <= 64); + if (shift_amount == 0) { + return; + } else if (shift_amount == -64) { + high_bits_ = low_bits_; + low_bits_ = 0; + } else if (shift_amount == 64) { + low_bits_ = high_bits_; + high_bits_ = 0; + } else if (shift_amount <= 0) { + high_bits_ <<= -shift_amount; + high_bits_ += low_bits_ >> (64 + shift_amount); + low_bits_ <<= -shift_amount; + } else { + low_bits_ >>= shift_amount; + low_bits_ += high_bits_ << (64 - shift_amount); + high_bits_ >>= shift_amount; + } + } + + // Modifies *this to *this MOD (2^power). + // Returns *this DIV (2^power). + int DivModPowerOf2(int power) { + if (power >= 64) { + int result = static_cast<int>(high_bits_ >> (power - 64)); + high_bits_ -= static_cast<uint64_t>(result) << (power - 64); + return result; + } else { + uint64_t part_low = low_bits_ >> power; + uint64_t part_high = high_bits_ << (64 - power); + int result = static_cast<int>(part_low + part_high); + high_bits_ = 0; + low_bits_ -= part_low << power; + return result; + } + } + + bool IsZero() const { + return high_bits_ == 0 && low_bits_ == 0; + } + + int BitAt(int position) { + if (position >= 64) { + return static_cast<int>(high_bits_ >> (position - 64)) & 1; + } else { + return static_cast<int>(low_bits_ >> position) & 1; + } + } + + private: + static const uint64_t kMask32 = 0xFFFFFFFF; + // Value == (high_bits_ << 64) + low_bits_ + uint64_t high_bits_; + uint64_t low_bits_; +}; + + +static const int kDoubleSignificandSize = 53; // Includes the hidden bit. + + +static void FillDigits32FixedLength(uint32_t number, int requested_length, + Vector<char> buffer, int* length) { + for (int i = requested_length - 1; i >= 0; --i) { + buffer[(*length) + i] = '0' + number % 10; + number /= 10; + } + *length += requested_length; +} + + +static void FillDigits32(uint32_t number, Vector<char> buffer, int* length) { + int number_length = 0; + // We fill the digits in reverse order and exchange them afterwards. + while (number != 0) { + int digit = number % 10; + number /= 10; + buffer[(*length) + number_length] = static_cast<char>('0' + digit); + number_length++; + } + // Exchange the digits. + int i = *length; + int j = *length + number_length - 1; + while (i < j) { + char tmp = buffer[i]; + buffer[i] = buffer[j]; + buffer[j] = tmp; + i++; + j--; + } + *length += number_length; +} + + +static void FillDigits64FixedLength(uint64_t number, + Vector<char> buffer, int* length) { + const uint32_t kTen7 = 10000000; + // For efficiency cut the number into 3 uint32_t parts, and print those. + uint32_t part2 = static_cast<uint32_t>(number % kTen7); + number /= kTen7; + uint32_t part1 = static_cast<uint32_t>(number % kTen7); + uint32_t part0 = static_cast<uint32_t>(number / kTen7); + + FillDigits32FixedLength(part0, 3, buffer, length); + FillDigits32FixedLength(part1, 7, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); +} + + +static void FillDigits64(uint64_t number, Vector<char> buffer, int* length) { + const uint32_t kTen7 = 10000000; + // For efficiency cut the number into 3 uint32_t parts, and print those. + uint32_t part2 = static_cast<uint32_t>(number % kTen7); + number /= kTen7; + uint32_t part1 = static_cast<uint32_t>(number % kTen7); + uint32_t part0 = static_cast<uint32_t>(number / kTen7); + + if (part0 != 0) { + FillDigits32(part0, buffer, length); + FillDigits32FixedLength(part1, 7, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); + } else if (part1 != 0) { + FillDigits32(part1, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); + } else { + FillDigits32(part2, buffer, length); + } +} + + +static void RoundUp(Vector<char> buffer, int* length, int* decimal_point) { + // An empty buffer represents 0. + if (*length == 0) { + buffer[0] = '1'; + *decimal_point = 1; + *length = 1; + return; + } + // Round the last digit until we either have a digit that was not '9' or until + // we reached the first digit. + buffer[(*length) - 1]++; + for (int i = (*length) - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) { + return; + } + buffer[i] = '0'; + buffer[i - 1]++; + } + // If the first digit is now '0' + 10, we would need to set it to '0' and add + // a '1' in front. However we reach the first digit only if all following + // digits had been '9' before rounding up. Now all trailing digits are '0' and + // we simply switch the first digit to '1' and update the decimal-point + // (indicating that the point is now one digit to the right). + if (buffer[0] == '0' + 10) { + buffer[0] = '1'; + (*decimal_point)++; + } +} + + +// The given fractionals number represents a fixed-point number with binary +// point at bit (-exponent). +// Preconditions: +// -128 <= exponent <= 0. +// 0 <= fractionals * 2^exponent < 1 +// The buffer holds the result. +// The function will round its result. During the rounding-process digits not +// generated by this function might be updated, and the decimal-point variable +// might be updated. If this function generates the digits 99 and the buffer +// already contained "199" (thus yielding a buffer of "19999") then a +// rounding-up will change the contents of the buffer to "20000". +static void FillFractionals(uint64_t fractionals, int exponent, + int fractional_count, Vector<char> buffer, + int* length, int* decimal_point) { + ASSERT(-128 <= exponent && exponent <= 0); + // 'fractionals' is a fixed-point number, with binary point at bit + // (-exponent). Inside the function the non-converted remainder of fractionals + // is a fixed-point number, with binary point at bit 'point'. + if (-exponent <= 64) { + // One 64 bit number is sufficient. + ASSERT(fractionals >> 56 == 0); + int point = -exponent; + for (int i = 0; i < fractional_count; ++i) { + if (fractionals == 0) break; + // Instead of multiplying by 10 we multiply by 5 and adjust the point + // location. This way the fractionals variable will not overflow. + // Invariant at the beginning of the loop: fractionals < 2^point. + // Initially we have: point <= 64 and fractionals < 2^56 + // After each iteration the point is decremented by one. + // Note that 5^3 = 125 < 128 = 2^7. + // Therefore three iterations of this loop will not overflow fractionals + // (even without the subtraction at the end of the loop body). At this + // time point will satisfy point <= 61 and therefore fractionals < 2^point + // and any further multiplication of fractionals by 5 will not overflow. + fractionals *= 5; + point--; + int digit = static_cast<int>(fractionals >> point); + ASSERT(digit <= 9); + buffer[*length] = static_cast<char>('0' + digit); + (*length)++; + fractionals -= static_cast<uint64_t>(digit) << point; + } + // If the first bit after the point is set we have to round up. + if (((fractionals >> (point - 1)) & 1) == 1) { + RoundUp(buffer, length, decimal_point); + } + } else { // We need 128 bits. + ASSERT(64 < -exponent && -exponent <= 128); + UInt128 fractionals128 = UInt128(fractionals, 0); + fractionals128.Shift(-exponent - 64); + int point = 128; + for (int i = 0; i < fractional_count; ++i) { + if (fractionals128.IsZero()) break; + // As before: instead of multiplying by 10 we multiply by 5 and adjust the + // point location. + // This multiplication will not overflow for the same reasons as before. + fractionals128.Multiply(5); + point--; + int digit = fractionals128.DivModPowerOf2(point); + ASSERT(digit <= 9); + buffer[*length] = static_cast<char>('0' + digit); + (*length)++; + } + if (fractionals128.BitAt(point - 1) == 1) { + RoundUp(buffer, length, decimal_point); + } + } +} + + +// Removes leading and trailing zeros. +// If leading zeros are removed then the decimal point position is adjusted. +static void TrimZeros(Vector<char> buffer, int* length, int* decimal_point) { + while (*length > 0 && buffer[(*length) - 1] == '0') { + (*length)--; + } + int first_non_zero = 0; + while (first_non_zero < *length && buffer[first_non_zero] == '0') { + first_non_zero++; + } + if (first_non_zero != 0) { + for (int i = first_non_zero; i < *length; ++i) { + buffer[i - first_non_zero] = buffer[i]; + } + *length -= first_non_zero; + *decimal_point -= first_non_zero; + } +} + + +bool FastFixedDtoa(double v, + int fractional_count, + Vector<char> buffer, + int* length, + int* decimal_point) { + const uint32_t kMaxUInt32 = 0xFFFFFFFF; + uint64_t significand = Double(v).Significand(); + int exponent = Double(v).Exponent(); + // v = significand * 2^exponent (with significand a 53bit integer). + // If the exponent is larger than 20 (i.e. we may have a 73bit number) then we + // don't know how to compute the representation. 2^73 ~= 9.5*10^21. + // If necessary this limit could probably be increased, but we don't need + // more. + if (exponent > 20) return false; + if (fractional_count > 20) return false; + *length = 0; + // At most kDoubleSignificandSize bits of the significand are non-zero. + // Given a 64 bit integer we have 11 0s followed by 53 potentially non-zero + // bits: 0..11*..0xxx..53*..xx + if (exponent + kDoubleSignificandSize > 64) { + // The exponent must be > 11. + // + // We know that v = significand * 2^exponent. + // And the exponent > 11. + // We simplify the task by dividing v by 10^17. + // The quotient delivers the first digits, and the remainder fits into a 64 + // bit number. + // Dividing by 10^17 is equivalent to dividing by 5^17*2^17. + const uint64_t kFive17 = UINT64_2PART_C(0xB1, A2BC2EC5); // 5^17 + uint64_t divisor = kFive17; + int divisor_power = 17; + uint64_t dividend = significand; + uint32_t quotient; + uint64_t remainder; + // Let v = f * 2^e with f == significand and e == exponent. + // Then need q (quotient) and r (remainder) as follows: + // v = q * 10^17 + r + // f * 2^e = q * 10^17 + r + // f * 2^e = q * 5^17 * 2^17 + r + // If e > 17 then + // f * 2^(e-17) = q * 5^17 + r/2^17 + // else + // f = q * 5^17 * 2^(17-e) + r/2^e + if (exponent > divisor_power) { + // We only allow exponents of up to 20 and therefore (17 - e) <= 3 + dividend <<= exponent - divisor_power; + quotient = static_cast<uint32_t>(dividend / divisor); + remainder = (dividend % divisor) << divisor_power; + } else { + divisor <<= divisor_power - exponent; + quotient = static_cast<uint32_t>(dividend / divisor); + remainder = (dividend % divisor) << exponent; + } + FillDigits32(quotient, buffer, length); + FillDigits64FixedLength(remainder, buffer, length); + *decimal_point = *length; + } else if (exponent >= 0) { + // 0 <= exponent <= 11 + significand <<= exponent; + FillDigits64(significand, buffer, length); + *decimal_point = *length; + } else if (exponent > -kDoubleSignificandSize) { + // We have to cut the number. + uint64_t integrals = significand >> -exponent; + uint64_t fractionals = significand - (integrals << -exponent); + if (integrals > kMaxUInt32) { + FillDigits64(integrals, buffer, length); + } else { + FillDigits32(static_cast<uint32_t>(integrals), buffer, length); + } + *decimal_point = *length; + FillFractionals(fractionals, exponent, fractional_count, + buffer, length, decimal_point); + } else if (exponent < -128) { + // This configuration (with at most 20 digits) means that all digits must be + // 0. + ASSERT(fractional_count <= 20); + buffer[0] = '\0'; + *length = 0; + *decimal_point = -fractional_count; + } else { + *decimal_point = 0; + FillFractionals(significand, exponent, fractional_count, + buffer, length, decimal_point); + } + TrimZeros(buffer, length, decimal_point); + buffer[*length] = '\0'; + if ((*length) == 0) { + // The string is empty and the decimal_point thus has no importance. Mimick + // Gay's dtoa and and set it to -fractional_count. + *decimal_point = -fractional_count; + } + return true; +} + +} // namespace double_conversion diff --git a/src/3rdparty/double-conversion/fixed-dtoa.h b/src/3rdparty/double-conversion/fixed-dtoa.h new file mode 100644 index 0000000000..3bdd08e21f --- /dev/null +++ b/src/3rdparty/double-conversion/fixed-dtoa.h @@ -0,0 +1,56 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_FIXED_DTOA_H_ +#define DOUBLE_CONVERSION_FIXED_DTOA_H_ + +#include "utils.h" + +namespace double_conversion { + +// Produces digits necessary to print a given number with +// 'fractional_count' digits after the decimal point. +// The buffer must be big enough to hold the result plus one terminating null +// character. +// +// The produced digits might be too short in which case the caller has to fill +// the gaps with '0's. +// Example: FastFixedDtoa(0.001, 5, ...) is allowed to return buffer = "1", and +// decimal_point = -2. +// Halfway cases are rounded towards +/-Infinity (away from 0). The call +// FastFixedDtoa(0.15, 2, ...) thus returns buffer = "2", decimal_point = 0. +// The returned buffer may contain digits that would be truncated from the +// shortest representation of the input. +// +// This method only works for some parameters. If it can't handle the input it +// returns false. The output is null-terminated when the function succeeds. +bool FastFixedDtoa(double v, int fractional_count, + Vector<char> buffer, int* length, int* decimal_point); + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_FIXED_DTOA_H_ diff --git a/src/3rdparty/double-conversion/ieee.h b/src/3rdparty/double-conversion/ieee.h new file mode 100644 index 0000000000..661141d1a8 --- /dev/null +++ b/src/3rdparty/double-conversion/ieee.h @@ -0,0 +1,402 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_DOUBLE_H_ +#define DOUBLE_CONVERSION_DOUBLE_H_ + +#include "diy-fp.h" + +namespace double_conversion { + +// We assume that doubles and uint64_t have the same endianness. +static uint64_t double_to_uint64(double d) { return BitCast<uint64_t>(d); } +static double uint64_to_double(uint64_t d64) { return BitCast<double>(d64); } +static uint32_t float_to_uint32(float f) { return BitCast<uint32_t>(f); } +static float uint32_to_float(uint32_t d32) { return BitCast<float>(d32); } + +// Helper functions for doubles. +class Double { + public: + static const uint64_t kSignMask = UINT64_2PART_C(0x80000000, 00000000); + static const uint64_t kExponentMask = UINT64_2PART_C(0x7FF00000, 00000000); + static const uint64_t kSignificandMask = UINT64_2PART_C(0x000FFFFF, FFFFFFFF); + static const uint64_t kHiddenBit = UINT64_2PART_C(0x00100000, 00000000); + static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit. + static const int kSignificandSize = 53; + + Double() : d64_(0) {} + explicit Double(double d) : d64_(double_to_uint64(d)) {} + explicit Double(uint64_t d64) : d64_(d64) {} + explicit Double(DiyFp diy_fp) + : d64_(DiyFpToUint64(diy_fp)) {} + + // The value encoded by this Double must be greater or equal to +0.0. + // It must not be special (infinity, or NaN). + DiyFp AsDiyFp() const { + ASSERT(Sign() > 0); + ASSERT(!IsSpecial()); + return DiyFp(Significand(), Exponent()); + } + + // The value encoded by this Double must be strictly greater than 0. + DiyFp AsNormalizedDiyFp() const { + ASSERT(value() > 0.0); + uint64_t f = Significand(); + int e = Exponent(); + + // The current double could be a denormal. + while ((f & kHiddenBit) == 0) { + f <<= 1; + e--; + } + // Do the final shifts in one go. + f <<= DiyFp::kSignificandSize - kSignificandSize; + e -= DiyFp::kSignificandSize - kSignificandSize; + return DiyFp(f, e); + } + + // Returns the double's bit as uint64. + uint64_t AsUint64() const { + return d64_; + } + + // Returns the next greater double. Returns +infinity on input +infinity. + double NextDouble() const { + if (d64_ == kInfinity) return Double(kInfinity).value(); + if (Sign() < 0 && Significand() == 0) { + // -0.0 + return 0.0; + } + if (Sign() < 0) { + return Double(d64_ - 1).value(); + } else { + return Double(d64_ + 1).value(); + } + } + + double PreviousDouble() const { + if (d64_ == (kInfinity | kSignMask)) return -Double::Infinity(); + if (Sign() < 0) { + return Double(d64_ + 1).value(); + } else { + if (Significand() == 0) return -0.0; + return Double(d64_ - 1).value(); + } + } + + int Exponent() const { + if (IsDenormal()) return kDenormalExponent; + + uint64_t d64 = AsUint64(); + int biased_e = + static_cast<int>((d64 & kExponentMask) >> kPhysicalSignificandSize); + return biased_e - kExponentBias; + } + + uint64_t Significand() const { + uint64_t d64 = AsUint64(); + uint64_t significand = d64 & kSignificandMask; + if (!IsDenormal()) { + return significand + kHiddenBit; + } else { + return significand; + } + } + + // Returns true if the double is a denormal. + bool IsDenormal() const { + uint64_t d64 = AsUint64(); + return (d64 & kExponentMask) == 0; + } + + // We consider denormals not to be special. + // Hence only Infinity and NaN are special. + bool IsSpecial() const { + uint64_t d64 = AsUint64(); + return (d64 & kExponentMask) == kExponentMask; + } + + bool IsNan() const { + uint64_t d64 = AsUint64(); + return ((d64 & kExponentMask) == kExponentMask) && + ((d64 & kSignificandMask) != 0); + } + + bool IsInfinite() const { + uint64_t d64 = AsUint64(); + return ((d64 & kExponentMask) == kExponentMask) && + ((d64 & kSignificandMask) == 0); + } + + int Sign() const { + uint64_t d64 = AsUint64(); + return (d64 & kSignMask) == 0? 1: -1; + } + + // Precondition: the value encoded by this Double must be greater or equal + // than +0.0. + DiyFp UpperBoundary() const { + ASSERT(Sign() > 0); + return DiyFp(Significand() * 2 + 1, Exponent() - 1); + } + + // Computes the two boundaries of this. + // The bigger boundary (m_plus) is normalized. The lower boundary has the same + // exponent as m_plus. + // Precondition: the value encoded by this Double must be greater than 0. + void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { + ASSERT(value() > 0.0); + DiyFp v = this->AsDiyFp(); + DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); + DiyFp m_minus; + if (LowerBoundaryIsCloser()) { + m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2); + } else { + m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1); + } + m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e())); + m_minus.set_e(m_plus.e()); + *out_m_plus = m_plus; + *out_m_minus = m_minus; + } + + bool LowerBoundaryIsCloser() const { + // The boundary is closer if the significand is of the form f == 2^p-1 then + // the lower boundary is closer. + // Think of v = 1000e10 and v- = 9999e9. + // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but + // at a distance of 1e8. + // The only exception is for the smallest normal: the largest denormal is + // at the same distance as its successor. + // Note: denormals have the same exponent as the smallest normals. + bool physical_significand_is_zero = ((AsUint64() & kSignificandMask) == 0); + return physical_significand_is_zero && (Exponent() != kDenormalExponent); + } + + double value() const { return uint64_to_double(d64_); } + + // Returns the significand size for a given order of magnitude. + // If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude. + // This function returns the number of significant binary digits v will have + // once it's encoded into a double. In almost all cases this is equal to + // kSignificandSize. The only exceptions are denormals. They start with + // leading zeroes and their effective significand-size is hence smaller. + static int SignificandSizeForOrderOfMagnitude(int order) { + if (order >= (kDenormalExponent + kSignificandSize)) { + return kSignificandSize; + } + if (order <= kDenormalExponent) return 0; + return order - kDenormalExponent; + } + + static double Infinity() { + return Double(kInfinity).value(); + } + + static double NaN() { + return Double(kNaN).value(); + } + + private: + static const int kExponentBias = 0x3FF + kPhysicalSignificandSize; + static const int kDenormalExponent = -kExponentBias + 1; + static const int kMaxExponent = 0x7FF - kExponentBias; + static const uint64_t kInfinity = UINT64_2PART_C(0x7FF00000, 00000000); + static const uint64_t kNaN = UINT64_2PART_C(0x7FF80000, 00000000); + + const uint64_t d64_; + + static uint64_t DiyFpToUint64(DiyFp diy_fp) { + uint64_t significand = diy_fp.f(); + int exponent = diy_fp.e(); + while (significand > kHiddenBit + kSignificandMask) { + significand >>= 1; + exponent++; + } + if (exponent >= kMaxExponent) { + return kInfinity; + } + if (exponent < kDenormalExponent) { + return 0; + } + while (exponent > kDenormalExponent && (significand & kHiddenBit) == 0) { + significand <<= 1; + exponent--; + } + uint64_t biased_exponent; + if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0) { + biased_exponent = 0; + } else { + biased_exponent = static_cast<uint64_t>(exponent + kExponentBias); + } + return (significand & kSignificandMask) | + (biased_exponent << kPhysicalSignificandSize); + } + + DISALLOW_COPY_AND_ASSIGN(Double); +}; + +class Single { + public: + static const uint32_t kSignMask = 0x80000000; + static const uint32_t kExponentMask = 0x7F800000; + static const uint32_t kSignificandMask = 0x007FFFFF; + static const uint32_t kHiddenBit = 0x00800000; + static const int kPhysicalSignificandSize = 23; // Excludes the hidden bit. + static const int kSignificandSize = 24; + + Single() : d32_(0) {} + explicit Single(float f) : d32_(float_to_uint32(f)) {} + explicit Single(uint32_t d32) : d32_(d32) {} + + // The value encoded by this Single must be greater or equal to +0.0. + // It must not be special (infinity, or NaN). + DiyFp AsDiyFp() const { + ASSERT(Sign() > 0); + ASSERT(!IsSpecial()); + return DiyFp(Significand(), Exponent()); + } + + // Returns the single's bit as uint64. + uint32_t AsUint32() const { + return d32_; + } + + int Exponent() const { + if (IsDenormal()) return kDenormalExponent; + + uint32_t d32 = AsUint32(); + int biased_e = + static_cast<int>((d32 & kExponentMask) >> kPhysicalSignificandSize); + return biased_e - kExponentBias; + } + + uint32_t Significand() const { + uint32_t d32 = AsUint32(); + uint32_t significand = d32 & kSignificandMask; + if (!IsDenormal()) { + return significand + kHiddenBit; + } else { + return significand; + } + } + + // Returns true if the single is a denormal. + bool IsDenormal() const { + uint32_t d32 = AsUint32(); + return (d32 & kExponentMask) == 0; + } + + // We consider denormals not to be special. + // Hence only Infinity and NaN are special. + bool IsSpecial() const { + uint32_t d32 = AsUint32(); + return (d32 & kExponentMask) == kExponentMask; + } + + bool IsNan() const { + uint32_t d32 = AsUint32(); + return ((d32 & kExponentMask) == kExponentMask) && + ((d32 & kSignificandMask) != 0); + } + + bool IsInfinite() const { + uint32_t d32 = AsUint32(); + return ((d32 & kExponentMask) == kExponentMask) && + ((d32 & kSignificandMask) == 0); + } + + int Sign() const { + uint32_t d32 = AsUint32(); + return (d32 & kSignMask) == 0? 1: -1; + } + + // Computes the two boundaries of this. + // The bigger boundary (m_plus) is normalized. The lower boundary has the same + // exponent as m_plus. + // Precondition: the value encoded by this Single must be greater than 0. + void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { + ASSERT(value() > 0.0); + DiyFp v = this->AsDiyFp(); + DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); + DiyFp m_minus; + if (LowerBoundaryIsCloser()) { + m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2); + } else { + m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1); + } + m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e())); + m_minus.set_e(m_plus.e()); + *out_m_plus = m_plus; + *out_m_minus = m_minus; + } + + // Precondition: the value encoded by this Single must be greater or equal + // than +0.0. + DiyFp UpperBoundary() const { + ASSERT(Sign() > 0); + return DiyFp(Significand() * 2 + 1, Exponent() - 1); + } + + bool LowerBoundaryIsCloser() const { + // The boundary is closer if the significand is of the form f == 2^p-1 then + // the lower boundary is closer. + // Think of v = 1000e10 and v- = 9999e9. + // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but + // at a distance of 1e8. + // The only exception is for the smallest normal: the largest denormal is + // at the same distance as its successor. + // Note: denormals have the same exponent as the smallest normals. + bool physical_significand_is_zero = ((AsUint32() & kSignificandMask) == 0); + return physical_significand_is_zero && (Exponent() != kDenormalExponent); + } + + float value() const { return uint32_to_float(d32_); } + + static float Infinity() { + return Single(kInfinity).value(); + } + + static float NaN() { + return Single(kNaN).value(); + } + + private: + static const int kExponentBias = 0x7F + kPhysicalSignificandSize; + static const int kDenormalExponent = -kExponentBias + 1; + static const int kMaxExponent = 0xFF - kExponentBias; + static const uint32_t kInfinity = 0x7F800000; + static const uint32_t kNaN = 0x7FC00000; + + const uint32_t d32_; + + DISALLOW_COPY_AND_ASSIGN(Single); +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_DOUBLE_H_ diff --git a/src/3rdparty/double-conversion/include/double-conversion/double-conversion.h b/src/3rdparty/double-conversion/include/double-conversion/double-conversion.h new file mode 100644 index 0000000000..6bdfa8d25d --- /dev/null +++ b/src/3rdparty/double-conversion/include/double-conversion/double-conversion.h @@ -0,0 +1,543 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ +#define DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ + +#include "utils.h" + +namespace double_conversion { + +class DoubleToStringConverter { + public: + // When calling ToFixed with a double > 10^kMaxFixedDigitsBeforePoint + // or a requested_digits parameter > kMaxFixedDigitsAfterPoint then the + // function returns false. + static const int kMaxFixedDigitsBeforePoint = 60; + static const int kMaxFixedDigitsAfterPoint = 60; + + // When calling ToExponential with a requested_digits + // parameter > kMaxExponentialDigits then the function returns false. + static const int kMaxExponentialDigits = 120; + + // When calling ToPrecision with a requested_digits + // parameter < kMinPrecisionDigits or requested_digits > kMaxPrecisionDigits + // then the function returns false. + static const int kMinPrecisionDigits = 1; + static const int kMaxPrecisionDigits = 120; + + enum Flags { + NO_FLAGS = 0, + EMIT_POSITIVE_EXPONENT_SIGN = 1, + EMIT_TRAILING_DECIMAL_POINT = 2, + EMIT_TRAILING_ZERO_AFTER_POINT = 4, + UNIQUE_ZERO = 8 + }; + + // Flags should be a bit-or combination of the possible Flags-enum. + // - NO_FLAGS: no special flags. + // - EMIT_POSITIVE_EXPONENT_SIGN: when the number is converted into exponent + // form, emits a '+' for positive exponents. Example: 1.2e+2. + // - EMIT_TRAILING_DECIMAL_POINT: when the input number is an integer and is + // converted into decimal format then a trailing decimal point is appended. + // Example: 2345.0 is converted to "2345.". + // - EMIT_TRAILING_ZERO_AFTER_POINT: in addition to a trailing decimal point + // emits a trailing '0'-character. This flag requires the + // EXMIT_TRAILING_DECIMAL_POINT flag. + // Example: 2345.0 is converted to "2345.0". + // - UNIQUE_ZERO: "-0.0" is converted to "0.0". + // + // Infinity symbol and nan_symbol provide the string representation for these + // special values. If the string is NULL and the special value is encountered + // then the conversion functions return false. + // + // The exponent_character is used in exponential representations. It is + // usually 'e' or 'E'. + // + // When converting to the shortest representation the converter will + // represent input numbers in decimal format if they are in the interval + // [10^decimal_in_shortest_low; 10^decimal_in_shortest_high[ + // (lower boundary included, greater boundary excluded). + // Example: with decimal_in_shortest_low = -6 and + // decimal_in_shortest_high = 21: + // ToShortest(0.000001) -> "0.000001" + // ToShortest(0.0000001) -> "1e-7" + // ToShortest(111111111111111111111.0) -> "111111111111111110000" + // ToShortest(100000000000000000000.0) -> "100000000000000000000" + // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" + // + // When converting to precision mode the converter may add + // max_leading_padding_zeroes before returning the number in exponential + // format. + // Example with max_leading_padding_zeroes_in_precision_mode = 6. + // ToPrecision(0.0000012345, 2) -> "0.0000012" + // ToPrecision(0.00000012345, 2) -> "1.2e-7" + // Similarily the converter may add up to + // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid + // returning an exponential representation. A zero added by the + // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: + // ToPrecision(230.0, 2) -> "230" + // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. + // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. + DoubleToStringConverter(int flags, + const char* infinity_symbol, + const char* nan_symbol, + char exponent_character, + int decimal_in_shortest_low, + int decimal_in_shortest_high, + int max_leading_padding_zeroes_in_precision_mode, + int max_trailing_padding_zeroes_in_precision_mode) + : flags_(flags), + infinity_symbol_(infinity_symbol), + nan_symbol_(nan_symbol), + exponent_character_(exponent_character), + decimal_in_shortest_low_(decimal_in_shortest_low), + decimal_in_shortest_high_(decimal_in_shortest_high), + max_leading_padding_zeroes_in_precision_mode_( + max_leading_padding_zeroes_in_precision_mode), + max_trailing_padding_zeroes_in_precision_mode_( + max_trailing_padding_zeroes_in_precision_mode) { + // When 'trailing zero after the point' is set, then 'trailing point' + // must be set too. + ASSERT(((flags & EMIT_TRAILING_DECIMAL_POINT) != 0) || + !((flags & EMIT_TRAILING_ZERO_AFTER_POINT) != 0)); + } + + // Returns a converter following the EcmaScript specification. + static const DoubleToStringConverter& EcmaScriptConverter(); + + // Computes the shortest string of digits that correctly represent the input + // number. Depending on decimal_in_shortest_low and decimal_in_shortest_high + // (see constructor) it then either returns a decimal representation, or an + // exponential representation. + // Example with decimal_in_shortest_low = -6, + // decimal_in_shortest_high = 21, + // EMIT_POSITIVE_EXPONENT_SIGN activated, and + // EMIT_TRAILING_DECIMAL_POINT deactived: + // ToShortest(0.000001) -> "0.000001" + // ToShortest(0.0000001) -> "1e-7" + // ToShortest(111111111111111111111.0) -> "111111111111111110000" + // ToShortest(100000000000000000000.0) -> "100000000000000000000" + // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" + // + // Note: the conversion may round the output if the returned string + // is accurate enough to uniquely identify the input-number. + // For example the most precise representation of the double 9e59 equals + // "899999999999999918767229449717619953810131273674690656206848", but + // the converter will return the shorter (but still correct) "9e59". + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except when the input value is special and no infinity_symbol or + // nan_symbol has been given to the constructor. + bool ToShortest(double value, StringBuilder* result_builder) const { + return ToShortestIeeeNumber(value, result_builder, SHORTEST); + } + + // Same as ToShortest, but for single-precision floats. + bool ToShortestSingle(float value, StringBuilder* result_builder) const { + return ToShortestIeeeNumber(value, result_builder, SHORTEST_SINGLE); + } + + + // Computes a decimal representation with a fixed number of digits after the + // decimal point. The last emitted digit is rounded. + // + // Examples: + // ToFixed(3.12, 1) -> "3.1" + // ToFixed(3.1415, 3) -> "3.142" + // ToFixed(1234.56789, 4) -> "1234.5679" + // ToFixed(1.23, 5) -> "1.23000" + // ToFixed(0.1, 4) -> "0.1000" + // ToFixed(1e30, 2) -> "1000000000000000019884624838656.00" + // ToFixed(0.1, 30) -> "0.100000000000000005551115123126" + // ToFixed(0.1, 17) -> "0.10000000000000001" + // + // If requested_digits equals 0, then the tail of the result depends on + // the EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT. + // Examples, for requested_digits == 0, + // let EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT be + // - false and false: then 123.45 -> 123 + // 0.678 -> 1 + // - true and false: then 123.45 -> 123. + // 0.678 -> 1. + // - true and true: then 123.45 -> 123.0 + // 0.678 -> 1.0 + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - 'value' > 10^kMaxFixedDigitsBeforePoint, or + // - 'requested_digits' > kMaxFixedDigitsAfterPoint. + // The last two conditions imply that the result will never contain more than + // 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint characters + // (one additional character for the sign, and one for the decimal point). + bool ToFixed(double value, + int requested_digits, + StringBuilder* result_builder) const; + + // Computes a representation in exponential format with requested_digits + // after the decimal point. The last emitted digit is rounded. + // If requested_digits equals -1, then the shortest exponential representation + // is computed. + // + // Examples with EMIT_POSITIVE_EXPONENT_SIGN deactivated, and + // exponent_character set to 'e'. + // ToExponential(3.12, 1) -> "3.1e0" + // ToExponential(5.0, 3) -> "5.000e0" + // ToExponential(0.001, 2) -> "1.00e-3" + // ToExponential(3.1415, -1) -> "3.1415e0" + // ToExponential(3.1415, 4) -> "3.1415e0" + // ToExponential(3.1415, 3) -> "3.142e0" + // ToExponential(123456789000000, 3) -> "1.235e14" + // ToExponential(1000000000000000019884624838656.0, -1) -> "1e30" + // ToExponential(1000000000000000019884624838656.0, 32) -> + // "1.00000000000000001988462483865600e30" + // ToExponential(1234, 0) -> "1e3" + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - 'requested_digits' > kMaxExponentialDigits. + // The last condition implies that the result will never contain more than + // kMaxExponentialDigits + 8 characters (the sign, the digit before the + // decimal point, the decimal point, the exponent character, the + // exponent's sign, and at most 3 exponent digits). + bool ToExponential(double value, + int requested_digits, + StringBuilder* result_builder) const; + + // Computes 'precision' leading digits of the given 'value' and returns them + // either in exponential or decimal format, depending on + // max_{leading|trailing}_padding_zeroes_in_precision_mode (given to the + // constructor). + // The last computed digit is rounded. + // + // Example with max_leading_padding_zeroes_in_precision_mode = 6. + // ToPrecision(0.0000012345, 2) -> "0.0000012" + // ToPrecision(0.00000012345, 2) -> "1.2e-7" + // Similarily the converter may add up to + // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid + // returning an exponential representation. A zero added by the + // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: + // ToPrecision(230.0, 2) -> "230" + // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. + // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 3, and no + // EMIT_TRAILING_ZERO_AFTER_POINT: + // ToPrecision(123450.0, 6) -> "123450" + // ToPrecision(123450.0, 5) -> "123450" + // ToPrecision(123450.0, 4) -> "123500" + // ToPrecision(123450.0, 3) -> "123000" + // ToPrecision(123450.0, 2) -> "1.2e5" + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - precision < kMinPericisionDigits + // - precision > kMaxPrecisionDigits + // The last condition implies that the result will never contain more than + // kMaxPrecisionDigits + 7 characters (the sign, the decimal point, the + // exponent character, the exponent's sign, and at most 3 exponent digits). + bool ToPrecision(double value, + int precision, + StringBuilder* result_builder) const; + + enum DtoaMode { + // Produce the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate + // but correct) 0.3. + SHORTEST, + // Same as SHORTEST, but for single-precision floats. + SHORTEST_SINGLE, + // Produce a fixed number of digits after the decimal point. + // For instance fixed(0.1, 4) becomes 0.1000 + // If the input number is big, the output will be big. + FIXED, + // Fixed number of digits (independent of the decimal point). + PRECISION + }; + + // The maximal number of digits that are needed to emit a double in base 10. + // A higher precision can be achieved by using more digits, but the shortest + // accurate representation of any double will never use more digits than + // kBase10MaximalLength. + // Note that DoubleToAscii null-terminates its input. So the given buffer + // should be at least kBase10MaximalLength + 1 characters long. + static const int kBase10MaximalLength = 17; + + // Converts the given double 'v' to ascii. 'v' must not be NaN, +Infinity, or + // -Infinity. In SHORTEST_SINGLE-mode this restriction also applies to 'v' + // after it has been casted to a single-precision float. That is, in this + // mode static_cast<float>(v) must not be NaN, +Infinity or -Infinity. + // + // The result should be interpreted as buffer * 10^(point-length). + // + // The output depends on the given mode: + // - SHORTEST: produce the least amount of digits for which the internal + // identity requirement is still satisfied. If the digits are printed + // (together with the correct exponent) then reading this number will give + // 'v' again. The buffer will choose the representation that is closest to + // 'v'. If there are two at the same distance, than the one farther away + // from 0 is chosen (halfway cases - ending with 5 - are rounded up). + // In this mode the 'requested_digits' parameter is ignored. + // - SHORTEST_SINGLE: same as SHORTEST but with single-precision. + // - FIXED: produces digits necessary to print a given number with + // 'requested_digits' digits after the decimal point. The produced digits + // might be too short in which case the caller has to fill the remainder + // with '0's. + // Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. + // Halfway cases are rounded towards +/-Infinity (away from 0). The call + // toFixed(0.15, 2) thus returns buffer="2", point=0. + // The returned buffer may contain digits that would be truncated from the + // shortest representation of the input. + // - PRECISION: produces 'requested_digits' where the first digit is not '0'. + // Even though the length of produced digits usually equals + // 'requested_digits', the function is allowed to return fewer digits, in + // which case the caller has to fill the missing digits with '0's. + // Halfway cases are again rounded away from 0. + // DoubleToAscii expects the given buffer to be big enough to hold all + // digits and a terminating null-character. In SHORTEST-mode it expects a + // buffer of at least kBase10MaximalLength + 1. In all other modes the + // requested_digits parameter and the padding-zeroes limit the size of the + // output. Don't forget the decimal point, the exponent character and the + // terminating null-character when computing the maximal output size. + // The given length is only used in debug mode to ensure the buffer is big + // enough. + static void DoubleToAscii(double v, + DtoaMode mode, + int requested_digits, + char* buffer, + int buffer_length, + bool* sign, + int* length, + int* point); + + private: + // Implementation for ToShortest and ToShortestSingle. + bool ToShortestIeeeNumber(double value, + StringBuilder* result_builder, + DtoaMode mode) const; + + // If the value is a special value (NaN or Infinity) constructs the + // corresponding string using the configured infinity/nan-symbol. + // If either of them is NULL or the value is not special then the + // function returns false. + bool HandleSpecialValues(double value, StringBuilder* result_builder) const; + // Constructs an exponential representation (i.e. 1.234e56). + // The given exponent assumes a decimal point after the first decimal digit. + void CreateExponentialRepresentation(const char* decimal_digits, + int length, + int exponent, + StringBuilder* result_builder) const; + // Creates a decimal representation (i.e 1234.5678). + void CreateDecimalRepresentation(const char* decimal_digits, + int length, + int decimal_point, + int digits_after_point, + StringBuilder* result_builder) const; + + const int flags_; + const char* const infinity_symbol_; + const char* const nan_symbol_; + const char exponent_character_; + const int decimal_in_shortest_low_; + const int decimal_in_shortest_high_; + const int max_leading_padding_zeroes_in_precision_mode_; + const int max_trailing_padding_zeroes_in_precision_mode_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter); +}; + + +class StringToDoubleConverter { + public: + // Enumeration for allowing octals and ignoring junk when converting + // strings to numbers. + enum Flags { + NO_FLAGS = 0, + ALLOW_HEX = 1, + ALLOW_OCTALS = 2, + ALLOW_TRAILING_JUNK = 4, + ALLOW_LEADING_SPACES = 8, + ALLOW_TRAILING_SPACES = 16, + ALLOW_SPACES_AFTER_SIGN = 32 + }; + + // Flags should be a bit-or combination of the possible Flags-enum. + // - NO_FLAGS: no special flags. + // - ALLOW_HEX: recognizes the prefix "0x". Hex numbers may only be integers. + // Ex: StringToDouble("0x1234") -> 4660.0 + // In StringToDouble("0x1234.56") the characters ".56" are trailing + // junk. The result of the call is hence dependent on + // the ALLOW_TRAILING_JUNK flag and/or the junk value. + // With this flag "0x" is a junk-string. Even with ALLOW_TRAILING_JUNK, + // the string will not be parsed as "0" followed by junk. + // + // - ALLOW_OCTALS: recognizes the prefix "0" for octals: + // If a sequence of octal digits starts with '0', then the number is + // read as octal integer. Octal numbers may only be integers. + // Ex: StringToDouble("01234") -> 668.0 + // StringToDouble("012349") -> 12349.0 // Not a sequence of octal + // // digits. + // In StringToDouble("01234.56") the characters ".56" are trailing + // junk. The result of the call is hence dependent on + // the ALLOW_TRAILING_JUNK flag and/or the junk value. + // In StringToDouble("01234e56") the characters "e56" are trailing + // junk, too. + // - ALLOW_TRAILING_JUNK: ignore trailing characters that are not part of + // a double literal. + // - ALLOW_LEADING_SPACES: skip over leading whitespace, including spaces, + // new-lines, and tabs. + // - ALLOW_TRAILING_SPACES: ignore trailing whitespace. + // - ALLOW_SPACES_AFTER_SIGN: ignore whitespace after the sign. + // Ex: StringToDouble("- 123.2") -> -123.2. + // StringToDouble("+ 123.2") -> 123.2 + // + // empty_string_value is returned when an empty string is given as input. + // If ALLOW_LEADING_SPACES or ALLOW_TRAILING_SPACES are set, then a string + // containing only spaces is converted to the 'empty_string_value', too. + // + // junk_string_value is returned when + // a) ALLOW_TRAILING_JUNK is not set, and a junk character (a character not + // part of a double-literal) is found. + // b) ALLOW_TRAILING_JUNK is set, but the string does not start with a + // double literal. + // + // infinity_symbol and nan_symbol are strings that are used to detect + // inputs that represent infinity and NaN. They can be null, in which case + // they are ignored. + // The conversion routine first reads any possible signs. Then it compares the + // following character of the input-string with the first character of + // the infinity, and nan-symbol. If either matches, the function assumes, that + // a match has been found, and expects the following input characters to match + // the remaining characters of the special-value symbol. + // This means that the following restrictions apply to special-value symbols: + // - they must not start with signs ('+', or '-'), + // - they must not have the same first character. + // - they must not start with digits. + // + // Examples: + // flags = ALLOW_HEX | ALLOW_TRAILING_JUNK, + // empty_string_value = 0.0, + // junk_string_value = NaN, + // infinity_symbol = "infinity", + // nan_symbol = "nan": + // StringToDouble("0x1234") -> 4660.0. + // StringToDouble("0x1234K") -> 4660.0. + // StringToDouble("") -> 0.0 // empty_string_value. + // StringToDouble(" ") -> NaN // junk_string_value. + // StringToDouble(" 1") -> NaN // junk_string_value. + // StringToDouble("0x") -> NaN // junk_string_value. + // StringToDouble("-123.45") -> -123.45. + // StringToDouble("--123.45") -> NaN // junk_string_value. + // StringToDouble("123e45") -> 123e45. + // StringToDouble("123E45") -> 123e45. + // StringToDouble("123e+45") -> 123e45. + // StringToDouble("123E-45") -> 123e-45. + // StringToDouble("123e") -> 123.0 // trailing junk ignored. + // StringToDouble("123e-") -> 123.0 // trailing junk ignored. + // StringToDouble("+NaN") -> NaN // NaN string literal. + // StringToDouble("-infinity") -> -inf. // infinity literal. + // StringToDouble("Infinity") -> NaN // junk_string_value. + // + // flags = ALLOW_OCTAL | ALLOW_LEADING_SPACES, + // empty_string_value = 0.0, + // junk_string_value = NaN, + // infinity_symbol = NULL, + // nan_symbol = NULL: + // StringToDouble("0x1234") -> NaN // junk_string_value. + // StringToDouble("01234") -> 668.0. + // StringToDouble("") -> 0.0 // empty_string_value. + // StringToDouble(" ") -> 0.0 // empty_string_value. + // StringToDouble(" 1") -> 1.0 + // StringToDouble("0x") -> NaN // junk_string_value. + // StringToDouble("0123e45") -> NaN // junk_string_value. + // StringToDouble("01239E45") -> 1239e45. + // StringToDouble("-infinity") -> NaN // junk_string_value. + // StringToDouble("NaN") -> NaN // junk_string_value. + StringToDoubleConverter(int flags, + double empty_string_value, + double junk_string_value, + const char* infinity_symbol, + const char* nan_symbol) + : flags_(flags), + empty_string_value_(empty_string_value), + junk_string_value_(junk_string_value), + infinity_symbol_(infinity_symbol), + nan_symbol_(nan_symbol) { + } + + // Performs the conversion. + // The output parameter 'processed_characters_count' is set to the number + // of characters that have been processed to read the number. + // Spaces than are processed with ALLOW_{LEADING|TRAILING}_SPACES are included + // in the 'processed_characters_count'. Trailing junk is never included. + double StringToDouble(const char* buffer, + int length, + int* processed_characters_count) const; + + // Same as StringToDouble above but for 16 bit characters. + double StringToDouble(const uc16* buffer, + int length, + int* processed_characters_count) const; + + // Same as StringToDouble but reads a float. + // Note that this is not equivalent to static_cast<float>(StringToDouble(...)) + // due to potential double-rounding. + float StringToFloat(const char* buffer, + int length, + int* processed_characters_count) const; + + // Same as StringToFloat above but for 16 bit characters. + float StringToFloat(const uc16* buffer, + int length, + int* processed_characters_count) const; + + private: + const int flags_; + const double empty_string_value_; + const double junk_string_value_; + const char* const infinity_symbol_; + const char* const nan_symbol_; + + template <class Iterator> + double StringToIeee(Iterator start_pointer, + int length, + bool read_as_double, + int* processed_characters_count) const; + + DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter); +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ diff --git a/src/3rdparty/double-conversion/include/double-conversion/utils.h b/src/3rdparty/double-conversion/include/double-conversion/utils.h new file mode 100644 index 0000000000..53eec64282 --- /dev/null +++ b/src/3rdparty/double-conversion/include/double-conversion/utils.h @@ -0,0 +1,330 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_UTILS_H_ +#define DOUBLE_CONVERSION_UTILS_H_ + +#include <stdlib.h> +#include <string.h> + +#include <assert.h> +#ifndef ASSERT +# if defined(WINCE) || defined(_WIN32_WCE) +# define ASSERT(condition) +# else +# define ASSERT(condition) \ + assert(condition); +# endif +#endif +#ifndef UNIMPLEMENTED +# define UNIMPLEMENTED() (exit(-1)) +#endif +#ifndef UNREACHABLE +# define UNREACHABLE() (exit(-1)) +#endif + +// Double operations detection based on target architecture. +// Linux uses a 80bit wide floating point stack on x86. This induces double +// rounding, which in turn leads to wrong results. +// An easy way to test if the floating-point operations are correct is to +// evaluate: 89255.0/1e22. If the floating-point stack is 64 bits wide then +// the result is equal to 89255e-22. +// The best way to test this, is to create a division-function and to compare +// the output of the division with the expected result. (Inlining must be +// disabled.) +// On Linux,x86 89255e-22 != Div_double(89255.0/1e22) +#if defined(_M_X64) || defined(__x86_64__) || \ + defined(__ARMEL__) || defined(__avr32__) || _M_ARM_FP || \ + defined(__hppa__) || defined(__ia64__) || \ + defined(__mips__) || \ + defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__) || \ + defined(__sparc__) || defined(__sparc) || defined(__s390__) || \ + defined(__SH4__) || defined(__alpha__) || \ + defined(_MIPS_ARCH_MIPS32R2) || \ + defined(__AARCH64EL__) +#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 +#elif defined(_M_IX86) || defined(__i386__) || defined(__i386) +#if defined(_WIN32) +// Windows uses a 64bit wide floating point stack. +#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 +#else +#undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS +#endif // _WIN32 +#elif defined(WINCE) || defined(_WIN32_WCE) +#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 +#else +#error Target architecture was not detected as supported by Double-Conversion. +#endif + +#if defined(__GNUC__) +#define DOUBLE_CONVERSION_UNUSED __attribute__((unused)) +#else +#define DOUBLE_CONVERSION_UNUSED +#endif + +#if defined(_WIN32) && !defined(__MINGW32__) + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; // NOLINT +typedef unsigned short uint16_t; // NOLINT +typedef int int32_t; +typedef unsigned int uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +// intptr_t and friends are defined in crtdefs.h through stdio.h. + +#else + +#include <stdint.h> + +#endif + +typedef uint16_t uc16; + +// The following macro works on both 32 and 64-bit platforms. +// Usage: instead of writing 0x1234567890123456 +// write UINT64_2PART_C(0x12345678,90123456); +#define UINT64_2PART_C(a, b) (((static_cast<uint64_t>(a) << 32) + 0x##b##u)) + + +// The expression ARRAY_SIZE(a) is a compile-time constant of type +// size_t which represents the number of elements of the given +// array. You should only use ARRAY_SIZE on statically allocated +// arrays. +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) \ + ((sizeof(a) / sizeof(*(a))) / \ + static_cast<size_t>(!(sizeof(a) % sizeof(*(a))))) +#endif + +// A macro to disallow the evil copy constructor and operator= functions +// This should be used in the private: declarations for a class +#ifndef DISALLOW_COPY_AND_ASSIGN +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) +#endif + +// A macro to disallow all the implicit constructors, namely the +// default constructor, copy constructor and operator= functions. +// +// This should be used in the private: declarations for a class +// that wants to prevent anyone from instantiating it. This is +// especially useful for classes containing only static methods. +#ifndef DISALLOW_IMPLICIT_CONSTRUCTORS +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName(); \ + DISALLOW_COPY_AND_ASSIGN(TypeName) +#endif + +namespace double_conversion { + +static const int kCharSize = sizeof(char); + +// Returns the maximum of the two parameters. +template <typename T> +static T Max(T a, T b) { + return a < b ? b : a; +} + + +// Returns the minimum of the two parameters. +template <typename T> +static T Min(T a, T b) { + return a < b ? a : b; +} + + +inline int StrLength(const char* string) { + size_t length = strlen(string); + ASSERT(length == static_cast<size_t>(static_cast<int>(length))); + return static_cast<int>(length); +} + +// This is a simplified version of V8's Vector class. +template <typename T> +class Vector { + public: + Vector() : start_(NULL), length_(0) {} + Vector(T* data, int length) : start_(data), length_(length) { + ASSERT(length == 0 || (length > 0 && data != NULL)); + } + + // Returns a vector using the same backing storage as this one, + // spanning from and including 'from', to but not including 'to'. + Vector<T> SubVector(int from, int to) { + ASSERT(to <= length_); + ASSERT(from < to); + ASSERT(0 <= from); + return Vector<T>(start() + from, to - from); + } + + // Returns the length of the vector. + int length() const { return length_; } + + // Returns whether or not the vector is empty. + bool is_empty() const { return length_ == 0; } + + // Returns the pointer to the start of the data in the vector. + T* start() const { return start_; } + + // Access individual vector elements - checks bounds in debug mode. + T& operator[](int index) const { + ASSERT(0 <= index && index < length_); + return start_[index]; + } + + T& first() { return start_[0]; } + + T& last() { return start_[length_ - 1]; } + + private: + T* start_; + int length_; +}; + + +// Helper class for building result strings in a character buffer. The +// purpose of the class is to use safe operations that checks the +// buffer bounds on all operations in debug mode. +class StringBuilder { + public: + StringBuilder(char* buffer, int size) + : buffer_(buffer, size), position_(0) { } + + ~StringBuilder() { if (!is_finalized()) Finalize(); } + + int size() const { return buffer_.length(); } + + // Get the current position in the builder. + int position() const { + ASSERT(!is_finalized()); + return position_; + } + + // Reset the position. + void Reset() { position_ = 0; } + + // Add a single character to the builder. It is not allowed to add + // 0-characters; use the Finalize() method to terminate the string + // instead. + void AddCharacter(char c) { + ASSERT(c != '\0'); + ASSERT(!is_finalized() && position_ < buffer_.length()); + buffer_[position_++] = c; + } + + // Add an entire string to the builder. Uses strlen() internally to + // compute the length of the input string. + void AddString(const char* s) { + AddSubstring(s, StrLength(s)); + } + + // Add the first 'n' characters of the given string 's' to the + // builder. The input string must have enough characters. + void AddSubstring(const char* s, int n) { + ASSERT(!is_finalized() && position_ + n < buffer_.length()); + ASSERT(static_cast<size_t>(n) <= strlen(s)); + memmove(&buffer_[position_], s, n * kCharSize); + position_ += n; + } + + + // Add character padding to the builder. If count is non-positive, + // nothing is added to the builder. + void AddPadding(char c, int count) { + for (int i = 0; i < count; i++) { + AddCharacter(c); + } + } + + // Finalize the string by 0-terminating it and returning the buffer. + char* Finalize() { + ASSERT(!is_finalized() && position_ < buffer_.length()); + buffer_[position_] = '\0'; + // Make sure nobody managed to add a 0-character to the + // buffer while building the string. + ASSERT(strlen(buffer_.start()) == static_cast<size_t>(position_)); + position_ = -1; + ASSERT(is_finalized()); + return buffer_.start(); + } + + private: + Vector<char> buffer_; + int position_; + + bool is_finalized() const { return position_ < 0; } + + DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder); +}; + +// The type-based aliasing rule allows the compiler to assume that pointers of +// different types (for some definition of different) never alias each other. +// Thus the following code does not work: +// +// float f = foo(); +// int fbits = *(int*)(&f); +// +// The compiler 'knows' that the int pointer can't refer to f since the types +// don't match, so the compiler may cache f in a register, leaving random data +// in fbits. Using C++ style casts makes no difference, however a pointer to +// char data is assumed to alias any other pointer. This is the 'memcpy +// exception'. +// +// Bit_cast uses the memcpy exception to move the bits from a variable of one +// type of a variable of another type. Of course the end result is likely to +// be implementation dependent. Most compilers (gcc-4.2 and MSVC 2005) +// will completely optimize BitCast away. +// +// There is an additional use for BitCast. +// Recent gccs will warn when they see casts that may result in breakage due to +// the type-based aliasing rule. If you have checked that there is no breakage +// you can use BitCast to cast one pointer type to another. This confuses gcc +// enough that it can no longer see that you have cast one pointer type to +// another thus avoiding the warning. +template <class Dest, class Source> +inline Dest BitCast(const Source& source) { + // Compile time assertion: sizeof(Dest) == sizeof(Source) + // A compile error here means your Dest and Source have different sizes. + DOUBLE_CONVERSION_UNUSED + typedef char VerifySizesAreEqual[sizeof(Dest) == sizeof(Source) ? 1 : -1]; + + Dest dest; + memmove(&dest, &source, sizeof(dest)); + return dest; +} + +template <class Dest, class Source> +inline Dest BitCast(Source* source) { + return BitCast<Dest>(reinterpret_cast<uintptr_t>(source)); +} + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_UTILS_H_ diff --git a/src/3rdparty/double-conversion/strtod.cc b/src/3rdparty/double-conversion/strtod.cc new file mode 100644 index 0000000000..34717562bd --- /dev/null +++ b/src/3rdparty/double-conversion/strtod.cc @@ -0,0 +1,555 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <stdarg.h> +#include <limits.h> + +#include "strtod.h" +#include "bignum.h" +#include "cached-powers.h" +#include "ieee.h" + +namespace double_conversion { + +// 2^53 = 9007199254740992. +// Any integer with at most 15 decimal digits will hence fit into a double +// (which has a 53bit significand) without loss of precision. +static const int kMaxExactDoubleIntegerDecimalDigits = 15; +// 2^64 = 18446744073709551616 > 10^19 +static const int kMaxUint64DecimalDigits = 19; + +// Max double: 1.7976931348623157 x 10^308 +// Min non-zero double: 4.9406564584124654 x 10^-324 +// Any x >= 10^309 is interpreted as +infinity. +// Any x <= 10^-324 is interpreted as 0. +// Note that 2.5e-324 (despite being smaller than the min double) will be read +// as non-zero (equal to the min non-zero double). +static const int kMaxDecimalPower = 309; +static const int kMinDecimalPower = -324; + +// 2^64 = 18446744073709551616 +static const uint64_t kMaxUint64 = UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF); + + +static const double exact_powers_of_ten[] = { + 1.0, // 10^0 + 10.0, + 100.0, + 1000.0, + 10000.0, + 100000.0, + 1000000.0, + 10000000.0, + 100000000.0, + 1000000000.0, + 10000000000.0, // 10^10 + 100000000000.0, + 1000000000000.0, + 10000000000000.0, + 100000000000000.0, + 1000000000000000.0, + 10000000000000000.0, + 100000000000000000.0, + 1000000000000000000.0, + 10000000000000000000.0, + 100000000000000000000.0, // 10^20 + 1000000000000000000000.0, + // 10^22 = 0x21e19e0c9bab2400000 = 0x878678326eac9 * 2^22 + 10000000000000000000000.0 +}; +static const int kExactPowersOfTenSize = ARRAY_SIZE(exact_powers_of_ten); + +// Maximum number of significant digits in the decimal representation. +// In fact the value is 772 (see conversions.cc), but to give us some margin +// we round up to 780. +static const int kMaxSignificantDecimalDigits = 780; + +static Vector<const char> TrimLeadingZeros(Vector<const char> buffer) { + for (int i = 0; i < buffer.length(); i++) { + if (buffer[i] != '0') { + return buffer.SubVector(i, buffer.length()); + } + } + return Vector<const char>(buffer.start(), 0); +} + + +static Vector<const char> TrimTrailingZeros(Vector<const char> buffer) { + for (int i = buffer.length() - 1; i >= 0; --i) { + if (buffer[i] != '0') { + return buffer.SubVector(0, i + 1); + } + } + return Vector<const char>(buffer.start(), 0); +} + + +static void CutToMaxSignificantDigits(Vector<const char> buffer, + int exponent, + char* significant_buffer, + int* significant_exponent) { + for (int i = 0; i < kMaxSignificantDecimalDigits - 1; ++i) { + significant_buffer[i] = buffer[i]; + } + // The input buffer has been trimmed. Therefore the last digit must be + // different from '0'. + ASSERT(buffer[buffer.length() - 1] != '0'); + // Set the last digit to be non-zero. This is sufficient to guarantee + // correct rounding. + significant_buffer[kMaxSignificantDecimalDigits - 1] = '1'; + *significant_exponent = + exponent + (buffer.length() - kMaxSignificantDecimalDigits); +} + + +// Trims the buffer and cuts it to at most kMaxSignificantDecimalDigits. +// If possible the input-buffer is reused, but if the buffer needs to be +// modified (due to cutting), then the input needs to be copied into the +// buffer_copy_space. +static void TrimAndCut(Vector<const char> buffer, int exponent, + char* buffer_copy_space, int space_size, + Vector<const char>* trimmed, int* updated_exponent) { + Vector<const char> left_trimmed = TrimLeadingZeros(buffer); + Vector<const char> right_trimmed = TrimTrailingZeros(left_trimmed); + exponent += left_trimmed.length() - right_trimmed.length(); + if (right_trimmed.length() > kMaxSignificantDecimalDigits) { + (void) space_size; // Mark variable as used. + ASSERT(space_size >= kMaxSignificantDecimalDigits); + CutToMaxSignificantDigits(right_trimmed, exponent, + buffer_copy_space, updated_exponent); + *trimmed = Vector<const char>(buffer_copy_space, + kMaxSignificantDecimalDigits); + } else { + *trimmed = right_trimmed; + *updated_exponent = exponent; + } +} + + +// Reads digits from the buffer and converts them to a uint64. +// Reads in as many digits as fit into a uint64. +// When the string starts with "1844674407370955161" no further digit is read. +// Since 2^64 = 18446744073709551616 it would still be possible read another +// digit if it was less or equal than 6, but this would complicate the code. +static uint64_t ReadUint64(Vector<const char> buffer, + int* number_of_read_digits) { + uint64_t result = 0; + int i = 0; + while (i < buffer.length() && result <= (kMaxUint64 / 10 - 1)) { + int digit = buffer[i++] - '0'; + ASSERT(0 <= digit && digit <= 9); + result = 10 * result + digit; + } + *number_of_read_digits = i; + return result; +} + + +// Reads a DiyFp from the buffer. +// The returned DiyFp is not necessarily normalized. +// If remaining_decimals is zero then the returned DiyFp is accurate. +// Otherwise it has been rounded and has error of at most 1/2 ulp. +static void ReadDiyFp(Vector<const char> buffer, + DiyFp* result, + int* remaining_decimals) { + int read_digits; + uint64_t significand = ReadUint64(buffer, &read_digits); + if (buffer.length() == read_digits) { + *result = DiyFp(significand, 0); + *remaining_decimals = 0; + } else { + // Round the significand. + if (buffer[read_digits] >= '5') { + significand++; + } + // Compute the binary exponent. + int exponent = 0; + *result = DiyFp(significand, exponent); + *remaining_decimals = buffer.length() - read_digits; + } +} + + +static bool DoubleStrtod(Vector<const char> trimmed, + int exponent, + double* result) { +#if !defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS) + // On x86 the floating-point stack can be 64 or 80 bits wide. If it is + // 80 bits wide (as is the case on Linux) then double-rounding occurs and the + // result is not accurate. + // We know that Windows32 uses 64 bits and is therefore accurate. + // Note that the ARM simulator is compiled for 32bits. It therefore exhibits + // the same problem. + return false; +#endif + if (trimmed.length() <= kMaxExactDoubleIntegerDecimalDigits) { + int read_digits; + // The trimmed input fits into a double. + // If the 10^exponent (resp. 10^-exponent) fits into a double too then we + // can compute the result-double simply by multiplying (resp. dividing) the + // two numbers. + // This is possible because IEEE guarantees that floating-point operations + // return the best possible approximation. + if (exponent < 0 && -exponent < kExactPowersOfTenSize) { + // 10^-exponent fits into a double. + *result = static_cast<double>(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result /= exact_powers_of_ten[-exponent]; + return true; + } + if (0 <= exponent && exponent < kExactPowersOfTenSize) { + // 10^exponent fits into a double. + *result = static_cast<double>(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result *= exact_powers_of_ten[exponent]; + return true; + } + int remaining_digits = + kMaxExactDoubleIntegerDecimalDigits - trimmed.length(); + if ((0 <= exponent) && + (exponent - remaining_digits < kExactPowersOfTenSize)) { + // The trimmed string was short and we can multiply it with + // 10^remaining_digits. As a result the remaining exponent now fits + // into a double too. + *result = static_cast<double>(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result *= exact_powers_of_ten[remaining_digits]; + *result *= exact_powers_of_ten[exponent - remaining_digits]; + return true; + } + } + return false; +} + + +// Returns 10^exponent as an exact DiyFp. +// The given exponent must be in the range [1; kDecimalExponentDistance[. +static DiyFp AdjustmentPowerOfTen(int exponent) { + ASSERT(0 < exponent); + ASSERT(exponent < PowersOfTenCache::kDecimalExponentDistance); + // Simply hardcode the remaining powers for the given decimal exponent + // distance. + ASSERT(PowersOfTenCache::kDecimalExponentDistance == 8); + switch (exponent) { + case 1: return DiyFp(UINT64_2PART_C(0xa0000000, 00000000), -60); + case 2: return DiyFp(UINT64_2PART_C(0xc8000000, 00000000), -57); + case 3: return DiyFp(UINT64_2PART_C(0xfa000000, 00000000), -54); + case 4: return DiyFp(UINT64_2PART_C(0x9c400000, 00000000), -50); + case 5: return DiyFp(UINT64_2PART_C(0xc3500000, 00000000), -47); + case 6: return DiyFp(UINT64_2PART_C(0xf4240000, 00000000), -44); + case 7: return DiyFp(UINT64_2PART_C(0x98968000, 00000000), -40); + default: + UNREACHABLE(); + } +} + + +// If the function returns true then the result is the correct double. +// Otherwise it is either the correct double or the double that is just below +// the correct double. +static bool DiyFpStrtod(Vector<const char> buffer, + int exponent, + double* result) { + DiyFp input; + int remaining_decimals; + ReadDiyFp(buffer, &input, &remaining_decimals); + // Since we may have dropped some digits the input is not accurate. + // If remaining_decimals is different than 0 than the error is at most + // .5 ulp (unit in the last place). + // We don't want to deal with fractions and therefore keep a common + // denominator. + const int kDenominatorLog = 3; + const int kDenominator = 1 << kDenominatorLog; + // Move the remaining decimals into the exponent. + exponent += remaining_decimals; + int error = (remaining_decimals == 0 ? 0 : kDenominator / 2); + + int old_e = input.e(); + input.Normalize(); + error <<= old_e - input.e(); + + ASSERT(exponent <= PowersOfTenCache::kMaxDecimalExponent); + if (exponent < PowersOfTenCache::kMinDecimalExponent) { + *result = 0.0; + return true; + } + DiyFp cached_power; + int cached_decimal_exponent; + PowersOfTenCache::GetCachedPowerForDecimalExponent(exponent, + &cached_power, + &cached_decimal_exponent); + + if (cached_decimal_exponent != exponent) { + int adjustment_exponent = exponent - cached_decimal_exponent; + DiyFp adjustment_power = AdjustmentPowerOfTen(adjustment_exponent); + input.Multiply(adjustment_power); + if (kMaxUint64DecimalDigits - buffer.length() >= adjustment_exponent) { + // The product of input with the adjustment power fits into a 64 bit + // integer. + ASSERT(DiyFp::kSignificandSize == 64); + } else { + // The adjustment power is exact. There is hence only an error of 0.5. + error += kDenominator / 2; + } + } + + input.Multiply(cached_power); + // The error introduced by a multiplication of a*b equals + // error_a + error_b + error_a*error_b/2^64 + 0.5 + // Substituting a with 'input' and b with 'cached_power' we have + // error_b = 0.5 (all cached powers have an error of less than 0.5 ulp), + // error_ab = 0 or 1 / kDenominator > error_a*error_b/ 2^64 + int error_b = kDenominator / 2; + int error_ab = (error == 0 ? 0 : 1); // We round up to 1. + int fixed_error = kDenominator / 2; + error += error_b + error_ab + fixed_error; + + old_e = input.e(); + input.Normalize(); + error <<= old_e - input.e(); + + // See if the double's significand changes if we add/subtract the error. + int order_of_magnitude = DiyFp::kSignificandSize + input.e(); + int effective_significand_size = + Double::SignificandSizeForOrderOfMagnitude(order_of_magnitude); + int precision_digits_count = + DiyFp::kSignificandSize - effective_significand_size; + if (precision_digits_count + kDenominatorLog >= DiyFp::kSignificandSize) { + // This can only happen for very small denormals. In this case the + // half-way multiplied by the denominator exceeds the range of an uint64. + // Simply shift everything to the right. + int shift_amount = (precision_digits_count + kDenominatorLog) - + DiyFp::kSignificandSize + 1; + input.set_f(input.f() >> shift_amount); + input.set_e(input.e() + shift_amount); + // We add 1 for the lost precision of error, and kDenominator for + // the lost precision of input.f(). + error = (error >> shift_amount) + 1 + kDenominator; + precision_digits_count -= shift_amount; + } + // We use uint64_ts now. This only works if the DiyFp uses uint64_ts too. + ASSERT(DiyFp::kSignificandSize == 64); + ASSERT(precision_digits_count < 64); + uint64_t one64 = 1; + uint64_t precision_bits_mask = (one64 << precision_digits_count) - 1; + uint64_t precision_bits = input.f() & precision_bits_mask; + uint64_t half_way = one64 << (precision_digits_count - 1); + precision_bits *= kDenominator; + half_way *= kDenominator; + DiyFp rounded_input(input.f() >> precision_digits_count, + input.e() + precision_digits_count); + if (precision_bits >= half_way + error) { + rounded_input.set_f(rounded_input.f() + 1); + } + // If the last_bits are too close to the half-way case than we are too + // inaccurate and round down. In this case we return false so that we can + // fall back to a more precise algorithm. + + *result = Double(rounded_input).value(); + if (half_way - error < precision_bits && precision_bits < half_way + error) { + // Too imprecise. The caller will have to fall back to a slower version. + // However the returned number is guaranteed to be either the correct + // double, or the next-lower double. + return false; + } else { + return true; + } +} + + +// Returns +// - -1 if buffer*10^exponent < diy_fp. +// - 0 if buffer*10^exponent == diy_fp. +// - +1 if buffer*10^exponent > diy_fp. +// Preconditions: +// buffer.length() + exponent <= kMaxDecimalPower + 1 +// buffer.length() + exponent > kMinDecimalPower +// buffer.length() <= kMaxDecimalSignificantDigits +static int CompareBufferWithDiyFp(Vector<const char> buffer, + int exponent, + DiyFp diy_fp) { + ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1); + ASSERT(buffer.length() + exponent > kMinDecimalPower); + ASSERT(buffer.length() <= kMaxSignificantDecimalDigits); + // Make sure that the Bignum will be able to hold all our numbers. + // Our Bignum implementation has a separate field for exponents. Shifts will + // consume at most one bigit (< 64 bits). + // ln(10) == 3.3219... + ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits); + Bignum buffer_bignum; + Bignum diy_fp_bignum; + buffer_bignum.AssignDecimalString(buffer); + diy_fp_bignum.AssignUInt64(diy_fp.f()); + if (exponent >= 0) { + buffer_bignum.MultiplyByPowerOfTen(exponent); + } else { + diy_fp_bignum.MultiplyByPowerOfTen(-exponent); + } + if (diy_fp.e() > 0) { + diy_fp_bignum.ShiftLeft(diy_fp.e()); + } else { + buffer_bignum.ShiftLeft(-diy_fp.e()); + } + return Bignum::Compare(buffer_bignum, diy_fp_bignum); +} + + +// Returns true if the guess is the correct double. +// Returns false, when guess is either correct or the next-lower double. +static bool ComputeGuess(Vector<const char> trimmed, int exponent, + double* guess) { + if (trimmed.length() == 0) { + *guess = 0.0; + return true; + } + if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) { + *guess = Double::Infinity(); + return true; + } + if (exponent + trimmed.length() <= kMinDecimalPower) { + *guess = 0.0; + return true; + } + + if (DoubleStrtod(trimmed, exponent, guess) || + DiyFpStrtod(trimmed, exponent, guess)) { + return true; + } + if (*guess == Double::Infinity()) { + return true; + } + return false; +} + +double Strtod(Vector<const char> buffer, int exponent) { + char copy_buffer[kMaxSignificantDecimalDigits]; + Vector<const char> trimmed; + int updated_exponent; + TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits, + &trimmed, &updated_exponent); + exponent = updated_exponent; + + double guess; + bool is_correct = ComputeGuess(trimmed, exponent, &guess); + if (is_correct) return guess; + + DiyFp upper_boundary = Double(guess).UpperBoundary(); + int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary); + if (comparison < 0) { + return guess; + } else if (comparison > 0) { + return Double(guess).NextDouble(); + } else if ((Double(guess).Significand() & 1) == 0) { + // Round towards even. + return guess; + } else { + return Double(guess).NextDouble(); + } +} + +float Strtof(Vector<const char> buffer, int exponent) { + char copy_buffer[kMaxSignificantDecimalDigits]; + Vector<const char> trimmed; + int updated_exponent; + TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits, + &trimmed, &updated_exponent); + exponent = updated_exponent; + + double double_guess; + bool is_correct = ComputeGuess(trimmed, exponent, &double_guess); + + float float_guess = static_cast<float>(double_guess); + if (float_guess == double_guess) { + // This shortcut triggers for integer values. + return float_guess; + } + + // We must catch double-rounding. Say the double has been rounded up, and is + // now a boundary of a float, and rounds up again. This is why we have to + // look at previous too. + // Example (in decimal numbers): + // input: 12349 + // high-precision (4 digits): 1235 + // low-precision (3 digits): + // when read from input: 123 + // when rounded from high precision: 124. + // To do this we simply look at the neigbors of the correct result and see + // if they would round to the same float. If the guess is not correct we have + // to look at four values (since two different doubles could be the correct + // double). + + double double_next = Double(double_guess).NextDouble(); + double double_previous = Double(double_guess).PreviousDouble(); + + float f1 = static_cast<float>(double_previous); + float f2 = float_guess; + float f3 = static_cast<float>(double_next); + float f4; + if (is_correct) { + f4 = f3; + } else { + double double_next2 = Double(double_next).NextDouble(); + f4 = static_cast<float>(double_next2); + } + (void) f2; // Mark variable as used. + ASSERT(f1 <= f2 && f2 <= f3 && f3 <= f4); + + // If the guess doesn't lie near a single-precision boundary we can simply + // return its float-value. + if (f1 == f4) { + return float_guess; + } + + ASSERT((f1 != f2 && f2 == f3 && f3 == f4) || + (f1 == f2 && f2 != f3 && f3 == f4) || + (f1 == f2 && f2 == f3 && f3 != f4)); + + // guess and next are the two possible canditates (in the same way that + // double_guess was the lower candidate for a double-precision guess). + float guess = f1; + float next = f4; + DiyFp upper_boundary; + if (guess == 0.0f) { + float min_float = 1e-45f; + upper_boundary = Double(static_cast<double>(min_float) / 2).AsDiyFp(); + } else { + upper_boundary = Single(guess).UpperBoundary(); + } + int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary); + if (comparison < 0) { + return guess; + } else if (comparison > 0) { + return next; + } else if ((Single(guess).Significand() & 1) == 0) { + // Round towards even. + return guess; + } else { + return next; + } +} + +} // namespace double_conversion diff --git a/src/3rdparty/double-conversion/strtod.h b/src/3rdparty/double-conversion/strtod.h new file mode 100644 index 0000000000..ed0293b8f5 --- /dev/null +++ b/src/3rdparty/double-conversion/strtod.h @@ -0,0 +1,45 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_STRTOD_H_ +#define DOUBLE_CONVERSION_STRTOD_H_ + +#include "utils.h" + +namespace double_conversion { + +// The buffer must only contain digits in the range [0-9]. It must not +// contain a dot or a sign. It must not start with '0', and must not be empty. +double Strtod(Vector<const char> buffer, int exponent); + +// The buffer must only contain digits in the range [0-9]. It must not +// contain a dot or a sign. It must not start with '0', and must not be empty. +float Strtof(Vector<const char> buffer, int exponent); + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_STRTOD_H_ diff --git a/src/3rdparty/sqlite/0001-Fixing-the-SQLite3-build-for-WEC2013-again.patch b/src/3rdparty/sqlite/0001-Fixing-the-SQLite3-build-for-WEC2013-again.patch new file mode 100644 index 0000000000..2d92cfffa7 --- /dev/null +++ b/src/3rdparty/sqlite/0001-Fixing-the-SQLite3-build-for-WEC2013-again.patch @@ -0,0 +1,33 @@ +From c7bbe85015995c1e0627d88bac6fd5715b1338a0 Mon Sep 17 00:00:00 2001 +From: Bjoern Breitmeyer <bjoern.breitmeyer@kdab.com> +Date: Fri, 3 Jul 2015 14:08:04 +0200 +Subject: [PATCH] Fixing the SQLite3 build for WEC2013 again. + +The new version broke the build again +-> fix it again. + +Change-Id: I75761d134d97a2784f1de5076412aa814fdf9bcd +--- + src/3rdparty/sqlite/sqlite3.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/3rdparty/sqlite/sqlite3.c b/src/3rdparty/sqlite/sqlite3.c +index 71f6c10..040a9e1 100644 +--- a/src/3rdparty/sqlite/sqlite3.c ++++ b/src/3rdparty/sqlite/sqlite3.c +@@ -15474,9 +15474,11 @@ static void clearYMD_HMS_TZ(DateTime *p){ + #define HAVE_LOCALTIME_S 1 + #endif + +-#if defined(_WIN32_WCE) ++#if defined(_WIN32_WCE) && _WIN32_WCE < 0x800 + #undef HAVE_LOCALTIME_S + struct tm *__cdecl localtime(const time_t *t); ++#elif defined(_WIN32_WCE) && _WIN32_WCE >= 0x800 ++# define SQLITE_MSVC_LOCALTIME_API 1 + #endif + + #ifndef SQLITE_OMIT_LOCALTIME +-- +1.8.1.msysgit.1 + diff --git a/src/3rdparty/sqlite/sqlite3.c b/src/3rdparty/sqlite/sqlite3.c index 06f6d154f1..65379a822d 100644 --- a/src/3rdparty/sqlite/sqlite3.c +++ b/src/3rdparty/sqlite/sqlite3.c @@ -15742,9 +15742,11 @@ static void clearYMD_HMS_TZ(DateTime *p){ #define HAVE_LOCALTIME_S 1 #endif -#if defined(_WIN32_WCE) +#if defined(_WIN32_WCE) && _WIN32_WCE < 0x800 #undef HAVE_LOCALTIME_S struct tm *__cdecl localtime(const time_t *t); +#elif defined(_WIN32_WCE) && _WIN32_WCE >= 0x800 +# define SQLITE_MSVC_LOCALTIME_API 1 #endif #ifndef SQLITE_OMIT_LOCALTIME diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java b/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java index 445896622a..f1a5e105ad 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java @@ -887,6 +887,23 @@ public class QtActivityDelegate } catch (Exception e) { e.printStackTrace(); } + + // if splash screen is defined, then show it + // Note: QtActivity handles settting the splash screen + // in onCreate, change that too if you are changing + // how the splash screen should be displayed + try { + if (m_surfaces.size() == 0) { + ActivityInfo info = m_activity.getPackageManager().getActivityInfo(m_activity.getComponentName(), PackageManager.GET_META_DATA); + if (info.metaData.containsKey("android.app.splash_screen_drawable")) + m_activity.getWindow().setBackgroundDrawableResource(info.metaData.getInt("android.app.splash_screen_drawable")); + else + m_activity.getWindow().setBackgroundDrawable(new ColorDrawable(0xff000000)); + } + } catch (Exception e) { + e.printStackTrace(); + } + int rotation = m_activity.getWindowManager().getDefaultDisplay().getRotation(); if (rotation != m_currentRotation) { QtNative.handleOrientationChanged(rotation, m_nativeOrientation); diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java index 146d6b34f2..e6527c440a 100644 --- a/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java +++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java @@ -886,6 +886,9 @@ public class QtActivity extends Activity if (null == getLastNonConfigurationInstance()) { // if splash screen is defined, then show it + // Note: QtActivityDelegate handles updating the splash screen + // in onConfigurationChanged, change that too if you are changing + // how the splash screen should be displayed if (m_activityInfo.metaData.containsKey("android.app.splash_screen_drawable")) getWindow().setBackgroundDrawableResource(m_activityInfo.metaData.getInt("android.app.splash_screen_drawable")); else diff --git a/src/corelib/global/qcompilerdetection.h b/src/corelib/global/qcompilerdetection.h index ba830977ad..24d022059d 100644 --- a/src/corelib/global/qcompilerdetection.h +++ b/src/corelib/global/qcompilerdetection.h @@ -1119,6 +1119,37 @@ #endif /* + * SG10's SD-6 feature detection and some useful extensions from Clang and GCC + * https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations + * http://clang.llvm.org/docs/LanguageExtensions.html#feature-checking-macros + */ +#ifdef __has_builtin +# define QT_HAS_BUILTIN(x) __has_builtin(x) +#else +# define QT_HAS_BUILTIN(x) 0 +#endif +#ifdef __has_attribute +# define QT_HAS_ATTRIBUTE(x) __has_attribute(x) +#else +# define QT_HAS_ATTRIBUTE(x) 0 +#endif +#ifdef __has_cpp_attribute +# define QT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define QT_HAS_CPP_ATTRIBUTE(x) 0 +#endif +#ifdef __has_include +# define QT_HAS_INCLUDE(x) __has_include(x) +#else +# define QT_HAS_INCLUDE(x) 0 +#endif +#ifdef __has_include_next +# define QT_HAS_INCLUDE_NEXT(x) __has_include_next(x) +#else +# define QT_HAS_INCLUDE_NEXT(x) 0 +#endif + +/* * Warning/diagnostic handling */ diff --git a/src/corelib/global/qendian.h b/src/corelib/global/qendian.h index 2ddefaec8b..dd0e5a7e5a 100644 --- a/src/corelib/global/qendian.h +++ b/src/corelib/global/qendian.h @@ -87,12 +87,6 @@ template <typename T> inline T qFromUnaligned(const uchar *src) */ template <typename T> T qbswap(T source); -#ifdef __has_builtin -# define QT_HAS_BUILTIN(x) __has_builtin(x) -#else -# define QT_HAS_BUILTIN(x) 0 -#endif - // GCC 4.3 implemented all the intrinsics, but the 16-bit one only got implemented in 4.8; // Clang 2.6 implemented the 32- and 64-bit but waited until 3.2 to implement the 16-bit one #if (defined(Q_CC_GNU) && Q_CC_GNU >= 403) || QT_HAS_BUILTIN(__builtin_bswap32) @@ -154,8 +148,6 @@ template <> inline quint16 qbswap<quint16>(quint16 source) } #endif // GCC & Clang intrinsics -#undef QT_HAS_BUILTIN - // signed specializations template <> inline qint64 qbswap<qint64>(qint64 source) { diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index 81925642f8..c1ab2d69fa 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -1183,30 +1183,35 @@ bool qSharedBuild() Q_DECL_NOTHROW \macro Q_OS_DARWIN \relates <QtGlobal> - Defined on Darwin-based operating systems such as OS X and iOS, - including any open source version(s) of Darwin. + Defined on Darwin-based operating systems such as OS X, iOS, watchOS, and tvOS. */ /*! - \macro Q_OS_MAC + \macro Q_OS_OSX \relates <QtGlobal> - Defined on Darwin-based operating systems distributed by Apple, which - currently includes OS X and iOS, but not the open source versions of Darwin. + Defined on OS X. */ /*! - \macro Q_OS_OSX + \macro Q_OS_IOS \relates <QtGlobal> - Defined on OS X. + Defined on iOS. */ /*! - \macro Q_OS_IOS + \macro Q_OS_WATCHOS \relates <QtGlobal> - Defined on iOS. + Defined on watchOS. + */ + +/*! + \macro Q_OS_TVOS + \relates <QtGlobal> + + Defined on tvOS. */ /*! diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index db8c60831f..64d9f79faf 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -915,8 +915,6 @@ QT_WARNING_DISABLE_MSVC(4530) /* C++ exception handler used, but unwind semantic # endif #endif -#if defined(Q_COMPILER_DECLTYPE) || defined(Q_CC_GNU) -/* make use of decltype or GCC's __typeof__ extension */ template <typename T> class QForeachContainer { QForeachContainer &operator=(const QForeachContainer &) Q_DECL_EQ_DELETE; @@ -927,17 +925,6 @@ public: int control; }; -// We need to use __typeof__ if we don't have decltype or if the compiler -// hasn't been updated to the fix of Core Language Defect Report 382 -// (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#382). -// GCC 4.3 and 4.4 have support for decltype, but are affected by DR 382. -# if defined(Q_COMPILER_DECLTYPE) && \ - (defined(Q_CC_CLANG) || defined(Q_CC_INTEL) || !defined(Q_CC_GNU) || Q_CC_GNU >= 405) -# define QT_FOREACH_DECLTYPE(x) typename QtPrivate::remove_reference<decltype(x)>::type -# else -# define QT_FOREACH_DECLTYPE(x) __typeof__((x)) -# endif - // Explanation of the control word: // - it's initialized to 1 // - that means both the inner and outer loops start @@ -947,61 +934,12 @@ public: // the outer loop to continue executing // - if there was a break inside the inner loop, it will exit with control still // set to 1; in that case, the outer loop will invert it to 0 and will exit too -# define Q_FOREACH(variable, container) \ -for (QForeachContainer<QT_FOREACH_DECLTYPE(container)> _container_((container)); \ +#define Q_FOREACH(variable, container) \ +for (QForeachContainer<typename QtPrivate::remove_reference<decltype(container)>::type> _container_((container)); \ _container_.control && _container_.i != _container_.e; \ ++_container_.i, _container_.control ^= 1) \ for (variable = *_container_.i; _container_.control; _container_.control = 0) -#else - -struct QForeachContainerBase {}; - -template <typename T> -class QForeachContainer : public QForeachContainerBase { - QForeachContainer &operator=(const QForeachContainer &) Q_DECL_EQ_DELETE; -public: - inline QForeachContainer(const T& t): c(t), brk(0), i(c.begin()), e(c.end()){} - QForeachContainer(const QForeachContainer &other) - : c(other.c), brk(other.brk), i(other.i), e(other.e) {} - const T c; - mutable int brk; - mutable typename T::const_iterator i, e; - inline bool condition() const { return (!brk++ && i != e); } -}; - -template <typename T> inline T *qForeachPointer(const T &) { return 0; } - -template <typename T> inline QForeachContainer<T> qForeachContainerNew(const T& t) -{ return QForeachContainer<T>(t); } - -template <typename T> -inline const QForeachContainer<T> *qForeachContainer(const QForeachContainerBase *base, const T *) -{ return static_cast<const QForeachContainer<T> *>(base); } - -#if defined(Q_CC_DIAB) -// VxWorks DIAB generates unresolvable symbols, if container is a function call -# define Q_FOREACH(variable,container) \ - if(0){}else \ - for (const QForeachContainerBase &_container_ = qForeachContainerNew(container); \ - qForeachContainer(&_container_, (__typeof__(container) *) 0)->condition(); \ - ++qForeachContainer(&_container_, (__typeof__(container) *) 0)->i) \ - for (variable = *qForeachContainer(&_container_, (__typeof__(container) *) 0)->i; \ - qForeachContainer(&_container_, (__typeof__(container) *) 0)->brk; \ - --qForeachContainer(&_container_, (__typeof__(container) *) 0)->brk) - -#else -# define Q_FOREACH(variable, container) \ - for (const QForeachContainerBase &_container_ = qForeachContainerNew(container); \ - qForeachContainer(&_container_, true ? 0 : qForeachPointer(container))->condition(); \ - ++qForeachContainer(&_container_, true ? 0 : qForeachPointer(container))->i) \ - for (variable = *qForeachContainer(&_container_, true ? 0 : qForeachPointer(container))->i; \ - qForeachContainer(&_container_, true ? 0 : qForeachPointer(container))->brk; \ - --qForeachContainer(&_container_, true ? 0 : qForeachPointer(container))->brk) -#endif // MSVC6 || MIPSpro - -#endif - #define Q_FOREVER for(;;) #ifndef QT_NO_KEYWORDS # ifndef foreach diff --git a/src/corelib/global/qhooks.cpp b/src/corelib/global/qhooks.cpp index 382f45f592..40a7c88f13 100644 --- a/src/corelib/global/qhooks.cpp +++ b/src/corelib/global/qhooks.cpp @@ -37,7 +37,7 @@ QT_BEGIN_NAMESPACE // Only add to the end, and bump version if you do. quintptr Q_CORE_EXPORT qtHookData[] = { - 2, // hook data version + 3, // hook data version QHooks::LastHookIndex, // size of qtHookData QT_VERSION, @@ -52,6 +52,15 @@ quintptr Q_CORE_EXPORT qtHookData[] = { 0, // Startup, void(*)(), called once QCoreApplication is operational + 0, + + // TypeInformationVersion, an integral value, bumped whenever private + // object sizes or member offsets that are used in Qt Creator's + // data structure "pretty printing" change. + // + // The required sizes and offsets are tested in tests/auto/other/toolsupport. + // When this fails and the change was intentional, adjust the test and + // adjust this value here. 0 }; diff --git a/src/corelib/global/qhooks_p.h b/src/corelib/global/qhooks_p.h index 3ff4980abe..2beb58f8a7 100644 --- a/src/corelib/global/qhooks_p.h +++ b/src/corelib/global/qhooks_p.h @@ -61,6 +61,7 @@ enum HookIndex { AddQObject = 3, RemoveQObject = 4, Startup = 5, + TypeInformationVersion = 6, LastHookIndex }; diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp index 20c31f7ef8..47591a3fa8 100644 --- a/src/corelib/global/qlogging.cpp +++ b/src/corelib/global/qlogging.cpp @@ -75,17 +75,13 @@ # include "private/qcore_unix_p.h" #endif -#ifndef __has_include -# define __has_include(x) 0 -#endif - #ifndef QT_BOOTSTRAPPED #if !defined QT_NO_REGULAREXPRESSION # ifdef __UCLIBC__ # if __UCLIBC_HAS_BACKTRACE__ # define QLOGGING_HAVE_BACKTRACE # endif -# elif (defined(__GLIBC__) && defined(__GLIBCXX__)) || (__has_include(<cxxabi.h>) && __has_include(<execinfo.h>)) +# elif (defined(__GLIBC__) && defined(__GLIBCXX__)) || (QT_HAS_INCLUDE(<cxxabi.h>) && QT_HAS_INCLUDE(<execinfo.h>)) # define QLOGGING_HAVE_BACKTRACE # endif #endif @@ -94,7 +90,7 @@ extern char *__progname; #endif -#if defined(Q_OS_LINUX) && (defined(__GLIBC__) || __has_include(<sys/syscall.h>)) +#if defined(Q_OS_LINUX) && (defined(__GLIBC__) || QT_HAS_INCLUDE(<sys/syscall.h>)) # include <sys/syscall.h> static long qt_gettid() { diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index c4f5415a01..45594d1914 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -482,7 +482,8 @@ public: AA_DontShowIconsInMenus = 2, AA_NativeWindows = 3, AA_DontCreateNativeWidgetSiblings = 4, - AA_MacPluginApplication = 5, + AA_PluginApplication = 5, + AA_MacPluginApplication = AA_PluginApplication, // ### Qt 6: remove me AA_DontUseNativeMenuBar = 6, AA_MacDontSwapCtrlAndMeta = 7, AA_Use96Dpi = 8, @@ -498,6 +499,7 @@ public: AA_SetPalette = 19, AA_EnableHighDpiScaling = 20, AA_DisableHighDpiScaling = 21, + AA_UseStyleSheetPropagationInWidgetStyles = 22, // ### Qt 6: remove me // Add new attributes before this line AA_AttributeCount diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc index e789daafbd..df376405e0 100644 --- a/src/corelib/global/qnamespace.qdoc +++ b/src/corelib/global/qnamespace.qdoc @@ -120,13 +120,21 @@ widgets stay non-native unless specifically set by the Qt::WA_NativeWindow attribute. - \value AA_MacPluginApplication Stops the Qt mac application from doing - specific initializations that do not necessarily make sense when using Qt - to author a plugin. This includes avoiding loading our nib for the main - menu and not taking possession of the native menu bar. When setting this + \value AA_PluginApplication Indicates that Qt is used to author a plugin. Depending + on the operating system, it suppresses specific initializations that do not + necessarily make sense in the plugin case. + + For example on OS X, this includes avoiding loading our nib for the main + menu and not taking possession of the native menu bar. Setting this attribute to true will also set the AA_DontUseNativeMenuBar attribute to true. It also disables native event filters. + This attribute has been added in Qt 5.7. It must be set before + \l {QGuiApplication}{Q(Gui)Application} is constructed. + + \value AA_MacPluginApplication This attribute has been deprecated. + Use AA_PluginApplication instead. + \value AA_DontUseNativeMenuBar All menubars created while this attribute is set to true won't be used as a native menubar (e.g, the menubar at the top of the main screen on OS X or at the bottom in Windows CE). @@ -217,6 +225,13 @@ environment variable to 0. This value has been added in Qt 5.6. This attribute must be set before Q(Gui)Application is constructed. + \value AA_UseStyleSheetPropagationInWidgetStyles By default, Qt Style Sheets + disable regular QWidget palette and font propagation. When this flag + is enabled, font and palette changes propagate as though the user had + manually called the corresponding QWidget methods. See + \l{The Style Sheet Syntax#Inheritance}{The Style Sheet Syntax - Inheritance} + for more details. This value has been added in Qt 5.7. + The following values are obsolete: \value AA_ImmediateWidgetCreation This attribute is no longer fully diff --git a/src/corelib/global/qnumeric_p.h b/src/corelib/global/qnumeric_p.h index e5f9d8e13e..b56cdef939 100644 --- a/src/corelib/global/qnumeric_p.h +++ b/src/corelib/global/qnumeric_p.h @@ -47,7 +47,7 @@ // #include "QtCore/qglobal.h" - +#include <cmath> #include <limits> #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) @@ -56,12 +56,57 @@ # include <immintrin.h> // for _addcarry_u<nn> #endif -#ifndef __has_builtin -# define __has_builtin(x) 0 +#if defined(Q_CC_MSVC) +#include <float.h> +#endif + +#if !defined(Q_CC_MSVC) && (defined(Q_OS_QNX) || !defined(__cplusplus) || __cplusplus < 201103L) +#include <math.h> +QT_BEGIN_NAMESPACE +namespace qnumeric_std_wrapper { +// the 'using namespace std' below is cases where the stdlib already put the math.h functions in the std namespace and undefined the macros. +static inline bool math_h_isnan(double d) { using namespace std; return isnan(d); } +static inline bool math_h_isinf(double d) { using namespace std; return isinf(d); } +static inline bool math_h_isfinite(double d) { using namespace std; return isfinite(d); } +static inline bool math_h_isnan(float f) { using namespace std; return isnan(f); } +static inline bool math_h_isinf(float f) { using namespace std; return isinf(f); } +static inline bool math_h_isfinite(float f) { using namespace std; return isfinite(f); } +} +QT_END_NAMESPACE +// These macros from math.h conflict with the real functions in the std namespace. +#undef signbit +#undef isnan +#undef isinf +#undef isfinite #endif QT_BEGIN_NAMESPACE +namespace qnumeric_std_wrapper { +#if defined(Q_CC_MSVC) && _MSC_VER < 1800 +static inline bool isnan(double d) { return !!_isnan(d); } +static inline bool isinf(double d) { return !_finite(d) && !_isnan(d); } +static inline bool isfinite(double d) { return !!_finite(d); } +static inline bool isnan(float f) { return !!_isnanf(f); } +static inline bool isinf(float f) { return !_finitef(f) && !_isnan(f); } +static inline bool isfinite(float f) { return !!_finite(f); } +#elif !defined(Q_CC_MSVC) && (defined(Q_OS_QNX) || !defined(__cplusplus) || __cplusplus < 201103L) +static inline bool isnan(double d) { return math_h_isnan(d); } +static inline bool isinf(double d) { return math_h_isinf(d); } +static inline bool isfinite(double d) { return math_h_isfinite(d); } +static inline bool isnan(float f) { return math_h_isnan(f); } +static inline bool isinf(float f) { return math_h_isinf(f); } +static inline bool isfinite(float f) { return math_h_isfinite(f); } +#else +static inline bool isnan(double d) { return std::isnan(d); } +static inline bool isinf(double d) { return std::isinf(d); } +static inline bool isfinite(double d) { return std::isfinite(d); } +static inline bool isnan(float f) { return std::isnan(f); } +static inline bool isinf(float f) { return std::isinf(f); } +static inline bool isfinite(float f) { return std::isfinite(f); } +#endif +} + #if !defined(Q_CC_MIPS) static const union { unsigned char c[8]; double d; } qt_be_inf_bytes = { { 0x7f, 0xf0, 0, 0, 0, 0, 0, 0 } }; @@ -143,62 +188,32 @@ static inline double qt_qnan() static inline bool qt_is_inf(double d) { - uchar *ch = (uchar *)&d; - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - return (ch[0] & 0x7f) == 0x7f && ch[1] == 0xf0; - } else { - return (ch[7] & 0x7f) == 0x7f && ch[6] == 0xf0; - } + return qnumeric_std_wrapper::isinf(d); } static inline bool qt_is_nan(double d) { - uchar *ch = (uchar *)&d; - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - return (ch[0] & 0x7f) == 0x7f && ch[1] > 0xf0; - } else { - return (ch[7] & 0x7f) == 0x7f && ch[6] > 0xf0; - } + return qnumeric_std_wrapper::isnan(d); } static inline bool qt_is_finite(double d) { - uchar *ch = (uchar *)&d; - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - return (ch[0] & 0x7f) != 0x7f || (ch[1] & 0xf0) != 0xf0; - } else { - return (ch[7] & 0x7f) != 0x7f || (ch[6] & 0xf0) != 0xf0; - } + return qnumeric_std_wrapper::isfinite(d); } -static inline bool qt_is_inf(float d) +static inline bool qt_is_inf(float f) { - uchar *ch = (uchar *)&d; - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - return (ch[0] & 0x7f) == 0x7f && ch[1] == 0x80; - } else { - return (ch[3] & 0x7f) == 0x7f && ch[2] == 0x80; - } + return qnumeric_std_wrapper::isinf(f); } -static inline bool qt_is_nan(float d) +static inline bool qt_is_nan(float f) { - uchar *ch = (uchar *)&d; - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - return (ch[0] & 0x7f) == 0x7f && ch[1] > 0x80; - } else { - return (ch[3] & 0x7f) == 0x7f && ch[2] > 0x80; - } + return qnumeric_std_wrapper::isnan(f); } -static inline bool qt_is_finite(float d) +static inline bool qt_is_finite(float f) { - uchar *ch = (uchar *)&d; - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - return (ch[0] & 0x7f) != 0x7f || (ch[1] & 0x80) != 0x80; - } else { - return (ch[3] & 0x7f) != 0x7f || (ch[2] & 0x80) != 0x80; - } + return qnumeric_std_wrapper::isfinite(f); } // @@ -230,28 +245,28 @@ mul_overflow(T v1, T v2, T *r) #endif // GCC 5 and Clang have builtins to detect overflows -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || __has_builtin(__builtin_uadd_overflow) +#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_uadd_overflow) template <> inline bool add_overflow(unsigned v1, unsigned v2, unsigned *r) { return __builtin_uadd_overflow(v1, v2, r); } #endif -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || __has_builtin(__builtin_uaddl_overflow) +#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_uaddl_overflow) template <> inline bool add_overflow(unsigned long v1, unsigned long v2, unsigned long *r) { return __builtin_uaddl_overflow(v1, v2, r); } #endif -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || __has_builtin(__builtin_uaddll_overflow) +#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_uaddll_overflow) template <> inline bool add_overflow(unsigned long long v1, unsigned long long v2, unsigned long long *r) { return __builtin_uaddll_overflow(v1, v2, r); } #endif -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || __has_builtin(__builtin_umul_overflow) +#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_umul_overflow) template <> inline bool mul_overflow(unsigned v1, unsigned v2, unsigned *r) { return __builtin_umul_overflow(v1, v2, r); } #endif -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || __has_builtin(__builtin_umull_overflow) +#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_umull_overflow) template <> inline bool mul_overflow(unsigned long v1, unsigned long v2, unsigned long *r) { return __builtin_umull_overflow(v1, v2, r); } #endif -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || __has_builtin(__builtin_umulll_overflow) +#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_umulll_overflow) template <> inline bool mul_overflow(unsigned long long v1, unsigned long long v2, unsigned long long *r) { return __builtin_umulll_overflow(v1, v2, r); } # define HAVE_MUL64_OVERFLOW diff --git a/src/corelib/global/qsystemdetection.h b/src/corelib/global/qsystemdetection.h index 751c6a9a0e..9ca376b84a 100644 --- a/src/corelib/global/qsystemdetection.h +++ b/src/corelib/global/qsystemdetection.h @@ -41,10 +41,11 @@ /* The operating system, must be one of: (Q_OS_x) - DARWIN - Any Darwin system - MAC - OS X and iOS + DARWIN - Any Darwin system (OS X, iOS, watchOS, tvOS) OSX - OS X IOS - iOS + WATCHOS - watchOS + TVOS - tvOS MSDOS - MS-DOS and Windows OS2 - OS/2 OS2EMX - XFree86 on OS/2 (not PM) @@ -87,12 +88,32 @@ */ #if defined(__APPLE__) && (defined(__GNUC__) || defined(__xlC__) || defined(__xlc__)) -# define Q_OS_DARWIN -# define Q_OS_BSD4 -# ifdef __LP64__ -# define Q_OS_DARWIN64 +# include <TargetConditionals.h> +# if defined(TARGET_OS_MAC) && TARGET_OS_MAC +# define Q_OS_DARWIN +# define Q_OS_BSD4 +# ifdef __LP64__ +# define Q_OS_DARWIN64 +# else +# define Q_OS_DARWIN32 +# endif +# if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE +# if defined(TARGET_OS_TV) && TARGET_OS_TV +# define Q_OS_TVOS +# elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH +# define Q_OS_WATCHOS +# else +# // TARGET_OS_IOS is only available in newer SDKs, +# // so assume any other iOS-based platform is iOS for now +# define Q_OS_IOS +# endif +# else +# // there is no "real" OS X define (rdar://22640089), +# // assume any non iOS-based platform is OS X for now +# define Q_OS_OSX +# endif # else -# define Q_OS_DARWIN32 +# error "Qt has not been ported to this Apple platform - see http://www.qt.io/developers" # endif #elif defined(__ANDROID__) || defined(ANDROID) # define Q_OS_ANDROID @@ -181,28 +202,26 @@ # define Q_OS_WIN #endif -#if defined(Q_OS_DARWIN) -# define Q_OS_MAC -# if defined(Q_OS_DARWIN64) -# define Q_OS_MAC64 -# elif defined(Q_OS_DARWIN32) -# define Q_OS_MAC32 -# endif -# include <TargetConditionals.h> -# if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE -# define Q_OS_IOS -# elif defined(TARGET_OS_MAC) && TARGET_OS_MAC -# define Q_OS_OSX -# define Q_OS_MACX // compatibility synonym -# endif -#endif - #if defined(Q_OS_WIN) # undef Q_OS_UNIX #elif !defined(Q_OS_UNIX) # define Q_OS_UNIX #endif +// Compatibility synonyms +#ifdef Q_OS_DARWIN +#define Q_OS_MAC +#endif +#ifdef Q_OS_DARWIN32 +#define Q_OS_MAC32 +#endif +#ifdef Q_OS_DARWIN64 +#define Q_OS_MAC64 +#endif +#ifdef Q_OS_OSX +#define Q_OS_MACX +#endif + #ifdef Q_OS_DARWIN # include <Availability.h> # include <AvailabilityMacros.h> diff --git a/src/corelib/global/qtypeinfo.h b/src/corelib/global/qtypeinfo.h index b42e5998fc..cd0a83ae80 100644 --- a/src/corelib/global/qtypeinfo.h +++ b/src/corelib/global/qtypeinfo.h @@ -276,13 +276,25 @@ Q_DECLARE_TYPEINFO(double, Q_PRIMITIVE_TYPE); Q_DECLARE_TYPEINFO(long double, Q_PRIMITIVE_TYPE); #endif + #if QT_VERSION >= QT_VERSION_CHECK(6,0,0) -// We can't do it now because it would break BC on QList<char32_t> +// ### Qt 6: remove the other branch +// This was required so that QList<T> for these types allocates out of the array storage +# ifdef Q_COMPILER_UNICODE_STRINGS Q_DECLARE_TYPEINFO(char16_t, Q_PRIMITIVE_TYPE); Q_DECLARE_TYPEINFO(char32_t, Q_PRIMITIVE_TYPE); +# endif # if !defined(Q_CC_MSVC) || defined(_NATIVE_WCHAR_T_DEFINED) Q_DECLARE_TYPEINFO(wchar_t, Q_PRIMITIVE_TYPE); # endif +#else +# ifdef Q_COMPILER_UNICODE_STRINGS +Q_DECLARE_TYPEINFO(char16_t, Q_RELOCATABLE_TYPE); +Q_DECLARE_TYPEINFO(char32_t, Q_RELOCATABLE_TYPE); +# endif +# if !defined(Q_CC_MSVC) || defined(_NATIVE_WCHAR_T_DEFINED) +Q_DECLARE_TYPEINFO(wchar_t, Q_RELOCATABLE_TYPE); +# endif #endif // Qt 6 QT_END_NAMESPACE diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri index b2bcbdf727..7be1e5883e 100644 --- a/src/corelib/io/io.pri +++ b/src/corelib/io/io.pri @@ -149,20 +149,14 @@ win32 { } freebsd: LIBS_PRIVATE += -lutil # qlockfile_unix.cpp requires this mac { + SOURCES += io/qstorageinfo_mac.cpp + OBJECTIVE_SOURCES += io/qstandardpaths_mac.mm osx { OBJECTIVE_SOURCES += io/qfilesystemwatcher_fsevents.mm HEADERS += io/qfilesystemwatcher_fsevents_p.h - } - macx { - SOURCES += io/qstorageinfo_mac.cpp - OBJECTIVE_SOURCES += io/qstandardpaths_mac.mm LIBS += -framework DiskArbitration -framework IOKit } else:ios { - OBJECTIVE_SOURCES += io/qstandardpaths_ios.mm - SOURCES += io/qstorageinfo_mac.cpp LIBS += -framework MobileCoreServices - } else { - SOURCES += io/qstandardpaths_unix.cpp } } else:blackberry { SOURCES += \ diff --git a/src/corelib/io/qdatastream.cpp b/src/corelib/io/qdatastream.cpp index 675178ea80..c32df4c8a6 100644 --- a/src/corelib/io/qdatastream.cpp +++ b/src/corelib/io/qdatastream.cpp @@ -527,6 +527,7 @@ void QDataStream::setByteOrder(ByteOrder bo) \value Qt_5_4 Version 16 (Qt 5.4) \value Qt_5_5 Same as Qt_5_4 \value Qt_5_6 Version 17 (Qt 5.6) + \value Qt_5_7 Same as Qt_5_6 \omitvalue Qt_DefaultCompiledVersion \sa setVersion(), version() diff --git a/src/corelib/io/qdatastream.h b/src/corelib/io/qdatastream.h index 5730c12907..744829c659 100644 --- a/src/corelib/io/qdatastream.h +++ b/src/corelib/io/qdatastream.h @@ -84,10 +84,11 @@ public: Qt_5_4 = 16, Qt_5_5 = Qt_5_4, Qt_5_6 = 17, -#if QT_VERSION >= 0x050700 + Qt_5_7 = Qt_5_6, +#if QT_VERSION >= 0x050800 #error Add the datastream version for this Qt version and update Qt_DefaultCompiledVersion #endif - Qt_DefaultCompiledVersion = Qt_5_6 + Qt_DefaultCompiledVersion = Qt_5_7 }; enum ByteOrder { diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp index 3c7a7d69e4..64078b5c54 100644 --- a/src/corelib/io/qiodevice.cpp +++ b/src/corelib/io/qiodevice.cpp @@ -670,7 +670,7 @@ bool QIODevice::seek(qint64 pos) // operation will then refill the buffer. We can optimize this, if we // find that seeking backwards becomes a significant performance hit. d->buffer.clear(); - else if (!d->buffer.isEmpty()) + else d->buffer.skip(offset); #if defined QIODEVICE_DEBUG diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index a5488f48cc..38f7ecd568 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -672,6 +672,7 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv qt_safe_close(childStartedPipe[0]); // enter the working directory + const char *callthatfailed = "chdir: "; if (workingDir && QT_CHDIR(workingDir) == -1) { // failed, stop the process goto report_errno; @@ -683,6 +684,7 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv // execute the process if (!envp) { qt_safe_execvp(argv[0], argv); + callthatfailed = "execvp: "; } else { if (path) { char **arg = path; @@ -700,15 +702,19 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv #endif qt_safe_execve(argv[0], argv, envp); } + callthatfailed = "execve: "; } // notify failure + // we're running in the child process, so we don't need to be thread-safe; + // we can use strerror report_errno: - QString error = qt_error_string(errno); + const char *msg = strerror(errno); #if defined (QPROCESS_DEBUG) - fprintf(stderr, "QProcessPrivate::execChild() failed (%s), notifying parent process\n", qPrintable(error)); + fprintf(stderr, "QProcessPrivate::execChild() failed (%s), notifying parent process\n", msg); #endif - qt_safe_write(childStartedPipe[1], error.data(), error.length() * sizeof(QChar)); + qt_safe_write(childStartedPipe[1], callthatfailed, strlen(callthatfailed)); + qt_safe_write(childStartedPipe[1], msg, strlen(msg)); qt_safe_close(childStartedPipe[1]); childStartedPipe[1] = -1; } @@ -716,8 +722,15 @@ report_errno: bool QProcessPrivate::processStarted(QString *errorMessage) { - ushort buf[errorBufferMax]; - int i = qt_safe_read(childStartedPipe[0], &buf, sizeof buf); + char buf[errorBufferMax]; + int i = 0; + int ret; + do { + ret = qt_safe_read(childStartedPipe[0], buf + i, sizeof buf - i); + if (ret > 0) + i += ret; + } while (ret > 0 && i < int(sizeof buf)); + if (startupSocketNotifier) { startupSocketNotifier->setEnabled(false); startupSocketNotifier->deleteLater(); @@ -732,7 +745,7 @@ bool QProcessPrivate::processStarted(QString *errorMessage) // did we read an error message? if ((i > 0) && errorMessage) - *errorMessage = QString((const QChar *)buf, i / sizeof(QChar)); + *errorMessage = QString::fromLocal8Bit(buf, i); return i <= 0; } diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp index 0c44582af8..3460753fbe 100644 --- a/src/corelib/io/qsettings.cpp +++ b/src/corelib/io/qsettings.cpp @@ -2413,14 +2413,24 @@ void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile, This enum type specifies the storage format used by QSettings. - \value NativeFormat Store the settings using the most - appropriate storage format for the platform. - On Windows, this means the system registry; - on OS X and iOS, this means the CFPreferences - API; on Unix, this means textual - configuration files in INI format. - \value IniFormat Store the settings in INI files. - \value InvalidFormat Special value returned by registerFormat(). + \value NativeFormat Store the settings using the most + appropriate storage format for the platform. + On Windows, this means the system registry; + on OS X and iOS, this means the CFPreferences + API; on Unix, this means textual + configuration files in INI format. + \value Registry32Format Windows only: Explicitly access the 32-bit system registry + from a 64-bit application running on 64-bit Windows. + On 32-bit Windows or from a 32-bit application on 64-bit Windows, + this works the same as specifying NativeFormat. + This enum value was added in Qt 5.7. + \value Registry64Format Windows only: Explicitly access the 64-bit system registry + from a 32-bit application running on 64-bit Windows. + On 32-bit Windows or from a 64-bit application on 64-bit Windows, + this works the same as specifying NativeFormat. + This enum value was added in Qt 5.7. + \value IniFormat Store the settings in INI files. + \value InvalidFormat Special value returned by registerFormat(). \omitvalue CustomFormat1 \omitvalue CustomFormat2 \omitvalue CustomFormat3 diff --git a/src/corelib/io/qsettings.h b/src/corelib/io/qsettings.h index 8f41273ffa..dd0c4a9bfb 100644 --- a/src/corelib/io/qsettings.h +++ b/src/corelib/io/qsettings.h @@ -79,6 +79,11 @@ public: NativeFormat, IniFormat, +#ifdef Q_OS_WIN + Registry32Format, + Registry64Format, +#endif + InvalidFormat = 16, CustomFormat1, CustomFormat2, diff --git a/src/corelib/io/qsettings_win.cpp b/src/corelib/io/qsettings_win.cpp index 1546219c3b..88f58422a8 100644 --- a/src/corelib/io/qsettings_win.cpp +++ b/src/corelib/io/qsettings_win.cpp @@ -41,6 +41,18 @@ #include "qdebug.h" #include <qt_windows.h> +// See "Accessing an Alternate Registry View" at: +// http://msdn.microsoft.com/en-us/library/aa384129%28VS.85%29.aspx +#ifndef KEY_WOW64_64KEY + // Access a 32-bit key from either a 32-bit or 64-bit application. +# define KEY_WOW64_64KEY 0x0100 +#endif + +#ifndef KEY_WOW64_32KEY + // Access a 64-bit key from either a 32-bit or 64-bit application. +# define KEY_WOW64_32KEY 0x0200 +#endif + QT_BEGIN_NAMESPACE /* Keys are stored in QStrings. If the variable name starts with 'u', this is a "user" @@ -135,12 +147,13 @@ static QString errorCodeToString(DWORD errorCode) return result; } -// Open a key with the specified perms -static HKEY openKey(HKEY parentHandle, REGSAM perms, const QString &rSubKey) +// Open a key with the specified "perms". +// "access" is to explicitly use the 32- or 64-bit branch. +static HKEY openKey(HKEY parentHandle, REGSAM perms, const QString &rSubKey, REGSAM access = 0) { HKEY resultHandle = 0; LONG res = RegOpenKeyEx(parentHandle, reinterpret_cast<const wchar_t *>(rSubKey.utf16()), - 0, perms, &resultHandle); + 0, perms | access, &resultHandle); if (res == ERROR_SUCCESS) return resultHandle; @@ -148,17 +161,18 @@ static HKEY openKey(HKEY parentHandle, REGSAM perms, const QString &rSubKey) return 0; } -// Open a key with the specified perms, create it if it does not exist -static HKEY createOrOpenKey(HKEY parentHandle, REGSAM perms, const QString &rSubKey) +// Open a key with the specified "perms", create it if it does not exist. +// "access" is to explicitly use the 32- or 64-bit branch. +static HKEY createOrOpenKey(HKEY parentHandle, REGSAM perms, const QString &rSubKey, REGSAM access = 0) { // try to open it - HKEY resultHandle = openKey(parentHandle, perms, rSubKey); + HKEY resultHandle = openKey(parentHandle, perms, rSubKey, access); if (resultHandle != 0) return resultHandle; // try to create it LONG res = RegCreateKeyEx(parentHandle, reinterpret_cast<const wchar_t *>(rSubKey.utf16()), 0, 0, - REG_OPTION_NON_VOLATILE, perms, 0, &resultHandle, 0); + REG_OPTION_NON_VOLATILE, perms | access, 0, &resultHandle, 0); if (res == ERROR_SUCCESS) return resultHandle; @@ -169,11 +183,12 @@ static HKEY createOrOpenKey(HKEY parentHandle, REGSAM perms, const QString &rSub return 0; } -// Open or create a key in read-write mode if possible, otherwise read-only -static HKEY createOrOpenKey(HKEY parentHandle, const QString &rSubKey, bool *readOnly) +// Open or create a key in read-write mode if possible, otherwise read-only. +// "access" is to explicitly use the 32- or 64-bit branch. +static HKEY createOrOpenKey(HKEY parentHandle, const QString &rSubKey, bool *readOnly, REGSAM access = 0) { // try to open or create it read/write - HKEY resultHandle = createOrOpenKey(parentHandle, registryPermissions, rSubKey); + HKEY resultHandle = createOrOpenKey(parentHandle, registryPermissions, rSubKey, access); if (resultHandle != 0) { if (readOnly != 0) *readOnly = false; @@ -181,7 +196,7 @@ static HKEY createOrOpenKey(HKEY parentHandle, const QString &rSubKey, bool *rea } // try to open or create it read/only - resultHandle = createOrOpenKey(parentHandle, KEY_READ, rSubKey); + resultHandle = createOrOpenKey(parentHandle, KEY_READ, rSubKey, access); if (resultHandle != 0) { if (readOnly != 0) *readOnly = true; @@ -247,9 +262,9 @@ static QStringList childKeysOrGroups(HKEY parentHandle, QSettingsPrivate::ChildS return result; } -static void allKeys(HKEY parentHandle, const QString &rSubKey, NameSet *result) +static void allKeys(HKEY parentHandle, const QString &rSubKey, NameSet *result, REGSAM access = 0) { - HKEY handle = openKey(parentHandle, KEY_READ, rSubKey); + HKEY handle = openKey(parentHandle, KEY_READ, rSubKey, access); if (handle == 0) return; @@ -270,11 +285,11 @@ static void allKeys(HKEY parentHandle, const QString &rSubKey, NameSet *result) if (!s.isEmpty()) s += QLatin1Char('\\'); s += childGroups.at(i); - allKeys(parentHandle, s, result); + allKeys(parentHandle, s, result, access); } } -static void deleteChildGroups(HKEY parentHandle) +static void deleteChildGroups(HKEY parentHandle, REGSAM access = 0) { QStringList childGroups = childKeysOrGroups(parentHandle, QSettingsPrivate::ChildGroups); @@ -282,10 +297,10 @@ static void deleteChildGroups(HKEY parentHandle) QString group = childGroups.at(i); // delete subgroups in group - HKEY childGroupHandle = openKey(parentHandle, registryPermissions, group); + HKEY childGroupHandle = openKey(parentHandle, registryPermissions, group, access); if (childGroupHandle == 0) continue; - deleteChildGroups(childGroupHandle); + deleteChildGroups(childGroupHandle, access); RegCloseKey(childGroupHandle); // delete group itself @@ -305,7 +320,7 @@ static void deleteChildGroups(HKEY parentHandle) class RegistryKey { public: - RegistryKey(HKEY parent_handle = 0, const QString &key = QString(), bool read_only = true); + RegistryKey(HKEY parent_handle = 0, const QString &key = QString(), bool read_only = true, REGSAM access = 0); QString key() const; HKEY handle() const; HKEY parentHandle() const; @@ -316,13 +331,15 @@ private: mutable HKEY m_handle; QString m_key; mutable bool m_read_only; + REGSAM m_access; }; -RegistryKey::RegistryKey(HKEY parent_handle, const QString &key, bool read_only) +RegistryKey::RegistryKey(HKEY parent_handle, const QString &key, bool read_only, REGSAM access) : m_parent_handle(parent_handle), m_handle(0), m_key(key), - m_read_only(read_only) + m_read_only(read_only), + m_access(access) { } @@ -337,9 +354,9 @@ HKEY RegistryKey::handle() const return m_handle; if (m_read_only) - m_handle = openKey(m_parent_handle, KEY_READ, m_key); + m_handle = openKey(m_parent_handle, KEY_READ, m_key, m_access); else - m_handle = createOrOpenKey(m_parent_handle, m_key, &m_read_only); + m_handle = createOrOpenKey(m_parent_handle, m_key, &m_read_only, m_access); return m_handle; } @@ -371,8 +388,8 @@ class QWinSettingsPrivate : public QSettingsPrivate { public: QWinSettingsPrivate(QSettings::Scope scope, const QString &organization, - const QString &application); - QWinSettingsPrivate(QString rKey); + const QString &application, REGSAM access = 0); + QWinSettingsPrivate(QString rKey, REGSAM access = 0); ~QWinSettingsPrivate(); void remove(const QString &uKey); @@ -390,11 +407,13 @@ public: private: RegistryKeyList regList; // list of registry locations to search for keys bool deleteWriteHandleOnExit; + REGSAM access; }; QWinSettingsPrivate::QWinSettingsPrivate(QSettings::Scope scope, const QString &organization, - const QString &application) - : QSettingsPrivate(QSettings::NativeFormat, scope, organization, application) + const QString &application, REGSAM access) + : QSettingsPrivate(QSettings::NativeFormat, scope, organization, application), + access(access) { deleteWriteHandleOnExit = false; @@ -405,23 +424,24 @@ QWinSettingsPrivate::QWinSettingsPrivate(QSettings::Scope scope, const QString & if (scope == QSettings::UserScope) { if (!application.isEmpty()) - regList.append(RegistryKey(HKEY_CURRENT_USER, appPrefix, !regList.isEmpty())); + regList.append(RegistryKey(HKEY_CURRENT_USER, appPrefix, !regList.isEmpty(), access)); - regList.append(RegistryKey(HKEY_CURRENT_USER, orgPrefix, !regList.isEmpty())); + regList.append(RegistryKey(HKEY_CURRENT_USER, orgPrefix, !regList.isEmpty(), access)); } if (!application.isEmpty()) - regList.append(RegistryKey(HKEY_LOCAL_MACHINE, appPrefix, !regList.isEmpty())); + regList.append(RegistryKey(HKEY_LOCAL_MACHINE, appPrefix, !regList.isEmpty(), access)); - regList.append(RegistryKey(HKEY_LOCAL_MACHINE, orgPrefix, !regList.isEmpty())); + regList.append(RegistryKey(HKEY_LOCAL_MACHINE, orgPrefix, !regList.isEmpty(), access)); } if (regList.isEmpty()) setStatus(QSettings::AccessError); } -QWinSettingsPrivate::QWinSettingsPrivate(QString rPath) - : QSettingsPrivate(QSettings::NativeFormat) +QWinSettingsPrivate::QWinSettingsPrivate(QString rPath, REGSAM access) + : QSettingsPrivate(QSettings::NativeFormat), + access(access) { deleteWriteHandleOnExit = false; @@ -460,9 +480,9 @@ QWinSettingsPrivate::QWinSettingsPrivate(QString rPath) } if (rPath.length() == keyLength) - regList.append(RegistryKey(keyName, QString(), false)); + regList.append(RegistryKey(keyName, QString(), false, access)); else if (rPath[keyLength] == QLatin1Char('\\')) - regList.append(RegistryKey(keyName, rPath.mid(keyLength+1), false)); + regList.append(RegistryKey(keyName, rPath.mid(keyLength+1), false, access)); } bool QWinSettingsPrivate::readKey(HKEY parentHandle, const QString &rSubKey, QVariant *value) const @@ -471,7 +491,7 @@ bool QWinSettingsPrivate::readKey(HKEY parentHandle, const QString &rSubKey, QVa QString rSubkeyPath = keyPath(rSubKey); // open a handle on the subkey - HKEY handle = openKey(parentHandle, KEY_READ, rSubkeyPath); + HKEY handle = openKey(parentHandle, KEY_READ, rSubkeyPath, access); if (handle == 0) return false; @@ -604,16 +624,16 @@ void QWinSettingsPrivate::remove(const QString &uKey) // try to delete value bar in key foo LONG res; - HKEY handle = openKey(writeHandle(), registryPermissions, keyPath(rKey)); + HKEY handle = openKey(writeHandle(), registryPermissions, keyPath(rKey), access); if (handle != 0) { res = RegDeleteValue(handle, reinterpret_cast<const wchar_t *>(keyName(rKey).utf16())); RegCloseKey(handle); } // try to delete key foo/bar and all subkeys - handle = openKey(writeHandle(), registryPermissions, rKey); + handle = openKey(writeHandle(), registryPermissions, rKey, access); if (handle != 0) { - deleteChildGroups(handle); + deleteChildGroups(handle, access); if (rKey.isEmpty()) { QStringList childKeys = childKeysOrGroups(handle, QSettingsPrivate::ChildKeys); @@ -661,7 +681,7 @@ void QWinSettingsPrivate::set(const QString &uKey, const QVariant &value) QString rKey = escapedKey(uKey); - HKEY handle = createOrOpenKey(writeHandle(), registryPermissions, keyPath(rKey)); + HKEY handle = createOrOpenKey(writeHandle(), registryPermissions, keyPath(rKey), access); if (handle == 0) { setStatus(QSettings::AccessError); return; @@ -775,13 +795,13 @@ QStringList QWinSettingsPrivate::children(const QString &uKey, ChildSpec spec) c HKEY parent_handle = regList.at(i).handle(); if (parent_handle == 0) continue; - HKEY handle = openKey(parent_handle, KEY_READ, rKey); + HKEY handle = openKey(parent_handle, KEY_READ, rKey, access); if (handle == 0) continue; if (spec == AllKeys) { NameSet keys; - allKeys(handle, QLatin1String(""), &keys); + allKeys(handle, QLatin1String(""), &keys, access); mergeKeySets(&result, keys); } else { // ChildGroups or ChildKeys QStringList names = childKeysOrGroups(handle, spec); @@ -836,20 +856,26 @@ bool QWinSettingsPrivate::isWritable() const QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::Scope scope, const QString &organization, const QString &application) { - if (format == QSettings::NativeFormat) { + if (format == QSettings::NativeFormat) return new QWinSettingsPrivate(scope, organization, application); - } else { + else if (format == QSettings::Registry32Format) + return new QWinSettingsPrivate(scope, organization, application, KEY_WOW64_32KEY); + else if (format == QSettings::Registry64Format) + return new QWinSettingsPrivate(scope, organization, application, KEY_WOW64_64KEY); + else return new QConfFileSettingsPrivate(format, scope, organization, application); - } } QSettingsPrivate *QSettingsPrivate::create(const QString &fileName, QSettings::Format format) { - if (format == QSettings::NativeFormat) { + if (format == QSettings::NativeFormat) return new QWinSettingsPrivate(fileName); - } else { + else if (format == QSettings::Registry32Format) + return new QWinSettingsPrivate(fileName, KEY_WOW64_32KEY); + else if (format == QSettings::Registry64Format) + return new QWinSettingsPrivate(fileName, KEY_WOW64_64KEY); + else return new QConfFileSettingsPrivate(fileName, format); - } } QT_END_NAMESPACE diff --git a/src/corelib/io/qstandardpaths.cpp b/src/corelib/io/qstandardpaths.cpp index 8828e09e8f..0137e859e7 100644 --- a/src/corelib/io/qstandardpaths.cpp +++ b/src/corelib/io/qstandardpaths.cpp @@ -277,13 +277,13 @@ QT_BEGIN_NAMESPACE \header \li Path type \li Android \li iOS \row \li DesktopLocation \li "<APPROOT>/files" - \li "<APPROOT>/<APPDIR>" (not writable) + \li "<APPROOT>/Documents/Desktop" \row \li DocumentsLocation \li "<USER>/Documents", "<USER>/<APPNAME>/Documents" \li "<APPROOT>/Documents" \row \li FontsLocation \li "/system/fonts" (not writable) - \li "<APPROOT>/Documents/.fonts" + \li "<APPROOT>/Library/Fonts" \row \li ApplicationsLocation \li not supported (directory not readable) \li not supported @@ -301,7 +301,7 @@ QT_BEGIN_NAMESPACE \li "<APPROOT>/tmp" \row \li HomeLocation \li "<APPROOT>/files" - \li "<APPROOT>/<APPDIR>" (not writable) + \li "<APPROOT>" (not writable) \row \li DataLocation \li "<APPROOT>/files", "<USER>/<APPNAME>/files" \li "<APPROOT>/Library/Application Support" @@ -316,13 +316,13 @@ QT_BEGIN_NAMESPACE \li not supported \row \li ConfigLocation \li "<APPROOT>/files/settings" - \li "<APPROOT>/Documents" + \li "<APPROOT>/Library/Preferences" \row \li GenericConfigLocation \li "<APPROOT>/files/settings" (there is no shared settings) - \li "<APPROOT>/Documents" + \li "<APPROOT>/Library/Preferences" \row \li DownloadLocation \li "<USER>/Downloads", "<USER>/<APPNAME>/Downloads" - \li "<APPROOT>/Documents/Download" + \li "<APPROOT>/Documents/Downloads" \row \li GenericCacheLocation \li "<APPROOT>/cache" (there is no shared cache) \li "<APPROOT>/Library/Caches" @@ -331,7 +331,7 @@ QT_BEGIN_NAMESPACE \li "<APPROOT>/Library/Application Support" \row \li AppConfigLocation \li "<APPROOT>/files/settings" - \li "<APPROOT>/Documents" + \li "<APPROOT>/Library/Preferences/<APPNAME>" \row \li AppLocalDataLocation \li "<APPROOT>/files", "<USER>/<APPNAME>/files" \li "<APPROOT>/Library/Application Support" @@ -555,7 +555,7 @@ QString QStandardPaths::findExecutable(const QString &executableName, const QStr an empty QString if no relevant location can be found. */ -#if !defined(Q_OS_OSX) && !defined(QT_BOOTSTRAPPED) +#if !defined(Q_OS_MAC) && !defined(QT_BOOTSTRAPPED) QString QStandardPaths::displayName(StandardLocation type) { switch (type) { diff --git a/src/corelib/io/qstandardpaths_ios.mm b/src/corelib/io/qstandardpaths_ios.mm deleted file mode 100644 index eb85e2fd23..0000000000 --- a/src/corelib/io/qstandardpaths_ios.mm +++ /dev/null @@ -1,133 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#import <UIKit/UIKit.h> - -#include "qstandardpaths.h" - -#ifndef QT_NO_STANDARDPATHS - -QT_BEGIN_NAMESPACE - -static QString pathForDirectory(NSSearchPathDirectory directory) -{ - return QString::fromNSString( - [NSSearchPathForDirectoriesInDomains(directory, NSUserDomainMask, YES) lastObject]); -} - -static QString bundlePath() -{ - return QString::fromNSString([[NSBundle mainBundle] bundlePath]); -} - -QString QStandardPaths::writableLocation(StandardLocation type) -{ - QString location; - - switch (type) { - case DocumentsLocation: - location = pathForDirectory(NSDocumentDirectory); - break; - case FontsLocation: - location = pathForDirectory(NSDocumentDirectory) + QLatin1String("/.fonts"); - break; - case ApplicationsLocation: - // NSApplicationDirectory points to a non-existing write-protected path. - break; - case MusicLocation: - // NSMusicDirectory points to a non-existing write-protected path. Use sensible fallback. - location = pathForDirectory(NSDocumentDirectory) + QLatin1String("/Music"); - break; - case MoviesLocation: - // NSMoviesDirectory points to a non-existing write-protected path. Use sensible fallback. - location = pathForDirectory(NSDocumentDirectory) + QLatin1String("/Movies"); - break; - case PicturesLocation: - // NSPicturesDirectory points to a non-existing write-protected path. Use sensible fallback. - location = pathForDirectory(NSDocumentDirectory) + QLatin1String("/Pictures"); - break; - case TempLocation: - location = QString::fromNSString(NSTemporaryDirectory()); - break; - case DesktopLocation: - case HomeLocation: - location = bundlePath(); - break; - case AppDataLocation: - case AppLocalDataLocation: - location = pathForDirectory(NSApplicationSupportDirectory); - break; - case GenericDataLocation: - location = pathForDirectory(NSDocumentDirectory); - break; - case CacheLocation: - case GenericCacheLocation: - location = pathForDirectory(NSCachesDirectory); - break; - case ConfigLocation: - case GenericConfigLocation: - case AppConfigLocation: - location = pathForDirectory(NSDocumentDirectory); - break; - case DownloadLocation: - // NSDownloadsDirectory points to a non-existing write-protected path. - location = pathForDirectory(NSDocumentDirectory) + QLatin1String("/Download"); - break; - case RuntimeLocation: - break; - default: - break; - } - - return location; -} - -QStringList QStandardPaths::standardLocations(StandardLocation type) -{ - QStringList dirs; - - switch (type) { - case PicturesLocation: - dirs << writableLocation(PicturesLocation) << QLatin1String("assets-library://"); - break; - default: - dirs << writableLocation(type); - break; - } - - return dirs; -} - -QT_END_NAMESPACE - -#endif // QT_NO_STANDARDPATHS diff --git a/src/corelib/io/qstandardpaths_mac.mm b/src/corelib/io/qstandardpaths_mac.mm index d6126ce1c3..57d365e916 100644 --- a/src/corelib/io/qstandardpaths_mac.mm +++ b/src/corelib/io/qstandardpaths_mac.mm @@ -32,6 +32,9 @@ ****************************************************************************/ #include "qstandardpaths.h" + +#ifndef QT_NO_STANDARDPATHS + #include <qdir.h> #include <qurl.h> #include <private/qcore_mac_p.h> @@ -40,63 +43,47 @@ #include <qcoreapplication.h> #endif -#include <CoreFoundation/CoreFoundation.h> -#include <ApplicationServices/ApplicationServices.h> +#import <Foundation/Foundation.h> QT_BEGIN_NAMESPACE -/* - Translates a QStandardPaths::StandardLocation into the mac equivalent. -*/ -OSType translateLocation(QStandardPaths::StandardLocation type) +static QString pathForDirectory(NSSearchPathDirectory directory, + NSSearchPathDomainMask mask) +{ + return QString::fromNSString( + [NSSearchPathForDirectoriesInDomains(directory, mask, YES) lastObject]); +} + +static NSSearchPathDirectory searchPathDirectory(QStandardPaths::StandardLocation type) { switch (type) { - case QStandardPaths::ConfigLocation: - case QStandardPaths::GenericConfigLocation: - case QStandardPaths::AppConfigLocation: - return kPreferencesFolderType; case QStandardPaths::DesktopLocation: - return kDesktopFolderType; + return NSDesktopDirectory; case QStandardPaths::DocumentsLocation: - return kDocumentsFolderType; - case QStandardPaths::FontsLocation: - // There are at least two different font directories on the mac: /Library/Fonts and ~/Library/Fonts. - // To select a specific one we have to specify a different first parameter when calling FSFindFolder. - return kFontsFolderType; + return NSDocumentDirectory; case QStandardPaths::ApplicationsLocation: - return kApplicationsFolderType; + return NSApplicationDirectory; case QStandardPaths::MusicLocation: - return kMusicDocumentsFolderType; + return NSMusicDirectory; case QStandardPaths::MoviesLocation: - return kMovieDocumentsFolderType; + return NSMoviesDirectory; case QStandardPaths::PicturesLocation: - return kPictureDocumentsFolderType; - case QStandardPaths::TempLocation: - return kTemporaryFolderType; + return NSPicturesDirectory; case QStandardPaths::GenericDataLocation: case QStandardPaths::RuntimeLocation: case QStandardPaths::AppDataLocation: case QStandardPaths::AppLocalDataLocation: - return kApplicationSupportFolderType; + return NSApplicationSupportDirectory; case QStandardPaths::GenericCacheLocation: case QStandardPaths::CacheLocation: - return kCachedDataFolderType; + return NSCachesDirectory; + case QStandardPaths::DownloadLocation: + return NSDownloadsDirectory; default: - return kDesktopFolderType; + return (NSSearchPathDirectory)0; } } -/* - Constructs a full unicode path from a FSRef. -*/ -static QString getFullPath(const FSRef &ref) -{ - QByteArray ba(2048, 0); - if (FSRefMakePath(&ref, reinterpret_cast<UInt8 *>(ba.data()), ba.size()) == noErr) - return QString::fromUtf8(ba.constData()).normalized(QString::NormalizationForm_C); - return QString(); -} - static void appendOrganizationAndApp(QString &path) { #ifndef QT_BOOTSTRAPPED @@ -111,28 +98,65 @@ static void appendOrganizationAndApp(QString &path) #endif } -static QString macLocation(QStandardPaths::StandardLocation type, short domain) +static QString baseWritableLocation(QStandardPaths::StandardLocation type, + NSSearchPathDomainMask mask = NSUserDomainMask, + bool appendOrgAndApp = false) { - // https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSFileManager_Class/index.html - if (type == QStandardPaths::DownloadLocation) { - NSFileManager *fileManager = [NSFileManager defaultManager]; - NSURL *url = [fileManager URLForDirectory:NSDownloadsDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil]; - if (!url) - return QString(); - return QString::fromNSString([url path]); + QString path; + const NSSearchPathDirectory dir = searchPathDirectory(type); + switch (type) { + case QStandardPaths::HomeLocation: + path = QDir::homePath(); + break; + case QStandardPaths::TempLocation: + path = QDir::tempPath(); + break; +#ifdef Q_OS_IOS + // These locations point to non-existing write-protected paths. Use sensible fallbacks. + case QStandardPaths::MusicLocation: + path = pathForDirectory(NSDocumentDirectory, mask) + QLatin1String("/Music"); + break; + case QStandardPaths::MoviesLocation: + path = pathForDirectory(NSDocumentDirectory, mask) + QLatin1String("/Movies"); + break; + case QStandardPaths::PicturesLocation: + path = pathForDirectory(NSDocumentDirectory, mask) + QLatin1String("/Pictures"); + break; + case QStandardPaths::DownloadLocation: + path = pathForDirectory(NSDocumentDirectory, mask) + QLatin1String("/Downloads"); + break; + case QStandardPaths::DesktopLocation: + path = pathForDirectory(NSDocumentDirectory, mask) + QLatin1String("/Desktop"); + break; + case QStandardPaths::ApplicationsLocation: + break; +#endif + case QStandardPaths::FontsLocation: + path = pathForDirectory(NSLibraryDirectory, mask) + QLatin1String("/Fonts"); + break; + case QStandardPaths::ConfigLocation: + case QStandardPaths::GenericConfigLocation: + case QStandardPaths::AppConfigLocation: + path = pathForDirectory(NSLibraryDirectory, mask) + QLatin1String("/Preferences"); + break; + default: + path = pathForDirectory(dir, mask); + break; } - // http://developer.apple.com/documentation/Carbon/Reference/Folder_Manager/Reference/reference.html - FSRef ref; - OSErr err = FSFindFolder(domain, translateLocation(type), false, &ref); - if (err) - return QString(); - - QString path = getFullPath(ref); + if (appendOrgAndApp) { + switch (type) { + case QStandardPaths::AppDataLocation: + case QStandardPaths::AppLocalDataLocation: + case QStandardPaths::AppConfigLocation: + case QStandardPaths::CacheLocation: + appendOrganizationAndApp(path); + break; + default: + break; + } + } - if (type == QStandardPaths::AppDataLocation || type == QStandardPaths::AppLocalDataLocation || - type == QStandardPaths::CacheLocation || type == QStandardPaths::AppConfigLocation) - appendOrganizationAndApp(path); return path; } @@ -167,31 +191,32 @@ QString QStandardPaths::writableLocation(StandardLocation type) } } - switch (type) { - case HomeLocation: - return QDir::homePath(); - case TempLocation: - return QDir::tempPath(); - case GenericDataLocation: - case AppDataLocation: - case AppLocalDataLocation: - case GenericCacheLocation: - case CacheLocation: - case RuntimeLocation: - return macLocation(type, kUserDomain); - default: - return macLocation(type, kOnAppropriateDisk); - } + return baseWritableLocation(type, NSUserDomainMask, true); } QStringList QStandardPaths::standardLocations(StandardLocation type) { QStringList dirs; - if (type == GenericDataLocation || type == AppDataLocation || type == AppLocalDataLocation || type == GenericCacheLocation || type == CacheLocation) { - const QString path = macLocation(type, kOnAppropriateDisk); - if (!path.isEmpty()) - dirs.append(path); +#ifdef Q_OS_IOS + if (type == PicturesLocation) + dirs << writableLocation(PicturesLocation) << QLatin1String("assets-library://"); +#endif + + if (type == GenericDataLocation || type == FontsLocation || type == ApplicationsLocation + || type == AppDataLocation || type == AppLocalDataLocation + || type == GenericCacheLocation || type == CacheLocation) { + QList<NSSearchPathDomainMask> masks; + masks << NSLocalDomainMask; + if (type == FontsLocation || type == GenericCacheLocation) + masks << NSSystemDomainMask; + + for (QList<NSSearchPathDomainMask>::const_iterator it = masks.begin(); + it != masks.end(); ++it) { + const QString path = baseWritableLocation(type, *it, true); + if (!path.isEmpty() && !dirs.contains(path)) + dirs.append(path); + } } if (type == AppDataLocation || type == AppLocalDataLocation) { @@ -219,28 +244,41 @@ QStringList QStandardPaths::standardLocations(StandardLocation type) } } const QString localDir = writableLocation(type); - dirs.prepend(localDir); + if (!localDir.isEmpty()) + dirs.prepend(localDir); return dirs; } #ifndef QT_BOOTSTRAPPED QString QStandardPaths::displayName(StandardLocation type) { + // Use "Home" instead of the user's Unix username if (QStandardPaths::HomeLocation == type) return QCoreApplication::translate("QStandardPaths", "Home"); - FSRef ref; - OSErr err = FSFindFolder(kOnAppropriateDisk, translateLocation(type), false, &ref); - if (err) - return QString(); + // The temporary directory returned by the old Carbon APIs is ~/Library/Caches/TemporaryItems, + // the display name of which ("TemporaryItems") isn't translated by the system. The standard + // temporary directory has no reasonable display name either, so use something more sensible. + if (QStandardPaths::TempLocation == type) + return QCoreApplication::translate("QStandardPaths", "Temporary Items"); - QCFString displayName; - err = LSCopyDisplayNameForRef(&ref, &displayName); - if (err) - return QString(); + // standardLocations() may return an empty list on some platforms + if (QStandardPaths::ApplicationsLocation == type) + return QCoreApplication::translate("QStandardPaths", "Applications"); - return static_cast<QString>(displayName); + if (QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, + standardLocations(type).first().toCFString(), + kCFURLPOSIXPathStyle, true)) { + QCFString name; + CFURLCopyResourcePropertyForKey(url, kCFURLLocalizedNameKey, &name, NULL); + if (name && CFStringGetLength(name)) + return QString::fromCFString(name); + } + + return QFileInfo(baseWritableLocation(type)).fileName(); } #endif QT_END_NAMESPACE + +#endif // QT_NO_STANDARDPATHS diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 2719019d30..53725c7f90 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -2686,7 +2686,7 @@ void QCoreApplication::removeLibraryPath(const QString &path) i.e. MSG or XCB event structs. \note Native event filters will be disabled when the application the - Qt::AA_MacPluginApplication attribute is set. + Qt::AA_PluginApplication attribute is set. For maximum portability, you should always try to use QEvent and QObject::installEventFilter() whenever possible. @@ -2697,8 +2697,8 @@ void QCoreApplication::removeLibraryPath(const QString &path) */ void QCoreApplication::installNativeEventFilter(QAbstractNativeEventFilter *filterObj) { - if (QCoreApplication::testAttribute(Qt::AA_MacPluginApplication)) { - qWarning("Native event filters are not applied when the Qt::AA_MacPluginApplication attribute is set"); + if (QCoreApplication::testAttribute(Qt::AA_PluginApplication)) { + qWarning("Native event filters are not applied when the Qt::AA_PluginApplication attribute is set"); return; } diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h index 53da4a849b..d9de828fa1 100644 --- a/src/corelib/kernel/qcoreevent.h +++ b/src/corelib/kernel/qcoreevent.h @@ -276,6 +276,8 @@ public: PlatformSurface = 217, // Platform surface created or about to be destroyed + Pointer = 218, // QQuickPointerEvent; ### Qt 6: QPointerEvent + // 512 reserved for Qt Jambi's MetaCall event // 513 reserved for Qt Jambi's DeleteOnMainThread event diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp index 021e137273..c2302cbc5e 100644 --- a/src/corelib/kernel/qmetaobjectbuilder.cpp +++ b/src/corelib/kernel/qmetaobjectbuilder.cpp @@ -36,6 +36,7 @@ #include "qobject_p.h" #include "qmetaobject_p.h" +#include <vector> #include <stdlib.h> QT_BEGIN_NAMESPACE @@ -88,7 +89,6 @@ static inline Q_DECL_UNUSED const QMetaObjectPrivate *priv(const uint* data) class QMetaMethodBuilderPrivate { public: - QMetaMethodBuilderPrivate() {} // for QVector, don't use QMetaMethodBuilderPrivate (QMetaMethod::MethodType _methodType, const QByteArray& _signature, @@ -145,7 +145,6 @@ Q_DECLARE_TYPEINFO(QMetaMethodBuilderPrivate, Q_MOVABLE_TYPE); class QMetaPropertyBuilderPrivate { public: - QMetaPropertyBuilderPrivate() {} // for QVector, don't use QMetaPropertyBuilderPrivate (const QByteArray& _name, const QByteArray& _type, int notifierIdx=-1, int _revision = 0) @@ -184,7 +183,6 @@ Q_DECLARE_TYPEINFO(QMetaPropertyBuilderPrivate, Q_MOVABLE_TYPE); class QMetaEnumBuilderPrivate { public: - QMetaEnumBuilderPrivate() {} // for QVector, don't use QMetaEnumBuilderPrivate(const QByteArray& _name) : name(_name), isFlag(false) { @@ -213,20 +211,20 @@ public: QByteArray className; const QMetaObject *superClass; QMetaObjectBuilder::StaticMetacallFunction staticMetacallFunction; - QVector<QMetaMethodBuilderPrivate> methods; - QVector<QMetaMethodBuilderPrivate> constructors; - QVector<QMetaPropertyBuilderPrivate> properties; + std::vector<QMetaMethodBuilderPrivate> methods; + std::vector<QMetaMethodBuilderPrivate> constructors; + std::vector<QMetaPropertyBuilderPrivate> properties; QList<QByteArray> classInfoNames; QList<QByteArray> classInfoValues; - QVector<QMetaEnumBuilderPrivate> enumerators; + std::vector<QMetaEnumBuilderPrivate> enumerators; QList<const QMetaObject *> relatedMetaObjects; int flags; }; bool QMetaObjectBuilderPrivate::hasRevisionedProperties() const { - for (int i = 0; i < properties.size(); ++i) { - if (properties.at(i).revision) + for (const auto &property : properties) { + if (property.revision) return true; } return false; @@ -234,8 +232,8 @@ bool QMetaObjectBuilderPrivate::hasRevisionedProperties() const bool QMetaObjectBuilderPrivate::hasRevisionedMethods() const { - for (int i = 0; i < methods.size(); ++i) { - if (methods.at(i).revision) + for (const auto &method : methods) { + if (method.revision) return true; } return false; @@ -353,7 +351,7 @@ void QMetaObjectBuilder::setFlags(MetaObjectFlags flags) */ int QMetaObjectBuilder::methodCount() const { - return d->methods.size(); + return int(d->methods.size()); } /*! @@ -363,7 +361,7 @@ int QMetaObjectBuilder::methodCount() const */ int QMetaObjectBuilder::constructorCount() const { - return d->constructors.size(); + return int(d->constructors.size()); } /*! @@ -374,7 +372,7 @@ int QMetaObjectBuilder::constructorCount() const */ int QMetaObjectBuilder::propertyCount() const { - return d->properties.size(); + return int(d->properties.size()); } /*! @@ -386,7 +384,7 @@ int QMetaObjectBuilder::propertyCount() const */ int QMetaObjectBuilder::enumeratorCount() const { - return d->enumerators.size(); + return int(d->enumerators.size()); } /*! @@ -427,8 +425,8 @@ int QMetaObjectBuilder::relatedMetaObjectCount() const */ QMetaMethodBuilder QMetaObjectBuilder::addMethod(const QByteArray& signature) { - int index = d->methods.size(); - d->methods.append(QMetaMethodBuilderPrivate(QMetaMethod::Method, signature)); + int index = int(d->methods.size()); + d->methods.push_back(QMetaMethodBuilderPrivate(QMetaMethod::Method, signature)); return QMetaMethodBuilder(this, index); } @@ -444,8 +442,8 @@ QMetaMethodBuilder QMetaObjectBuilder::addMethod(const QByteArray& signature) QMetaMethodBuilder QMetaObjectBuilder::addMethod (const QByteArray& signature, const QByteArray& returnType) { - int index = d->methods.size(); - d->methods.append(QMetaMethodBuilderPrivate + int index = int(d->methods.size()); + d->methods.push_back(QMetaMethodBuilderPrivate (QMetaMethod::Method, signature, returnType)); return QMetaMethodBuilder(this, index); } @@ -491,8 +489,8 @@ QMetaMethodBuilder QMetaObjectBuilder::addMethod(const QMetaMethod& prototype) */ QMetaMethodBuilder QMetaObjectBuilder::addSlot(const QByteArray& signature) { - int index = d->methods.size(); - d->methods.append(QMetaMethodBuilderPrivate(QMetaMethod::Slot, signature)); + int index = int(d->methods.size()); + d->methods.push_back(QMetaMethodBuilderPrivate(QMetaMethod::Slot, signature)); return QMetaMethodBuilder(this, index); } @@ -506,8 +504,8 @@ QMetaMethodBuilder QMetaObjectBuilder::addSlot(const QByteArray& signature) */ QMetaMethodBuilder QMetaObjectBuilder::addSignal(const QByteArray& signature) { - int index = d->methods.size(); - d->methods.append(QMetaMethodBuilderPrivate + int index = int(d->methods.size()); + d->methods.push_back(QMetaMethodBuilderPrivate (QMetaMethod::Signal, signature, QByteArray("void"), QMetaMethod::Public)); return QMetaMethodBuilder(this, index); } @@ -523,9 +521,9 @@ QMetaMethodBuilder QMetaObjectBuilder::addSignal(const QByteArray& signature) */ QMetaMethodBuilder QMetaObjectBuilder::addConstructor(const QByteArray& signature) { - int index = d->constructors.size(); - d->constructors.append(QMetaMethodBuilderPrivate(QMetaMethod::Constructor, signature, - /*returnType=*/QByteArray())); + int index = int(d->constructors.size()); + d->constructors.push_back(QMetaMethodBuilderPrivate(QMetaMethod::Constructor, signature, + /*returnType=*/QByteArray())); return QMetaMethodBuilder(this, -(index + 1)); } @@ -564,8 +562,8 @@ QMetaMethodBuilder QMetaObjectBuilder::addConstructor(const QMetaMethod& prototy QMetaPropertyBuilder QMetaObjectBuilder::addProperty (const QByteArray& name, const QByteArray& type, int notifierId) { - int index = d->properties.size(); - d->properties.append(QMetaPropertyBuilderPrivate(name, type, notifierId)); + int index = int(d->properties.size()); + d->properties.push_back(QMetaPropertyBuilderPrivate(name, type, notifierId)); return QMetaPropertyBuilder(this, index); } @@ -615,8 +613,8 @@ QMetaPropertyBuilder QMetaObjectBuilder::addProperty(const QMetaProperty& protot */ QMetaEnumBuilder QMetaObjectBuilder::addEnumerator(const QByteArray& name) { - int index = d->enumerators.size(); - d->enumerators.append(QMetaEnumBuilderPrivate(name)); + int index = int(d->enumerators.size()); + d->enumerators.push_back(QMetaEnumBuilderPrivate(name)); return QMetaEnumBuilder(this, index); } @@ -762,7 +760,7 @@ void QMetaObjectBuilder::addMetaObject */ QMetaMethodBuilder QMetaObjectBuilder::method(int index) const { - if (index >= 0 && index < d->methods.size()) + if (uint(index) < d->methods.size()) return QMetaMethodBuilder(this, index); else return QMetaMethodBuilder(); @@ -775,7 +773,7 @@ QMetaMethodBuilder QMetaObjectBuilder::method(int index) const */ QMetaMethodBuilder QMetaObjectBuilder::constructor(int index) const { - if (index >= 0 && index < d->constructors.size()) + if (uint(index) < d->constructors.size()) return QMetaMethodBuilder(this, -(index + 1)); else return QMetaMethodBuilder(); @@ -788,7 +786,7 @@ QMetaMethodBuilder QMetaObjectBuilder::constructor(int index) const */ QMetaPropertyBuilder QMetaObjectBuilder::property(int index) const { - if (index >= 0 && index < d->properties.size()) + if (uint(index) < d->properties.size()) return QMetaPropertyBuilder(this, index); else return QMetaPropertyBuilder(); @@ -802,7 +800,7 @@ QMetaPropertyBuilder QMetaObjectBuilder::property(int index) const */ QMetaEnumBuilder QMetaObjectBuilder::enumerator(int index) const { - if (index >= 0 && index < d->enumerators.size()) + if (uint(index) < d->enumerators.size()) return QMetaEnumBuilder(this, index); else return QMetaEnumBuilder(); @@ -866,15 +864,15 @@ QByteArray QMetaObjectBuilder::classInfoValue(int index) const */ void QMetaObjectBuilder::removeMethod(int index) { - if (index >= 0 && index < d->methods.size()) { - d->methods.removeAt(index); - for (int prop = 0; prop < d->properties.size(); ++prop) { + if (uint(index) < d->methods.size()) { + d->methods.erase(d->methods.begin() + index); + for (auto &property : d->properties) { // Adjust the indices of property notify signal references. - if (d->properties[prop].notifySignal == index) { - d->properties[prop].notifySignal = -1; - d->properties[prop].setFlag(Notify, false); - } else if (d->properties[prop].notifySignal > index) - (d->properties[prop].notifySignal)--; + if (property.notifySignal == index) { + property.notifySignal = -1; + property.setFlag(Notify, false); + } else if (property.notifySignal > index) + property.notifySignal--; } } } @@ -888,8 +886,8 @@ void QMetaObjectBuilder::removeMethod(int index) */ void QMetaObjectBuilder::removeConstructor(int index) { - if (index >= 0 && index < d->constructors.size()) - d->constructors.removeAt(index); + if (uint(index) < d->constructors.size()) + d->constructors.erase(d->constructors.begin() + index); } /*! @@ -900,8 +898,8 @@ void QMetaObjectBuilder::removeConstructor(int index) */ void QMetaObjectBuilder::removeProperty(int index) { - if (index >= 0 && index < d->properties.size()) - d->properties.removeAt(index); + if (uint(index) < d->properties.size()) + d->properties.erase(d->properties.begin() + index); } /*! @@ -913,8 +911,8 @@ void QMetaObjectBuilder::removeProperty(int index) */ void QMetaObjectBuilder::removeEnumerator(int index) { - if (index >= 0 && index < d->enumerators.size()) - d->enumerators.removeAt(index); + if (uint(index) < d->enumerators.size()) + d->enumerators.erase(d->enumerators.begin() + index); } /*! @@ -959,9 +957,9 @@ void QMetaObjectBuilder::removeRelatedMetaObject(int index) int QMetaObjectBuilder::indexOfMethod(const QByteArray& signature) { QByteArray sig = QMetaObject::normalizedSignature(signature); - for (int index = 0; index < d->methods.size(); ++index) { - if (sig == d->methods[index].signature) - return index; + for (const auto &method : d->methods) { + if (sig == method.signature) + return int(&method - &d->methods.front()); } return -1; } @@ -975,10 +973,9 @@ int QMetaObjectBuilder::indexOfMethod(const QByteArray& signature) int QMetaObjectBuilder::indexOfSignal(const QByteArray& signature) { QByteArray sig = QMetaObject::normalizedSignature(signature); - for (int index = 0; index < d->methods.size(); ++index) { - if (sig == d->methods[index].signature && - d->methods[index].methodType() == QMetaMethod::Signal) - return index; + for (const auto &method : d->methods) { + if (method.methodType() == QMetaMethod::Signal && sig == method.signature) + return int(&method - &d->methods.front()); } return -1; } @@ -992,10 +989,9 @@ int QMetaObjectBuilder::indexOfSignal(const QByteArray& signature) int QMetaObjectBuilder::indexOfSlot(const QByteArray& signature) { QByteArray sig = QMetaObject::normalizedSignature(signature); - for (int index = 0; index < d->methods.size(); ++index) { - if (sig == d->methods[index].signature && - d->methods[index].methodType() == QMetaMethod::Slot) - return index; + for (const auto &method : d->methods) { + if (method.methodType() == QMetaMethod::Slot && sig == method.signature) + return int(&method - &d->methods.front()); } return -1; } @@ -1009,9 +1005,9 @@ int QMetaObjectBuilder::indexOfSlot(const QByteArray& signature) int QMetaObjectBuilder::indexOfConstructor(const QByteArray& signature) { QByteArray sig = QMetaObject::normalizedSignature(signature); - for (int index = 0; index < d->constructors.size(); ++index) { - if (sig == d->constructors[index].signature) - return index; + for (const auto &constructor : d->constructors) { + if (sig == constructor.signature) + return int(&constructor - &d->constructors.front()); } return -1; } @@ -1024,9 +1020,9 @@ int QMetaObjectBuilder::indexOfConstructor(const QByteArray& signature) */ int QMetaObjectBuilder::indexOfProperty(const QByteArray& name) { - for (int index = 0; index < d->properties.size(); ++index) { - if (name == d->properties[index].name) - return index; + for (const auto &property : d->properties) { + if (name == property.name) + return int(&property - &d->properties.front()); } return -1; } @@ -1039,9 +1035,9 @@ int QMetaObjectBuilder::indexOfProperty(const QByteArray& name) */ int QMetaObjectBuilder::indexOfEnumerator(const QByteArray& name) { - for (int index = 0; index < d->enumerators.size(); ++index) { - if (name == d->enumerators[index].name) - return index; + for (const auto &enumerator : d->enumerators) { + if (name == enumerator.name) + return int(&enumerator - &d->enumerators.front()); } return -1; } @@ -1155,11 +1151,11 @@ void QMetaStringTable::writeBlob(char *out) const // Returns the sum of all parameters (including return type) for the given // \a methods. This is needed for calculating the size of the methods' // parameter type/name meta-data. -static int aggregateParameterCount(const QVector<QMetaMethodBuilderPrivate> &methods) +static int aggregateParameterCount(const std::vector<QMetaMethodBuilderPrivate> &methods) { int sum = 0; - for (int i = 0; i < methods.size(); ++i) - sum += methods.at(i).parameterCount() + 1; // +1 for return type + for (const auto &method : methods) + sum += method.parameterCount() + 1; // +1 for return type return sum; } @@ -1200,8 +1196,8 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, = reinterpret_cast<QMetaObjectPrivate *>(buf + size); int pmetaSize = size; dataIndex = MetaObjectPrivateFieldCount; - for (index = 0; index < d->properties.size(); ++index) { - if (d->properties[index].notifySignal != -1) { + for (const auto &property : d->properties) { + if (property.notifySignal != -1) { hasNotifySignals = true; break; } @@ -1209,8 +1205,8 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, int methodParametersDataSize = ((aggregateParameterCount(d->methods) + aggregateParameterCount(d->constructors)) * 2) // types and parameter names - - d->methods.size() // return "parameters" don't have names - - d->constructors.size(); // "this" parameters don't have names + - int(d->methods.size()) // return "parameters" don't have names + - int(d->constructors.size()); // "this" parameters don't have names if (buf) { Q_STATIC_ASSERT_X(QMetaObjectPrivate::OutputRevision == 7, "QMetaObjectBuilder should generate the same version as moc"); pmeta->revision = QMetaObjectPrivate::OutputRevision; @@ -1222,51 +1218,49 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, pmeta->classInfoData = dataIndex; dataIndex += 2 * d->classInfoNames.size(); - pmeta->methodCount = d->methods.size(); + pmeta->methodCount = int(d->methods.size()); pmeta->methodData = dataIndex; - dataIndex += 5 * d->methods.size(); + dataIndex += 5 * int(d->methods.size()); if (hasRevisionedMethods) - dataIndex += d->methods.size(); + dataIndex += int(d->methods.size()); paramsIndex = dataIndex; dataIndex += methodParametersDataSize; - pmeta->propertyCount = d->properties.size(); + pmeta->propertyCount = int(d->properties.size()); pmeta->propertyData = dataIndex; - dataIndex += 3 * d->properties.size(); + dataIndex += 3 * int(d->properties.size()); if (hasNotifySignals) - dataIndex += d->properties.size(); + dataIndex += int(d->properties.size()); if (hasRevisionedProperties) - dataIndex += d->properties.size(); + dataIndex += int(d->properties.size()); - pmeta->enumeratorCount = d->enumerators.size(); + pmeta->enumeratorCount = int(d->enumerators.size()); pmeta->enumeratorData = dataIndex; - dataIndex += 4 * d->enumerators.size(); + dataIndex += 4 * int(d->enumerators.size()); - pmeta->constructorCount = d->constructors.size(); + pmeta->constructorCount = int(d->constructors.size()); pmeta->constructorData = dataIndex; - dataIndex += 5 * d->constructors.size(); + dataIndex += 5 * int(d->constructors.size()); } else { - dataIndex += 2 * d->classInfoNames.size(); - dataIndex += 5 * d->methods.size(); + dataIndex += 2 * int(d->classInfoNames.size()); + dataIndex += 5 * int(d->methods.size()); if (hasRevisionedMethods) - dataIndex += d->methods.size(); + dataIndex += int(d->methods.size()); paramsIndex = dataIndex; dataIndex += methodParametersDataSize; - dataIndex += 3 * d->properties.size(); + dataIndex += 3 * int(d->properties.size()); if (hasNotifySignals) - dataIndex += d->properties.size(); + dataIndex += int(d->properties.size()); if (hasRevisionedProperties) - dataIndex += d->properties.size(); - dataIndex += 4 * d->enumerators.size(); - dataIndex += 5 * d->constructors.size(); + dataIndex += int(d->properties.size()); + dataIndex += 4 * int(d->enumerators.size()); + dataIndex += 5 * int(d->constructors.size()); } // Allocate space for the enumerator key names and values. enumIndex = dataIndex; - for (index = 0; index < d->enumerators.size(); ++index) { - QMetaEnumBuilderPrivate *enumerator = &(d->enumerators[index]); - dataIndex += 2 * enumerator->keys.size(); - } + for (const auto &enumerator : d->enumerators) + dataIndex += 2 * enumerator.keys.size(); // Zero terminator at the end of the data offset table. ++dataIndex; @@ -1305,44 +1299,41 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, // Output the methods in the class. Q_ASSERT(!buf || dataIndex == pmeta->methodData); - for (index = 0; index < d->methods.size(); ++index) { - QMetaMethodBuilderPrivate *method = &(d->methods[index]); - int name = strings.enter(method->name()); - int argc = method->parameterCount(); - int tag = strings.enter(method->tag); - int attrs = method->attributes; + for (const auto &method : d->methods) { + int name = strings.enter(method.name()); + int argc = method.parameterCount(); + int tag = strings.enter(method.tag); + int attrs = method.attributes; if (buf) { data[dataIndex] = name; data[dataIndex + 1] = argc; data[dataIndex + 2] = paramsIndex; data[dataIndex + 3] = tag; data[dataIndex + 4] = attrs; - if (method->methodType() == QMetaMethod::Signal) + if (method.methodType() == QMetaMethod::Signal) pmeta->signalCount++; } dataIndex += 5; paramsIndex += 1 + argc * 2; } if (hasRevisionedMethods) { - for (index = 0; index < d->methods.size(); ++index) { - QMetaMethodBuilderPrivate *method = &(d->methods[index]); + for (const auto &method : d->methods) { if (buf) - data[dataIndex] = method->revision; + data[dataIndex] = method.revision; ++dataIndex; } } // Output the method parameters in the class. - Q_ASSERT(!buf || dataIndex == pmeta->methodData + d->methods.size() * 5 - + (hasRevisionedMethods ? d->methods.size() : 0)); + Q_ASSERT(!buf || dataIndex == pmeta->methodData + int(d->methods.size()) * 5 + + (hasRevisionedMethods ? int(d->methods.size()) : 0)); for (int x = 0; x < 2; ++x) { - QVector<QMetaMethodBuilderPrivate> &methods = (x == 0) ? d->methods : d->constructors; - for (index = 0; index < methods.size(); ++index) { - QMetaMethodBuilderPrivate *method = &(methods[index]); - QList<QByteArray> paramTypeNames = method->parameterTypes(); + const std::vector<QMetaMethodBuilderPrivate> &methods = (x == 0) ? d->methods : d->constructors; + for (const auto &method : methods) { + const QList<QByteArray> paramTypeNames = method.parameterTypes(); int paramCount = paramTypeNames.size(); for (int i = -1; i < paramCount; ++i) { - const QByteArray &typeName = (i < 0) ? method->returnType : paramTypeNames.at(i); + const QByteArray &typeName = (i < 0) ? method.returnType : paramTypeNames.at(i); int typeInfo; if (QtPrivate::isBuiltinType(typeName)) typeInfo = QMetaType::type(typeName); @@ -1353,7 +1344,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, ++dataIndex; } - QList<QByteArray> paramNames = method->parameterNames; + QList<QByteArray> paramNames = method.parameterNames; while (paramNames.size() < paramCount) paramNames.append(QByteArray()); for (int i = 0; i < paramCount; ++i) { @@ -1367,19 +1358,18 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, // Output the properties in the class. Q_ASSERT(!buf || dataIndex == pmeta->propertyData); - for (index = 0; index < d->properties.size(); ++index) { - QMetaPropertyBuilderPrivate *prop = &(d->properties[index]); - int name = strings.enter(prop->name); + for (const auto &prop : d->properties) { + int name = strings.enter(prop.name); int typeInfo; - if (QtPrivate::isBuiltinType(prop->type)) - typeInfo = QMetaType::type(prop->type); + if (QtPrivate::isBuiltinType(prop.type)) + typeInfo = QMetaType::type(prop.type); else - typeInfo = IsUnresolvedType | strings.enter(prop->type); + typeInfo = IsUnresolvedType | strings.enter(prop.type); - int flags = prop->flags; + int flags = prop.flags; - if (!QtPrivate::isBuiltinType(prop->type)) + if (!QtPrivate::isBuiltinType(prop.type)) flags |= EnumOrFlag; if (buf) { @@ -1390,11 +1380,10 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, dataIndex += 3; } if (hasNotifySignals) { - for (index = 0; index < d->properties.size(); ++index) { - QMetaPropertyBuilderPrivate *prop = &(d->properties[index]); + for (const auto &prop : d->properties) { if (buf) { - if (prop->notifySignal != -1) - data[dataIndex] = prop->notifySignal; + if (prop.notifySignal != -1) + data[dataIndex] = prop.notifySignal; else data[dataIndex] = 0; } @@ -1402,21 +1391,19 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, } } if (hasRevisionedProperties) { - for (index = 0; index < d->properties.size(); ++index) { - QMetaPropertyBuilderPrivate *prop = &(d->properties[index]); + for (const auto &prop : d->properties) { if (buf) - data[dataIndex] = prop->revision; + data[dataIndex] = prop.revision; ++dataIndex; } } // Output the enumerators in the class. Q_ASSERT(!buf || dataIndex == pmeta->enumeratorData); - for (index = 0; index < d->enumerators.size(); ++index) { - QMetaEnumBuilderPrivate *enumerator = &(d->enumerators[index]); - int name = strings.enter(enumerator->name); - int isFlag = (int)(enumerator->isFlag); - int count = enumerator->keys.size(); + for (const auto &enumerator : d->enumerators) { + int name = strings.enter(enumerator.name); + int isFlag = (int)(enumerator.isFlag); + int count = enumerator.keys.size(); int enumOffset = enumIndex; if (buf) { data[dataIndex] = name; @@ -1425,10 +1412,10 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, data[dataIndex + 3] = enumOffset; } for (int key = 0; key < count; ++key) { - int keyIndex = strings.enter(enumerator->keys[key]); + int keyIndex = strings.enter(enumerator.keys[key]); if (buf) { data[enumOffset++] = keyIndex; - data[enumOffset++] = enumerator->values[key]; + data[enumOffset++] = enumerator.values[key]; } } dataIndex += 4; @@ -1437,12 +1424,11 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, // Output the constructors in the class. Q_ASSERT(!buf || dataIndex == pmeta->constructorData); - for (index = 0; index < d->constructors.size(); ++index) { - QMetaMethodBuilderPrivate *method = &(d->constructors[index]); - int name = strings.enter(method->name()); - int argc = method->parameterCount(); - int tag = strings.enter(method->tag); - int attrs = method->attributes; + for (const auto &ctor : d->constructors) { + int name = strings.enter(ctor.name()); + int argc = ctor.parameterCount(); + int tag = strings.enter(ctor.tag); + int attrs = ctor.attributes; if (buf) { data[dataIndex] = name; data[dataIndex + 1] = argc; @@ -1612,10 +1598,10 @@ void QMetaObjectBuilder::serialize(QDataStream& stream) const // Write the counts for each type of class member. stream << d->classInfoNames.size(); - stream << d->methods.size(); - stream << d->properties.size(); - stream << d->enumerators.size(); - stream << d->constructors.size(); + stream << int(d->methods.size()); + stream << int(d->properties.size()); + stream << int(d->enumerators.size()); + stream << int(d->constructors.size()); stream << d->relatedMetaObjects.size(); // Write the items of class information. @@ -1625,45 +1611,41 @@ void QMetaObjectBuilder::serialize(QDataStream& stream) const } // Write the methods. - for (index = 0; index < d->methods.size(); ++index) { - const QMetaMethodBuilderPrivate *method = &(d->methods[index]); - stream << method->signature; - stream << method->returnType; - stream << method->parameterNames; - stream << method->tag; - stream << method->attributes; - if (method->revision) - stream << method->revision; + for (const auto &method : d->methods) { + stream << method.signature; + stream << method.returnType; + stream << method.parameterNames; + stream << method.tag; + stream << method.attributes; + if (method.revision) + stream << method.revision; } // Write the properties. - for (index = 0; index < d->properties.size(); ++index) { - const QMetaPropertyBuilderPrivate *property = &(d->properties[index]); - stream << property->name; - stream << property->type; - stream << property->flags; - stream << property->notifySignal; - if (property->revision) - stream << property->revision; + for (const auto &property : d->properties) { + stream << property.name; + stream << property.type; + stream << property.flags; + stream << property.notifySignal; + if (property.revision) + stream << property.revision; } // Write the enumerators. - for (index = 0; index < d->enumerators.size(); ++index) { - const QMetaEnumBuilderPrivate *enumerator = &(d->enumerators[index]); - stream << enumerator->name; - stream << enumerator->isFlag; - stream << enumerator->keys; - stream << enumerator->values; + for (const auto &enumerator : d->enumerators) { + stream << enumerator.name; + stream << enumerator.isFlag; + stream << enumerator.keys; + stream << enumerator.values; } // Write the constructors. - for (index = 0; index < d->constructors.size(); ++index) { - const QMetaMethodBuilderPrivate *method = &(d->constructors[index]); - stream << method->signature; - stream << method->returnType; - stream << method->parameterNames; - stream << method->tag; - stream << method->attributes; + for (const auto &ctor : d->constructors) { + stream << ctor.signature; + stream << ctor.returnType; + stream << ctor.parameterNames; + stream << ctor.tag; + stream << ctor.attributes; } // Write the related meta objects. @@ -1770,14 +1752,14 @@ void QMetaObjectBuilder::deserialize return; stream >> name; addMethod(name); - QMetaMethodBuilderPrivate *method = &(d->methods[index]); - stream >> method->returnType; - stream >> method->parameterNames; - stream >> method->tag; - stream >> method->attributes; - if (method->attributes & MethodRevisioned) - stream >> method->revision; - if (method->methodType() == QMetaMethod::Constructor) { + QMetaMethodBuilderPrivate &method = d->methods[index]; + stream >> method.returnType; + stream >> method.parameterNames; + stream >> method.tag; + stream >> method.attributes; + if (method.attributes & MethodRevisioned) + stream >> method.revision; + if (method.methodType() == QMetaMethod::Constructor) { // Cannot add a constructor in this set of methods. stream.setStatus(QDataStream::ReadCorruptData); return; @@ -1792,23 +1774,23 @@ void QMetaObjectBuilder::deserialize stream >> name; stream >> type; addProperty(name, type); - QMetaPropertyBuilderPrivate *property = &(d->properties[index]); - stream >> property->flags; - stream >> property->notifySignal; - if (property->notifySignal < -1 || - property->notifySignal >= d->methods.size()) { + QMetaPropertyBuilderPrivate &property = d->properties[index]; + stream >> property.flags; + stream >> property.notifySignal; + if (property.notifySignal < -1 || + property.notifySignal >= int(d->methods.size())) { // Notify signal method index is out of range. stream.setStatus(QDataStream::ReadCorruptData); return; } - if (property->notifySignal >= 0 && - d->methods[property->notifySignal].methodType() != QMetaMethod::Signal) { + if (property.notifySignal >= 0 && + d->methods[property.notifySignal].methodType() != QMetaMethod::Signal) { // Notify signal method index does not refer to a signal. stream.setStatus(QDataStream::ReadCorruptData); return; } - if (property->flags & Revisioned) - stream >> property->revision; + if (property.flags & Revisioned) + stream >> property.revision; } // Read the enumerators. @@ -1817,11 +1799,11 @@ void QMetaObjectBuilder::deserialize return; stream >> name; addEnumerator(name); - QMetaEnumBuilderPrivate *enumerator = &(d->enumerators[index]); - stream >> enumerator->isFlag; - stream >> enumerator->keys; - stream >> enumerator->values; - if (enumerator->keys.size() != enumerator->values.size()) { + QMetaEnumBuilderPrivate &enumerator = d->enumerators[index]; + stream >> enumerator.isFlag; + stream >> enumerator.keys; + stream >> enumerator.values; + if (enumerator.keys.size() != enumerator.values.size()) { // Mismatch between number of keys and number of values. stream.setStatus(QDataStream::ReadCorruptData); return; @@ -1834,12 +1816,12 @@ void QMetaObjectBuilder::deserialize return; stream >> name; addConstructor(name); - QMetaMethodBuilderPrivate *method = &(d->constructors[index]); - stream >> method->returnType; - stream >> method->parameterNames; - stream >> method->tag; - stream >> method->attributes; - if (method->methodType() != QMetaMethod::Constructor) { + QMetaMethodBuilderPrivate &method = d->constructors[index]; + stream >> method.returnType; + stream >> method.parameterNames; + stream >> method.tag; + stream >> method.attributes; + if (method.methodType() != QMetaMethod::Constructor) { // The type must be Constructor. stream.setStatus(QDataStream::ReadCorruptData); return; @@ -1875,9 +1857,9 @@ void QMetaObjectBuilder::deserialize QMetaMethodBuilderPrivate *QMetaMethodBuilder::d_func() const { // Positive indices indicate methods, negative indices indicate constructors. - if (_mobj && _index >= 0 && _index < _mobj->d->methods.size()) + if (_mobj && _index >= 0 && _index < int(_mobj->d->methods.size())) return &(_mobj->d->methods[_index]); - else if (_mobj && -_index >= 1 && -_index <= _mobj->d->constructors.size()) + else if (_mobj && -_index >= 1 && -_index <= int(_mobj->d->constructors.size())) return &(_mobj->d->constructors[(-_index) - 1]); else return 0; @@ -2116,7 +2098,7 @@ void QMetaMethodBuilder::setRevision(int revision) QMetaPropertyBuilderPrivate *QMetaPropertyBuilder::d_func() const { - if (_mobj && _index >= 0 && _index < _mobj->d->properties.size()) + if (_mobj && _index >= 0 && _index < int(_mobj->d->properties.size())) return &(_mobj->d->properties[_index]); else return 0; @@ -2588,7 +2570,7 @@ void QMetaPropertyBuilder::setRevision(int revision) QMetaEnumBuilderPrivate *QMetaEnumBuilder::d_func() const { - if (_mobj && _index >= 0 && _index < _mobj->d->enumerators.size()) + if (_mobj && _index >= 0 && _index < int(_mobj->d->enumerators.size())) return &(_mobj->d->enumerators[_index]); else return 0; diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index 64c5b58fd4..118316484b 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -293,7 +293,7 @@ public: connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, const QObject *context, Func2 slot, Qt::ConnectionType type = Qt::AutoConnection) { -#if defined (Q_COMPILER_DECLTYPE) && defined (Q_COMPILER_VARIADIC_TEMPLATES) +#if defined (Q_COMPILER_VARIADIC_TEMPLATES) typedef QtPrivate::FunctionPointer<Func1> SignalType; const int FunctorArgumentCount = QtPrivate::ComputeFunctorArgumentCount<Func2 , typename SignalType::Arguments>::Value; @@ -313,15 +313,7 @@ public: Functors with overloaded or templated operator() are only supported if the compiler supports C++11 variadic templates */ -#ifndef Q_COMPILER_DECLTYPE //Workaround the lack of decltype using another function as indirection - return connect_functor(sender, signal, context, slot, &Func2::operator(), type); } - template <typename Func1, typename Func2, typename Func2Operator> - static inline QMetaObject::Connection connect_functor(const QObject *sender, Func1 signal, const QObject *context, - Func2 slot, Func2Operator, Qt::ConnectionType type) { - typedef QtPrivate::FunctionPointer<Func2Operator> SlotType ; -#else typedef QtPrivate::FunctionPointer<decltype(&Func2::operator())> SlotType ; -#endif typedef QtPrivate::FunctionPointer<Func1> SignalType; typedef typename SlotType::ReturnType SlotReturnType; const int SlotArgumentCount = SlotType::ArgumentCount; diff --git a/src/corelib/kernel/qobjectdefs_impl.h b/src/corelib/kernel/qobjectdefs_impl.h index d5574a4dd1..dc445a426d 100644 --- a/src/corelib/kernel/qobjectdefs_impl.h +++ b/src/corelib/kernel/qobjectdefs_impl.h @@ -593,7 +593,7 @@ namespace QtPrivate { }; #endif -#if defined(Q_COMPILER_DECLTYPE) && defined(Q_COMPILER_VARIADIC_TEMPLATES) +#if defined(Q_COMPILER_VARIADIC_TEMPLATES) /* Find the maximum number of arguments a functor object can take and be still compatible with the arguments from the signal. diff --git a/src/corelib/kernel/qvariant_p.h b/src/corelib/kernel/qvariant_p.h index d84d702982..986eb2c467 100644 --- a/src/corelib/kernel/qvariant_p.h +++ b/src/corelib/kernel/qvariant_p.h @@ -202,7 +202,6 @@ class QVariantIsNull /// \internal /// This class checks if a type T has method called isNull. Result is kept in the Value property /// TODO Can we somehow generalize it? A macro version? -#if defined(Q_COMPILER_DECLTYPE) // C++11 version template<typename T> class HasIsNullMethod { struct Yes { char unused[1]; }; @@ -214,44 +213,6 @@ class QVariantIsNull public: static const bool Value = (sizeof(test<T>(0)) == sizeof(Yes)); }; -#elif defined(Q_CC_MSVC) && _MSC_VER >= 1400 && !defined(Q_CC_INTEL) // MSVC 2005, 2008 version: no decltype, but 'sealed' classes (>=2010 has decltype) - template<typename T> - class HasIsNullMethod { - struct Yes { char unused[1]; }; - struct No { char unused[2]; }; - Q_STATIC_ASSERT(sizeof(Yes) != sizeof(No)); - - template<class C> static Yes test(char (*)[(&C::isNull == 0) + 1]); - template<class C> static No test(...); - public: - static const bool Value = (sizeof(test<T>(0)) == sizeof(Yes)); - }; -#else // C++98 version (doesn't work for final classes) - template<typename T, bool IsClass = QTypeInfo<T>::isComplex> - class HasIsNullMethod - { - struct Yes { char unused[1]; }; - struct No { char unused[2]; }; - Q_STATIC_ASSERT(sizeof(Yes) != sizeof(No)); - - struct FallbackMixin { bool isNull() const; }; - struct Derived : public T, public FallbackMixin {}; // <- doesn't work for final classes - template<class C, C> struct TypeCheck {}; - - template<class C> static Yes test(...); - template<class C> static No test(TypeCheck<bool (FallbackMixin::*)() const, &C::isNull> *); - public: - static const bool Value = (sizeof(test<Derived>(0)) == sizeof(Yes)); - }; - - // We need to exclude primitive types as they won't compile with HasIsNullMethod::Check classes - // anyway it is not a problem as the types do not have isNull method. - template<typename T> - class HasIsNullMethod<T, /* IsClass = */ false> { - public: - static const bool Value = false; - }; -#endif // TODO This part should go to autotests during HasIsNullMethod generalization. Q_STATIC_ASSERT(!HasIsNullMethod<bool>::Value); diff --git a/src/corelib/plugin/plugin.pri b/src/corelib/plugin/plugin.pri index 8b64f93467..9dc60c5d39 100644 --- a/src/corelib/plugin/plugin.pri +++ b/src/corelib/plugin/plugin.pri @@ -35,4 +35,8 @@ integrity { SOURCES += plugin/qlibrary_unix.cpp } +darwin { + OBJECTIVE_SOURCES += plugin/quuid_darwin.mm +} + LIBS_PRIVATE += $$QMAKE_LIBS_DYNLOAD diff --git a/src/corelib/plugin/qfactoryloader.cpp b/src/corelib/plugin/qfactoryloader.cpp index cc5aa1394a..f7545b5bcb 100644 --- a/src/corelib/plugin/qfactoryloader.cpp +++ b/src/corelib/plugin/qfactoryloader.cpp @@ -33,7 +33,7 @@ #include "qfactoryloader_p.h" -#ifndef QT_NO_LIBRARY +#ifndef QT_NO_QOBJECT #include "qfactoryinterface.h" #include "qmap.h" #include <qdir.h> @@ -50,10 +50,6 @@ QT_BEGIN_NAMESPACE -Q_GLOBAL_STATIC(QList<QFactoryLoader *>, qt_factory_loaders) - -Q_GLOBAL_STATIC_WITH_ARGS(QMutex, qt_factoryloader_mutex, (QMutex::Recursive)) - namespace { // avoid duplicate QStringLiteral data: @@ -69,18 +65,24 @@ class QFactoryLoaderPrivate : public QObjectPrivate Q_DECLARE_PUBLIC(QFactoryLoader) public: QFactoryLoaderPrivate(){} + QByteArray iid; +#ifndef QT_NO_LIBRARY ~QFactoryLoaderPrivate(); mutable QMutex mutex; - QByteArray iid; QList<QLibraryPrivate*> libraryList; QMap<QString,QLibraryPrivate*> keyMap; QString suffix; Qt::CaseSensitivity cs; QStringList loadedPaths; - - void unloadPath(const QString &path); +#endif }; +#ifndef QT_NO_LIBRARY + +Q_GLOBAL_STATIC(QList<QFactoryLoader *>, qt_factory_loaders) + +Q_GLOBAL_STATIC_WITH_ARGS(QMutex, qt_factoryloader_mutex, (QMutex::Recursive)) + QFactoryLoaderPrivate::~QFactoryLoaderPrivate() { for (int i = 0; i < libraryList.count(); ++i) { @@ -90,25 +92,6 @@ QFactoryLoaderPrivate::~QFactoryLoaderPrivate() } } -QFactoryLoader::QFactoryLoader(const char *iid, - const QString &suffix, - Qt::CaseSensitivity cs) - : QObject(*new QFactoryLoaderPrivate) -{ - moveToThread(QCoreApplicationPrivate::mainThread()); - Q_D(QFactoryLoader); - d->iid = iid; - d->cs = cs; - d->suffix = suffix; - - - QMutexLocker locker(qt_factoryloader_mutex()); - update(); - qt_factory_loaders()->append(this); -} - - - void QFactoryLoader::update() { #ifdef QT_SHARED @@ -229,13 +212,56 @@ QFactoryLoader::~QFactoryLoader() qt_factory_loaders()->removeAll(this); } +#if defined(Q_OS_UNIX) && !defined (Q_OS_MAC) +QLibraryPrivate *QFactoryLoader::library(const QString &key) const +{ + Q_D(const QFactoryLoader); + return d->keyMap.value(d->cs ? key : key.toLower()); +} +#endif + +void QFactoryLoader::refreshAll() +{ + QMutexLocker locker(qt_factoryloader_mutex()); + QList<QFactoryLoader *> *loaders = qt_factory_loaders(); + for (QList<QFactoryLoader *>::const_iterator it = loaders->constBegin(); + it != loaders->constEnd(); ++it) { + (*it)->update(); + } +} + +#endif // QT_NO_LIBRARY + +QFactoryLoader::QFactoryLoader(const char *iid, + const QString &suffix, + Qt::CaseSensitivity cs) + : QObject(*new QFactoryLoaderPrivate) +{ + moveToThread(QCoreApplicationPrivate::mainThread()); + Q_D(QFactoryLoader); + d->iid = iid; +#ifndef QT_NO_LIBRARY + d->cs = cs; + d->suffix = suffix; + + QMutexLocker locker(qt_factoryloader_mutex()); + update(); + qt_factory_loaders()->append(this); +#else + Q_UNUSED(suffix); + Q_UNUSED(cs); +#endif +} + QList<QJsonObject> QFactoryLoader::metaData() const { Q_D(const QFactoryLoader); - QMutexLocker locker(&d->mutex); QList<QJsonObject> metaData; +#ifndef QT_NO_LIBRARY + QMutexLocker locker(&d->mutex); for (int i = 0; i < d->libraryList.size(); ++i) metaData.append(d->libraryList.at(i)->metaData); +#endif foreach (const QStaticPlugin &plugin, QPluginLoader::staticPlugins()) { const QJsonObject object = plugin.metaData(); @@ -252,6 +278,7 @@ QObject *QFactoryLoader::instance(int index) const if (index < 0) return 0; +#ifndef QT_NO_LIBRARY if (index < d->libraryList.size()) { QLibraryPrivate *library = d->libraryList.at(index); if (library->instance || library->loadPlugin()) { @@ -266,8 +293,9 @@ QObject *QFactoryLoader::instance(int index) const } return 0; } - index -= d->libraryList.size(); +#endif + QVector<QStaticPlugin> staticPlugins = QPluginLoader::staticPlugins(); for (int i = 0; i < staticPlugins.count(); ++i) { const QJsonObject object = staticPlugins.at(i).metaData(); @@ -282,24 +310,6 @@ QObject *QFactoryLoader::instance(int index) const return 0; } -#if defined(Q_OS_UNIX) && !defined (Q_OS_MAC) -QLibraryPrivate *QFactoryLoader::library(const QString &key) const -{ - Q_D(const QFactoryLoader); - return d->keyMap.value(d->cs ? key : key.toLower()); -} -#endif - -void QFactoryLoader::refreshAll() -{ - QMutexLocker locker(qt_factoryloader_mutex()); - QList<QFactoryLoader *> *loaders = qt_factory_loaders(); - for (QList<QFactoryLoader *>::const_iterator it = loaders->constBegin(); - it != loaders->constEnd(); ++it) { - (*it)->update(); - } -} - QMultiMap<int, QString> QFactoryLoader::keyMap() const { QMultiMap<int, QString> result; @@ -335,4 +345,4 @@ int QFactoryLoader::indexOf(const QString &needle) const QT_END_NAMESPACE -#endif // QT_NO_LIBRARY +#endif // QT_NO_QOBJECT diff --git a/src/corelib/plugin/qfactoryloader_p.h b/src/corelib/plugin/qfactoryloader_p.h index 1c48491b0d..ee07084180 100644 --- a/src/corelib/plugin/qfactoryloader_p.h +++ b/src/corelib/plugin/qfactoryloader_p.h @@ -45,17 +45,18 @@ // We mean it. // +#include "QtCore/qglobal.h" +#ifndef QT_NO_QOBJECT + #include "QtCore/qobject.h" #include "QtCore/qstringlist.h" #include "QtCore/qjsonobject.h" #include "QtCore/qmap.h" #include "private/qlibrary_p.h" -#ifndef QT_NO_LIBRARY QT_BEGIN_NAMESPACE class QFactoryLoaderPrivate; - class Q_CORE_EXPORT QFactoryLoader : public QObject { Q_OBJECT @@ -65,21 +66,23 @@ public: explicit QFactoryLoader(const char *iid, const QString &suffix = QString(), Qt::CaseSensitivity = Qt::CaseSensitive); + +#ifndef QT_NO_LIBRARY ~QFactoryLoader(); - QList<QJsonObject> metaData() const; - QObject *instance(int index) const; + void update(); + static void refreshAll(); #if defined(Q_OS_UNIX) && !defined (Q_OS_MAC) QLibraryPrivate *library(const QString &key) const; -#endif +#endif // Q_OS_UNIX && !Q_OS_MAC +#endif // !QT_NO_LIBRARY QMultiMap<int, QString> keyMap() const; int indexOf(const QString &needle) const; - void update(); - - static void refreshAll(); + QList<QJsonObject> metaData() const; + QObject *instance(int index) const; }; template <class PluginInterface, class FactoryInterface> @@ -112,6 +115,6 @@ PluginInterface *qLoadPlugin1(const QFactoryLoader *loader, QT_END_NAMESPACE -#endif // QT_NO_LIBRARY +#endif // QT_NO_QOBJECT #endif // QFACTORYLOADER_P_H diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp index 920e02ae5a..763f0fb1e9 100644 --- a/src/corelib/plugin/qlibrary.cpp +++ b/src/corelib/plugin/qlibrary.cpp @@ -302,7 +302,7 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) if (pos >= 0) { if (hasMetaData) { const char *data = filedata + pos; - QJsonDocument doc = QLibraryPrivate::fromRawMetaData(data); + QJsonDocument doc = qJsonFromRawLibraryMetaData(data); lib->metaData = doc.object(); if (qt_debug_component()) qWarning("Found metadata in lib %s, metadata=\n%s\n", @@ -677,7 +677,7 @@ static bool qt_get_metadata(QtPluginQueryVerificationDataFunction pfn, QLibraryP if (!szData) return false; - QJsonDocument doc = QLibraryPrivate::fromRawMetaData(szData); + QJsonDocument doc = qJsonFromRawLibraryMetaData(szData); if (doc.isNull()) return false; priv->metaData = doc.object(); diff --git a/src/corelib/plugin/qlibrary_p.h b/src/corelib/plugin/qlibrary_p.h index ada90d7bfd..11b0cb1eb9 100644 --- a/src/corelib/plugin/qlibrary_p.h +++ b/src/corelib/plugin/qlibrary_p.h @@ -57,9 +57,20 @@ # include "QtCore/qt_windows.h" #endif +QT_BEGIN_NAMESPACE + +// Needed also in case of QT_NO_LIBRARY, for static plugin loading. +inline QJsonDocument qJsonFromRawLibraryMetaData(const char *raw) +{ + raw += strlen("QTMETADATA "); + // the size of the embedded JSON object can be found 8 bytes into the data (see qjson_p.h), + // but doesn't include the size of the header (8 bytes) + QByteArray json(raw, qFromLittleEndian<uint>(*(const uint *)(raw + 8)) + 8); + return QJsonDocument::fromBinaryData(json); +} + #ifndef QT_NO_LIBRARY -QT_BEGIN_NAMESPACE bool qt_debug_component(); @@ -104,14 +115,6 @@ public: void updatePluginState(); bool isPlugin(); - static inline QJsonDocument fromRawMetaData(const char *raw) { - raw += strlen("QTMETADATA "); - // the size of the embedded JSON object can be found 8 bytes into the data (see qjson_p.h), - // but doesn't include the size of the header (8 bytes) - QByteArray json(raw, qFromLittleEndian<uint>(*(const uint *)(raw + 8)) + 8); - return QJsonDocument::fromBinaryData(json); - } - private: explicit QLibraryPrivate(const QString &canonicalFileName, const QString &version, QLibrary::LoadHints loadHints); ~QLibraryPrivate(); @@ -132,8 +135,8 @@ private: friend class QLibraryStore; }; -QT_END_NAMESPACE - #endif // QT_NO_LIBRARY +QT_END_NAMESPACE + #endif // QLIBRARY_P_H diff --git a/src/corelib/plugin/qpluginloader.cpp b/src/corelib/plugin/qpluginloader.cpp index 24101be87b..8264a27842 100644 --- a/src/corelib/plugin/qpluginloader.cpp +++ b/src/corelib/plugin/qpluginloader.cpp @@ -41,10 +41,10 @@ #include "qdebug.h" #include "qdir.h" -#ifndef QT_NO_LIBRARY - QT_BEGIN_NAMESPACE +#ifndef QT_NO_LIBRARY + /*! \class QPluginLoader \inmodule QtCore @@ -382,9 +382,6 @@ QString QPluginLoader::errorString() const return (!d || d->errorString.isEmpty()) ? tr("Unknown error") : d->errorString; } -typedef QVector<QStaticPlugin> StaticPluginList; -Q_GLOBAL_STATIC(StaticPluginList, staticPluginList) - /*! \since 4.4 \property QPluginLoader::loadHints @@ -413,6 +410,11 @@ QLibrary::LoadHints QPluginLoader::loadHints() const return d ? d->loadHints() : QLibrary::LoadHints(); } +#endif // QT_NO_LIBRARY + +typedef QVector<QStaticPlugin> StaticPluginList; +Q_GLOBAL_STATIC(StaticPluginList, staticPluginList) + /*! \relates QPluginLoader \since 5.0 @@ -465,9 +467,8 @@ QVector<QStaticPlugin> QPluginLoader::staticPlugins() */ QJsonObject QStaticPlugin::metaData() const { - return QLibraryPrivate::fromRawMetaData(rawMetaData()).object(); + return qJsonFromRawLibraryMetaData(rawMetaData()).object(); } QT_END_NAMESPACE -#endif // QT_NO_LIBRARY diff --git a/src/corelib/plugin/qpluginloader.h b/src/corelib/plugin/qpluginloader.h index 0ab25bbb07..5dc7d1b66c 100644 --- a/src/corelib/plugin/qpluginloader.h +++ b/src/corelib/plugin/qpluginloader.h @@ -37,10 +37,10 @@ #include <QtCore/qlibrary.h> #include <QtCore/qplugin.h> -#ifndef QT_NO_LIBRARY - QT_BEGIN_NAMESPACE +#ifndef QT_NO_LIBRARY + class QLibraryPrivate; class QJsonObject; @@ -78,8 +78,17 @@ private: Q_DISABLE_COPY(QPluginLoader) }; -QT_END_NAMESPACE +#else + +class Q_CORE_EXPORT QPluginLoader +{ +public: + static QObjectList staticInstances(); + static QVector<QStaticPlugin> staticPlugins(); +}; #endif // QT_NO_LIBRARY +QT_END_NAMESPACE + #endif //QPLUGINLOADER_H diff --git a/src/corelib/plugin/quuid.cpp b/src/corelib/plugin/quuid.cpp index 253789b4f0..b7ea1f2f60 100644 --- a/src/corelib/plugin/quuid.cpp +++ b/src/corelib/plugin/quuid.cpp @@ -803,6 +803,39 @@ QUuid::Version QUuid::version() const Q_DECL_NOTHROW return ver; } +/*! \fn QUuid QUuid::fromCFUUID(CFUUIDRef uuid) + \since 5.7 + + Constructs a new QUuid containing a copy of the \a uuid CFUUID. + + \note this function is only available on Apple platforms. +*/ + +/*! \fn CFUUIDRef QUuid::toCFUUID() const + \since 5.7 + + Creates a CFUUID from a QUuid. The caller owns the CFUUID and is + responsible for releasing it. + + \note this function is only available on Apple platforms. +*/ + +/*! \fn QUuid QUuid::fromNSUUID(const NSUUID *uuid) + \since 5.7 + + Constructs a new QUuid containing a copy of the \a uuid NSUUID. + + \note this function is only available on Apple platforms. +*/ + +/*! \fn NSUUID QUuid::toNSUUID() const + \since 5.7 + + Creates a NSUUID from a QUuid. The NSUUID is autoreleased. + + \note this function is only available on Apple platforms. +*/ + /*! \fn bool QUuid::operator<(const QUuid &other) const diff --git a/src/corelib/plugin/quuid.h b/src/corelib/plugin/quuid.h index f004cba77e..07cd9c4103 100644 --- a/src/corelib/plugin/quuid.h +++ b/src/corelib/plugin/quuid.h @@ -49,6 +49,12 @@ typedef struct _GUID #endif #endif +#ifdef Q_OS_DARWIN +Q_FORWARD_DECLARE_CF_TYPE(CFUUID); +# ifdef __OBJC__ +Q_FORWARD_DECLARE_OBJC_CLASS(NSUUID); +# endif +#endif QT_BEGIN_NAMESPACE @@ -195,6 +201,15 @@ public: QUuid::Variant variant() const Q_DECL_NOTHROW; QUuid::Version version() const Q_DECL_NOTHROW; +#if defined(Q_OS_DARWIN) || defined(Q_QDOC) + static QUuid fromCFUUID(CFUUIDRef uuid); + CFUUIDRef toCFUUID() const Q_DECL_CF_RETURNS_RETAINED; +# if defined(__OBJC__) || defined(Q_QDOC) + static QUuid fromNSUUID(const NSUUID *uuid); + NSUUID *toNSUUID() const Q_DECL_NS_RETURNS_AUTORELEASED; +# endif +#endif + uint data1; ushort data2; ushort data3; diff --git a/src/widgets/styles/qgtkpainter.cpp b/src/corelib/plugin/quuid_darwin.mm index e86fee079e..b316b88d52 100644 --- a/src/widgets/styles/qgtkpainter.cpp +++ b/src/corelib/plugin/quuid_darwin.mm @@ -3,7 +3,7 @@ ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** -** This file is part of the QtWidgets module of the Qt Toolkit. +** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage @@ -31,46 +31,39 @@ ** ****************************************************************************/ -#include "qgtkpainter_p.h" +#include "quuid.h" -#if !defined(QT_NO_STYLE_GTK) - -#include <private/qhexstring_p.h> +#import <Foundation/Foundation.h> QT_BEGIN_NAMESPACE -QGtkPainter::QGtkPainter() +QUuid QUuid::fromCFUUID(CFUUIDRef uuid) { - reset(0); + if (!uuid) + return QUuid(); + const CFUUIDBytes bytes = CFUUIDGetUUIDBytes(uuid); + return QUuid::fromRfc4122(QByteArray::fromRawData(reinterpret_cast<const char *>(&bytes), sizeof(bytes))); } -QGtkPainter::~QGtkPainter() +CFUUIDRef QUuid::toCFUUID() const { + const QByteArray bytes = toRfc4122(); + return CFUUIDCreateFromUUIDBytes(0, *reinterpret_cast<const CFUUIDBytes *>(bytes.constData())); } -void QGtkPainter::reset(QPainter *painter) +QUuid QUuid::fromNSUUID(const NSUUID *uuid) { - m_painter = painter; - m_alpha = true; - m_hflipped = false; - m_vflipped = false; - m_usePixmapCache = true; - m_cliprect = QRect(); + if (!uuid) + return QUuid(); + uuid_t bytes; + [uuid getUUIDBytes:bytes]; + return QUuid::fromRfc4122(QByteArray::fromRawData(reinterpret_cast<const char *>(bytes), sizeof(bytes))); } -QString QGtkPainter::uniqueName(const QString &key, GtkStateType state, GtkShadowType shadow, - const QSize &size, GtkWidget *widget) +NSUUID *QUuid::toNSUUID() const { - // Note the widget arg should ideally use the widget path, though would compromise performance - QString tmp = key - % HexString<uint>(state) - % HexString<uint>(shadow) - % HexString<uint>(size.width()) - % HexString<uint>(size.height()) - % HexString<quint64>(quint64(widget)); - return tmp; + const QByteArray bytes = toRfc4122(); + return [[[NSUUID alloc] initWithUUIDBytes:*reinterpret_cast<const uuid_t *>(bytes.constData())] autorelease]; } QT_END_NAMESPACE - -#endif //!defined(QT_NO_STYLE_GTK) diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index a9f361c205..3a6f5afa60 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -39,6 +39,7 @@ #include "qlist.h" #include "qlocale.h" #include "qlocale_p.h" +#include "qlocale_tools_p.h" #include "qstringalgorithms_p.h" #include "qscopedpointer.h" #include "qbytearray_p.h" @@ -1713,6 +1714,14 @@ QByteArray &QByteArray::prepend(const char *str, int len) return *this; } +/*! \fn QByteArray &QByteArray::prepend(int count, char ch) + + \overload + \since 5.7 + + Prepends \a count copies of character \a ch to this byte array. +*/ + /*! \overload @@ -1825,6 +1834,17 @@ QByteArray &QByteArray::append(const char *str, int len) return *this; } +/*! \fn QByteArray &QByteArray::append(int count, char ch) + + \overload + \since 5.7 + + Appends \a count copies of character \a ch to this byte + array and returns a reference to this byte array. + + If \a count is negative or zero nothing is appended to the byte array. +*/ + /*! \overload @@ -1941,6 +1961,33 @@ QByteArray &QByteArray::insert(int i, char ch) return qbytearray_insert(this, i, &ch, 1); } +/*! \fn QByteArray &QByteArray::insert(int i, int count, char ch) + + \overload + \since 5.7 + + Inserts \a count copies of character \a ch at index position \a i in the + byte array. + + If \a i is greater than size(), the array is first extended using resize(). +*/ + +QByteArray &QByteArray::insert(int i, int count, char ch) +{ + if (i < 0 || count <= 0) + return *this; + + int oldsize = size(); + resize(qMax(i, oldsize) + count); + char *dst = d->data(); + if (i > oldsize) + ::memset(dst + oldsize, 0x20, i - oldsize); + else if (i < oldsize) + ::memmove(dst + i + count, dst + i, oldsize - i); + ::memset(dst + i, ch, count); + return *this; +} + /*! Removes \a len bytes from the array, starting at index position \a pos, and returns a reference to the array. @@ -3648,7 +3695,13 @@ ushort QByteArray::toUShort(bool *ok, int base) const double QByteArray::toDouble(bool *ok) const { - return QLocaleData::bytearrayToDouble(nulTerminated().constData(), ok); + QByteArray nulled = nulTerminated(); + bool nonNullOk = false; + int processed = 0; + double d = asciiToDouble(nulled.constData(), nulled.length(), nonNullOk, processed); + if (ok) + *ok = nonNullOk; + return d; } /*! diff --git a/src/corelib/tools/qbytearray.h b/src/corelib/tools/qbytearray.h index f0032227e8..6c79a603d3 100644 --- a/src/corelib/tools/qbytearray.h +++ b/src/corelib/tools/qbytearray.h @@ -255,21 +255,21 @@ public: # define Q_REQUIRED_RESULT # define Q_REQUIRED_RESULT_pushed # endif - QByteArray toLower() const & Q_REQUIRED_RESULT + Q_ALWAYS_INLINE QByteArray toLower() const & Q_REQUIRED_RESULT { return toLower_helper(*this); } - QByteArray toLower() && Q_REQUIRED_RESULT + Q_ALWAYS_INLINE QByteArray toLower() && Q_REQUIRED_RESULT { return toLower_helper(*this); } - QByteArray toUpper() const & Q_REQUIRED_RESULT + Q_ALWAYS_INLINE QByteArray toUpper() const & Q_REQUIRED_RESULT { return toUpper_helper(*this); } - QByteArray toUpper() && Q_REQUIRED_RESULT + Q_ALWAYS_INLINE QByteArray toUpper() && Q_REQUIRED_RESULT { return toUpper_helper(*this); } - QByteArray trimmed() const & Q_REQUIRED_RESULT + Q_ALWAYS_INLINE QByteArray trimmed() const & Q_REQUIRED_RESULT { return trimmed_helper(*this); } - QByteArray trimmed() && Q_REQUIRED_RESULT + Q_ALWAYS_INLINE QByteArray trimmed() && Q_REQUIRED_RESULT { return trimmed_helper(*this); } - QByteArray simplified() const & Q_REQUIRED_RESULT + Q_ALWAYS_INLINE QByteArray simplified() const & Q_REQUIRED_RESULT { return simplified_helper(*this); } - QByteArray simplified() && Q_REQUIRED_RESULT + Q_ALWAYS_INLINE QByteArray simplified() && Q_REQUIRED_RESULT { return simplified_helper(*this); } # ifdef Q_REQUIRED_RESULT_pushed # pragma pop_macro("Q_REQUIRED_RESULT") @@ -285,14 +285,17 @@ public: QByteArray rightJustified(int width, char fill = ' ', bool truncate = false) const Q_REQUIRED_RESULT; QByteArray &prepend(char c); + QByteArray &prepend(int count, char c); QByteArray &prepend(const char *s); QByteArray &prepend(const char *s, int len); QByteArray &prepend(const QByteArray &a); QByteArray &append(char c); + QByteArray &append(int count, char c); QByteArray &append(const char *s); QByteArray &append(const char *s, int len); QByteArray &append(const QByteArray &a); QByteArray &insert(int i, char c); + QByteArray &insert(int i, int count, char c); QByteArray &insert(int i, const char *s); QByteArray &insert(int i, const char *s, int len); QByteArray &insert(int i, const QByteArray &a); @@ -568,6 +571,10 @@ inline QByteArray::const_iterator QByteArray::cend() const { return d->data() + d->size; } inline QByteArray::const_iterator QByteArray::constEnd() const { return d->data() + d->size; } +inline QByteArray &QByteArray::append(int n, char ch) +{ return insert(d->size, n, ch); } +inline QByteArray &QByteArray::prepend(int n, char ch) +{ return insert(0, n, ch); } inline QByteArray &QByteArray::operator+=(char c) { return append(c); } inline QByteArray &QByteArray::operator+=(const char *s) diff --git a/src/corelib/tools/qdoublescanprint_p.h b/src/corelib/tools/qdoublescanprint_p.h new file mode 100644 index 0000000000..30ac584d06 --- /dev/null +++ b/src/corelib/tools/qdoublescanprint_p.h @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDOUBLESCANPRINT_P_H +#define QDOUBLESCANPRINT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of internal files. This header file may change from version to version +// without notice, or even be removed. +// +// We mean it. +// + +#include <qglobal.h> + +#if defined(Q_CC_MSVC) && (defined(QT_BOOTSTRAPPED) || defined(QT_NO_DOUBLECONVERSION)) +# include <stdio.h> +# include <locale.h> + +QT_BEGIN_NAMESPACE + +// We can always use _sscanf_l and _snprintf_l on MSVC as those were introduced in 2005. + +// MSVC doesn't document what it will do with a NULL locale passed to _sscanf_l or _snprintf_l. +// The documentation for _create_locale() does not formally document "C" to be valid, but an example +// code snippet in the same documentation shows it. + +struct QCLocaleT { + QCLocaleT() : locale(_create_locale(LC_ALL, "C")) + { + } + + ~QCLocaleT() + { + _free_locale(locale); + } + + const _locale_t locale; +}; + +# define QT_CLOCALE_HOLDER Q_GLOBAL_STATIC(QCLocaleT, cLocaleT) +# define QT_CLOCALE cLocaleT()->locale + +inline int qDoubleSscanf(const char *buf, _locale_t locale, const char *format, double *d, + int *processed) +{ + return _sscanf_l(buf, format, locale, d, processed); +} + +inline int qDoubleSnprintf(char *buf, size_t buflen, _locale_t locale, const char *format, double d) +{ + return _snprintf_l(buf, buflen, format, locale, d); +} + +QT_END_NAMESPACE + +#elif defined(QT_BOOTSTRAPPED) +# include <stdio.h> + +QT_BEGIN_NAMESPACE + +// When bootstrapping we don't have libdouble-conversion available, yet. We can also not use locale +// aware snprintf and sscanf variants in the general case because those are only available on select +// platforms. We can use the regular snprintf and sscanf because we don't do setlocale(3) when +// bootstrapping and the locale is always "C" then. + +# define QT_CLOCALE_HOLDER +# define QT_CLOCALE 0 + +inline int qDoubleSscanf(const char *buf, int, const char *format, double *d, int *processed) +{ + return sscanf(buf, format, d, processed); +} +inline int qDoubleSnprintf(char *buf, size_t buflen, int, const char *format, double d) +{ + return snprintf(buf, buflen, format, d); +} + +QT_END_NAMESPACE + +#else // !QT_BOOTSTRAPPED && (!Q_CC_MSVC || !QT_NO_DOUBLECONVERSION) +# ifdef QT_NO_DOUBLECONVERSION +# include <stdio.h> +# include <xlocale.h> + +QT_BEGIN_NAMESPACE + +// OS X and FreeBSD both treat NULL as the "C" locale for snprintf_l and sscanf_l. +// When other implementations with different behavior show up, we'll have to do newlocale(3) and +// freelocale(3) here. The arguments to those will depend on what the other implementations will +// offer. OS X and FreeBSD again interpret a locale name of NULL as "C", but "C" itself is not +// documented as valid locale name. Mind that the names of the LC_* constants differ between e.g. +// BSD variants and linux. + +# define QT_CLOCALE_HOLDER +# define QT_CLOCALE NULL + +inline int qDoubleSscanf(const char *buf, locale_t locale, const char *format, double *d, + int *processed) +{ + return sscanf_l(buf, locale, format, d, processed); +} +inline int qDoubleSnprintf(char *buf, size_t buflen, locale_t locale, const char *format, double d) +{ + return snprintf_l(buf, buflen, locale, format, d); +} + +QT_END_NAMESPACE + +# else // !QT_NO_DOUBLECONVERSION +# include <double-conversion/double-conversion.h> +# define QT_CLOCALE_HOLDER +# endif // QT_NO_DOUBLECONVERSION +#endif // QT_BOOTSTRAPPED + +#endif // QDOUBLESCANPRINT_P_H diff --git a/src/corelib/tools/qlocale.cpp b/src/corelib/tools/qlocale.cpp index b1f53dc7a2..dcb77a2c1b 100644 --- a/src/corelib/tools/qlocale.cpp +++ b/src/corelib/tools/qlocale.cpp @@ -2749,51 +2749,30 @@ QString QLocaleData::doubleToString(const QChar _zero, const QChar plus, const Q width = 0; bool negative = false; - bool special_number = false; // nan, +/-inf QString num_str; - // Detect special numbers (nan, +/-inf) - if (qt_is_inf(d)) { - num_str = QString::fromLatin1("inf"); - special_number = true; - negative = d < 0; - } else if (qt_is_nan(d)) { - num_str = QString::fromLatin1("nan"); - special_number = true; - } - - // Handle normal numbers - if (!special_number) { - int decpt, sign; - QString digits; - - int mode; - if (form == DFDecimal) - mode = 3; - else - mode = 2; - - /* This next bit is a bit quirky. In DFExponent form, the precision - is the number of digits after decpt. So that would suggest using - mode=3 for qdtoa. But qdtoa behaves strangely when mode=3 and - precision=0. So we get around this by using mode=2 and reasoning - that we want precision+1 significant digits, since the decimal - point in this mode is always after the first digit. */ - int pr = precision; - if (form == DFExponent) - ++pr; - - char *rve = 0; - char *buff = 0; - QT_TRY { - digits = QLatin1String(qdtoa(d, mode, pr, &decpt, &sign, &rve, &buff)); - } QT_CATCH(...) { - if (buff != 0) - free(buff); - QT_RETHROW; - } - if (buff != 0) - free(buff); + int decpt; + int bufSize = 1; + if (form == DFDecimal) // optimize for numbers smaller than 512k + bufSize += ((d > (1 << 19) || d < -(1 << 19)) ? DoubleMaxDigitsBeforeDecimal : 6) + + precision; + else // Add extra digit due to different interpretations of precision. Also, "nan" has to fit. + bufSize += qMax(2, precision) + 1; + + QVarLengthArray<char> buf(bufSize); + int length; + + doubleToAscii(d, form, precision, buf.data(), bufSize, negative, length, decpt); + + if (qstrncmp(buf.data(), "inf", 3) == 0 || qstrncmp(buf.data(), "nan", 3) == 0) { + num_str = QString::fromLatin1(buf.data(), length); + } else { // Handle normal numbers + + // Chop trailing zeros + int last_nonzero_idx = length - 1; + while (last_nonzero_idx > 0 && buf[last_nonzero_idx] == '0') + --last_nonzero_idx; + QString digits = QString::fromLatin1(buf.data(), last_nonzero_idx + 1); if (_zero.unicode() != '0') { ushort z = _zero.unicode() - '0'; @@ -2831,23 +2810,22 @@ QString QLocaleData::doubleToString(const QChar _zero, const QChar plus, const Q } } - negative = sign != 0 && !isZero(d); - } - - // pad with zeros. LeftAdjusted overrides this flag). Also, we don't - // pad special numbers - if (flags & QLocaleData::ZeroPadded - && !(flags & QLocaleData::LeftAdjusted) - && !special_number) { - int num_pad_chars = width - num_str.length(); - // leave space for the sign - if (negative - || flags & QLocaleData::AlwaysShowSign - || flags & QLocaleData::BlankBeforePositive) - --num_pad_chars; - - for (int i = 0; i < num_pad_chars; ++i) - num_str.prepend(_zero); + if (isZero(d)) + negative = false; + + // pad with zeros. LeftAdjusted overrides this flag). Also, we don't + // pad special numbers + if (flags & QLocaleData::ZeroPadded && !(flags & QLocaleData::LeftAdjusted)) { + int num_pad_chars = width - num_str.length(); + // leave space for the sign + if (negative + || flags & QLocaleData::AlwaysShowSign + || flags & QLocaleData::BlankBeforePositive) + --num_pad_chars; + + for (int i = 0; i < num_pad_chars; ++i) + num_str.prepend(_zero); + } } // add sign @@ -3243,7 +3221,12 @@ double QLocaleData::stringToDouble(const QChar *begin, int len, bool *ok, *ok = false; return 0.0; } - return bytearrayToDouble(buff.constData(), ok); + int processed = 0; + bool nonNullOk = false; + double d = asciiToDouble(buff.constData(), buff.length() - 1, nonNullOk, processed); + if (ok) + *ok = nonNullOk; + return d; } qlonglong QLocaleData::stringToLongLong(const QChar *begin, int len, int base, @@ -3274,53 +3257,15 @@ qulonglong QLocaleData::stringToUnsLongLong(const QChar *begin, int len, int bas double QLocaleData::bytearrayToDouble(const char *num, bool *ok, bool *overflow) { - if (ok != 0) - *ok = true; - if (overflow != 0) - *overflow = false; - - if (*num == '\0') { - if (ok != 0) - *ok = false; - return 0.0; - } - - if (qstrcmp(num, "nan") == 0) - return qt_snan(); - - if (qstrcmp(num, "+inf") == 0 || qstrcmp(num, "inf") == 0) - return qt_inf(); - - if (qstrcmp(num, "-inf") == 0) - return -qt_inf(); - - bool _ok; - const char *endptr; - double d = qstrtod(num, &endptr, &_ok); - - if (!_ok) { - // the only way strtod can fail with *endptr != '\0' on a non-empty - // input string is overflow - if (ok != 0) - *ok = false; - if (overflow != 0) - *overflow = *endptr != '\0'; - return 0.0; - } - - if (*endptr != '\0') { - // we stopped at a non-digit character after converting some digits - if (ok != 0) - *ok = false; - if (overflow != 0) - *overflow = false; - return 0.0; - } - - if (ok != 0) - *ok = true; - if (overflow != 0) - *overflow = false; + bool nonNullOk = false; + int len = static_cast<int>(strlen(num)); + Q_ASSERT(len >= 0); + int processed = 0; + double d = asciiToDouble(num, len, nonNullOk, processed); + if (ok) + *ok = nonNullOk; + if (overflow) + *overflow = processed < len; return d; } diff --git a/src/corelib/tools/qlocale_p.h b/src/corelib/tools/qlocale_p.h index b3fd7a96b8..d943612ff1 100644 --- a/src/corelib/tools/qlocale_p.h +++ b/src/corelib/tools/qlocale_p.h @@ -165,6 +165,17 @@ public: QLocale::Country country); static const QLocaleData *c(); + // Maximum number of significant digits needed to represent a double. + // We cannot use std::numeric_limits here without constexpr. + static const int DoubleMantissaBits = 53; + static const int Log10_2_100000 = 30103; // log10(2) * 100000 + // same as C++11 std::numeric_limits<T>::max_digits10 + static const int DoubleMaxSignificant = (DoubleMantissaBits * Log10_2_100000) / 100000 + 2; + + // Maximum number of digits before decimal point to represent a double + // Same as std::numeric_limits<double>::max_exponent10 + 1 + static const int DoubleMaxDigitsBeforeDecimal = 309; + enum DoubleForm { DFExponent = 0, DFDecimal, diff --git a/src/corelib/tools/qlocale_tools.cpp b/src/corelib/tools/qlocale_tools.cpp index 03b911c4b3..18d1096c12 100644 --- a/src/corelib/tools/qlocale_tools.cpp +++ b/src/corelib/tools/qlocale_tools.cpp @@ -33,9 +33,12 @@ ****************************************************************************/ #include "qlocale_tools_p.h" +#include "qdoublescanprint_p.h" #include "qlocale_p.h" #include "qstring.h" +#include <private/qnumeric_p.h> + #include <ctype.h> #include <errno.h> #include <float.h> @@ -44,10 +47,6 @@ #include <stdlib.h> #include <time.h> -#ifdef Q_OS_WINCE -# include "qfunctions_wince.h" // for _control87 -#endif - #if defined(Q_OS_LINUX) && !defined(__UCLIBC__) # include <fenv.h> #endif @@ -68,6 +67,297 @@ QT_BEGIN_NAMESPACE #include "../../3rdparty/freebsd/strtoull.c" #include "../../3rdparty/freebsd/strtoll.c" +QT_CLOCALE_HOLDER + +void doubleToAscii(double d, QLocaleData::DoubleForm form, int precision, char *buf, int bufSize, + bool &sign, int &length, int &decpt) +{ + if (bufSize == 0) { + decpt = 0; + sign = d < 0; + length = 0; + return; + } + + // Detect special numbers (nan, +/-inf) + // We cannot use the high-level API of libdouble-conversion as we need to apply locale-specific + // formatting, such as decimal points, thousands-separators, etc. Because of this, we have to + // check for infinity and NaN before calling DoubleToAscii. + if (qt_is_inf(d)) { + sign = d < 0; + if (bufSize >= 3) { + buf[0] = 'i'; + buf[1] = 'n'; + buf[2] = 'f'; + length = 3; + } else { + length = 0; + } + return; + } else if (qt_is_nan(d)) { + if (bufSize >= 3) { + buf[0] = 'n'; + buf[1] = 'a'; + buf[2] = 'n'; + length = 3; + } else { + length = 0; + } + return; + } + + if (form == QLocaleData::DFSignificantDigits && precision == 0) + precision = 1; // 0 significant digits is silently converted to 1 + +#if !defined(QT_NO_DOUBLECONVERSION) && !defined(QT_BOOTSTRAPPED) + // one digit before the decimal dot, counts as significant digit for DoubleToStringConverter + if (form == QLocaleData::DFExponent && precision >= 0) + ++precision; + + double_conversion::DoubleToStringConverter::DoubleToAscii(d, + form == QLocaleData::DFDecimal ? double_conversion::DoubleToStringConverter::FIXED : + double_conversion::DoubleToStringConverter::PRECISION, + precision, buf, bufSize, &sign, &length, &decpt); +#else // QT_NO_DOUBLECONVERSION || QT_BOOTSTRAPPED + + // Cut the precision at 999, to fit it into the format string. We can't get more than 17 + // significant digits, so anything after that is mostly noise. You do get closer to the "middle" + // of the range covered by the given double with more digits, so to a degree it does make sense + // to honor higher precisions. We define that at more than 999 digits that is not the case. + if (precision > 999) + precision = 999; + + if (isZero(d)) { + // Negative zero is expected as simple "0", not "-0". We cannot do d < 0, though. + sign = false; + buf[0] = '0'; + length = 1; + decpt = 1; + return; + } else if (d < 0) { + sign = true; + d = -d; + } else { + sign = false; + } + + const int formatLength = 7; // '%', '.', 3 digits precision, 'f', '\0' + char format[formatLength]; + format[formatLength - 1] = '\0'; + format[0] = '%'; + format[1] = '.'; + format[2] = char((precision / 100) % 10) + '0'; + format[3] = char((precision / 10) % 10) + '0'; + format[4] = char(precision % 10) + '0'; + int extraChars; + switch (form) { + case QLocaleData::DFDecimal: + format[formatLength - 2] = 'f'; + // <anything> '.' <precision> '\0' - optimize for numbers smaller than 512k + extraChars = (d > (1 << 19) ? QLocaleData::DoubleMaxDigitsBeforeDecimal : 6) + 2; + break; + case QLocaleData::DFExponent: + format[formatLength - 2] = 'e'; + // '.', 'e', '-', <exponent> '\0' + extraChars = 7; + break; + case QLocaleData::DFSignificantDigits: + format[formatLength - 2] = 'g'; + + // either the same as in the 'e' case, or '.' and '\0' + // precision covers part before '.' + extraChars = 7; + break; + default: + Q_UNREACHABLE(); + } + + QVarLengthArray<char> target(precision + extraChars); + + length = qDoubleSnprintf(target.data(), target.size(), QT_CLOCALE, format, d); + int firstSignificant = 0; + int decptInTarget = length; + + // Find the first significant digit (not 0), and note any '.' we encounter. + // There is no '-' at the front of target because we made sure d > 0 above. + while (firstSignificant < length) { + if (target[firstSignificant] == '.') + decptInTarget = firstSignificant; + else if (target[firstSignificant] != '0') + break; + ++firstSignificant; + } + + // If no '.' found so far, search the rest of the target buffer for it. + if (decptInTarget == length) + decptInTarget = std::find(target.data() + firstSignificant, target.data() + length, '.') - + target.data(); + + int eSign = length; + if (form != QLocaleData::DFDecimal) { + // In 'e' or 'g' form, look for the 'e'. + eSign = std::find(target.data() + firstSignificant, target.data() + length, 'e') - + target.data(); + + if (eSign < length) { + // If 'e' is found, the final decimal point is determined by the number after 'e'. + // Mind that the final decimal point, decpt, is the offset of the decimal point from the + // start of the resulting string in buf. It may be negative or larger than bufSize, in + // which case the missing digits are zeroes. In the 'e' case decptInTarget is always 1, + // as variants of snprintf always generate numbers with one digit before the '.' then. + // This is why the final decimal point is offset by 1, relative to the number after 'e'. + bool ok; + const char *endptr; + decpt = qstrtoll(target.data() + eSign + 1, &endptr, 10, &ok) + 1; + Q_ASSERT(ok); + Q_ASSERT(endptr - target.data() <= length - eSign -1); + } else { + // No 'e' found, so it's the 'f' form. Variants of snprintf generate numbers with + // potentially multiple digits before the '.', but without decimal exponent then. So we + // get the final decimal point from the position of the '.'. The '.' itself takes up one + // character. We adjust by 1 below if that gets in the way. + decpt = decptInTarget - firstSignificant; + } + } else { + // In 'f' form, there can not be an 'e', so it's enough to look for the '.' + // (and possibly adjust by 1 below) + decpt = decptInTarget - firstSignificant; + } + + // Move the actual digits from the snprintf target to the actual buffer. + if (decptInTarget > firstSignificant) { + // First move the digits before the '.', if any + int lengthBeforeDecpt = decptInTarget - firstSignificant; + memcpy(buf, target.data() + firstSignificant, qMin(lengthBeforeDecpt, bufSize)); + if (eSign > decptInTarget && lengthBeforeDecpt < bufSize) { + // Then move any remaining digits, until 'e' + memcpy(buf + lengthBeforeDecpt, target.data() + decptInTarget + 1, + qMin(eSign - decptInTarget - 1, bufSize - lengthBeforeDecpt)); + // The final length of the output is the distance between the first significant digit + // and 'e' minus 1, for the '.', except if the buffer is smaller. + length = qMin(eSign - firstSignificant - 1, bufSize); + } else { + // 'e' was before the decpt or things didn't fit. Don't subtract the '.' from the length. + length = qMin(eSign - firstSignificant, bufSize); + } + } else { + if (eSign > firstSignificant) { + // If there are any significant digits at all, they are all after the '.' now. + // Just copy them straight away. + memcpy(buf, target.data() + firstSignificant, qMin(eSign - firstSignificant, bufSize)); + + // The decimal point was before the first significant digit, so we were one off above. + // Consider 0.1 - buf will be just '1', and decpt should be 0. But + // "decptInTarget - firstSignificant" will yield -1. + ++decpt; + length = qMin(eSign - firstSignificant, bufSize); + } else { + // No significant digits means the number is just 0. + buf[0] = '0'; + length = 1; + decpt = 1; + } + } +#endif // QT_NO_DOUBLECONVERSION || QT_BOOTSTRAPPED +} + +double asciiToDouble(const char *num, int numLen, bool &ok, int &processed) +{ + if (*num == '\0') { + ok = false; + processed = 0; + return 0.0; + } + + ok = true; + + // We have to catch NaN before because we need NaN as marker for "garbage" in the + // libdouble-conversion case and, in contrast to libdouble-conversion or sscanf, we don't allow + // "-nan" or "+nan" + if (qstrcmp(num, "nan") == 0) { + processed = 3; + return qt_snan(); + } else if ((num[0] == '-' || num[0] == '+') && qstrcmp(num + 1, "nan") == 0) { + processed = 0; + ok = false; + return 0.0; + } + + // Infinity values are implementation defined in the sscanf case. In the libdouble-conversion + // case we need infinity as overflow marker. + if (qstrcmp(num, "+inf") == 0) { + processed = 4; + return qt_inf(); + } else if (qstrcmp(num, "inf") == 0) { + processed = 3; + return qt_inf(); + } else if (qstrcmp(num, "-inf") == 0) { + processed = 4; + return -qt_inf(); + } + + double d = 0.0; +#if !defined(QT_NO_DOUBLECONVERSION) && !defined(QT_BOOTSTRAPPED) + int conv_flags = double_conversion::StringToDoubleConverter::NO_FLAGS; + double_conversion::StringToDoubleConverter conv(conv_flags, 0.0, qt_snan(), 0, 0); + d = conv.StringToDouble(num, numLen, &processed); + + if (!qIsFinite(d)) { + ok = false; + if (qIsNaN(d)) { + // Garbage found. We don't accept it and return 0. + processed = 0; + return 0.0; + } else { + // Overflow. That's not OK, but we still return infinity. + return d; + } + } +#else + if (qDoubleSscanf(num, QT_CLOCALE, "%lf%n", &d, &processed) < 1) + processed = 0; + + if (processed != numLen || qIsNaN(d)) { + // Implementation defined nan symbol or garbage found. We don't accept it. + processed = 0; + ok = false; + return 0.0; + } + + if (!qIsFinite(d)) { + // Overflow. Check for implementation-defined infinity symbols and reject them. + // We assume that any infinity symbol has to contain a character that cannot be part of a + // "normal" number (that is 0-9, ., -, +, e). + ok = false; + for (int i = 0; i < numLen; ++i) { + char c = num[i]; + if ((c < '0' || c > '9') && c != '.' && c != '-' && c != '+' && c != 'e') { + // Garbage found + processed = 0; + return 0.0; + } + } + return d; + } +#endif // !defined(QT_NO_DOUBLECONVERSION) && !defined(QT_BOOTSTRAPPED) + + Q_ASSERT(processed == numLen); // Otherwise we would have gotten NaN or sorted it out above. + + // Check if underflow has occurred. + if (isZero(d)) { + for (int i = 0; i < numLen; ++i) { + if (num[i] >= '1' && num[i] <= '9') { + // if a digit before any 'e' is not 0, then a non-zero number was intended. + ok = false; + return 0.0; + } else if (num[i] == 'e') { + break; + } + } + } + return d; +} + unsigned long long qstrtoull(const char * nptr, const char **endptr, int base, bool *ok) { @@ -114,9 +404,6 @@ qstrtoll(const char * nptr, const char **endptr, int base, bool *ok) return result; } -static char *_qdtoa( NEEDS_VOLATILE double d, int mode, int ndigits, int *decpt, - int *sign, char **rve, char **digits_str); - QString qulltoa(qulonglong l, int base, const QChar _zero) { ushort buff[65]; // length of MAX_ULLONG in base 2 @@ -225,2393 +512,41 @@ QString &exponentForm(QChar zero, QChar decimal, QChar exponential, return digits; } -/* From: NetBSD: strtod.c,v 1.26 1998/02/03 18:44:21 perry Exp */ -/* $FreeBSD: src/lib/libc/stdlib/netbsd_strtod.c,v 1.2.2.2 2001/03/02 17:14:15 tegge Exp $ */ - -/* Please send bug reports to - David M. Gay - AT&T Bell Laboratories, Room 2C-463 - 600 Mountain Avenue - Murray Hill, NJ 07974-2070 - U.S.A. - dmg@research.att.com or research!dmg - */ - -/* strtod for IEEE-, VAX-, and IBM-arithmetic machines. - * - * This strtod returns a nearest machine number to the input decimal - * string (or sets errno to ERANGE). With IEEE arithmetic, ties are - * broken by the IEEE round-even rule. Otherwise ties are broken by - * biased rounding (add half and chop). - * - * Inspired loosely by William D. Clinger's paper "How to Read Floating - * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. - * - * Modifications: - * - * 1. We only require IEEE, IBM, or VAX double-precision - * arithmetic (not IEEE double-extended). - * 2. We get by with floating-point arithmetic in a case that - * Clinger missed -- when we're computing d * 10^n - * for a small integer d and the integer n is not too - * much larger than 22 (the maximum integer k for which - * we can represent 10^k exactly), we may be able to - * compute (d*10^k) * 10^(e-k) with just one roundoff. - * 3. Rather than a bit-at-a-time adjustment of the binary - * result in the hard case, we use floating-point - * arithmetic to determine the adjustment to within - * one bit; only in really hard cases do we need to - * compute a second residual. - * 4. Because of 3., we don't need a large table of powers of 10 - * for ten-to-e (just some small tables, e.g. of 10^k - * for 0 <= k <= 22). - */ - -/* - * #define IEEE_LITTLE_ENDIAN for IEEE-arithmetic machines where the least - * significant byte has the lowest address. - * #define IEEE_BIG_ENDIAN for IEEE-arithmetic machines where the most - * significant byte has the lowest address. - * #define Long int on machines with 32-bit ints and 64-bit longs. - * #define Sudden_Underflow for IEEE-format machines without gradual - * underflow (i.e., that flush to zero on underflow). - * #define IBM for IBM mainframe-style floating-point arithmetic. - * #define VAX for VAX-style floating-point arithmetic. - * #define Unsigned_Shifts if >> does treats its left operand as unsigned. - * #define No_leftright to omit left-right logic in fast floating-point - * computation of dtoa. - * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3. - * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines - * that use extended-precision instructions to compute rounded - * products and quotients) with IBM. - * #define ROUND_BIASED for IEEE-format with biased rounding. - * #define Inaccurate_Divide for IEEE-format with correctly rounded - * products but inaccurate quotients, e.g., for Intel i860. - * #define Just_16 to store 16 bits per 32-bit Long when doing high-precision - * integer arithmetic. Whether this speeds things up or slows things - * down depends on the machine and the number being converted. - * #define KR_headers for old-style C function headers. - * #define Bad_float_h if your system lacks a float.h or if it does not - * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, - * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. - * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n) - * if memory is available and otherwise does something you deem - * appropriate. If MALLOC is undefined, malloc will be invoked - * directly -- and assumed always to succeed. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -__RCSID("$NetBSD: strtod.c,v 1.26 1998/02/03 18:44:21 perry Exp $"); -#endif /* LIBC_SCCS and not lint */ - -/* -#if defined(__m68k__) || defined(__sparc__) || defined(__i386__) || \ - defined(__mips__) || defined(__ns32k__) || defined(__alpha__) || \ - defined(__powerpc__) || defined(Q_OS_WIN) || defined(Q_OS_DARWIN) || defined(Q_OS_MAC) || \ - defined(mips) || defined(Q_OS_AIX) || defined(Q_OS_SOLARIS) -# define IEEE_BIG_OR_LITTLE_ENDIAN 1 -#endif -*/ - -// *All* of our architectures have IEEE arithmetic, don't they? -#define IEEE_BIG_OR_LITTLE_ENDIAN 1 - -#ifdef __arm32__ -/* - * Although the CPU is little endian the FP has different - * byte and word endianness. The byte order is still little endian - * but the word order is big endian. - */ -#define IEEE_BIG_OR_LITTLE_ENDIAN -#endif - -#ifdef vax -#define VAX -#endif - -#define Long qint32 -#define ULong quint32 - -#define MALLOC malloc - -#ifdef BSD_QDTOA_DEBUG -QT_BEGIN_INCLUDE_NAMESPACE -#include <stdio.h> -QT_END_INCLUDE_NAMESPACE - -#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);} -#endif - -#ifdef Unsigned_Shifts -#define Sign_Extend(a,b) if (b < 0) a |= 0xffff0000; -#else -#define Sign_Extend(a,b) /*no-op*/ -#endif - -#if (defined(IEEE_BIG_OR_LITTLE_ENDIAN) + defined(VAX) + defined(IBM)) != 1 -#error Exactly one of IEEE_BIG_OR_LITTLE_ENDIAN, VAX, or IBM should be defined. -#endif - -static inline ULong getWord0(const NEEDS_VOLATILE double x) -{ - const NEEDS_VOLATILE uchar *ptr = reinterpret_cast<const NEEDS_VOLATILE uchar *>(&x); - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - return (ptr[0]<<24) + (ptr[1]<<16) + (ptr[2]<<8) + ptr[3]; - } else { - return (ptr[7]<<24) + (ptr[6]<<16) + (ptr[5]<<8) + ptr[4]; - } -} - -static inline void setWord0(NEEDS_VOLATILE double *x, ULong l) -{ - NEEDS_VOLATILE uchar *ptr = reinterpret_cast<NEEDS_VOLATILE uchar *>(x); - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - ptr[0] = uchar(l>>24); - ptr[1] = uchar(l>>16); - ptr[2] = uchar(l>>8); - ptr[3] = uchar(l); - } else { - ptr[7] = uchar(l>>24); - ptr[6] = uchar(l>>16); - ptr[5] = uchar(l>>8); - ptr[4] = uchar(l); - } -} - -static inline ULong getWord1(const NEEDS_VOLATILE double x) -{ - const NEEDS_VOLATILE uchar *ptr = reinterpret_cast<const NEEDS_VOLATILE uchar *>(&x); - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - return (ptr[4]<<24) + (ptr[5]<<16) + (ptr[6]<<8) + ptr[7]; - } else { - return (ptr[3]<<24) + (ptr[2]<<16) + (ptr[1]<<8) + ptr[0]; - } -} -static inline void setWord1(NEEDS_VOLATILE double *x, ULong l) -{ - NEEDS_VOLATILE uchar *ptr = reinterpret_cast<uchar NEEDS_VOLATILE *>(x); - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - ptr[4] = uchar(l>>24); - ptr[5] = uchar(l>>16); - ptr[6] = uchar(l>>8); - ptr[7] = uchar(l); - } else { - ptr[3] = uchar(l>>24); - ptr[2] = uchar(l>>16); - ptr[1] = uchar(l>>8); - ptr[0] = uchar(l); - } -} - -static inline void Storeinc(ULong *&a, const ULong &b, const ULong &c) -{ - - *a = (ushort(b) << 16) | ushort(c); - ++a; -} - -/* #define P DBL_MANT_DIG */ -/* Ten_pmax = floor(P*log(2)/log(5)) */ -/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ -/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ -/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ - -#if defined(IEEE_BIG_OR_LITTLE_ENDIAN) -#define Exp_shift 20 -#define Exp_shift1 20 -#define Exp_msk1 0x100000 -#define Exp_msk11 0x100000 -#define Exp_mask 0x7ff00000 -#define P 53 -#define Bias 1023 -#define IEEE_Arith -#define Emin (-1022) -#define Exp_1 0x3ff00000 -#define Exp_11 0x3ff00000 -#define Ebits 11 -#define Frac_mask 0xfffff -#define Frac_mask1 0xfffff -#define Ten_pmax 22 -#define Bletch 0x10 -#define Bndry_mask 0xfffff -#define Bndry_mask1 0xfffff -#if defined(LSB) && defined(Q_OS_VXWORKS) -#undef LSB -#endif -#define LSB 1 -#define Sign_bit 0x80000000 -#define Log2P 1 -#define Tiny0 0 -#define Tiny1 1 -#define Quick_max 14 -#define Int_max 14 -#define Infinite(x) (getWord0(x) == 0x7ff00000) /* sufficient test for here */ -#else -#undef Sudden_Underflow -#define Sudden_Underflow -#ifdef IBM -#define Exp_shift 24 -#define Exp_shift1 24 -#define Exp_msk1 0x1000000 -#define Exp_msk11 0x1000000 -#define Exp_mask 0x7f000000 -#define P 14 -#define Bias 65 -#define Exp_1 0x41000000 -#define Exp_11 0x41000000 -#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ -#define Frac_mask 0xffffff -#define Frac_mask1 0xffffff -#define Bletch 4 -#define Ten_pmax 22 -#define Bndry_mask 0xefffff -#define Bndry_mask1 0xffffff -#define LSB 1 -#define Sign_bit 0x80000000 -#define Log2P 4 -#define Tiny0 0x100000 -#define Tiny1 0 -#define Quick_max 14 -#define Int_max 15 -#else /* VAX */ -#define Exp_shift 23 -#define Exp_shift1 7 -#define Exp_msk1 0x80 -#define Exp_msk11 0x800000 -#define Exp_mask 0x7f80 -#define P 56 -#define Bias 129 -#define Exp_1 0x40800000 -#define Exp_11 0x4080 -#define Ebits 8 -#define Frac_mask 0x7fffff -#define Frac_mask1 0xffff007f -#define Ten_pmax 24 -#define Bletch 2 -#define Bndry_mask 0xffff007f -#define Bndry_mask1 0xffff007f -#define LSB 0x10000 -#define Sign_bit 0x8000 -#define Log2P 1 -#define Tiny0 0x80 -#define Tiny1 0 -#define Quick_max 15 -#define Int_max 15 -#endif -#endif - -#ifndef IEEE_Arith -#define ROUND_BIASED -#endif - -#ifdef RND_PRODQUOT -#define rounded_product(a,b) a = rnd_prod(a, b) -#define rounded_quotient(a,b) a = rnd_quot(a, b) -extern double rnd_prod(double, double), rnd_quot(double, double); -#else -#define rounded_product(a,b) a *= b -#define rounded_quotient(a,b) a /= b -#endif - -#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) -#define Big1 0xffffffff - -#ifndef Just_16 -/* When Pack_32 is not defined, we store 16 bits per 32-bit Long. - * This makes some inner loops simpler and sometimes saves work - * during multiplications, but it often seems to make things slightly - * slower. Hence the default is now to store 32 bits per Long. - */ -#ifndef Pack_32 -#define Pack_32 -#endif -#endif - -#define Kmax 15 - -struct -Bigint { - struct Bigint *next; - int k, maxwds, sign, wds; - ULong x[1]; -}; - - typedef struct Bigint Bigint; - -static Bigint *Balloc(int k) -{ - int x; - Bigint *rv; - - x = 1 << k; - rv = static_cast<Bigint *>(MALLOC(sizeof(Bigint) + (x-1)*sizeof(Long))); - Q_CHECK_PTR(rv); - rv->k = k; - rv->maxwds = x; - rv->sign = rv->wds = 0; - return rv; -} - -static void Bfree(Bigint *v) -{ - free(v); -} - -#define Bcopy(x,y) memcpy(reinterpret_cast<char *>(&x->sign), reinterpret_cast<char *>(&y->sign), \ -y->wds*sizeof(Long) + 2*sizeof(int)) - -/* multiply by m and add a */ -static Bigint *multadd(Bigint *b, int m, int a) -{ - int i, wds; - ULong *x, y; -#ifdef Pack_32 - ULong xi, z; -#endif - Bigint *b1; - - wds = b->wds; - x = b->x; - i = 0; - do { -#ifdef Pack_32 - xi = *x; - y = (xi & 0xffff) * m + a; - z = (xi >> 16) * m + (y >> 16); - a = (z >> 16); - *x++ = (z << 16) + (y & 0xffff); -#else - y = *x * m + a; - a = (y >> 16); - *x++ = y & 0xffff; -#endif - } - while(++i < wds); - if (a) { - if (wds >= b->maxwds) { - b1 = Balloc(b->k+1); - Bcopy(b1, b); - Bfree(b); - b = b1; - } - b->x[wds++] = a; - b->wds = wds; - } - return b; -} - -static Bigint *s2b(const char *s, int nd0, int nd, ULong y9) -{ - Bigint *b; - int i, k; - Long x, y; - - x = (nd + 8) / 9; - for(k = 0, y = 1; x > y; y <<= 1, k++) ; -#ifdef Pack_32 - b = Balloc(k); - b->x[0] = y9; - b->wds = 1; -#else - b = Balloc(k+1); - b->x[0] = y9 & 0xffff; - b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; -#endif - - i = 9; - if (9 < nd0) { - s += 9; - do b = multadd(b, 10, *s++ - '0'); - while(++i < nd0); - s++; - } - else - s += 10; - for(; i < nd; i++) - b = multadd(b, 10, *s++ - '0'); - return b; -} - -static int hi0bits(ULong x) -{ - int k = 0; - - if (!(x & 0xffff0000)) { - k = 16; - x <<= 16; - } - if (!(x & 0xff000000)) { - k += 8; - x <<= 8; - } - if (!(x & 0xf0000000)) { - k += 4; - x <<= 4; - } - if (!(x & 0xc0000000)) { - k += 2; - x <<= 2; - } - if (!(x & 0x80000000)) { - k++; - if (!(x & 0x40000000)) - return 32; - } - return k; -} - -static int lo0bits(ULong *y) -{ - int k; - ULong x = *y; - - if (x & 7) { - if (x & 1) - return 0; - if (x & 2) { - *y = x >> 1; - return 1; - } - *y = x >> 2; - return 2; - } - k = 0; - if (!(x & 0xffff)) { - k = 16; - x >>= 16; - } - if (!(x & 0xff)) { - k += 8; - x >>= 8; - } - if (!(x & 0xf)) { - k += 4; - x >>= 4; - } - if (!(x & 0x3)) { - k += 2; - x >>= 2; - } - if (!(x & 1)) { - k++; - x >>= 1; - if (!x & 1) - return 32; - } - *y = x; - return k; -} - -static Bigint *i2b(int i) -{ - Bigint *b; - - b = Balloc(1); - b->x[0] = i; - b->wds = 1; - return b; -} - -static Bigint *mult(Bigint *a, Bigint *b) -{ - Bigint *c; - int k, wa, wb, wc; - ULong carry, y, z; - ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0; -#ifdef Pack_32 - ULong z2; -#endif - - if (a->wds < b->wds) { - c = a; - a = b; - b = c; - } - k = a->k; - wa = a->wds; - wb = b->wds; - wc = wa + wb; - if (wc > a->maxwds) - k++; - c = Balloc(k); - for(x = c->x, xa = x + wc; x < xa; x++) - *x = 0; - xa = a->x; - xae = xa + wa; - xb = b->x; - xbe = xb + wb; - xc0 = c->x; -#ifdef Pack_32 - for(; xb < xbe; xb++, xc0++) { - if ((y = *xb & 0xffff) != 0) { - x = xa; - xc = xc0; - carry = 0; - do { - z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; - carry = z >> 16; - z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; - carry = z2 >> 16; - Storeinc(xc, z2, z); - } - while(x < xae); - *xc = carry; - } - if ((y = *xb >> 16) != 0) { - x = xa; - xc = xc0; - carry = 0; - z2 = *xc; - do { - z = (*x & 0xffff) * y + (*xc >> 16) + carry; - carry = z >> 16; - Storeinc(xc, z, z2); - z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; - carry = z2 >> 16; - } - while(x < xae); - *xc = z2; - } - } -#else - for(; xb < xbe; xc0++) { - if (y = *xb++) { - x = xa; - xc = xc0; - carry = 0; - do { - z = *x++ * y + *xc + carry; - carry = z >> 16; - *xc++ = z & 0xffff; - } - while(x < xae); - *xc = carry; - } - } -#endif - for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; - c->wds = wc; - return c; -} - -static Bigint *p5s; - -struct p5s_deleter -{ - ~p5s_deleter() - { - while (p5s) { - Bigint *next = p5s->next; - Bfree(p5s); - p5s = next; - } - } -}; - -static Bigint *pow5mult(Bigint *b, int k) -{ - Bigint *b1, *p5, *p51; - int i; - static const int p05[3] = { 5, 25, 125 }; - - if ((i = k & 3) != 0) - b = multadd(b, p05[i-1], 0); - - if (!(k >>= 2)) - return b; - if (!(p5 = p5s)) { - /* first time */ - static p5s_deleter deleter; - p5 = p5s = i2b(625); - p5->next = 0; - } - for(;;) { - if (k & 1) { - b1 = mult(b, p5); - Bfree(b); - b = b1; - } - if (!(k >>= 1)) - break; - if (!(p51 = p5->next)) { - p51 = p5->next = mult(p5,p5); - p51->next = 0; - } - p5 = p51; - } - return b; -} - -static Bigint *lshift(Bigint *b, int k) -{ - int i, k1, n, n1; - Bigint *b1; - ULong *x, *x1, *xe, z; - -#ifdef Pack_32 - n = k >> 5; -#else - n = k >> 4; -#endif - k1 = b->k; - n1 = n + b->wds + 1; - for(i = b->maxwds; n1 > i; i <<= 1) - k1++; - b1 = Balloc(k1); - x1 = b1->x; - for(i = 0; i < n; i++) - *x1++ = 0; - x = b->x; - xe = x + b->wds; -#ifdef Pack_32 - if (k &= 0x1f) { - k1 = 32 - k; - z = 0; - do { - *x1++ = *x << k | z; - z = *x++ >> k1; - } - while(x < xe); - if ((*x1 = z) != 0) - ++n1; - } -#else - if (k &= 0xf) { - k1 = 16 - k; - z = 0; - do { - *x1++ = *x << k & 0xffff | z; - z = *x++ >> k1; - } - while(x < xe); - if (*x1 = z) - ++n1; - } -#endif - else do - *x1++ = *x++; - while(x < xe); - b1->wds = n1 - 1; - Bfree(b); - return b1; -} - -static int cmp(Bigint *a, Bigint *b) -{ - ULong *xa, *xa0, *xb, *xb0; - int i, j; - - i = a->wds; - j = b->wds; -#ifdef BSD_QDTOA_DEBUG - if (i > 1 && !a->x[i-1]) - Bug("cmp called with a->x[a->wds-1] == 0"); - if (j > 1 && !b->x[j-1]) - Bug("cmp called with b->x[b->wds-1] == 0"); -#endif - if (i -= j) - return i; - xa0 = a->x; - xa = xa0 + j; - xb0 = b->x; - xb = xb0 + j; - for(;;) { - if (*--xa != *--xb) - return *xa < *xb ? -1 : 1; - if (xa <= xa0) - break; - } - return 0; -} - -static Bigint *diff(Bigint *a, Bigint *b) -{ - Bigint *c; - int i, wa, wb; - Long borrow, y; /* We need signed shifts here. */ - ULong *xa, *xae, *xb, *xbe, *xc; -#ifdef Pack_32 - Long z; -#endif - - i = cmp(a,b); - if (!i) { - c = Balloc(0); - c->wds = 1; - c->x[0] = 0; - return c; - } - if (i < 0) { - c = a; - a = b; - b = c; - i = 1; - } - else - i = 0; - c = Balloc(a->k); - c->sign = i; - wa = a->wds; - xa = a->x; - xae = xa + wa; - wb = b->wds; - xb = b->x; - xbe = xb + wb; - xc = c->x; - borrow = 0; -#ifdef Pack_32 - do { - y = (*xa & 0xffff) - (*xb & 0xffff) + borrow; - borrow = y >> 16; - Sign_Extend(borrow, y); - z = (*xa++ >> 16) - (*xb++ >> 16) + borrow; - borrow = z >> 16; - Sign_Extend(borrow, z); - Storeinc(xc, z, y); - } - while(xb < xbe); - while(xa < xae) { - y = (*xa & 0xffff) + borrow; - borrow = y >> 16; - Sign_Extend(borrow, y); - z = (*xa++ >> 16) + borrow; - borrow = z >> 16; - Sign_Extend(borrow, z); - Storeinc(xc, z, y); - } -#else - do { - y = *xa++ - *xb++ + borrow; - borrow = y >> 16; - Sign_Extend(borrow, y); - *xc++ = y & 0xffff; - } - while(xb < xbe); - while(xa < xae) { - y = *xa++ + borrow; - borrow = y >> 16; - Sign_Extend(borrow, y); - *xc++ = y & 0xffff; - } -#endif - while(!*--xc) - wa--; - c->wds = wa; - return c; -} - -static double ulp(double x) +double qstrtod(const char *s00, const char **se, bool *ok) { - Long L; - double a; - - L = (getWord0(x) & Exp_mask) - (P-1)*Exp_msk1; -#ifndef Sudden_Underflow - if (L > 0) { -#endif -#ifdef IBM - L |= Exp_msk1 >> 4; -#endif - setWord0(&a, L); - setWord1(&a, 0); -#ifndef Sudden_Underflow - } - else { - L = -L >> Exp_shift; - if (L < Exp_shift) { - setWord0(&a, 0x80000 >> L); - setWord1(&a, 0); - } - else { - setWord0(&a, 0); - L -= Exp_shift; - setWord1(&a, L >= 31 ? 1U : 1U << (31 - L)); - } - } -#endif - return a; -} - -static double b2d(Bigint *a, int *e) -{ - ULong *xa, *xa0, w, y, z; - int k; - double d; - - xa0 = a->x; - xa = xa0 + a->wds; - y = *--xa; -#ifdef BSD_QDTOA_DEBUG - if (!y) Bug("zero y in b2d"); -#endif - k = hi0bits(y); - *e = 32 - k; -#ifdef Pack_32 - if (k < Ebits) { - setWord0(&d, Exp_1 | y >> (Ebits - k)); - w = xa > xa0 ? *--xa : 0; - setWord1(&d, y << ((32-Ebits) + k) | w >> (Ebits - k)); - goto ret_d; - } - z = xa > xa0 ? *--xa : 0; - if (k -= Ebits) { - setWord0(&d, Exp_1 | y << k | z >> (32 - k)); - y = xa > xa0 ? *--xa : 0; - setWord1(&d, z << k | y >> (32 - k)); - } - else { - setWord0(&d, Exp_1 | y); - setWord1(&d, z); - } -#else - if (k < Ebits + 16) { - z = xa > xa0 ? *--xa : 0; - setWord0(&d, Exp_1 | y << k - Ebits | z >> Ebits + 16 - k); - w = xa > xa0 ? *--xa : 0; - y = xa > xa0 ? *--xa : 0; - setWord1(&d, z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k); - goto ret_d; - } - z = xa > xa0 ? *--xa : 0; - w = xa > xa0 ? *--xa : 0; - k -= Ebits + 16; - setWord0(&d, Exp_1 | y << k + 16 | z << k | w >> 16 - k); - y = xa > xa0 ? *--xa : 0; - setWord1(&d, w << k + 16 | y << k); -#endif - ret_d: - return d; -} - -static Bigint *d2b(double d, int *e, int *bits) -{ - Bigint *b; - int de, i, k; - ULong *x, y, z; - -#ifdef Pack_32 - b = Balloc(1); -#else - b = Balloc(2); -#endif - x = b->x; - - z = getWord0(d) & Frac_mask; - setWord0(&d, getWord0(d) & 0x7fffffff); /* clear sign bit, which we ignore */ -#ifdef Sudden_Underflow - de = (int)(getWord0(d) >> Exp_shift); -#ifndef IBM - z |= Exp_msk11; -#endif -#else - if ((de = int(getWord0(d) >> Exp_shift)) != 0) - z |= Exp_msk1; -#endif -#ifdef Pack_32 - if ((y = getWord1(d)) != 0) { - if ((k = lo0bits(&y)) != 0) { - x[0] = y | z << (32 - k); - z >>= k; - } - else - x[0] = y; - i = b->wds = (x[1] = z) ? 2 : 1; - } - else { -#ifdef BSD_QDTOA_DEBUG - if (!z) - Bug("Zero passed to d2b"); -#endif - k = lo0bits(&z); - x[0] = z; - i = b->wds = 1; - k += 32; - } -#else - if (y = getWord1(d)) { - if (k = lo0bits(&y)) - if (k >= 16) { - x[0] = y | z << 32 - k & 0xffff; - x[1] = z >> k - 16 & 0xffff; - x[2] = z >> k; - i = 2; - } - else { - x[0] = y & 0xffff; - x[1] = y >> 16 | z << 16 - k & 0xffff; - x[2] = z >> k & 0xffff; - x[3] = z >> k+16; - i = 3; - } - else { - x[0] = y & 0xffff; - x[1] = y >> 16; - x[2] = z & 0xffff; - x[3] = z >> 16; - i = 3; - } - } - else { -#ifdef BSD_QDTOA_DEBUG - if (!z) - Bug("Zero passed to d2b"); -#endif - k = lo0bits(&z); - if (k >= 16) { - x[0] = z; - i = 0; - } - else { - x[0] = z & 0xffff; - x[1] = z >> 16; - i = 1; - } - k += 32; - } - while(!x[i]) - --i; - b->wds = i + 1; -#endif -#ifndef Sudden_Underflow - if (de) { -#endif -#ifdef IBM - *e = (de - Bias - (P-1) << 2) + k; - *bits = 4*P + 8 - k - hi0bits(getWord0(d) & Frac_mask); -#else - *e = de - Bias - (P-1) + k; - *bits = P - k; -#endif -#ifndef Sudden_Underflow - } - else { - *e = de - Bias - (P-1) + 1 + k; -#ifdef Pack_32 - *bits = 32*i - hi0bits(x[i-1]); -#else - *bits = (i+2)*16 - hi0bits(x[i]); -#endif - } -#endif - return b; -} - -static double ratio(Bigint *a, Bigint *b) -{ - double da, db; - int k, ka, kb; - - da = b2d(a, &ka); - db = b2d(b, &kb); -#ifdef Pack_32 - k = ka - kb + 32*(a->wds - b->wds); -#else - k = ka - kb + 16*(a->wds - b->wds); -#endif -#ifdef IBM - if (k > 0) { - setWord0(&da, getWord0(da) + (k >> 2)*Exp_msk1); - if (k &= 3) - da *= 1 << k; - } - else { - k = -k; - setWord0(&db, getWord0(db) + (k >> 2)*Exp_msk1); - if (k &= 3) - db *= 1 << k; - } -#else - if (k > 0) - setWord0(&da, getWord0(da) + k*Exp_msk1); - else { - k = -k; - setWord0(&db, getWord0(db) + k*Exp_msk1); - } -#endif - return da / db; -} - -static const double tens[] = { - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, - 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, - 1e20, 1e21, 1e22 -#ifdef VAX - , 1e23, 1e24 -#endif -}; - -#ifdef IEEE_Arith -static const double bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; -static const double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 }; -#define n_bigtens 5 -#else -#ifdef IBM -static const double bigtens[] = { 1e16, 1e32, 1e64 }; -static const double tinytens[] = { 1e-16, 1e-32, 1e-64 }; -#define n_bigtens 3 -#else -static const double bigtens[] = { 1e16, 1e32 }; -static const double tinytens[] = { 1e-16, 1e-32 }; -#define n_bigtens 2 -#endif -#endif - -/* - The pre-release gcc3.3 shipped with SuSE 8.2 has a bug which causes - the comparison 1e-100 == 0.0 to return true. As a workaround, we - compare it to a global variable containing 0.0, which produces - correct assembler output. - - ### consider detecting the broken compilers and using the static - ### double for these, and use a #define for all working compilers -*/ -static double g_double_zero = 0.0; - -Q_CORE_EXPORT double qstrtod(const char *s00, const char **se, bool *ok) -{ - int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign, - e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign; - const char *s, *s0, *s1; - double aadj, aadj1, adj, rv, rv0; - Long L; - ULong y, z; - Bigint *bb1, *bd0; - Bigint *bb = NULL, *bd = NULL, *bs = NULL, *delta = NULL;/* pacify gcc */ - - /* - #ifndef KR_headers - const char decimal_point = localeconv()->decimal_point[0]; - #else - const char decimal_point = '.'; - #endif */ - if (ok != 0) - *ok = true; - - const char decimal_point = '.'; - - sign = nz0 = nz = 0; - rv = 0.; - - - for(s = s00; ascii_isspace(uchar(*s)); s++) - ; - - if (*s == '-') { - sign = 1; - s++; - } else if (*s == '+') { - s++; - } - - if (*s == '\0') { - s = s00; - goto ret; - } - - if (*s == '0') { - nz0 = 1; - while(*++s == '0') ; - if (!*s) - goto ret; - } - s0 = s; - y = z = 0; - for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) - if (nd < 9) - y = 10*y + c - '0'; - else if (nd < 16) - z = 10*z + c - '0'; - nd0 = nd; - if (c == decimal_point) { - c = *++s; - if (!nd) { - for(; c == '0'; c = *++s) - nz++; - if (c > '0' && c <= '9') { - s0 = s; - nf += nz; - nz = 0; - goto have_dig; - } - goto dig_done; - } - for(; c >= '0' && c <= '9'; c = *++s) { - have_dig: - nz++; - if (c -= '0') { - nf += nz; - for(i = 1; i < nz; i++) - if (nd++ < 9) - y *= 10; - else if (nd <= DBL_DIG + 1) - z *= 10; - if (nd++ < 9) - y = 10*y + c; - else if (nd <= DBL_DIG + 1) - z = 10*z + c; - nz = 0; - } - } - } - dig_done: - e = 0; - if (c == 'e' || c == 'E') { - if (!nd && !nz && !nz0) { - s = s00; - goto ret; - } - s00 = s; - esign = 0; - switch(c = *++s) { - case '-': - esign = 1; - case '+': - c = *++s; - } - if (c >= '0' && c <= '9') { - while(c == '0') - c = *++s; - if (c > '0' && c <= '9') { - L = c - '0'; - s1 = s; - while((c = *++s) >= '0' && c <= '9') - L = 10*L + c - '0'; - if (s - s1 > 8 || L > 19999) - /* Avoid confusion from exponents - * so large that e might overflow. - */ - e = 19999; /* safe for 16 bit ints */ - else - e = int(L); - if (esign) - e = -e; - } - else - e = 0; - } - else - s = s00; - } - if (!nd) { - if (!nz && !nz0) - s = s00; - goto ret; - } - e1 = e -= nf; - - /* Now we have nd0 digits, starting at s0, followed by a - * decimal point, followed by nd-nd0 digits. The number we're - * after is the integer represented by those digits times - * 10**e */ - - if (!nd0) - nd0 = nd; - k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; - rv = y; - if (k > 9) - rv = tens[k - 9] * rv + z; - - bd0 = 0; - if (nd <= DBL_DIG -#ifndef RND_PRODQUOT - && FLT_ROUNDS == 1 -#endif - ) { - if (!e) - goto ret; - if (e > 0) { - if (e <= Ten_pmax) { -#ifdef VAX - goto vax_ovfl_check; -#else - /* rv = */ rounded_product(rv, tens[e]); - goto ret; -#endif - } - i = DBL_DIG - nd; - if (e <= Ten_pmax + i) { - /* A fancier test would sometimes let us do - * this for larger i values. - */ - e -= i; - rv *= tens[i]; -#ifdef VAX - /* VAX exponent range is so narrow we must - * worry about overflow here... - */ - vax_ovfl_check: - setWord0(&rv, getWord0(rv) - P*Exp_msk1); - /* rv = */ rounded_product(rv, tens[e]); - if ((getWord0(rv) & Exp_mask) - > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) - goto ovfl; - setWord0(&rv, getWord0(rv) + P*Exp_msk1); -#else - /* rv = */ rounded_product(rv, tens[e]); -#endif - goto ret; - } - } -#ifndef Inaccurate_Divide - else if (e >= -Ten_pmax) { - /* rv = */ rounded_quotient(rv, tens[-e]); - goto ret; - } -#endif - } - e1 += nd - k; - - /* Get starting approximation = rv * 10**e1 */ - - if (e1 > 0) { - if ((i = e1 & 15) != 0) - rv *= tens[i]; - if (e1 &= ~15) { - if (e1 > DBL_MAX_10_EXP) { - ovfl: - // errno = ERANGE; - if (ok != 0) - *ok = false; -#ifdef __STDC__ - rv = HUGE_VAL; -#else - /* Can't trust HUGE_VAL */ -#ifdef IEEE_Arith - setWord0(&rv, Exp_mask); - setWord1(&rv, 0); -#else - setWord0(&rv, Big0); - setWord1(&rv, Big1); -#endif -#endif - if (bd0) - goto retfree; - goto ret; - } - if (e1 >>= 4) { - for(j = 0; e1 > 1; j++, e1 >>= 1) - if (e1 & 1) - rv *= bigtens[j]; - /* The last multiplication could overflow. */ - setWord0(&rv, getWord0(rv) - P*Exp_msk1); - rv *= bigtens[j]; - if ((z = getWord0(rv) & Exp_mask) - > Exp_msk1*(DBL_MAX_EXP+Bias-P)) - goto ovfl; - if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { - /* set to largest number */ - /* (Can't trust DBL_MAX) */ - setWord0(&rv, Big0); - setWord1(&rv, Big1); - } - else - setWord0(&rv, getWord0(rv) + P*Exp_msk1); - } - - } - } - else if (e1 < 0) { - e1 = -e1; - if ((i = e1 & 15) != 0) - rv /= tens[i]; - if (e1 &= ~15) { - e1 >>= 4; - if (e1 >= 1 << n_bigtens) - goto undfl; - for(j = 0; e1 > 1; j++, e1 >>= 1) - if (e1 & 1) - rv *= tinytens[j]; - /* The last multiplication could underflow. */ - rv0 = rv; - rv *= tinytens[j]; - if (rv == g_double_zero) - { - rv = 2.*rv0; - rv *= tinytens[j]; - if (rv == g_double_zero) - { - undfl: - rv = 0.; - // errno = ERANGE; - if (ok != 0) - *ok = false; - if (bd0) - goto retfree; - goto ret; - } - setWord0(&rv, Tiny0); - setWord1(&rv, Tiny1); - /* The refinement below will clean - * this approximation up. - */ - } - } - } - - /* Now the hard part -- adjusting rv to the correct value.*/ - - /* Put digits into bd: true value = bd * 10^e */ - - bd0 = s2b(s0, nd0, nd, y); - - for(;;) { - bd = Balloc(bd0->k); - Bcopy(bd, bd0); - bb = d2b(rv, &bbe, &bbbits); /* rv = bb * 2^bbe */ - bs = i2b(1); - - if (e >= 0) { - bb2 = bb5 = 0; - bd2 = bd5 = e; - } - else { - bb2 = bb5 = -e; - bd2 = bd5 = 0; - } - if (bbe >= 0) - bb2 += bbe; - else - bd2 -= bbe; - bs2 = bb2; -#ifdef Sudden_Underflow -#ifdef IBM - j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3); -#else - j = P + 1 - bbbits; -#endif -#else - i = bbe + bbbits - 1; /* logb(rv) */ - if (i < Emin) /* denormal */ - j = bbe + (P-Emin); - else - j = P + 1 - bbbits; -#endif - bb2 += j; - bd2 += j; - i = bb2 < bd2 ? bb2 : bd2; - if (i > bs2) - i = bs2; - if (i > 0) { - bb2 -= i; - bd2 -= i; - bs2 -= i; - } - if (bb5 > 0) { - bs = pow5mult(bs, bb5); - bb1 = mult(bs, bb); - Bfree(bb); - bb = bb1; - } - if (bb2 > 0) - bb = lshift(bb, bb2); - if (bd5 > 0) - bd = pow5mult(bd, bd5); - if (bd2 > 0) - bd = lshift(bd, bd2); - if (bs2 > 0) - bs = lshift(bs, bs2); - delta = diff(bb, bd); - dsign = delta->sign; - delta->sign = 0; - i = cmp(delta, bs); - if (i < 0) { - /* Error is less than half an ulp -- check for - * special case of mantissa a power of two. - */ - if (dsign || getWord1(rv) || getWord0(rv) & Bndry_mask) - break; - delta = lshift(delta,Log2P); - if (cmp(delta, bs) > 0) - goto drop_down; - break; - } - if (i == 0) { - /* exactly half-way between */ - if (dsign) { - if ((getWord0(rv) & Bndry_mask1) == Bndry_mask1 - && getWord1(rv) == 0xffffffff) { - /*boundary case -- increment exponent*/ - setWord0(&rv, (getWord0(rv) & Exp_mask) - + Exp_msk1 -#ifdef IBM - | Exp_msk1 >> 4 -#endif - ); - setWord1(&rv, 0); - break; - } - } - else if (!(getWord0(rv) & Bndry_mask) && !getWord1(rv)) { - drop_down: - /* boundary case -- decrement exponent */ -#ifdef Sudden_Underflow - L = getWord0(rv) & Exp_mask; -#ifdef IBM - if (L < Exp_msk1) -#else - if (L <= Exp_msk1) -#endif - goto undfl; - L -= Exp_msk1; -#else - L = (getWord0(rv) & Exp_mask) - Exp_msk1; -#endif - setWord0(&rv, L | Bndry_mask1); - setWord1(&rv, 0xffffffff); -#ifdef IBM - goto cont; -#else - break; -#endif - } -#ifndef ROUND_BIASED - if (!(getWord1(rv) & LSB)) - break; -#endif - if (dsign) - rv += ulp(rv); -#ifndef ROUND_BIASED - else { - rv -= ulp(rv); -#ifndef Sudden_Underflow - if (rv == g_double_zero) - goto undfl; -#endif - } -#endif - break; - } - if ((aadj = ratio(delta, bs)) <= 2.) { - if (dsign) - aadj = aadj1 = 1.; - else if (getWord1(rv) || getWord0(rv) & Bndry_mask) { -#ifndef Sudden_Underflow - if (getWord1(rv) == Tiny1 && !getWord0(rv)) - goto undfl; -#endif - aadj = 1.; - aadj1 = -1.; - } - else { - /* special case -- power of FLT_RADIX to be */ - /* rounded down... */ - - if (aadj < 2./FLT_RADIX) - aadj = 1./FLT_RADIX; - else - aadj *= 0.5; - aadj1 = -aadj; - } - } - else { - aadj *= 0.5; - aadj1 = dsign ? aadj : -aadj; -#ifdef Check_FLT_ROUNDS - switch(FLT_ROUNDS) { - case 2: /* towards +infinity */ - aadj1 -= 0.5; - break; - case 0: /* towards 0 */ - case 3: /* towards -infinity */ - aadj1 += 0.5; - } -#else - if (FLT_ROUNDS == 0) - aadj1 += 0.5; -#endif - } - y = getWord0(rv) & Exp_mask; - - /* Check for overflow */ - - if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { - rv0 = rv; - setWord0(&rv, getWord0(rv) - P*Exp_msk1); - adj = aadj1 * ulp(rv); - rv += adj; - if ((getWord0(rv) & Exp_mask) >= - Exp_msk1*(DBL_MAX_EXP+Bias-P)) { - if (getWord0(rv0) == Big0 && getWord1(rv0) == Big1) - goto ovfl; - setWord0(&rv, Big0); - setWord1(&rv, Big1); - goto cont; - } - else - setWord0(&rv, getWord0(rv) + P*Exp_msk1); - } - else { -#ifdef Sudden_Underflow - if ((getWord0(rv) & Exp_mask) <= P*Exp_msk1) { - rv0 = rv; - setWord0(&rv, getWord0(rv) + P*Exp_msk1); - adj = aadj1 * ulp(rv); - rv += adj; -#ifdef IBM - if ((getWord0(rv) & Exp_mask) < P*Exp_msk1) -#else - if ((getWord0(rv) & Exp_mask) <= P*Exp_msk1) -#endif - { - if (getWord0(rv0) == Tiny0 - && getWord1(rv0) == Tiny1) - goto undfl; - setWord0(&rv, Tiny0); - setWord1(&rv, Tiny1); - goto cont; - } - else - setWord0(&rv, getWord0(rv) - P*Exp_msk1); - } - else { - adj = aadj1 * ulp(rv); - rv += adj; - } -#else - /* Compute adj so that the IEEE rounding rules will - * correctly round rv + adj in some half-way cases. - * If rv * ulp(rv) is denormalized (i.e., - * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid - * trouble from bits lost to denormalization; - * example: 1.2e-307 . - */ - if (y <= (P-1)*Exp_msk1 && aadj >= 1.) { - aadj1 = int(aadj + 0.5); - if (!dsign) - aadj1 = -aadj1; - } - adj = aadj1 * ulp(rv); - rv += adj; -#endif - } - z = getWord0(rv) & Exp_mask; - if (y == z) { - /* Can we stop now? */ - L = Long(aadj); - aadj -= L; - /* The tolerances below are conservative. */ - if (dsign || getWord1(rv) || getWord0(rv) & Bndry_mask) { - if (aadj < .4999999 || aadj > .5000001) - break; - } - else if (aadj < .4999999/FLT_RADIX) - break; - } - cont: - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(delta); - } - retfree: - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(bd0); - Bfree(delta); - ret: + int processed = 0; + bool nonNullOk = false; + int len = static_cast<int>(strlen(s00)); + Q_ASSERT(len >= 0); + double d = asciiToDouble(s00, len, nonNullOk, processed); if (se) - *se = s; - return sign ? -rv : rv; -} - -static int quorem(Bigint *b, Bigint *S) -{ - int n; - Long borrow, y; - ULong carry, q, ys; - ULong *bx, *bxe, *sx, *sxe; -#ifdef Pack_32 - Long z; - ULong si, zs; -#endif - - n = S->wds; -#ifdef BSD_QDTOA_DEBUG - /*debug*/ if (b->wds > n) - /*debug*/ Bug("oversize b in quorem"); -#endif - if (b->wds < n) - return 0; - sx = S->x; - sxe = sx + --n; - bx = b->x; - bxe = bx + n; - q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ -#ifdef BSD_QDTOA_DEBUG - /*debug*/ if (q > 9) - /*debug*/ Bug("oversized quotient in quorem"); -#endif - if (q) { - borrow = 0; - carry = 0; - do { -#ifdef Pack_32 - si = *sx++; - ys = (si & 0xffff) * q + carry; - zs = (si >> 16) * q + (ys >> 16); - carry = zs >> 16; - y = (*bx & 0xffff) - (ys & 0xffff) + borrow; - borrow = y >> 16; - Sign_Extend(borrow, y); - z = (*bx >> 16) - (zs & 0xffff) + borrow; - borrow = z >> 16; - Sign_Extend(borrow, z); - Storeinc(bx, z, y); -#else - ys = *sx++ * q + carry; - carry = ys >> 16; - y = *bx - (ys & 0xffff) + borrow; - borrow = y >> 16; - Sign_Extend(borrow, y); - *bx++ = y & 0xffff; -#endif - } - while(sx <= sxe); - if (!*bxe) { - bx = b->x; - while(--bxe > bx && !*bxe) - --n; - b->wds = n; - } - } - if (cmp(b, S) >= 0) { - q++; - borrow = 0; - carry = 0; - bx = b->x; - sx = S->x; - do { -#ifdef Pack_32 - si = *sx++; - ys = (si & 0xffff) + carry; - zs = (si >> 16) + (ys >> 16); - carry = zs >> 16; - y = (*bx & 0xffff) - (ys & 0xffff) + borrow; - borrow = y >> 16; - Sign_Extend(borrow, y); - z = (*bx >> 16) - (zs & 0xffff) + borrow; - borrow = z >> 16; - Sign_Extend(borrow, z); - Storeinc(bx, z, y); -#else - ys = *sx++ + carry; - carry = ys >> 16; - y = *bx - (ys & 0xffff) + borrow; - borrow = y >> 16; - Sign_Extend(borrow, y); - *bx++ = y & 0xffff; -#endif - } - while(sx <= sxe); - bx = b->x; - bxe = bx + n; - if (!*bxe) { - while(--bxe > bx && !*bxe) - --n; - b->wds = n; - } - } - return q; -} - -/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. - * - * Inspired by "How to Print Floating-Point Numbers Accurately" by - * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101]. - * - * Modifications: - * 1. Rather than iterating, we use a simple numeric overestimate - * to determine k = floor(log10(d)). We scale relevant - * quantities using O(log2(k)) rather than O(k) multiplications. - * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't - * try to generate digits strictly left to right. Instead, we - * compute with fewer bits and propagate the carry if necessary - * when rounding the final digit up. This is often faster. - * 3. Under the assumption that input will be rounded nearest, - * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. - * That is, we allow equality in stopping tests when the - * round-nearest rule will give the same floating-point value - * as would satisfaction of the stopping test with strict - * inequality. - * 4. We remove common factors of powers of 2 from relevant - * quantities. - * 5. When converting floating-point integers less than 1e16, - * we use floating-point arithmetic rather than resorting - * to multiple-precision integers. - * 6. When asked to produce fewer than 15 digits, we first try - * to get by with floating-point arithmetic; we resort to - * multiple-precision integer arithmetic only if we cannot - * guarantee that the floating-point calculation has given - * the correctly rounded result. For k requested digits and - * "uniformly" distributed input, the probability is - * something like 10^(k-15) that we must resort to the Long - * calculation. - */ - -#if defined(Q_OS_WIN) && defined (Q_CC_GNU) && !defined(_clear87) // See QTBUG-7576 -extern "C" { -__attribute__ ((dllimport)) unsigned int __cdecl __MINGW_NOTHROW _control87 (unsigned int unNew, unsigned int unMask); -__attribute__ ((dllimport)) unsigned int __cdecl __MINGW_NOTHROW _clearfp (void); /* Clear the FPU status word */ -} -# define _clear87 _clearfp -#endif - -/* This actually sometimes returns a pointer to a string literal - cast to a char*. Do NOT try to modify the return value. */ - -Q_CORE_EXPORT char *qdtoa ( double d, int mode, int ndigits, int *decpt, int *sign, char **rve, char **resultp) -{ - // Some values of the floating-point control word can cause _qdtoa to crash with an underflow. - // We set a safe value here. -#ifdef Q_OS_WIN - _clear87(); - unsigned int oldbits = _control87(0, 0); -#ifndef MCW_EM -# ifdef _MCW_EM -# define MCW_EM _MCW_EM -# else -# define MCW_EM 0x0008001F -# endif -#endif - _control87(MCW_EM, MCW_EM); -#endif - -#if defined(Q_OS_LINUX) && !defined(__UCLIBC__) - fenv_t envp; - feholdexcept(&envp); -#endif - - char *s = _qdtoa(d, mode, ndigits, decpt, sign, rve, resultp); - -#ifdef Q_OS_WIN - _clear87(); -#ifndef _M_X64 - _control87(oldbits, 0xFFFFF); -#else -# ifndef _MCW_EM // Potentially missing on MinGW -# define _MCW_EM 0x0008001f -# endif -# ifndef _MCW_RC -# define _MCW_RC 0x00000300 -# endif -# ifndef _MCW_DN -# define _MCW_DN 0x03000000 -# endif - _control87(oldbits, _MCW_EM|_MCW_DN|_MCW_RC); -#endif //_M_X64 -#endif //Q_OS_WIN - -#if defined(Q_OS_LINUX) && !defined(__UCLIBC__) - fesetenv(&envp); -#endif - - return s; + *se = s00 + processed; + if (ok) + *ok = nonNullOk; + return d; } -static char *_qdtoa( NEEDS_VOLATILE double d, int mode, int ndigits, int *decpt, int *sign, char **rve, char **resultp) +QString qdtoa(qreal d, int *decpt, int *sign) { - /* - Arguments ndigits, decpt, sign are similar to those - of ecvt and fcvt; trailing zeros are suppressed from - the returned string. If not null, *rve is set to point - to the end of the return value. If d is +-Infinity or NaN, - then *decpt is set to 9999. - - mode: - 0 ==> shortest string that yields d when read in - and rounded to nearest. - 1 ==> like 0, but with Steele & White stopping rule; - e.g. with IEEE P754 arithmetic , mode 0 gives - 1e23 whereas mode 1 gives 9.999999999999999e22. - 2 ==> max(1,ndigits) significant digits. This gives a - return value similar to that of ecvt, except - that trailing zeros are suppressed. - 3 ==> through ndigits past the decimal point. This - gives a return value similar to that from fcvt, - except that trailing zeros are suppressed, and - ndigits can be negative. - 4-9 should give the same return values as 2-3, i.e., - 4 <= mode <= 9 ==> same return as mode - 2 + (mode & 1). These modes are mainly for - debugging; often they run slower but sometimes - faster than modes 2-3. - 4,5,8,9 ==> left-to-right digit generation. - 6-9 ==> don't try fast floating-point estimate - (if applicable). - - Values of mode other than 0-9 are treated as mode 0. - - Sufficient space is allocated to the return value - to hold the suppressed trailing zeros. - */ - - int bbits, b2, b5, be, dig, i, ieps, ilim0, - j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, - try_quick; - int ilim = 0, ilim1 = 0, spec_case = 0; /* pacify gcc */ - Long L; -#ifndef Sudden_Underflow - int denorm; - ULong x; -#endif - Bigint *b, *b1, *delta, *mhi, *S; - Bigint *mlo = NULL; /* pacify gcc */ - double d2; - double ds, eps; - char *s, *s0; - - if (getWord0(d) & Sign_bit) { - /* set sign for everything, including 0's and NaNs */ - *sign = 1; - setWord0(&d, getWord0(d) & ~Sign_bit); /* clear sign bit */ - } - else - *sign = 0; - -#if defined(IEEE_Arith) + defined(VAX) -#ifdef IEEE_Arith - if ((getWord0(d) & Exp_mask) == Exp_mask) -#else - if (getWord0(d) == 0x8000) -#endif - { - /* Infinity or NaN */ - *decpt = 9999; - s = -#ifdef IEEE_Arith - !getWord1(d) && !(getWord0(d) & 0xfffff) ? const_cast<char*>("Infinity") : -#endif - const_cast<char*>("NaN"); - if (rve) - *rve = -#ifdef IEEE_Arith - s[3] ? s + 8 : -#endif - s + 3; - return s; - } -#endif -#ifdef IBM - d += 0; /* normalize */ -#endif - if (d == g_double_zero) - { - *decpt = 1; - s = const_cast<char*>("0"); - if (rve) - *rve = s + 1; - return s; - } - - b = d2b(d, &be, &bbits); - i = (int)(getWord0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); -#ifndef Sudden_Underflow - if (i != 0) { -#endif - d2 = d; - setWord0(&d2, getWord0(d2) & Frac_mask1); - setWord0(&d2, getWord0(d2) | Exp_11); -#ifdef IBM - if (j = 11 - hi0bits(getWord0(d2) & Frac_mask)) - d2 /= 1 << j; -#endif - - /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 - * log10(x) = log(x) / log(10) - * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) - * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) - * - * This suggests computing an approximation k to log10(d) by - * - * k = (i - Bias)*0.301029995663981 - * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); - * - * We want k to be too large rather than too small. - * The error in the first-order Taylor series approximation - * is in our favor, so we just round up the constant enough - * to compensate for any error in the multiplication of - * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, - * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, - * adding 1e-13 to the constant term more than suffices. - * Hence we adjust the constant term to 0.1760912590558. - * (We could get a more accurate k by invoking log10, - * but this is probably not worthwhile.) - */ - - i -= Bias; -#ifdef IBM - i <<= 2; - i += j; -#endif -#ifndef Sudden_Underflow - denorm = 0; - } - else { - /* d is denormalized */ - - i = bbits + be + (Bias + (P-1) - 1); - x = i > 32 ? getWord0(d) << (64 - i) | getWord1(d) >> (i - 32) - : getWord1(d) << (32 - i); - d2 = x; - setWord0(&d2, getWord0(d2) - 31*Exp_msk1); /* adjust exponent */ - i -= (Bias + (P-1) - 1) + 1; - denorm = 1; - } -#endif - ds = (d2-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; - k = int(ds); - if (ds < 0. && ds != k) - k--; /* want k = floor(ds) */ - k_check = 1; - if (k >= 0 && k <= Ten_pmax) { - if (d < tens[k]) - k--; - k_check = 0; - } - j = bbits - i - 1; - if (j >= 0) { - b2 = 0; - s2 = j; - } - else { - b2 = -j; - s2 = 0; - } - if (k >= 0) { - b5 = 0; - s5 = k; - s2 += k; - } - else { - b2 -= k; - b5 = -k; - s5 = 0; - } - if (mode < 0 || mode > 9) - mode = 0; - try_quick = 1; - if (mode > 5) { - mode -= 4; - try_quick = 0; - } - leftright = 1; - switch(mode) { - case 0: - case 1: - ilim = ilim1 = -1; - i = 18; - ndigits = 0; - break; - case 2: - leftright = 0; - /* no break */ - case 4: - if (ndigits <= 0) - ndigits = 1; - ilim = ilim1 = i = ndigits; - break; - case 3: - leftright = 0; - /* no break */ - case 5: - i = ndigits + k + 1; - ilim = i; - ilim1 = i - 1; - if (i <= 0) - i = 1; - } - QT_TRY { - *resultp = static_cast<char *>(malloc(i + 1)); - Q_CHECK_PTR(*resultp); - } QT_CATCH(...) { - Bfree(b); - QT_RETHROW; - } - s = s0 = *resultp; - - if (ilim >= 0 && ilim <= Quick_max && try_quick) { - - /* Try to get by with floating-point arithmetic. */ - - i = 0; - d2 = d; - k0 = k; - ilim0 = ilim; - ieps = 2; /* conservative */ - if (k > 0) { - ds = tens[k&0xf]; - j = k >> 4; - if (j & Bletch) { - /* prevent overflows */ - j &= Bletch - 1; - d /= bigtens[n_bigtens-1]; - ieps++; - } - for(; j; j >>= 1, i++) - if (j & 1) { - ieps++; - ds *= bigtens[i]; - } - d /= ds; - } - else if ((j1 = -k) != 0) { - d *= tens[j1 & 0xf]; - for(j = j1 >> 4; j; j >>= 1, i++) - if (j & 1) { - ieps++; - d *= bigtens[i]; - } - } - if (k_check && d < 1. && ilim > 0) { - if (ilim1 <= 0) - goto fast_failed; - ilim = ilim1; - k--; - d *= 10.; - ieps++; - } - eps = ieps*d + 7.; - setWord0(&eps, getWord0(eps) - (P-1)*Exp_msk1); - if (ilim == 0) { - S = mhi = 0; - d -= 5.; - if (d > eps) - goto one_digit; - if (d < -eps) - goto no_digits; - goto fast_failed; - } -#ifndef No_leftright - if (leftright) { - /* Use Steele & White method of only - * generating digits needed. - */ - eps = 0.5/tens[ilim-1] - eps; - for(i = 0;;) { - L = Long(d); - d -= L; - *s++ = '0' + int(L); - if (d < eps) - goto ret1; - if (1. - d < eps) - goto bump_up; - if (++i >= ilim) - break; - eps *= 10.; - d *= 10.; - } - } - else { -#endif - /* Generate ilim digits, then fix them up. */ - eps *= tens[ilim-1]; - for(i = 1;; i++, d *= 10.) { - L = Long(d); - d -= L; - *s++ = '0' + int(L); - if (i == ilim) { - if (d > 0.5 + eps) - goto bump_up; - else if (d < 0.5 - eps) { - while(*--s == '0') {} - s++; - goto ret1; - } - break; - } - } -#ifndef No_leftright - } -#endif - fast_failed: - s = s0; - d = d2; - k = k0; - ilim = ilim0; - } - - /* Do we have a "small" integer? */ - - if (be >= 0 && k <= Int_max) { - /* Yes. */ - ds = tens[k]; - if (ndigits < 0 && ilim <= 0) { - S = mhi = 0; - if (ilim < 0 || d <= 5*ds) - goto no_digits; - goto one_digit; - } - for(i = 1;; i++) { - L = Long(d / ds); - d -= L*ds; -#ifdef Check_FLT_ROUNDS - /* If FLT_ROUNDS == 2, L will usually be high by 1 */ - if (d < 0) { - L--; - d += ds; - } -#endif - *s++ = '0' + int(L); - if (i == ilim) { - d += d; - if (d > ds || (d == ds && L & 1)) { - bump_up: - while(*--s == '9') - if (s == s0) { - k++; - *s = '0'; - break; - } - ++*s++; - } - break; - } - if ((d *= 10.) == g_double_zero) - break; - } - goto ret1; - } - - m2 = b2; - m5 = b5; - mhi = mlo = 0; - if (leftright) { - if (mode < 2) { - i = -#ifndef Sudden_Underflow - denorm ? be + (Bias + (P-1) - 1 + 1) : -#endif -#ifdef IBM - 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3); -#else - 1 + P - bbits; -#endif - } - else { - j = ilim - 1; - if (m5 >= j) - m5 -= j; - else { - s5 += j -= m5; - b5 += j; - m5 = 0; - } - if ((i = ilim) < 0) { - m2 -= i; - i = 0; - } - } - b2 += i; - s2 += i; - mhi = i2b(1); - } - if (m2 > 0 && s2 > 0) { - i = m2 < s2 ? m2 : s2; - b2 -= i; - m2 -= i; - s2 -= i; - } - if (b5 > 0) { - if (leftright) { - if (m5 > 0) { - mhi = pow5mult(mhi, m5); - b1 = mult(mhi, b); - Bfree(b); - b = b1; - } - if ((j = b5 - m5) != 0) - b = pow5mult(b, j); - } - else - b = pow5mult(b, b5); - } - S = i2b(1); - if (s5 > 0) - S = pow5mult(S, s5); + bool nonNullSign = false; + int nonNullDecpt = 0; + int length = 0; - /* Check for special case that d is a normalized power of 2. */ + // Some versions of libdouble-conversion like an extra digit, probably for '\0' + char result[QLocaleData::DoubleMaxSignificant + 1]; + doubleToAscii(d, QLocaleData::DFSignificantDigits, QLocaleData::DoubleMaxSignificant, result, + QLocaleData::DoubleMaxSignificant + 1, nonNullSign, length, nonNullDecpt); - if (mode < 2) { - if (!getWord1(d) && !(getWord0(d) & Bndry_mask) -#ifndef Sudden_Underflow - && getWord0(d) & Exp_mask -#endif - ) { - /* The special case */ - b2 += Log2P; - s2 += Log2P; - spec_case = 1; - } - else - spec_case = 0; - } + // Skip trailing zeroes. The DoubleMaxSignificant precision is the worst case. + while (length > 0 && result[length - 1] == '0') + --length; - /* Arrange for convenient computation of quotients: - * shift left if necessary so divisor has 4 leading 0 bits. - * - * Perhaps we should just compute leading 28 bits of S once - * and for all and pass them and a shift to quorem, so it - * can do shifts and ors to compute the numerator for q. - */ -#ifdef Pack_32 - if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f) != 0) - i = 32 - i; -#else - if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf) - i = 16 - i; -#endif - if (i > 4) { - i -= 4; - b2 += i; - m2 += i; - s2 += i; - } - else if (i < 4) { - i += 28; - b2 += i; - m2 += i; - s2 += i; - } - if (b2 > 0) - b = lshift(b, b2); - if (s2 > 0) - S = lshift(S, s2); - if (k_check) { - if (cmp(b,S) < 0) { - k--; - b = multadd(b, 10, 0); /* we botched the k estimate */ - if (leftright) - mhi = multadd(mhi, 10, 0); - ilim = ilim1; - } - } - if (ilim <= 0 && mode > 2) { - if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) { - /* no digits, fcvt style */ - no_digits: - k = -1 - ndigits; - goto ret; - } - one_digit: - *s++ = '1'; - k++; - goto ret; - } - if (leftright) { - if (m2 > 0) - mhi = lshift(mhi, m2); - - /* Compute mlo -- check for special case - * that d is a normalized power of 2. - */ - - mlo = mhi; - if (spec_case) { - mhi = Balloc(mhi->k); - Bcopy(mhi, mlo); - mhi = lshift(mhi, Log2P); - } + if (sign) + *sign = nonNullSign ? 1 : 0; + if (decpt) + *decpt = nonNullDecpt; - for(i = 1;;i++) { - dig = quorem(b,S) + '0'; - /* Do we yet have the shortest decimal string - * that will round to d? - */ - j = cmp(b, mlo); - delta = diff(S, mhi); - j1 = delta->sign ? 1 : cmp(b, delta); - Bfree(delta); -#ifndef ROUND_BIASED - if (j1 == 0 && !mode && !(getWord1(d) & 1)) { - if (dig == '9') - goto round_9_up; - if (j > 0) - dig++; - *s++ = dig; - goto ret; - } -#endif - if (j < 0 || (j == 0 && !mode -#ifndef ROUND_BIASED - && !(getWord1(d) & 1) -#endif - )) { - if (j1 > 0) { - b = lshift(b, 1); - j1 = cmp(b, S); - if ((j1 > 0 || (j1 == 0 && dig & 1)) - && dig++ == '9') - goto round_9_up; - } - *s++ = dig; - goto ret; - } - if (j1 > 0) { - if (dig == '9') { /* possible if i == 1 */ - round_9_up: - *s++ = '9'; - goto roundoff; - } - *s++ = dig + 1; - goto ret; - } - *s++ = dig; - if (i == ilim) - break; - b = multadd(b, 10, 0); - if (mlo == mhi) - mlo = mhi = multadd(mhi, 10, 0); - else { - mlo = multadd(mlo, 10, 0); - mhi = multadd(mhi, 10, 0); - } - } - } - else - for(i = 1;; i++) { - *s++ = dig = quorem(b,S) + '0'; - if (i >= ilim) - break; - b = multadd(b, 10, 0); - } - - /* Round off last digit */ - - b = lshift(b, 1); - j = cmp(b, S); - if (j > 0 || (j == 0 && dig & 1)) { - roundoff: - while(*--s == '9') - if (s == s0) { - k++; - *s++ = '1'; - goto ret; - } - ++*s++; - } - else { - while(*--s == '0') {} - s++; - } - ret: - Bfree(S); - if (mhi) { - if (mlo && mlo != mhi) - Bfree(mlo); - Bfree(mhi); - } - ret1: - Bfree(b); - if (s == s0) { /* don't return empty string */ - *s++ = '0'; - k = 0; - } - *s = 0; - *decpt = k + 1; - if (rve) - *rve = s; - return s0; + return QLatin1String(result, length); } QT_END_NAMESPACE diff --git a/src/corelib/tools/qlocale_tools_p.h b/src/corelib/tools/qlocale_tools_p.h index 03f35209b4..840ae8f163 100644 --- a/src/corelib/tools/qlocale_tools_p.h +++ b/src/corelib/tools/qlocale_tools_p.h @@ -66,8 +66,13 @@ QT_BEGIN_NAMESPACE +double asciiToDouble(const char *num, int numLen, bool &ok, int &processed); +void doubleToAscii(double d, QLocaleData::DoubleForm form, int precision, char *buf, int bufSize, + bool &sign, int &length, int &decpt); + QString qulltoa(qulonglong l, int base, const QChar _zero); QString qlltoa(qlonglong l, int base, const QChar zero); +Q_CORE_EXPORT QString qdtoa(qreal d, int *decpt, int *sign); enum PrecisionMode { PMDecimalDigits = 0x01, @@ -96,8 +101,6 @@ inline bool isZero(double d) } } -Q_CORE_EXPORT char *qdtoa(double d, int mode, int ndigits, int *decpt, - int *sign, char **rve, char **digits_str); Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok); qlonglong qstrtoll(const char *nptr, const char **endptr, int base, bool *ok); qulonglong qstrtoull(const char *nptr, const char **endptr, int base, bool *ok); diff --git a/src/corelib/tools/qregexp.h b/src/corelib/tools/qregexp.h index f384e6c51f..9c68c194e0 100644 --- a/src/corelib/tools/qregexp.h +++ b/src/corelib/tools/qregexp.h @@ -68,10 +68,9 @@ public: ~QRegExp(); QRegExp &operator=(const QRegExp &rx); #ifdef Q_COMPILER_RVALUE_REFS - inline QRegExp &operator=(QRegExp &&other) - { qSwap(priv,other.priv); return *this; } + QRegExp &operator=(QRegExp &&other) Q_DECL_NOTHROW { swap(other); return *this; } #endif - inline void swap(QRegExp &other) { qSwap(priv, other.priv); } + void swap(QRegExp &other) Q_DECL_NOTHROW { qSwap(priv, other.priv); } bool operator==(const QRegExp &rx) const; inline bool operator!=(const QRegExp &rx) const { return !operator==(rx); } diff --git a/src/corelib/tools/qringbuffer.cpp b/src/corelib/tools/qringbuffer.cpp index 85cfdaf129..e9b655c01e 100644 --- a/src/corelib/tools/qringbuffer.cpp +++ b/src/corelib/tools/qringbuffer.cpp @@ -65,6 +65,8 @@ const char *QRingBuffer::readPointerAtPosition(qint64 pos, qint64 &length) const void QRingBuffer::free(qint64 bytes) { + Q_ASSERT(bytes <= bufferSize); + while (bytes > 0) { const qint64 blockSize = buffers.first().size() - head; @@ -100,20 +102,25 @@ char *QRingBuffer::reserve(qint64 bytes) if (bytes <= 0 || bytes >= MaxByteArraySize) return 0; - const qint64 newSize = bytes + tail; - // if need buffer reallocation - if (newSize > buffers.last().size()) { - if (newSize > buffers.last().capacity() && (tail >= basicBlockSize - || newSize >= MaxByteArraySize)) { - // shrink this buffer to its current size - buffers.last().resize(tail); - - // create a new QByteArray - buffers.append(QByteArray()); - ++tailBuffer; - tail = 0; + if (buffers.isEmpty()) { + buffers.append(QByteArray()); + buffers.first().resize(qMax(basicBlockSize, int(bytes))); + } else { + const qint64 newSize = bytes + tail; + // if need buffer reallocation + if (newSize > buffers.last().size()) { + if (newSize > buffers.last().capacity() && (tail >= basicBlockSize + || newSize >= MaxByteArraySize)) { + // shrink this buffer to its current size + buffers.last().resize(tail); + + // create a new QByteArray + buffers.append(QByteArray()); + ++tailBuffer; + tail = 0; + } + buffers.last().resize(qMax(basicBlockSize, tail + int(bytes))); } - buffers.last().resize(qMax(basicBlockSize, tail + int(bytes))); } char *writePtr = buffers.last().data() + tail; @@ -134,9 +141,13 @@ char *QRingBuffer::reserveFront(qint64 bytes) return 0; if (head < bytes) { - buffers.first().remove(0, head); - if (tailBuffer == 0) - tail -= head; + if (buffers.isEmpty()) { + buffers.append(QByteArray()); + } else { + buffers.first().remove(0, head); + if (tailBuffer == 0) + tail -= head; + } head = qMax(basicBlockSize, int(bytes)); if (bufferSize == 0) { @@ -155,6 +166,8 @@ char *QRingBuffer::reserveFront(qint64 bytes) void QRingBuffer::chop(qint64 bytes) { + Q_ASSERT(bytes <= bufferSize); + while (bytes > 0) { if (tailBuffer == 0 || tail > bytes) { // keep a single block around if it does not exceed @@ -185,6 +198,9 @@ void QRingBuffer::chop(qint64 bytes) void QRingBuffer::clear() { + if (buffers.isEmpty()) + return; + buffers.erase(buffers.begin() + 1, buffers.end()); buffers.first().clear(); @@ -193,20 +209,32 @@ void QRingBuffer::clear() bufferSize = 0; } -qint64 QRingBuffer::indexOf(char c, qint64 maxLength) const +qint64 QRingBuffer::indexOf(char c, qint64 maxLength, qint64 pos) const { - qint64 index = 0; - qint64 j = head; - for (int i = 0; index < maxLength && i < buffers.size(); ++i) { - const char *ptr = buffers[i].constData() + j; - j = qMin(index + (i == tailBuffer ? tail : buffers[i].size()) - j, maxLength); - - while (index < j) { - if (*ptr++ == c) - return index; - ++index; + if (maxLength <= 0 || pos < 0) + return -1; + + qint64 index = -(pos + head); + for (int i = 0; i < buffers.size(); ++i) { + const qint64 nextBlockIndex = qMin(index + (i == tailBuffer ? tail : buffers[i].size()), + maxLength); + + if (nextBlockIndex > 0) { + const char *ptr = buffers[i].constData(); + if (index < 0) { + ptr -= index; + index = 0; + } + + const char *findPtr = reinterpret_cast<const char *>(memchr(ptr, c, + nextBlockIndex - index)); + if (findPtr) + return qint64(findPtr - ptr) + index + pos; + + if (nextBlockIndex == maxLength) + return -1; } - j = 0; + index = nextBlockIndex; } return -1; } @@ -288,7 +316,10 @@ qint64 QRingBuffer::peek(char *data, qint64 maxLength, qint64 pos) const void QRingBuffer::append(const QByteArray &qba) { if (tail == 0) { - buffers.last() = qba; + if (buffers.isEmpty()) + buffers.append(qba); + else + buffers.last() = qba; } else { buffers.last().resize(tail); buffers.append(qba); diff --git a/src/corelib/tools/qringbuffer_p.h b/src/corelib/tools/qringbuffer_p.h index 68509a6a80..96c4f9acb6 100644 --- a/src/corelib/tools/qringbuffer_p.h +++ b/src/corelib/tools/qringbuffer_p.h @@ -54,9 +54,7 @@ class QRingBuffer { public: explicit inline QRingBuffer(int growth = 4096) : - head(0), tail(0), tailBuffer(0), basicBlockSize(growth), bufferSize(0) { - buffers.append(QByteArray()); - } + head(0), tail(0), tailBuffer(0), basicBlockSize(growth), bufferSize(0) { } inline qint64 nextDataBlockSize() const { return (tailBuffer == 0 ? tail : buffers.first().size()) - head; @@ -114,14 +112,17 @@ public: Q_CORE_EXPORT void clear(); inline qint64 indexOf(char c) const { return indexOf(c, size()); } - Q_CORE_EXPORT qint64 indexOf(char c, qint64 maxLength) const; + Q_CORE_EXPORT qint64 indexOf(char c, qint64 maxLength, qint64 pos = 0) const; Q_CORE_EXPORT qint64 read(char *data, qint64 maxLength); Q_CORE_EXPORT QByteArray read(); Q_CORE_EXPORT qint64 peek(char *data, qint64 maxLength, qint64 pos = 0) const; Q_CORE_EXPORT void append(const QByteArray &qba); inline qint64 skip(qint64 length) { - return read(0, length); + qint64 bytesToSkip = qMin(length, bufferSize); + + free(bytesToSkip); + return bytesToSkip; } Q_CORE_EXPORT qint64 readLine(char *data, qint64 maxLength); diff --git a/src/corelib/tools/qsharedpointer.cpp b/src/corelib/tools/qsharedpointer.cpp index 86f4c6a268..4d30396cb6 100644 --- a/src/corelib/tools/qsharedpointer.cpp +++ b/src/corelib/tools/qsharedpointer.cpp @@ -1292,6 +1292,17 @@ compile. Use qSharedPointerConstCast to cast away the constness. */ +/*! + \fn QDebug operator<<(QDebug debug, const QSharedPointer<T> &ptr) + \relates QSharedPointer + \since 5.7 + + Writes the pointer tracked by \a ptr into the debug object \a debug for + debugging purposes. + + \sa {Debugging Techniques} +*/ + #include <qset.h> #include <qmutex.h> diff --git a/src/corelib/tools/qsharedpointer.h b/src/corelib/tools/qsharedpointer.h index 279ec36a28..56e13d500f 100644 --- a/src/corelib/tools/qsharedpointer.h +++ b/src/corelib/tools/qsharedpointer.h @@ -153,6 +153,8 @@ template <class X, class T> QSharedPointer<X> qSharedPointerObjectCast(const QWe template <class X, class T> QWeakPointer<X> qWeakPointerCast(const QWeakPointer<T> &src); +template <class T> QDebug operator<<(QDebug debug, const QSharedPointer<T> &ptr); + QT_END_NAMESPACE #endif // Q_QDOC diff --git a/src/corelib/tools/qsharedpointer_impl.h b/src/corelib/tools/qsharedpointer_impl.h index 1323dd6b1c..ace6c5d093 100644 --- a/src/corelib/tools/qsharedpointer_impl.h +++ b/src/corelib/tools/qsharedpointer_impl.h @@ -55,6 +55,7 @@ QT_END_NAMESPACE #include <new> #include <QtCore/qatomic.h> #include <QtCore/qobject.h> // for qobject_cast +#include <QtCore/qdebug.h> #if QT_DEPRECATED_SINCE(5, 5) #include <QtCore/qhash.h> #endif @@ -305,7 +306,7 @@ public: inline T &operator*() const { return *data(); } inline T *operator->() const { return data(); } - QSharedPointer() : value(Q_NULLPTR), d(Q_NULLPTR) { } + QSharedPointer() Q_DECL_NOTHROW : value(Q_NULLPTR), d(Q_NULLPTR) {} ~QSharedPointer() { deref(); } inline explicit QSharedPointer(T *ptr) : value(ptr) // noexcept @@ -315,22 +316,22 @@ public: inline QSharedPointer(T *ptr, Deleter deleter) : value(ptr) // throws { internalConstruct(ptr, deleter); } - inline QSharedPointer(const QSharedPointer &other) : value(other.value), d(other.d) + QSharedPointer(const QSharedPointer &other) Q_DECL_NOTHROW : value(other.value), d(other.d) { if (d) ref(); } - inline QSharedPointer &operator=(const QSharedPointer &other) + QSharedPointer &operator=(const QSharedPointer &other) Q_DECL_NOTHROW { QSharedPointer copy(other); swap(copy); return *this; } #ifdef Q_COMPILER_RVALUE_REFS - inline QSharedPointer(QSharedPointer &&other) + QSharedPointer(QSharedPointer &&other) Q_DECL_NOTHROW : value(other.value), d(other.d) { other.d = Q_NULLPTR; other.value = Q_NULLPTR; } - inline QSharedPointer &operator=(QSharedPointer &&other) + QSharedPointer &operator=(QSharedPointer &&other) Q_DECL_NOTHROW { QSharedPointer moved(std::move(other)); swap(moved); @@ -596,7 +597,7 @@ public: inline bool operator !() const { return isNull(); } inline T *data() const { return d == Q_NULLPTR || d->strongref.load() == 0 ? Q_NULLPTR : value; } - inline QWeakPointer() : d(Q_NULLPTR), value(Q_NULLPTR) { } + inline QWeakPointer() Q_DECL_NOTHROW : d(Q_NULLPTR), value(Q_NULLPTR) { } inline ~QWeakPointer() { if (d && !d->weakref.deref()) delete d; } #ifndef QT_NO_QOBJECT @@ -614,15 +615,26 @@ public: { return *this = QWeakPointer(ptr); } #endif - inline QWeakPointer(const QWeakPointer &o) : d(o.d), value(o.value) + QWeakPointer(const QWeakPointer &other) Q_DECL_NOTHROW : d(other.d), value(other.value) { if (d) d->weakref.ref(); } - inline QWeakPointer &operator=(const QWeakPointer &o) +#ifdef Q_COMPILER_RVALUE_REFS + QWeakPointer(QWeakPointer &&other) Q_DECL_NOTHROW + : d(other.d), value(other.value) { - internalSet(o.d, o.value); + other.d = Q_NULLPTR; + other.value = Q_NULLPTR; + } + QWeakPointer &operator=(QWeakPointer &&other) Q_DECL_NOTHROW + { QWeakPointer moved(std::move(other)); swap(moved); return *this; } +#endif + QWeakPointer &operator=(const QWeakPointer &other) Q_DECL_NOTHROW + { + QWeakPointer copy(other); + swap(copy); return *this; } - inline void swap(QWeakPointer &other) + void swap(QWeakPointer &other) Q_DECL_NOTHROW { qSwap(this->d, other.d); qSwap(this->value, other.value); @@ -858,6 +870,16 @@ inline void qSwap(QSharedPointer<T> &p1, QSharedPointer<T> &p2) p1.swap(p2); } +#ifndef QT_NO_DEBUG_STREAM +template <class T> +Q_INLINE_TEMPLATE QDebug operator<<(QDebug debug, const QSharedPointer<T> &ptr) +{ + QDebugStateSaver saver(debug); + debug.nospace() << "QSharedPointer(" << ptr.data() << ")"; + return debug; +} +#endif + QT_END_NAMESPACE namespace std { template <class T> diff --git a/src/corelib/tools/qsimd_p.h b/src/corelib/tools/qsimd_p.h index 1e8b3420cf..96f5419af6 100644 --- a/src/corelib/tools/qsimd_p.h +++ b/src/corelib/tools/qsimd_p.h @@ -73,6 +73,7 @@ * SSE4_2 | x86 | I & C | I & C | I only | * AVX | x86 | I & C | I & C | I & C | * AVX2 | x86 | I & C | I & C | I only | + * AVX512xx | x86 | I & C | I & C | I only | * I = intrinsics; C = code generation * * Code can use the following constructs to determine compiler support & status: diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index d21708efb9..ba07259e77 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -387,25 +387,25 @@ public: # define Q_REQUIRED_RESULT # define Q_REQUIRED_RESULT_pushed # endif - QString toLower() const & Q_REQUIRED_RESULT + Q_ALWAYS_INLINE QString toLower() const & Q_REQUIRED_RESULT { return toLower_helper(*this); } - QString toLower() && Q_REQUIRED_RESULT + Q_ALWAYS_INLINE QString toLower() && Q_REQUIRED_RESULT { return toLower_helper(*this); } - QString toUpper() const & Q_REQUIRED_RESULT + Q_ALWAYS_INLINE QString toUpper() const & Q_REQUIRED_RESULT { return toUpper_helper(*this); } - QString toUpper() && Q_REQUIRED_RESULT + Q_ALWAYS_INLINE QString toUpper() && Q_REQUIRED_RESULT { return toUpper_helper(*this); } - QString toCaseFolded() const & Q_REQUIRED_RESULT + Q_ALWAYS_INLINE QString toCaseFolded() const & Q_REQUIRED_RESULT { return toCaseFolded_helper(*this); } - QString toCaseFolded() && Q_REQUIRED_RESULT + Q_ALWAYS_INLINE QString toCaseFolded() && Q_REQUIRED_RESULT { return toCaseFolded_helper(*this); } - QString trimmed() const & Q_REQUIRED_RESULT + Q_ALWAYS_INLINE QString trimmed() const & Q_REQUIRED_RESULT { return trimmed_helper(*this); } - QString trimmed() && Q_REQUIRED_RESULT + Q_ALWAYS_INLINE QString trimmed() && Q_REQUIRED_RESULT { return trimmed_helper(*this); } - QString simplified() const & Q_REQUIRED_RESULT + Q_ALWAYS_INLINE QString simplified() const & Q_REQUIRED_RESULT { return simplified_helper(*this); } - QString simplified() && Q_REQUIRED_RESULT + Q_ALWAYS_INLINE QString simplified() && Q_REQUIRED_RESULT { return simplified_helper(*this); } # ifdef Q_REQUIRED_RESULT_pushed # pragma pop_macro("Q_REQUIRED_RESULT") diff --git a/src/corelib/tools/qversionnumber.h b/src/corelib/tools/qversionnumber.h index ebf1844f38..6a2718ca28 100644 --- a/src/corelib/tools/qversionnumber.h +++ b/src/corelib/tools/qversionnumber.h @@ -273,7 +273,7 @@ public: Q_CORE_EXPORT static Q_DECL_PURE_FUNCTION QVersionNumber commonPrefix(const QVersionNumber &v1, const QVersionNumber &v2) Q_REQUIRED_RESULT; Q_CORE_EXPORT QString toString() const Q_REQUIRED_RESULT; - Q_CORE_EXPORT static Q_DECL_PURE_FUNCTION QVersionNumber fromString(const QString &string, int *suffixIndex = 0) Q_REQUIRED_RESULT; + Q_CORE_EXPORT static Q_DECL_PURE_FUNCTION QVersionNumber fromString(const QString &string, int *suffixIndex = Q_NULLPTR) Q_REQUIRED_RESULT; private: #ifndef QT_NO_DATASTREAM diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index ed07f70e87..278b1c4a41 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -24,6 +24,7 @@ HEADERS += \ tools/qdatetime.h \ tools/qdatetime_p.h \ tools/qdatetimeparser_p.h \ + tools/qdoublescanprint_p.h \ tools/qeasingcurve.h \ tools/qfreelist_p.h \ tools/qhash.h \ @@ -198,6 +199,14 @@ INCLUDEPATH += ../3rdparty/md5 \ ../3rdparty/md4 \ ../3rdparty/sha3 +contains(QT_CONFIG, doubleconversion) { + include($$PWD/../../3rdparty/double-conversion/double-conversion.pri) +} else:contains(QT_CONFIG, system-doubleconversion) { + LIBS_PRIVATE += -ldouble-conversion +} else { + DEFINES += QT_NO_DOUBLECONVERSION +} + # Note: libm should be present by default becaue this is C++ !macx-icc:!vxworks:!haiku:unix:LIBS_PRIVATE += -lm diff --git a/src/dbus/qdbusconnection.cpp b/src/dbus/qdbusconnection.cpp index e9196173ad..d0f5c06ee5 100644 --- a/src/dbus/qdbusconnection.cpp +++ b/src/dbus/qdbusconnection.cpp @@ -1177,8 +1177,8 @@ void QDBusConnectionPrivate::createBusService() ref.deref(); // busService has increased the refcounting to us // avoid cyclic refcounting - QObject::connect(this, SIGNAL(callWithCallbackFailed(QDBusError,QDBusMessage)), - busService, SIGNAL(callWithCallbackFailed(QDBusError,QDBusMessage)), + QObject::connect(this, &QDBusConnectionPrivate::callWithCallbackFailed, + busService, emit &QDBusConnectionInterface::callWithCallbackFailed, Qt::QueuedConnection); } diff --git a/src/dbus/qdbusconnectioninterface.cpp b/src/dbus/qdbusconnectioninterface.cpp index ff923ba282..9dc0b3f9bd 100644 --- a/src/dbus/qdbusconnectioninterface.cpp +++ b/src/dbus/qdbusconnectioninterface.cpp @@ -158,10 +158,10 @@ QDBusConnectionInterface::QDBusConnectionInterface(const QDBusConnection &connec QDBusUtil::dbusPath(), DBUS_INTERFACE_DBUS, connection, parent) { - connect(this, SIGNAL(NameAcquired(QString)), this, SIGNAL(serviceRegistered(QString))); - connect(this, SIGNAL(NameLost(QString)), this, SIGNAL(serviceUnregistered(QString))); - connect(this, SIGNAL(NameOwnerChanged(QString,QString,QString)), - this, SIGNAL(serviceOwnerChanged(QString,QString,QString))); + connect(this, &QDBusConnectionInterface::NameAcquired, this, emit &QDBusConnectionInterface::serviceRegistered); + connect(this, &QDBusConnectionInterface::NameLost, this, emit &QDBusConnectionInterface::serviceUnregistered); + connect(this, &QDBusConnectionInterface::NameOwnerChanged, + this, emit &QDBusConnectionInterface::serviceOwnerChanged); } /*! diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp index c465706913..d3f899676b 100644 --- a/src/dbus/qdbusintegrator.cpp +++ b/src/dbus/qdbusintegrator.cpp @@ -208,14 +208,14 @@ static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data) watcher.watch = watch; watcher.read = new QSocketNotifier(fd, QSocketNotifier::Read, d); watcher.read->setEnabled(q_dbus_watch_get_enabled(watch)); - d->connect(watcher.read, SIGNAL(activated(int)), SLOT(socketRead(int))); + d->connect(watcher.read, &QSocketNotifier::activated, d, &QDBusConnectionPrivate::socketRead); } if (flags & DBUS_WATCH_WRITABLE) { //qDebug("addWriteWatch %d", fd); watcher.watch = watch; watcher.write = new QSocketNotifier(fd, QSocketNotifier::Write, d); watcher.write->setEnabled(q_dbus_watch_get_enabled(watch)); - d->connect(watcher.write, SIGNAL(activated(int)), SLOT(socketWrite(int))); + d->connect(watcher.write, &QSocketNotifier::activated, d, &QDBusConnectionPrivate::socketWrite); } d->watchers.insertMulti(fd, watcher); @@ -1892,8 +1892,8 @@ QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message, if (sendMode == QDBus::BlockWithGui) { pcall->watcherHelper = new QDBusPendingCallWatcherHelper; QEventLoop loop; - loop.connect(pcall->watcherHelper, SIGNAL(reply(QDBusMessage)), SLOT(quit())); - loop.connect(pcall->watcherHelper, SIGNAL(error(QDBusError,QDBusMessage)), SLOT(quit())); + loop.connect(pcall->watcherHelper, &QDBusPendingCallWatcherHelper::reply, &loop, &QEventLoop::quit); + loop.connect(pcall->watcherHelper, &QDBusPendingCallWatcherHelper::error, &loop, &QEventLoop::quit); // enter the event loop and wait for a reply loop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents); @@ -2078,7 +2078,7 @@ void QDBusConnectionPrivate::addSignalHook(const QString &key, const SignalHook } signalHooks.insertMulti(key, hook); - connect(hook.obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)), + connect(hook.obj, &QObject::destroyed, this, &QDBusConnectionPrivate::objectDestroyed, Qt::ConnectionType(Qt::BlockingQueuedConnection | Qt::UniqueConnection)); MatchRefCountHash::iterator mit = matchRefCounts.find(hook.matchRule); @@ -2204,7 +2204,7 @@ QDBusConnectionPrivate::removeSignalHookNoLock(SignalHookHash::Iterator it) void QDBusConnectionPrivate::registerObject(const ObjectTreeNode *node) { - connect(node->obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)), + connect(node->obj, &QObject::destroyed, this, &QDBusConnectionPrivate::objectDestroyed, Qt::ConnectionType(Qt::BlockingQueuedConnection | Qt::UniqueConnection)); if (node->flags & (QDBusConnection::ExportAdaptors diff --git a/src/gui/accessible/accessible.pri b/src/gui/accessible/accessible.pri index 86ed4c3a71..b7f341d5b7 100644 --- a/src/gui/accessible/accessible.pri +++ b/src/gui/accessible/accessible.pri @@ -17,5 +17,9 @@ contains(QT_CONFIG, accessibility) { HEADERS += accessible/qaccessiblebridge.h SOURCES += accessible/qaccessiblebridge.cpp - OBJECTIVE_SOURCES += accessible/qaccessiblecache_mac.mm + mac { + OBJECTIVE_SOURCES += accessible/qaccessiblecache_mac.mm + + LIBS_PRIVATE += -framework Foundation + } } diff --git a/src/gui/gui.pro b/src/gui/gui.pro index aa05d72a3d..55837bcf3b 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -33,7 +33,7 @@ testcocoon { load(testcocoon) } -mac:!ios: LIBS_PRIVATE += -framework Cocoa +osx: LIBS_PRIVATE += -framework AppKit CONFIG += simd optimize_full diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp index b2d9ed18f5..cc00f5c172 100644 --- a/src/gui/image/qicon.cpp +++ b/src/gui/image/qicon.cpp @@ -919,7 +919,7 @@ void QIcon::paint(QPainter *painter, const QRect &rect, Qt::Alignment alignment, */ bool QIcon::isNull() const { - return !d; + return !d || d->engine->isNull(); } /*!\internal @@ -934,7 +934,12 @@ bool QIcon::isDetached() const void QIcon::detach() { if (d) { - if (d->ref.load() != 1) { + if (d->engine->isNull()) { + if (!d->ref.deref()) + delete d; + d = 0; + return; + } else if (d->ref.load() != 1) { QIconPrivate *x = new QIconPrivate; x->engine = d->engine->clone(); if (!d->ref.deref()) @@ -958,11 +963,10 @@ void QIcon::addPixmap(const QPixmap &pixmap, Mode mode, State state) { if (pixmap.isNull()) return; + detach(); if (!d) { d = new QIconPrivate; d->engine = new QPixmapIconEngine; - } else { - detach(); } d->engine->addPixmap(pixmap, mode, state); } @@ -1002,6 +1006,7 @@ void QIcon::addFile(const QString &fileName, const QSize &size, Mode mode, State { if (fileName.isEmpty()) return; + detach(); if (!d) { #ifndef QT_NO_LIBRARY QFileInfo info(fileName); @@ -1024,8 +1029,6 @@ void QIcon::addFile(const QString &fileName, const QSize &size, Mode mode, State d = new QIconPrivate; d->engine = new QPixmapIconEngine; } - } else { - detach(); } d->engine->addFile(fileName, size, mode, state); @@ -1135,8 +1138,7 @@ QString QIcon::themeName() \since 4.6 Returns the QIcon corresponding to \a name in the current - icon theme. If no such icon is found in the current theme - \a fallback is returned instead. + icon theme. The latest version of the freedesktop icon specification and naming specification can be obtained here: @@ -1150,19 +1152,18 @@ QString QIcon::themeName() \snippet code/src_gui_image_qicon.cpp 3 - Or if you want to provide a guaranteed fallback for platforms that - do not support theme icons, you can use the second argument: - - \snippet code/src_gui_image_qicon.cpp 4 - \note By default, only X11 will support themed icons. In order to use themed icons on Mac and Windows, you will have to bundle a compliant theme in one of your themeSearchPaths() and set the appropriate themeName(). + \note Qt will make use of GTK's icon-theme.cache if present to speed up + the lookup. These caches can be generated using gtk-update-icon-cache: + \l{https://developer.gnome.org/gtk3/stable/gtk-update-icon-cache.html}. + \sa themeName(), setThemeName(), themeSearchPaths() */ -QIcon QIcon::fromTheme(const QString &name, const QIcon &fallback) +QIcon QIcon::fromTheme(const QString &name) { QIcon icon; @@ -1178,7 +1179,26 @@ QIcon QIcon::fromTheme(const QString &name, const QIcon &fallback) qtIconCache()->insert(name, cachedIcon); } - if (qApp && icon.availableSizes().isEmpty()) + return icon; +} + +/*! + \overload + + Returns the QIcon corresponding to \a name in the current + icon theme. If no such icon is found in the current theme + \a fallback is returned instead. + + If you want to provide a guaranteed fallback for platforms that + do not support theme icons, you can use the second argument: + + \snippet code/src_gui_image_qicon.cpp 4 +*/ +QIcon QIcon::fromTheme(const QString &name, const QIcon &fallback) +{ + QIcon icon = fromTheme(name); + + if (icon.isNull() || icon.availableSizes().isEmpty()) return fallback; return icon; diff --git a/src/gui/image/qicon.h b/src/gui/image/qicon.h index 9ed7336502..989e40bbb5 100644 --- a/src/gui/image/qicon.h +++ b/src/gui/image/qicon.h @@ -105,7 +105,8 @@ public: void setIsMask(bool isMask); bool isMask() const; - static QIcon fromTheme(const QString &name, const QIcon &fallback = QIcon()); + static QIcon fromTheme(const QString &name); + static QIcon fromTheme(const QString &name, const QIcon &fallback); static bool hasThemeIcon(const QString &name); static QStringList themeSearchPaths(); diff --git a/src/gui/image/qiconengine.cpp b/src/gui/image/qiconengine.cpp index c09933d45f..7411dbb054 100644 --- a/src/gui/image/qiconengine.cpp +++ b/src/gui/image/qiconengine.cpp @@ -150,6 +150,11 @@ void QIconEngine::addFile(const QString &/*fileName*/, const QSize &/*size*/, QI icon, for example when instantiating an icon using QIcon::fromTheme(). + \value IsNullHook Allow to query if this engine represents a null + icon. The \a data argument of the virtual_hook() is a pointer to a + bool that can be set to true if the icon is null. This enum value + was added in Qt 5.7. + \sa virtual_hook() */ @@ -283,4 +288,16 @@ QString QIconEngine::iconName() const return name; } +/*! + \since 5.7 + + Returns true if this icon engine represent a null QIcon. + */ +bool QIconEngine::isNull() const +{ + bool isNull = false; + const_cast<QIconEngine *>(this)->virtual_hook(QIconEngine::IsNullHook, &isNull); + return isNull; +} + QT_END_NAMESPACE diff --git a/src/gui/image/qiconengine.h b/src/gui/image/qiconengine.h index 9977113054..6c45cd216f 100644 --- a/src/gui/image/qiconengine.h +++ b/src/gui/image/qiconengine.h @@ -58,7 +58,7 @@ public: virtual bool read(QDataStream &in); virtual bool write(QDataStream &out) const; - enum IconEngineHook { AvailableSizesHook = 1, IconNameHook }; + enum IconEngineHook { AvailableSizesHook = 1, IconNameHook, IsNullHook }; struct AvailableSizesArgument { @@ -71,6 +71,7 @@ public: QIcon::State state = QIcon::Off) const; virtual QString iconName() const; + bool isNull() const; // ### Qt6 make virtual virtual void virtual_hook(int id, void *data); }; diff --git a/src/gui/image/qiconloader.cpp b/src/gui/image/qiconloader.cpp index 3ead72dfbb..ecce7f9967 100644 --- a/src/gui/image/qiconloader.cpp +++ b/src/gui/image/qiconloader.cpp @@ -155,6 +155,141 @@ QStringList QIconLoader::themeSearchPaths() const return m_iconDirs; } +/*! + \class QIconCacheGtkReader + \internal + Helper class that reads and looks up into the icon-theme.cache generated with + gtk-update-icon-cache. If at any point we detect a corruption in the file + (because the offsets point at wrong locations for example), the reader + is marked as invalid. +*/ +class QIconCacheGtkReader +{ +public: + explicit QIconCacheGtkReader(const QString &themeDir); + QVector<const char *> lookup(const QString &); + bool isValid() const { return m_isValid; } +private: + QFile m_file; + const unsigned char *m_data; + quint64 m_size; + bool m_isValid; + + quint16 read16(uint offset) + { + if (offset > m_size - 2 || (offset & 0x1)) { + m_isValid = false; + return 0; + } + return m_data[offset+1] | m_data[offset] << 8; + } + quint32 read32(uint offset) + { + if (offset > m_size - 4 || (offset & 0x3)) { + m_isValid = false; + return 0; + } + return m_data[offset+3] | m_data[offset+2] << 8 + | m_data[offset+1] << 16 | m_data[offset] << 24; + } +}; + + +QIconCacheGtkReader::QIconCacheGtkReader(const QString &dirName) + : m_isValid(false) +{ + QFileInfo info(dirName + QLatin1Literal("/icon-theme.cache")); + if (!info.exists() || info.lastModified() < QFileInfo(dirName).lastModified()) + return; + m_file.setFileName(info.absoluteFilePath()); + if (!m_file.open(QFile::ReadOnly)) + return; + m_size = m_file.size(); + m_data = m_file.map(0, m_size); + if (!m_data) + return; + if (read16(0) != 1) // VERSION_MAJOR + return; + + m_isValid = true; + + // Check that all the directories are older than the cache + auto lastModified = info.lastModified(); + quint32 dirListOffset = read32(8); + quint32 dirListLen = read32(dirListOffset); + for (uint i = 0; i < dirListLen; ++i) { + quint32 offset = read32(dirListOffset + 4 + 4 * i); + if (!m_isValid || offset >= m_size || lastModified < QFileInfo(dirName + QLatin1Char('/') + + QString::fromUtf8(reinterpret_cast<const char*>(m_data + offset))).lastModified()) { + m_isValid = false; + return; + } + } +} + +static quint32 icon_name_hash(const char *p) +{ + quint32 h = static_cast<signed char>(*p); + for (p += 1; *p != '\0'; p++) + h = (h << 5) - h + *p; + return h; +} + +/*! \internal + lookup the icon name and return the list of subdirectories in which an icon + with this name is present. The char* are pointers to the mapped data. + For example, this would return { "32x32/apps", "24x24/apps" , ... } + */ +QVector<const char *> QIconCacheGtkReader::lookup(const QString &name) +{ + QVector<const char *> ret; + if (!isValid()) + return ret; + + QByteArray nameUtf8 = name.toUtf8(); + quint32 hash = icon_name_hash(nameUtf8); + + quint32 hashOffset = read32(4); + quint32 hashBucketCount = read32(hashOffset); + + if (!isValid() || hashBucketCount == 0) { + m_isValid = false; + return ret; + } + + quint32 bucketIndex = hash % hashBucketCount; + quint32 bucketOffset = read32(hashOffset + 4 + bucketIndex * 4); + while (bucketOffset > 0 && bucketOffset <= m_size - 12) { + quint32 nameOff = read32(bucketOffset + 4); + if (nameOff < m_size && strcmp(reinterpret_cast<const char*>(m_data + nameOff), nameUtf8) == 0) { + quint32 dirListOffset = read32(8); + quint32 dirListLen = read32(dirListOffset); + + quint32 listOffset = read32(bucketOffset+8); + quint32 listLen = read32(listOffset); + + if (!m_isValid || listOffset + 4 + 8 * listLen > m_size) { + m_isValid = false; + return ret; + } + + ret.reserve(listLen); + for (uint j = 0; j < listLen && m_isValid; ++j) { + quint32 dirIndex = read16(listOffset + 4 + 8 * j); + quint32 o = read32(dirListOffset + 4 + dirIndex*4); + if (!m_isValid || dirIndex >= dirListLen || o >= m_size) { + m_isValid = false; + return ret; + } + ret.append(reinterpret_cast<const char*>(m_data) + o); + } + return ret; + } + bucketOffset = read32(bucketOffset); + } + return ret; +} + QIconTheme::QIconTheme(const QString &themeName) : m_valid(false) { @@ -166,8 +301,10 @@ QIconTheme::QIconTheme(const QString &themeName) QString themeDir = iconDir.path() + QLatin1Char('/') + themeName; QFileInfo themeDirInfo(themeDir); - if (themeDirInfo.isDir()) + if (themeDirInfo.isDir()) { m_contentDirs << themeDir; + m_gtkCaches << QSharedPointer<QIconCacheGtkReader>::create(themeDir); + } if (!m_valid) { themeIndex.setFileName(themeDir + QLatin1String("/index.theme")); @@ -257,7 +394,6 @@ QThemeIconInfo QIconLoader::findIconHelper(const QString &themeName, } const QStringList contentDirs = theme.contentDirs(); - const QVector<QIconDirInfo> subDirs = theme.keyList(); QString iconNameFallback = iconName; @@ -268,6 +404,29 @@ QThemeIconInfo QIconLoader::findIconHelper(const QString &themeName, // Add all relevant files for (int i = 0; i < contentDirs.size(); ++i) { + QVector<QIconDirInfo> subDirs = theme.keyList(); + + // Try to reduce the amount of subDirs by looking in the GTK+ cache in order to save + // a massive amount of file stat (especially if the icon is not there) + auto cache = theme.m_gtkCaches.at(i); + if (cache->isValid()) { + auto result = cache->lookup(iconNameFallback); + if (cache->isValid()) { + const QVector<QIconDirInfo> subDirsCopy = subDirs; + subDirs.clear(); + subDirs.reserve(result.count()); + foreach (const char *s, result) { + QString path = QString::fromUtf8(s); + auto it = std::find_if(subDirsCopy.cbegin(), subDirsCopy.cend(), + [&](const QIconDirInfo &info) { + return info.path == path; } ); + if (it != subDirsCopy.cend()) { + subDirs.append(*it); + } + } + } + } + QString contentDir = contentDirs.at(i) + QLatin1Char('/'); for (int j = 0; j < subDirs.size() ; ++j) { const QIconDirInfo &dirInfo = subDirs.at(j); @@ -587,6 +746,11 @@ void QIconLoaderEngine::virtual_hook(int id, void *data) name = m_info.iconName; } break; + case QIconEngine::IsNullHook: + { + *reinterpret_cast<bool*>(data) = m_info.entries.isEmpty(); + } + break; default: QIconEngine::virtual_hook(id, data); } diff --git a/src/gui/image/qiconloader_p.h b/src/gui/image/qiconloader_p.h index ccf0a9d438..193154e44e 100644 --- a/src/gui/image/qiconloader_p.h +++ b/src/gui/image/qiconloader_p.h @@ -139,6 +139,8 @@ private: friend class QIconLoader; }; +class QIconCacheGtkReader; + class QIconTheme { public: @@ -148,12 +150,13 @@ public: QVector<QIconDirInfo> keyList() { return m_keyList; } QStringList contentDirs() { return m_contentDirs; } bool isValid() { return m_valid; } - private: QStringList m_contentDirs; QVector<QIconDirInfo> m_keyList; QStringList m_parents; bool m_valid; +public: + QVector<QSharedPointer<QIconCacheGtkReader>> m_gtkCaches; }; class Q_GUI_EXPORT QIconLoader diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h index 888c7beb32..9d8e3efcc4 100644 --- a/src/gui/image/qimage.h +++ b/src/gui/image/qimage.h @@ -168,9 +168,9 @@ public: Format format() const; #if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(QT_COMPILING_QIMAGE_COMPAT_CPP) - QImage convertToFormat(Format f, Qt::ImageConversionFlags flags = Qt::AutoColor) const & Q_REQUIRED_RESULT + Q_ALWAYS_INLINE QImage convertToFormat(Format f, Qt::ImageConversionFlags flags = Qt::AutoColor) const & Q_REQUIRED_RESULT { return convertToFormat_helper(f, flags); } - QImage convertToFormat(Format f, Qt::ImageConversionFlags flags = Qt::AutoColor) && Q_REQUIRED_RESULT + Q_ALWAYS_INLINE QImage convertToFormat(Format f, Qt::ImageConversionFlags flags = Qt::AutoColor) && Q_REQUIRED_RESULT { if (convertToFormat_inplace(f, flags)) return std::move(*this); diff --git a/src/gui/image/qpixmapcache.cpp b/src/gui/image/qpixmapcache.cpp index d29ddcf978..6265a0c16d 100644 --- a/src/gui/image/qpixmapcache.cpp +++ b/src/gui/image/qpixmapcache.cpp @@ -156,6 +156,16 @@ bool QPixmapCache::Key::operator ==(const Key &key) const */ /*! + Returns \c true if there is a cached pixmap associated with this key. + Otherwise, if pixmap was flushed, the key is no longer valid. + \since 5.7 +*/ +bool QPixmapCache::Key::isValid() const Q_DECL_NOTHROW +{ + return d && d->isValid; +} + +/*! \internal */ QPixmapCache::Key &QPixmapCache::Key::operator =(const Key &other) diff --git a/src/gui/image/qpixmapcache.h b/src/gui/image/qpixmapcache.h index 37a0588e06..ca18f299a7 100644 --- a/src/gui/image/qpixmapcache.h +++ b/src/gui/image/qpixmapcache.h @@ -59,6 +59,7 @@ public: Key &operator =(const Key &other); void swap(Key &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + bool isValid() const Q_DECL_NOTHROW; private: KeyData *d; diff --git a/src/gui/kernel/qdrag.cpp b/src/gui/kernel/qdrag.cpp index 2736fac8e0..36527966b7 100644 --- a/src/gui/kernel/qdrag.cpp +++ b/src/gui/kernel/qdrag.cpp @@ -33,6 +33,8 @@ #include <qdrag.h> #include "private/qguiapplication_p.h" +#include "qpa/qplatformintegration.h" +#include "qpa/qplatformdrag.h" #include <qpixmap.h> #include <qpoint.h> #include "qdnd_p.h" @@ -223,6 +225,8 @@ QObject *QDrag::target() const loop. Other events are still delivered to the application while the operation is performed. On Windows, the Qt event loop is blocked during the operation. + + \sa cancel() */ Qt::DropAction QDrag::exec(Qt::DropActions supportedActions) @@ -377,6 +381,21 @@ Qt::DropAction QDrag::defaultAction() const Q_D(const QDrag); return d->default_action; } + +/*! + Cancels a drag operation initiated by Qt. + + \note This is currently implemented on Windows and X11. + + \since 5.6 + \sa exec() +*/ +void QDrag::cancel() +{ + if (QPlatformDrag *platformDrag = QGuiApplicationPrivate::platformIntegration()->drag()) + platformDrag->cancelDrag(); +} + /*! \fn void QDrag::actionChanged(Qt::DropAction action) diff --git a/src/gui/kernel/qdrag.h b/src/gui/kernel/qdrag.h index 0672cb00f9..961d7c89d9 100644 --- a/src/gui/kernel/qdrag.h +++ b/src/gui/kernel/qdrag.h @@ -77,6 +77,8 @@ public: Qt::DropActions supportedActions() const; Qt::DropAction defaultAction() const; + static void cancel(); + Q_SIGNALS: void actionChanged(Qt::DropAction action); void targetChanged(QObject *newTarget); diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 0bb21752bc..233b9ef3f7 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -37,6 +37,7 @@ #include "qpa/qplatformintegration.h" #include "qpa/qplatformdrag.h" #include "private/qevent_p.h" +#include "qfile.h" #include "qmetaobject.h" #include "qmimedata.h" #include "private/qdnd_p.h" @@ -3948,9 +3949,11 @@ QDebug operator<<(QDebug dbg, const QEvent *e) QtDebugUtils::formatQEnum(dbg, static_cast<const QApplicationStateChangeEvent *>(e)->applicationState()); dbg << ')'; break; +# ifndef QT_NO_CONTEXTMENU case QEvent::ContextMenu: dbg << "QContextMenuEvent(" << static_cast<const QContextMenuEvent *>(e)->pos() << ')'; break; +# endif // !QT_NO_CONTEXTMENU # ifndef QT_NO_TABLETEVENT case QEvent::TabletEnterProximity: case QEvent::TabletLeaveProximity: diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index b90fce97e0..66e650c42d 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -35,20 +35,19 @@ #define QEVENT_H #include <QtGui/qwindowdefs.h> -#include <QtCore/qobject.h> #include <QtGui/qregion.h> #include <QtCore/qnamespace.h> #include <QtCore/qstring.h> #include <QtGui/qkeysequence.h> #include <QtCore/qcoreevent.h> #include <QtCore/qvariant.h> -#include <QtCore/qmap.h> +#include <QtCore/qmap.h> // ### Qt 6: Remove #include <QtCore/qvector.h> -#include <QtCore/qset.h> +#include <QtCore/qset.h> // ### Qt 6: Remove #include <QtCore/qurl.h> -#include <QtCore/qfile.h> +#include <QtCore/qfile.h> // ### Qt 6: Replace by <qiodevice.h> and forward declare QFile #include <QtGui/qvector2d.h> -#include <QtGui/qtouchdevice.h> +#include <QtGui/qtouchdevice.h> // ### Qt 6: Replace by forward declaration QT_BEGIN_NAMESPACE diff --git a/src/gui/kernel/qgenericplugin.cpp b/src/gui/kernel/qgenericplugin.cpp index 47f3ea5811..ae423b93e3 100644 --- a/src/gui/kernel/qgenericplugin.cpp +++ b/src/gui/kernel/qgenericplugin.cpp @@ -33,8 +33,6 @@ #include "qgenericplugin.h" -#ifndef QT_NO_LIBRARY - QT_BEGIN_NAMESPACE /*! @@ -90,5 +88,3 @@ QGenericPlugin::~QGenericPlugin() */ QT_END_NAMESPACE - -#endif // QT_NO_LIBRARY diff --git a/src/gui/kernel/qgenericplugin.h b/src/gui/kernel/qgenericplugin.h index 03c1df7fba..21ae97f045 100644 --- a/src/gui/kernel/qgenericplugin.h +++ b/src/gui/kernel/qgenericplugin.h @@ -39,9 +39,6 @@ QT_BEGIN_NAMESPACE - -#ifndef QT_NO_LIBRARY - #define QGenericPluginFactoryInterface_iid "org.qt-project.Qt.QGenericPluginFactoryInterface" class Q_GUI_EXPORT QGenericPlugin : public QObject @@ -54,8 +51,6 @@ public: virtual QObject* create(const QString& name, const QString &spec) = 0; }; -#endif // QT_NO_LIBRARY - QT_END_NAMESPACE #endif // QGENERICPLUGIN_H diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index c5762fe1ee..66f1b1dfce 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -37,6 +37,7 @@ #include <qpa/qplatformintegrationfactory_p.h> #include "private/qevent_p.h" #include "qfont.h" +#include "qtouchdevice.h" #include <qpa/qplatformfontdatabase.h> #include <qpa/qplatformwindow.h> #include <qpa/qplatformnativeinterface.h> @@ -150,6 +151,7 @@ QIcon *QGuiApplicationPrivate::app_icon = 0; QString *QGuiApplicationPrivate::platform_name = 0; QString *QGuiApplicationPrivate::displayName = 0; +QString *QGuiApplicationPrivate::desktopFileName = 0; QPalette *QGuiApplicationPrivate::app_pal = 0; // default application palette @@ -610,6 +612,8 @@ QGuiApplication::~QGuiApplication() QGuiApplicationPrivate::platform_name = 0; delete QGuiApplicationPrivate::displayName; QGuiApplicationPrivate::displayName = 0; + delete QGuiApplicationPrivate::desktopFileName; + QGuiApplicationPrivate::desktopFileName = 0; } QGuiApplicationPrivate::QGuiApplicationPrivate(int &argc, char **argv, int flags) @@ -651,6 +655,34 @@ QString QGuiApplication::applicationDisplayName() } /*! + \property QGuiApplication::desktopFileName + \brief the base name of the desktop entry for this application + \since 5.7 + + This is the file name, without the full path, of the desktop entry + that represents this application according to the freedesktop desktop + entry specification. + + This property gives a precise indication of what desktop entry represents + the application and it is needed by the windowing system to retrieve + such information without resorting to imprecise heuristics. + + The latest version of the freedesktop desktop entry specification can be obtained + \l{http://standards.freedesktop.org/desktop-entry-spec/latest/}{here}. +*/ +void QGuiApplication::setDesktopFileName(const QString &name) +{ + if (!QGuiApplicationPrivate::desktopFileName) + QGuiApplicationPrivate::desktopFileName = new QString; + *QGuiApplicationPrivate::desktopFileName = name; +} + +QString QGuiApplication::desktopFileName() +{ + return QGuiApplicationPrivate::desktopFileName ? *QGuiApplicationPrivate::desktopFileName : QString(); +} + +/*! Returns the most recently shown modal window. If no modal windows are visible, this function returns zero. diff --git a/src/gui/kernel/qguiapplication.h b/src/gui/kernel/qguiapplication.h index d995387d66..a773122d3e 100644 --- a/src/gui/kernel/qguiapplication.h +++ b/src/gui/kernel/qguiapplication.h @@ -67,6 +67,7 @@ class Q_GUI_EXPORT QGuiApplication : public QCoreApplication Q_OBJECT Q_PROPERTY(QIcon windowIcon READ windowIcon WRITE setWindowIcon) Q_PROPERTY(QString applicationDisplayName READ applicationDisplayName WRITE setApplicationDisplayName) + Q_PROPERTY(QString desktopFileName READ desktopFileName WRITE setDesktopFileName) Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged) Q_PROPERTY(QString platformName READ platformName STORED false) Q_PROPERTY(bool quitOnLastWindowClosed READ quitOnLastWindowClosed WRITE setQuitOnLastWindowClosed) @@ -83,6 +84,9 @@ public: static void setApplicationDisplayName(const QString &name); static QString applicationDisplayName(); + static void setDesktopFileName(const QString &name); + static QString desktopFileName(); + static QWindowList allWindows(); static QWindowList topLevelWindows(); static QWindow *topLevelAt(const QPoint &pos); diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h index 7c7da9790b..4d5797a8fa 100644 --- a/src/gui/kernel/qguiapplication_p.h +++ b/src/gui/kernel/qguiapplication_p.h @@ -183,6 +183,7 @@ public: static QIcon *app_icon; static QString *platform_name; static QString *displayName; + static QString *desktopFileName; QWindowList modalWindowList; static void showModalWindow(QWindow *window); diff --git a/src/gui/kernel/qinputdevicemanager_p.h b/src/gui/kernel/qinputdevicemanager_p.h index d64793c23c..4c24b1bc93 100644 --- a/src/gui/kernel/qinputdevicemanager_p.h +++ b/src/gui/kernel/qinputdevicemanager_p.h @@ -61,7 +61,8 @@ public: DeviceTypeUnknown, DeviceTypePointer, DeviceTypeKeyboard, - DeviceTypeTouch + DeviceTypeTouch, + DeviceTypeTablet }; QInputDeviceManager(QObject *parent = 0); diff --git a/src/gui/kernel/qplatformdrag.cpp b/src/gui/kernel/qplatformdrag.cpp index d789c75d1d..11230194fc 100644 --- a/src/gui/kernel/qplatformdrag.cpp +++ b/src/gui/kernel/qplatformdrag.cpp @@ -155,6 +155,20 @@ Qt::DropAction QPlatformDrag::defaultAction(Qt::DropActions possibleActions, } /*! + \brief Cancels the currently active drag (only for drags of + the current application initiated by QPlatformDrag::drag()). + + The default implementation does nothing. + + \since 5.6 + */ + +void QPlatformDrag::cancelDrag() +{ + Q_UNIMPLEMENTED(); +} + +/*! \brief Called to notify QDrag about changes of the current action. */ diff --git a/src/gui/kernel/qplatformdrag.h b/src/gui/kernel/qplatformdrag.h index 10ee88477f..72e28d2745 100644 --- a/src/gui/kernel/qplatformdrag.h +++ b/src/gui/kernel/qplatformdrag.h @@ -92,6 +92,7 @@ public: virtual QMimeData *platformDropData() = 0; virtual Qt::DropAction drag(QDrag *m_drag) = 0; + virtual void cancelDrag(); void updateAction(Qt::DropAction action); virtual Qt::DropAction defaultAction(Qt::DropActions possibleActions, Qt::KeyboardModifiers modifiers) const; diff --git a/src/gui/kernel/qplatformintegration.cpp b/src/gui/kernel/qplatformintegration.cpp index 14633d8b30..0968c9deed 100644 --- a/src/gui/kernel/qplatformintegration.cpp +++ b/src/gui/kernel/qplatformintegration.cpp @@ -149,13 +149,11 @@ QPlatformServices *QPlatformIntegration::services() const /*! \fn QPlatformWindow *QPlatformIntegration::createPlatformWindow(QWindow *window) const - Factory function for QPlatformWindow. The \a window parameter is a pointer to the top level - window which the QPlatformWindow is supposed to be created for. + Factory function for QPlatformWindow. The \a window parameter is a pointer to the window + which the QPlatformWindow is supposed to be created for. - All top level windows have to have a QPlatformWindow, and it will be created when the - QPlatformWindow is set to be visible for the first time. If the top level window's flags are - changed, or if the top level window's QPlatformWindowFormat is changed, then the top level - window's QPlatformWindow is deleted and a new one is created. + All windows have to have a QPlatformWindow, and it will be created on-demand when the + QWindow is made visible for the first time, or explicitly through calling QWindow::create(). In the constructor, of the QPlatformWindow, the window flags, state, title and geometry of the \a window should be applied to the underlying window. If the resulting flags or state diff --git a/src/gui/kernel/qplatformintegrationfactory.cpp b/src/gui/kernel/qplatformintegrationfactory.cpp index 5a1fb3ca83..d109ceb2f0 100644 --- a/src/gui/kernel/qplatformintegrationfactory.cpp +++ b/src/gui/kernel/qplatformintegrationfactory.cpp @@ -42,11 +42,13 @@ QT_BEGIN_NAMESPACE -#ifndef QT_NO_LIBRARY Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, (QPlatformIntegrationFactoryInterface_iid, QLatin1String("/platforms"), Qt::CaseInsensitive)) + +#ifndef QT_NO_LIBRARY Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, directLoader, (QPlatformIntegrationFactoryInterface_iid, QLatin1String(""), Qt::CaseInsensitive)) +#endif // !QT_NO_LIBRARY static inline QPlatformIntegration *loadIntegration(QFactoryLoader *loader, const QString &key, const QStringList ¶meters, int &argc, char ** argv) { @@ -59,8 +61,6 @@ static inline QPlatformIntegration *loadIntegration(QFactoryLoader *loader, cons return 0; } -#endif // !QT_NO_LIBRARY - QPlatformIntegration *QPlatformIntegrationFactory::create(const QString &platform, const QStringList ¶mList, int &argc, char **argv, const QString &platformPluginPath) { #ifndef QT_NO_LIBRARY @@ -70,16 +70,10 @@ QPlatformIntegration *QPlatformIntegrationFactory::create(const QString &platfor if (QPlatformIntegration *ret = loadIntegration(directLoader(), platform, paramList, argc, argv)) return ret; } - if (QPlatformIntegration *ret = loadIntegration(loader(), platform, paramList, argc, argv)) - return ret; #else - Q_UNUSED(platform); - Q_UNUSED(paramList); - Q_UNUSED(argc); - Q_UNUSED(argv); Q_UNUSED(platformPluginPath); #endif - return 0; + return loadIntegration(loader(), platform, paramList, argc, argv); } /*! diff --git a/src/gui/kernel/qplatformtheme.cpp b/src/gui/kernel/qplatformtheme.cpp index ce8548f628..61dacfa076 100644 --- a/src/gui/kernel/qplatformtheme.cpp +++ b/src/gui/kernel/qplatformtheme.cpp @@ -682,6 +682,41 @@ QString QPlatformTheme::defaultStandardButtonText(int button) return QString(); } +QString QPlatformTheme::removeMnemonics(const QString &original) +{ + QString returnText(original.size(), 0); + int finalDest = 0; + int currPos = 0; + int l = original.length(); + while (l) { + if (original.at(currPos) == QLatin1Char('&') + && (l == 1 || original.at(currPos + 1) != QLatin1Char('&'))) { + ++currPos; + --l; + if (l == 0) + break; + } else if (original.at(currPos) == QLatin1Char('(') && l >= 4 && + original.at(currPos + 1) == QLatin1Char('&') && + original.at(currPos + 2) != QLatin1Char('&') && + original.at(currPos + 3) == QLatin1Char(')')) { + /* remove mnemonics its format is "\s*(&X)" */ + int n = 0; + while (finalDest > n && returnText.at(finalDest - n - 1).isSpace()) + ++n; + finalDest -= n; + currPos += 4; + l -= 4; + continue; + } + returnText[finalDest] = original.at(currPos); + ++currPos; + ++finalDest; + --l; + } + returnText.truncate(finalDest); + return returnText; +} + unsigned QPlatformThemePrivate::currentKeyPlatforms() { const uint keyboardScheme = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::KeyboardScheme).toInt(); diff --git a/src/gui/kernel/qplatformtheme.h b/src/gui/kernel/qplatformtheme.h index 69cc2f90af..9355e83491 100644 --- a/src/gui/kernel/qplatformtheme.h +++ b/src/gui/kernel/qplatformtheme.h @@ -301,6 +301,7 @@ public: static QVariant defaultThemeHint(ThemeHint hint); static QString defaultStandardButtonText(int button); + static QString removeMnemonics(const QString &original); protected: explicit QPlatformTheme(QPlatformThemePrivate *priv); diff --git a/src/gui/kernel/qsimpledrag.cpp b/src/gui/kernel/qsimpledrag.cpp index 9f38c9b78a..706786385b 100644 --- a/src/gui/kernel/qsimpledrag.cpp +++ b/src/gui/kernel/qsimpledrag.cpp @@ -191,6 +191,14 @@ Qt::DropAction QBasicDrag::drag(QDrag *o) return m_executed_drop_action; } +void QBasicDrag::cancelDrag() +{ + if (m_eventLoop) { + cancel(); + m_eventLoop->quit(); + } +} + void QBasicDrag::restoreCursor() { if (m_restoreCursor) { diff --git a/src/gui/kernel/qsimpledrag_p.h b/src/gui/kernel/qsimpledrag_p.h index 055136c436..d5dacd8fd2 100644 --- a/src/gui/kernel/qsimpledrag_p.h +++ b/src/gui/kernel/qsimpledrag_p.h @@ -66,6 +66,7 @@ public: virtual ~QBasicDrag(); virtual Qt::DropAction drag(QDrag *drag) Q_DECL_OVERRIDE; + void cancelDrag() Q_DECL_OVERRIDE; virtual bool eventFilter(QObject *o, QEvent *e) Q_DECL_OVERRIDE; diff --git a/src/gui/kernel/qstylehints.cpp b/src/gui/kernel/qstylehints.cpp index 7ff0f9f860..0ecd2e12e8 100644 --- a/src/gui/kernel/qstylehints.cpp +++ b/src/gui/kernel/qstylehints.cpp @@ -65,6 +65,7 @@ class QStyleHintsPrivate : public QObjectPrivate public: inline QStyleHintsPrivate() : m_mouseDoubleClickInterval(-1) + , m_mousePressAndHoldInterval(-1) , m_startDragDistance(-1) , m_startDragTime(-1) , m_keyboardInputInterval(-1) @@ -72,6 +73,7 @@ public: {} int m_mouseDoubleClickInterval; + int m_mousePressAndHoldInterval; int m_startDragDistance; int m_startDragTime; int m_keyboardInputInterval; @@ -129,6 +131,21 @@ int QStyleHints::mouseDoubleClickInterval() const } /*! + Sets the \a mousePressAndHoldInterval. + \internal + \sa mousePressAndHoldInterval() + \since 5.7 +*/ +void QStyleHints::setMousePressAndHoldInterval(int mousePressAndHoldInterval) +{ + Q_D(QStyleHints); + if (d->m_mousePressAndHoldInterval == mousePressAndHoldInterval) + return; + d->m_mousePressAndHoldInterval = mousePressAndHoldInterval; + emit mousePressAndHoldIntervalChanged(mousePressAndHoldInterval); +} + +/*! \property QStyleHints::mousePressAndHoldInterval \brief the time limit in milliseconds that activates a press and hold. @@ -137,7 +154,10 @@ int QStyleHints::mouseDoubleClickInterval() const */ int QStyleHints::mousePressAndHoldInterval() const { - return themeableHint(QPlatformTheme::MousePressAndHoldInterval, QPlatformIntegration::MousePressAndHoldInterval).toInt(); + Q_D(const QStyleHints); + return d->m_mousePressAndHoldInterval >= 0 ? + d->m_mousePressAndHoldInterval : + themeableHint(QPlatformTheme::MousePressAndHoldInterval, QPlatformIntegration::MousePressAndHoldInterval).toInt(); } /*! diff --git a/src/gui/kernel/qstylehints.h b/src/gui/kernel/qstylehints.h index 82eb8a6f7d..5762482f35 100644 --- a/src/gui/kernel/qstylehints.h +++ b/src/gui/kernel/qstylehints.h @@ -51,7 +51,7 @@ class Q_GUI_EXPORT QStyleHints : public QObject Q_PROPERTY(int keyboardAutoRepeatRate READ keyboardAutoRepeatRate STORED false CONSTANT FINAL) Q_PROPERTY(int keyboardInputInterval READ keyboardInputInterval NOTIFY keyboardInputIntervalChanged FINAL) Q_PROPERTY(int mouseDoubleClickInterval READ mouseDoubleClickInterval NOTIFY mouseDoubleClickIntervalChanged FINAL) - Q_PROPERTY(int mousePressAndHoldInterval READ mousePressAndHoldInterval STORED false CONSTANT FINAL) + Q_PROPERTY(int mousePressAndHoldInterval READ mousePressAndHoldInterval NOTIFY mousePressAndHoldIntervalChanged FINAL) Q_PROPERTY(QChar passwordMaskCharacter READ passwordMaskCharacter STORED false CONSTANT FINAL) Q_PROPERTY(int passwordMaskDelay READ passwordMaskDelay STORED false CONSTANT FINAL) Q_PROPERTY(bool setFocusOnTouchRelease READ setFocusOnTouchRelease STORED false CONSTANT FINAL) @@ -66,6 +66,7 @@ class Q_GUI_EXPORT QStyleHints : public QObject public: void setMouseDoubleClickInterval(int mouseDoubleClickInterval); int mouseDoubleClickInterval() const; + void setMousePressAndHoldInterval(int mousePressAndHoldInterval); int mousePressAndHoldInterval() const; void setStartDragDistance(int startDragDistance); int startDragDistance() const; @@ -90,6 +91,7 @@ Q_SIGNALS: void cursorFlashTimeChanged(int cursorFlashTime); void keyboardInputIntervalChanged(int keyboardInputInterval); void mouseDoubleClickIntervalChanged(int mouseDoubleClickInterval); + void mousePressAndHoldIntervalChanged(int mousePressAndHoldInterval); void startDragDistanceChanged(int startDragDistance); void startDragTimeChanged(int startDragTime); diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index e262f3f8a4..3f971d700e 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -392,6 +392,9 @@ void QWindowPrivate::create(bool recursive) if (platformWindow) return; + if (q->parent()) + q->parent()->create(); + platformWindow = QGuiApplicationPrivate::platformIntegration()->createPlatformWindow(q); Q_ASSERT(platformWindow); @@ -403,13 +406,21 @@ void QWindowPrivate::create(bool recursive) QObjectList childObjects = q->children(); for (int i = 0; i < childObjects.size(); i ++) { QObject *object = childObjects.at(i); - if (object->isWindowType()) { - QWindow *window = static_cast<QWindow *>(object); - if (recursive) - window->d_func()->create(true); - if (window->d_func()->platformWindow) - window->d_func()->platformWindow->setParent(platformWindow); - } + if (!object->isWindowType()) + continue; + + QWindow *childWindow = static_cast<QWindow *>(object); + if (recursive) + childWindow->d_func()->create(recursive); + + // The child may have had deferred creation due to this window not being created + // at the time setVisible was called, so we re-apply the visible state, which + // may result in creating the child, and emitting the appropriate signals. + if (childWindow->isVisible()) + childWindow->setVisible(true); + + if (QPlatformWindow *childPlatformWindow = childWindow->d_func()->platformWindow) + childPlatformWindow->setParent(this->platformWindow); } QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated); @@ -474,14 +485,23 @@ void QWindow::setVisible(bool visible) { Q_D(QWindow); - if (d->visible == visible) + if (d->visible != visible) { + d->visible = visible; + emit visibleChanged(visible); + d->updateVisibility(); + } else if (d->platformWindow) { + // Visibility hasn't changed, and the platform window is in sync return; - d->visible = visible; - emit visibleChanged(visible); - d->updateVisibility(); + } - if (!d->platformWindow) - create(); + if (!d->platformWindow) { + // If we have a parent window, but the parent hasn't been created yet, we + // can defer creation until the parent is created or we're re-parented. + if (parent() && !parent()->handle()) + return; + else + create(); + } if (visible) { // remove posted quit events when showing a new window @@ -520,6 +540,7 @@ void QWindow::setVisible(bool visible) if (visible && (d->hasCursor || QGuiApplication::overrideCursor())) d->applyCursor(); #endif + d->platformWindow->setVisible(visible); if (!visible) { @@ -609,20 +630,25 @@ void QWindow::setParent(QWindow *parent) } QObject::setParent(parent); + d->parentWindow = parent; + if (parent) d->disconnectFromScreen(); else d->connectToScreen(newScreen); + // If we were set visible, but not created because we were a child, and we're now + // re-parented into a created parent, or to being a top level, we need re-apply the + // visibility state, which will also create. + if (isVisible() && (!parent || parent->handle())) + setVisible(true); + if (d->platformWindow) { - if (parent && parent->d_func()->platformWindow) { - d->platformWindow->setParent(parent->d_func()->platformWindow); - } else { - d->platformWindow->setParent(0); - } - } + if (parent) + parent->create(); - d->parentWindow = parent; + d->platformWindow->setParent(parent ? parent->d_func()->platformWindow : 0); + } QGuiApplicationPrivate::updateBlockedStatus(this); } diff --git a/src/gui/opengl/qtriangulator_p.h b/src/gui/opengl/qtriangulator_p.h index 0ab3f7496c..4e13e5bdd0 100644 --- a/src/gui/opengl/qtriangulator_p.h +++ b/src/gui/opengl/qtriangulator_p.h @@ -88,12 +88,13 @@ public: inline QVertexIndexVector &operator = (const QVertexIndexVector &other) { - if (t == UnsignedInt) - indices32 = other.indices32; - else - indices16 = other.indices16; + if (t == UnsignedInt) + indices32 = other.indices32; + else + indices16 = other.indices16; - return *this; + t = other.t; + return *this; } private: diff --git a/src/gui/text/qrawfont.cpp b/src/gui/text/qrawfont.cpp index 0fd5f510c7..0e8771b0ac 100644 --- a/src/gui/text/qrawfont.cpp +++ b/src/gui/text/qrawfont.cpp @@ -44,6 +44,7 @@ #include <qpa/qplatformfontdatabase.h> #include <QtCore/qendian.h> +#include <QtCore/qfile.h> QT_BEGIN_NAMESPACE diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp index f8f41bb53d..a09ed2c040 100644 --- a/src/gui/text/qtexthtmlparser.cpp +++ b/src/gui/text/qtexthtmlparser.cpp @@ -1140,6 +1140,7 @@ void QTextHtmlParserNode::setListStyle(const QVector<QCss::Value> &cssValues) for (int i = 0; i < cssValues.count(); ++i) { if (cssValues.at(i).type == QCss::Value::KnownIdentifier) { switch (static_cast<QCss::KnownValue>(cssValues.at(i).variant.toInt())) { + case QCss::Value_None: hasOwnListStyle = true; listStyle = QTextListFormat::ListStyleUndefined; break; case QCss::Value_Disc: hasOwnListStyle = true; listStyle = QTextListFormat::ListDisc; break; case QCss::Value_Square: hasOwnListStyle = true; listStyle = QTextListFormat::ListSquare; break; case QCss::Value_Circle: hasOwnListStyle = true; listStyle = QTextListFormat::ListCircle; break; @@ -1495,6 +1496,8 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes) node->listStyle = QTextListFormat::ListDisc; else if (value == QLatin1String("circle")) node->listStyle = QTextListFormat::ListCircle; + else if (value == QLatin1String("none")) + node->listStyle = QTextListFormat::ListStyleUndefined; } } break; diff --git a/src/gui/text/qtextimagehandler.cpp b/src/gui/text/qtextimagehandler.cpp index 16d81a7d6d..d7a418e7c8 100644 --- a/src/gui/text/qtextimagehandler.cpp +++ b/src/gui/text/qtextimagehandler.cpp @@ -38,6 +38,7 @@ #include <qtextformat.h> #include <qpainter.h> #include <qdebug.h> +#include <qfile.h> #include <private/qtextengine_p.h> #include <qpalette.h> #include <qthread.h> diff --git a/src/gui/util/qvalidator.cpp b/src/gui/util/qvalidator.cpp index 31dfd20d1a..4a92b7ea3a 100644 --- a/src/gui/util/qvalidator.cpp +++ b/src/gui/util/qvalidator.cpp @@ -38,6 +38,7 @@ #ifndef QT_NO_VALIDATOR #include "private/qobject_p.h" #include "private/qlocale_p.h" +#include "private/qnumeric_p.h" #include <limits.h> #include <cmath> @@ -676,9 +677,9 @@ QValidator::State QDoubleValidatorPrivate::validateWithLocale(QString &input, QL if (q->t < 0 && buff.startsWith('+')) return QValidator::Invalid; - bool ok, overflow; - double i = QLocaleData::bytearrayToDouble(buff.constData(), &ok, &overflow); - if (overflow) + bool ok = false; + double i = buff.toDouble(&ok); // returns 0.0 if !ok + if (i == qt_qnan()) return QValidator::Invalid; if (!ok) return QValidator::Intermediate; diff --git a/src/network/access/access.pri b/src/network/access/access.pri index e829d52cbe..42c7c80f3b 100644 --- a/src/network/access/access.pri +++ b/src/network/access/access.pri @@ -78,6 +78,8 @@ ios { OBJECTIVE_SOURCES += \ access/qnetworkreplynsurlconnectionimpl.mm + + LIBS_PRIVATE += -framework Foundation } include($$PWD/../../3rdparty/zlib_dependency.pri) diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index ad8a5321b4..2c31afc225 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -1733,7 +1733,7 @@ void QNetworkReplyHttpImplPrivate::_q_startOperation() // ensure this function is only being called once if (state == Working) { - qDebug("QNetworkReplyImpl::_q_startOperation was called more than once"); + qDebug() << "QNetworkReplyHttpImplPrivate::_q_startOperation was called more than once" << url; return; } state = Working; diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp index c73e6162d1..3df39c9bdc 100644 --- a/src/network/access/qnetworkreplyimpl.cpp +++ b/src/network/access/qnetworkreplyimpl.cpp @@ -67,7 +67,7 @@ void QNetworkReplyImplPrivate::_q_startOperation() { // ensure this function is only being called once if (state == Working || state == Finished) { - qDebug("QNetworkReplyImpl::_q_startOperation was called more than once"); + qDebug() << "QNetworkReplyImpl::_q_startOperation was called more than once" << url; return; } state = Working; diff --git a/src/network/kernel/qnetworkinterface.cpp b/src/network/kernel/qnetworkinterface.cpp index 4a527052d1..81906edc47 100644 --- a/src/network/kernel/qnetworkinterface.cpp +++ b/src/network/kernel/qnetworkinterface.cpp @@ -519,6 +519,31 @@ QList<QNetworkAddressEntry> QNetworkInterface::addressEntries() const } /*! + \since 5.7 + + Returns the index of the interface whose name is \a name or 0 if there is + no interface with that name. This function should produce the same result + as the following code, but will probably execute faster. + + \code + QNetworkInterface::interfaceFromName(name).index() + \endcode + + \sa interfaceFromName(), interfaceNameFromIndex(), QUdpDatagram::interfaceIndex() +*/ +int QNetworkInterface::interfaceIndexFromName(const QString &name) +{ + if (name.isEmpty()) + return 0; + + bool ok; + uint id = name.toUInt(&ok); + if (!ok) + id = QNetworkInterfaceManager::interfaceIndexFromName(name); + return int(id); +} + +/*! Returns a QNetworkInterface object for the interface named \a name. If no such interface exists, this function returns an invalid QNetworkInterface object. @@ -553,6 +578,27 @@ QNetworkInterface QNetworkInterface::interfaceFromIndex(int index) } /*! + \since 5.7 + + Returns the name of the interface whose index is \a index or an empty + string if there is no interface with that index. This function should + produce the same result as the following code, but will probably execute + faster. + + \code + QNetworkInterface::interfaceFromIndex(index).name() + \endcode + + \sa interfaceFromIndex(), interfaceIndexFromName(), QUdpDatagram::interfaceIndex() +*/ +QString QNetworkInterface::interfaceNameFromIndex(int index) +{ + if (!index) + return QString(); + return QNetworkInterfaceManager::interfaceNameFromIndex(index); +} + +/*! Returns a listing of all the network interfaces found on the host machine. In case of failure it returns a list with zero elements. */ diff --git a/src/network/kernel/qnetworkinterface.h b/src/network/kernel/qnetworkinterface.h index b3daa3d4a0..bd57aea1cb 100644 --- a/src/network/kernel/qnetworkinterface.h +++ b/src/network/kernel/qnetworkinterface.h @@ -113,8 +113,10 @@ public: QString hardwareAddress() const; QList<QNetworkAddressEntry> addressEntries() const; + static int interfaceIndexFromName(const QString &name); static QNetworkInterface interfaceFromName(const QString &name); static QNetworkInterface interfaceFromIndex(int index); + static QString interfaceNameFromIndex(int index); static QList<QNetworkInterface> allInterfaces(); static QList<QHostAddress> allAddresses(); diff --git a/src/network/kernel/qnetworkinterface_p.h b/src/network/kernel/qnetworkinterface_p.h index 140a28c536..146ba7820c 100644 --- a/src/network/kernel/qnetworkinterface_p.h +++ b/src/network/kernel/qnetworkinterface_p.h @@ -100,6 +100,9 @@ public: QSharedDataPointer<QNetworkInterfacePrivate> interfaceFromIndex(int index); QList<QSharedDataPointer<QNetworkInterfacePrivate> > allInterfaces(); + static uint interfaceIndexFromName(const QString &name); + static QString interfaceNameFromIndex(uint index); + // convenience: QSharedDataPointer<QNetworkInterfacePrivate> empty; diff --git a/src/network/kernel/qnetworkinterface_unix.cpp b/src/network/kernel/qnetworkinterface_unix.cpp index cc53087024..541c78d838 100644 --- a/src/network/kernel/qnetworkinterface_unix.cpp +++ b/src/network/kernel/qnetworkinterface_unix.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Intel Corporation. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -93,14 +94,8 @@ static QHostAddress addressFromSockaddr(sockaddr *sa, int ifindex = 0, const QSt // this is the most likely scenario: // a scope ID in a socket is that of the interface this address came from address.setScopeId(ifname); - } else if (scope) { -#ifndef QT_NO_IPV6IFNAME - char scopeid[IFNAMSIZ]; - if (::if_indextoname(scope, scopeid)) { - address.setScopeId(QLatin1String(scopeid)); - } else -#endif - address.setScopeId(QString::number(uint(scope))); + } else if (scope) { + address.setScopeId(QNetworkInterfaceManager::interfaceNameFromIndex(scope)); } } return address; @@ -124,6 +119,53 @@ static QNetworkInterface::InterfaceFlags convertFlags(uint rawFlags) return flags; } +uint QNetworkInterfaceManager::interfaceIndexFromName(const QString &name) +{ +#ifndef QT_NO_IPV6IFNAME + return ::if_nametoindex(name.toLatin1()); +#elif defined(SIOCGIFINDEX) + struct ifreq req; + int socket = qt_safe_socket(AF_INET, SOCK_STREAM, 0); + if (socket < 0) + return 0; + + QByteArray name8bit = name.toLatin1(); + memset(&req, 0, sizeof(ifreq)); + memcpy(req.ifr_name, name8bit, qMin<int>(name8bit.length() + 1, sizeof(req.ifr_name) - 1)); + + uint id = 0; + if (qt_safe_ioctl(socket, SIOCGIFINDEX, &req) >= 0) + id = req.ifr_ifindex; + qt_safe_close(socket); + return id; +#else + return 0; +#endif +} + +QString QNetworkInterfaceManager::interfaceNameFromIndex(uint index) +{ +#ifndef QT_NO_IPV6IFNAME + char buf[IF_NAMESIZE]; + if (::if_indextoname(index, buf)) + return QString::fromLatin1(buf); +#elif defined(SIOCGIFNAME) + struct ifreq req; + int socket = qt_safe_socket(AF_INET, SOCK_STREAM, 0); + if (socket >= 0) { + memset(&req, 0, sizeof(ifreq)); + req.ifr_ifindex = index; + + if (qt_safe_ioctl(socket, SIOCGIFNAME, &req) >= 0) { + qt_safe_close(socket); + return QString::fromLatin1(req.ifr_name); + } + qt_safe_close(socket); + } +#endif + return QString::number(uint(index)); +} + #ifdef QT_NO_GETIFADDRS // getifaddrs not available diff --git a/src/network/kernel/qnetworkinterface_win.cpp b/src/network/kernel/qnetworkinterface_win.cpp index 907638f73e..238f913846 100644 --- a/src/network/kernel/qnetworkinterface_win.cpp +++ b/src/network/kernel/qnetworkinterface_win.cpp @@ -57,8 +57,14 @@ QT_BEGIN_NAMESPACE +typedef NETIO_STATUS (WINAPI *PtrConvertInterfaceIndexToLuid)(NET_IFINDEX, PNET_LUID); typedef NETIO_STATUS (WINAPI *PtrConvertInterfaceLuidToName)(const NET_LUID *, PWSTR, SIZE_T); +typedef NETIO_STATUS (WINAPI *PtrConvertInterfaceLuidToIndex)(const NET_LUID *, PNET_IFINDEX); +typedef NETIO_STATUS (WINAPI *PtrConvertInterfaceNameToLuid)(const WCHAR *, PNET_LUID); +static PtrConvertInterfaceIndexToLuid ptrConvertInterfaceIndexToLuid = 0; static PtrConvertInterfaceLuidToName ptrConvertInterfaceLuidToName = 0; +static PtrConvertInterfaceLuidToIndex ptrConvertInterfaceLuidToIndex = 0; +static PtrConvertInterfaceNameToLuid ptrConvertInterfaceNameToLuid = 0; static void resolveLibs() { @@ -71,10 +77,16 @@ static void resolveLibs() #if defined(Q_OS_WINCE) // since Windows Embedded Compact 7 + ptrConvertInterfaceIndexToLuid = (PtrConvertInterfaceIndexToLuid)GetProcAddress(iphlpapiHnd, L"ConvertInterfaceIndexToLuid"); ptrConvertInterfaceLuidToName = (PtrConvertInterfaceLuidToName)GetProcAddress(iphlpapiHnd, L"ConvertInterfaceLuidToNameW"); + ptrConvertInterfaceLuidToIndex = (PtrConvertInterfaceLuidToIndex)GetProcAddress(iphlpapiHnd, L"ConvertInterfaceLuidToIndex"); + ptrConvertInterfaceNameToLuid = (PtrConvertInterfaceNameToLuid)GetProcAddress(iphlpapiHnd, L"ConvertInterfaceNameToLuidW"); #else // since Windows Vista + ptrConvertInterfaceIndexToLuid = (PtrConvertInterfaceIndexToLuid)GetProcAddress(iphlpapiHnd, "ConvertInterfaceIndexToLuid"); ptrConvertInterfaceLuidToName = (PtrConvertInterfaceLuidToName)GetProcAddress(iphlpapiHnd, "ConvertInterfaceLuidToNameW"); + ptrConvertInterfaceLuidToIndex = (PtrConvertInterfaceLuidToIndex)GetProcAddress(iphlpapiHnd, "ConvertInterfaceLuidToIndex"); + ptrConvertInterfaceNameToLuid = (PtrConvertInterfaceNameToLuid)GetProcAddress(iphlpapiHnd, "ConvertInterfaceNameToLuidW"); #endif done = true; } @@ -92,13 +104,42 @@ static QHostAddress addressFromSockaddr(sockaddr *sa) address.setAddress(((sockaddr_in6 *)sa)->sin6_addr.s6_addr); int scope = ((sockaddr_in6 *)sa)->sin6_scope_id; if (scope) - address.setScopeId(QString::number(scope)); + address.setScopeId(QNetworkInterfaceManager::interfaceNameFromIndex(scope)); } else qWarning("Got unknown socket family %d", sa->sa_family); return address; } +uint QNetworkInterfaceManager::interfaceIndexFromName(const QString &name) +{ + resolveLibs(); + if (!ptrConvertInterfaceNameToLuid || !ptrConvertInterfaceLuidToIndex) + return 0; + + NET_IFINDEX id; + NET_LUID luid; + if (ptrConvertInterfaceNameToLuid(reinterpret_cast<const wchar_t *>(name.constData()), &luid) == NO_ERROR + && ptrConvertInterfaceLuidToIndex(&luid, &id) == NO_ERROR) + return uint(id); + return 0; +} + +QString QNetworkInterfaceManager::interfaceNameFromIndex(uint index) +{ + resolveLibs(); + if (ptrConvertInterfaceIndexToLuid && ptrConvertInterfaceLuidToName) { + NET_LUID luid; + if (ptrConvertInterfaceIndexToLuid(index, &luid) == NO_ERROR) { + WCHAR buf[IF_MAX_STRING_SIZE + 1]; + if (ptrConvertInterfaceLuidToName(&luid, buf, sizeof(buf)/sizeof(buf[0])) == NO_ERROR) + return QString::fromWCharArray(buf); + } + } + + return QString::number(index); +} + static QHash<QHostAddress, QHostAddress> ipv4Netmasks() { //Retrieve all the IPV4 addresses & netmasks diff --git a/src/network/kernel/qnetworkinterface_winrt.cpp b/src/network/kernel/qnetworkinterface_winrt.cpp index 6791caaf21..18bfcaabda 100644 --- a/src/network/kernel/qnetworkinterface_winrt.cpp +++ b/src/network/kernel/qnetworkinterface_winrt.cpp @@ -61,6 +61,19 @@ struct HostNameInfo { QString address; }; +uint QNetworkInterfaceManager::interfaceIndexFromName(const QString &name) +{ + // TBD - may not be possible + Q_UNUSED(name); + return 0; +} + +QString QNetworkInterfaceManager::interfaceNameFromIndex(uint index) +{ + // TBD - may not be possible + return QString::number(index); +} + static QNetworkInterfacePrivate *interfaceFromProfile(IConnectionProfile *profile, QList<HostNameInfo> *hostList) { if (!profile) diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index 1831d25718..08ccff83fa 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -722,7 +722,7 @@ bool QAbstractSocketPrivate::canReadNotification() } } - // only emit readyRead() when not recursing, and only if there is data available + // Only emit readyRead() if there is data available. bool hasData = newBytes > 0 #ifndef QT_NO_UDPSOCKET || (!isBuffered && socketType != QAbstractSocket::TcpSocket && socketEngine && socketEngine->hasPendingDatagrams()) @@ -730,11 +730,8 @@ bool QAbstractSocketPrivate::canReadNotification() || (!isBuffered && socketType == QAbstractSocket::TcpSocket && socketEngine) ; - if (!emittedReadyRead && hasData) { - QScopedValueRollback<bool> r(emittedReadyRead); - emittedReadyRead = true; - emit q->readyRead(); - } + if (hasData) + emitReadyRead(); // If we were closed as a result of the readyRead() signal, // return. @@ -792,12 +789,12 @@ void QAbstractSocketPrivate::canCloseNotification() // then occur when we read from the socket again and fail // in canReadNotification or by the manually created // closeNotification below. - emit q->readyRead(); + emitReadyRead(); QMetaObject::invokeMethod(socketEngine, "closeNotification", Qt::QueuedConnection); } } else if (socketType == QAbstractSocket::TcpSocket && socketEngine) { - emit q->readyRead(); + emitReadyRead(); } } @@ -817,8 +814,7 @@ bool QAbstractSocketPrivate::canWriteNotification() #if defined (QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocketPrivate::canWriteNotification() flushing"); #endif - qint64 tmp = writeBuffer.size(); - flush(); + bool dataWasWritten = writeToSocket(); if (socketEngine) { #if defined (Q_OS_WIN) @@ -830,7 +826,7 @@ bool QAbstractSocketPrivate::canWriteNotification() #endif } - return (writeBuffer.size() < tmp); + return dataWasWritten; } /*! \internal @@ -852,21 +848,20 @@ void QAbstractSocketPrivate::connectionNotification() /*! \internal - Writes pending data in the write buffers to the socket. The - function writes as much as it can without blocking. + Writes one pending data block in the write buffer to the socket. It is usually invoked by canWriteNotification after one or more calls to write(). Emits bytesWritten(). */ -bool QAbstractSocketPrivate::flush() +bool QAbstractSocketPrivate::writeToSocket() { Q_Q(QAbstractSocket); if (!socketEngine || !socketEngine->isValid() || (writeBuffer.isEmpty() && socketEngine->bytesToWrite() == 0)) { #if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocketPrivate::flush() nothing to do: valid ? %s, writeBuffer.isEmpty() ? %s", + qDebug("QAbstractSocketPrivate::writeToSocket() nothing to do: valid ? %s, writeBuffer.isEmpty() ? %s", (socketEngine && socketEngine->isValid()) ? "yes" : "no", writeBuffer.isEmpty() ? "yes" : "no"); #endif @@ -884,7 +879,8 @@ bool QAbstractSocketPrivate::flush() qint64 written = socketEngine->write(ptr, nextSize); if (written < 0) { #if defined (QABSTRACTSOCKET_DEBUG) - qDebug() << "QAbstractSocketPrivate::flush() write error, aborting." << socketEngine->errorString(); + qDebug() << "QAbstractSocketPrivate::writeToSocket() write error, aborting." + << socketEngine->errorString(); #endif setErrorAndEmit(socketEngine->error(), socketEngine->errorString()); // an unexpected error so close the socket. @@ -893,7 +889,7 @@ bool QAbstractSocketPrivate::flush() } #if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocketPrivate::flush() %lld bytes written to the network", + qDebug("QAbstractSocketPrivate::writeToSocket() %lld bytes written to the network", written); #endif @@ -914,7 +910,23 @@ bool QAbstractSocketPrivate::flush() if (state == QAbstractSocket::ClosingState) q->disconnectFromHost(); - return true; + return written > 0; +} + +/*! \internal + + Writes pending data in the write buffers to the socket. The function + writes as much as it can without blocking. If any data was written, + this function returns true; otherwise false is returned. +*/ +bool QAbstractSocketPrivate::flush() +{ + bool dataWasWritten = false; + + while (!writeBuffer.isEmpty() && writeToSocket()) + dataWasWritten = true; + + return dataWasWritten; } #ifndef QT_NO_NETWORKPROXY @@ -1248,10 +1260,7 @@ void QAbstractSocketPrivate::_q_forceDisconnect() */ bool QAbstractSocketPrivate::readFromSocket() { -#ifdef QABSTRACTSOCKET_DEBUG Q_Q(QAbstractSocket); -#endif - // Find how many bytes we can read from the socket layer. qint64 bytesToRead = socketEngine->bytesAvailable(); if (bytesToRead == 0) { @@ -1280,7 +1289,7 @@ bool QAbstractSocketPrivate::readFromSocket() buffer.chop(bytesToRead); return true; } - buffer.chop(bytesToRead - (readBytes < 0 ? qint64(0) : readBytes)); + buffer.chop(bytesToRead - ((readBytes < 0 || !q->isReadable()) ? qint64(0) : readBytes)); #if defined(QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocketPrivate::readFromSocket() got %lld bytes, buffer size = %lld", readBytes, buffer.size()); @@ -1301,6 +1310,21 @@ bool QAbstractSocketPrivate::readFromSocket() /*! \internal + Prevents from the recursive readyRead() emission. +*/ +void QAbstractSocketPrivate::emitReadyRead() +{ + Q_Q(QAbstractSocket); + // Only emit readyRead() when not recursing. + if (!emittedReadyRead) { + QScopedValueRollback<bool> r(emittedReadyRead); + emittedReadyRead = true; + emit q->readyRead(); + } +} + +/*! \internal + Sets up the internal state after the connection has succeeded. */ void QAbstractSocketPrivate::fetchConnectionParameters() @@ -2378,7 +2402,7 @@ bool QAbstractSocket::isSequential() const */ bool QAbstractSocket::atEnd() const { - return QIODevice::atEnd() && (!isOpen() || d_func()->buffer.isEmpty()); + return QIODevice::atEnd(); } /*! diff --git a/src/network/socket/qabstractsocket.h b/src/network/socket/qabstractsocket.h index 23f0d26cbd..f8edc74a24 100644 --- a/src/network/socket/qabstractsocket.h +++ b/src/network/socket/qabstractsocket.h @@ -176,7 +176,7 @@ public: // from QIODevice void close() Q_DECL_OVERRIDE; bool isSequential() const Q_DECL_OVERRIDE; - bool atEnd() const Q_DECL_OVERRIDE; + bool atEnd() const Q_DECL_OVERRIDE; // ### Qt6: remove me bool flush(); // for synchronous access diff --git a/src/network/socket/qabstractsocket_p.h b/src/network/socket/qabstractsocket_p.h index a905625b19..ba129b48df 100644 --- a/src/network/socket/qabstractsocket_p.h +++ b/src/network/socket/qabstractsocket_p.h @@ -135,6 +135,8 @@ public: void fetchConnectionParameters(); void setupSocketNotifiers(); bool readFromSocket(); + bool writeToSocket(); + void emitReadyRead(); void setError(QAbstractSocket::SocketError errorCode, const QString &errorString); void setErrorAndEmit(QAbstractSocket::SocketError errorCode, const QString &errorString); diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h index 0fa1d8f96e..5cc5e529fc 100644 --- a/src/network/socket/qnativesocketengine_p.h +++ b/src/network/socket/qnativesocketengine_p.h @@ -45,6 +45,7 @@ // We mean it. // #include "QtNetwork/qhostaddress.h" +#include "QtNetwork/qnetworkinterface.h" #include "private/qabstractsocketengine_p.h" #ifndef Q_OS_WIN # include "qplatformdefs.h" @@ -264,7 +265,8 @@ public: bool checkProxy(const QHostAddress &address); bool fetchConnectionParameters(); - static uint scopeIdFromString(const QString &scopeid); + static uint scopeIdFromString(const QString &scopeid) + { return QNetworkInterface::interfaceIndexFromName(scopeid); } /*! \internal Sets \a address and \a port in the \a aa sockaddr structure and the size in \a sockAddrSize. diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index 8626a6be0f..6aff1d25b2 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -107,15 +107,8 @@ static inline void qt_socket_getPortAndAddress(const qt_sockaddr *s, quint16 *po QHostAddress tmpAddress; tmpAddress.setAddress(tmp); *addr = tmpAddress; - if (s->a6.sin6_scope_id) { -#ifndef QT_NO_IPV6IFNAME - char scopeid[IFNAMSIZ]; - if (::if_indextoname(s->a6.sin6_scope_id, scopeid)) { - addr->setScopeId(QLatin1String(scopeid)); - } else -#endif - addr->setScopeId(QString::number(s->a6.sin6_scope_id)); - } + if (s->a6.sin6_scope_id) + addr->setScopeId(QNetworkInterface::interfaceNameFromIndex(s->a6.sin6_scope_id)); } if (port) *port = ntohs(s->a6.sin6_port); @@ -131,21 +124,6 @@ static inline void qt_socket_getPortAndAddress(const qt_sockaddr *s, quint16 *po } } -// inline on purpose -inline uint QNativeSocketEnginePrivate::scopeIdFromString(const QString &scopeid) -{ - if (scopeid.isEmpty()) - return 0; - - bool ok; - uint id = scopeid.toUInt(&ok); -#ifndef QT_NO_IPV6IFNAME - if (!ok) - id = ::if_nametoindex(scopeid.toLatin1()); -#endif - return id; -} - static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt, QAbstractSocket::NetworkLayerProtocol socketProtocol, int &level, int &n) { diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index 9aed0caa25..1379ed93ba 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -322,12 +322,6 @@ static inline int qt_socket_getMaxMsgSize(qintptr socketDescriptor) # define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) #endif -// inline on purpose -inline uint QNativeSocketEnginePrivate::scopeIdFromString(const QString &scopeid) -{ - return scopeid.toUInt(); -} - bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType socketType, QAbstractSocket::NetworkLayerProtocol &socketProtocol) { @@ -657,6 +651,13 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin int tries = 0; do { if (::getsockopt(socketDescriptor, SOL_SOCKET, SO_ERROR, (char *) &value, &valueSize) == 0) { + if (value != NOERROR) { + // MSDN says getsockopt with SO_ERROR clears the error, but it's not actually cleared + // and this can affect all subsequent WSAConnect attempts, so clear it now. + const int val = NO_ERROR; + ::setsockopt(socketDescriptor, SOL_SOCKET, SO_ERROR, reinterpret_cast<const char*>(&val), sizeof val); + } + if (value == WSAECONNREFUSED) { setError(QAbstractSocket::ConnectionRefusedError, ConnectionRefusedErrorString); socketState = QAbstractSocket::UnconnectedState; diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp index 06c707f271..a8f7b7320e 100644 --- a/src/network/ssl/qsslsocket_mac.cpp +++ b/src/network/ssl/qsslsocket_mac.cpp @@ -589,6 +589,9 @@ QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSLCipherSuite(SSLCipherSui case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: ciph.d->name = QLatin1String("ECDHE-RSA-AES256-SHA384"); break; + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + ciph.d->name = QLatin1String("ECDHE-RSA-AES256-GCM-SHA384"); + break; default: return ciph; } @@ -636,6 +639,10 @@ QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSLCipherSuite(SSLCipherSui ciph.d->encryptionMethod = QLatin1String("AES(128)"); ciph.d->bits = 128; ciph.d->supportedBits = 128; + } else if (ciph.d->name.contains("AES256-GCM")) { + ciph.d->encryptionMethod = QLatin1String("AESGCM(256)"); + ciph.d->bits = 256; + ciph.d->supportedBits = 256; } else if (ciph.d->name.contains("AES256-")) { ciph.d->encryptionMethod = QLatin1String("AES(256)"); ciph.d->bits = 256; diff --git a/src/platformsupport/cglconvenience/cglconvenience.mm b/src/platformsupport/cglconvenience/cglconvenience.mm index 6b0a91e13f..b20f324442 100644 --- a/src/platformsupport/cglconvenience/cglconvenience.mm +++ b/src/platformsupport/cglconvenience/cglconvenience.mm @@ -34,7 +34,7 @@ #include "cglconvenience_p.h" #include <QtCore/qglobal.h> #include <QtCore/private/qcore_mac_p.h> -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> #include <QVector> void (*qcgl_getProcAddress(const QByteArray &procName))() diff --git a/src/platformsupport/cglconvenience/cglconvenience.pri b/src/platformsupport/cglconvenience/cglconvenience.pri index 0e86ddb208..1de38bbd08 100644 --- a/src/platformsupport/cglconvenience/cglconvenience.pri +++ b/src/platformsupport/cglconvenience/cglconvenience.pri @@ -1,4 +1,4 @@ -mac:!ios { +osx { INCLUDEPATH += $$PWD HEADERS += \ @@ -7,5 +7,5 @@ mac:!ios { OBJECTIVE_SOURCES += \ $$PWD/cglconvenience.mm - LIBS_PRIVATE += -framework Cocoa -framework OpenGL + LIBS_PRIVATE += -framework AppKit -framework OpenGL } diff --git a/src/platformsupport/clipboard/qmacmime.mm b/src/platformsupport/clipboard/qmacmime.mm index ffa548bf83..5fec32c650 100644 --- a/src/platformsupport/clipboard/qmacmime.mm +++ b/src/platformsupport/clipboard/qmacmime.mm @@ -32,10 +32,13 @@ ****************************************************************************/ #include <QtCore/qsystemdetection.h> + +#if defined(Q_OS_OSX) +#import <AppKit/AppKit.h> +#endif + #if defined(Q_OS_IOS) #import <UIKit/UIKit.h> -#elif defined(Q_OS_OSX) -#import <Cocoa/Cocoa.h> #endif #include "qmacmime_p.h" diff --git a/src/platformsupport/devicediscovery/qdevicediscovery_static.cpp b/src/platformsupport/devicediscovery/qdevicediscovery_static.cpp index 3aab785b34..de2f3415af 100644 --- a/src/platformsupport/devicediscovery/qdevicediscovery_static.cpp +++ b/src/platformsupport/devicediscovery/qdevicediscovery_static.cpp @@ -56,6 +56,12 @@ #ifndef ABS_CNT #define ABS_CNT (ABS_MAX+1) #endif +#ifndef ABS_MT_POSITION_X +#define ABS_MT_POSITION_X 0x35 +#endif +#ifndef ABS_MT_POSITION_Y +#define ABS_MT_POSITION_Y 0x36 +#endif #define LONG_BITS (sizeof(long) * 8 ) #define LONG_FIELD_SIZE(bits) ((bits / LONG_BITS) + 1) @@ -113,67 +119,71 @@ QStringList QDeviceDiscoveryStatic::scanConnectedDevices() bool QDeviceDiscoveryStatic::checkDeviceType(const QString &device) { - bool ret = false; int fd = QT_OPEN(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0); if (!fd) { qWarning() << "Device discovery cannot open device" << device; return false; } + qCDebug(lcDD) << "doing static device discovery for " << device; + + if ((m_types & Device_DRM) && device.contains(QString::fromLatin1(QT_DRM_DEVICE_PREFIX))) + return true; + + long bitsAbs[LONG_FIELD_SIZE(ABS_CNT)]; long bitsKey[LONG_FIELD_SIZE(KEY_CNT)]; - if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(bitsKey)), bitsKey) >= 0 ) { - if (!ret && (m_types & Device_Keyboard)) { - if (testBit(KEY_Q, bitsKey)) { - qCDebug(lcDD) << "Found keyboard at" << device; - ret = true; - } - } + long bitsRel[LONG_FIELD_SIZE(REL_CNT)]; + memset(bitsAbs, 0, sizeof(bitsAbs)); + memset(bitsKey, 0, sizeof(bitsKey)); + memset(bitsRel, 0, sizeof(bitsRel)); - if (!ret && (m_types & Device_Mouse)) { - long bitsRel[LONG_FIELD_SIZE(REL_CNT)]; - if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(bitsRel)), bitsRel) >= 0 ) { - if (testBit(REL_X, bitsRel) && testBit(REL_Y, bitsRel) && testBit(BTN_MOUSE, bitsKey)) { - qCDebug(lcDD) << "Found mouse at" << device; - ret = true; - } - } + ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(bitsAbs)), bitsAbs); + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(bitsKey)), bitsKey); + ioctl(fd, EVIOCGBIT(EV_REL, sizeof(bitsRel)), bitsRel); + + QT_CLOSE(fd); + + if ((m_types & Device_Keyboard)) { + if (testBit(KEY_Q, bitsKey)) { + qCDebug(lcDD) << "Found keyboard at" << device; + return true; } + } - if (!ret && (m_types & (Device_Touchpad | Device_Touchscreen))) { - long bitsAbs[LONG_FIELD_SIZE(ABS_CNT)]; - if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(bitsAbs)), bitsAbs) >= 0 ) { - if (testBit(ABS_X, bitsAbs) && testBit(ABS_Y, bitsAbs)) { - if ((m_types & Device_Touchpad) && testBit(BTN_TOOL_FINGER, bitsKey)) { - qCDebug(lcDD) << "Found touchpad at" << device; - ret = true; - } else if ((m_types & Device_Touchscreen) && testBit(BTN_TOUCH, bitsKey)) { - qCDebug(lcDD) << "Found touchscreen at" << device; - ret = true; - } else if ((m_types & Device_Tablet) && (testBit(BTN_STYLUS, bitsKey) || testBit(BTN_TOOL_PEN, bitsKey))) { - qCDebug(lcDD) << "Found tablet at" << device; - ret = true; - } - } - } + if ((m_types & Device_Mouse)) { + if (testBit(REL_X, bitsRel) && testBit(REL_Y, bitsRel) && testBit(BTN_MOUSE, bitsKey)) { + qCDebug(lcDD) << "Found mouse at" << device; + return true; } + } - if (!ret && (m_types & Device_Joystick)) { - long bitsAbs[LONG_FIELD_SIZE(ABS_CNT)]; - if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(bitsAbs)), bitsAbs) >= 0 ) { - if ((m_types & Device_Joystick) - && (testBit(BTN_A, bitsKey) || testBit(BTN_TRIGGER, bitsKey) || testBit(ABS_RX, bitsAbs))) { - qCDebug(lcDD) << "Found joystick/gamepad at" << device; - ret = true; - } + if ((m_types & (Device_Touchpad | Device_Touchscreen))) { + if (testBit(ABS_X, bitsAbs) && testBit(ABS_Y, bitsAbs)) { + if ((m_types & Device_Touchpad) && testBit(BTN_TOOL_FINGER, bitsKey)) { + qCDebug(lcDD) << "Found touchpad at" << device; + return true; + } else if ((m_types & Device_Touchscreen) && testBit(BTN_TOUCH, bitsKey)) { + qCDebug(lcDD) << "Found touchscreen at" << device; + return true; + } else if ((m_types & Device_Tablet) && (testBit(BTN_STYLUS, bitsKey) || testBit(BTN_TOOL_PEN, bitsKey))) { + qCDebug(lcDD) << "Found tablet at" << device; + return true; } + } else if (testBit(ABS_MT_POSITION_X, bitsAbs) && + testBit(ABS_MT_POSITION_Y, bitsAbs)) { + qCDebug(lcDD) << "Found new-style touchscreen at" << device; + return true; } } - if (!ret && (m_types & Device_DRM) && device.contains(QString::fromLatin1(QT_DRM_DEVICE_PREFIX))) - ret = true; + if ((m_types & Device_Joystick)) { + if (testBit(BTN_A, bitsKey) || testBit(BTN_TRIGGER, bitsKey) || testBit(ABS_RX, bitsAbs)) { + qCDebug(lcDD) << "Found joystick/gamepad at" << device; + return true; + } + } - QT_CLOSE(fd); - return ret; + return false; } QT_END_NAMESPACE diff --git a/src/platformsupport/devicediscovery/qdevicediscovery_udev.cpp b/src/platformsupport/devicediscovery/qdevicediscovery_udev.cpp index f285e61a9f..334eb51a86 100644 --- a/src/platformsupport/devicediscovery/qdevicediscovery_udev.cpp +++ b/src/platformsupport/devicediscovery/qdevicediscovery_udev.cpp @@ -185,11 +185,11 @@ void QDeviceDiscoveryUDev::handleUDevNotification() // if we cannot determine a type, walk up the device tree if (!checkDeviceType(dev)) { // does not increase the refcount - dev = udev_device_get_parent_with_subsystem_devtype(dev, subsystem, 0); - if (!dev) + struct udev_device *parent_dev = udev_device_get_parent_with_subsystem_devtype(dev, subsystem, 0); + if (!parent_dev) goto cleanup; - if (!checkDeviceType(dev)) + if (!checkDeviceType(parent_dev)) goto cleanup; } diff --git a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp index 0af9440b50..2a39fc1bbd 100644 --- a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp +++ b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp @@ -36,6 +36,7 @@ #include <QtCore/QList> #include <QtCore/QElapsedTimer> +#include <QtCore/QFile> #include <qpa/qplatformnativeinterface.h> #include <qpa/qplatformscreen.h> diff --git a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm index 0af779097c..a87443cc85 100644 --- a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm +++ b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm @@ -35,8 +35,8 @@ #include <sys/param.h> -#if defined(Q_OS_MACX) -#import <Cocoa/Cocoa.h> +#if defined(Q_OS_OSX) +#import <AppKit/AppKit.h> #import <IOKit/graphics/IOGraphicsLib.h> #elif defined(Q_OS_IOS) #import <UIKit/UIFont.h> diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp index 06751de0ef..d467a62abd 100644 --- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp +++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp @@ -35,6 +35,7 @@ #include <qplatformdefs.h> +#include <QFile> #include <QSocketNotifier> #include <QStringList> #include <QCoreApplication> diff --git a/src/platformsupport/input/evdevtablet/evdevtablet.pri b/src/platformsupport/input/evdevtablet/evdevtablet.pri index 5ace0df61d..fb9489353c 100644 --- a/src/platformsupport/input/evdevtablet/evdevtablet.pri +++ b/src/platformsupport/input/evdevtablet/evdevtablet.pri @@ -1,8 +1,10 @@ HEADERS += \ - $$PWD/qevdevtablet_p.h + $$PWD/qevdevtablethandler_p.h \ + $$PWD/qevdevtabletmanager_p.h SOURCES += \ - $$PWD/qevdevtablet.cpp + $$PWD/qevdevtablethandler.cpp \ + $$PWD/qevdevtabletmanager.cpp contains(QT_CONFIG, libudev) { LIBS_PRIVATE += $$QMAKE_LIBS_LIBUDEV diff --git a/src/platformsupport/input/evdevtablet/qevdevtablet.cpp b/src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp index c6f952c64d..aa87340112 100644 --- a/src/platformsupport/input/evdevtablet/qevdevtablet.cpp +++ b/src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp @@ -31,14 +31,14 @@ ** ****************************************************************************/ -#include "qevdevtablet_p.h" -#include <qpa/qwindowsysteminterface.h> +#include "qevdevtablethandler_p.h" + #include <QStringList> #include <QSocketNotifier> #include <QGuiApplication> #include <QLoggingCategory> #include <QtCore/private/qcore_unix_p.h> -#include <QtPlatformSupport/private/qdevicediscovery_p.h> +#include <qpa/qwindowsysteminterface.h> #include <linux/input.h> QT_BEGIN_NAMESPACE @@ -49,16 +49,11 @@ class QEvdevTabletData { public: QEvdevTabletData(QEvdevTabletHandler *q_ptr); - bool queryLimits(); - void testGrab(); + void processInputEvent(input_event *ev); - void reportProximityEnter(); - void reportProximityLeave(); void report(); QEvdevTabletHandler *q; - QSocketNotifier *notifier; - int fd; int lastEventType; QString devName; struct { @@ -73,57 +68,13 @@ public: }; QEvdevTabletData::QEvdevTabletData(QEvdevTabletHandler *q_ptr) - : q(q_ptr), notifier(0), fd(-1), lastEventType(0) + : q(q_ptr), lastEventType(0) { memset(&minValues, 0, sizeof(minValues)); memset(&maxValues, 0, sizeof(maxValues)); memset(&state, 0, sizeof(state)); } -bool QEvdevTabletData::queryLimits() -{ - bool ok = true; - input_absinfo absInfo; - memset(&absInfo, 0, sizeof(input_absinfo)); - ok &= ioctl(fd, EVIOCGABS(ABS_X), &absInfo) >= 0; - if (ok) { - minValues.x = absInfo.minimum; - maxValues.x = absInfo.maximum; - qCDebug(qLcEvdevTablet, "evdevtablet: min X: %d max X: %d", minValues.x, maxValues.x); - } - ok &= ioctl(fd, EVIOCGABS(ABS_Y), &absInfo) >= 0; - if (ok) { - minValues.y = absInfo.minimum; - maxValues.y = absInfo.maximum; - qCDebug(qLcEvdevTablet, "evdevtablet: min Y: %d max Y: %d", minValues.y, maxValues.y); - } - if (ioctl(fd, EVIOCGABS(ABS_PRESSURE), &absInfo) >= 0) { - minValues.p = absInfo.minimum; - maxValues.p = absInfo.maximum; - qCDebug(qLcEvdevTablet, "evdevtablet: min pressure: %d max pressure: %d", minValues.p, maxValues.p); - } - if (ioctl(fd, EVIOCGABS(ABS_DISTANCE), &absInfo) >= 0) { - minValues.d = absInfo.minimum; - maxValues.d = absInfo.maximum; - qCDebug(qLcEvdevTablet, "evdevtablet: min distance: %d max distance: %d", minValues.d, maxValues.d); - } - char name[128]; - if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), name) >= 0) { - devName = QString::fromLocal8Bit(name); - qCDebug(qLcEvdevTablet, "evdevtablet: device name: %s", name); - } - return ok; -} - -void QEvdevTabletData::testGrab() -{ - bool grabSuccess = !ioctl(fd, EVIOCGRAB, (void *) 1); - if (grabSuccess) - ioctl(fd, EVIOCGRAB, (void *) 0); - else - qWarning("evdevtablet: ERROR: The device is grabbed by another process. No events will be read."); -} - void QEvdevTabletData::processInputEvent(input_event *ev) { if (ev->type == EV_ABS) { @@ -167,20 +118,10 @@ void QEvdevTabletData::processInputEvent(input_event *ev) lastEventType = ev->type; } -void QEvdevTabletData::reportProximityEnter() -{ - QWindowSystemInterface::handleTabletEnterProximityEvent(QTabletEvent::Stylus, state.tool, 1); -} - -void QEvdevTabletData::reportProximityLeave() -{ - QWindowSystemInterface::handleTabletLeaveProximityEvent(QTabletEvent::Stylus, state.tool, 1); -} - void QEvdevTabletData::report() { if (!state.lastReportTool && state.tool) - reportProximityEnter(); + QWindowSystemInterface::handleTabletEnterProximityEvent(QTabletEvent::Stylus, state.tool, q->deviceId()); qreal nx = (state.x - minValues.x) / qreal(maxValues.x - minValues.x); qreal ny = (state.y - minValues.y) / qreal(maxValues.y - minValues.y); @@ -194,16 +135,17 @@ void QEvdevTabletData::report() pointer = state.lastReportTool; } - qreal pressure = (state.p - minValues.p) / qreal(maxValues.p - minValues.p); + int pressureRange = maxValues.p - minValues.p; + qreal pressure = pressureRange ? (state.p - minValues.p) / qreal(pressureRange) : qreal(1); if (state.down || state.lastReportDown) { QWindowSystemInterface::handleTabletEvent(0, state.down, QPointF(), globalPos, QTabletEvent::Stylus, pointer, - pressure, 0, 0, 0, 0, 0, 1, qGuiApp->keyboardModifiers()); + pressure, 0, 0, 0, 0, 0, q->deviceId(), qGuiApp->keyboardModifiers()); } if (state.lastReportTool && !state.tool) - reportProximityLeave(); + QWindowSystemInterface::handleTabletLeaveProximityEvent(QTabletEvent::Stylus, state.tool, q->deviceId()); state.lastReportDown = state.down; state.lastReportTool = state.tool; @@ -211,69 +153,104 @@ void QEvdevTabletData::report() } -QEvdevTabletHandler::QEvdevTabletHandler(const QString &spec, QObject *parent) - : QObject(parent), d(0) +QEvdevTabletHandler::QEvdevTabletHandler(const QString &device, const QString &spec, QObject *parent) + : QObject(parent), m_fd(-1), m_device(device), m_notifier(0), d(0) { + Q_UNUSED(spec) + setObjectName(QLatin1String("Evdev Tablet Handler")); - d = new QEvdevTabletData(this); - QString dev; - QStringList args = spec.split(QLatin1Char(':')); - for (int i = 0; i < args.count(); ++i) { - if (args.at(i).startsWith(QLatin1String("/dev/"))) { - dev = args.at(i); - break; - } - } - if (dev.isEmpty()) { - QScopedPointer<QDeviceDiscovery> deviceDiscovery( - QDeviceDiscovery::create(QDeviceDiscovery::Device_Tablet, this)); - if (deviceDiscovery) { - QStringList devices = deviceDiscovery->scanConnectedDevices(); - if (!devices.isEmpty()) - dev = devices.at(0); - } - } - if (!dev.isEmpty()) { - qCDebug(qLcEvdevTablet, "evdevtablet: using %s", qPrintable(dev)); - d->fd = QT_OPEN(dev.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0); - if (d->fd >= 0) { - d->testGrab(); - if (d->queryLimits()) { - d->notifier = new QSocketNotifier(d->fd, QSocketNotifier::Read, this); - connect(d->notifier, SIGNAL(activated(int)), this, SLOT(readData())); - } - } else { - qErrnoWarning(errno, "evdevtablet: Cannot open input device"); - } + + qCDebug(qLcEvdevTablet, "evdevtablet: using %s", qPrintable(device)); + + m_fd = QT_OPEN(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0); + if (m_fd < 0) { + qErrnoWarning(errno, "evdevtablet: Cannot open input device %s", qPrintable(device)); + return; } + + bool grabSuccess = !ioctl(m_fd, EVIOCGRAB, (void *) 1); + if (grabSuccess) + ioctl(m_fd, EVIOCGRAB, (void *) 0); + else + qWarning("evdevtablet: %s: The device is grabbed by another process. No events will be read.", qPrintable(device)); + + d = new QEvdevTabletData(this); + if (!queryLimits()) + qWarning("evdevtablet: %s: Unset or invalid ABS limits. Behavior will be unspecified.", qPrintable(device)); + + m_notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this); + connect(m_notifier, &QSocketNotifier::activated, this, &QEvdevTabletHandler::readData); } QEvdevTabletHandler::~QEvdevTabletHandler() { - delete d->notifier; - if (d->fd >= 0) - QT_CLOSE(d->fd); + if (m_fd >= 0) + QT_CLOSE(m_fd); delete d; } +qint64 QEvdevTabletHandler::deviceId() const +{ + return m_fd; +} + +bool QEvdevTabletHandler::queryLimits() +{ + bool ok = true; + input_absinfo absInfo; + memset(&absInfo, 0, sizeof(input_absinfo)); + ok &= ioctl(m_fd, EVIOCGABS(ABS_X), &absInfo) >= 0; + if (ok) { + d->minValues.x = absInfo.minimum; + d->maxValues.x = absInfo.maximum; + qCDebug(qLcEvdevTablet, "evdevtablet: %s: min X: %d max X: %d", qPrintable(m_device), + d->minValues.x, d->maxValues.x); + } + ok &= ioctl(m_fd, EVIOCGABS(ABS_Y), &absInfo) >= 0; + if (ok) { + d->minValues.y = absInfo.minimum; + d->maxValues.y = absInfo.maximum; + qCDebug(qLcEvdevTablet, "evdevtablet: %s: min Y: %d max Y: %d", qPrintable(m_device), + d->minValues.y, d->maxValues.y); + } + if (ioctl(m_fd, EVIOCGABS(ABS_PRESSURE), &absInfo) >= 0) { + d->minValues.p = absInfo.minimum; + d->maxValues.p = absInfo.maximum; + qCDebug(qLcEvdevTablet, "evdevtablet: %s: min pressure: %d max pressure: %d", qPrintable(m_device), + d->minValues.p, d->maxValues.p); + } + if (ioctl(m_fd, EVIOCGABS(ABS_DISTANCE), &absInfo) >= 0) { + d->minValues.d = absInfo.minimum; + d->maxValues.d = absInfo.maximum; + qCDebug(qLcEvdevTablet, "evdevtablet: %s: min distance: %d max distance: %d", qPrintable(m_device), + d->minValues.d, d->maxValues.d); + } + char name[128]; + if (ioctl(m_fd, EVIOCGNAME(sizeof(name) - 1), name) >= 0) { + d->devName = QString::fromLocal8Bit(name); + qCDebug(qLcEvdevTablet, "evdevtablet: %s: device name: %s", qPrintable(m_device), name); + } + return ok; +} + void QEvdevTabletHandler::readData() { static input_event buffer[32]; int n = 0; for (; ;) { - int result = QT_READ(d->fd, reinterpret_cast<char*>(buffer) + n, sizeof(buffer) - n); + int result = QT_READ(m_fd, reinterpret_cast<char*>(buffer) + n, sizeof(buffer) - n); if (!result) { - qWarning("evdevtablet: Got EOF from input device"); + qWarning("evdevtablet: %s: Got EOF from input device", qPrintable(m_device)); return; } else if (result < 0) { if (errno != EINTR && errno != EAGAIN) { - qErrnoWarning(errno, "evdevtablet: Could not read from input device"); + qErrnoWarning(errno, "evdevtablet: %s: Could not read from input device", qPrintable(m_device)); if (errno == ENODEV) { // device got disconnected -> stop reading - delete d->notifier; - d->notifier = 0; - QT_CLOSE(d->fd); - d->fd = -1; + delete m_notifier; + m_notifier = 0; + QT_CLOSE(m_fd); + m_fd = -1; } return; } @@ -291,8 +268,8 @@ void QEvdevTabletHandler::readData() } -QEvdevTabletHandlerThread::QEvdevTabletHandlerThread(const QString &spec, QObject *parent) - : QDaemonThread(parent), m_spec(spec), m_handler(0) +QEvdevTabletHandlerThread::QEvdevTabletHandlerThread(const QString &device, const QString &spec, QObject *parent) + : QDaemonThread(parent), m_device(device), m_spec(spec), m_handler(0) { start(); } @@ -305,7 +282,7 @@ QEvdevTabletHandlerThread::~QEvdevTabletHandlerThread() void QEvdevTabletHandlerThread::run() { - m_handler = new QEvdevTabletHandler(m_spec); + m_handler = new QEvdevTabletHandler(m_device, m_spec); exec(); delete m_handler; m_handler = 0; diff --git a/src/platformsupport/input/evdevtablet/qevdevtablet_p.h b/src/platformsupport/input/evdevtablet/qevdevtablethandler_p.h index f546f9a88a..4a9b2bab34 100644 --- a/src/platformsupport/input/evdevtablet/qevdevtablet_p.h +++ b/src/platformsupport/input/evdevtablet/qevdevtablethandler_p.h @@ -31,8 +31,8 @@ ** ****************************************************************************/ -#ifndef QEVDEVTABLET_P_H -#define QEVDEVTABLET_P_H +#ifndef QEVDEVTABLETHANDLER_P_H +#define QEVDEVTABLETHANDLER_P_H // // W A R N I N G @@ -52,6 +52,7 @@ QT_BEGIN_NAMESPACE +class QSocketNotifier; class QEvdevTabletData; class QEvdevTabletHandler : public QObject @@ -59,29 +60,37 @@ class QEvdevTabletHandler : public QObject Q_OBJECT public: - explicit QEvdevTabletHandler(const QString &spec = QString(), QObject *parent = 0); + explicit QEvdevTabletHandler(const QString &device, const QString &spec = QString(), QObject *parent = 0); ~QEvdevTabletHandler(); + qint64 deviceId() const; + private slots: void readData(); private: + bool queryLimits(); + + int m_fd; + QString m_device; + QSocketNotifier *m_notifier; QEvdevTabletData *d; }; class QEvdevTabletHandlerThread : public QDaemonThread { public: - explicit QEvdevTabletHandlerThread(const QString &spec, QObject *parent = 0); + explicit QEvdevTabletHandlerThread(const QString &device, const QString &spec, QObject *parent = 0); ~QEvdevTabletHandlerThread(); void run() Q_DECL_OVERRIDE; QEvdevTabletHandler *handler() { return m_handler; } private: + QString m_device; QString m_spec; QEvdevTabletHandler *m_handler; }; QT_END_NAMESPACE -#endif // QEVDEVTABLET_P_H +#endif // QEVDEVTABLETHANDLER_P_H diff --git a/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp b/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp new file mode 100644 index 0000000000..05fd6e655c --- /dev/null +++ b/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qevdevtabletmanager_p.h" +#include "qevdevtablethandler_p.h" + +#include <QStringList> +#include <QGuiApplication> +#include <QLoggingCategory> +#include <QtPlatformSupport/private/qdevicediscovery_p.h> +#include <private/qguiapplication_p.h> +#include <private/qinputdevicemanager_p_p.h> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(qLcEvdevTablet) + +QEvdevTabletManager::QEvdevTabletManager(const QString &key, const QString &specification, QObject *parent) + : QObject(parent) +{ + Q_UNUSED(key); + + if (qEnvironmentVariableIsSet("QT_QPA_EVDEV_DEBUG")) + const_cast<QLoggingCategory &>(qLcEvdevTablet()).setEnabled(QtDebugMsg, true); + + QString spec = QString::fromLocal8Bit(qgetenv("QT_QPA_EVDEV_TABLET_PARAMETERS")); + + if (spec.isEmpty()) + spec = specification; + + QStringList args = spec.split(QLatin1Char(':')); + QStringList devices; + + foreach (const QString &arg, args) { + if (arg.startsWith(QLatin1String("/dev/"))) { + devices.append(arg); + args.removeAll(arg); + } + } + + // build new specification without /dev/ elements + m_spec = args.join(QLatin1Char(':')); + + foreach (const QString &device, devices) + addDevice(device); + + // when no devices specified, use device discovery to scan and monitor + if (devices.isEmpty()) { + qCDebug(qLcEvdevTablet) << "evdevtablet: Using device discovery"; + m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Tablet, this); + if (m_deviceDiscovery) { + QStringList devices = m_deviceDiscovery->scanConnectedDevices(); + foreach (const QString &device, devices) + addDevice(device); + connect(m_deviceDiscovery, SIGNAL(deviceDetected(QString)), this, SLOT(addDevice(QString))); + connect(m_deviceDiscovery, SIGNAL(deviceRemoved(QString)), this, SLOT(removeDevice(QString))); + } + } +} + +QEvdevTabletManager::~QEvdevTabletManager() +{ + qDeleteAll(m_activeDevices); +} + +void QEvdevTabletManager::addDevice(const QString &deviceNode) +{ + qCDebug(qLcEvdevTablet) << "Adding device at" << deviceNode; + QEvdevTabletHandlerThread *handler; + handler = new QEvdevTabletHandlerThread(deviceNode, m_spec); + if (handler) { + m_activeDevices.insert(deviceNode, handler); + QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( + QInputDeviceManager::DeviceTypeTablet, m_activeDevices.count()); + } else { + qWarning("evdevtablet: Failed to open tablet device %s", qPrintable(deviceNode)); + } +} + +void QEvdevTabletManager::removeDevice(const QString &deviceNode) +{ + if (m_activeDevices.contains(deviceNode)) { + qCDebug(qLcEvdevTablet) << "Removing device at" << deviceNode; + QEvdevTabletHandlerThread *handler = m_activeDevices.value(deviceNode); + m_activeDevices.remove(deviceNode); + QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( + QInputDeviceManager::DeviceTypeTablet, m_activeDevices.count()); + delete handler; + } +} + +QT_END_NAMESPACE diff --git a/src/widgets/styles/qgtkglobal_p.h b/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h index 7d729d7fc0..893ff03fa7 100644 --- a/src/widgets/styles/qgtkglobal_p.h +++ b/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h @@ -3,7 +3,7 @@ ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** -** This file is part of the QtWidgets module of the Qt Toolkit. +** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage @@ -31,8 +31,8 @@ ** ****************************************************************************/ -#ifndef QGTKGLOBAL_P_H -#define QGTKGLOBAL_P_H +#ifndef QEVDEVTABLETMANAGER_P_H +#define QEVDEVTABLETMANAGER_P_H // // W A R N I N G @@ -45,38 +45,32 @@ // We mean it. // -#include <QtCore/qglobal.h> -#if !defined(QT_NO_STYLE_GTK) +#include <QObject> +#include <QHash> +#include <QSocketNotifier> -#undef signals // Collides with GTK symbols -#include <gtk/gtk.h> - -typedef unsigned long XID; +QT_BEGIN_NAMESPACE -#undef GTK_OBJECT_FLAGS -#define GTK_OBJECT_FLAGS(obj)(((GtkObject*)(obj))->flags) +class QDeviceDiscovery; +class QEvdevTabletHandlerThread; -#define QLS(x) QLatin1String(x) +class QEvdevTabletManager : public QObject +{ + Q_OBJECT +public: + QEvdevTabletManager(const QString &key, const QString &spec, QObject *parent = 0); + ~QEvdevTabletManager(); -QT_BEGIN_NAMESPACE +private slots: + void addDevice(const QString &deviceNode); + void removeDevice(const QString &deviceNode); -#if Q_BYTE_ORDER == Q_BIG_ENDIAN -# define QT_RED 3 -# define QT_GREEN 2 -# define QT_BLUE 1 -# define QT_ALPHA 0 -#else -# define QT_RED 0 -# define QT_GREEN 1 -# define QT_BLUE 2 -# define QT_ALPHA 3 -#endif -# define GTK_RED 2 -# define GTK_GREEN 1 -# define GTK_BLUE 0 -# define GTK_ALPHA 3 +private: + QString m_spec; + QDeviceDiscovery *m_deviceDiscovery; + QHash<QString, QEvdevTabletHandlerThread *> m_activeDevices; +}; QT_END_NAMESPACE -#endif // !QT_NO_STYLE_GTK -#endif // QGTKGLOBAL_P_H +#endif // QEVDEVTABLETMANAGER_P_H diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp b/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp index ad348cc083..dc40f728e1 100644 --- a/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp +++ b/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp @@ -36,6 +36,7 @@ #include <QHash> #include <QSocketNotifier> #include <QGuiApplication> +#include <QTouchDevice> #include <QLoggingCategory> #include <QtCore/private/qcore_unix_p.h> #include <QtGui/private/qhighdpiscaling_p.h> diff --git a/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp b/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp index c94bfd4f4e..60fb7e2cce 100644 --- a/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp +++ b/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp @@ -616,7 +616,7 @@ QVariant QGnomeTheme::themeHint(QPlatformTheme::ThemeHint hint) const return QVariant(QGenericUnixTheme::xdgIconThemePaths()); case QPlatformTheme::StyleNames: { QStringList styleNames; - styleNames << QStringLiteral("GTK+") << QStringLiteral("fusion") << QStringLiteral("windows"); + styleNames << QStringLiteral("fusion") << QStringLiteral("windows"); return QVariant(styleNames); } case QPlatformTheme::KeyboardScheme: @@ -714,9 +714,9 @@ QStringList QGenericUnixTheme::themeNames() result.push_back(QLatin1String(QKdeTheme::name)); #endif } else if (gtkBasedEnvironments.contains(desktopName)) { - // prefer the GTK2 theme implementation with native dialogs etc. - result.push_back(QStringLiteral("gtk2")); - // fallback to the generic Gnome theme if loading the GTK2 theme fails + // prefer the GTK3 theme implementation with native dialogs etc. + result.push_back(QStringLiteral("gtk3")); + // fallback to the generic Gnome theme if loading the GTK3 theme fails result.push_back(QLatin1String(QGnomeTheme::name)); } } diff --git a/src/plugins/generic/evdevtablet/main.cpp b/src/plugins/generic/evdevtablet/main.cpp index 62524e8f33..3a9fd4f1dd 100644 --- a/src/plugins/generic/evdevtablet/main.cpp +++ b/src/plugins/generic/evdevtablet/main.cpp @@ -32,7 +32,7 @@ ****************************************************************************/ #include <QtGui/qgenericplugin.h> -#include <QtPlatformSupport/private/qevdevtablet_p.h> +#include <QtPlatformSupport/private/qevdevtabletmanager_p.h> QT_BEGIN_NAMESPACE @@ -44,7 +44,6 @@ class QEvdevTabletPlugin : public QGenericPlugin public: QEvdevTabletPlugin(); - QStringList keys() const; QObject* create(const QString &key, const QString &specification) Q_DECL_OVERRIDE; }; @@ -52,16 +51,11 @@ QEvdevTabletPlugin::QEvdevTabletPlugin() { } -QStringList QEvdevTabletPlugin::keys() const -{ - return QStringList() << "EvdevTablet"; -} - -QObject *QEvdevTabletPlugin::create(const QString &key, +QObject* QEvdevTabletPlugin::create(const QString &key, const QString &spec) { if (!key.compare(QLatin1String("EvdevTablet"), Qt::CaseInsensitive)) - return new QEvdevTabletHandlerThread(spec); + return new QEvdevTabletManager(key, spec); return 0; } diff --git a/src/plugins/generic/tuiotouch/qtuiohandler.cpp b/src/plugins/generic/tuiotouch/qtuiohandler.cpp index 2b42889cb1..2598bc5caf 100644 --- a/src/plugins/generic/tuiotouch/qtuiohandler.cpp +++ b/src/plugins/generic/tuiotouch/qtuiohandler.cpp @@ -36,6 +36,7 @@ #include <QRect> #include <QWindow> #include <QGuiApplication> +#include <QTouchDevice> #include <qpa/qwindowsysteminterface.h> diff --git a/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp b/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp index a952123576..0148d36a6c 100644 --- a/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp +++ b/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp @@ -397,6 +397,7 @@ void QIBusPlatformInputContext::filterEventFinished(QDBusPendingCallWatcher *cal bool retval = reply.value(); qCDebug(qtQpaInputMethods) << "filterEventFinished return" << code << sym << state << retval; if (!retval) { +#ifndef QT_NO_CONTEXTMENU if (type == QEvent::KeyPress && qtcode == Qt::Key_Menu && window != NULL) { const QPoint globalPos = window->screen()->handle()->cursor()->pos(); @@ -404,6 +405,7 @@ void QIBusPlatformInputContext::filterEventFinished(QDBusPendingCallWatcher *cal QWindowSystemInterface::handleContextMenuEvent(window, false, pos, globalPos, modifiers); } +#endif // QT_NO_CONTEXTMENU QWindowSystemInterface::handleExtendedKeyEvent(window, time, type, qtcode, modifiers, code, sym, state, string, isAutoRepeat); diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro index ba0e6b001a..fec1da334f 100644 --- a/src/plugins/platforms/cocoa/cocoa.pro +++ b/src/plugins/platforms/cocoa/cocoa.pro @@ -84,7 +84,7 @@ contains(QT_CONFIG, opengl.*) { RESOURCES += qcocoaresources.qrc -LIBS += -framework Cocoa -framework Carbon -framework IOKit -lcups +LIBS += -framework AppKit -framework Carbon -framework IOKit -lcups QT += core-private gui-private platformsupport-private diff --git a/src/plugins/platforms/cocoa/main.mm b/src/plugins/platforms/cocoa/main.mm index 43ff715161..4ec2b4ffc4 100644 --- a/src/plugins/platforms/cocoa/main.mm +++ b/src/plugins/platforms/cocoa/main.mm @@ -31,7 +31,7 @@ ** ****************************************************************************/ -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> #include <qpa/qplatformintegrationplugin.h> #include <qpa/qplatformthemeplugin.h> diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.h b/src/plugins/platforms/cocoa/qcocoaaccessibility.h index 228643ab26..2d1aa41a9a 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibility.h +++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.h @@ -33,7 +33,7 @@ #ifndef QCOCOAACCESIBILITY_H #define QCOCOAACCESIBILITY_H -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> #include <QtGui> #include <qpa/qplatformaccessibility.h> diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm index 4e901ba015..f83d15f48e 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm @@ -334,6 +334,7 @@ bool hasValueAttribute(QAccessibleInterface *interface) Q_ASSERT(interface); const QAccessible::Role qtrole = interface->role(); if (qtrole == QAccessible::EditableText + || qtrole == QAccessible::StaticText || interface->valueInterface() || interface->state().checkable) { return true; @@ -345,6 +346,9 @@ bool hasValueAttribute(QAccessibleInterface *interface) id getValueAttribute(QAccessibleInterface *interface) { const QAccessible::Role qtrole = interface->role(); + if (qtrole == QAccessible::StaticText) { + return QCFString::toNSString(interface->text(QAccessible::Name)); + } if (qtrole == QAccessible::EditableText) { if (QAccessibleTextInterface *textInterface = interface->textInterface()) { // VoiceOver will read out the entire text string at once when returning diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm index b55393c7dc..5b2ee1c284 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm @@ -292,6 +292,8 @@ static void convertLineOffset(QAccessibleTextInterface *text, int &line, int &of QSize qtSize = iface->rect().size(); return [NSValue valueWithSize: NSMakeSize(qtSize.width(), qtSize.height())]; } else if ([attribute isEqualToString:NSAccessibilityTitleAttribute]) { + if (iface->role() == QAccessible::StaticText) + return nil; return QCFString::toNSString(iface->text(QAccessible::Name)); } else if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) { return QCFString::toNSString(iface->text(QAccessible::Description)); diff --git a/src/plugins/platforms/cocoa/qcocoaapplication.mm b/src/plugins/platforms/cocoa/qcocoaapplication.mm index d9919f1120..7d0018a595 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplication.mm +++ b/src/plugins/platforms/cocoa/qcocoaapplication.mm @@ -181,7 +181,7 @@ QT_BEGIN_NAMESPACE void qt_redirectNSApplicationSendEvent() { - if (QCoreApplication::testAttribute(Qt::AA_MacPluginApplication)) + if (QCoreApplication::testAttribute(Qt::AA_PluginApplication)) // In a plugin we cannot chain sendEvent hooks: a second plugin could // store the implementation of the first, which during the program flow // can be unloaded. @@ -207,7 +207,7 @@ void qt_redirectNSApplicationSendEvent() void qt_resetNSApplicationSendEvent() { - if (QCoreApplication::testAttribute(Qt::AA_MacPluginApplication)) + if (QCoreApplication::testAttribute(Qt::AA_PluginApplication)) return; diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h index abaaba91a5..a0eb8cc6fd 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h @@ -79,7 +79,7 @@ // -#import <Cocoa/Cocoa.h> +#import <AppKit/AppKit.h> #include <qglobal.h> #include <private/qcore_mac_p.h> diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h index 5a199de4a5..839b536863 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.h +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h @@ -34,7 +34,7 @@ #ifndef QBACKINGSTORE_COCOA_H #define QBACKINGSTORE_COCOA_H -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> #include "qcocoawindow.h" #include "qnsview.h" diff --git a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm index 8843e009a2..ddc2938855 100644 --- a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm @@ -31,13 +31,13 @@ ** ****************************************************************************/ -#include "qcocoacolordialoghelper.h" - #ifndef QT_NO_COLORDIALOG #include <QtCore/qdebug.h> #include <QtCore/qtimer.h> +#include <qpa/qplatformtheme.h> +#include "qcocoacolordialoghelper.h" #include "qcocoahelpers.h" #import <AppKit/AppKit.h> @@ -52,7 +52,7 @@ static NSButton *macCreateButton(const char *text, NSView *superview) [button setButtonType:NSMomentaryLightButton]; [button setBezelStyle:NSRoundedBezelStyle]; [button setTitle:(NSString*)(CFStringRef)QCFString( - qt_mac_removeMnemonics(QCoreApplication::translate("QDialogButtonBox", text)))]; + QPlatformTheme::removeMnemonics(QCoreApplication::translate("QDialogButtonBox", text)))]; [[button cell] setFont:[NSFont systemFontOfSize: [NSFont systemFontSizeForControlSize:NSRegularControlSize]]]; [superview addSubview:button]; diff --git a/src/plugins/platforms/cocoa/qcocoacursor.h b/src/plugins/platforms/cocoa/qcocoacursor.h index d104939f0c..58be5414f1 100644 --- a/src/plugins/platforms/cocoa/qcocoacursor.h +++ b/src/plugins/platforms/cocoa/qcocoacursor.h @@ -34,7 +34,7 @@ #ifndef QWINDOWSCURSOR_H #define QWINDOWSCURSOR_H -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> #include <QtCore> #include <qpa/qplatformcursor.h> diff --git a/src/plugins/platforms/cocoa/qcocoadrag.h b/src/plugins/platforms/cocoa/qcocoadrag.h index c1eeb34679..51de5dd5ed 100644 --- a/src/plugins/platforms/cocoa/qcocoadrag.h +++ b/src/plugins/platforms/cocoa/qcocoadrag.h @@ -34,7 +34,7 @@ #ifndef QCOCOADRAG_H #define QCOCOADRAG_H -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> #include <QtGui> #include <qpa/qplatformdrag.h> #include <private/qsimpledrag_p.h> diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm index 1865624d57..117eba1b2a 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm @@ -80,7 +80,7 @@ #include <qdebug.h> #undef slots -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> #include <Carbon/Carbon.h> QT_BEGIN_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm index 9dc013ba4d..0bfb105a5d 100644 --- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm @@ -31,6 +31,8 @@ ** ****************************************************************************/ +#include <qpa/qplatformtheme.h> + #include "qcocoafiledialoghelper.h" #ifndef QT_NO_FILEDIALOG @@ -180,7 +182,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSOpenSavePanelDelegate); static QString strippedText(QString s) { s.remove( QString::fromLatin1("...") ); - return qt_mac_removeMnemonics(s).trimmed(); + return QPlatformTheme::removeMnemonics(s).trimmed(); } - (NSString *)strip:(const QString &)label diff --git a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm index d1802fe4f9..c1c871f02b 100644 --- a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm @@ -31,17 +31,17 @@ ** ****************************************************************************/ -#include "qcocoafontdialoghelper.h" - #ifndef QT_NO_FONTDIALOG #include <QtCore/qtimer.h> #include <QtGui/qfontdatabase.h> +#include <qpa/qplatformtheme.h> #include <private/qfont_p.h> #include <private/qfontengine_p.h> #include <private/qfontengine_coretext_p.h> +#include "qcocoafontdialoghelper.h" #include "qcocoahelpers.h" #import <AppKit/AppKit.h> @@ -72,7 +72,7 @@ static NSButton *macCreateButton(const char *text, NSView *superview) [button setButtonType:NSMomentaryLightButton]; [button setBezelStyle:NSRoundedBezelStyle]; [button setTitle:(NSString*)(CFStringRef)QCFString( - qt_mac_removeMnemonics(QCoreApplication::translate("QDialogButtonBox", text)))]; + QPlatformTheme::removeMnemonics(QCoreApplication::translate("QDialogButtonBox", text)))]; [[button cell] setFont:[NSFont systemFontOfSize: [NSFont systemFontSizeForControlSize:NSRegularControlSize]]]; [superview addSubview:button]; diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.h b/src/plugins/platforms/cocoa/qcocoaglcontext.h index fa6db018a7..cac53b14f9 100644 --- a/src/plugins/platforms/cocoa/qcocoaglcontext.h +++ b/src/plugins/platforms/cocoa/qcocoaglcontext.h @@ -40,7 +40,7 @@ #include <QtGui/QWindow> #undef slots -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> QT_BEGIN_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm index 0f9b8b900d..d43c8e5ee9 100644 --- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm +++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm @@ -39,7 +39,7 @@ #include <QtPlatformSupport/private/cglconvenience_p.h> #include <QtPlatformHeaders/qcocoanativecontext.h> -#import <Cocoa/Cocoa.h> +#import <AppKit/AppKit.h> QT_BEGIN_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h index b86a17ca12..a75316acba 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.h +++ b/src/plugins/platforms/cocoa/qcocoahelpers.h @@ -90,7 +90,6 @@ Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions); // Misc void qt_mac_transformProccessToForegroundApplication(); -QString qt_mac_removeMnemonics(const QString &original); CGColorSpaceRef qt_mac_genericColorSpace(); CGColorSpaceRef qt_mac_displayColorSpace(const QWidget *widget); CGColorSpaceRef qt_mac_colorSpaceForDeviceType(const QPaintDevice *paintDevice); diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index f51c21ee9b..61ececc93a 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -31,6 +31,8 @@ ** ****************************************************************************/ +#include <qpa/qplatformtheme.h> + #include "qcocoahelpers.h" @@ -485,42 +487,6 @@ void qt_mac_transformProccessToForegroundApplication() } } } - -QString qt_mac_removeMnemonics(const QString &original) -{ - QString returnText(original.size(), 0); - int finalDest = 0; - int currPos = 0; - int l = original.length(); - while (l) { - if (original.at(currPos) == QLatin1Char('&') - && (l == 1 || original.at(currPos + 1) != QLatin1Char('&'))) { - ++currPos; - --l; - if (l == 0) - break; - } else if (original.at(currPos) == QLatin1Char('(') && l >= 4 && - original.at(currPos + 1) == QLatin1Char('&') && - original.at(currPos + 2) != QLatin1Char('&') && - original.at(currPos + 3) == QLatin1Char(')')) { - /* remove mnemonics its format is "\s*(&X)" */ - int n = 0; - while (finalDest > n && returnText.at(finalDest - n - 1).isSpace()) - ++n; - finalDest -= n; - currPos += 4; - l -= 4; - continue; - } - returnText[finalDest] = original.at(currPos); - ++currPos; - ++finalDest; - --l; - } - returnText.truncate(finalDest); - return returnText; -} - static CGColorSpaceRef m_genericColorSpace = 0; static QHash<CGDirectDisplayID, CGColorSpaceRef> m_displayColorSpaceHash; static bool m_postRoutineRegistered = false; @@ -774,7 +740,7 @@ bool qt_mac_execute_apple_script(const QString &script, AEDesc *ret) QString qt_mac_removeAmpersandEscapes(QString s) { - return qt_mac_removeMnemonics(s).trimmed(); + return QPlatformTheme::removeMnemonics(s).trimmed(); } /*! \internal diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h index e7e21c356a..c7875af83e 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.h +++ b/src/plugins/platforms/cocoa/qcocoaintegration.h @@ -34,7 +34,7 @@ #ifndef QPLATFORMINTEGRATION_COCOA_H #define QPLATFORMINTEGRATION_COCOA_H -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> #include "qcocoacursor.h" #include "qcocoawindow.h" diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 6bec6b191d..e469ec5c74 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -317,7 +317,7 @@ QCocoaIntegration::QCocoaIntegration(const QStringList ¶mList) // ### For AA_MacPluginApplication we don't want to load the menu nib. // Qt 4 also does not set the application delegate, so that behavior // is matched here. - if (!QCoreApplication::testAttribute(Qt::AA_MacPluginApplication)) { + if (!QCoreApplication::testAttribute(Qt::AA_PluginApplication)) { // Set app delegate, link to the current delegate (if any) QCocoaApplicationDelegate *newDelegate = [QCocoaApplicationDelegate sharedDelegate]; @@ -344,7 +344,7 @@ QCocoaIntegration::~QCocoaIntegration() qt_resetNSApplicationSendEvent(); QMacAutoReleasePool pool; - if (!QCoreApplication::testAttribute(Qt::AA_MacPluginApplication)) { + if (!QCoreApplication::testAttribute(Qt::AA_PluginApplication)) { // remove the apple event handlers installed by QCocoaApplicationDelegate QCocoaApplicationDelegate *delegate = [QCocoaApplicationDelegate sharedDelegate]; [delegate removeAppleEventHandlers]; diff --git a/src/plugins/platforms/cocoa/qcocoakeymapper.h b/src/plugins/platforms/cocoa/qcocoakeymapper.h index 11bbdc5426..9e79d634ab 100644 --- a/src/plugins/platforms/cocoa/qcocoakeymapper.h +++ b/src/plugins/platforms/cocoa/qcocoakeymapper.h @@ -36,7 +36,7 @@ #include <qcocoahelpers.h> -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> #include <Carbon/Carbon.h> #include <QtCore/QList> diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.mm b/src/plugins/platforms/cocoa/qcocoamenubar.mm index 7775cdbde6..af5905f846 100644 --- a/src/plugins/platforms/cocoa/qcocoamenubar.mm +++ b/src/plugins/platforms/cocoa/qcocoamenubar.mm @@ -31,7 +31,7 @@ ** ****************************************************************************/ -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> #include "qcocoamenubar.h" #include "qcocoawindow.h" diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm index 0fc1bff325..76406539c3 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.mm +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm @@ -31,6 +31,8 @@ ** ****************************************************************************/ +#include <qpa/qplatformtheme.h> + #include "qcocoamenuitem.h" #include "qcocoamenu.h" @@ -318,7 +320,7 @@ NSMenuItem *QCocoaMenuItem::sync() if (accel.count() > 1) text += QLatin1String(" (") + accel.toString(QKeySequence::NativeText) + QLatin1String(")"); - QString finalString = qt_mac_removeMnemonics(text); + QString finalString = QPlatformTheme::removeMnemonics(text); bool useAttributedTitle = false; // Cocoa Font and title if (m_font.resolve()) { diff --git a/src/plugins/platforms/cocoa/qcocoamenuloader.h b/src/plugins/platforms/cocoa/qcocoamenuloader.h index bdc943f78a..a3eea8bc15 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuloader.h +++ b/src/plugins/platforms/cocoa/qcocoamenuloader.h @@ -45,7 +45,7 @@ // We mean it. // -#import <Cocoa/Cocoa.h> +#import <AppKit/AppKit.h> #include <QtCore/private/qcore_mac_p.h> @interface QT_MANGLE_NAMESPACE(QCocoaMenuLoader) : NSResponder diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm index 41ea1fa49c..d2d3a7d562 100644 --- a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm +++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm @@ -60,7 +60,7 @@ #include <QtPlatformHeaders/qcocoawindowfunctions.h> -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> QT_BEGIN_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoaservices.mm b/src/plugins/platforms/cocoa/qcocoaservices.mm index 6f3f6c4e1d..75fc32f338 100644 --- a/src/plugins/platforms/cocoa/qcocoaservices.mm +++ b/src/plugins/platforms/cocoa/qcocoaservices.mm @@ -33,8 +33,6 @@ #include "qcocoaservices.h" -#include "qt_mac_p.h" - #include <AppKit/NSWorkspace.h> #include <Foundation/NSURL.h> @@ -47,7 +45,7 @@ bool QCocoaServices::openUrl(const QUrl &url) const QString scheme = url.scheme(); if (scheme.isEmpty()) return openDocument(url); - return [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:QCFString::toNSString(url.toString(QUrl::FullyEncoded))]]; + return [[NSWorkspace sharedWorkspace] openURL:url.toNSURL()]; } bool QCocoaServices::openDocument(const QUrl &url) @@ -55,7 +53,7 @@ bool QCocoaServices::openDocument(const QUrl &url) if (!url.isValid()) return false; - return [[NSWorkspace sharedWorkspace] openFile:QCFString::toNSString(url.toLocalFile())]; + return [[NSWorkspace sharedWorkspace] openFile:url.toLocalFile().toNSString()]; } QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm index 4b73d0af08..42ddce6e23 100644 --- a/src/plugins/platforms/cocoa/qcocoatheme.mm +++ b/src/plugins/platforms/cocoa/qcocoatheme.mm @@ -31,7 +31,7 @@ ** ****************************************************************************/ -#import <Cocoa/Cocoa.h> +#import <AppKit/AppKit.h> #include "qcocoatheme.h" #include "messages.h" diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h index 05e6cf3c9e..e830be212a 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.h +++ b/src/plugins/platforms/cocoa/qcocoawindow.h @@ -34,7 +34,7 @@ #ifndef QCOCOAWINDOW_H #define QCOCOAWINDOW_H -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> #include <qpa/qplatformwindow.h> #include <QRect> diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index a545dbddd5..4c6b1bac9f 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -47,7 +47,7 @@ #include <qpa/qwindowsysteminterface.h> #include <qpa/qplatformscreen.h> -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> #include <Carbon/Carbon.h> #include <QDebug> @@ -685,7 +685,16 @@ void QCocoaWindow::setVisible(bool visible) cocoaEventDispatcherPrivate->beginModalSession(window()); m_hasModalSession = true; } else if ([m_nsWindow canBecomeKeyWindow]) { - [m_nsWindow makeKeyAndOrderFront:nil]; + QCocoaEventDispatcher *cocoaEventDispatcher = qobject_cast<QCocoaEventDispatcher *>(QGuiApplication::instance()->eventDispatcher()); + QCocoaEventDispatcherPrivate *cocoaEventDispatcherPrivate = 0; + if (cocoaEventDispatcher) + cocoaEventDispatcherPrivate = static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(cocoaEventDispatcher)); + + if (!(cocoaEventDispatcherPrivate && cocoaEventDispatcherPrivate->currentModalSession())) + [m_nsWindow makeKeyAndOrderFront:nil]; + else + [m_nsWindow orderFront:nil]; + foreach (QCocoaWindow *childWindow, m_childWindows) childWindow->show(true); } else { @@ -1341,6 +1350,9 @@ void QCocoaWindow::recreateWindow(const QPlatformWindow *parentWindow) } } else { // Child windows have no NSWindow, link the NSViews instead. + if ([m_contentView superview]) + [m_contentView removeFromSuperview]; + [m_parentCocoaWindow->m_contentView addSubview : m_contentView]; QRect rect = windowGeometry(); // Prevent setting a (0,0) window size; causes opengl context diff --git a/src/plugins/platforms/cocoa/qmacclipboard.h b/src/plugins/platforms/cocoa/qmacclipboard.h index 0d1f195f48..8b290866c6 100644 --- a/src/plugins/platforms/cocoa/qmacclipboard.h +++ b/src/plugins/platforms/cocoa/qmacclipboard.h @@ -39,7 +39,7 @@ #undef slots -#import <Cocoa/Cocoa.h> +#import <AppKit/AppKit.h> QT_BEGIN_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qmacdefines_mac.h b/src/plugins/platforms/cocoa/qmacdefines_mac.h index b2773a2b9f..fd540e7c34 100644 --- a/src/plugins/platforms/cocoa/qmacdefines_mac.h +++ b/src/plugins/platforms/cocoa/qmacdefines_mac.h @@ -92,7 +92,7 @@ typedef signed long OSStatus; # define old_slots slots # undef slots # endif -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> # ifdef old_slots # undef slots # define slots diff --git a/src/plugins/platforms/cocoa/qmultitouch_mac_p.h b/src/plugins/platforms/cocoa/qmultitouch_mac_p.h index e4eec41295..0af0505a47 100644 --- a/src/plugins/platforms/cocoa/qmultitouch_mac_p.h +++ b/src/plugins/platforms/cocoa/qmultitouch_mac_p.h @@ -47,7 +47,7 @@ #include <QtCore/qglobal.h> -#import <Cocoa/Cocoa.h> +#import <AppKit/AppKit.h> #include <qpa/qwindowsysteminterface.h> #include <qhash.h> diff --git a/src/plugins/platforms/cocoa/qnsview.h b/src/plugins/platforms/cocoa/qnsview.h index bad67f2516..e754855e7f 100644 --- a/src/plugins/platforms/cocoa/qnsview.h +++ b/src/plugins/platforms/cocoa/qnsview.h @@ -34,7 +34,7 @@ #ifndef QNSVIEW_H #define QNSVIEW_H -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> #include <QtCore/QPointer> #include <QtGui/QImage> diff --git a/src/plugins/platforms/cocoa/qnswindowdelegate.h b/src/plugins/platforms/cocoa/qnswindowdelegate.h index b8d344aa0e..794059bc68 100644 --- a/src/plugins/platforms/cocoa/qnswindowdelegate.h +++ b/src/plugins/platforms/cocoa/qnswindowdelegate.h @@ -34,7 +34,7 @@ #ifndef QNSWINDOWDELEGATE_H #define QNSWINDOWDELEGATE_H -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> #include "qcocoawindow.h" diff --git a/src/plugins/platforms/cocoa/qt_mac_p.h b/src/plugins/platforms/cocoa/qt_mac_p.h index 576e0f9729..c59e0941b2 100644 --- a/src/plugins/platforms/cocoa/qt_mac_p.h +++ b/src/plugins/platforms/cocoa/qt_mac_p.h @@ -48,7 +48,7 @@ #include "qmacdefines_mac.h" #ifdef __OBJC__ -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> #include <objc/runtime.h> #endif @@ -103,8 +103,6 @@ public: } }; -QString qt_mac_removeMnemonics(const QString &original); //implemented in qmacstyle_mac.cpp - class Q_WIDGETS_EXPORT QMacWindowChangeEvent { private: diff --git a/src/plugins/platforms/eglfs/qeglfscursor.cpp b/src/plugins/platforms/eglfs/qeglfscursor.cpp index eea130a754..bfb69d3e8f 100644 --- a/src/plugins/platforms/eglfs/qeglfscursor.cpp +++ b/src/plugins/platforms/eglfs/qeglfscursor.cpp @@ -38,6 +38,7 @@ #include <qpa/qwindowsysteminterface.h> #include <QtGui/QOpenGLContext> #include <QtGui/QOpenGLShaderProgram> +#include <QtCore/QFile> #include <QtCore/QJsonDocument> #include <QtCore/QJsonArray> #include <QtCore/QJsonObject> diff --git a/src/plugins/platforms/ios/ios.pro b/src/plugins/platforms/ios/ios.pro index bf7849b740..236234628d 100644 --- a/src/plugins/platforms/ios/ios.pro +++ b/src/plugins/platforms/ios/ios.pro @@ -30,7 +30,8 @@ OBJECTIVE_SOURCES = \ qiostextresponder.mm \ qiosmenu.mm \ qiosfileengineassetslibrary.mm \ - qiosfiledialog.mm + qiosfiledialog.mm \ + qiosmessagedialog.mm HEADERS = \ qiosintegration.h \ @@ -54,7 +55,8 @@ HEADERS = \ qiosmenu.h \ qiosfileenginefactory.h \ qiosfileengineassetslibrary.h \ - qiosfiledialog.h + qiosfiledialog.h \ + qiosmessagedialog.h OTHER_FILES = \ quiview_textinput.mm \ diff --git a/src/plugins/platforms/ios/qiosmenu.h b/src/plugins/platforms/ios/qiosmenu.h index ec23b55507..3beb58b503 100644 --- a/src/plugins/platforms/ios/qiosmenu.h +++ b/src/plugins/platforms/ios/qiosmenu.h @@ -74,9 +74,6 @@ public: bool m_separator; QIOSMenu *m_menu; QKeySequence m_shortcut; - -private: - QString removeMnemonics(const QString &original); }; typedef QList<QIOSMenuItem *> QIOSMenuItemList; diff --git a/src/plugins/platforms/ios/qiosmenu.mm b/src/plugins/platforms/ios/qiosmenu.mm index 7aea3729fd..f68321fb3b 100644 --- a/src/plugins/platforms/ios/qiosmenu.mm +++ b/src/plugins/platforms/ios/qiosmenu.mm @@ -33,6 +33,7 @@ #include <qglobal.h> #include <qguiapplication.h> +#include <qpa/qplatformtheme.h> #include "qiosglobal.h" #include "qiosmenu.h" @@ -254,7 +255,7 @@ quintptr QIOSMenuItem::tag() const void QIOSMenuItem::setText(const QString &text) { - m_text = removeMnemonics(text); + m_text = QPlatformTheme::removeMnemonics(text); } void QIOSMenuItem::setMenu(QPlatformMenu *menu) @@ -287,41 +288,6 @@ void QIOSMenuItem::setEnabled(bool enabled) m_enabled = enabled; } -QString QIOSMenuItem::removeMnemonics(const QString &original) -{ - // Copied from qcocoahelpers - QString returnText(original.size(), 0); - int finalDest = 0; - int currPos = 0; - int l = original.length(); - while (l) { - if (original.at(currPos) == QLatin1Char('&') - && (l == 1 || original.at(currPos + 1) != QLatin1Char('&'))) { - ++currPos; - --l; - if (l == 0) - break; - } else if (original.at(currPos) == QLatin1Char('(') && l >= 4 && - original.at(currPos + 1) == QLatin1Char('&') && - original.at(currPos + 2) != QLatin1Char('&') && - original.at(currPos + 3) == QLatin1Char(')')) { - /* remove mnemonics its format is "\s*(&X)" */ - int n = 0; - while (finalDest > n && returnText.at(finalDest - n - 1).isSpace()) - ++n; - finalDest -= n; - currPos += 4; - l -= 4; - continue; - } - returnText[finalDest] = original.at(currPos); - ++currPos; - ++finalDest; - --l; - } - returnText.truncate(finalDest); - return returnText; -} QIOSMenu::QIOSMenu() : QPlatformMenu() diff --git a/src/plugins/platforms/ios/qiosmessagedialog.h b/src/plugins/platforms/ios/qiosmessagedialog.h new file mode 100644 index 0000000000..a9bd42324e --- /dev/null +++ b/src/plugins/platforms/ios/qiosmessagedialog.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QIOSMESSAGEDIALOG_H +#define QIOSMESSAGEDIALOG_H + +#include <QtCore/qeventloop.h> +#include <qpa/qplatformdialoghelper.h> + +QT_BEGIN_NAMESPACE + +Q_FORWARD_DECLARE_OBJC_CLASS(UIAlertController); +Q_FORWARD_DECLARE_OBJC_CLASS(UIAlertAction); + +class QIOSMessageDialog : public QPlatformMessageDialogHelper +{ +public: + QIOSMessageDialog(); + ~QIOSMessageDialog(); + + void exec() Q_DECL_OVERRIDE; + bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) Q_DECL_OVERRIDE; + void hide() Q_DECL_OVERRIDE; + +private: + QEventLoop m_eventLoop; + UIAlertController *m_alertController; + QString messageTextPlain(); + UIAlertAction *createAction(StandardButton button); +}; + +QT_END_NAMESPACE + +#endif // QIOSMESSAGEDIALOG_H + diff --git a/src/plugins/platforms/ios/qiosmessagedialog.mm b/src/plugins/platforms/ios/qiosmessagedialog.mm new file mode 100644 index 0000000000..39f7d4b1cd --- /dev/null +++ b/src/plugins/platforms/ios/qiosmessagedialog.mm @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import <UIKit/UIKit.h> + +#include <QtGui/qwindow.h> +#include <QtGui/private/qguiapplication_p.h> +#include <qpa/qplatformtheme.h> + +#include "qiosglobal.h" +#include "quiview.h" +#include "qiosmessagedialog.h" + +QIOSMessageDialog::QIOSMessageDialog() + : m_alertController(Q_NULLPTR) +{ +} + +QIOSMessageDialog::~QIOSMessageDialog() +{ + hide(); +} + +inline QString QIOSMessageDialog::messageTextPlain() +{ + // Concatenate text fragments, and remove HTML tags + const QSharedPointer<QMessageDialogOptions> &opt = options(); + const QString &lineShift = QStringLiteral("\n\n"); + const QString &informativeText = opt->informativeText(); + const QString &detailedText = opt->detailedText(); + + QString text = opt->text(); + if (!informativeText.isEmpty()) + text += lineShift + informativeText; + if (!detailedText.isEmpty()) + text += lineShift + detailedText; + + text.replace(QLatin1String("<p>"), QStringLiteral("\n"), Qt::CaseInsensitive); + text.remove(QRegularExpression(QStringLiteral("<[^>]*>"))); + + return text; +} + +inline UIAlertAction *QIOSMessageDialog::createAction(StandardButton button) +{ + const StandardButton labelButton = button == NoButton ? Ok : button; + const QString &standardLabel = QGuiApplicationPrivate::platformTheme()->standardButtonText(labelButton); + const QString &label = QPlatformTheme::removeMnemonics(standardLabel); + + UIAlertActionStyle style = UIAlertActionStyleDefault; + if (button == Cancel) + style = UIAlertActionStyleCancel; + else if (button == Discard) + style = UIAlertActionStyleDestructive; + + return [UIAlertAction actionWithTitle:label.toNSString() style:style handler:^(UIAlertAction *) { + hide(); + if (button == NoButton) + emit reject(); + else + emit clicked(button, buttonRole(button)); + }]; +} + +void QIOSMessageDialog::exec() +{ + m_eventLoop.exec(QEventLoop::DialogExec); +} + +bool QIOSMessageDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) +{ + Q_UNUSED(windowFlags); + if (m_alertController // Ensure that the dialog is not showing already + || !options() // Some message dialogs don't have options (QErrorMessage) + || windowModality != Qt::ApplicationModal // We can only do app modal dialogs + || QSysInfo::MacintoshVersion < QSysInfo::MV_IOS_8_0) // API limitation + return false; + + m_alertController = [[UIAlertController + alertControllerWithTitle:options()->windowTitle().toNSString() + message:messageTextPlain().toNSString() + preferredStyle:UIAlertControllerStyleAlert] retain]; + + if (StandardButtons buttons = options()->standardButtons()) { + for (int i = FirstButton; i < LastButton; i<<=1) { + if (i & buttons) + [m_alertController addAction:createAction(StandardButton(i))]; + } + } else { + // We need at least one button to allow the user close the dialog + [m_alertController addAction:createAction(NoButton)]; + } + + UIWindow *window = parent ? reinterpret_cast<UIView *>(parent->winId()).window : [UIApplication sharedApplication].keyWindow; + [window.rootViewController presentViewController:m_alertController animated:YES completion:nil]; + return true; +} + +void QIOSMessageDialog::hide() +{ + m_eventLoop.exit(); + [m_alertController dismissViewControllerAnimated:YES completion:nil]; + [m_alertController release]; + m_alertController = Q_NULLPTR; +} diff --git a/src/plugins/platforms/ios/qiostheme.mm b/src/plugins/platforms/ios/qiostheme.mm index bc40069670..dccacb2c0e 100644 --- a/src/plugins/platforms/ios/qiostheme.mm +++ b/src/plugins/platforms/ios/qiostheme.mm @@ -47,6 +47,7 @@ #include "qiosmenu.h" #include "qiosfiledialog.h" +#include "qiosmessagedialog.h" QT_BEGIN_NAMESPACE @@ -85,6 +86,7 @@ bool QIOSTheme::usePlatformNativeDialog(QPlatformTheme::DialogType type) const { switch (type) { case FileDialog: + case MessageDialog: return true; default: return false; @@ -97,6 +99,9 @@ QPlatformDialogHelper *QIOSTheme::createPlatformDialogHelper(QPlatformTheme::Dia case FileDialog: return new QIOSFileDialog(); break; + case MessageDialog: + return new QIOSMessageDialog(); + break; default: return 0; } diff --git a/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp b/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp index 91708c0a47..45f66db75d 100644 --- a/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp +++ b/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp @@ -34,6 +34,7 @@ #include "qlinuxfbscreen.h" #include <QtPlatformSupport/private/qfbcursor_p.h> #include <QtPlatformSupport/private/qfbwindow_p.h> +#include <QtCore/QFile> #include <QtCore/QRegularExpression> #include <QtGui/QPainter> diff --git a/src/plugins/platforms/mirclient/qmirclientglcontext.cpp b/src/plugins/platforms/mirclient/qmirclientglcontext.cpp index bfba5051e5..bbd7f5ee75 100644 --- a/src/plugins/platforms/mirclient/qmirclientglcontext.cpp +++ b/src/plugins/platforms/mirclient/qmirclientglcontext.cpp @@ -39,6 +39,7 @@ #include "qmirclientwindow.h" #include "qmirclientlogging.h" #include <QtPlatformSupport/private/qeglconvenience_p.h> +#include <QtGui/private/qopenglcontext_p.h> #if !defined(QT_NO_DEBUG) static void printOpenGLESConfig() { @@ -103,6 +104,15 @@ bool QMirClientOpenGLContext::makeCurrent(QPlatformSurface* surface) ASSERT(eglMakeCurrent(mEglDisplay, eglSurface, eglSurface, mEglContext) == EGL_TRUE); printOpenGLESConfig(); #endif + + // When running on the emulator, shaders will be compiled using a thin wrapper around the desktop drivers. + // These wrappers might not support the precision qualifiers, so set the workaround flag to true. + const char *rendererString = reinterpret_cast<const char *>(glGetString(GL_RENDERER)); + if (rendererString != 0 && qstrncmp(rendererString, "Android Emulator", 16) == 0) { + QOpenGLContextPrivate *ctx_d = QOpenGLContextPrivate::get(context()); + ctx_d->workaround_missingPrecisionQualifiers = true; + } + return true; } diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp index 870e7fec07..fcd9911a56 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.cpp +++ b/src/plugins/platforms/windows/qwindowsdrag.cpp @@ -239,8 +239,6 @@ private: }; typedef QMap<Qt::DropAction, CursorEntry> ActionCursorMap; - typedef ActionCursorMap::Iterator ActionCursorMapIt; - typedef ActionCursorMap::ConstIterator ActionCursorMapConstIt; const Mode m_mode; QWindowsDrag *m_drag; @@ -318,7 +316,7 @@ void QWindowsOleDropSource::createCursors() if (cursorPixmap.isNull()) cursorPixmap = m_drag->defaultCursor(action); const qint64 cacheKey = cursorPixmap.cacheKey(); - const ActionCursorMapIt it = m_cursors.find(action); + const auto it = m_cursors.find(action); if (it != m_cursors.end() && it.value().cacheKey == cacheKey) continue; if (cursorPixmap.isNull()) { @@ -399,7 +397,7 @@ QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) { HRESULT hr = S_OK; do { - if (fEscapePressed) { + if (fEscapePressed || QWindowsDrag::isCanceled()) { hr = ResultFromScode(DRAGDROP_S_CANCEL); break; } @@ -447,7 +445,7 @@ QWindowsOleDropSource::GiveFeedback(DWORD dwEffect) m_drag->updateAction(action); const qint64 currentCacheKey = m_drag->currentDrag()->dragCursor(action).cacheKey(); - ActionCursorMapConstIt it = m_cursors.constFind(action); + auto it = m_cursors.constFind(action); // If a custom drag cursor is set, check its cache key to detect changes. if (it == m_cursors.constEnd() || (currentCacheKey && currentCacheKey != it.value().cacheKey)) { createCursors(); @@ -679,6 +677,8 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState, \ingroup qt-lighthouse-win */ +bool QWindowsDrag::m_canceled = false; + QWindowsDrag::QWindowsDrag() : m_dropDataObject(0), m_cachedDropTargetHelper(0) { @@ -808,6 +808,7 @@ Qt::DropAction QWindowsDrag::drag(QDrag *drag) Qt::DropAction dragResult = Qt::IgnoreAction; DWORD resultEffect; + QWindowsDrag::m_canceled = false; QWindowsOleDropSource *windowDropSource = new QWindowsOleDropSource(this); windowDropSource->createCursors(); QWindowsOleDataObject *dropDataObject = new QWindowsOleDataObject(dropData); diff --git a/src/plugins/platforms/windows/qwindowsdrag.h b/src/plugins/platforms/windows/qwindowsdrag.h index 9a5e0b17f2..b031faa884 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.h +++ b/src/plugins/platforms/windows/qwindowsdrag.h @@ -87,6 +87,8 @@ public: Qt::DropAction drag(QDrag *drag) Q_DECL_OVERRIDE; static QWindowsDrag *instance(); + void cancelDrag() Q_DECL_OVERRIDE { QWindowsDrag::m_canceled = true; } + static bool isCanceled() { return QWindowsDrag::m_canceled; } IDataObject *dropDataObject() const { return m_dropDataObject; } void setDropDataObject(IDataObject *dataObject) { m_dropDataObject = dataObject; } @@ -98,6 +100,8 @@ public: QPixmap defaultCursor(Qt::DropAction action) const; private: + static bool m_canceled; + QWindowsDropMimeData m_dropData; IDataObject *m_dropDataObject; diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp index c8eaccd956..a30e545807 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp +++ b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp @@ -44,6 +44,7 @@ #include <QtCore/qmath.h> #include <QtCore/QDebug> +#include <QtCore/QFile> #include <QtCore/QtEndian> #include <QtCore/QThreadStorage> diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp index 16cc2afef6..361e7f4445 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp +++ b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp @@ -145,10 +145,8 @@ static FontKeys &fontKeys() static const FontKey *findFontKey(const QString &name, int *indexIn = Q_NULLPTR) { - typedef FontKeys::ConstIterator ConstIt; - const FontKeys &keys = fontKeys(); - for (ConstIt it = keys.constBegin(), cend = keys.constEnd(); it != cend; ++it) { + for (auto it = keys.constBegin(), cend = keys.constEnd(); it != cend; ++it) { const int index = it->fontNames.indexOf(name); if (index >= 0) { if (indexIn) diff --git a/src/plugins/platforms/windows/qwindowsfontengine.cpp b/src/plugins/platforms/windows/qwindowsfontengine.cpp index 3685197430..ca3e2527e9 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.cpp +++ b/src/plugins/platforms/windows/qwindowsfontengine.cpp @@ -54,6 +54,7 @@ #include <QtGui/private/qpaintengine_raster_p.h> #include <QtCore/QtEndian> +#include <QtCore/QFile> #include <QtCore/qmath.h> #include <QtCore/QThreadStorage> #include <QtCore/private/qsystemlibrary_p.h> diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index 797a96cda7..81525775c6 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -61,6 +61,7 @@ #if !defined(Q_OS_WINCE) && !defined(QT_NO_SESSIONMANAGER) # include "qwindowssessionmanager.h" #endif +#include <QtGui/qtouchdevice.h> #include <QtGui/private/qguiapplication_p.h> #include <QtGui/private/qhighdpiscaling_p.h> #include <QtGui/qpa/qplatforminputcontextfactory_p.h> @@ -220,11 +221,13 @@ QWindowsIntegrationPrivate::QWindowsIntegrationPrivate(const QStringList ¶mL if (tabletAbsoluteRange >= 0) m_context.setTabletAbsoluteRange(tabletAbsoluteRange); if (!dpiAwarenessSet) { // Set only once in case of repeated instantiations of QGuiApplication. - m_context.setProcessDpiAwareness(dpiAwareness); + if (!QCoreApplication::testAttribute(Qt::AA_PluginApplication)) { + m_context.setProcessDpiAwareness(dpiAwareness); + qCDebug(lcQpaWindows) + << __FUNCTION__ << "DpiAwareness=" << dpiAwareness; + } dpiAwarenessSet = true; } - qCDebug(lcQpaWindows) - << __FUNCTION__ << "DpiAwareness=" << dpiAwareness; m_context.initTouch(m_options); } diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp index e26010b5c4..80f3d3e2b8 100644 --- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp +++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp @@ -41,6 +41,7 @@ #include <qpa/qwindowsysteminterface.h> #include <QtGui/QGuiApplication> #include <QtGui/QScreen> +#include <QtGui/QTouchDevice> #include <QtGui/QWindow> #include <QtGui/QCursor> diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp index 2e088d3ca5..0bfefc962e 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp +++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp @@ -1511,11 +1511,13 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type, QWindow *window = targetWindow->window(); if (!filtered) { +#ifndef QT_NO_CONTEXTMENU if (type == QEvent::KeyPress && qtcode == Qt::Key_Menu) { const QPoint globalPos = window->screen()->handle()->cursor()->pos(); const QPoint pos = window->mapFromGlobal(globalPos); QWindowSystemInterface::handleContextMenuEvent(window, false, pos, globalPos, modifiers); } +#endif // QT_NO_CONTEXTMENU QWindowSystemInterface::handleExtendedKeyEvent(window, time, type, qtcode, modifiers, code, sym, state, string, isAutoRepeat); } diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index 2a53b18890..55bae05523 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -182,8 +182,6 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDe updateRefreshRate(crtc->mode); free(crtc); } - } else { - updateGeometry(output ? output->timestamp : 0); } if (m_geometry.isEmpty()) { diff --git a/src/plugins/platformthemes/gtk2/gtk2.json b/src/plugins/platformthemes/gtk2/gtk2.json deleted file mode 100644 index 86dd8e58fa..0000000000 --- a/src/plugins/platformthemes/gtk2/gtk2.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Keys": [ "gtk2" ] -} diff --git a/src/plugins/platformthemes/gtk2/gtk2.pro b/src/plugins/platformthemes/gtk2/gtk2.pro deleted file mode 100644 index 73c156f82b..0000000000 --- a/src/plugins/platformthemes/gtk2/gtk2.pro +++ /dev/null @@ -1,21 +0,0 @@ -TARGET = qgtk2 - -PLUGIN_TYPE = platformthemes -PLUGIN_EXTENDS = - -PLUGIN_CLASS_NAME = QGtk2ThemePlugin -load(qt_plugin) - -QT += core-private gui-private platformsupport-private - -CONFIG += X11 -QMAKE_CXXFLAGS += $$QT_CFLAGS_QGTK2 -LIBS += $$QT_LIBS_QGTK2 - -HEADERS += \ - qgtk2dialoghelpers.h \ - qgtk2theme.h - -SOURCES += \ - main.cpp \ - qgtk2dialoghelpers.cpp \ - qgtk2theme.cpp \ diff --git a/src/plugins/platformthemes/gtk3/gtk3.json b/src/plugins/platformthemes/gtk3/gtk3.json new file mode 100644 index 0000000000..3887248353 --- /dev/null +++ b/src/plugins/platformthemes/gtk3/gtk3.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "gtk3" ] +} diff --git a/src/plugins/platformthemes/gtk3/gtk3.pro b/src/plugins/platformthemes/gtk3/gtk3.pro new file mode 100644 index 0000000000..cd19e73ed8 --- /dev/null +++ b/src/plugins/platformthemes/gtk3/gtk3.pro @@ -0,0 +1,21 @@ +TARGET = qgtk3 + +PLUGIN_TYPE = platformthemes +PLUGIN_EXTENDS = - +PLUGIN_CLASS_NAME = QGtk3ThemePlugin +load(qt_plugin) + +QT += core-private gui-private platformsupport-private + +CONFIG += X11 +QMAKE_CXXFLAGS += $$QT_CFLAGS_QGTK3 +LIBS += $$QT_LIBS_QGTK3 + +HEADERS += \ + qgtk3dialoghelpers.h \ + qgtk3theme.h + +SOURCES += \ + main.cpp \ + qgtk3dialoghelpers.cpp \ + qgtk3theme.cpp diff --git a/src/plugins/platformthemes/gtk2/main.cpp b/src/plugins/platformthemes/gtk3/main.cpp index 34ac3ffc07..c3e81b18e3 100644 --- a/src/plugins/platformthemes/gtk2/main.cpp +++ b/src/plugins/platformthemes/gtk3/main.cpp @@ -32,24 +32,24 @@ ****************************************************************************/ #include <qpa/qplatformthemeplugin.h> -#include "qgtk2theme.h" +#include "qgtk3theme.h" QT_BEGIN_NAMESPACE -class QGtk2ThemePlugin : public QPlatformThemePlugin +class QGtk3ThemePlugin : public QPlatformThemePlugin { Q_OBJECT - Q_PLUGIN_METADATA(IID QPlatformThemeFactoryInterface_iid FILE "gtk2.json") + Q_PLUGIN_METADATA(IID QPlatformThemeFactoryInterface_iid FILE "gtk3.json") public: QPlatformTheme *create(const QString &key, const QStringList ¶ms) Q_DECL_OVERRIDE; }; -QPlatformTheme *QGtk2ThemePlugin::create(const QString &key, const QStringList ¶ms) +QPlatformTheme *QGtk3ThemePlugin::create(const QString &key, const QStringList ¶ms) { Q_UNUSED(params); - if (!key.compare(QLatin1String(QGtk2Theme::name), Qt::CaseInsensitive)) - return new QGtk2Theme; + if (!key.compare(QLatin1String(QGtk3Theme::name), Qt::CaseInsensitive)) + return new QGtk3Theme; return 0; } diff --git a/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.cpp b/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.cpp index 2f0bceafe6..2a3585464f 100644 --- a/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.cpp +++ b/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.cpp @@ -31,7 +31,7 @@ ** ****************************************************************************/ -#include "qgtk2dialoghelpers.h" +#include "qgtk3dialoghelpers.h" #include <qeventloop.h> #include <qwindow.h> @@ -50,13 +50,13 @@ QT_BEGIN_NAMESPACE -class QGtk2Dialog : public QWindow +class QGtk3Dialog : public QWindow { Q_OBJECT public: - QGtk2Dialog(GtkWidget *gtkWidget); - ~QGtk2Dialog(); + QGtk3Dialog(GtkWidget *gtkWidget); + ~QGtk3Dialog(); GtkDialog *gtkDialog() const; @@ -69,30 +69,30 @@ Q_SIGNALS: void reject(); protected: - static void onResponse(QGtk2Dialog *dialog, int response); + static void onResponse(QGtk3Dialog *dialog, int response); private: GtkWidget *gtkWidget; }; -QGtk2Dialog::QGtk2Dialog(GtkWidget *gtkWidget) : gtkWidget(gtkWidget) +QGtk3Dialog::QGtk3Dialog(GtkWidget *gtkWidget) : gtkWidget(gtkWidget) { g_signal_connect_swapped(G_OBJECT(gtkWidget), "response", G_CALLBACK(onResponse), this); g_signal_connect(G_OBJECT(gtkWidget), "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL); } -QGtk2Dialog::~QGtk2Dialog() +QGtk3Dialog::~QGtk3Dialog() { gtk_clipboard_store(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)); gtk_widget_destroy(gtkWidget); } -GtkDialog *QGtk2Dialog::gtkDialog() const +GtkDialog *QGtk3Dialog::gtkDialog() const { return GTK_DIALOG(gtkWidget); } -void QGtk2Dialog::exec() +void QGtk3Dialog::exec() { if (modality() == Qt::ApplicationModal) { // block input to the whole app, including other GTK dialogs @@ -106,7 +106,7 @@ void QGtk2Dialog::exec() } } -bool QGtk2Dialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) +bool QGtk3Dialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) { setParent(parent); setFlags(flags); @@ -114,29 +114,31 @@ bool QGtk2Dialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWind gtk_widget_realize(gtkWidget); // creates X window + GdkWindow *gdkWindow = gtk_widget_get_window(gtkWidget); if (parent) { - XSetTransientForHint(gdk_x11_drawable_get_xdisplay(gtkWidget->window), - gdk_x11_drawable_get_xid(gtkWidget->window), + GdkDisplay *gdkDisplay = gdk_window_get_display(gdkWindow); + XSetTransientForHint(gdk_x11_display_get_xdisplay(gdkDisplay), + gdk_x11_window_get_xid(gdkWindow), parent->winId()); } if (modality != Qt::NonModal) { - gdk_window_set_modal_hint(gtkWidget->window, true); + gdk_window_set_modal_hint(gdkWindow, true); QGuiApplicationPrivate::showModalWindow(this); } gtk_widget_show(gtkWidget); - gdk_window_focus(gtkWidget->window, 0); + gdk_window_focus(gdkWindow, GDK_CURRENT_TIME); return true; } -void QGtk2Dialog::hide() +void QGtk3Dialog::hide() { QGuiApplicationPrivate::hideModalWindow(this); gtk_widget_hide(gtkWidget); } -void QGtk2Dialog::onResponse(QGtk2Dialog *dialog, int response) +void QGtk3Dialog::onResponse(QGtk3Dialog *dialog, int response) { if (response == GTK_RESPONSE_OK) emit dialog->accept(); @@ -144,95 +146,78 @@ void QGtk2Dialog::onResponse(QGtk2Dialog *dialog, int response) emit dialog->reject(); } -QGtk2ColorDialogHelper::QGtk2ColorDialogHelper() +QGtk3ColorDialogHelper::QGtk3ColorDialogHelper() { - d.reset(new QGtk2Dialog(gtk_color_selection_dialog_new(""))); + d.reset(new QGtk3Dialog(gtk_color_chooser_dialog_new("", 0))); connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted())); connect(d.data(), SIGNAL(reject()), this, SIGNAL(reject())); - GtkWidget *gtkColorSelection = gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(d->gtkDialog())); - g_signal_connect_swapped(gtkColorSelection, "color-changed", G_CALLBACK(onColorChanged), this); + g_signal_connect_swapped(d->gtkDialog(), "color-activated", G_CALLBACK(onColorChanged), this); } -QGtk2ColorDialogHelper::~QGtk2ColorDialogHelper() +QGtk3ColorDialogHelper::~QGtk3ColorDialogHelper() { } -bool QGtk2ColorDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) +bool QGtk3ColorDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) { applyOptions(); return d->show(flags, modality, parent); } -void QGtk2ColorDialogHelper::exec() +void QGtk3ColorDialogHelper::exec() { d->exec(); } -void QGtk2ColorDialogHelper::hide() +void QGtk3ColorDialogHelper::hide() { d->hide(); } -void QGtk2ColorDialogHelper::setCurrentColor(const QColor &color) +void QGtk3ColorDialogHelper::setCurrentColor(const QColor &color) { GtkDialog *gtkDialog = d->gtkDialog(); - GtkWidget *gtkColorSelection = gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(gtkDialog)); - GdkColor gdkColor; - gdkColor.red = color.red() << 8; - gdkColor.green = color.green() << 8; - gdkColor.blue = color.blue() << 8; - gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(gtkColorSelection), &gdkColor); - if (color.alpha() < 255) { - gtk_color_selection_set_has_opacity_control(GTK_COLOR_SELECTION(gtkColorSelection), true); - gtk_color_selection_set_current_alpha(GTK_COLOR_SELECTION(gtkColorSelection), color.alpha() << 8); - } + if (color.alpha() < 255) + gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(gtkDialog), true); + GdkRGBA gdkColor; + gdkColor.red = color.redF(); + gdkColor.green = color.greenF(); + gdkColor.blue = color.blueF(); + gdkColor.alpha = color.alphaF(); + gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(gtkDialog), &gdkColor); } -QColor QGtk2ColorDialogHelper::currentColor() const +QColor QGtk3ColorDialogHelper::currentColor() const { GtkDialog *gtkDialog = d->gtkDialog(); - GtkWidget *gtkColorSelection = gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(gtkDialog)); - GdkColor gdkColor; - gtk_color_selection_get_current_color(GTK_COLOR_SELECTION(gtkColorSelection), &gdkColor); - guint16 alpha = gtk_color_selection_get_current_alpha(GTK_COLOR_SELECTION(gtkColorSelection)); - return QColor(gdkColor.red >> 8, gdkColor.green >> 8, gdkColor.blue >> 8, alpha >> 8); + GdkRGBA gdkColor; + gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(gtkDialog), &gdkColor); + return QColor::fromRgbF(gdkColor.red, gdkColor.green, gdkColor.blue, gdkColor.alpha); } -void QGtk2ColorDialogHelper::onAccepted() +void QGtk3ColorDialogHelper::onAccepted() { emit accept(); emit colorSelected(currentColor()); } -void QGtk2ColorDialogHelper::onColorChanged(QGtk2ColorDialogHelper *dialog) +void QGtk3ColorDialogHelper::onColorChanged(QGtk3ColorDialogHelper *dialog) { emit dialog->currentColorChanged(dialog->currentColor()); } -void QGtk2ColorDialogHelper::applyOptions() +void QGtk3ColorDialogHelper::applyOptions() { GtkDialog *gtkDialog = d->gtkDialog(); gtk_window_set_title(GTK_WINDOW(gtkDialog), options()->windowTitle().toUtf8()); - GtkWidget *gtkColorSelection = gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(gtkDialog)); - gtk_color_selection_set_has_opacity_control(GTK_COLOR_SELECTION(gtkColorSelection), options()->testOption(QColorDialogOptions::ShowAlphaChannel)); - - GtkWidget *okButton = 0; - GtkWidget *cancelButton = 0; - GtkWidget *helpButton = 0; - g_object_get(G_OBJECT(gtkDialog), "ok-button", &okButton, "cancel-button", &cancelButton, "help-button", &helpButton, NULL); - if (okButton) - g_object_set(G_OBJECT(okButton), "visible", !options()->testOption(QColorDialogOptions::NoButtons), NULL); - if (cancelButton) - g_object_set(G_OBJECT(cancelButton), "visible", !options()->testOption(QColorDialogOptions::NoButtons), NULL); - if (helpButton) - gtk_widget_hide(helpButton); + gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(gtkDialog), options()->testOption(QColorDialogOptions::ShowAlphaChannel)); } -QGtk2FileDialogHelper::QGtk2FileDialogHelper() +QGtk3FileDialogHelper::QGtk3FileDialogHelper() { - d.reset(new QGtk2Dialog(gtk_file_chooser_dialog_new("", 0, + d.reset(new QGtk3Dialog(gtk_file_chooser_dialog_new("", 0, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL))); @@ -243,11 +228,11 @@ QGtk2FileDialogHelper::QGtk2FileDialogHelper() g_signal_connect_swapped(GTK_FILE_CHOOSER(d->gtkDialog()), "current-folder-changed", G_CALLBACK(onCurrentFolderChanged), this); } -QGtk2FileDialogHelper::~QGtk2FileDialogHelper() +QGtk3FileDialogHelper::~QGtk3FileDialogHelper() { } -bool QGtk2FileDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) +bool QGtk3FileDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) { _dir.clear(); _selection.clear(); @@ -256,12 +241,12 @@ bool QGtk2FileDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modal return d->show(flags, modality, parent); } -void QGtk2FileDialogHelper::exec() +void QGtk3FileDialogHelper::exec() { d->exec(); } -void QGtk2FileDialogHelper::hide() +void QGtk3FileDialogHelper::hide() { // After GtkFileChooserDialog has been hidden, gtk_file_chooser_get_current_folder() // & gtk_file_chooser_get_filenames() will return bogus values -> cache the actual @@ -272,18 +257,18 @@ void QGtk2FileDialogHelper::hide() d->hide(); } -bool QGtk2FileDialogHelper::defaultNameFilterDisables() const +bool QGtk3FileDialogHelper::defaultNameFilterDisables() const { return false; } -void QGtk2FileDialogHelper::setDirectory(const QUrl &directory) +void QGtk3FileDialogHelper::setDirectory(const QUrl &directory) { GtkDialog *gtkDialog = d->gtkDialog(); gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gtkDialog), directory.toLocalFile().toUtf8()); } -QUrl QGtk2FileDialogHelper::directory() const +QUrl QGtk3FileDialogHelper::directory() const { // While GtkFileChooserDialog is hidden, gtk_file_chooser_get_current_folder() // returns a bogus value -> return the cached value before hiding @@ -300,7 +285,7 @@ QUrl QGtk2FileDialogHelper::directory() const return QUrl::fromLocalFile(ret); } -void QGtk2FileDialogHelper::selectFile(const QUrl &filename) +void QGtk3FileDialogHelper::selectFile(const QUrl &filename) { GtkDialog *gtkDialog = d->gtkDialog(); if (options()->acceptMode() == QFileDialogOptions::AcceptSave) { @@ -312,7 +297,7 @@ void QGtk2FileDialogHelper::selectFile(const QUrl &filename) } } -QList<QUrl> QGtk2FileDialogHelper::selectedFiles() const +QList<QUrl> QGtk3FileDialogHelper::selectedFiles() const { // While GtkFileChooserDialog is hidden, gtk_file_chooser_get_filenames() // returns a bogus value -> return the cached value before hiding @@ -328,12 +313,12 @@ QList<QUrl> QGtk2FileDialogHelper::selectedFiles() const return selection; } -void QGtk2FileDialogHelper::setFilter() +void QGtk3FileDialogHelper::setFilter() { applyOptions(); } -void QGtk2FileDialogHelper::selectNameFilter(const QString &filter) +void QGtk3FileDialogHelper::selectNameFilter(const QString &filter) { GtkFileFilter *gtkFilter = _filters.value(filter); if (gtkFilter) { @@ -342,14 +327,14 @@ void QGtk2FileDialogHelper::selectNameFilter(const QString &filter) } } -QString QGtk2FileDialogHelper::selectedNameFilter() const +QString QGtk3FileDialogHelper::selectedNameFilter() const { GtkDialog *gtkDialog = d->gtkDialog(); GtkFileFilter *gtkFilter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(gtkDialog)); return _filterNames.value(gtkFilter); } -void QGtk2FileDialogHelper::onAccepted() +void QGtk3FileDialogHelper::onAccepted() { emit accept(); @@ -363,7 +348,7 @@ void QGtk2FileDialogHelper::onAccepted() emit fileSelected(files.first()); } -void QGtk2FileDialogHelper::onSelectionChanged(GtkDialog *gtkDialog, QGtk2FileDialogHelper *helper) +void QGtk3FileDialogHelper::onSelectionChanged(GtkDialog *gtkDialog, QGtk3FileDialogHelper *helper) { QString selection; gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(gtkDialog)); @@ -374,7 +359,7 @@ void QGtk2FileDialogHelper::onSelectionChanged(GtkDialog *gtkDialog, QGtk2FileDi emit helper->currentChanged(QUrl::fromLocalFile(selection)); } -void QGtk2FileDialogHelper::onCurrentFolderChanged(QGtk2FileDialogHelper *dialog) +void QGtk3FileDialogHelper::onCurrentFolderChanged(QGtk3FileDialogHelper *dialog) { emit dialog->directoryEntered(dialog->directory()); } @@ -399,7 +384,7 @@ static GtkFileChooserAction gtkFileChooserAction(const QSharedPointer<QFileDialo } } -void QGtk2FileDialogHelper::applyOptions() +void QGtk3FileDialogHelper::applyOptions() { GtkDialog *gtkDialog = d->gtkDialog(); const QSharedPointer<QFileDialogOptions> &opts = options(); @@ -430,7 +415,6 @@ void QGtk2FileDialogHelper::applyOptions() if (!initialNameFilter.isEmpty()) selectNameFilter(initialNameFilter); -#if GTK_CHECK_VERSION(2, 20, 0) GtkWidget *acceptButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_OK); if (acceptButton) { if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept)) @@ -448,10 +432,9 @@ void QGtk2FileDialogHelper::applyOptions() else gtk_button_set_label(GTK_BUTTON(rejectButton), GTK_STOCK_CANCEL); } -#endif } -void QGtk2FileDialogHelper::setNameFilters(const QStringList &filters) +void QGtk3FileDialogHelper::setNameFilters(const QStringList &filters) { GtkDialog *gtkDialog = d->gtkDialog(); foreach (GtkFileFilter *filter, _filters) @@ -476,29 +459,29 @@ void QGtk2FileDialogHelper::setNameFilters(const QStringList &filters) } } -QGtk2FontDialogHelper::QGtk2FontDialogHelper() +QGtk3FontDialogHelper::QGtk3FontDialogHelper() { - d.reset(new QGtk2Dialog(gtk_font_selection_dialog_new(""))); + d.reset(new QGtk3Dialog(gtk_font_chooser_dialog_new("", 0))); connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted())); connect(d.data(), SIGNAL(reject()), this, SIGNAL(reject())); } -QGtk2FontDialogHelper::~QGtk2FontDialogHelper() +QGtk3FontDialogHelper::~QGtk3FontDialogHelper() { } -bool QGtk2FontDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) +bool QGtk3FontDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) { applyOptions(); return d->show(flags, modality, parent); } -void QGtk2FontDialogHelper::exec() +void QGtk3FontDialogHelper::exec() { d->exec(); } -void QGtk2FontDialogHelper::hide() +void QGtk3FontDialogHelper::hide() { d->hide(); } @@ -569,43 +552,36 @@ static QFont qt_fontFromString(const QString &name) return font; } -void QGtk2FontDialogHelper::setCurrentFont(const QFont &font) +void QGtk3FontDialogHelper::setCurrentFont(const QFont &font) { - GtkFontSelectionDialog *gtkDialog = GTK_FONT_SELECTION_DIALOG(d->gtkDialog()); - gtk_font_selection_dialog_set_font_name(gtkDialog, qt_fontToString(font).toUtf8()); + GtkFontChooser *gtkDialog = GTK_FONT_CHOOSER(d->gtkDialog()); + gtk_font_chooser_set_font(gtkDialog, qt_fontToString(font).toUtf8()); } -QFont QGtk2FontDialogHelper::currentFont() const +QFont QGtk3FontDialogHelper::currentFont() const { - GtkFontSelectionDialog *gtkDialog = GTK_FONT_SELECTION_DIALOG(d->gtkDialog()); - gchar *name = gtk_font_selection_dialog_get_font_name(gtkDialog); + GtkFontChooser *gtkDialog = GTK_FONT_CHOOSER(d->gtkDialog()); + gchar *name = gtk_font_chooser_get_font(gtkDialog); QFont font = qt_fontFromString(QString::fromUtf8(name)); g_free(name); return font; } -void QGtk2FontDialogHelper::onAccepted() +void QGtk3FontDialogHelper::onAccepted() { emit currentFontChanged(currentFont()); emit accept(); emit fontSelected(currentFont()); } -void QGtk2FontDialogHelper::applyOptions() +void QGtk3FontDialogHelper::applyOptions() { GtkDialog *gtkDialog = d->gtkDialog(); const QSharedPointer<QFontDialogOptions> &opts = options(); gtk_window_set_title(GTK_WINDOW(gtkDialog), opts->windowTitle().toUtf8()); - - GtkWidget *okButton = gtk_font_selection_dialog_get_ok_button(GTK_FONT_SELECTION_DIALOG(gtkDialog)); - GtkWidget *cancelButton = gtk_font_selection_dialog_get_cancel_button(GTK_FONT_SELECTION_DIALOG(gtkDialog)); - if (okButton) - gtk_widget_set_visible(okButton, !options()->testOption(QFontDialogOptions::NoButtons)); - if (cancelButton) - gtk_widget_set_visible(cancelButton, !options()->testOption(QFontDialogOptions::NoButtons)); } QT_END_NAMESPACE -#include "qgtk2dialoghelpers.moc" +#include "qgtk3dialoghelpers.moc" diff --git a/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.h b/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.h index 141056637e..438dee861b 100644 --- a/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.h +++ b/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.h @@ -31,8 +31,8 @@ ** ****************************************************************************/ -#ifndef QGTK2DIALOGHELPERS_P_H -#define QGTK2DIALOGHELPERS_P_H +#ifndef QGTK3DIALOGHELPERS_H +#define QGTK3DIALOGHELPERS_H #include <QtCore/qhash.h> #include <QtCore/qlist.h> @@ -46,16 +46,16 @@ typedef struct _GtkFileFilter GtkFileFilter; QT_BEGIN_NAMESPACE -class QGtk2Dialog; +class QGtk3Dialog; class QColor; -class QGtk2ColorDialogHelper : public QPlatformColorDialogHelper +class QGtk3ColorDialogHelper : public QPlatformColorDialogHelper { Q_OBJECT public: - QGtk2ColorDialogHelper(); - ~QGtk2ColorDialogHelper(); + QGtk3ColorDialogHelper(); + ~QGtk3ColorDialogHelper(); bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) Q_DECL_OVERRIDE; void exec() Q_DECL_OVERRIDE; @@ -68,19 +68,19 @@ private Q_SLOTS: void onAccepted(); private: - static void onColorChanged(QGtk2ColorDialogHelper *helper); + static void onColorChanged(QGtk3ColorDialogHelper *helper); void applyOptions(); - QScopedPointer<QGtk2Dialog> d; + QScopedPointer<QGtk3Dialog> d; }; -class QGtk2FileDialogHelper : public QPlatformFileDialogHelper +class QGtk3FileDialogHelper : public QPlatformFileDialogHelper { Q_OBJECT public: - QGtk2FileDialogHelper(); - ~QGtk2FileDialogHelper(); + QGtk3FileDialogHelper(); + ~QGtk3FileDialogHelper(); bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) Q_DECL_OVERRIDE; void exec() Q_DECL_OVERRIDE; @@ -99,8 +99,8 @@ private Q_SLOTS: void onAccepted(); private: - static void onSelectionChanged(GtkDialog *dialog, QGtk2FileDialogHelper *helper); - static void onCurrentFolderChanged(QGtk2FileDialogHelper *helper); + static void onSelectionChanged(GtkDialog *dialog, QGtk3FileDialogHelper *helper); + static void onCurrentFolderChanged(QGtk3FileDialogHelper *helper); void applyOptions(); void setNameFilters(const QStringList &filters); @@ -108,16 +108,16 @@ private: QList<QUrl> _selection; QHash<QString, GtkFileFilter*> _filters; QHash<GtkFileFilter*, QString> _filterNames; - QScopedPointer<QGtk2Dialog> d; + QScopedPointer<QGtk3Dialog> d; }; -class QGtk2FontDialogHelper : public QPlatformFontDialogHelper +class QGtk3FontDialogHelper : public QPlatformFontDialogHelper { Q_OBJECT public: - QGtk2FontDialogHelper(); - ~QGtk2FontDialogHelper(); + QGtk3FontDialogHelper(); + ~QGtk3FontDialogHelper(); bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) Q_DECL_OVERRIDE; void exec() Q_DECL_OVERRIDE; @@ -132,9 +132,9 @@ private Q_SLOTS: private: void applyOptions(); - QScopedPointer<QGtk2Dialog> d; + QScopedPointer<QGtk3Dialog> d; }; QT_END_NAMESPACE -#endif // QGTK2DIALOGHELPERS_P_H +#endif // QGTK3DIALOGHELPERS_H diff --git a/src/plugins/platformthemes/gtk2/qgtk2theme.cpp b/src/plugins/platformthemes/gtk3/qgtk3theme.cpp index a7f08bc047..76afc441a4 100644 --- a/src/plugins/platformthemes/gtk2/qgtk2theme.cpp +++ b/src/plugins/platformthemes/gtk3/qgtk3theme.cpp @@ -31,8 +31,8 @@ ** ****************************************************************************/ -#include "qgtk2theme.h" -#include "qgtk2dialoghelpers.h" +#include "qgtk3theme.h" +#include "qgtk3dialoghelpers.h" #include <QVariant> #undef signals @@ -42,7 +42,7 @@ QT_BEGIN_NAMESPACE -const char *QGtk2Theme::name = "gtk2"; +const char *QGtk3Theme::name = "gtk3"; static QString gtkSetting(const gchar *propertyName) { @@ -54,7 +54,21 @@ static QString gtkSetting(const gchar *propertyName) return str; } -QGtk2Theme::QGtk2Theme() +void gtkMessageHandler(const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer unused_data) { + /* Silence false-positive Gtk warnings (we are using Xlib to set + * the WM_TRANSIENT_FOR hint). + */ + if (g_strcmp0(message, "GtkDialog mapped without a transient parent. " + "This is discouraged.") != 0) { + /* For other messages, call the default handler. */ + g_log_default_handler(log_domain, log_level, message, unused_data); + } +} + +QGtk3Theme::QGtk3Theme() { // gtk_init will reset the Xlib error handler, and that causes // Qt applications to quit on X errors. Therefore, we need to manually restore it. @@ -63,9 +77,18 @@ QGtk2Theme::QGtk2Theme() gtk_init(0, 0); XSetErrorHandler(oldErrorHandler); + + /* Initialize some types here so that Gtk+ does not crash when reading + * the treemodel for GtkFontChooser. + */ + g_type_ensure(PANGO_TYPE_FONT_FAMILY); + g_type_ensure(PANGO_TYPE_FONT_FACE); + + /* Use our custom log handler. */ + g_log_set_handler("Gtk", G_LOG_LEVEL_MESSAGE, gtkMessageHandler, NULL); } -QVariant QGtk2Theme::themeHint(QPlatformTheme::ThemeHint hint) const +QVariant QGtk3Theme::themeHint(QPlatformTheme::ThemeHint hint) const { switch (hint) { case QPlatformTheme::SystemIconThemeName: @@ -77,7 +100,7 @@ QVariant QGtk2Theme::themeHint(QPlatformTheme::ThemeHint hint) const } } -QString QGtk2Theme::gtkFontName() const +QString QGtk3Theme::gtkFontName() const { QString cfgFontName = gtkSetting("gtk-font-name"); if (!cfgFontName.isEmpty()) @@ -85,7 +108,7 @@ QString QGtk2Theme::gtkFontName() const return QGnomeTheme::gtkFontName(); } -bool QGtk2Theme::usePlatformNativeDialog(DialogType type) const +bool QGtk3Theme::usePlatformNativeDialog(DialogType type) const { switch (type) { case ColorDialog: @@ -99,15 +122,15 @@ bool QGtk2Theme::usePlatformNativeDialog(DialogType type) const } } -QPlatformDialogHelper *QGtk2Theme::createPlatformDialogHelper(DialogType type) const +QPlatformDialogHelper *QGtk3Theme::createPlatformDialogHelper(DialogType type) const { switch (type) { case ColorDialog: - return new QGtk2ColorDialogHelper; + return new QGtk3ColorDialogHelper; case FileDialog: - return new QGtk2FileDialogHelper; + return new QGtk3FileDialogHelper; case FontDialog: - return new QGtk2FontDialogHelper; + return new QGtk3FontDialogHelper; default: return 0; } diff --git a/src/plugins/platformthemes/gtk2/qgtk2theme.h b/src/plugins/platformthemes/gtk3/qgtk3theme.h index 8798fed1f7..1597f6e911 100644 --- a/src/plugins/platformthemes/gtk2/qgtk2theme.h +++ b/src/plugins/platformthemes/gtk3/qgtk3theme.h @@ -31,17 +31,17 @@ ** ****************************************************************************/ -#ifndef QGTK2THEME_H -#define QGTK2THEME_H +#ifndef QGTK3THEME_H +#define QGTK3THEME_H #include <private/qgenericunixthemes_p.h> QT_BEGIN_NAMESPACE -class QGtk2Theme : public QGnomeTheme +class QGtk3Theme : public QGnomeTheme { public: - QGtk2Theme(); + QGtk3Theme(); virtual QVariant themeHint(ThemeHint hint) const Q_DECL_OVERRIDE; virtual QString gtkFontName() const Q_DECL_OVERRIDE; @@ -54,4 +54,4 @@ public: QT_END_NAMESPACE -#endif // QGTK2THEME_H +#endif // QGTK3THEME_H diff --git a/src/plugins/platformthemes/platformthemes.pro b/src/plugins/platformthemes/platformthemes.pro index 23dcda2c82..f5f54068a2 100644 --- a/src/plugins/platformthemes/platformthemes.pro +++ b/src/plugins/platformthemes/platformthemes.pro @@ -1,3 +1,3 @@ TEMPLATE = subdirs -contains(QT_CONFIG, gtk2): SUBDIRS += gtk2 +contains(QT_CONFIG, gtk3): SUBDIRS += gtk3 diff --git a/src/plugins/printsupport/cocoa/cocoa.pro b/src/plugins/printsupport/cocoa/cocoa.pro index a3b9e2dfcf..c993b42574 100644 --- a/src/plugins/printsupport/cocoa/cocoa.pro +++ b/src/plugins/printsupport/cocoa/cocoa.pro @@ -5,7 +5,7 @@ PLUGIN_CLASS_NAME = QCocoaPrinterSupportPlugin load(qt_plugin) QT += gui-private printsupport-private -LIBS += -framework Cocoa +LIBS += -framework AppKit SOURCES += main.cpp diff --git a/src/printsupport/dialogs/dialogs.pri b/src/printsupport/dialogs/dialogs.pri index bb07167f68..7b520a05de 100644 --- a/src/printsupport/dialogs/dialogs.pri +++ b/src/printsupport/dialogs/dialogs.pri @@ -8,10 +8,10 @@ HEADERS += \ dialogs/qprintdialog.h \ dialogs/qprintpreviewdialog.h -mac:!ios { +osx { OBJECTIVE_SOURCES += dialogs/qpagesetupdialog_mac.mm \ dialogs/qprintdialog_mac.mm - LIBS_PRIVATE += -framework Cocoa + LIBS_PRIVATE += -framework AppKit } win32 { diff --git a/src/printsupport/dialogs/qpagesetupdialog_mac.mm b/src/printsupport/dialogs/qpagesetupdialog_mac.mm index b86de31883..3147ccb58e 100644 --- a/src/printsupport/dialogs/qpagesetupdialog_mac.mm +++ b/src/printsupport/dialogs/qpagesetupdialog_mac.mm @@ -32,7 +32,7 @@ ****************************************************************************/ -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> #include "qpagesetupdialog.h" diff --git a/src/printsupport/dialogs/qprintdialog_mac.mm b/src/printsupport/dialogs/qprintdialog_mac.mm index 030526954d..e7948c2a37 100644 --- a/src/printsupport/dialogs/qprintdialog_mac.mm +++ b/src/printsupport/dialogs/qprintdialog_mac.mm @@ -31,7 +31,7 @@ ** ****************************************************************************/ -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> #include "qprintdialog.h" #include "qabstractprintdialog_p.h" diff --git a/src/testlib/qplaintestlogger.cpp b/src/testlib/qplaintestlogger.cpp index d820e87ccb..80b93886bb 100644 --- a/src/testlib/qplaintestlogger.cpp +++ b/src/testlib/qplaintestlogger.cpp @@ -348,16 +348,17 @@ void QPlainTestLogger::startLogging() void QPlainTestLogger::stopLogging() { char buf[1024]; + const int timeMs = qRound(QTestLog::msecsTotalTime()); if (QTestLog::verboseLevel() < 0) { - qsnprintf(buf, sizeof(buf), "Totals: %d passed, %d failed, %d skipped, %d blacklisted\n", + qsnprintf(buf, sizeof(buf), "Totals: %d passed, %d failed, %d skipped, %d blacklisted, %dms\n", QTestLog::passCount(), QTestLog::failCount(), - QTestLog::skipCount(), QTestLog::blacklistCount()); + QTestLog::skipCount(), QTestLog::blacklistCount(), timeMs); } else { qsnprintf(buf, sizeof(buf), - "Totals: %d passed, %d failed, %d skipped, %d blacklisted\n" + "Totals: %d passed, %d failed, %d skipped, %d blacklisted, %dms\n" "********* Finished testing of %s *********\n", QTestLog::passCount(), QTestLog::failCount(), - QTestLog::skipCount(), QTestLog::blacklistCount(), + QTestLog::skipCount(), QTestLog::blacklistCount(), timeMs, QTestResult::currentTestObjectName()); } outputMessage(buf); diff --git a/src/testlib/qtestblacklist.cpp b/src/testlib/qtestblacklist.cpp index c2643a2304..fe69e097e7 100644 --- a/src/testlib/qtestblacklist.cpp +++ b/src/testlib/qtestblacklist.cpp @@ -121,6 +121,13 @@ static QSet<QByteArray> keywords() #endif #endif +#ifdef Q_PROCESSOR_X86 + << "x86" +#endif +#ifdef Q_PROCESSOR_ARM + << "arm" +#endif + #ifdef Q_AUTOTEST_EXPORT << "developer-build" #endif diff --git a/src/testlib/qtestcase.h b/src/testlib/qtestcase.h index 7d2fd2e701..9d79439e04 100644 --- a/src/testlib/qtestcase.h +++ b/src/testlib/qtestcase.h @@ -238,7 +238,7 @@ namespace QTest inline typename QtPrivate::QEnableIf<QtPrivate::IsQEnumHelper<T>::Value, char*>::Type toString(T e) { QMetaEnum me = QMetaEnum::fromType<T>(); - return qstrdup(me.key(e)); + return qstrdup(me.key(int(e))); // int cast is necessary to support enum classes } template <typename T> // Fallback diff --git a/src/tools/moc/preprocessor.cpp b/src/tools/moc/preprocessor.cpp index a2a1a958cf..590f17c24f 100644 --- a/src/tools/moc/preprocessor.cpp +++ b/src/tools/moc/preprocessor.cpp @@ -154,6 +154,11 @@ bool Preprocessor::skipBranch() Symbols Preprocessor::tokenize(const QByteArray& input, int lineNum, Preprocessor::TokenizeMode mode) { Symbols symbols; + // Preallocate some space to speed up the code below. + // The magic divisor value was found by calculating the average ratio between + // input size and the final size of symbols. + // This yielded a value of 16.x when compiling Qt Base. + symbols.reserve(input.size() / 16); const char *begin = input.constData(); const char *data = begin; while (*data) { diff --git a/src/widgets/dialogs/qfilesystemmodel.cpp b/src/widgets/dialogs/qfilesystemmodel.cpp index b1e77540e7..d0c3edcd69 100644 --- a/src/widgets/dialogs/qfilesystemmodel.cpp +++ b/src/widgets/dialogs/qfilesystemmodel.cpp @@ -251,6 +251,21 @@ QModelIndex QFileSystemModel::index(int row, int column, const QModelIndex &pare } /*! + \reimp +*/ +QModelIndex QFileSystemModel::sibling(int row, int column, const QModelIndex &idx) const +{ + if (row == idx.row() && column < QFileSystemModelPrivate::NumColumns) { + // cheap sibling operation: just adjust the column: + return createIndex(row, column, idx.internalPointer()); + } else { + // for anything else: call the default implementation + // (this could probably be optimized, too): + return QAbstractItemModel::sibling(row, column, idx); + } +} + +/*! \overload Returns the model item index for the given \a path and \a column. diff --git a/src/widgets/dialogs/qfilesystemmodel.h b/src/widgets/dialogs/qfilesystemmodel.h index f749c97bcb..cef85a0c8c 100644 --- a/src/widgets/dialogs/qfilesystemmodel.h +++ b/src/widgets/dialogs/qfilesystemmodel.h @@ -75,6 +75,7 @@ public: QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; QModelIndex index(const QString &path, int column = 0) const; QModelIndex parent(const QModelIndex &child) const Q_DECL_OVERRIDE; + QModelIndex sibling(int row, int column, const QModelIndex &idx) const Q_DECL_OVERRIDE; bool hasChildren(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; bool canFetchMore(const QModelIndex &parent) const Q_DECL_OVERRIDE; void fetchMore(const QModelIndex &parent) Q_DECL_OVERRIDE; diff --git a/src/widgets/dialogs/qmessagebox.cpp b/src/widgets/dialogs/qmessagebox.cpp index 8a48100ea7..4ad235051c 100644 --- a/src/widgets/dialogs/qmessagebox.cpp +++ b/src/widgets/dialogs/qmessagebox.cpp @@ -89,16 +89,14 @@ public: { public: TextEdit(QWidget *parent=0) : QTextEdit(parent) { } +#ifndef QT_NO_CONTEXTMENU void contextMenuEvent(QContextMenuEvent * e) Q_DECL_OVERRIDE { -#ifndef QT_NO_CONTEXTMENU QMenu *menu = createStandardContextMenu(); menu->setAttribute(Qt::WA_DeleteOnClose); menu->popup(e->globalPos()); -#else - Q_UNUSED(e); -#endif } +#endif // QT_NO_CONTEXTMENU }; QMessageBoxDetailsText(QWidget *parent=0) diff --git a/src/widgets/doc/images/gtk-calendarwidget.png b/src/widgets/doc/images/gtk-calendarwidget.png Binary files differdeleted file mode 100644 index 568cd1a5da..0000000000 --- a/src/widgets/doc/images/gtk-calendarwidget.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-checkbox.png b/src/widgets/doc/images/gtk-checkbox.png Binary files differdeleted file mode 100644 index 1fd5fc77bf..0000000000 --- a/src/widgets/doc/images/gtk-checkbox.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-combobox.png b/src/widgets/doc/images/gtk-combobox.png Binary files differdeleted file mode 100644 index 3b4544df13..0000000000 --- a/src/widgets/doc/images/gtk-combobox.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-dateedit.png b/src/widgets/doc/images/gtk-dateedit.png Binary files differdeleted file mode 100644 index 25229f0b3b..0000000000 --- a/src/widgets/doc/images/gtk-dateedit.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-datetimeedit.png b/src/widgets/doc/images/gtk-datetimeedit.png Binary files differdeleted file mode 100644 index 0c934a4d0d..0000000000 --- a/src/widgets/doc/images/gtk-datetimeedit.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-dial.png b/src/widgets/doc/images/gtk-dial.png Binary files differdeleted file mode 100644 index 18e14b3650..0000000000 --- a/src/widgets/doc/images/gtk-dial.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-doublespinbox.png b/src/widgets/doc/images/gtk-doublespinbox.png Binary files differdeleted file mode 100644 index 3a69043c0b..0000000000 --- a/src/widgets/doc/images/gtk-doublespinbox.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-fontcombobox.png b/src/widgets/doc/images/gtk-fontcombobox.png Binary files differdeleted file mode 100644 index 4cb1bc1343..0000000000 --- a/src/widgets/doc/images/gtk-fontcombobox.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-frame.png b/src/widgets/doc/images/gtk-frame.png Binary files differdeleted file mode 100644 index c1bf52f6a6..0000000000 --- a/src/widgets/doc/images/gtk-frame.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-groupbox.png b/src/widgets/doc/images/gtk-groupbox.png Binary files differdeleted file mode 100644 index 6d217c89f9..0000000000 --- a/src/widgets/doc/images/gtk-groupbox.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-horizontalscrollbar.png b/src/widgets/doc/images/gtk-horizontalscrollbar.png Binary files differdeleted file mode 100644 index 2887730c13..0000000000 --- a/src/widgets/doc/images/gtk-horizontalscrollbar.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-label.png b/src/widgets/doc/images/gtk-label.png Binary files differdeleted file mode 100644 index 006d0133d8..0000000000 --- a/src/widgets/doc/images/gtk-label.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-lcdnumber.png b/src/widgets/doc/images/gtk-lcdnumber.png Binary files differdeleted file mode 100644 index 142d298ffc..0000000000 --- a/src/widgets/doc/images/gtk-lcdnumber.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-lineedit.png b/src/widgets/doc/images/gtk-lineedit.png Binary files differdeleted file mode 100644 index 8fb513c39f..0000000000 --- a/src/widgets/doc/images/gtk-lineedit.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-listview.png b/src/widgets/doc/images/gtk-listview.png Binary files differdeleted file mode 100644 index d32f8e8aee..0000000000 --- a/src/widgets/doc/images/gtk-listview.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-progressbar.png b/src/widgets/doc/images/gtk-progressbar.png Binary files differdeleted file mode 100644 index 6162484bf3..0000000000 --- a/src/widgets/doc/images/gtk-progressbar.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-pushbutton.png b/src/widgets/doc/images/gtk-pushbutton.png Binary files differdeleted file mode 100644 index f4f4d7c1e4..0000000000 --- a/src/widgets/doc/images/gtk-pushbutton.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-radiobutton.png b/src/widgets/doc/images/gtk-radiobutton.png Binary files differdeleted file mode 100644 index b3620fa054..0000000000 --- a/src/widgets/doc/images/gtk-radiobutton.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-slider.png b/src/widgets/doc/images/gtk-slider.png Binary files differdeleted file mode 100644 index 3d8e0ee89e..0000000000 --- a/src/widgets/doc/images/gtk-slider.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-spinbox.png b/src/widgets/doc/images/gtk-spinbox.png Binary files differdeleted file mode 100644 index a39eb3aa93..0000000000 --- a/src/widgets/doc/images/gtk-spinbox.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-tableview.png b/src/widgets/doc/images/gtk-tableview.png Binary files differdeleted file mode 100644 index a025193188..0000000000 --- a/src/widgets/doc/images/gtk-tableview.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-tabwidget.png b/src/widgets/doc/images/gtk-tabwidget.png Binary files differdeleted file mode 100644 index 089c76dad2..0000000000 --- a/src/widgets/doc/images/gtk-tabwidget.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-textedit.png b/src/widgets/doc/images/gtk-textedit.png Binary files differdeleted file mode 100644 index e4b91c01c9..0000000000 --- a/src/widgets/doc/images/gtk-textedit.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-timeedit.png b/src/widgets/doc/images/gtk-timeedit.png Binary files differdeleted file mode 100644 index acf6730a86..0000000000 --- a/src/widgets/doc/images/gtk-timeedit.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-toolbox.png b/src/widgets/doc/images/gtk-toolbox.png Binary files differdeleted file mode 100644 index 25e6137699..0000000000 --- a/src/widgets/doc/images/gtk-toolbox.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-toolbutton.png b/src/widgets/doc/images/gtk-toolbutton.png Binary files differdeleted file mode 100644 index f0eb86efc5..0000000000 --- a/src/widgets/doc/images/gtk-toolbutton.png +++ /dev/null diff --git a/src/widgets/doc/images/gtk-treeview.png b/src/widgets/doc/images/gtk-treeview.png Binary files differdeleted file mode 100644 index 7b4e3044be..0000000000 --- a/src/widgets/doc/images/gtk-treeview.png +++ /dev/null diff --git a/src/widgets/doc/snippets/code/doc_src_stylesheet.cpp b/src/widgets/doc/snippets/code/doc_src_stylesheet.cpp index a937498fe5..01f4c528a2 100644 --- a/src/widgets/doc/snippets/code/doc_src_stylesheet.cpp +++ b/src/widgets/doc/snippets/code/doc_src_stylesheet.cpp @@ -138,3 +138,7 @@ emailEdit->setProperty("mandatoryField", true); QSpinBox *ageSpinBox = new QSpinBox(this); ageSpinBox->setProperty("mandatoryField", true); //! [95] + +//! [96] +qApp->setAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles, true); +//! [97] diff --git a/src/widgets/doc/snippets/macmainwindow.mm b/src/widgets/doc/snippets/macmainwindow.mm index d0d74631ab..17aff45fa0 100644 --- a/src/widgets/doc/snippets/macmainwindow.mm +++ b/src/widgets/doc/snippets/macmainwindow.mm @@ -31,7 +31,7 @@ ** ****************************************************************************/ #include "macmainwindow.h" -#import <Cocoa/Cocoa.h> +#import <AppKit/AppKit.h> #include <QtGui> diff --git a/src/widgets/doc/snippets/qmacnativewidget/main.mm b/src/widgets/doc/snippets/qmacnativewidget/main.mm index e541aac1f8..38701917c0 100644 --- a/src/widgets/doc/snippets/qmacnativewidget/main.mm +++ b/src/widgets/doc/snippets/qmacnativewidget/main.mm @@ -40,7 +40,7 @@ #include <QtGui/QtGui> #include <QtGui/qmacnativewidget_mac.h> -#import <Cocoa/Cocoa.h> +#import <AppKit/AppKit.h> int main(int argc, char **argv) { diff --git a/src/widgets/doc/src/widgets-and-layouts/gallery-gtk.qdoc b/src/widgets/doc/src/widgets-and-layouts/gallery-gtk.qdoc deleted file mode 100644 index 5c3ae8cb97..0000000000 --- a/src/widgets/doc/src/widgets-and-layouts/gallery-gtk.qdoc +++ /dev/null @@ -1,142 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** 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 http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: http://www.gnu.org/copyleft/fdl.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/*! - \page gallery-gtk.html - - \title GTK Style Widget Gallery - \ingroup gallery - - This page shows some of the widgets available in Qt - when configured to use the "gtk" style. This style is - only available on desktop environments with GTK runtime - support. This style provides native look'n'feel by - integrating to the GTK platform theme. Thus, the final - appearance varies depending on the active GTK theme. - -\section2 Buttons - -\table 100% -\row -\li \image gtk-pushbutton.png - \caption The QPushButton widget provides a command button. -\li \image gtk-toolbutton.png - \caption The QToolButton class provides a quick-access button to commands - or options, usually used inside a QToolBar. -\endtable - -\table 100% -\row -\li \image gtk-checkbox.png - \caption The QCheckBox widget provides a checkbox with a text label. -\li \image gtk-radiobutton.png - \caption The QRadioButton widget provides a radio button with a text or pixmap label. -\endtable - -\section2 Containers - -\table 100% -\row -\li \image gtk-groupbox.png - The QGroupBox widget provides a group box frame with a title. -\li \image gtk-tabwidget.png - The QTabWidget class provides a stack of tabbed widgets. -\li \image gtk-frame.png - The QFrame widget provides a simple decorated container for other widgets. -\li \image gtk-toolbox.png - The QToolBox class provides a column of tabbed widget items. -\endtable - -\section2 Item Views - -\table 100% -\row -\li \image gtk-listview.png - The QListView class provides a default model/view implementation of a list/icon view. The QListWidget class provides a classic item-based list/icon view. -\li \image gtk-treeview.png - The QTreeView class provides a default model/view implementation of a tree view. The QTreeWidget class provides a classic item-based tree view. -\li \image gtk-tableview.png - The QTableView class provides a default model/view implementation of a table view. The QTableWidget class provides a classic item-based table view.\li -\li -\endtable - -\section2 Display Widgets - -\table 100% -\row -\li \image gtk-progressbar.png - The QProgressBar widget provides a horizontal progress bar. -\li \image gtk-label.png - The QLabel widget provides a text or image display. -\li \image gtk-lcdnumber.png - The QLCDNumber widget displays a number with LCD-like digits. -\endtable - -\section2 Input Widgets - -\table 100% -\row -\li \image gtk-lineedit.png - The QLineEdit widget is a one-line text editor. -\li \image gtk-dateedit.png - The QDateEdit class provides a widget for editing dates. -\li \image gtk-timeedit.png - The QTimeEdit class provides a widget for editing times. -\li \image gtk-datetimeedit.png - The QDateTimeEdit class provides a widget for editing dates and times. -\endtable - -\table 100% -\row -\li \image gtk-slider.png - The QSlider widget provides a vertical or horizontal slider. -\li \image gtk-combobox.png - The QComboBox widget is a combined button and pop-up list. -\li \image gtk-spinbox.png - The QSpinBox class provides a spin box widget. -\endtable - -\table 100% -\row -\li \image gtk-fontcombobox.png - The QFontComboBox widget is a specialized combobox that enables fonts to be selected from a pop-up list containing previews of available fonts. -\li \image gtk-doublespinbox.png - The QDoubleSpinBox class provides a spin box widget that allows double precision floating point numbers to be entered. -\li \image gtk-horizontalscrollbar.png - The QScrollBar widget provides a vertical or horizontal scroll bar. Here, we show a scroll bar with horizontal orientation. -\endtable - -\table 100% -\row -\li \image gtk-dial.png - The QDial class provides a rounded range control (like a speedometer or potentiometer). -\li \image gtk-textedit.png - The QTextEdit class provides a widget that is used to edit and display both plain and rich text. -\li \image gtk-calendarwidget.png - The QCalendarWidget class provides a monthly calendar widget that can be used to select dates. -\endtable -*/ diff --git a/src/widgets/doc/src/widgets-and-layouts/gallery.qdoc b/src/widgets/doc/src/widgets-and-layouts/gallery.qdoc index cc6446b8a5..c32eceb558 100644 --- a/src/widgets/doc/src/widgets-and-layouts/gallery.qdoc +++ b/src/widgets/doc/src/widgets-and-layouts/gallery.qdoc @@ -54,10 +54,6 @@ \caption \l{Macintosh Style Widget Gallery} The Macintosh style is provided by QMacStyle. - \li \image gtk-tabwidget.png GTK Style Widget Gallery - \caption \l{GTK Style Widget Gallery} - - The GTK style is provided by QGtkStyle. \li \image fusion-tabwidget.png Fusion Style Widget Gallery \caption \l{Fusion Style Widget Gallery} diff --git a/src/widgets/doc/src/widgets-and-layouts/styles.qdoc b/src/widgets/doc/src/widgets-and-layouts/styles.qdoc index 7d1bffd0b4..a42ee0db61 100644 --- a/src/widgets/doc/src/widgets-and-layouts/styles.qdoc +++ b/src/widgets/doc/src/widgets-and-layouts/styles.qdoc @@ -38,7 +38,7 @@ native widgets. Qt comes with a selection of built-in styles. Some styles are only - available on specific platforms (such as the Mac, GTK+ and Windows + available on specific platforms (such as the Mac and Windows Vista styles). Custom styles are made available as plugins or by creating an instance of a specific style class with QStyleFactory::create() and setting it with QApplication::setStyle(). diff --git a/src/widgets/doc/src/widgets-and-layouts/stylesheet.qdoc b/src/widgets/doc/src/widgets-and-layouts/stylesheet.qdoc index 843807b67c..9e6bd1b62d 100644 --- a/src/widgets/doc/src/widgets-and-layouts/stylesheet.qdoc +++ b/src/widgets/doc/src/widgets-and-layouts/stylesheet.qdoc @@ -492,9 +492,9 @@ \section1 Inheritance In classic CSS, when font and color of an item is not explicitly set, - it gets automatically inherited from the parent. When using Qt Style Sheets, - a widget does \b{not} automatically inherit its font and color setting - from its parent widget. + it gets automatically inherited from the parent. By default, when using + Qt Style Sheets, a widget does \b{not} automatically inherit its font + and color setting from its parent widget. For example, consider a QPushButton inside a QGroupBox: @@ -507,9 +507,23 @@ \snippet code/doc_src_stylesheet.cpp 25 - In contrast, setting a font and propagate using QWidget::setFont() and + In contrast, setting a font and palette using QWidget::setFont() and QWidget::setPalette() propagates to child widgets. + If you would prefer that the font and palette propagate to child widgets, + you can set the Qt::AA_UseStyleSheetPropagationInWidgetStyles flag, like + this: + + Usage: + \snippet code/doc_src_stylesheet.cpp 96 + + When the widget-style font and palette propagation is enabled, font and + palette changes made through Qt Style Sheets will behave as though the + user had manually called the corresponding QWidget::setPalette() and + QWidget::setFont() methods on all of the QWidgets targeted by the style + sheet. If this would have caused propagation in C++, it will cause + propagation in style sheets and visa versa. + \section1 Widgets Inside C++ Namespaces The Type Selector can be used to style widgets of a particular type. For diff --git a/src/widgets/graphicsview/qgraphicsscene.cpp b/src/widgets/graphicsview/qgraphicsscene.cpp index ad58aeb488..df810064c7 100644 --- a/src/widgets/graphicsview/qgraphicsscene.cpp +++ b/src/widgets/graphicsview/qgraphicsscene.cpp @@ -232,6 +232,7 @@ #include <QtGui/qpainter.h> #include <QtGui/qpixmapcache.h> #include <QtGui/qpolygon.h> +#include <QtGui/qtouchdevice.h> #include <QtWidgets/qstyleoption.h> #include <QtWidgets/qtooltip.h> #include <QtGui/qtransform.h> diff --git a/src/widgets/itemviews/qfileiconprovider.cpp b/src/widgets/itemviews/qfileiconprovider.cpp index f43bcd5d5a..18b9755477 100644 --- a/src/widgets/itemviews/qfileiconprovider.cpp +++ b/src/widgets/itemviews/qfileiconprovider.cpp @@ -52,10 +52,6 @@ # endif #endif -#if defined(Q_OS_UNIX) && !defined(QT_NO_STYLE_GTK) -# include <private/qgtkstyle_p_p.h> -#endif - QT_BEGIN_NAMESPACE static bool isCacheable(const QFileInfo &fi); @@ -358,15 +354,6 @@ QIcon QFileIconProvider::icon(const QFileInfo &info) const { Q_D(const QFileIconProvider); -#if defined(Q_OS_UNIX) && !defined(QT_NO_STYLE_GTK) - const QByteArray desktopEnvironment = QGuiApplicationPrivate::platformIntegration()->services()->desktopEnvironment(); - if (desktopEnvironment != QByteArrayLiteral("KDE")) { - QIcon gtkIcon = QGtkStylePrivate::getFilesystemIcon(info); - if (!gtkIcon.isNull()) - return gtkIcon; - } -#endif - QIcon retIcon = d->getIcon(info); if (!retIcon.isNull()) return retIcon; diff --git a/src/widgets/itemviews/qitemeditorfactory.cpp b/src/widgets/itemviews/qitemeditorfactory.cpp index e966c83fe7..701e7ab79b 100644 --- a/src/widgets/itemviews/qitemeditorfactory.cpp +++ b/src/widgets/itemviews/qitemeditorfactory.cpp @@ -47,6 +47,7 @@ #include <qapplication.h> #include <qdebug.h> +#include <algorithm> QT_BEGIN_NAMESPACE @@ -202,7 +203,7 @@ void QItemEditorFactory::registerEditor(int userType, QItemEditorCreatorBase *cr QItemEditorCreatorBase *oldCreator = it.value(); Q_ASSERT(oldCreator); creatorMap.erase(it); - if (!creatorMap.values().contains(oldCreator)) + if (std::find(creatorMap.cbegin(), creatorMap.cend(), oldCreator) == creatorMap.cend()) delete oldCreator; // if it is no more in use we can delete it } diff --git a/src/widgets/kernel/mac.pri b/src/widgets/kernel/mac.pri index 4c507ae80e..129fdebe1b 100644 --- a/src/widgets/kernel/mac.pri +++ b/src/widgets/kernel/mac.pri @@ -1,4 +1,4 @@ -!x11:mac:!ios { - LIBS_PRIVATE += -framework Carbon -framework Cocoa -lz +!x11:osx { + LIBS_PRIVATE += -framework Carbon -framework AppKit -lz *-mwerks:INCLUDEPATH += compat } diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 29bff8227f..70c91b9aa4 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -65,6 +65,7 @@ #include <QtGui/qstylehints.h> #include <QtGui/qinputmethod.h> #include <QtGui/private/qwindow_p.h> +#include <QtGui/qtouchdevice.h> #include <qpa/qplatformtheme.h> #ifndef QT_NO_WHATSTHIS #include <QtWidgets/QWhatsThis> diff --git a/src/widgets/kernel/qgesturemanager.cpp b/src/widgets/kernel/qgesturemanager.cpp index fb2914d53a..51cffc6321 100644 --- a/src/widgets/kernel/qgesturemanager.cpp +++ b/src/widgets/kernel/qgesturemanager.cpp @@ -106,7 +106,7 @@ QGestureManager::QGestureManager(QObject *parent) QGestureManager::~QGestureManager() { - qDeleteAll(m_recognizers.values()); + qDeleteAll(m_recognizers); foreach (QGestureRecognizer *recognizer, m_obsoleteGestures.keys()) { qDeleteAll(m_obsoleteGestures.value(recognizer)); delete recognizer; diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 4037310088..14eb368973 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -1957,11 +1957,14 @@ void QWidgetPrivate::propagatePaletteChange() } int mask = data.pal.resolve() | inheritedPaletteResolveMask; + const bool useStyleSheetPropagationInWidgetStyles = + QCoreApplication::testAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles); + QEvent pc(QEvent::PaletteChange); QApplication::sendEvent(q, &pc); for (int i = 0; i < children.size(); ++i) { QWidget *w = qobject_cast<QWidget*>(children.at(i)); - if (w && !w->testAttribute(Qt::WA_StyleSheet) + if (w && (!w->testAttribute(Qt::WA_StyleSheet) || useStyleSheetPropagationInWidgetStyles) && (!w->isWindow() || w->testAttribute(Qt::WA_WindowPropagation))) { QWidgetPrivate *wd = w->d_func(); wd->inheritedPaletteResolveMask = mask; @@ -4546,15 +4549,19 @@ void QWidget::setPalette(const QPalette &palette) QPalette QWidgetPrivate::naturalWidgetPalette(uint inheritedMask) const { Q_Q(const QWidget); + + const bool useStyleSheetPropagationInWidgetStyles = + QCoreApplication::testAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles); + QPalette naturalPalette = QApplication::palette(q); - if (!q->testAttribute(Qt::WA_StyleSheet) + if ((!q->testAttribute(Qt::WA_StyleSheet) || useStyleSheetPropagationInWidgetStyles) && (!q->isWindow() || q->testAttribute(Qt::WA_WindowPropagation) #ifndef QT_NO_GRAPHICSVIEW || (extra && extra->proxyWidget) #endif //QT_NO_GRAPHICSVIEW )) { if (QWidget *p = q->parentWidget()) { - if (!p->testAttribute(Qt::WA_StyleSheet)) { + if (!p->testAttribute(Qt::WA_StyleSheet) || useStyleSheetPropagationInWidgetStyles) { if (!naturalPalette.isCopyOf(QApplication::palette())) { QPalette inheritedPalette = p->palette(); inheritedPalette.resolve(inheritedMask); @@ -4690,15 +4697,19 @@ void QWidget::setFont(const QFont &font) QFont QWidgetPrivate::naturalWidgetFont(uint inheritedMask) const { Q_Q(const QWidget); + + const bool useStyleSheetPropagationInWidgetStyles = + QCoreApplication::testAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles); + QFont naturalFont = QApplication::font(q); - if (!q->testAttribute(Qt::WA_StyleSheet) + if ((!q->testAttribute(Qt::WA_StyleSheet) || useStyleSheetPropagationInWidgetStyles) && (!q->isWindow() || q->testAttribute(Qt::WA_WindowPropagation) #ifndef QT_NO_GRAPHICSVIEW || (extra && extra->proxyWidget) #endif //QT_NO_GRAPHICSVIEW )) { if (QWidget *p = q->parentWidget()) { - if (!p->testAttribute(Qt::WA_StyleSheet)) { + if (!p->testAttribute(Qt::WA_StyleSheet) || useStyleSheetPropagationInWidgetStyles) { if (!naturalFont.isCopyOf(QApplication::font())) { if (inheritedMask != 0) { QFont inheritedFont = p->font(); @@ -4754,6 +4765,8 @@ void QWidgetPrivate::updateFont(const QFont &font) #ifndef QT_NO_STYLE_STYLESHEET const QStyleSheetStyle* cssStyle; cssStyle = extra ? qobject_cast<const QStyleSheetStyle*>(extra->style) : 0; + const bool useStyleSheetPropagationInWidgetStyles = + QCoreApplication::testAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles); #endif data.fnt = QFont(font, q); @@ -4778,7 +4791,7 @@ void QWidgetPrivate::updateFont(const QFont &font) if (w) { if (0) { #ifndef QT_NO_STYLE_STYLESHEET - } else if (w->testAttribute(Qt::WA_StyleSheet)) { + } else if (!useStyleSheetPropagationInWidgetStyles && w->testAttribute(Qt::WA_StyleSheet)) { // Style sheets follow a different font propagation scheme. if (cssStyle) cssStyle->updateStyleSheetFont(w); @@ -4793,7 +4806,7 @@ void QWidgetPrivate::updateFont(const QFont &font) } #ifndef QT_NO_STYLE_STYLESHEET - if (cssStyle) { + if (!useStyleSheetPropagationInWidgetStyles && cssStyle) { cssStyle->updateStyleSheetFont(q); } #endif @@ -5147,9 +5160,9 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset, d->render(target, targetOffset, toBePainted, renderFlags); // Restore system clip, viewport and transform. - enginePriv->systemClip = oldSystemClip; enginePriv->setSystemViewport(oldSystemViewport); enginePriv->setSystemTransform(oldTransform); + enginePriv->systemClip = oldSystemClip; // Restore shared painter. d->setSharedPainter(oldPainter); @@ -10455,7 +10468,11 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) d->reparentFocusWidgets(oldtlw); setAttribute(Qt::WA_Resized, resized); - if (!testAttribute(Qt::WA_StyleSheet) + + const bool useStyleSheetPropagationInWidgetStyles = + QCoreApplication::testAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles); + + if (!useStyleSheetPropagationInWidgetStyles && !testAttribute(Qt::WA_StyleSheet) && (!parent || !parent->testAttribute(Qt::WA_StyleSheet))) { d->resolveFont(); d->resolvePalette(); diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index a194993328..e3c40ef95e 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -283,7 +283,7 @@ bool QWidgetWindow::event(QEvent *event) case QEvent::ContextMenu: handleContextMenuEvent(static_cast<QContextMenuEvent *>(event)); return true; -#endif +#endif // QT_NO_CONTEXTMENU // Handing show events to widgets (see below) here would cause them to be triggered twice case QEvent::Show: @@ -510,8 +510,12 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event) popupEvent = popupChild; QContextMenuEvent e(QContextMenuEvent::Mouse, mapped, event->globalPos(), event->modifiers()); QApplication::sendSpontaneousEvent(popupEvent, &e); -#endif } +#else + Q_UNUSED(contextMenuTrigger) + Q_UNUSED(oldOpenPopupCount) + } +#endif if (releaseAfter) { qt_button_down = 0; diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp index 83e68d5f5f..5a08801e94 100644 --- a/src/widgets/styles/qcommonstyle.cpp +++ b/src/widgets/styles/qcommonstyle.cpp @@ -552,18 +552,31 @@ void QCommonStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, Q case PE_IndicatorTabTear: if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) { bool rtl = tab->direction == Qt::RightToLeft; - QRect rect = tab->rect; + const bool horizontal = tab->rect.height() > tab->rect.width(); + const int margin = 4; QPainterPath path; - rect.setTop(rect.top() + ((tab->state & State_Selected) ? 1 : 3)); - rect.setBottom(rect.bottom() - ((tab->state & State_Selected) ? 0 : 2)); + if (horizontal) { + QRect rect = tab->rect.adjusted(rtl ? margin : 0, 0, rtl ? 1 : -margin, 0); + rect.setTop(rect.top() + ((tab->state & State_Selected) ? 1 : 3)); + rect.setBottom(rect.bottom() - ((tab->state & State_Selected) ? 0 : 2)); - path.moveTo(QPoint(rtl ? rect.right() : rect.left(), rect.top())); - int count = 4; - for(int jags = 1; jags <= count; ++jags, rtl = !rtl) - path.lineTo(QPoint(rtl ? rect.left() : rect.right(), rect.top() + jags * rect.height()/count)); + path.moveTo(QPoint(rtl ? rect.right() : rect.left(), rect.top())); + int count = 4; + for (int jags = 1; jags <= count; ++jags, rtl = !rtl) + path.lineTo(QPoint(rtl ? rect.left() : rect.right(), rect.top() + jags * rect.height()/count)); + } else { + QRect rect = tab->rect.adjusted(0, 0, 0, -margin); + rect.setLeft(rect.left() + ((tab->state & State_Selected) ? 1 : 3)); + rect.setRight(rect.right() - ((tab->state & State_Selected) ? 0 : 2)); + + path.moveTo(QPoint(rect.left(), rect.top())); + int count = 4; + for (int jags = 1; jags <= count; ++jags, rtl = !rtl) + path.lineTo(QPoint(rect.left() + jags * rect.width()/count, rtl ? rect.top() : rect.bottom())); + } - p->setPen(QPen(tab->palette.light(), qreal(.8))); + p->setPen(QPen(tab->palette.dark(), qreal(.8))); p->setBrush(tab->palette.background()); p->setRenderHint(QPainter::Antialiasing); p->drawPath(path); @@ -2796,13 +2809,13 @@ QRect QCommonStyle::subElementRect(SubElement sr, const QStyleOption *opt, case QTabBar::TriangularNorth: case QTabBar::RoundedSouth: case QTabBar::TriangularSouth: - r.setRect(tab->rect.left(), tab->rect.top(), 4, opt->rect.height()); + r.setRect(tab->rect.left(), tab->rect.top(), 8, opt->rect.height()); break; case QTabBar::RoundedWest: case QTabBar::TriangularWest: case QTabBar::RoundedEast: case QTabBar::TriangularEast: - r.setRect(tab->rect.left(), tab->rect.top(), opt->rect.width(), 4); + r.setRect(tab->rect.left(), tab->rect.top(), opt->rect.width(), 8); break; default: break; @@ -2810,6 +2823,23 @@ QRect QCommonStyle::subElementRect(SubElement sr, const QStyleOption *opt, r = visualRect(opt->direction, opt->rect, r); } break; + case SE_TabBarScrollLeftButton: { + const bool vertical = opt->rect.width() < opt->rect.height(); + const Qt::LayoutDirection ld = widget->layoutDirection(); + const int buttonWidth = qMax(pixelMetric(QStyle::PM_TabBarScrollButtonWidth, 0, widget), QApplication::globalStrut().width()); + const int buttonOverlap = pixelMetric(QStyle::PM_TabBar_ScrollButtonOverlap, 0, widget); + + r = vertical ? QRect(0, opt->rect.height() - (buttonWidth * 2) + buttonOverlap, opt->rect.width(), buttonWidth) + : QStyle::visualRect(ld, opt->rect, QRect(opt->rect.width() - (buttonWidth * 2) + buttonOverlap, 0, buttonWidth, opt->rect.height())); + break; } + case SE_TabBarScrollRightButton: { + const bool vertical = opt->rect.width() < opt->rect.height(); + const Qt::LayoutDirection ld = widget->layoutDirection(); + const int buttonWidth = qMax(pixelMetric(QStyle::PM_TabBarScrollButtonWidth, 0, widget), QApplication::globalStrut().width()); + + r = vertical ? QRect(0, opt->rect.height() - buttonWidth, opt->rect.width(), buttonWidth) + : QStyle::visualRect(ld, opt->rect, QRect(opt->rect.width() - buttonWidth, 0, buttonWidth, opt->rect.height())); + break; } #endif case SE_TreeViewDisclosureItem: r = opt->rect; diff --git a/src/widgets/styles/qgtk2painter.cpp b/src/widgets/styles/qgtk2painter.cpp deleted file mode 100644 index ea8afbc93c..0000000000 --- a/src/widgets/styles/qgtk2painter.cpp +++ /dev/null @@ -1,699 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qgtk2painter_p.h" - -#include <QtCore/qglobal.h> -#if !defined(QT_NO_STYLE_GTK) - -// This class is primarily a wrapper around the gtk painter functions -// and takes care of converting all such calls into cached Qt pixmaps. - -#include <private/qgtkstyle_p_p.h> -#include <private/qhexstring_p.h> -#include <QtWidgets/QWidget> -#include <QtGui/QPixmapCache> -#include <QtCore/QLibrary> - -QT_BEGIN_NAMESPACE - -typedef GdkPixbuf* (*Ptr_gdk_pixbuf_get_from_drawable) (GdkPixbuf *, GdkDrawable *, GdkColormap *, int, int, int, int, int, int); -typedef GdkPixmap* (*Ptr_gdk_pixmap_new) (GdkDrawable *, gint, gint, gint); -typedef void (*Ptr_gdk_draw_rectangle) (GdkDrawable *, GdkGC *, gboolean, gint, gint, gint, gint); -typedef void (*Ptr_gdk_drawable_unref)(GdkDrawable *); - -typedef void (*Ptr_gtk_paint_check) (GtkStyle *, GdkWindow *, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint , gint , gint , gint); -typedef void (*Ptr_gtk_paint_box) (GtkStyle *, GdkWindow *, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint , gint , gint , gint); -typedef void (*Ptr_gtk_paint_box_gap) (GtkStyle *, GdkWindow *, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint, gint, gint , gint, GtkPositionType, gint, gint); -typedef void (*Ptr_gtk_paint_resize_grip) (GtkStyle *, GdkWindow *, GtkStateType, const GdkRectangle *, GtkWidget *, const gchar *, GdkWindowEdge, gint , gint , gint , gint); -typedef void (*Ptr_gtk_paint_focus) (GtkStyle *, GdkWindow *, GtkStateType, const GdkRectangle *, GtkWidget *, const gchar *, gint , gint , gint , gint); -typedef void (*Ptr_gtk_paint_shadow) (GtkStyle *, GdkWindow *, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint , gint , gint , gint); -typedef void (*Ptr_gtk_paint_slider) (GtkStyle *, GdkWindow *, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint , gint , gint , gint, GtkOrientation); -typedef void (*Ptr_gtk_paint_expander) (GtkStyle *, GdkWindow *, GtkStateType, const GdkRectangle *, GtkWidget *, const gchar *, gint , gint , GtkExpanderStyle ); -typedef void (*Ptr_gtk_paint_handle) (GtkStyle *, GdkWindow *, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint , gint , gint , gint, GtkOrientation); -typedef void (*Ptr_gtk_paint_arrow) (GtkStyle *, GdkWindow *, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, GtkArrowType, gboolean, gint , gint , gint , gint); -typedef void (*Ptr_gtk_paint_option) (GtkStyle *, GdkWindow *, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint , gint , gint , gint); -typedef void (*Ptr_gtk_paint_flat_box) (GtkStyle *, GdkWindow *, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint , gint , gint , gint); -typedef void (*Ptr_gtk_paint_extension) (GtkStyle *, GdkWindow *, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint, gint, gint, gint, GtkPositionType); -typedef void (*Ptr_gtk_paint_hline) (GtkStyle *, GdkWindow *, GtkStateType, const GdkRectangle *, GtkWidget *, const gchar *, gint, gint, gint y); -typedef void (*Ptr_gtk_paint_vline) (GtkStyle *, GdkWindow *, GtkStateType, const GdkRectangle *, GtkWidget *, const gchar *, gint, gint, gint); - -namespace QGtk2PainterPrivate { - static Ptr_gdk_pixmap_new gdk_pixmap_new = 0; - static Ptr_gdk_pixbuf_get_from_drawable gdk_pixbuf_get_from_drawable = 0; - static Ptr_gdk_draw_rectangle gdk_draw_rectangle = 0; - static Ptr_gdk_drawable_unref gdk_drawable_unref = 0; - - static Ptr_gtk_paint_check gtk_paint_check = 0; - static Ptr_gtk_paint_box gtk_paint_box = 0; - static Ptr_gtk_paint_box_gap gtk_paint_box_gap = 0; - static Ptr_gtk_paint_flat_box gtk_paint_flat_box = 0; - static Ptr_gtk_paint_option gtk_paint_option = 0; - static Ptr_gtk_paint_extension gtk_paint_extension = 0; - static Ptr_gtk_paint_slider gtk_paint_slider = 0; - static Ptr_gtk_paint_shadow gtk_paint_shadow = 0; - static Ptr_gtk_paint_resize_grip gtk_paint_resize_grip = 0; - static Ptr_gtk_paint_focus gtk_paint_focus = 0; - static Ptr_gtk_paint_arrow gtk_paint_arrow = 0; - static Ptr_gtk_paint_handle gtk_paint_handle = 0; - static Ptr_gtk_paint_expander gtk_paint_expander = 0; - static Ptr_gtk_paint_vline gtk_paint_vline = 0; - static Ptr_gtk_paint_hline gtk_paint_hline = 0; -} - -static void initGtk() -{ -#ifndef QT_NO_LIBRARY - static bool initialized = false; - if (!initialized) { - // enforce the "0" suffix, so we'll open libgtk-x11-2.0.so.0 - QLibrary libgtk(QLS("gtk-x11-2.0"), 0, 0); - - QGtk2PainterPrivate::gdk_pixmap_new = (Ptr_gdk_pixmap_new)libgtk.resolve("gdk_pixmap_new"); - QGtk2PainterPrivate::gdk_pixbuf_get_from_drawable = (Ptr_gdk_pixbuf_get_from_drawable)libgtk.resolve("gdk_pixbuf_get_from_drawable"); - QGtk2PainterPrivate::gdk_draw_rectangle = (Ptr_gdk_draw_rectangle)libgtk.resolve("gdk_draw_rectangle"); - QGtk2PainterPrivate::gdk_drawable_unref = (Ptr_gdk_drawable_unref)libgtk.resolve("gdk_drawable_unref"); - - QGtk2PainterPrivate::gtk_paint_check = (Ptr_gtk_paint_check)libgtk.resolve("gtk_paint_check"); - QGtk2PainterPrivate::gtk_paint_box = (Ptr_gtk_paint_box)libgtk.resolve("gtk_paint_box"); - QGtk2PainterPrivate::gtk_paint_flat_box = (Ptr_gtk_paint_flat_box)libgtk.resolve("gtk_paint_flat_box"); - QGtk2PainterPrivate::gtk_paint_check = (Ptr_gtk_paint_check)libgtk.resolve("gtk_paint_check"); - QGtk2PainterPrivate::gtk_paint_box = (Ptr_gtk_paint_box)libgtk.resolve("gtk_paint_box"); - QGtk2PainterPrivate::gtk_paint_resize_grip = (Ptr_gtk_paint_resize_grip)libgtk.resolve("gtk_paint_resize_grip"); - QGtk2PainterPrivate::gtk_paint_focus = (Ptr_gtk_paint_focus)libgtk.resolve("gtk_paint_focus"); - QGtk2PainterPrivate::gtk_paint_shadow = (Ptr_gtk_paint_shadow)libgtk.resolve("gtk_paint_shadow"); - QGtk2PainterPrivate::gtk_paint_slider = (Ptr_gtk_paint_slider)libgtk.resolve("gtk_paint_slider"); - QGtk2PainterPrivate::gtk_paint_expander = (Ptr_gtk_paint_expander)libgtk.resolve("gtk_paint_expander"); - QGtk2PainterPrivate::gtk_paint_handle = (Ptr_gtk_paint_handle)libgtk.resolve("gtk_paint_handle"); - QGtk2PainterPrivate::gtk_paint_option = (Ptr_gtk_paint_option)libgtk.resolve("gtk_paint_option"); - QGtk2PainterPrivate::gtk_paint_arrow = (Ptr_gtk_paint_arrow)libgtk.resolve("gtk_paint_arrow"); - QGtk2PainterPrivate::gtk_paint_box_gap = (Ptr_gtk_paint_box_gap)libgtk.resolve("gtk_paint_box_gap"); - QGtk2PainterPrivate::gtk_paint_extension = (Ptr_gtk_paint_extension)libgtk.resolve("gtk_paint_extension"); - QGtk2PainterPrivate::gtk_paint_hline = (Ptr_gtk_paint_hline)libgtk.resolve("gtk_paint_hline"); - QGtk2PainterPrivate::gtk_paint_vline = (Ptr_gtk_paint_vline)libgtk.resolve("gtk_paint_vline"); - - initialized = true; - } -#endif // !QT_NO_LIBRARY -} - -// To recover alpha we apply the gtk painting function two times to -// white, and black window backgrounds. This can be used to -// recover the premultiplied alpha channel -QPixmap QGtk2Painter::renderTheme(uchar *bdata, uchar *wdata, const QRect &rect) const -{ - const int bytecount = rect.width() * rect.height() * 4; - for (int index = 0; index < bytecount ; index += 4) { - uchar val = bdata[index + GTK_BLUE]; - if (m_alpha) { - int alphaval = qMax(bdata[index + GTK_BLUE] - wdata[index + GTK_BLUE], - bdata[index + GTK_GREEN] - wdata[index + GTK_GREEN]); - alphaval = qMax(alphaval, bdata[index + GTK_RED] - wdata[index + GTK_RED]) + 255; - bdata[index + QT_ALPHA] = alphaval; - } - bdata[index + QT_RED] = bdata[index + GTK_RED]; - bdata[index + QT_GREEN] = bdata[index + GTK_GREEN]; - bdata[index + QT_BLUE] = val; - } - QImage converted((const uchar*)bdata, rect.width(), rect.height(), m_alpha ? - QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32); - - if (m_hflipped || m_vflipped) { - return QPixmap::fromImage(converted.mirrored(m_hflipped, m_vflipped)); - } else { - // on raster graphicssystem we need to do a copy here, because - // we intend to deallocate the qimage bits shortly after... - return QPixmap::fromImage(converted.copy()); - } -} - -// This macro is responsible for painting any GtkStyle painting function onto a QPixmap -#define DRAW_TO_CACHE(draw_func) \ - if (rect.width() > QWIDGETSIZE_MAX || rect.height() > QWIDGETSIZE_MAX) \ - return; \ - QRect pixmapRect(0, 0, rect.width(), rect.height()); \ - { \ - GdkPixmap *pixmap = QGtk2PainterPrivate::gdk_pixmap_new((GdkDrawable*)(m_window->window), \ - rect.width(), rect.height(), -1); \ - if (!pixmap) \ - return; \ - style = QGtkStylePrivate::gtk_style_attach (style, m_window->window); \ - QGtk2PainterPrivate::gdk_draw_rectangle(pixmap, m_alpha ? style->black_gc : *style->bg_gc, \ - true, 0, 0, rect.width(), rect.height()); \ - draw_func; \ - GdkPixbuf *imgb = QGtkStylePrivate::gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, \ - rect.width(), rect.height()); \ - if (!imgb) \ - return; \ - imgb = QGtk2PainterPrivate::gdk_pixbuf_get_from_drawable(imgb, pixmap, NULL, 0, 0, 0, 0, \ - rect.width(), rect.height()); \ - uchar* bdata = (uchar*)QGtkStylePrivate::gdk_pixbuf_get_pixels(imgb); \ - if (m_alpha) { \ - QGtk2PainterPrivate::gdk_draw_rectangle(pixmap, style->white_gc, true, 0, 0, \ - rect.width(), rect.height()); \ - draw_func; \ - GdkPixbuf *imgw = QGtkStylePrivate::gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, \ - rect.width(), rect.height()); \ - if (!imgw) \ - return; \ - imgw = QGtk2PainterPrivate::gdk_pixbuf_get_from_drawable(imgw, pixmap, NULL, 0, 0, 0, 0,\ - rect.width(), rect.height()); \ - uchar* wdata = (uchar*)QGtkStylePrivate::gdk_pixbuf_get_pixels(imgw); \ - cache = renderTheme(bdata, wdata, rect); \ - QGtkStylePrivate::gdk_pixbuf_unref(imgw); \ - } else { \ - cache = renderTheme(bdata, 0, rect); \ - } \ - QGtk2PainterPrivate::gdk_drawable_unref(pixmap); \ - QGtkStylePrivate::gdk_pixbuf_unref(imgb); \ - } - -QGtk2Painter::QGtk2Painter() : QGtkPainter(), m_window(QGtkStylePrivate::gtkWidget("GtkWindow")) -{ - initGtk(); -} - -// Note currently painted without alpha for performance reasons -void QGtk2Painter::paintBoxGap(GtkWidget *gtkWidget, const gchar* part, - const QRect &paintRect, GtkStateType state, - GtkShadowType shadow, GtkPositionType gap_side, - gint x, gint width, - GtkStyle *style) -{ - if (!paintRect.isValid()) - return; - - QPixmap cache; - QRect rect = paintRect; - - // To avoid exhausting cache on large tabframes we cheat a bit by - // tiling the center part. - - const int maxHeight = 256; - const int border = 16; - if (rect.height() > maxHeight && (gap_side == GTK_POS_TOP || gap_side == GTK_POS_BOTTOM)) - rect.setHeight(2 * border + 1); - - QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size(), gtkWidget) - % HexString<uchar>(gap_side) - % HexString<gint>(width) - % HexString<gint>(x); - - if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) { - DRAW_TO_CACHE(QGtk2PainterPrivate::gtk_paint_box_gap (style, - pixmap, - state, - shadow, - NULL, - gtkWidget, - (const gchar*)part, - 0, 0, - rect.width(), - rect.height(), - gap_side, - x, - width)); - if (m_usePixmapCache) - QPixmapCache::insert(pixmapName, cache); - } - if (rect.size() != paintRect.size()) { - // We assume we can stretch the middle tab part - // Note: the side effect of this is that pinstripe patterns will get fuzzy - const QSize size = cache.size(); - // top part - m_painter->drawPixmap(QRect(paintRect.left(), paintRect.top(), - paintRect.width(), border), cache, - QRect(0, 0, size.width(), border)); - - // tiled center part - QPixmap tilePart(cache.width(), 1); - QPainter scanLinePainter(&tilePart); - scanLinePainter.drawPixmap(QRect(0, 0, tilePart.width(), tilePart.height()), cache, QRect(0, border, size.width(), 1)); - scanLinePainter.end(); - m_painter->drawTiledPixmap(QRect(paintRect.left(), paintRect.top() + border, - paintRect.width(), paintRect.height() - 2*border), tilePart); - - // bottom part - m_painter->drawPixmap(QRect(paintRect.left(), paintRect.top() + paintRect.height() - border, - paintRect.width(), border), cache, - QRect(0, size.height() - border, size.width(), border)); - } else - m_painter->drawPixmap(paintRect.topLeft(), cache); -} - -void QGtk2Painter::paintBox(GtkWidget *gtkWidget, const gchar* part, - const QRect &paintRect, GtkStateType state, - GtkShadowType shadow, GtkStyle *style, - const QString &pmKey) -{ - if (!paintRect.isValid()) - return; - - QPixmap cache; - QRect rect = paintRect; - - // To avoid exhausting cache on large tabframes we cheat a bit by - // tiling the center part. - - const int maxHeight = 256; - const int maxArea = 256*512; - const int border = 32; - if (rect.height() > maxHeight && (rect.width()*rect.height() > maxArea)) - rect.setHeight(2 * border + 1); - - QString pixmapName = uniqueName(QLS(part), state, shadow, - rect.size(), gtkWidget) % pmKey; - - if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) { - DRAW_TO_CACHE(QGtk2PainterPrivate::gtk_paint_box (style, - pixmap, - state, - shadow, - NULL, - gtkWidget, - part, - 0, 0, - rect.width(), - rect.height())); - if (m_usePixmapCache) - QPixmapCache::insert(pixmapName, cache); - } - if (rect.size() != paintRect.size()) { - // We assume we can stretch the middle tab part - // Note: the side effect of this is that pinstripe patterns will get fuzzy - const QSize size = cache.size(); - // top part - m_painter->drawPixmap(QRect(paintRect.left(), paintRect.top(), - paintRect.width(), border), cache, - QRect(0, 0, size.width(), border)); - - // tiled center part - QPixmap tilePart(cache.width(), 1); - QPainter scanLinePainter(&tilePart); - scanLinePainter.drawPixmap(QRect(0, 0, tilePart.width(), tilePart.height()), cache, QRect(0, border, size.width(), 1)); - scanLinePainter.end(); - m_painter->drawTiledPixmap(QRect(paintRect.left(), paintRect.top() + border, - paintRect.width(), paintRect.height() - 2*border), tilePart); - - // bottom part - m_painter->drawPixmap(QRect(paintRect.left(), paintRect.top() + paintRect.height() - border, - paintRect.width(), border), cache, - QRect(0, size.height() - border, size.width(), border)); - } else - m_painter->drawPixmap(paintRect.topLeft(), cache); -} - -void QGtk2Painter::paintHline(GtkWidget *gtkWidget, const gchar* part, - const QRect &rect, GtkStateType state, - GtkStyle *style, int x1, int x2, int y, - const QString &pmKey) -{ - if (!rect.isValid()) - return; - - QPixmap cache; - QString pixmapName = uniqueName(QLS(part), state, GTK_SHADOW_NONE, rect.size(), gtkWidget) - % HexString<int>(x1) - % HexString<int>(x2) - % HexString<int>(y) - % pmKey; - if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) { - DRAW_TO_CACHE(QGtk2PainterPrivate::gtk_paint_hline (style, - pixmap, - state, - NULL, - gtkWidget, - part, - x1, x2, y)); - if (m_usePixmapCache) - QPixmapCache::insert(pixmapName, cache); - } - - m_painter->drawPixmap(rect.topLeft(), cache); -} - -void QGtk2Painter::paintVline(GtkWidget *gtkWidget, const gchar* part, - const QRect &rect, GtkStateType state, - GtkStyle *style, int y1, int y2, int x, - const QString &pmKey) -{ - if (!rect.isValid()) - return; - - QPixmap cache; - QString pixmapName = uniqueName(QLS(part), state, GTK_SHADOW_NONE, rect.size(), gtkWidget) - % HexString<int>(y1) - % HexString<int>(y2) - % HexString<int>(x) - % pmKey; - - if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) { - DRAW_TO_CACHE(QGtk2PainterPrivate::gtk_paint_vline (style, - pixmap, - state, - NULL, - gtkWidget, - part, - y1, y2, - x)); - if (m_usePixmapCache) - QPixmapCache::insert(pixmapName, cache); - } - m_painter->drawPixmap(rect.topLeft(), cache); -} - - -void QGtk2Painter::paintExpander(GtkWidget *gtkWidget, - const gchar* part, const QRect &rect, - GtkStateType state, GtkExpanderStyle expander_state, - GtkStyle *style, const QString &pmKey) -{ - if (!rect.isValid()) - return; - - QPixmap cache; - QString pixmapName = uniqueName(QLS(part), state, GTK_SHADOW_NONE, rect.size(), gtkWidget) - % HexString<uchar>(expander_state) - % pmKey; - - if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) { - DRAW_TO_CACHE(QGtk2PainterPrivate::gtk_paint_expander (style, pixmap, - state, NULL, - gtkWidget, part, - rect.width()/2, - rect.height()/2, - expander_state)); - if (m_usePixmapCache) - QPixmapCache::insert(pixmapName, cache); - } - - m_painter->drawPixmap(rect.topLeft(), cache); -} - -void QGtk2Painter::paintFocus(GtkWidget *gtkWidget, const gchar* part, - const QRect &rect, GtkStateType state, - GtkStyle *style, const QString &pmKey) -{ - if (!rect.isValid()) - return; - - QPixmap cache; - QString pixmapName = uniqueName(QLS(part), state, GTK_SHADOW_NONE, rect.size(), gtkWidget) % pmKey; - if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) { - DRAW_TO_CACHE(QGtk2PainterPrivate::gtk_paint_focus (style, pixmap, state, NULL, - gtkWidget, - part, - 0, 0, - rect.width(), - rect.height())); - if (m_usePixmapCache) - QPixmapCache::insert(pixmapName, cache); - } - - m_painter->drawPixmap(rect.topLeft(), cache); -} - - -void QGtk2Painter::paintResizeGrip(GtkWidget *gtkWidget, const gchar* part, - const QRect &rect, GtkStateType state, - GtkShadowType shadow, GdkWindowEdge edge, - GtkStyle *style, const QString &pmKey) -{ - if (!rect.isValid()) - return; - - QPixmap cache; - QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size(), gtkWidget) % pmKey; - if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) { - DRAW_TO_CACHE(QGtk2PainterPrivate::gtk_paint_resize_grip (style, pixmap, state, - NULL, gtkWidget, - part, edge, 0, 0, - rect.width(), - rect.height())); - if (m_usePixmapCache) - QPixmapCache::insert(pixmapName, cache); - } - - m_painter->drawPixmap(rect.topLeft(), cache); -} - - -void QGtk2Painter::paintArrow(GtkWidget *gtkWidget, const gchar* part, - const QRect &arrowrect, GtkArrowType arrow_type, - GtkStateType state, GtkShadowType shadow, - gboolean fill, GtkStyle *style, const QString &pmKey) -{ - QRect rect = m_cliprect.isValid() ? m_cliprect : arrowrect; - if (!rect.isValid()) - return; - - QPixmap cache; - QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size()) - % HexString<uchar>(arrow_type) - % pmKey; - - GdkRectangle gtkCliprect = {0, 0, rect.width(), rect.height()}; - int xOffset = m_cliprect.isValid() ? arrowrect.x() - m_cliprect.x() : 0; - int yOffset = m_cliprect.isValid() ? arrowrect.y() - m_cliprect.y() : 0; - if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) { - DRAW_TO_CACHE(QGtk2PainterPrivate::gtk_paint_arrow (style, pixmap, state, shadow, - >kCliprect, - gtkWidget, - part, - arrow_type, fill, - xOffset, yOffset, - arrowrect.width(), - arrowrect.height())) - if (m_usePixmapCache) - QPixmapCache::insert(pixmapName, cache); - } - - m_painter->drawPixmap(rect.topLeft(), cache); -} - - -void QGtk2Painter::paintHandle(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, - GtkStateType state, GtkShadowType shadow, - GtkOrientation orientation, GtkStyle *style) -{ - if (!rect.isValid()) - return; - - QPixmap cache; - QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size()) - % HexString<uchar>(orientation); - - if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) { - DRAW_TO_CACHE(QGtk2PainterPrivate::gtk_paint_handle (style, - pixmap, - state, - shadow, - NULL, - gtkWidget, - part, 0, 0, - rect.width(), - rect.height(), - orientation)); - if (m_usePixmapCache) - QPixmapCache::insert(pixmapName, cache); - } - m_painter->drawPixmap(rect.topLeft(), cache); -} - - -void QGtk2Painter::paintSlider(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, - GtkStateType state, GtkShadowType shadow, - GtkStyle *style, GtkOrientation orientation, - const QString &pmKey) -{ - if (!rect.isValid()) - return; - - QPixmap cache; - QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size(), gtkWidget) % pmKey; - if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) { - DRAW_TO_CACHE(QGtk2PainterPrivate::gtk_paint_slider (style, - pixmap, - state, - shadow, - NULL, - gtkWidget, - part, - 0, 0, - rect.width(), - rect.height(), - orientation)); - if (m_usePixmapCache) - QPixmapCache::insert(pixmapName, cache); - } - m_painter->drawPixmap(rect.topLeft(), cache); -} - - -void QGtk2Painter::paintShadow(GtkWidget *gtkWidget, const gchar* part, - const QRect &rect, GtkStateType state, - GtkShadowType shadow, GtkStyle *style, - const QString &pmKey) - -{ - if (!rect.isValid()) - return; - - QPixmap cache; - QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size()) % pmKey; - if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) { - DRAW_TO_CACHE(QGtk2PainterPrivate::gtk_paint_shadow(style, pixmap, state, shadow, NULL, - gtkWidget, part, 0, 0, rect.width(), rect.height())); - if (m_usePixmapCache) - QPixmapCache::insert(pixmapName, cache); - } - m_painter->drawPixmap(rect.topLeft(), cache); -} - -void QGtk2Painter::paintFlatBox(GtkWidget *gtkWidget, const gchar* part, - const QRect &rect, GtkStateType state, - GtkShadowType shadow, GtkStyle *style, - const QString &pmKey) -{ - if (!rect.isValid()) - return; - QPixmap cache; - QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size()) % pmKey; - if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) { - DRAW_TO_CACHE(QGtk2PainterPrivate::gtk_paint_flat_box (style, - pixmap, - state, - shadow, - NULL, - gtkWidget, - part, 0, 0, - rect.width(), - rect.height())); - if (m_usePixmapCache) - QPixmapCache::insert(pixmapName, cache); - } - m_painter->drawPixmap(rect.topLeft(), cache); -} - -void QGtk2Painter::paintExtention(GtkWidget *gtkWidget, - const gchar *part, const QRect &rect, - GtkStateType state, GtkShadowType shadow, - GtkPositionType gap_pos, GtkStyle *style) -{ - if (!rect.isValid()) - return; - - QPixmap cache; - QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size(), gtkWidget) - % HexString<uchar>(gap_pos); - - if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) { - DRAW_TO_CACHE(QGtk2PainterPrivate::gtk_paint_extension (style, pixmap, state, shadow, - NULL, gtkWidget, - (const gchar*)part, 0, 0, - rect.width(), - rect.height(), - gap_pos)); - if (m_usePixmapCache) - QPixmapCache::insert(pixmapName, cache); - } - - m_painter->drawPixmap(rect.topLeft(), cache); -} - -void QGtk2Painter::paintOption(GtkWidget *gtkWidget, const QRect &radiorect, - GtkStateType state, GtkShadowType shadow, - GtkStyle *style, const QString &detail) - -{ - QRect rect = m_cliprect.isValid() ? m_cliprect : radiorect; - if (!rect.isValid()) - return; - - QPixmap cache; - QString pixmapName = uniqueName(detail, state, shadow, rect.size()); - GdkRectangle gtkCliprect = {0, 0, rect.width(), rect.height()}; - int xOffset = m_cliprect.isValid() ? radiorect.x() - m_cliprect.x() : 0; - int yOffset = m_cliprect.isValid() ? radiorect.y() - m_cliprect.y() : 0; - if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) { - DRAW_TO_CACHE(QGtk2PainterPrivate::gtk_paint_option(style, pixmap, - state, shadow, - >kCliprect, - gtkWidget, - detail.toLatin1(), - xOffset, yOffset, - radiorect.width(), - radiorect.height())); - - if (m_usePixmapCache) - QPixmapCache::insert(pixmapName, cache); - } - - m_painter->drawPixmap(rect.topLeft(), cache); -} - -void QGtk2Painter::paintCheckbox(GtkWidget *gtkWidget, const QRect &checkrect, - GtkStateType state, GtkShadowType shadow, - GtkStyle *style, const QString &detail) - -{ - QRect rect = m_cliprect.isValid() ? m_cliprect : checkrect; - if (!rect.isValid()) - return; - - QPixmap cache; - QString pixmapName = uniqueName(detail, state, shadow, rect.size()); - GdkRectangle gtkCliprect = {0, 0, rect.width(), rect.height()}; - int xOffset = m_cliprect.isValid() ? checkrect.x() - m_cliprect.x() : 0; - int yOffset = m_cliprect.isValid() ? checkrect.y() - m_cliprect.y() : 0; - if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) { - DRAW_TO_CACHE(QGtk2PainterPrivate::gtk_paint_check (style, - pixmap, - state, - shadow, - >kCliprect, - gtkWidget, - detail.toLatin1(), - xOffset, yOffset, - checkrect.width(), - checkrect.height())); - if (m_usePixmapCache) - QPixmapCache::insert(pixmapName, cache); - } - - m_painter->drawPixmap(rect.topLeft(), cache); -} - -QT_END_NAMESPACE - -#endif //!defined(QT_NO_STYLE_GTK) diff --git a/src/widgets/styles/qgtk2painter_p.h b/src/widgets/styles/qgtk2painter_p.h deleted file mode 100644 index 1e489b5bf9..0000000000 --- a/src/widgets/styles/qgtk2painter_p.h +++ /dev/null @@ -1,100 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QGTK2PAINTER_P_H -#define QGTK2PAINTER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qglobal.h> -#if !defined(QT_NO_STYLE_GTK) - -#include <private/qgtkpainter_p.h> - -QT_BEGIN_NAMESPACE - -class QGtk2Painter : public QGtkPainter -{ -public: - QGtk2Painter(); - - void paintBoxGap(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, - GtkStateType state, GtkShadowType shadow, GtkPositionType gap_side, gint x, - gint width, GtkStyle *style) Q_DECL_OVERRIDE; - void paintBox(GtkWidget *gtkWidget, const gchar* part, - const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, - const QString &pmKey = QString()) Q_DECL_OVERRIDE; - void paintHline(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style, - int x1, int x2, int y, const QString &pmKey = QString()) Q_DECL_OVERRIDE; - void paintVline(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style, - int y1, int y2, int x, const QString &pmKey = QString()) Q_DECL_OVERRIDE; - void paintExpander(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, - GtkExpanderStyle expander_state, GtkStyle *style, const QString &pmKey = QString()) Q_DECL_OVERRIDE; - void paintFocus(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style, - const QString &pmKey = QString()) Q_DECL_OVERRIDE; - void paintResizeGrip(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, - GdkWindowEdge edge, GtkStyle *style, const QString &pmKey = QString()) Q_DECL_OVERRIDE; - void paintArrow(GtkWidget *gtkWidget, const gchar* part, const QRect &arrowrect, GtkArrowType arrow_type, GtkStateType state, GtkShadowType shadow, - gboolean fill, GtkStyle *style, const QString &pmKey = QString()) Q_DECL_OVERRIDE; - void paintHandle(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, - GtkStateType state, GtkShadowType shadow, GtkOrientation orientation, GtkStyle *style) Q_DECL_OVERRIDE; - void paintSlider(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, - GtkStyle *style, GtkOrientation orientation, const QString &pmKey = QString()) Q_DECL_OVERRIDE; - void paintShadow(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, - GtkStyle *style, const QString &pmKey = QString()) Q_DECL_OVERRIDE; - void paintFlatBox(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString & = QString()) Q_DECL_OVERRIDE; - void paintExtention(GtkWidget *gtkWidget, const gchar *part, const QRect &rect, GtkStateType state, GtkShadowType shadow, - GtkPositionType gap_pos, GtkStyle *style) Q_DECL_OVERRIDE; - void paintOption(GtkWidget *gtkWidget, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString &detail) Q_DECL_OVERRIDE; - void paintCheckbox(GtkWidget *gtkWidget, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString &detail) Q_DECL_OVERRIDE; - -private: - QPixmap renderTheme(uchar *bdata, uchar *wdata, const QRect &rect) const; - - GtkWidget *m_window; -}; - -QT_END_NAMESPACE - -#endif //!defined(QT_NO_STYLE_QGTK) - -#endif // QGTK2PAINTER_P_H diff --git a/src/widgets/styles/qgtkpainter_p.h b/src/widgets/styles/qgtkpainter_p.h deleted file mode 100644 index bfe97ccaef..0000000000 --- a/src/widgets/styles/qgtkpainter_p.h +++ /dev/null @@ -1,119 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QGTKPAINTER_H -#define QGTKPAINTER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qglobal.h> -#if !defined(QT_NO_STYLE_GTK) - -#include <private/qgtkglobal_p.h> -#include <QtCore/qsize.h> -#include <QtCore/qrect.h> -#include <QtCore/qpoint.h> -#include <QtGui/qpixmap.h> -#include <QtGui/qpainter.h> - -QT_BEGIN_NAMESPACE - -class QGtkPainter -{ -public: - QGtkPainter(); - virtual ~QGtkPainter(); - - void reset(QPainter *painter = 0); - - void setAlphaSupport(bool value) { m_alpha = value; } - void setClipRect(const QRect &rect) { m_cliprect = rect; } - void setFlipHorizontal(bool value) { m_hflipped = value; } - void setFlipVertical(bool value) { m_vflipped = value; } - void setUsePixmapCache(bool value) { m_usePixmapCache = value; } - - virtual void paintBoxGap(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, - GtkStateType state, GtkShadowType shadow, GtkPositionType gap_side, gint x, - gint width, GtkStyle *style) = 0; - virtual void paintBox(GtkWidget *gtkWidget, const gchar* part, - const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, - const QString &pmKey = QString()) = 0; - virtual void paintHline(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style, - int x1, int x2, int y, const QString &pmKey = QString()) = 0; - virtual void paintVline(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style, - int y1, int y2, int x, const QString &pmKey = QString()) = 0; - virtual void paintExpander(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, - GtkExpanderStyle expander_state, GtkStyle *style, const QString &pmKey = QString()) = 0; - virtual void paintFocus(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style, - const QString &pmKey = QString()) = 0; - virtual void paintResizeGrip(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, - GdkWindowEdge edge, GtkStyle *style, const QString &pmKey = QString()) = 0; - virtual void paintArrow(GtkWidget *gtkWidget, const gchar* part, const QRect &arrowrect, GtkArrowType arrow_type, GtkStateType state, GtkShadowType shadow, - gboolean fill, GtkStyle *style, const QString &pmKey = QString()) = 0; - virtual void paintHandle(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, - GtkStateType state, GtkShadowType shadow, GtkOrientation orientation, GtkStyle *style) = 0; - virtual void paintSlider(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, - GtkStyle *style, GtkOrientation orientation, const QString &pmKey = QString()) = 0; - virtual void paintShadow(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, - GtkStyle *style, const QString &pmKey = QString()) = 0; - virtual void paintFlatBox(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString & = QString()) = 0; - virtual void paintExtention(GtkWidget *gtkWidget, const gchar *part, const QRect &rect, GtkStateType state, GtkShadowType shadow, - GtkPositionType gap_pos, GtkStyle *style) = 0; - virtual void paintOption(GtkWidget *gtkWidget, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString &detail) = 0; - virtual void paintCheckbox(GtkWidget *gtkWidget, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString &detail) = 0; - -protected: - static QString uniqueName(const QString &key, GtkStateType state, GtkShadowType shadow, const QSize &size, GtkWidget *widget = 0); - - QPainter *m_painter; - bool m_alpha; - bool m_hflipped; - bool m_vflipped; - bool m_usePixmapCache; - QRect m_cliprect; -}; - -QT_END_NAMESPACE - -#endif //!defined(QT_NO_STYLE_QGTK) - -#endif // QGTKPAINTER_H diff --git a/src/widgets/styles/qgtkstyle.cpp b/src/widgets/styles/qgtkstyle.cpp deleted file mode 100644 index 7ed0dce91d..0000000000 --- a/src/widgets/styles/qgtkstyle.cpp +++ /dev/null @@ -1,4257 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "qgtkstyle_p.h" - -#if !defined(QT_NO_STYLE_GTK) - -#include <private/qapplication_p.h> -#include <QtCore/QLibrary> -#include <QtCore/QSettings> -#include <QtWidgets/QDialogButtonBox> -#include <QtWidgets/QStatusBar> -#include <QtWidgets/QLineEdit> -#include <QtWidgets/QWidget> -#include <QtWidgets/QListView> -#include <QtWidgets/QApplication> -#include <QtWidgets/QStyleOption> -#include <QtWidgets/QPushButton> -#include <QtGui/QPainter> -#include <QtWidgets/QMainWindow> -#include <QtWidgets/QToolBar> -#include <QtWidgets/QHeaderView> -#include <QtWidgets/QMenuBar> -#include <QtWidgets/QComboBox> -#include <QtWidgets/QSpinBox> -#include <QtWidgets/QScrollBar> -#include <QtWidgets/QAbstractButton> -#include <QtWidgets/QToolButton> -#include <QtWidgets/QGroupBox> -#include <QtWidgets/QRadioButton> -#include <QtWidgets/QCheckBox> -#include <QtWidgets/QTreeView> -#include <QtWidgets/QStyledItemDelegate> -#include <QtWidgets/QWizard> - -#include <qpixmapcache.h> -#include <private/qstyleanimation_p.h> -#undef signals // Collides with GTK stymbols -#include <private/qgtkpainter_p.h> -#include <private/qstylehelper_p.h> -#include <private/qgtkstyle_p_p.h> - -QT_BEGIN_NAMESPACE - -static GtkStateType qt_gtk_state(const QStyleOption *option) -{ - GtkStateType state = GTK_STATE_NORMAL; - if (!(option->state & QStyle::State_Enabled)) - state = GTK_STATE_INSENSITIVE; - else if (option->state & QStyle::State_MouseOver) - state = GTK_STATE_PRELIGHT; - - return state; -} - -static QPixmap qt_gtk_get_icon(const char* iconName, GtkIconSize size = GTK_ICON_SIZE_BUTTON) -{ - GtkStyle *style = QGtkStylePrivate::gtkStyle(); - GtkIconSet* iconSet = QGtkStylePrivate::gtk_icon_factory_lookup_default (iconName); - GdkPixbuf* icon = QGtkStylePrivate::gtk_icon_set_render_icon(iconSet, - style, - GTK_TEXT_DIR_LTR, - GTK_STATE_NORMAL, - size, - NULL, - "button"); - uchar* data = (uchar*)QGtkStylePrivate::gdk_pixbuf_get_pixels(icon); - int width = QGtkStylePrivate::gdk_pixbuf_get_width(icon); - int height = QGtkStylePrivate::gdk_pixbuf_get_height(icon); - QImage converted(width, height, QImage::Format_ARGB32); - uchar* tdata = (uchar*)converted.bits(); - - for ( int index = 0 ; index < height * width*4 ; index +=4 ) { - //int index = y * rowstride + x; - tdata[index + QT_RED] = data[index + GTK_RED]; - tdata[index + QT_GREEN] = data[index + GTK_GREEN]; - tdata[index + QT_BLUE] = data[index + GTK_BLUE]; - tdata[index + QT_ALPHA] = data[index + GTK_ALPHA]; - } - - QGtkStylePrivate::gdk_pixbuf_unref(icon); - - // should we free iconset? - return QPixmap::fromImage(converted); -} - -static void qt_gtk_draw_mdibutton(QPainter *painter, const QStyleOptionTitleBar *option, const QRect &tmp, bool hover, bool sunken) -{ - QColor dark; - dark.setHsv(option->palette.button().color().hue(), - qMin(255, (int)(option->palette.button().color().saturation()*1.9)), - qMin(255, (int)(option->palette.button().color().value()*0.7))); - - QColor highlight = option->palette.highlight().color(); - - bool active = (option->titleBarState & QStyle::State_Active); - QColor titleBarHighlight(255, 255, 255, 60); - - if (sunken) - painter->fillRect(tmp.adjusted(1, 1, -1, -1), option->palette.highlight().color().darker(120)); - else if (hover) - painter->fillRect(tmp.adjusted(1, 1, -1, -1), QColor(255, 255, 255, 20)); - - QColor mdiButtonGradientStartColor; - QColor mdiButtonGradientStopColor; - - mdiButtonGradientStartColor = QColor(0, 0, 0, 40); - mdiButtonGradientStopColor = QColor(255, 255, 255, 60); - - if (sunken) - titleBarHighlight = highlight.darker(130); - - QLinearGradient gradient(tmp.center().x(), tmp.top(), tmp.center().x(), tmp.bottom()); - gradient.setColorAt(0, mdiButtonGradientStartColor); - gradient.setColorAt(1, mdiButtonGradientStopColor); - QColor mdiButtonBorderColor(active ? option->palette.highlight().color().darker(180): dark.darker(110)); - - painter->setPen(QPen(mdiButtonBorderColor, 1)); - const QLine lines[4] = { - QLine(tmp.left() + 2, tmp.top(), tmp.right() - 2, tmp.top()), - QLine(tmp.left() + 2, tmp.bottom(), tmp.right() - 2, tmp.bottom()), - QLine(tmp.left(), tmp.top() + 2, tmp.left(), tmp.bottom() - 2), - QLine(tmp.right(), tmp.top() + 2, tmp.right(), tmp.bottom() - 2) - }; - painter->drawLines(lines, 4); - const QPoint points[4] = { - QPoint(tmp.left() + 1, tmp.top() + 1), - QPoint(tmp.right() - 1, tmp.top() + 1), - QPoint(tmp.left() + 1, tmp.bottom() - 1), - QPoint(tmp.right() - 1, tmp.bottom() - 1) - }; - painter->drawPoints(points, 4); - - painter->setPen(titleBarHighlight); - painter->drawLine(tmp.left() + 2, tmp.top() + 1, tmp.right() - 2, tmp.top() + 1); - painter->drawLine(tmp.left() + 1, tmp.top() + 2, tmp.left() + 1, tmp.bottom() - 2); - - painter->setPen(QPen(gradient, 1)); - painter->drawLine(tmp.right() + 1, tmp.top() + 2, tmp.right() + 1, tmp.bottom() - 2); - painter->drawPoint(tmp.right() , tmp.top() + 1); - - painter->drawLine(tmp.left() + 2, tmp.bottom() + 1, tmp.right() - 2, tmp.bottom() + 1); - painter->drawPoint(tmp.left() + 1, tmp.bottom()); - painter->drawPoint(tmp.right() - 1, tmp.bottom()); - painter->drawPoint(tmp.right() , tmp.bottom() - 1); -} - -static const char * const dock_widget_close_xpm[] = - { - "11 13 5 1", - " c None", - ". c #D5CFCB", - "+ c #6C6A67", - "@ c #6C6A67", - "$ c #B5B0AC", - " ", - " @@@@@@@@@ ", - "@+ +@", - "@ +@ @+ @", - "@ @@@ @@@ @", - "@ @@@@@ @", - "@ @@@ @", - "@ @@@@@ @", - "@ @@@ @@@ @", - "@ +@ @+ @", - "@+ +@", - " @@@@@@@@@ ", - " " - }; - -static const char * const dock_widget_restore_xpm[] = - { - "11 13 5 1", - " c None", - ". c #D5CFCB", - "+ c #6C6A67", - "@ c #6C6A67", - "# c #6C6A67", - " ", - " @@@@@@@@@ ", - "@+ +@", - "@ #@@@# @", - "@ @ @ @", - "@ #@@@# @ @", - "@ @ @ @ @", - "@ @ @@@ @", - "@ @ @ @", - "@ #@@@@ @", - "@+ +@", - " @@@@@@@@@ ", - " " - }; - -static const char * const qt_titlebar_context_help[] = { - "10 10 3 1", - " c None", - "# c #000000", - "+ c #444444", - " +####+ ", - " ### ### ", - " ## ## ", - " +##+ ", - " +## ", - " ## ", - " ## ", - " ", - " ## ", - " ## "}; - -static const char * const qt_scrollbar_button_arrow_up[] = { - "7 4 2 1", - " c None", - "* c #BFBFBF", - " * ", - " *** ", - " ***** ", - "*******"}; - -static const char * const qt_scrollbar_button_arrow_down[] = { - "7 4 2 1", - " c None", - "* c #BFBFBF", - "*******", - " ***** ", - " *** ", - " * "}; - -static const int groupBoxBottomMargin = 2; // space below the groupbox -static const int groupBoxTitleMargin = 6; // space between contents and title -static const int groupBoxTopMargin = 2; - -/*! - Returns the configuration string for \a value. - Returns \a fallback if \a value is not found. - */ -QString QGtkStyle::getGConfString(const QString &value, const QString &fallback) -{ - return QGtkStylePrivate::getGConfString(value, fallback); -} - -/*! - Returns the configuration boolean for \a key. - Returns \a fallback if \a key is not found. - */ -bool QGtkStyle::getGConfBool(const QString &key, bool fallback) -{ - return QGtkStylePrivate::getGConfBool(key, fallback); -} - -static QColor mergedColors(const QColor &colorA, const QColor &colorB, int factor = 50) -{ - const int maxFactor = 100; - QColor tmp = colorA; - tmp.setRed((tmp.red() * factor) / maxFactor + (colorB.red() * (maxFactor - factor)) / maxFactor); - tmp.setGreen((tmp.green() * factor) / maxFactor + (colorB.green() * (maxFactor - factor)) / maxFactor); - tmp.setBlue((tmp.blue() * factor) / maxFactor + (colorB.blue() * (maxFactor - factor)) / maxFactor); - return tmp; -} - -static GdkColor fromQColor(const QColor &color) -{ - GdkColor retval; - retval.pixel = 0; - retval.red = color.red() * 255; - retval.green = color.green() * 255; - retval.blue = color.blue() * 255; - return retval; -} - -/*! - \class QGtkStyle - \brief The QGtkStyle class provides a widget style rendered by GTK+ - \since 4.5 - - \internal - \inmodule QtWidgets - - The QGtkStyle style provides a look and feel that integrates well - into GTK-based desktop environments such as the XFCe and GNOME. - - It does this by making use of the GTK+ theme engine, ensuring - that Qt applications look and feel native on these platforms. - - Note: The style requires GTK+ version 2.18 or later. - The Qt3-based "Qt" GTK+ theme engine will not work with QGtkStyle. - - \sa QWindowsXPStyle, QMacStyle, QWindowsStyle, QFusionStyle -*/ - -/*! - Constructs a QGtkStyle object. -*/ -QGtkStyle::QGtkStyle() - : QCommonStyle(*new QGtkStylePrivate) -{ - Q_D(QGtkStyle); - d->init(); -} - -/*! - \internal - - Constructs a QGtkStyle object. -*/ -QGtkStyle::QGtkStyle(QGtkStylePrivate &dd) - : QCommonStyle(dd) -{ - Q_D(QGtkStyle); - d->init(); -} - - -/*! - Destroys the QGtkStyle object. -*/ -QGtkStyle::~QGtkStyle() -{ -} - -/*! - \reimp -*/ -QPalette QGtkStyle::standardPalette() const -{ - Q_D(const QGtkStyle); - - QPalette palette = QCommonStyle::standardPalette(); - if (d->isThemeAvailable()) { - GtkStyle *style = d->gtkStyle(); - GtkWidget *gtkButton = d->gtkWidget("GtkButton"); - GtkWidget *gtkEntry = d->getTextColorWidget(); - GdkColor gdkBg, gdkBase, gdkText, gdkForeground, gdkSbg, gdkSfg, gdkaSbg, gdkaSfg; - QColor bg, base, text, fg, highlight, highlightText, inactiveHighlight, inactiveHighlightedTExt; - gdkBg = style->bg[GTK_STATE_NORMAL]; - gdkForeground = d->gtk_widget_get_style(gtkButton)->fg[GTK_STATE_NORMAL]; - - // Our base and selected color is primarily used for text - // so we assume a gtkEntry will have the most correct value - GtkStyle *gtkEntryStyle = d->gtk_widget_get_style(gtkEntry); - gdkBase = gtkEntryStyle->base[GTK_STATE_NORMAL]; - gdkText = gtkEntryStyle->text[GTK_STATE_NORMAL]; - gdkSbg = gtkEntryStyle->base[GTK_STATE_SELECTED]; - gdkSfg = gtkEntryStyle->text[GTK_STATE_SELECTED]; - - // The ACTIVE base color is really used for inactive windows - gdkaSbg = gtkEntryStyle->base[GTK_STATE_ACTIVE]; - gdkaSfg = gtkEntryStyle->text[GTK_STATE_ACTIVE]; - - bg = QColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8); - text = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); - fg = QColor(gdkForeground.red>>8, gdkForeground.green>>8, gdkForeground.blue>>8); - base = QColor(gdkBase.red>>8, gdkBase.green>>8, gdkBase.blue>>8); - highlight = QColor(gdkSbg.red>>8, gdkSbg.green>>8, gdkSbg.blue>>8); - highlightText = QColor(gdkSfg.red>>8, gdkSfg.green>>8, gdkSfg.blue>>8); - inactiveHighlight = QColor(gdkaSbg.red>>8, gdkaSbg.green>>8, gdkaSbg.blue>>8); - inactiveHighlightedTExt = QColor(gdkaSfg.red>>8, gdkaSfg.green>>8, gdkaSfg.blue>>8); - - palette.setColor(QPalette::HighlightedText, highlightText); - - - palette.setColor(QPalette::Light, bg.lighter(125)); - palette.setColor(QPalette::Shadow, bg.darker(130)); - palette.setColor(QPalette::Dark, bg.darker(120)); - palette.setColor(QPalette::Text, text); - palette.setColor(QPalette::WindowText, fg); - palette.setColor(QPalette::ButtonText, fg); - palette.setColor(QPalette::Base, base); - - QColor alternateRowColor = palette.base().color().lighter(93); // ref gtkstyle.c draw_flat_box - GtkWidget *gtkTreeView = d->gtkWidget("GtkTreeView"); - GdkColor *gtkAltBase = NULL; - d->gtk_widget_style_get(gtkTreeView, "odd-row-color", >kAltBase, NULL); - if (gtkAltBase) { - alternateRowColor = QColor(gtkAltBase->red>>8, gtkAltBase->green>>8, gtkAltBase->blue>>8); - d->gdk_color_free(gtkAltBase); - } - palette.setColor(QPalette::AlternateBase, alternateRowColor); - - palette.setColor(QPalette::Window, bg); - palette.setColor(QPalette::Button, bg); - palette.setColor(QPalette::Background, bg); - QColor disabled((fg.red() + bg.red()) / 2, - (fg.green() + bg.green())/ 2, - (fg.blue() + bg.blue()) / 2); - palette.setColor(QPalette::Disabled, QPalette::Text, disabled); - palette.setColor(QPalette::Disabled, QPalette::WindowText, disabled); - palette.setColor(QPalette::Disabled, QPalette::Foreground, disabled); - palette.setColor(QPalette::Disabled, QPalette::ButtonText, disabled); - palette.setColor(QPalette::Highlight, highlight); - // calculate disabled colors by removing saturation - highlight.setHsv(highlight.hue(), 0, highlight.value(), highlight.alpha()); - highlightText.setHsv(highlightText.hue(), 0, highlightText.value(), highlightText.alpha()); - palette.setColor(QPalette::Disabled, QPalette::Highlight, highlight); - palette.setColor(QPalette::Disabled, QPalette::HighlightedText, highlightText); - - palette.setColor(QPalette::Inactive, QPalette::HighlightedText, inactiveHighlightedTExt); - palette.setColor(QPalette::Inactive, QPalette::Highlight, inactiveHighlight); - - style = d->gtk_rc_get_style_by_paths(d->gtk_settings_get_default(), "gtk-tooltips", "GtkWindow", - d->gtk_window_get_type()); - if (style) { - gdkText = style->fg[GTK_STATE_NORMAL]; - text = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); - palette.setColor(QPalette::ToolTipText, text); - } - } - return palette; -} - -/*! - \reimp -*/ -void QGtkStyle::polish(QPalette &palette) -{ - Q_D(QGtkStyle); - - if (!d->isThemeAvailable()) - QCommonStyle::polish(palette); - else - palette = palette.resolve(standardPalette()); -} - -/*! - \reimp -*/ -void QGtkStyle::polish(QApplication *app) -{ - Q_D(QGtkStyle); - - QCommonStyle::polish(app); - // Custom fonts and palettes with QtConfig are intentionally - // not supported as these should be entirely determined by - // current Gtk settings - if (app->desktopSettingsAware() && d->isThemeAvailable()) { - QApplicationPrivate::setSystemPalette(standardPalette()); - QApplicationPrivate::setSystemFont(d->getThemeFont()); - d->applyCustomPaletteHash(); - if (!d->isKDE4Session()) - qApp->installEventFilter(&d->filter); - } -} - -/*! - \reimp -*/ -void QGtkStyle::unpolish(QApplication *app) -{ - Q_D(QGtkStyle); - - QCommonStyle::unpolish(app); - QPixmapCache::clear(); - - if (app->desktopSettingsAware() && d->isThemeAvailable() && !d->isKDE4Session()) - qApp->removeEventFilter(&d->filter); -} - -/*! - \reimp -*/ - -void QGtkStyle::polish(QWidget *widget) -{ - Q_D(QGtkStyle); - - QCommonStyle::polish(widget); - if (!d->isThemeAvailable()) - return; - if (qobject_cast<QAbstractButton*>(widget) - || qobject_cast<QToolButton*>(widget) - || qobject_cast<QComboBox*>(widget) - || qobject_cast<QGroupBox*>(widget) - || qobject_cast<QScrollBar*>(widget) - || qobject_cast<QSlider*>(widget) - || qobject_cast<QAbstractSpinBox*>(widget) - || qobject_cast<QSpinBox*>(widget) - || qobject_cast<QHeaderView*>(widget)) - widget->setAttribute(Qt::WA_Hover); -#ifndef QT_NO_TREEVIEW - else if (QTreeView *tree = qobject_cast<QTreeView *> (widget)) - tree->viewport()->setAttribute(Qt::WA_Hover); -#endif -} - -/*! - \reimp -*/ -void QGtkStyle::unpolish(QWidget *widget) -{ - QCommonStyle::unpolish(widget); -} - -/*! - \reimp -*/ -int QGtkStyle::pixelMetric(PixelMetric metric, - const QStyleOption *option, - const QWidget *widget) const -{ - Q_D(const QGtkStyle); - - if (!d->isThemeAvailable()) - return QCommonStyle::pixelMetric(metric, option, widget); - - switch (metric) { - case PM_DefaultFrameWidth: - if (qobject_cast<const QFrame*>(widget)) { - if (GtkStyle *style = - d->gtk_rc_get_style_by_paths(d->gtk_settings_get_default(), - "*.GtkScrolledWindow", - "*.GtkScrolledWindow", - d->gtk_window_get_type())) - return qMax(style->xthickness, style->ythickness); - } - return 2; - - case PM_MenuButtonIndicator: - return 20; - - case PM_TabBarBaseOverlap: - return 1; - - case PM_ToolBarSeparatorExtent: - return 11; - - case PM_ToolBarFrameWidth: - return 1; - - case PM_ToolBarItemSpacing: - return 0; - - case PM_ButtonShiftHorizontal: { - GtkWidget *gtkButton = d->gtkWidget("GtkButton"); - guint horizontal_shift; - d->gtk_widget_style_get(gtkButton, "child-displacement-x", &horizontal_shift, NULL); - return horizontal_shift; - } - - case PM_ButtonShiftVertical: { - GtkWidget *gtkButton = d->gtkWidget("GtkButton"); - guint vertical_shift; - d->gtk_widget_style_get(gtkButton, "child-displacement-y", &vertical_shift, NULL); - return vertical_shift; - } - - case PM_MenuBarPanelWidth: - return 0; - - case PM_MenuPanelWidth: { - GtkWidget *gtkMenu = d->gtkWidget("GtkMenu"); - guint horizontal_padding = 0; - // horizontal-padding is used by Maemo to get thicker borders - if (!d->gtk_check_version(2, 10, 0)) - d->gtk_widget_style_get(gtkMenu, "horizontal-padding", &horizontal_padding, NULL); - int padding = qMax<int>(d->gtk_widget_get_style(gtkMenu)->xthickness, horizontal_padding); - return padding; - } - - case PM_ButtonIconSize: { - int retVal = 24; - GtkSettings *settings = d->gtk_settings_get_default(); - gchararray icon_sizes; - g_object_get(settings, "gtk-icon-sizes", &icon_sizes, NULL); - QStringList values = QString(QLS(icon_sizes)).split(QLatin1Char(':')); - g_free(icon_sizes); - QChar splitChar(QLatin1Char(',')); - foreach (const QString &value, values) { - if (value.startsWith(QLS("gtk-button="))) { - QString iconSize = value.right(value.size() - 11); - - if (iconSize.contains(splitChar)) - retVal = iconSize.split(splitChar)[0].toInt(); - break; - } - } - return retVal; - } - - case PM_MenuVMargin: - - case PM_MenuHMargin: - return 0; - - case PM_DockWidgetTitleMargin: - return 0; - - case PM_DockWidgetTitleBarButtonMargin: - return 5; - - case PM_TabBarTabVSpace: - return 12; - - case PM_TabBarTabHSpace: - return 14; - - case PM_TabBarTabShiftVertical: - return 2; - - case PM_ToolBarHandleExtent: - return 9; - - case PM_SplitterWidth: - return 6; - - case PM_SliderThickness: - case PM_SliderControlThickness: { - GtkWidget *gtkScale = d->gtkWidget("GtkHScale"); - gint val; - d->gtk_widget_style_get(gtkScale, "slider-width", &val, NULL); - if (metric == PM_SliderControlThickness) - return val + 2*d->gtk_widget_get_style(gtkScale)->ythickness; - return val; - } - - case PM_ScrollBarExtent: { - gint sliderLength; - gint trough_border; - GtkWidget *hScrollbar = d->gtkWidget("GtkHScrollbar"); - d->gtk_widget_style_get(hScrollbar, - "trough-border", &trough_border, - "slider-width", &sliderLength, - NULL); - return sliderLength + trough_border*2; - } - - case PM_ScrollBarSliderMin: - return 34; - - case PM_SliderLength: - gint val; - d->gtk_widget_style_get(d->gtkWidget("GtkHScale"), "slider-length", &val, NULL); - return val; - - case PM_ExclusiveIndicatorWidth: - case PM_ExclusiveIndicatorHeight: - case PM_IndicatorWidth: - case PM_IndicatorHeight: { - GtkWidget *gtkCheckButton = d->gtkWidget("GtkCheckButton"); - gint size, spacing; - d->gtk_widget_style_get(gtkCheckButton, "indicator-spacing", &spacing, "indicator-size", &size, NULL); - return size + 2 * spacing; - } - - case PM_MenuBarVMargin: { - GtkWidget *gtkMenubar = d->gtkWidget("GtkMenuBar"); - return qMax(0, d->gtk_widget_get_style(gtkMenubar)->ythickness); - } - case PM_ScrollView_ScrollBarSpacing: - { - gint spacing = 3; - GtkWidget *gtkScrollWindow = d->gtkWidget("GtkScrolledWindow"); - Q_ASSERT(gtkScrollWindow); - d->gtk_widget_style_get(gtkScrollWindow, "scrollbar-spacing", &spacing, NULL); - return spacing; - } - case PM_SubMenuOverlap: { - gint offset = 0; - GtkWidget *gtkMenu = d->gtkWidget("GtkMenu"); - d->gtk_widget_style_get(gtkMenu, "horizontal-offset", &offset, NULL); - return offset; - } - case PM_ToolTipLabelFrameWidth: - return 2; - case PM_ButtonDefaultIndicator: - return 0; - case PM_ListViewIconSize: - return 24; - case PM_DialogButtonsSeparator: - return 6; - case PM_TitleBarHeight: - return 24; - case PM_SpinBoxFrameWidth: - return 3; - case PM_MenuBarItemSpacing: - return 6; - case PM_MenuBarHMargin: - return 0; - case PM_ToolBarItemMargin: - return 1; - case PM_SmallIconSize: - return 16; - case PM_MaximumDragDistance: - return -1; - case PM_TabCloseIndicatorWidth: - case PM_TabCloseIndicatorHeight: - return 20; - default: - return QCommonStyle::pixelMetric(metric, option, widget); - } -} - -/*! - \reimp -*/ -int QGtkStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, - - QStyleHintReturn *returnData = 0) const -{ - Q_D(const QGtkStyle); - - if (!d->isThemeAvailable()) - return QCommonStyle::styleHint(hint, option, widget, returnData); - - switch (hint) { - case SH_ItemView_ChangeHighlightOnFocus: - return true; - case SH_ScrollBar_MiddleClickAbsolutePosition: - return true; - case SH_Menu_AllowActiveAndDisabled: - return false; - case SH_MainWindow_SpaceBelowMenuBar: - return false; - case SH_MenuBar_MouseTracking: - return true; - case SH_Menu_MouseTracking: - return true; - case SH_TitleBar_AutoRaise: - return true; - case SH_TitleBar_NoBorder: - return true; - case SH_ItemView_ShowDecorationSelected: - return true; - case SH_Table_GridLineColor: - if (option) - return option->palette.background().color().darker(120).rgb(); - break; - case SH_WindowFrame_Mask: - if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask *>(returnData)) { - //left rounded corner - mask->region = option->rect; - mask->region -= QRect(option->rect.left(), option->rect.top(), 5, 1); - mask->region -= QRect(option->rect.left(), option->rect.top() + 1, 3, 1); - mask->region -= QRect(option->rect.left(), option->rect.top() + 2, 2, 1); - mask->region -= QRect(option->rect.left(), option->rect.top() + 3, 1, 2); - - //right rounded corner - mask->region -= QRect(option->rect.right() - 4, option->rect.top(), 5, 1); - mask->region -= QRect(option->rect.right() - 2, option->rect.top() + 1, 3, 1); - mask->region -= QRect(option->rect.right() - 1, option->rect.top() + 2, 2, 1); - mask->region -= QRect(option->rect.right() , option->rect.top() + 3, 1, 2); - } - return QCommonStyle::styleHint(hint, option, widget, returnData); - case SH_MessageBox_TextInteractionFlags: - return Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse; - case SH_MessageBox_CenterButtons: - return false; -#ifndef QT_NO_WIZARD - case SH_WizardStyle: - return QWizard::ClassicStyle; -#endif - case SH_ItemView_ArrowKeysNavigateIntoChildren: - return false; - case SH_DialogButtonLayout: { - int ret = QDialogButtonBox::GnomeLayout; - gboolean alternateOrder = 0; - GtkSettings *settings = d->gtk_settings_get_default(); - g_object_get(settings, "gtk-alternative-button-order", &alternateOrder, NULL); - - if (alternateOrder) - ret = QDialogButtonBox::WinLayout; - - return ret; - } - break; - - case SH_ToolButtonStyle: - { - if (d->isKDE4Session()) - return QCommonStyle::styleHint(hint, option, widget, returnData); - GtkWidget *gtkToolbar = d->gtkWidget("GtkToolbar"); - GtkToolbarStyle toolbar_style = GTK_TOOLBAR_ICONS; - g_object_get(gtkToolbar, "toolbar-style", &toolbar_style, NULL); - switch (toolbar_style) { - case GTK_TOOLBAR_TEXT: - return Qt::ToolButtonTextOnly; - case GTK_TOOLBAR_BOTH: - return Qt::ToolButtonTextUnderIcon; - case GTK_TOOLBAR_BOTH_HORIZ: - return Qt::ToolButtonTextBesideIcon; - case GTK_TOOLBAR_ICONS: - default: - return Qt::ToolButtonIconOnly; - } - } - break; - case SH_SpinControls_DisableOnBounds: - return int(true); - - case SH_DitherDisabledText: - return int(false); - - case SH_ComboBox_Popup: { - GtkWidget *gtkComboBox = d->gtkWidget("GtkComboBox"); - gboolean appears_as_list; - d->gtk_widget_style_get((GtkWidget*)gtkComboBox, "appears-as-list", &appears_as_list, NULL); - return appears_as_list ? 0 : 1; - } - - case SH_MenuBar_AltKeyNavigation: - return int(false); - - case SH_EtchDisabledText: - return int(false); - - case SH_Menu_SubMenuPopupDelay: { - gint delay = 225; - GtkSettings *settings = d->gtk_settings_get_default(); - g_object_get(settings, "gtk-menu-popup-delay", &delay, NULL); - return delay; - } - - case SH_ScrollView_FrameOnlyAroundContents: { - gboolean scrollbars_within_bevel = false; - if (widget && widget->isWindow()) - scrollbars_within_bevel = true; - else if (!d->gtk_check_version(2, 12, 0)) { - GtkWidget *gtkScrollWindow = d->gtkWidget("GtkScrolledWindow"); - d->gtk_widget_style_get(gtkScrollWindow, "scrollbars-within-bevel", &scrollbars_within_bevel, NULL); - } - return !scrollbars_within_bevel; - } - - case SH_DialogButtonBox_ButtonsHaveIcons: { - static bool buttonsHaveIcons = d->getGConfBool(QLS("/desktop/gnome/interface/buttons_have_icons")); - return buttonsHaveIcons; - } - - case SH_UnderlineShortcut: { - gboolean underlineShortcut = true; - if (!d->gtk_check_version(2, 12, 0)) { - GtkSettings *settings = d->gtk_settings_get_default(); - g_object_get(settings, "gtk-enable-mnemonics", &underlineShortcut, NULL); - } - return underlineShortcut; - } - - default: - break; - } - return QCommonStyle::styleHint(hint, option, widget, returnData); -} - -/*! - \reimp -*/ -void QGtkStyle::drawPrimitive(PrimitiveElement element, - const QStyleOption *option, - QPainter *painter, - const QWidget *widget) const -{ - Q_D(const QGtkStyle); - - if (!d->isThemeAvailable()) { - QCommonStyle::drawPrimitive(element, option, painter, widget); - return; - } - - GtkStyle* style = d->gtkStyle(); - QGtkPainter* gtkPainter = d->gtkPainter(painter); - - switch (element) { - case PE_Frame: { - if (widget && widget->inherits("QComboBoxPrivateContainer")){ - QStyleOption copy = *option; - copy.state |= State_Raised; - proxy()->drawPrimitive(PE_PanelMenu, ©, painter, widget); - break; - } - // Drawing the entire itemview frame is very expensive, especially on the native X11 engine - // Instead we cheat a bit and draw a border image without the center part, hence only scaling - // thin rectangular images - const int pmSize = 64; - const int border = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget); - const QString pmKey = QLatin1String("windowframe") % HexString<uint>(option->state); - - QPixmap pixmap; - QRect pmRect(QPoint(0,0), QSize(pmSize, pmSize)); - - // Only draw through style once - if (!QPixmapCache::find(pmKey, pixmap)) { - pixmap = QPixmap(pmSize, pmSize); - pixmap.fill(Qt::transparent); - QPainter pmPainter(&pixmap); - gtkPainter->reset(&pmPainter); - gtkPainter->setUsePixmapCache(false); // Don't cache twice - - GtkShadowType shadow_type = GTK_SHADOW_NONE; - if (option->state & State_Sunken) - shadow_type = GTK_SHADOW_IN; - else if (option->state & State_Raised) - shadow_type = GTK_SHADOW_OUT; - - GtkStyle *style = d->gtk_rc_get_style_by_paths(d->gtk_settings_get_default(), - "*.GtkScrolledWindow", "*.GtkScrolledWindow", d->gtk_window_get_type()); - if (style) - gtkPainter->paintShadow(d->gtkWidget("GtkFrame"), "viewport", pmRect, - option->state & State_Enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE, - shadow_type, style); - QPixmapCache::insert(pmKey, pixmap); - gtkPainter->reset(painter); - } - - QRect rect = option->rect; - const int rw = rect.width() - border; - const int rh = rect.height() - border; - const int pw = pmRect.width() - border; - const int ph = pmRect.height() - border; - - // Sidelines - painter->drawPixmap(rect.adjusted(border, 0, -border, -rh), pixmap, pmRect.adjusted(border, 0, -border,-ph)); - painter->drawPixmap(rect.adjusted(border, rh, -border, 0), pixmap, pmRect.adjusted(border, ph,-border,0)); - painter->drawPixmap(rect.adjusted(0, border, -rw, -border), pixmap, pmRect.adjusted(0, border, -pw, -border)); - painter->drawPixmap(rect.adjusted(rw, border, 0, -border), pixmap, pmRect.adjusted(pw, border, 0, -border)); - - // Corners - painter->drawPixmap(rect.adjusted(0, 0, -rw, -rh), pixmap, pmRect.adjusted(0, 0, -pw,-ph)); - painter->drawPixmap(rect.adjusted(rw, 0, 0, -rh), pixmap, pmRect.adjusted(pw, 0, 0,-ph)); - painter->drawPixmap(rect.adjusted(0, rh, -rw, 0), pixmap, pmRect.adjusted(0, ph, -pw,0)); - painter->drawPixmap(rect.adjusted(rw, rh, 0, 0), pixmap, pmRect.adjusted(pw, ph, 0,0)); - } - break; - case PE_FrameWindow: - painter->save(); - { - QRect rect= option->rect; - painter->setPen(QPen(option->palette.dark().color().darker(150), 0)); - painter->drawRect(option->rect.adjusted(0, 0, -1, -1)); - painter->setPen(QPen(option->palette.light(), 0)); - painter->drawLine(QPoint(rect.left() + 1, rect.top() + 1), - QPoint(rect.left() + 1, rect.bottom() - 1)); - painter->setPen(QPen(option->palette.background().color().darker(120), 0)); - painter->drawLine(QPoint(rect.left() + 1, rect.bottom() - 1), - QPoint(rect.right() - 2, rect.bottom() - 1)); - painter->drawLine(QPoint(rect.right() - 1, rect.top() + 1), - QPoint(rect.right() - 1, rect.bottom() - 1)); - } - painter->restore(); - break; - - case PE_PanelTipLabel: { - GtkWidget *gtkWindow = d->gtkWidget("GtkWindow"); // The Murrine Engine currently assumes a widget is passed - style = d->gtk_rc_get_style_by_paths(d->gtk_settings_get_default(), "gtk-tooltips", "GtkWindow", - d->gtk_window_get_type()); - gtkPainter->paintFlatBox(gtkWindow, "tooltip", option->rect, GTK_STATE_NORMAL, GTK_SHADOW_NONE, style); - } - break; - - case PE_PanelStatusBar: { - if (widget && widget->testAttribute(Qt::WA_SetPalette) && - option->palette.resolve() & (1 << QPalette::Window)) { - // Respect custom palette - painter->fillRect(option->rect, option->palette.window()); - break; - } - GtkShadowType shadow_type; - GtkWidget *gtkStatusbarFrame = d->gtkWidget("GtkStatusbar.GtkFrame"); - d->gtk_widget_style_get(d->gtk_widget_get_parent(gtkStatusbarFrame), "shadow-type", &shadow_type, NULL); - gtkPainter->paintShadow(gtkStatusbarFrame, "frame", option->rect, GTK_STATE_NORMAL, - shadow_type, d->gtk_widget_get_style(gtkStatusbarFrame)); - } - break; - - case PE_IndicatorHeaderArrow: - if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) { - GtkWidget *gtkTreeHeader = d->gtkWidget("GtkTreeView.GtkButton"); - GtkStateType state = qt_gtk_state(option); - style = d->gtk_widget_get_style(gtkTreeHeader); - GtkArrowType type = GTK_ARROW_UP; - // This sorting indicator inversion is intentional, and follows the GNOME HIG. - // See http://library.gnome.org/devel/hig-book/stable/controls-lists.html.en#controls-lists-sortable - if (header->sortIndicator & QStyleOptionHeader::SortUp) - type = GTK_ARROW_UP; - else if (header->sortIndicator & QStyleOptionHeader::SortDown) - type = GTK_ARROW_DOWN; - - gtkPainter->paintArrow(gtkTreeHeader, "button", option->rect.adjusted(1, 1, -1, -1), type, state, - GTK_SHADOW_NONE, false, style); - } - break; - - case PE_FrameDefaultButton: // fall through - case PE_FrameFocusRect: { - QRect frameRect = option->rect.adjusted(1, 1, -2, -2); // ### this mess should move to subcontrolrect - if (qobject_cast<const QAbstractItemView*>(widget)) { - // Don't draw anything - } else if (qobject_cast<const QTabBar*>(widget)) { - GtkWidget *gtkNotebook = d->gtkWidget("GtkNotebook"); - style = d->gtk_widget_get_style(gtkNotebook); - gtkPainter->paintFocus(gtkNotebook, "tab", frameRect.adjusted(-1, 1, 1, 1), GTK_STATE_ACTIVE, style); - } else { - GtkWidget *gtkRadioButton = d->gtkWidget("GtkRadioButton"); - gtkPainter->paintFocus(gtkRadioButton, "radiobutton", frameRect, GTK_STATE_ACTIVE, style); - } - } - break; - - case PE_IndicatorBranch: - if (option->state & State_Children) { - QRect rect = option->rect; - rect = QRect(0, 0, 12, 12); - rect.moveCenter(option->rect.center()); - rect.translate(2, 0); - GtkExpanderStyle openState = GTK_EXPANDER_EXPANDED; - GtkExpanderStyle closedState = GTK_EXPANDER_COLLAPSED; - GtkWidget *gtkTreeView = d->gtkWidget("GtkTreeView"); - - GtkStateType state = GTK_STATE_NORMAL; - if (!(option->state & State_Enabled)) - state = GTK_STATE_INSENSITIVE; - else if (option->state & State_MouseOver) - state = GTK_STATE_PRELIGHT; - - gtkPainter->paintExpander(gtkTreeView, "treeview", rect, state, - option->state & State_Open ? openState : closedState , d->gtk_widget_get_style(gtkTreeView)); - } - break; - - case PE_PanelItemViewRow: - // This primitive is only used to draw selection behind selected expander arrows. - // We try not to decorate the tree branch background unless you inherit from StyledItemDelegate - // The reason for this is that a lot of code that relies on custom item delegates will look odd having - // a gradient on the branch but a flat shaded color on the item itself. - QCommonStyle::drawPrimitive(element, option, painter, widget); - if (!(option->state & State_Selected)) { - break; - } else { - if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView*>(widget)) { - if (!qobject_cast<QStyledItemDelegate*>(view->itemDelegate())) - break; - } - } // fall through - - case PE_PanelItemViewItem: - if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) { - uint resolve_mask = vopt->palette.resolve(); - if (vopt->backgroundBrush.style() != Qt::NoBrush - || (resolve_mask & (1 << QPalette::Base))) - { - QPointF oldBO = painter->brushOrigin(); - painter->setBrushOrigin(vopt->rect.topLeft()); - painter->fillRect(vopt->rect, vopt->backgroundBrush); - painter->setBrushOrigin(oldBO); - if (!(option->state & State_Selected)) - break; - } - if (GtkWidget *gtkTreeView = d->gtkWidget("GtkTreeView")) { - const char *detail = "cell_even_ruled"; - if (vopt && vopt->features & QStyleOptionViewItem::Alternate) - detail = "cell_odd_ruled"; - bool isActive = option->state & State_Active; - QString key; - if (isActive ) { - // Required for active/non-active window appearance - key = QLS("a"); - QGtkStylePrivate::gtkWidgetSetFocus(gtkTreeView, true); - } - bool isEnabled = (widget ? widget->isEnabled() : (vopt->state & QStyle::State_Enabled)); - gtkPainter->paintFlatBox(gtkTreeView, detail, option->rect, - option->state & State_Selected ? GTK_STATE_SELECTED : - isEnabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE, - GTK_SHADOW_OUT, d->gtk_widget_get_style(gtkTreeView), key); - if (isActive ) - QGtkStylePrivate::gtkWidgetSetFocus(gtkTreeView, false); - } - } - break; - case PE_IndicatorToolBarSeparator: - { - const int margin = 6; - GtkWidget *gtkSeparator = d->gtkWidget("GtkToolbar.GtkSeparatorToolItem"); - if (option->state & State_Horizontal) { - const int offset = option->rect.width()/2; - QRect rect = option->rect.adjusted(offset, margin, 0, -margin); - painter->setPen(QPen(option->palette.background().color().darker(110))); - gtkPainter->paintVline(gtkSeparator, "vseparator", - rect, GTK_STATE_NORMAL, d->gtk_widget_get_style(gtkSeparator), - 0, rect.height(), 0); - } else { //Draw vertical separator - const int offset = option->rect.height()/2; - QRect rect = option->rect.adjusted(margin, offset, -margin, 0); - painter->setPen(QPen(option->palette.background().color().darker(110))); - gtkPainter->paintHline(gtkSeparator, "hseparator", - rect, GTK_STATE_NORMAL, d->gtk_widget_get_style(gtkSeparator), - 0, rect.width(), 0); - } - } - break; - - case PE_IndicatorToolBarHandle: { - GtkWidget *gtkToolbar = d->gtkWidget("GtkToolbar"); - GtkShadowType shadow_type; - d->gtk_widget_style_get(gtkToolbar, "shadow-type", &shadow_type, NULL); - //Note when the toolbar is horizontal, the handle is vertical - painter->setClipRect(option->rect); - gtkPainter->paintHandle(gtkToolbar, "toolbar", option->rect.adjusted(-1, -1 ,0 ,1), - GTK_STATE_NORMAL, shadow_type, !(option->state & State_Horizontal) ? - GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL, d->gtk_widget_get_style(gtkToolbar)); - } - break; - - case PE_IndicatorArrowUp: - case PE_IndicatorArrowDown: - case PE_IndicatorArrowLeft: - case PE_IndicatorArrowRight: { - - - GtkArrowType type = GTK_ARROW_UP; - - switch (element) { - - case PE_IndicatorArrowDown: - type = GTK_ARROW_DOWN; - break; - - case PE_IndicatorArrowLeft: - type = GTK_ARROW_LEFT; - break; - - case PE_IndicatorArrowRight: - type = GTK_ARROW_RIGHT; - break; - - default: - break; - } - int size = qMin(option->rect.height(), option->rect.width()); - int border = (size > 9) ? (size/4) : 0; //Allow small arrows to have exact dimensions - int bsx = 0, bsy = 0; - if (option->state & State_Sunken) { - bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal); - bsy = proxy()->pixelMetric(PM_ButtonShiftVertical); - } - QRect arrowRect = option->rect.adjusted(border + bsx, border + bsy, -border + bsx, -border + bsy); - GtkShadowType shadow = option->state & State_Sunken ? GTK_SHADOW_IN : GTK_SHADOW_OUT; - GtkStateType state = qt_gtk_state(option); - - QColor arrowColor = option->palette.buttonText().color(); - GtkWidget *gtkArrow = d->gtkWidget("GtkArrow"); - GdkColor color = fromQColor(arrowColor); - d->gtk_widget_modify_fg (gtkArrow, state, &color); - gtkPainter->paintArrow(gtkArrow, "button", arrowRect, - type, state, shadow, false, d->gtk_widget_get_style(gtkArrow), - QString::number(arrowColor.rgba(), 16)); - // Passing NULL will revert the color change - d->gtk_widget_modify_fg (gtkArrow, state, NULL); - } - break; - - case PE_FrameGroupBox: - // Do nothing here, the GNOME groupboxes are flat - break; - - case PE_PanelMenu: { - GtkWidget *gtkMenu = d->gtkWidget("GtkMenu"); - gtkPainter->setAlphaSupport(false); // Note, alpha disabled for performance reasons - gtkPainter->paintBox(gtkMenu, "menu", option->rect, GTK_STATE_NORMAL, GTK_SHADOW_OUT, d->gtk_widget_get_style(gtkMenu), QString()); - } - break; - - case PE_FrameMenu: - //This is actually done by PE_Widget due to a clipping issue - //Otherwise Menu items will not be able to span the entire menu width - - // This is only used by floating tool bars - if (qobject_cast<const QToolBar *>(widget)) { - GtkWidget *gtkMenubar = d->gtkWidget("GtkMenuBar"); - gtkPainter->paintBox(gtkMenubar, "toolbar", option->rect, - GTK_STATE_NORMAL, GTK_SHADOW_OUT, style); - gtkPainter->paintBox(gtkMenubar, "menu", option->rect, - GTK_STATE_NORMAL, GTK_SHADOW_OUT, style); - } - break; - - case PE_FrameLineEdit: { - GtkWidget *gtkEntry = d->gtkWidget("GtkEntry"); - - - gboolean interior_focus; - gint focus_line_width; - QRect rect = option->rect; - d->gtk_widget_style_get(gtkEntry, - "interior-focus", &interior_focus, - "focus-line-width", &focus_line_width, NULL); - - // See https://bugzilla.mozilla.org/show_bug.cgi?id=405421 for info about this hack - g_object_set_data(G_OBJECT(gtkEntry), "transparent-bg-hint", GINT_TO_POINTER(true)); - - if (!interior_focus && option->state & State_HasFocus) - rect.adjust(focus_line_width, focus_line_width, -focus_line_width, -focus_line_width); - - if (option->state & State_HasFocus) - QGtkStylePrivate::gtkWidgetSetFocus(gtkEntry, true); - gtkPainter->paintShadow(gtkEntry, "entry", rect, option->state & State_Enabled ? - GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE, - GTK_SHADOW_IN, d->gtk_widget_get_style(gtkEntry), - option->state & State_HasFocus ? QLS("focus") : QString()); - if (!interior_focus && option->state & State_HasFocus) - gtkPainter->paintShadow(gtkEntry, "entry", option->rect, option->state & State_Enabled ? - GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE, - GTK_SHADOW_IN, d->gtk_widget_get_style(gtkEntry), QLS("GtkEntryShadowIn")); - - if (option->state & State_HasFocus) - QGtkStylePrivate::gtkWidgetSetFocus(gtkEntry, false); - } - break; - - case PE_PanelLineEdit: - if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) { - GtkWidget *gtkEntry = d->gtkWidget("GtkEntry"); - if (panel->lineWidth > 0) - proxy()->drawPrimitive(PE_FrameLineEdit, option, painter, widget); - uint resolve_mask = option->palette.resolve(); - GtkStyle *gtkEntryStyle = d->gtk_widget_get_style(gtkEntry); - QRect textRect = option->rect.adjusted(gtkEntryStyle->xthickness, gtkEntryStyle->ythickness, - -gtkEntryStyle->xthickness, -gtkEntryStyle->ythickness); - - if (widget && widget->testAttribute(Qt::WA_SetPalette) && - resolve_mask & (1 << QPalette::Base)) // Palette overridden by user - painter->fillRect(textRect, option->palette.base()); - else - gtkPainter->paintFlatBox(gtkEntry, "entry_bg", textRect, - option->state & State_Enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE, GTK_SHADOW_NONE, gtkEntryStyle); - } - break; - - case PE_FrameTabWidget: - if (const QStyleOptionTabWidgetFrame *frame = qstyleoption_cast<const QStyleOptionTabWidgetFrame*>(option)) { - GtkWidget *gtkNotebook = d->gtkWidget("GtkNotebook"); - style = d->gtk_widget_get_style(gtkNotebook); - gtkPainter->setAlphaSupport(false); - GtkShadowType shadow = GTK_SHADOW_OUT; - GtkStateType state = GTK_STATE_NORMAL; // Only state supported by gtknotebook - bool reverse = (option->direction == Qt::RightToLeft); - QGtkStylePrivate::gtk_widget_set_direction(gtkNotebook, reverse ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR); - if (const QStyleOptionTabWidgetFrameV2 *tabframe = qstyleoption_cast<const QStyleOptionTabWidgetFrameV2*>(option)) { - GtkPositionType frameType = GTK_POS_TOP; - QTabBar::Shape shape = frame->shape; - int gapStart = 0; - int gapSize = 0; - if (shape == QTabBar::RoundedNorth || shape == QTabBar::RoundedSouth) { - frameType = (shape == QTabBar::RoundedNorth) ? GTK_POS_TOP : GTK_POS_BOTTOM; - gapStart = tabframe->selectedTabRect.left(); - gapSize = tabframe->selectedTabRect.width(); - } else { - frameType = (shape == QTabBar::RoundedWest) ? GTK_POS_LEFT : GTK_POS_RIGHT; - gapStart = tabframe->selectedTabRect.y(); - gapSize = tabframe->selectedTabRect.height(); - } - gtkPainter->paintBoxGap(gtkNotebook, "notebook", option->rect, state, shadow, frameType, - gapStart, gapSize, style); - break; // done - } - - // Note this is only the fallback option - gtkPainter->paintBox(gtkNotebook, "notebook", option->rect, state, shadow, style); - } - break; - - case PE_PanelButtonCommand: - case PE_PanelButtonTool: { - bool isDefault = false; - bool isTool = (element == PE_PanelButtonTool); - if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton*>(option)) - isDefault = btn->features & QStyleOptionButton::DefaultButton; - - // don't draw a frame for tool buttons that have the autoRaise flag and are not enabled or on - if (isTool && !(option->state & State_Enabled || option->state & State_On) && (option->state & State_AutoRaise)) - break; - // don't draw a frame for dock widget buttons, unless we are hovering - if (widget && widget->inherits("QDockWidgetTitleButton") && !(option->state & State_MouseOver)) - break; - - GtkStateType state = qt_gtk_state(option); - if (option->state & State_On || option->state & State_Sunken) - state = GTK_STATE_ACTIVE; - GtkWidget *gtkButton = isTool ? d->gtkWidget("GtkToolButton.GtkButton") : d->gtkWidget("GtkButton"); - gint focusWidth, focusPad; - gboolean interiorFocus = false; - d->gtk_widget_style_get (gtkButton, - "focus-line-width", &focusWidth, - "focus-padding", &focusPad, - "interior-focus", &interiorFocus, NULL); - - style = d->gtk_widget_get_style(gtkButton); - - QRect buttonRect = option->rect; - - QString key; - if (isDefault) { - key += QLS("def"); - QGtkStylePrivate::gtk_widget_set_can_default(gtkButton, true); - QGtkStylePrivate::gtk_window_set_default((GtkWindow*)QGtkStylePrivate::gtk_widget_get_toplevel(gtkButton), gtkButton); - gtkPainter->paintBox(gtkButton, "buttondefault", buttonRect, state, GTK_SHADOW_IN, - style, isDefault ? QLS("d") : QString()); - } - - bool hasFocus = option->state & State_HasFocus; - - if (hasFocus) { - key += QLS("def"); - QGtkStylePrivate::gtkWidgetSetFocus(gtkButton, true); - } - - if (!interiorFocus) - buttonRect = buttonRect.adjusted(focusWidth, focusWidth, -focusWidth, -focusWidth); - - GtkShadowType shadow = (option->state & State_Sunken || option->state & State_On ) ? - GTK_SHADOW_IN : GTK_SHADOW_OUT; - - gtkPainter->paintBox(gtkButton, "button", buttonRect, state, shadow, - style, key); - if (isDefault) - QGtkStylePrivate::gtk_window_set_default((GtkWindow*)QGtkStylePrivate::gtk_widget_get_toplevel(gtkButton), 0); - if (hasFocus) - QGtkStylePrivate::gtkWidgetSetFocus(gtkButton, false); - } - break; - - case PE_IndicatorRadioButton: { - GtkShadowType shadow = GTK_SHADOW_OUT; - GtkStateType state = qt_gtk_state(option); - - if (option->state & State_Sunken) - state = GTK_STATE_ACTIVE; - - if (option->state & State_NoChange) - shadow = GTK_SHADOW_ETCHED_IN; - else if (option->state & State_On) - shadow = GTK_SHADOW_IN; - else - shadow = GTK_SHADOW_OUT; - - GtkWidget *gtkRadioButton = d->gtkWidget("GtkRadioButton"); - gint spacing; - d->gtk_widget_style_get(gtkRadioButton, "indicator-spacing", &spacing, NULL); - QRect buttonRect = option->rect.adjusted(spacing, spacing, -spacing, -spacing); - gtkPainter->setClipRect(option->rect); - // ### Note: Ubuntulooks breaks when the proper widget is passed - // Murrine engine requires a widget not to get RGBA check - warnings - GtkWidget *gtkCheckButton = d->gtkWidget("GtkCheckButton"); - QString key(QLS("radiobutton")); - if (option->state & State_HasFocus) { // Themes such as Nodoka check this flag - key += QLatin1Char('f'); - QGtkStylePrivate::gtkWidgetSetFocus(gtkCheckButton, true); - } - gtkPainter->paintOption(gtkCheckButton , buttonRect, state, shadow, d->gtk_widget_get_style(gtkRadioButton), key); - if (option->state & State_HasFocus) - QGtkStylePrivate::gtkWidgetSetFocus(gtkCheckButton, false); - } - break; - - case PE_IndicatorCheckBox: { - GtkShadowType shadow = GTK_SHADOW_OUT; - GtkStateType state = qt_gtk_state(option); - - if (option->state & State_Sunken) - state = GTK_STATE_ACTIVE; - - if (option->state & State_NoChange) - shadow = GTK_SHADOW_ETCHED_IN; - else if (option->state & State_On) - shadow = GTK_SHADOW_IN; - else - shadow = GTK_SHADOW_OUT; - - int spacing; - - GtkWidget *gtkCheckButton = d->gtkWidget("GtkCheckButton"); - QString key(QLS("checkbutton")); - if (option->state & State_HasFocus) { // Themes such as Nodoka checks this flag - key += QLatin1Char('f'); - QGtkStylePrivate::gtkWidgetSetFocus(gtkCheckButton, true); - } - - // Some styles such as aero-clone assume they can paint in the spacing area - gtkPainter->setClipRect(option->rect); - - d->gtk_widget_style_get(gtkCheckButton, "indicator-spacing", &spacing, NULL); - - QRect checkRect = option->rect.adjusted(spacing, spacing, -spacing, -spacing); - - gtkPainter->paintCheckbox(gtkCheckButton, checkRect, state, shadow, d->gtk_widget_get_style(gtkCheckButton), - key); - if (option->state & State_HasFocus) - QGtkStylePrivate::gtkWidgetSetFocus(gtkCheckButton, false); - - } - break; - -#ifndef QT_NO_TABBAR - - case PE_FrameTabBarBase: - if (const QStyleOptionTabBarBase *tbb - = qstyleoption_cast<const QStyleOptionTabBarBase *>(option)) { - QRect tabRect = tbb->rect; - painter->save(); - painter->setPen(QPen(option->palette.dark().color().dark(110), 0)); - switch (tbb->shape) { - - case QTabBar::RoundedNorth: - painter->drawLine(tabRect.topLeft(), tabRect.topRight()); - break; - - case QTabBar::RoundedWest: - painter->drawLine(tabRect.left(), tabRect.top(), tabRect.left(), tabRect.bottom()); - break; - - case QTabBar::RoundedSouth: - painter->drawLine(tbb->rect.left(), tbb->rect.bottom(), - tabRect.right(), tabRect.bottom()); - break; - - case QTabBar::RoundedEast: - painter->drawLine(tabRect.topRight(), tabRect.bottomRight()); - break; - - case QTabBar::TriangularNorth: - case QTabBar::TriangularEast: - case QTabBar::TriangularWest: - case QTabBar::TriangularSouth: - painter->restore(); - QCommonStyle::drawPrimitive(element, option, painter, widget); - return; - } - - painter->restore(); - } - return; - -#endif // QT_NO_TABBAR - - case PE_Widget: - break; - - default: - QCommonStyle::drawPrimitive(element, option, painter, widget); - } -} - -/*! - \reimp -*/ -void QGtkStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, - - QPainter *painter, const QWidget *widget) const -{ - Q_D(const QGtkStyle); - - if (!d->isThemeAvailable()) { - QCommonStyle::drawComplexControl(control, option, painter, widget); - return; - } - - GtkStyle* style = d->gtkStyle(); - QGtkPainter* gtkPainter = d->gtkPainter(painter); - QColor button = option->palette.button().color(); - QColor dark; - QColor grooveColor; - QColor darkOutline; - dark.setHsv(button.hue(), - qMin(255, (int)(button.saturation()*1.9)), - qMin(255, (int)(button.value()*0.7))); - grooveColor.setHsv(button.hue(), - qMin(255, (int)(button.saturation()*2.6)), - qMin(255, (int)(button.value()*0.9))); - darkOutline.setHsv(button.hue(), - qMin(255, (int)(button.saturation()*3.0)), - qMin(255, (int)(button.value()*0.6))); - - QColor alphaCornerColor; - - if (widget) - alphaCornerColor = mergedColors(option->palette.color(widget->backgroundRole()), darkOutline); - else - alphaCornerColor = mergedColors(option->palette.background().color(), darkOutline); - - switch (control) { - - case CC_TitleBar: - painter->save(); - if (const QStyleOptionTitleBar *titleBar = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) { - // Since this is drawn by metacity and not Gtk we - // have to do custom drawing - - GdkColor gdkBg = style->bg[GTK_STATE_SELECTED]; - QColor bgColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8); - - const int buttonMargin = 5; - bool active = (titleBar->titleBarState & State_Active); - QRect fullRect = titleBar->rect; - QPalette palette = option->palette; - QColor highlight = bgColor; - - QColor titleBarFrameBorder(active ? highlight.darker(180): dark.darker(110)); - QColor titleBarHighlight(active ? highlight.lighter(120): palette.background().color().lighter(120)); - QColor textColor(active ? 0xffffff : 0xff000000); - QColor textAlphaColor(active ? 0xffffff : 0xff000000 ); - - { - // Fill title bar gradient - QColor titlebarColor = QColor(active ? highlight: palette.background().color()); - QLinearGradient gradient(option->rect.center().x(), option->rect.top(), - option->rect.center().x(), option->rect.bottom()); - - gradient.setColorAt(0, titlebarColor.lighter(114)); - gradient.setColorAt(0.5, titlebarColor.lighter(102)); - gradient.setColorAt(0.51, titlebarColor.darker(104)); - gradient.setColorAt(1, titlebarColor); - painter->fillRect(option->rect.adjusted(1, 1, -1, 0), gradient); - - // Frame and rounded corners - painter->setPen(titleBarFrameBorder); - - // top outline - painter->drawLine(fullRect.left() + 5, fullRect.top(), fullRect.right() - 5, fullRect.top()); - painter->drawLine(fullRect.left(), fullRect.top() + 4, fullRect.left(), fullRect.bottom()); - const QPoint points[5] = { - QPoint(fullRect.left() + 4, fullRect.top() + 1), - QPoint(fullRect.left() + 3, fullRect.top() + 1), - QPoint(fullRect.left() + 2, fullRect.top() + 2), - QPoint(fullRect.left() + 1, fullRect.top() + 3), - QPoint(fullRect.left() + 1, fullRect.top() + 4) - }; - painter->drawPoints(points, 5); - - painter->drawLine(fullRect.right(), fullRect.top() + 4, fullRect.right(), fullRect.bottom()); - const QPoint points2[5] = { - QPoint(fullRect.right() - 3, fullRect.top() + 1), - QPoint(fullRect.right() - 4, fullRect.top() + 1), - QPoint(fullRect.right() - 2, fullRect.top() + 2), - QPoint(fullRect.right() - 1, fullRect.top() + 3), - QPoint(fullRect.right() - 1, fullRect.top() + 4) - }; - painter->drawPoints(points2, 5); - - // draw bottomline - painter->drawLine(fullRect.right(), fullRect.bottom(), fullRect.left(), fullRect.bottom()); - - // top highlight - painter->setPen(titleBarHighlight); - painter->drawLine(fullRect.left() + 6, fullRect.top() + 1, fullRect.right() - 6, fullRect.top() + 1); - } - // draw title - QRect textRect = proxy()->subControlRect(CC_TitleBar, titleBar, SC_TitleBarLabel, widget); - QFont font = painter->font(); - font.setBold(true); - painter->setFont(font); - painter->setPen(active? (titleBar->palette.text().color().lighter(120)) : - titleBar->palette.text().color() ); - // Note workspace also does elliding but it does not use the correct font - QString title = QFontMetrics(font).elidedText(titleBar->text, Qt::ElideRight, textRect.width() - 14); - painter->drawText(textRect.adjusted(1, 1, 1, 1), title, QTextOption(Qt::AlignHCenter | Qt::AlignVCenter)); - painter->setPen(Qt::white); - if (active) - painter->drawText(textRect, title, QTextOption(Qt::AlignHCenter | Qt::AlignVCenter)); - // min button - if ((titleBar->subControls & SC_TitleBarMinButton) && (titleBar->titleBarFlags & Qt::WindowMinimizeButtonHint) && - !(titleBar->titleBarState& Qt::WindowMinimized)) { - QRect minButtonRect = proxy()->subControlRect(CC_TitleBar, titleBar, SC_TitleBarMinButton, widget); - if (minButtonRect.isValid()) { - bool hover = (titleBar->activeSubControls & SC_TitleBarMinButton) && (titleBar->state & State_MouseOver); - bool sunken = (titleBar->activeSubControls & SC_TitleBarMinButton) && (titleBar->state & State_Sunken); - qt_gtk_draw_mdibutton(painter, titleBar, minButtonRect, hover, sunken); - QRect minButtonIconRect = minButtonRect.adjusted(buttonMargin ,buttonMargin , -buttonMargin, -buttonMargin); - painter->setPen(textColor); - painter->drawLine(minButtonIconRect.center().x() - 2, minButtonIconRect.center().y() + 3, - minButtonIconRect.center().x() + 3, minButtonIconRect.center().y() + 3); - painter->drawLine(minButtonIconRect.center().x() - 2, minButtonIconRect.center().y() + 4, - minButtonIconRect.center().x() + 3, minButtonIconRect.center().y() + 4); - painter->setPen(textAlphaColor); - painter->drawLine(minButtonIconRect.center().x() - 3, minButtonIconRect.center().y() + 3, - minButtonIconRect.center().x() - 3, minButtonIconRect.center().y() + 4); - painter->drawLine(minButtonIconRect.center().x() + 4, minButtonIconRect.center().y() + 3, - minButtonIconRect.center().x() + 4, minButtonIconRect.center().y() + 4); - } - } - // max button - if ((titleBar->subControls & SC_TitleBarMaxButton) && (titleBar->titleBarFlags & Qt::WindowMaximizeButtonHint) && - !(titleBar->titleBarState & Qt::WindowMaximized)) { - QRect maxButtonRect = proxy()->subControlRect(CC_TitleBar, titleBar, SC_TitleBarMaxButton, widget); - if (maxButtonRect.isValid()) { - bool hover = (titleBar->activeSubControls & SC_TitleBarMaxButton) && (titleBar->state & State_MouseOver); - bool sunken = (titleBar->activeSubControls & SC_TitleBarMaxButton) && (titleBar->state & State_Sunken); - qt_gtk_draw_mdibutton(painter, titleBar, maxButtonRect, hover, sunken); - - QRect maxButtonIconRect = maxButtonRect.adjusted(buttonMargin, buttonMargin, -buttonMargin, -buttonMargin); - - painter->setPen(textColor); - painter->drawRect(maxButtonIconRect.adjusted(0, 0, -1, -1)); - painter->drawLine(maxButtonIconRect.left() + 1, maxButtonIconRect.top() + 1, - maxButtonIconRect.right() - 1, maxButtonIconRect.top() + 1); - painter->setPen(textAlphaColor); - const QPoint points[4] = { - maxButtonIconRect.topLeft(), - maxButtonIconRect.topRight(), - maxButtonIconRect.bottomLeft(), - maxButtonIconRect.bottomRight() - }; - painter->drawPoints(points, 4); - } - } - - // close button - if ((titleBar->subControls & SC_TitleBarCloseButton) && (titleBar->titleBarFlags & Qt::WindowSystemMenuHint)) { - QRect closeButtonRect = proxy()->subControlRect(CC_TitleBar, titleBar, SC_TitleBarCloseButton, widget); - if (closeButtonRect.isValid()) { - bool hover = (titleBar->activeSubControls & SC_TitleBarCloseButton) && (titleBar->state & State_MouseOver); - bool sunken = (titleBar->activeSubControls & SC_TitleBarCloseButton) && (titleBar->state & State_Sunken); - qt_gtk_draw_mdibutton(painter, titleBar, closeButtonRect, hover, sunken); - QRect closeIconRect = closeButtonRect.adjusted(buttonMargin, buttonMargin, -buttonMargin, -buttonMargin); - painter->setPen(textAlphaColor); - const QLine lines[4] = { - QLine(closeIconRect.left() + 1, closeIconRect.top(), - closeIconRect.right(), closeIconRect.bottom() - 1), - QLine(closeIconRect.left(), closeIconRect.top() + 1, - closeIconRect.right() - 1, closeIconRect.bottom()), - QLine(closeIconRect.right() - 1, closeIconRect.top(), - closeIconRect.left(), closeIconRect.bottom() - 1), - QLine(closeIconRect.right(), closeIconRect.top() + 1, - closeIconRect.left() + 1, closeIconRect.bottom()) - }; - painter->drawLines(lines, 4); - const QPoint points[4] = { - closeIconRect.topLeft(), - closeIconRect.topRight(), - closeIconRect.bottomLeft(), - closeIconRect.bottomRight() - }; - painter->drawPoints(points, 4); - - painter->setPen(textColor); - painter->drawLine(closeIconRect.left() + 1, closeIconRect.top() + 1, - closeIconRect.right() - 1, closeIconRect.bottom() - 1); - painter->drawLine(closeIconRect.left() + 1, closeIconRect.bottom() - 1, - closeIconRect.right() - 1, closeIconRect.top() + 1); - } - } - - // normalize button - if ((titleBar->subControls & SC_TitleBarNormalButton) && - (((titleBar->titleBarFlags & Qt::WindowMinimizeButtonHint) && - (titleBar->titleBarState & Qt::WindowMinimized)) || - ((titleBar->titleBarFlags & Qt::WindowMaximizeButtonHint) && - (titleBar->titleBarState & Qt::WindowMaximized)))) { - QRect normalButtonRect = proxy()->subControlRect(CC_TitleBar, titleBar, SC_TitleBarNormalButton, widget); - if (normalButtonRect.isValid()) { - - bool hover = (titleBar->activeSubControls & SC_TitleBarNormalButton) && (titleBar->state & State_MouseOver); - bool sunken = (titleBar->activeSubControls & SC_TitleBarNormalButton) && (titleBar->state & State_Sunken); - QRect normalButtonIconRect = normalButtonRect.adjusted(buttonMargin, buttonMargin, -buttonMargin, -buttonMargin); - qt_gtk_draw_mdibutton(painter, titleBar, normalButtonRect, hover, sunken); - - QRect frontWindowRect = normalButtonIconRect.adjusted(0, 3, -3, 0); - painter->setPen(textColor); - painter->drawRect(frontWindowRect.adjusted(0, 0, -1, -1)); - painter->drawLine(frontWindowRect.left() + 1, frontWindowRect.top() + 1, - frontWindowRect.right() - 1, frontWindowRect.top() + 1); - painter->setPen(textAlphaColor); - const QPoint points[4] = { - frontWindowRect.topLeft(), - frontWindowRect.topRight(), - frontWindowRect.bottomLeft(), - frontWindowRect.bottomRight() - }; - painter->drawPoints(points, 4); - - QRect backWindowRect = normalButtonIconRect.adjusted(3, 0, 0, -3); - QRegion clipRegion = backWindowRect; - clipRegion -= frontWindowRect; - painter->save(); - painter->setClipRegion(clipRegion); - painter->setPen(textColor); - painter->drawRect(backWindowRect.adjusted(0, 0, -1, -1)); - painter->drawLine(backWindowRect.left() + 1, backWindowRect.top() + 1, - backWindowRect.right() - 1, backWindowRect.top() + 1); - painter->setPen(textAlphaColor); - const QPoint points2[4] = { - backWindowRect.topLeft(), - backWindowRect.topRight(), - backWindowRect.bottomLeft(), - backWindowRect.bottomRight() - }; - painter->drawPoints(points2, 4); - painter->restore(); - } - } - - // context help button - if (titleBar->subControls & SC_TitleBarContextHelpButton - && (titleBar->titleBarFlags & Qt::WindowContextHelpButtonHint)) { - QRect contextHelpButtonRect = proxy()->subControlRect(CC_TitleBar, titleBar, SC_TitleBarContextHelpButton, widget); - if (contextHelpButtonRect.isValid()) { - bool hover = (titleBar->activeSubControls & SC_TitleBarContextHelpButton) && (titleBar->state & State_MouseOver); - bool sunken = (titleBar->activeSubControls & SC_TitleBarContextHelpButton) && (titleBar->state & State_Sunken); - qt_gtk_draw_mdibutton(painter, titleBar, contextHelpButtonRect, hover, sunken); - - QColor blend; - QImage image(qt_titlebar_context_help); - QColor alpha = textColor; - alpha.setAlpha(128); - image.setColor(1, textColor.rgba()); - image.setColor(2, alpha.rgba()); - painter->setRenderHint(QPainter::SmoothPixmapTransform); - painter->drawImage(contextHelpButtonRect.adjusted(4, 4, -4, -4), image); - } - } - - // shade button - if (titleBar->subControls & SC_TitleBarShadeButton && (titleBar->titleBarFlags & Qt::WindowShadeButtonHint)) { - QRect shadeButtonRect = proxy()->subControlRect(CC_TitleBar, titleBar, SC_TitleBarShadeButton, widget); - if (shadeButtonRect.isValid()) { - bool hover = (titleBar->activeSubControls & SC_TitleBarShadeButton) && (titleBar->state & State_MouseOver); - bool sunken = (titleBar->activeSubControls & SC_TitleBarShadeButton) && (titleBar->state & State_Sunken); - qt_gtk_draw_mdibutton(painter, titleBar, shadeButtonRect, hover, sunken); - QImage image(qt_scrollbar_button_arrow_up); - image.setColor(1, textColor.rgba()); - painter->drawImage(shadeButtonRect.adjusted(5, 7, -5, -7), image); - } - } - - // unshade button - if (titleBar->subControls & SC_TitleBarUnshadeButton && (titleBar->titleBarFlags & Qt::WindowShadeButtonHint)) { - QRect unshadeButtonRect = proxy()->subControlRect(CC_TitleBar, titleBar, SC_TitleBarUnshadeButton, widget); - if (unshadeButtonRect.isValid()) { - bool hover = (titleBar->activeSubControls & SC_TitleBarUnshadeButton) && (titleBar->state & State_MouseOver); - bool sunken = (titleBar->activeSubControls & SC_TitleBarUnshadeButton) && (titleBar->state & State_Sunken); - qt_gtk_draw_mdibutton(painter, titleBar, unshadeButtonRect, hover, sunken); - QImage image(qt_scrollbar_button_arrow_down); - image.setColor(1, textColor.rgba()); - painter->drawImage(unshadeButtonRect.adjusted(5, 7, -5, -7), image); - } - } - - if ((titleBar->subControls & SC_TitleBarSysMenu) && (titleBar->titleBarFlags & Qt::WindowSystemMenuHint)) { - QRect iconRect = proxy()->subControlRect(CC_TitleBar, titleBar, SC_TitleBarSysMenu, widget); - if (iconRect.isValid()) { - if (!titleBar->icon.isNull()) { - titleBar->icon.paint(painter, iconRect); - } else { - QStyleOption tool(0); - tool.palette = titleBar->palette; - QPixmap pm = proxy()->standardIcon(SP_TitleBarMenuButton, &tool, widget).pixmap(16, 16); - tool.rect = iconRect; - painter->save(); - proxy()->drawItemPixmap(painter, iconRect, Qt::AlignCenter, pm); - painter->restore(); - } - } - } - } - painter->restore(); - break; - -#ifndef QT_NO_GROUPBOX - - case CC_GroupBox: - painter->save(); - - if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(option)) { - QRect textRect = proxy()->subControlRect(CC_GroupBox, groupBox, SC_GroupBoxLabel, widget); - QRect checkBoxRect = proxy()->subControlRect(CC_GroupBox, groupBox, SC_GroupBoxCheckBox, widget); - // Draw title - - if ((groupBox->subControls & QStyle::SC_GroupBoxLabel) && !groupBox->text.isEmpty()) { - // Draw prelight background - GtkWidget *gtkCheckButton = d->gtkWidget("GtkCheckButton"); - - if (option->state & State_MouseOver) { - QRect bgRect = textRect | checkBoxRect; - gtkPainter->paintFlatBox(gtkCheckButton, "checkbutton", bgRect.adjusted(0, 0, 0, -2), - GTK_STATE_PRELIGHT, GTK_SHADOW_ETCHED_OUT, d->gtk_widget_get_style(gtkCheckButton)); - } - - if (!groupBox->text.isEmpty()) { - int alignment = int(groupBox->textAlignment); - if (!proxy()->styleHint(QStyle::SH_UnderlineShortcut, option, widget)) - alignment |= Qt::TextHideMnemonic; - QColor textColor = groupBox->textColor; // Note: custom textColor is currently ignored - int labelState = GTK_STATE_INSENSITIVE; - - if (option->state & State_Enabled) - labelState = (option->state & State_MouseOver) ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL; - - GdkColor gdkText = d->gtk_widget_get_style(gtkCheckButton)->fg[labelState]; - textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); - painter->setPen(textColor); - QFont font = painter->font(); - font.setBold(true); - painter->setFont(font); - painter->drawText(textRect, Qt::TextShowMnemonic | Qt::AlignLeft| alignment, groupBox->text); - - if (option->state & State_HasFocus) - gtkPainter->paintFocus(gtkCheckButton, "checkbutton", textRect.adjusted(-4, -1, 0, -3), GTK_STATE_ACTIVE, style); - } - } - - if (groupBox->subControls & SC_GroupBoxCheckBox) { - QStyleOptionButton box; - box.QStyleOption::operator=(*groupBox); - box.rect = checkBoxRect; - proxy()->drawPrimitive(PE_IndicatorCheckBox, &box, painter, widget); - } - } - - painter->restore(); - break; -#endif // QT_NO_GROUPBOX - -#ifndef QT_NO_COMBOBOX - - case CC_ComboBox: - // See: http://live.gnome.org/GnomeArt/Tutorials/GtkThemes/GtkComboBox - // and http://live.gnome.org/GnomeArt/Tutorials/GtkThemes/GtkComboBoxEntry - if (const QStyleOptionComboBox *comboBox = qstyleoption_cast<const QStyleOptionComboBox *>(option)) { - bool sunken = comboBox->state & State_On; // play dead, if combobox has no items - BEGIN_STYLE_PIXMAPCACHE(QString::fromLatin1("cb-%0-%1").arg(sunken).arg(comboBox->editable)); - gtkPainter->reset(p); - gtkPainter->setUsePixmapCache(false); // cached externally - - bool isEnabled = (comboBox->state & State_Enabled); - bool focus = isEnabled && (comboBox->state & State_HasFocus); - GtkStateType state = qt_gtk_state(option); - int appears_as_list = !proxy()->styleHint(QStyle::SH_ComboBox_Popup, comboBox, widget); - QStyleOptionComboBox comboBoxCopy = *comboBox; - comboBoxCopy.rect = option->rect; - - bool reverse = (option->direction == Qt::RightToLeft); - QRect rect = option->rect; - QRect arrowButtonRect = proxy()->subControlRect(CC_ComboBox, &comboBoxCopy, - SC_ComboBoxArrow, widget); - - GtkShadowType shadow = (option->state & State_Sunken || option->state & State_On ) ? - GTK_SHADOW_IN : GTK_SHADOW_OUT; - const QHashableLatin1Literal comboBoxPath = comboBox->editable ? QHashableLatin1Literal("GtkComboBoxEntry") : QHashableLatin1Literal("GtkComboBox"); - - // We use the gtk widget to position arrows and separators for us - GtkWidget *gtkCombo = d->gtkWidget(comboBoxPath); - GtkAllocation geometry = {0, 0, option->rect.width(), option->rect.height()}; - d->gtk_widget_set_direction(gtkCombo, reverse ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR); - d->gtk_widget_size_allocate(gtkCombo, &geometry); - - QHashableLatin1Literal buttonPath = comboBox->editable ? QHashableLatin1Literal("GtkComboBoxEntry.GtkToggleButton") - : QHashableLatin1Literal("GtkComboBox.GtkToggleButton"); - GtkWidget *gtkToggleButton = d->gtkWidget(buttonPath); - d->gtk_widget_set_direction(gtkToggleButton, reverse ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR); - if (gtkToggleButton && (appears_as_list || comboBox->editable)) { - if (focus) - QGtkStylePrivate::gtkWidgetSetFocus(gtkToggleButton, true); - // Draw the combo box as a line edit with a button next to it - if (comboBox->editable || appears_as_list) { - GtkStateType frameState = (state == GTK_STATE_PRELIGHT) ? GTK_STATE_NORMAL : state; - QHashableLatin1Literal entryPath = comboBox->editable ? QHashableLatin1Literal("GtkComboBoxEntry.GtkEntry") : QHashableLatin1Literal("GtkComboBox.GtkFrame"); - GtkWidget *gtkEntry = d->gtkWidget(entryPath); - d->gtk_widget_set_direction(gtkEntry, reverse ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR); - QRect frameRect = option->rect; - - if (reverse) - frameRect.setLeft(arrowButtonRect.right()); - else - frameRect.setRight(arrowButtonRect.left()); - - // Fill the line edit background - // We could have used flat_box with "entry_bg" but that is probably not worth the overhead - uint resolve_mask = option->palette.resolve(); - GtkStyle *gtkEntryStyle = d->gtk_widget_get_style(gtkEntry); - QRect contentRect = frameRect.adjusted(gtkEntryStyle->xthickness, gtkEntryStyle->ythickness, - -gtkEntryStyle->xthickness, -gtkEntryStyle->ythickness); - // Required for inner blue highlight with clearlooks - if (focus) - QGtkStylePrivate::gtkWidgetSetFocus(gtkEntry, true); - - if (widget && widget->testAttribute(Qt::WA_SetPalette) && - resolve_mask & (1 << QPalette::Base)) // Palette overridden by user - p->fillRect(contentRect, option->palette.base().color()); - else { - gtkPainter->paintFlatBox(gtkEntry, "entry_bg", contentRect, - option->state & State_Enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE, - GTK_SHADOW_NONE, gtkEntryStyle, entryPath.toString() + QString::number(focus)); - } - - gtkPainter->paintShadow(gtkEntry, comboBox->editable ? "entry" : "frame", frameRect, frameState, - GTK_SHADOW_IN, gtkEntryStyle, entryPath.toString() + - QString::number(focus) + QString::number(comboBox->editable) + - QString::number(option->direction)); - if (focus) - QGtkStylePrivate::gtkWidgetSetFocus(gtkEntry, false); - } - - GtkStateType buttonState = GTK_STATE_NORMAL; - - if (!(option->state & State_Enabled)) - buttonState = GTK_STATE_INSENSITIVE; - else if (option->state & State_Sunken || option->state & State_On) - buttonState = GTK_STATE_ACTIVE; - else if (option->state & State_MouseOver && comboBox->activeSubControls & SC_ComboBoxArrow) - buttonState = GTK_STATE_PRELIGHT; - - Q_ASSERT(gtkToggleButton); - gtkPainter->paintBox(gtkToggleButton, "button", arrowButtonRect, buttonState, - shadow, d->gtk_widget_get_style(gtkToggleButton), buttonPath.toString() + - QString::number(focus) + QString::number(option->direction)); - if (focus) - QGtkStylePrivate::gtkWidgetSetFocus(gtkToggleButton, false); - } else { - // Draw combo box as a button - QRect buttonRect = option->rect; - GtkStyle *gtkToggleButtonStyle = d->gtk_widget_get_style(gtkToggleButton); - - if (focus) // Clearlooks actually check the widget for the default state - QGtkStylePrivate::gtkWidgetSetFocus(gtkToggleButton, true); - gtkPainter->paintBox(gtkToggleButton, "button", - buttonRect, state, - shadow, gtkToggleButtonStyle, - buttonPath.toString() + QString::number(focus)); - if (focus) - QGtkStylePrivate::gtkWidgetSetFocus(gtkToggleButton, false); - - - // Draw the separator between label and arrows - QHashableLatin1Literal vSeparatorPath = comboBox->editable - ? QHashableLatin1Literal("GtkComboBoxEntry.GtkToggleButton.GtkHBox.GtkVSeparator") - : QHashableLatin1Literal("GtkComboBox.GtkToggleButton.GtkHBox.GtkVSeparator"); - - if (GtkWidget *gtkVSeparator = d->gtkWidget(vSeparatorPath)) { - GtkAllocation allocation; - d->gtk_widget_get_allocation(gtkVSeparator, &allocation); - QRect vLineRect(allocation.x, allocation.y, allocation.width, allocation.height); - - gtkPainter->paintVline(gtkVSeparator, "vseparator", - vLineRect, state, d->gtk_widget_get_style(gtkVSeparator), - 0, vLineRect.height(), 0, vSeparatorPath.toString()); - - - gint interiorFocus = true; - d->gtk_widget_style_get(gtkToggleButton, "interior-focus", &interiorFocus, NULL); - int xt = interiorFocus ? gtkToggleButtonStyle->xthickness : 0; - int yt = interiorFocus ? gtkToggleButtonStyle->ythickness : 0; - if (focus && ((option->state & State_KeyboardFocusChange) || styleHint(SH_UnderlineShortcut, option, widget))) - gtkPainter->paintFocus(gtkToggleButton, "button", - option->rect.adjusted(xt, yt, -xt, -yt), - option->state & State_Sunken ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL, - gtkToggleButtonStyle); - } - } - - if (comboBox->subControls & SC_ComboBoxArrow) { - if (!isEnabled) - state = GTK_STATE_INSENSITIVE; - else if (sunken) - state = GTK_STATE_ACTIVE; - else if (option->state & State_MouseOver) - state = GTK_STATE_PRELIGHT; - else - state = GTK_STATE_NORMAL; - - QHashableLatin1Literal arrowPath(""); - if (comboBox->editable) { - if (appears_as_list) - arrowPath = QHashableLatin1Literal("GtkComboBoxEntry.GtkToggleButton.GtkArrow"); - else - arrowPath = QHashableLatin1Literal("GtkComboBoxEntry.GtkToggleButton.GtkHBox.GtkArrow"); - } else { - if (appears_as_list) - arrowPath = QHashableLatin1Literal("GtkComboBox.GtkToggleButton.GtkArrow"); - else - arrowPath = QHashableLatin1Literal("GtkComboBox.GtkToggleButton.GtkHBox.GtkArrow"); - } - - GtkWidget *gtkArrow = d->gtkWidget(arrowPath); - gfloat scale = 0.7; - gint minSize = 15; - QRect arrowWidgetRect; - - if (gtkArrow && !d->gtk_check_version(2, 12, 0)) { - d->gtk_widget_style_get(gtkArrow, "arrow-scaling", &scale, NULL); - d->gtk_widget_style_get(gtkCombo, "arrow-size", &minSize, NULL); - } - if (gtkArrow) { - GtkAllocation allocation; - d->gtk_widget_get_allocation(gtkArrow, &allocation); - arrowWidgetRect = QRect(allocation.x, allocation.y, allocation.width, allocation.height); - style = d->gtk_widget_get_style(gtkArrow); - } - - // Note that for some reason the arrow-size is not properly respected with Hildon - // Hence we enforce the minimum "arrow-size" ourselves - int arrowSize = qMax(qMin(rect.height() - d->gtk_widget_get_style(gtkCombo)->ythickness * 2, minSize), - qMin(arrowWidgetRect.width(), arrowWidgetRect.height())); - QRect arrowRect(0, 0, static_cast<int>(arrowSize * scale), static_cast<int>(arrowSize * scale)); - - arrowRect.moveCenter(arrowWidgetRect.center()); - - if (sunken) { - int xoff, yoff; - const QHashableLatin1Literal toggleButtonPath = comboBox->editable - ? QHashableLatin1Literal("GtkComboBoxEntry.GtkToggleButton") - : QHashableLatin1Literal("GtkComboBox.GtkToggleButton"); - - GtkWidget *gtkButton = d->gtkWidget(toggleButtonPath); - d->gtk_widget_style_get(gtkButton, "child-displacement-x", &xoff, NULL); - d->gtk_widget_style_get(gtkButton, "child-displacement-y", &yoff, NULL); - arrowRect = arrowRect.adjusted(xoff, yoff, xoff, yoff); - } - - // Some styles such as Nimbus paint outside the arrowRect - // hence we have provide the whole widget as the cliprect - if (gtkArrow) { - gtkPainter->setClipRect(option->rect); - gtkPainter->paintArrow(gtkArrow, "arrow", arrowRect, - GTK_ARROW_DOWN, state, GTK_SHADOW_NONE, true, - style, arrowPath.toString() + QString::number(option->direction)); - } - } - END_STYLE_PIXMAPCACHE; - } - break; -#endif // QT_NO_COMBOBOX -#ifndef QT_NO_TOOLBUTTON - - case CC_ToolButton: - if (const QStyleOptionToolButton *toolbutton - = qstyleoption_cast<const QStyleOptionToolButton *>(option)) { - QRect button, menuarea; - button = proxy()->subControlRect(control, toolbutton, SC_ToolButton, widget); - menuarea = proxy()->subControlRect(control, toolbutton, SC_ToolButtonMenu, widget); - State bflags = toolbutton->state & ~(State_Sunken | State_MouseOver); - - if (bflags & State_AutoRaise) - if (!(bflags & State_MouseOver)) - bflags &= ~State_Raised; - - State mflags = bflags; - - if (toolbutton->state & State_Sunken) { - if (toolbutton->activeSubControls & SC_ToolButton) - bflags |= State_Sunken; - if (toolbutton->activeSubControls & SC_ToolButtonMenu) - mflags |= State_Sunken; - } else if (toolbutton->state & State_MouseOver) { - if (toolbutton->activeSubControls & SC_ToolButton) - bflags |= State_MouseOver; - if (toolbutton->activeSubControls & SC_ToolButtonMenu) - mflags |= State_MouseOver; - } - - QStyleOption tool(0); - - tool.palette = toolbutton->palette; - - if (toolbutton->subControls & SC_ToolButton) { - if (bflags & (State_Sunken | State_On | State_Raised | State_MouseOver)) { - tool.rect = button; - tool.state = bflags; - proxy()->drawPrimitive(PE_PanelButtonTool, &tool, painter, widget); - } - } - - bool drawMenuArrow = toolbutton->features & QStyleOptionToolButton::HasMenu && - !(toolbutton->features & QStyleOptionToolButton::MenuButtonPopup); - int popupArrowSize = drawMenuArrow ? 7 : 0; - - if (toolbutton->state & State_HasFocus) { - QStyleOptionFocusRect fr; - fr.QStyleOption::operator=(*toolbutton); - fr.rect = proxy()->subControlRect(CC_ToolButton, toolbutton, SC_ToolButton, widget); - fr.rect.adjust(1, 1, -1, -1); - proxy()->drawPrimitive(PE_FrameFocusRect, &fr, painter, widget); - } - - QStyleOptionToolButton label = *toolbutton; - label.state = bflags; - GtkWidget *gtkButton = d->gtkWidget("GtkToolButton.GtkButton"); - QPalette pal = toolbutton->palette; - if (option->state & State_Enabled && - option->state & State_MouseOver && !(widget && widget->testAttribute(Qt::WA_SetPalette))) { - GdkColor gdkText = d->gtk_widget_get_style(gtkButton)->fg[GTK_STATE_PRELIGHT]; - QColor textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); - pal.setBrush(QPalette::All, QPalette::ButtonText, textColor); - label.palette = pal; - } - label.rect = button.adjusted(style->xthickness, style->ythickness, - -style->xthickness - popupArrowSize, -style->ythickness); - proxy()->drawControl(CE_ToolButtonLabel, &label, painter, widget); - - if (toolbutton->subControls & SC_ToolButtonMenu) { - tool.rect = menuarea; - tool.state = mflags; - if ((mflags & State_Enabled && (mflags & (State_Sunken | State_Raised | State_MouseOver))) || !(mflags & State_AutoRaise)) - proxy()->drawPrimitive(PE_IndicatorButtonDropDown, &tool, painter, widget); - - proxy()->drawPrimitive(PE_IndicatorArrowDown, &tool, painter, widget); - - } else if (drawMenuArrow) { - QRect ir = toolbutton->rect; - QStyleOptionToolButton newBtn = *toolbutton; - newBtn.rect = QRect(ir.right() - popupArrowSize - style->xthickness - 3, ir.height()/2 - 1, popupArrowSize, popupArrowSize); - proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, painter, widget); - } - } - break; - -#endif // QT_NO_TOOLBUTTON -#ifndef QT_NO_SCROLLBAR - - case CC_ScrollBar: - if (const QStyleOptionSlider *scrollBar = qstyleoption_cast<const QStyleOptionSlider *>(option)) { - GtkWidget *gtkHScrollBar = d->gtkWidget("GtkHScrollbar"); - GtkWidget *gtkVScrollBar = d->gtkWidget("GtkVScrollbar"); - - // Fill background in case the scrollbar is partially transparent - painter->fillRect(option->rect, option->palette.background()); - - QRect rect = scrollBar->rect; - QRect scrollBarSubLine = proxy()->subControlRect(control, scrollBar, SC_ScrollBarSubLine, widget); - QRect scrollBarAddLine = proxy()->subControlRect(control, scrollBar, SC_ScrollBarAddLine, widget); - QRect scrollBarSlider = proxy()->subControlRect(control, scrollBar, SC_ScrollBarSlider, widget); - QRect grooveRect = proxy()->subControlRect(control, scrollBar, SC_ScrollBarGroove, widget); - bool horizontal = scrollBar->orientation == Qt::Horizontal; - GtkWidget * scrollbarWidget = horizontal ? gtkHScrollBar : gtkVScrollBar; - style = d->gtk_widget_get_style(scrollbarWidget); - gboolean trough_under_steppers = true; - gboolean trough_side_details = false; - gboolean activate_slider = false; - gboolean stepper_size = 14; - gint trough_border = 1; - if (!d->gtk_check_version(2, 10, 0)) { - d->gtk_widget_style_get((GtkWidget*)(scrollbarWidget), - "trough-border", &trough_border, - "trough-side-details", &trough_side_details, - "trough-under-steppers", &trough_under_steppers, - "activate-slider", &activate_slider, - "stepper-size", &stepper_size, NULL); - } - if (trough_under_steppers) { - scrollBarAddLine.adjust(trough_border, trough_border, -trough_border, -trough_border); - scrollBarSubLine.adjust(trough_border, trough_border, -trough_border, -trough_border); - scrollBarSlider.adjust(horizontal ? -trough_border : 0, horizontal ? 0 : -trough_border, - horizontal ? trough_border : 0, horizontal ? 0 : trough_border); - } - - // Some styles check the position of scrollbars in order to determine - // if lines should be painted when the scrollbar is in max or min positions. - int maximum = 2; - int fakePos = 0; - bool reverse = (option->direction == Qt::RightToLeft); - if (scrollBar->minimum == scrollBar->maximum) - maximum = 0; - if (scrollBar->sliderPosition == scrollBar->maximum) - fakePos = maximum; - else if (scrollBar->sliderPosition > scrollBar->minimum) - fakePos = maximum - 1; - - - GtkRange *range = (GtkRange*)(horizontal ? gtkHScrollBar : gtkVScrollBar); - GtkAdjustment *adjustment = 0; - - if (d->gtk_adjustment_configure) - adjustment = d->gtk_range_get_adjustment(range); - if (adjustment) { - d->gtk_adjustment_configure(adjustment, fakePos, 0, maximum, 0, 0, 0); - } else { - adjustment = (GtkAdjustment*)d->gtk_adjustment_new(fakePos, 0, maximum, 0, 0, 0); - d->gtk_range_set_adjustment(range, adjustment); - } - - if (scrollBar->subControls & SC_ScrollBarGroove) { - GtkStateType state = GTK_STATE_ACTIVE; - - if (!(option->state & State_Enabled)) - state = GTK_STATE_INSENSITIVE; - - if (trough_under_steppers) - grooveRect = option->rect; - - gtkPainter->paintBox(scrollbarWidget, "trough", grooveRect, state, GTK_SHADOW_IN, style); - } - - //paint slider - if (scrollBar->subControls & SC_ScrollBarSlider) { - GtkStateType state = GTK_STATE_NORMAL; - - if (!(option->state & State_Enabled)) - state = GTK_STATE_INSENSITIVE; - else if (activate_slider && - option->state & State_Sunken && (scrollBar->activeSubControls & SC_ScrollBarSlider)) - state = GTK_STATE_ACTIVE; - else if (option->state & State_MouseOver && (scrollBar->activeSubControls & SC_ScrollBarSlider)) - state = GTK_STATE_PRELIGHT; - - GtkShadowType shadow = GTK_SHADOW_OUT; - - if (trough_under_steppers) { - if (!horizontal) - scrollBarSlider.adjust(trough_border, 0, -trough_border, 0); - else - scrollBarSlider.adjust(0, trough_border, 0, -trough_border); - } - - gtkPainter->paintSlider(scrollbarWidget, "slider", scrollBarSlider, state, shadow, style, - horizontal ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL, QString(QLS("%0%1")).arg(fakePos).arg(maximum)); - } - - if (scrollBar->subControls & SC_ScrollBarAddLine) { - GtkAllocation vAllocation; - vAllocation.y = scrollBarAddLine.top(); - vAllocation.height = scrollBarAddLine.height() - rect.height() + 6; - d->gtk_widget_set_allocation(gtkVScrollBar, &vAllocation); - - GtkAllocation hAllocation; - hAllocation.x = scrollBarAddLine.right(); - hAllocation.width = scrollBarAddLine.width() - rect.width(); - d->gtk_widget_set_allocation(gtkHScrollBar, &hAllocation); - - GtkShadowType shadow = GTK_SHADOW_OUT; - GtkStateType state = GTK_STATE_NORMAL; - - if (!(option->state & State_Enabled) || (fakePos == maximum)) - state = GTK_STATE_INSENSITIVE; - else if (option->state & State_Sunken && (scrollBar->activeSubControls & SC_ScrollBarAddLine)) { - state = GTK_STATE_ACTIVE; - shadow = GTK_SHADOW_IN; - - } else if (option->state & State_MouseOver && (scrollBar->activeSubControls & SC_ScrollBarAddLine)) - state = GTK_STATE_PRELIGHT; - - gtkPainter->paintBox(scrollbarWidget, - horizontal ? "hscrollbar" : "vscrollbar", scrollBarAddLine, - state, shadow, style, QLS("add")); - - gtkPainter->paintArrow(scrollbarWidget, horizontal ? "hscrollbar" : "vscrollbar", scrollBarAddLine.adjusted(4, 4, -4, -4), - horizontal ? (reverse ? GTK_ARROW_LEFT : GTK_ARROW_RIGHT) : - GTK_ARROW_DOWN, state, GTK_SHADOW_NONE, false, style); - } - - if (scrollBar->subControls & SC_ScrollBarSubLine) { - GtkAllocation vAllocation; - vAllocation.y = 0; - vAllocation.height = scrollBarSubLine.height(); - d->gtk_widget_set_allocation(gtkVScrollBar, &vAllocation); - - GtkAllocation hAllocation; - hAllocation.x = 0; - hAllocation.width = scrollBarSubLine.width(); - d->gtk_widget_set_allocation(gtkHScrollBar, &hAllocation); - - GtkShadowType shadow = GTK_SHADOW_OUT; - GtkStateType state = GTK_STATE_NORMAL; - - if (!(option->state & State_Enabled) || (fakePos == 0)) - state = GTK_STATE_INSENSITIVE; - else if (option->state & State_Sunken && (scrollBar->activeSubControls & SC_ScrollBarSubLine)) { - shadow = GTK_SHADOW_IN; - state = GTK_STATE_ACTIVE; - - } else if (option->state & State_MouseOver && (scrollBar->activeSubControls & SC_ScrollBarSubLine)) - state = GTK_STATE_PRELIGHT; - - gtkPainter->paintBox(scrollbarWidget, horizontal ? "hscrollbar" : "vscrollbar", scrollBarSubLine, - state, shadow, style, QLS("sub")); - - gtkPainter->paintArrow(scrollbarWidget, horizontal ? "hscrollbar" : "vscrollbar", scrollBarSubLine.adjusted(4, 4, -4, -4), - horizontal ? (reverse ? GTK_ARROW_RIGHT : GTK_ARROW_LEFT) : - GTK_ARROW_UP, state, GTK_SHADOW_NONE, false, style); - } - } - break; - -#endif //QT_NO_SCROLLBAR -#ifndef QT_NO_SPINBOX - - case CC_SpinBox: - if (const QStyleOptionSpinBox *spinBox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) { - - GtkWidget *gtkSpinButton = spinBox->buttonSymbols == QAbstractSpinBox::NoButtons - ? d->gtkWidget("GtkEntry") - : d->gtkWidget("GtkSpinButton"); - bool isEnabled = (spinBox->state & State_Enabled); - bool hover = isEnabled && (spinBox->state & State_MouseOver); - bool sunken = (spinBox->state & State_Sunken); - bool upIsActive = (spinBox->activeSubControls == SC_SpinBoxUp); - bool downIsActive = (spinBox->activeSubControls == SC_SpinBoxDown); - bool reverse = (spinBox->direction == Qt::RightToLeft); - - QRect editArea = option->rect; - QRect editRect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxEditField, widget); - QRect upRect, downRect, buttonRect; - if (spinBox->buttonSymbols != QAbstractSpinBox::NoButtons) { - upRect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget); - downRect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget); - - //### Move this to subControlRect - upRect.setTop(option->rect.top()); - - if (reverse) - upRect.setLeft(option->rect.left()); - else - upRect.setRight(option->rect.right()); - - downRect.setBottom(option->rect.bottom()); - - if (reverse) - downRect.setLeft(option->rect.left()); - else - downRect.setRight(option->rect.right()); - - buttonRect = upRect | downRect; - - if (reverse) - editArea.setLeft(upRect.right()); - else - editArea.setRight(upRect.left()); - } - if (spinBox->frame) { - GtkStateType state = qt_gtk_state(option); - - if (!(option->state & State_Enabled)) - state = GTK_STATE_INSENSITIVE; - else if (option->state & State_HasFocus) - state = GTK_STATE_NORMAL; - else if (state == GTK_STATE_PRELIGHT) - state = GTK_STATE_NORMAL; - - style = d->gtk_widget_get_style(gtkSpinButton); - - - QString key; - - if (option->state & State_HasFocus) { - key += QLatin1Char('f'); - QGtkStylePrivate::gtkWidgetSetFocus(gtkSpinButton, true); - } - - uint resolve_mask = option->palette.resolve(); - - if (resolve_mask & (1 << QPalette::Base)) // Palette overridden by user - painter->fillRect(editRect, option->palette.base().color()); - else - gtkPainter->paintFlatBox(gtkSpinButton, "entry_bg", editArea.adjusted(style->xthickness, style->ythickness, - -style->xthickness, -style->ythickness), - option->state & State_Enabled ? - GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE, GTK_SHADOW_NONE, style, key); - - gtkPainter->paintShadow(gtkSpinButton, "entry", editArea, state, GTK_SHADOW_IN, d->gtk_widget_get_style(gtkSpinButton), key); - if (spinBox->buttonSymbols != QAbstractSpinBox::NoButtons) { - gtkPainter->paintBox(gtkSpinButton, "spinbutton", buttonRect, state, GTK_SHADOW_IN, style, key); - - upRect.setSize(downRect.size()); - if (!(option->state & State_Enabled)) - gtkPainter->paintBox(gtkSpinButton, "spinbutton_up", upRect, GTK_STATE_INSENSITIVE, GTK_SHADOW_IN, style, key); - else if (upIsActive && sunken) - gtkPainter->paintBox(gtkSpinButton, "spinbutton_up", upRect, GTK_STATE_ACTIVE, GTK_SHADOW_IN, style, key); - else if (upIsActive && hover) - gtkPainter->paintBox(gtkSpinButton, "spinbutton_up", upRect, GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, style, key); - else - gtkPainter->paintBox(gtkSpinButton, "spinbutton_up", upRect, GTK_STATE_NORMAL, GTK_SHADOW_OUT, style, key); - - if (!(option->state & State_Enabled)) - gtkPainter->paintBox(gtkSpinButton, "spinbutton_down", downRect, GTK_STATE_INSENSITIVE, GTK_SHADOW_IN, style, key); - else if (downIsActive && sunken) - gtkPainter->paintBox(gtkSpinButton, "spinbutton_down", downRect, GTK_STATE_ACTIVE, GTK_SHADOW_IN, style, key); - else if (downIsActive && hover) - gtkPainter->paintBox(gtkSpinButton, "spinbutton_down", downRect, GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, style, key); - else - gtkPainter->paintBox(gtkSpinButton, "spinbutton_down", downRect, GTK_STATE_NORMAL, GTK_SHADOW_OUT, style, key); - - if (option->state & State_HasFocus) - QGtkStylePrivate::gtkWidgetSetFocus(gtkSpinButton, false); - } - } - - if (spinBox->buttonSymbols == QAbstractSpinBox::PlusMinus) { - int centerX = upRect.center().x(); - int centerY = upRect.center().y(); - // plus/minus - - if (spinBox->activeSubControls == SC_SpinBoxUp && sunken) { - painter->drawLine(1 + centerX - 2, 1 + centerY, 1 + centerX + 2, 1 + centerY); - painter->drawLine(1 + centerX, 1 + centerY - 2, 1 + centerX, 1 + centerY + 2); - - } else { - painter->drawLine(centerX - 2, centerY, centerX + 2, centerY); - painter->drawLine(centerX, centerY - 2, centerX, centerY + 2); - } - centerX = downRect.center().x(); - centerY = downRect.center().y(); - - if (spinBox->activeSubControls == SC_SpinBoxDown && sunken) { - painter->drawLine(1 + centerX - 2, 1 + centerY, 1 + centerX + 2, 1 + centerY); - } else { - painter->drawLine(centerX - 2, centerY, centerX + 2, centerY); - } - - } else if (spinBox->buttonSymbols == QAbstractSpinBox::UpDownArrows) { - int size = d->getSpinboxArrowSize(); - int w = size / 2 - 1; - w -= w % 2 - 1; // force odd - int h = (w + 1)/2; - QRect arrowRect(0, 0, w, h); - arrowRect.moveCenter(upRect.center()); - // arrows - GtkStateType state = GTK_STATE_NORMAL; - - if (!(option->state & State_Enabled) || !(spinBox->stepEnabled & QAbstractSpinBox::StepUpEnabled)) - state = GTK_STATE_INSENSITIVE; - - gtkPainter->paintArrow(gtkSpinButton, "spinbutton", arrowRect, GTK_ARROW_UP, state, - GTK_SHADOW_NONE, false, style); - - arrowRect.moveCenter(downRect.center()); - - if (!(option->state & State_Enabled) || !(spinBox->stepEnabled & QAbstractSpinBox::StepDownEnabled)) - state = GTK_STATE_INSENSITIVE; - - gtkPainter->paintArrow(gtkSpinButton, "spinbutton", arrowRect, GTK_ARROW_DOWN, state, - GTK_SHADOW_NONE, false, style); - } - } - break; - -#endif // QT_NO_SPINBOX - -#ifndef QT_NO_SLIDER - - case CC_Slider: - if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) { - GtkWidget *hScaleWidget = d->gtkWidget("GtkHScale"); - GtkWidget *vScaleWidget = d->gtkWidget("GtkVScale"); - - QRect groove = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget); - QRect handle = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget); - - bool horizontal = slider->orientation == Qt::Horizontal; - bool ticksAbove = slider->tickPosition & QSlider::TicksAbove; - bool ticksBelow = slider->tickPosition & QSlider::TicksBelow; - - QBrush oldBrush = painter->brush(); - QPen oldPen = painter->pen(); - - QColor shadowAlpha(Qt::black); - shadowAlpha.setAlpha(10); - QColor highlightAlpha(Qt::white); - highlightAlpha.setAlpha(80); - - QGtkStylePrivate::gtk_widget_set_direction(hScaleWidget, slider->upsideDown ? - GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR); - GtkWidget *scaleWidget = horizontal ? hScaleWidget : vScaleWidget; - style = d->gtk_widget_get_style(scaleWidget); - - if ((option->subControls & SC_SliderGroove) && groove.isValid()) { - - GtkRange *range = (GtkRange*)scaleWidget; - GtkAdjustment *adjustment = 0; - if (d->gtk_adjustment_configure) - adjustment = d->gtk_range_get_adjustment(range); - if (adjustment) { - d->gtk_adjustment_configure(adjustment, - slider->sliderPosition, - slider->minimum, - slider->maximum, - slider->singleStep, - slider->singleStep, - slider->pageStep); - } else { - adjustment = (GtkAdjustment*)d->gtk_adjustment_new(slider->sliderPosition, - slider->minimum, - slider->maximum, - slider->singleStep, - slider->singleStep, - slider->pageStep); - d->gtk_range_set_adjustment(range, adjustment); - } - - int outerSize; - d->gtk_range_set_inverted(range, !horizontal); - d->gtk_widget_style_get(scaleWidget, "trough-border", &outerSize, NULL); - outerSize++; - - GtkStateType state = qt_gtk_state(option); - int focusFrameMargin = 2; - QRect grooveRect = option->rect.adjusted(focusFrameMargin, outerSize + focusFrameMargin, - -focusFrameMargin, -outerSize - focusFrameMargin); - - gboolean trough_side_details = false; // Indicates if the upper or lower scale background differs - if (!d->gtk_check_version(2, 10, 0)) - d->gtk_widget_style_get((GtkWidget*)(scaleWidget), "trough-side-details", &trough_side_details, NULL); - - if (!trough_side_details) { - gtkPainter->paintBox(scaleWidget, "trough", grooveRect, state, - GTK_SHADOW_IN, style, QString(QLS("p%0")).arg(slider->sliderPosition)); - } else { - QRect upperGroove = grooveRect; - QRect lowerGroove = grooveRect; - - if (horizontal) { - if (slider->upsideDown) { - lowerGroove.setLeft(handle.center().x()); - upperGroove.setRight(handle.center().x()); - } else { - upperGroove.setLeft(handle.center().x()); - lowerGroove.setRight(handle.center().x()); - } - } else { - if (!slider->upsideDown) { - lowerGroove.setBottom(handle.center().y()); - upperGroove.setTop(handle.center().y()); - } else { - upperGroove.setBottom(handle.center().y()); - lowerGroove.setTop(handle.center().y()); - } - } - - gtkPainter->paintBox(scaleWidget, "trough-upper", upperGroove, state, - GTK_SHADOW_IN, style, QString(QLS("p%0")).arg(slider->sliderPosition)); - gtkPainter->paintBox(scaleWidget, "trough-lower", lowerGroove, state, - GTK_SHADOW_IN, style, QString(QLS("p%0")).arg(slider->sliderPosition)); - } - } - - if (option->subControls & SC_SliderTickmarks) { - painter->setPen(darkOutline); - int tickSize = proxy()->pixelMetric(PM_SliderTickmarkOffset, option, widget); - int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget); - int interval = slider->tickInterval; - - if (interval <= 0) { - interval = slider->singleStep; - - if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval, - available) - - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, - 0, available) < 3) - interval = slider->pageStep; - } - - if (interval <= 0) - interval = 1; - - int v = slider->minimum; - int len = proxy()->pixelMetric(PM_SliderLength, slider, widget); - while (v <= slider->maximum + 1) { - if (v == slider->maximum + 1 && interval == 1) - break; - const int v_ = qMin(v, slider->maximum); - int pos = sliderPositionFromValue(slider->minimum, slider->maximum, - v_, (horizontal - ? slider->rect.width() - : slider->rect.height()) - len, - slider->upsideDown) + len / 2; - int extra = 2 - ((v_ == slider->minimum || v_ == slider->maximum) ? 1 : 0); - if (horizontal) { - if (ticksAbove) - painter->drawLine(pos, slider->rect.top() + extra, - pos, slider->rect.top() + tickSize); - if (ticksBelow) - painter->drawLine(pos, slider->rect.bottom() - extra, - pos, slider->rect.bottom() - tickSize); - - } else { - if (ticksAbove) - painter->drawLine(slider->rect.left() + extra, pos, - slider->rect.left() + tickSize, pos); - if (ticksBelow) - painter->drawLine(slider->rect.right() - extra, pos, - slider->rect.right() - tickSize, pos); - } - - // In the case where maximum is max int - int nextInterval = v + interval; - if (nextInterval < v) - break; - v = nextInterval; - } - } - - // Draw slider handle - if (option->subControls & SC_SliderHandle) { - GtkShadowType shadow = GTK_SHADOW_OUT; - GtkStateType state = GTK_STATE_NORMAL; - - if (!(option->state & State_Enabled)) - state = GTK_STATE_INSENSITIVE; - else if (option->state & State_MouseOver && option->activeSubControls & SC_SliderHandle) - state = GTK_STATE_PRELIGHT; - - bool horizontal = option->state & State_Horizontal; - - if (slider->state & State_HasFocus) { - QStyleOptionFocusRect fropt; - fropt.QStyleOption::operator=(*slider); - fropt.rect = slider->rect.adjusted(-1, -1 ,1, 1); - - if (horizontal) { - fropt.rect.setTop(handle.top() - 3); - fropt.rect.setBottom(handle.bottom() + 4); - - } else { - fropt.rect.setLeft(handle.left() - 3); - fropt.rect.setRight(handle.right() + 3); - } - proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget); - } - gtkPainter->paintSlider(scaleWidget, horizontal ? "hscale" : "vscale", handle, state, shadow, style, - horizontal ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL); - } - painter->setBrush(oldBrush); - painter->setPen(oldPen); - } - break; - case CC_Dial: - if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(option)) - QStyleHelper::drawDial(dial, painter); - break; - -#endif // QT_NO_SLIDER - - default: - QCommonStyle::drawComplexControl(control, option, painter, widget); - - break; - } -} - - -/*! - \reimp -*/ -void QGtkStyle::drawControl(ControlElement element, - const QStyleOption *option, - QPainter *painter, - const QWidget *widget) const -{ - Q_D(const QGtkStyle); - - if (!d->isThemeAvailable()) { - QCommonStyle::drawControl(element, option, painter, widget); - return; - } - - GtkStyle* style = d->gtkStyle(); - QGtkPainter* gtkPainter = d->gtkPainter(painter); - - switch (element) { - case CE_ProgressBarLabel: - if (const QStyleOptionProgressBar *bar = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) { - GtkWidget *gtkProgressBar = d->gtkWidget("GtkProgressBar"); - if (!gtkProgressBar) - return; - - QRect leftRect; - QRect rect = bar->rect; - GtkStyle *gtkProgressBarStyle = d->gtk_widget_get_style(gtkProgressBar); - GdkColor gdkText = gtkProgressBarStyle->fg[GTK_STATE_NORMAL]; - QColor textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); - gdkText = gtkProgressBarStyle->fg[GTK_STATE_PRELIGHT]; - QColor alternateTextColor= QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); - - painter->save(); - bool vertical = false, inverted = false; - if (const QStyleOptionProgressBarV2 *bar2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(option)) { - vertical = (bar2->orientation == Qt::Vertical); - inverted = bar2->invertedAppearance; - } - if (vertical) - rect = QRect(rect.left(), rect.top(), rect.height(), rect.width()); // flip width and height - const int progressIndicatorPos = (bar->progress - qreal(bar->minimum)) * rect.width() / - qMax(qreal(1.0), qreal(bar->maximum) - bar->minimum); - if (progressIndicatorPos >= 0 && progressIndicatorPos <= rect.width()) - leftRect = QRect(rect.left(), rect.top(), progressIndicatorPos, rect.height()); - if (vertical) - leftRect.translate(rect.width() - progressIndicatorPos, 0); - - bool flip = (!vertical && (((bar->direction == Qt::RightToLeft) && !inverted) || - ((bar->direction == Qt::LeftToRight) && inverted))); - - QRegion rightRect = rect; - rightRect = rightRect.subtracted(leftRect); - painter->setClipRegion(rightRect); - painter->setPen(flip ? alternateTextColor : textColor); - painter->drawText(rect, bar->text, QTextOption(Qt::AlignAbsolute | Qt::AlignHCenter | Qt::AlignVCenter)); - if (!leftRect.isNull()) { - painter->setPen(flip ? textColor : alternateTextColor); - painter->setClipRect(leftRect); - painter->drawText(rect, bar->text, QTextOption(Qt::AlignAbsolute | Qt::AlignHCenter | Qt::AlignVCenter)); - } - painter->restore(); - } - break; - case CE_PushButtonLabel: - if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) { - QRect ir = button->rect; - uint tf = Qt::AlignVCenter | Qt::TextShowMnemonic; - QPoint buttonShift; - - if (option->state & State_Sunken) - buttonShift = QPoint(pixelMetric(PM_ButtonShiftHorizontal, option, widget), - proxy()->pixelMetric(PM_ButtonShiftVertical, option, widget)); - - if (proxy()->styleHint(SH_UnderlineShortcut, button, widget)) - tf |= Qt::TextShowMnemonic; - else - tf |= Qt::TextHideMnemonic; - - if (!button->icon.isNull()) { - //Center both icon and text - QPoint point; - - QIcon::Mode mode = button->state & State_Enabled ? QIcon::Normal : QIcon::Disabled; - if (mode == QIcon::Normal && button->state & State_HasFocus) - mode = QIcon::Active; - - QIcon::State state = QIcon::Off; - - if (button->state & State_On) - state = QIcon::On; - - QPixmap pixmap = button->icon.pixmap(button->iconSize, mode, state); - int w = pixmap.width(); - int h = pixmap.height(); - - if (!button->text.isEmpty()) - w += button->fontMetrics.boundingRect(option->rect, tf, button->text).width() + 4; - - point = QPoint(ir.x() + ir.width() / 2 - w / 2, - ir.y() + ir.height() / 2 - h / 2); - - if (button->direction == Qt::RightToLeft) - point.rx() += pixmap.width(); - - painter->drawPixmap(visualPos(button->direction, button->rect, point + buttonShift), pixmap); - - if (button->direction == Qt::RightToLeft) - ir.translate(-point.x() - 2, 0); - else - ir.translate(point.x() + pixmap.width() + 2, 0); - - // left-align text if there is - if (!button->text.isEmpty()) - tf |= Qt::AlignLeft; - - } else { - tf |= Qt::AlignHCenter; - } - - ir.translate(buttonShift); - - if (button->features & QStyleOptionButton::HasMenu) - ir = ir.adjusted(0, 0, -pixelMetric(PM_MenuButtonIndicator, button, widget), 0); - - GtkWidget *gtkButton = d->gtkWidget("GtkButton"); - QPalette pal = button->palette; - int labelState = GTK_STATE_INSENSITIVE; - if (option->state & State_Enabled) - labelState = (option->state & State_MouseOver && !(option->state & State_Sunken)) ? - GTK_STATE_PRELIGHT : GTK_STATE_NORMAL; - - GdkColor gdkText = d->gtk_widget_get_style(gtkButton)->fg[labelState]; - QColor textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); - pal.setBrush(QPalette::ButtonText, textColor); - proxy()->drawItemText(painter, ir, tf, pal, (button->state & State_Enabled), - button->text, QPalette::ButtonText); - } - break; - - case CE_RadioButton: // Fall through - case CE_CheckBox: - if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) { - bool isRadio = (element == CE_RadioButton); - - // Draw prelight background - GtkWidget *gtkRadioButton = d->gtkWidget("GtkRadioButton"); - - if (option->state & State_MouseOver) { - gtkPainter->paintFlatBox(gtkRadioButton, "checkbutton", option->rect, - GTK_STATE_PRELIGHT, GTK_SHADOW_ETCHED_OUT, d->gtk_widget_get_style(gtkRadioButton)); - } - - QStyleOptionButton subopt = *btn; - subopt.rect = subElementRect(isRadio ? SE_RadioButtonIndicator - : SE_CheckBoxIndicator, btn, widget); - proxy()->drawPrimitive(isRadio ? PE_IndicatorRadioButton : PE_IndicatorCheckBox, - &subopt, painter, widget); - subopt.rect = subElementRect(isRadio ? SE_RadioButtonContents - : SE_CheckBoxContents, btn, widget); - // Get label text color - QPalette pal = subopt.palette; - int labelState = GTK_STATE_INSENSITIVE; - if (option->state & State_Enabled) - labelState = (option->state & State_MouseOver) ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL; - - GdkColor gdkText = d->gtk_widget_get_style(gtkRadioButton)->fg[labelState]; - QColor textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); - pal.setBrush(QPalette::WindowText, textColor); - subopt.palette = pal; - proxy()->drawControl(isRadio ? CE_RadioButtonLabel : CE_CheckBoxLabel, &subopt, painter, widget); - - if (btn->state & State_HasFocus) { - QStyleOptionFocusRect fropt; - fropt.QStyleOption::operator=(*btn); - fropt.rect = subElementRect(isRadio ? SE_RadioButtonFocusRect - : SE_CheckBoxFocusRect, btn, widget); - proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget); - } - } - break; - -#ifndef QT_NO_COMBOBOX - - case CE_ComboBoxLabel: - if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) { - QRect editRect = proxy()->subControlRect(CC_ComboBox, cb, SC_ComboBoxEditField, widget); - bool appearsAsList = !proxy()->styleHint(QStyle::SH_ComboBox_Popup, cb, widget); - painter->save(); - painter->setClipRect(editRect); - - if (!cb->currentIcon.isNull()) { - QIcon::Mode mode = cb->state & State_Enabled ? QIcon::Normal - : QIcon::Disabled; - QPixmap pixmap = cb->currentIcon.pixmap(cb->iconSize, mode); - QRect iconRect(editRect); - iconRect.setWidth(cb->iconSize.width() + 4); - - iconRect = alignedRect(cb->direction, - Qt::AlignLeft | Qt::AlignVCenter, - iconRect.size(), editRect); - - if (cb->editable) - painter->fillRect(iconRect, option->palette.brush(QPalette::Base)); - - proxy()->drawItemPixmap(painter, iconRect, Qt::AlignCenter, pixmap); - - if (cb->direction == Qt::RightToLeft) - editRect.translate(-4 - cb->iconSize.width(), 0); - else - editRect.translate(cb->iconSize.width() + 4, 0); - } - - if (!cb->currentText.isEmpty() && !cb->editable) { - GtkWidget *gtkCombo = d->gtkWidget("GtkComboBox"); - QPalette pal = cb->palette; - int labelState = GTK_STATE_INSENSITIVE; - - if (option->state & State_Enabled) - labelState = (option->state & State_MouseOver && !appearsAsList) ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL; - - GdkColor gdkText = d->gtk_widget_get_style(gtkCombo)->fg[labelState]; - - QColor textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); - - pal.setBrush(QPalette::ButtonText, textColor); - - proxy()->drawItemText(painter, editRect.adjusted(1, 0, -1, 0), - visualAlignment(cb->direction, Qt::AlignLeft | Qt::AlignVCenter), - pal, cb->state & State_Enabled, cb->currentText, QPalette::ButtonText); - } - - painter->restore(); - } - break; - -#endif // QT_NO_COMBOBOX - - case CE_DockWidgetTitle: - painter->save(); - if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) { - const QStyleOptionDockWidgetV2 *v2 - = qstyleoption_cast<const QStyleOptionDockWidgetV2*>(dwOpt); - bool verticalTitleBar = v2 == 0 ? false : v2->verticalTitleBar; - - QRect rect = dwOpt->rect; - QRect titleRect = subElementRect(SE_DockWidgetTitleBarText, option, widget).adjusted(-2, 0, -2, 0); - QRect r = rect.adjusted(0, 0, -1, -1); - if (verticalTitleBar) - r.adjust(0, 0, 0, -1); - - if (verticalTitleBar) { - QRect r = rect; - r.setSize(r.size().transposed()); - - titleRect = QRect(r.left() + rect.bottom() - - titleRect.bottom(), - r.top() + titleRect.left() - rect.left(), - titleRect.height(), titleRect.width()); - - painter->translate(r.left(), r.top() + r.width()); - painter->rotate(-90); - painter->translate(-r.left(), -r.top()); - - rect = r; - } - - if (!dwOpt->title.isEmpty()) { - QString titleText - = painter->fontMetrics().elidedText(dwOpt->title, - Qt::ElideRight, titleRect.width()); - proxy()->drawItemText(painter, - titleRect, - Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, dwOpt->palette, - dwOpt->state & State_Enabled, titleText, - QPalette::WindowText); - } - } - painter->restore(); - break; - - - - case CE_HeaderSection: - painter->save(); - - // Draws the header in tables. - if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) { - Q_UNUSED(header); - GtkWidget *gtkTreeView = d->gtkWidget("GtkTreeView"); - // Get the middle column - GtkTreeViewColumn *column = d->gtk_tree_view_get_column((GtkTreeView*)gtkTreeView, 1); - Q_ASSERT(column); - - GtkWidget *gtkTreeHeader = column->button; - GtkStateType state = qt_gtk_state(option); - GtkShadowType shadow = GTK_SHADOW_OUT; - - if (option->state & State_Sunken) - shadow = GTK_SHADOW_IN; - - gtkPainter->paintBox(gtkTreeHeader, "button", option->rect.adjusted(-1, 0, 0, 0), state, shadow, d->gtk_widget_get_style(gtkTreeHeader)); - } - - painter->restore(); - break; - -#ifndef QT_NO_SIZEGRIP - - case CE_SizeGrip: { - GtkWidget *gtkStatusbar = d->gtkWidget("GtkStatusbar.GtkFrame"); - GtkStyle *gtkStatusbarStyle = d->gtk_widget_get_style(gtkStatusbar); - QRect gripRect = option->rect.adjusted(0, 0, -gtkStatusbarStyle->xthickness, -gtkStatusbarStyle->ythickness); - gtkPainter->paintResizeGrip(gtkStatusbar, "statusbar", gripRect, GTK_STATE_NORMAL, - GTK_SHADOW_OUT, option->direction == Qt::RightToLeft ? - GDK_WINDOW_EDGE_SOUTH_WEST : GDK_WINDOW_EDGE_SOUTH_EAST, - gtkStatusbarStyle); - } - break; - -#endif // QT_NO_SIZEGRIP - - case CE_MenuBarEmptyArea: { - GtkWidget *gtkMenubar = d->gtkWidget("GtkMenuBar"); - GdkColor gdkBg = d->gtk_widget_get_style(gtkMenubar)->bg[GTK_STATE_NORMAL]; // Theme can depend on transparency - painter->fillRect(option->rect, QColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8)); - if (widget) { // See CE_MenuBarItem - QRect menuBarRect = widget->rect(); - QPixmap pixmap(menuBarRect.size()); - pixmap.fill(Qt::transparent); - QPainter pmPainter(&pixmap); - gtkPainter->reset(&pmPainter); - GtkShadowType shadow_type; - d->gtk_widget_style_get(gtkMenubar, "shadow-type", &shadow_type, NULL); - gtkPainter->paintBox(gtkMenubar, "menubar", menuBarRect, - GTK_STATE_NORMAL, shadow_type, d->gtk_widget_get_style(gtkMenubar)); - pmPainter.end(); - painter->drawPixmap(option->rect, pixmap, option->rect); - gtkPainter->reset(painter); - } - } - break; - - case CE_MenuBarItem: - painter->save(); - - if (const QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) { - GtkWidget *gtkMenubarItem = d->gtkWidget("GtkMenuBar.GtkMenuItem"); - GtkWidget *gtkMenubar = d->gtkWidget("GtkMenuBar"); - - style = d->gtk_widget_get_style(gtkMenubarItem); - - if (widget) { - // Since Qt does not currently allow filling the entire background - // we use a hack for this by making a complete menubar each time and - // paint with the correct offset inside it. Pixmap caching should resolve - // most of the performance penalty. - QRect menuBarRect = widget->rect(); - QPixmap pixmap(menuBarRect.size()); - pixmap.fill(Qt::transparent); - QPainter pmPainter(&pixmap); - gtkPainter->reset(&pmPainter); - GtkShadowType shadow_type; - d->gtk_widget_style_get(gtkMenubar, "shadow-type", &shadow_type, NULL); - GdkColor gdkBg = d->gtk_widget_get_style(gtkMenubar)->bg[GTK_STATE_NORMAL]; // Theme can depend on transparency - painter->fillRect(option->rect, QColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8)); - gtkPainter->paintBox(gtkMenubar, "menubar", menuBarRect, - GTK_STATE_NORMAL, shadow_type, d->gtk_widget_get_style(gtkMenubar)); - pmPainter.end(); - painter->drawPixmap(option->rect, pixmap, option->rect); - gtkPainter->reset(painter); - } - - QStyleOptionMenuItem item = *mbi; - bool act = mbi->state & State_Selected && mbi->state & State_Sunken; - bool dis = !(mbi->state & State_Enabled); - item.rect = mbi->rect; - GdkColor gdkText = style->fg[dis ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL]; - GdkColor gdkHText = style->fg[GTK_STATE_PRELIGHT]; - QColor normalTextColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); - QColor highlightedTextColor = QColor(gdkHText.red>>8, gdkHText.green>>8, gdkHText.blue>>8); - item.palette.setBrush(QPalette::HighlightedText, highlightedTextColor); - item.palette.setBrush(QPalette::Text, normalTextColor); - item.palette.setBrush(QPalette::ButtonText, normalTextColor); - QCommonStyle::drawControl(element, &item, painter, widget); - - if (act) { - GtkShadowType shadowType = GTK_SHADOW_NONE; - d->gtk_widget_style_get (gtkMenubarItem, "selected-shadow-type", &shadowType, NULL); - gtkPainter->paintBox(gtkMenubarItem, "menuitem", option->rect.adjusted(0, 0, 0, 3), - GTK_STATE_PRELIGHT, shadowType, style); - //draw text - QPalette::ColorRole textRole = dis ? QPalette::Text : QPalette::HighlightedText; - uint alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; - - if (!proxy()->styleHint(SH_UnderlineShortcut, mbi, widget)) - alignment |= Qt::TextHideMnemonic; - - proxy()->drawItemText(painter, item.rect, alignment, item.palette, mbi->state & State_Enabled, mbi->text, textRole); - } - } - painter->restore(); - break; - - case CE_Splitter: { - GtkWidget *gtkWindow = d->gtkWidget("GtkWindow"); // The Murrine Engine currently assumes a widget is passed - gtkPainter->paintHandle(gtkWindow, "splitter", option->rect, qt_gtk_state(option), GTK_SHADOW_NONE, - !(option->state & State_Horizontal) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL, - style); - } - break; - -#ifndef QT_NO_TOOLBAR - - case CE_ToolBar: - if (const QStyleOptionToolBar *toolbar = qstyleoption_cast<const QStyleOptionToolBar *>(option)) { - // Reserve the beveled appearance only for mainwindow toolbars - if (!(widget && qobject_cast<const QMainWindow*> (widget->parentWidget()))) - break; - - QRect rect = option->rect; - // There is a 1 pixel gap between toolbar lines in some styles (i.e Human) - if (toolbar->positionWithinLine != QStyleOptionToolBar::End) - rect.adjust(0, 0, 1, 0); - - GtkWidget *gtkToolbar = d->gtkWidget("GtkToolbar"); - GtkShadowType shadow_type = GTK_SHADOW_NONE; - d->gtk_widget_style_get(gtkToolbar, "shadow-type", &shadow_type, NULL); - gtkPainter->paintBox(gtkToolbar, "toolbar", rect, - GTK_STATE_NORMAL, shadow_type, d->gtk_widget_get_style(gtkToolbar)); - } - break; - -#endif // QT_NO_TOOLBAR - - case CE_MenuItem: - painter->save(); - - // Draws one item in a popup menu. - if (const QStyleOptionMenuItem *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) { - const int windowsItemHMargin = 3; // menu item hor text margin - const int windowsItemVMargin = 26; // menu item ver text margin - GtkWidget *gtkMenuItem = menuItem->checked ? d->gtkWidget("GtkMenu.GtkCheckMenuItem") : - d->gtkWidget("GtkMenu.GtkMenuItem"); - - style = d->gtk_widget_get_style(gtkMenuItem); - QColor shadow = option->palette.dark().color(); - - if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) { - GtkWidget *gtkMenuSeparator = d->gtkWidget("GtkMenu.GtkSeparatorMenuItem"); - painter->setPen(shadow.lighter(106)); - gboolean wide_separators = 0; - gint separator_height = 0; - guint horizontal_padding = 3; - QRect separatorRect = option->rect; - if (!d->gtk_check_version(2, 10, 0)) { - d->gtk_widget_style_get(gtkMenuSeparator, - "wide-separators", &wide_separators, - "separator-height", &separator_height, - "horizontal-padding", &horizontal_padding, - NULL); - } - GtkStyle *gtkMenuSeparatorStyle = d->gtk_widget_get_style(gtkMenuSeparator); - separatorRect.setHeight(option->rect.height() - 2 * gtkMenuSeparatorStyle->ythickness); - separatorRect.setWidth(option->rect.width() - 2 * (horizontal_padding + gtkMenuSeparatorStyle->xthickness)); - separatorRect.moveCenter(option->rect.center()); - if (wide_separators) - gtkPainter->paintBox(gtkMenuSeparator, "hseparator", - separatorRect, GTK_STATE_NORMAL, GTK_SHADOW_NONE, gtkMenuSeparatorStyle); - else - gtkPainter->paintHline(gtkMenuSeparator, "hseparator", - separatorRect, GTK_STATE_NORMAL, gtkMenuSeparatorStyle, - 0, option->rect.right() - 1, 1); - painter->restore(); - break; - } - - bool selected = menuItem->state & State_Selected && menuItem->state & State_Enabled; - - if (selected) { - QRect rect = option->rect; -#ifndef QT_NO_COMBOBOX - if (qobject_cast<const QComboBox*>(widget)) - rect = option->rect; -#endif - gtkPainter->paintBox(gtkMenuItem, "menuitem", rect, GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, style); - } - - bool checkable = menuItem->checkType != QStyleOptionMenuItem::NotCheckable; - bool checked = menuItem->checked; - bool enabled = menuItem->state & State_Enabled; - bool ignoreCheckMark = false; - - gint checkSize; - d->gtk_widget_style_get(d->gtkWidget("GtkMenu.GtkCheckMenuItem"), "indicator-size", &checkSize, NULL); - - int checkcol = qMax(menuItem->maxIconWidth, qMax(20, checkSize)); - -#ifndef QT_NO_COMBOBOX - - if (qobject_cast<const QComboBox*>(widget) || - (option->styleObject && option->styleObject->property("_q_isComboBoxPopupItem").toBool())) - ignoreCheckMark = true; // Ignore the checkmarks provided by the QComboMenuDelegate - -#endif - if (!ignoreCheckMark) { - // Check - QRect checkRect(option->rect.left() + 7, option->rect.center().y() - checkSize/2 + 1, checkSize, checkSize); - checkRect = visualRect(menuItem->direction, menuItem->rect, checkRect); - - if (checkable && menuItem->icon.isNull()) { - // Some themes such as aero-clone draw slightly outside the paint rect - int spacing = 1; // ### Consider using gtkCheckBox : "indicator-spacing" instead - - if (menuItem->checkType & QStyleOptionMenuItem::Exclusive) { - // Radio button - GtkShadowType shadow = GTK_SHADOW_OUT; - GtkStateType state = qt_gtk_state(option); - - if (selected) - state = GTK_STATE_PRELIGHT; - if (checked) - shadow = GTK_SHADOW_IN; - - gtkPainter->setClipRect(checkRect.adjusted(-spacing, -spacing, spacing, spacing)); - gtkPainter->paintOption(gtkMenuItem, checkRect.translated(-spacing, -spacing), state, shadow, - style, QLS("option")); - gtkPainter->setClipRect(QRect()); - - } else { - // Check box - if (menuItem->icon.isNull()) { - GtkShadowType shadow = GTK_SHADOW_OUT; - GtkStateType state = qt_gtk_state(option); - - if (selected) - state = GTK_STATE_PRELIGHT; - if (checked) - shadow = GTK_SHADOW_IN; - - gtkPainter->setClipRect(checkRect.adjusted(-spacing, -spacing, -spacing, -spacing)); - gtkPainter->paintCheckbox(gtkMenuItem, checkRect.translated(-spacing, -spacing), state, shadow, - style, QLS("check")); - gtkPainter->setClipRect(QRect()); - } - } - } - - } else { - // Ignore checkmark - if (menuItem->icon.isNull()) - checkcol = 0; - else - checkcol = menuItem->maxIconWidth; - } - - bool dis = !(menuItem->state & State_Enabled); - bool act = menuItem->state & State_Selected; - const QStyleOption *opt = option; - const QStyleOptionMenuItem *menuitem = menuItem; - QPainter *p = painter; - QRect vCheckRect = visualRect(opt->direction, menuitem->rect, - QRect(menuitem->rect.x() + 3, menuitem->rect.y(), - checkcol, menuitem->rect.height())); - - if (!menuItem->icon.isNull()) { - QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal; - - if (act && !dis) - mode = QIcon::Active; - - QPixmap pixmap; - int smallIconSize = proxy()->pixelMetric(PM_SmallIconSize, option, widget); - QSize iconSize(smallIconSize, smallIconSize); - -#ifndef QT_NO_COMBOBOX - if (const QComboBox *combo = qobject_cast<const QComboBox*>(widget)) - iconSize = combo->iconSize(); - -#endif // QT_NO_COMBOBOX - if (checked) - pixmap = menuItem->icon.pixmap(iconSize, mode, QIcon::On); - else - pixmap = menuItem->icon.pixmap(iconSize, mode); - - const int pixw = pixmap.width() / pixmap.devicePixelRatio(); - const int pixh = pixmap.height() / pixmap.devicePixelRatio(); - QRect pmr(0, 0, pixw, pixh); - pmr.moveCenter(vCheckRect.center() - QPoint(0, 1)); - painter->setPen(menuItem->palette.text().color()); - if (!ignoreCheckMark && checkable && checked) { - QStyleOption opt = *option; - - if (act) { - QColor activeColor = mergedColors(option->palette.background().color(), - option->palette.highlight().color()); - opt.palette.setBrush(QPalette::Button, activeColor); - } - opt.state |= State_Sunken; - opt.rect = vCheckRect; - proxy()->drawPrimitive(PE_PanelButtonCommand, &opt, painter, widget); - } - painter->drawPixmap(pmr.topLeft(), pixmap); - } - - GdkColor gdkText = style->fg[GTK_STATE_NORMAL]; - GdkColor gdkDText = style->fg[GTK_STATE_INSENSITIVE]; - GdkColor gdkHText = style->fg[GTK_STATE_PRELIGHT]; - uint resolve_mask = option->palette.resolve(); - QColor textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); - QColor disabledTextColor = QColor(gdkDText.red>>8, gdkDText.green>>8, gdkDText.blue>>8); - if (resolve_mask & (1 << QPalette::ButtonText)) { - textColor = option->palette.buttonText().color(); - disabledTextColor = option->palette.brush(QPalette::Disabled, QPalette::ButtonText).color(); - } - - QColor highlightedTextColor = QColor(gdkHText.red>>8, gdkHText.green>>8, gdkHText.blue>>8); - if (resolve_mask & (1 << QPalette::HighlightedText)) { - highlightedTextColor = option->palette.highlightedText().color(); - } - - if (selected) - painter->setPen(highlightedTextColor); - else - painter->setPen(textColor); - - int x, y, w, h; - menuitem->rect.getRect(&x, &y, &w, &h); - int tab = menuitem->tabWidth; - int xm = QGtkStylePrivate::menuItemFrame + checkcol + windowsItemHMargin; - int xpos = menuitem->rect.x() + xm + 1; - QRect textRect(xpos, y + windowsItemVMargin, w - xm - QGtkStylePrivate::menuRightBorder - tab + 1, h - 2 * windowsItemVMargin); - QRect vTextRect = visualRect(opt->direction, menuitem->rect, textRect); - QString s = menuitem->text; - - if (!s.isEmpty()) { // Draw text - p->save(); - int t = s.indexOf(QLatin1Char('\t')); - int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; - - if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget)) - text_flags |= Qt::TextHideMnemonic; - - // Draw shortcut right aligned - text_flags |= Qt::AlignRight; - - if (t >= 0) { - int rightMargin = 12; // Hardcode for now - QRect vShortcutRect = visualRect(opt->direction, menuitem->rect, - QRect(textRect.topRight(), QPoint(menuitem->rect.right() - rightMargin, textRect.bottom()))); - - if (dis) - p->setPen(disabledTextColor); - p->drawText(vShortcutRect, text_flags , s.mid(t + 1)); - s = s.left(t); - } - - text_flags &= ~Qt::AlignRight; - text_flags |= Qt::AlignLeft; - QFont font = menuitem->font; - if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem) - font.setBold(true); - p->setFont(font); - - if (dis) - p->setPen(disabledTextColor); - p->drawText(vTextRect, text_flags, s.left(t)); - p->restore(); - } - - // Arrow - if (menuItem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow - - QFontMetrics fm(menuitem->font); - int arrow_size = fm.ascent() + fm.descent() - 2 * style->ythickness; - gfloat arrow_scaling = 0.8; - int extra = 0; - if (!d->gtk_check_version(2, 16, 0)) { - // "arrow-scaling" is actually hardcoded and fails on hardy (see gtk+-2.12/gtkmenuitem.c) - // though the current documentation states otherwise - d->gtk_widget_style_get(gtkMenuItem, "arrow-scaling", &arrow_scaling, NULL); - // in versions < 2.16 ythickness was previously subtracted from the arrow_size - extra = 2 * style->ythickness; - } - - int horizontal_padding; - d->gtk_widget_style_get(gtkMenuItem, "horizontal-padding", &horizontal_padding, NULL); - - const int dim = static_cast<int>(arrow_size * arrow_scaling) + extra; - int xpos = menuItem->rect.left() + menuItem->rect.width() - horizontal_padding - dim; - QRect vSubMenuRect = visualRect(option->direction, menuItem->rect, - QRect(xpos, menuItem->rect.top() + - menuItem->rect.height() / 2 - dim / 2, dim, dim)); - GtkStateType state = enabled ? (act ? GTK_STATE_PRELIGHT: GTK_STATE_NORMAL) : GTK_STATE_INSENSITIVE; - GtkShadowType shadowType = (state == GTK_STATE_PRELIGHT) ? GTK_SHADOW_OUT : GTK_SHADOW_IN; - gtkPainter->paintArrow(gtkMenuItem, "menuitem", vSubMenuRect, option->direction == Qt::RightToLeft ? GTK_ARROW_LEFT : GTK_ARROW_RIGHT, state, - shadowType, false, style); - } - } - painter->restore(); - break; - - case CE_PushButton: - if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) { - GtkWidget *gtkButton = d->gtkWidget("GtkButton"); - proxy()->drawControl(CE_PushButtonBevel, btn, painter, widget); - QStyleOptionButton subopt = *btn; - subopt.rect = subElementRect(SE_PushButtonContents, btn, widget); - gint interiorFocus = true; - d->gtk_widget_style_get(gtkButton, "interior-focus", &interiorFocus, NULL); - GtkStyle *gtkButtonStyle = d->gtk_widget_get_style(gtkButton); - int xt = interiorFocus ? gtkButtonStyle->xthickness : 0; - int yt = interiorFocus ? gtkButtonStyle->ythickness : 0; - - if (btn->features & QStyleOptionButton::Flat && btn->state & State_HasFocus) - // The normal button focus rect does not work well for flat buttons in Clearlooks - proxy()->drawPrimitive(PE_FrameFocusRect, option, painter, widget); - else if (btn->state & State_HasFocus) - gtkPainter->paintFocus(gtkButton, "button", - option->rect.adjusted(xt, yt, -xt, -yt), - btn->state & State_Sunken ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL, - gtkButtonStyle); - - proxy()->drawControl(CE_PushButtonLabel, &subopt, painter, widget); - } - break; - -#ifndef QT_NO_TABBAR - - case CE_TabBarTabShape: - if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) { - GtkWidget *gtkNotebook = d->gtkWidget("GtkNotebook"); - style = d->gtk_widget_get_style(gtkNotebook); - - QRect rect = option->rect; - GtkShadowType shadow = GTK_SHADOW_OUT; - GtkStateType state = GTK_STATE_ACTIVE; - if (tab->state & State_Selected) - state = GTK_STATE_NORMAL; - - bool selected = (tab->state & State_Selected); - bool first = false, last = false; - if (widget) { - // This is most accurate and avoids resizing tabs while moving - first = tab->rect.left() == widget->rect().left(); - last = tab->rect.right() == widget->rect().right(); - } else if (option->direction == Qt::RightToLeft) { - bool tmp = first; - first = last; - last = tmp; - } - int topIndent = 3; - int bottomIndent = 1; - int tabOverlap = 1; - painter->save(); - - switch (tab->shape) { - case QTabBar::RoundedNorth: - if (!selected) - rect.adjust(first ? 0 : -tabOverlap, topIndent, last ? 0 : tabOverlap, -bottomIndent); - gtkPainter->paintExtention(gtkNotebook, "tab", rect, - state, shadow, GTK_POS_BOTTOM, style); - break; - - case QTabBar::RoundedSouth: - if (!selected) - rect.adjust(first ? 0 : -tabOverlap, 0, last ? 0 : tabOverlap, -topIndent); - gtkPainter->paintExtention(gtkNotebook, "tab", rect.adjusted(0, 1, 0, 0), - state, shadow, GTK_POS_TOP, style); - break; - - case QTabBar::RoundedWest: - if (!selected) - rect.adjust(topIndent, 0, -bottomIndent, 0); - gtkPainter->paintExtention(gtkNotebook, "tab", rect, state, shadow, GTK_POS_RIGHT, style); - break; - - case QTabBar::RoundedEast: - if (!selected) - rect.adjust(bottomIndent, 0, -topIndent, 0); - gtkPainter->paintExtention(gtkNotebook, "tab", rect, state, shadow, GTK_POS_LEFT, style); - break; - - default: - QCommonStyle::drawControl(element, option, painter, widget); - break; - } - - painter->restore(); - } - - break; - -#endif //QT_NO_TABBAR - - case CE_ProgressBarGroove: - if (const QStyleOptionProgressBar *bar = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) { - Q_UNUSED(bar); - GtkWidget *gtkProgressBar = d->gtkWidget("GtkProgressBar"); - GtkStateType state = qt_gtk_state(option); - gtkPainter->paintBox(gtkProgressBar, "trough", option->rect, state, GTK_SHADOW_IN, d->gtk_widget_get_style(gtkProgressBar)); - } - - break; - - case CE_ProgressBarContents: - if (const QStyleOptionProgressBar *bar = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) { - GtkStateType state = option->state & State_Enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE; - GtkWidget *gtkProgressBar = d->gtkWidget("GtkProgressBar"); - style = d->gtk_widget_get_style(gtkProgressBar); - gtkPainter->paintBox(gtkProgressBar, "trough", option->rect, state, GTK_SHADOW_IN, style); - int xt = style->xthickness; - int yt = style->ythickness; - QRect rect = bar->rect.adjusted(xt, yt, -xt, -yt); - bool vertical = false; - bool inverted = false; - bool indeterminate = (bar->minimum == 0 && bar->maximum == 0); - // Get extra style options if version 2 - - if (const QStyleOptionProgressBarV2 *bar2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(option)) { - vertical = (bar2->orientation == Qt::Vertical); - inverted = bar2->invertedAppearance; - } - - // If the orientation is vertical, we use a transform to rotate - // the progress bar 90 degrees clockwise. This way we can use the - // same rendering code for both orientations. - if (vertical) { - rect.translate(xt, -yt * 2); - rect = QRect(rect.left(), rect.top(), rect.height(), rect.width()); // Flip width and height - QTransform m = QTransform::fromTranslate(rect.height(), 0); - m.rotate(90.0); - painter->setTransform(m); - } - - int maxWidth = rect.width(); - int minWidth = 4; - - qint64 progress = (qint64)qMax(bar->progress, bar->minimum); // Workaround for bug in QProgressBar - double vc6_workaround = ((progress - qint64(bar->minimum)) / double(qint64(bar->maximum) - qint64(bar->minimum))) * maxWidth; - int progressBarWidth = (int(vc6_workaround) > minWidth ) ? int(vc6_workaround) : minWidth; - int width = indeterminate ? maxWidth : progressBarWidth; - bool reverse = (!vertical && (bar->direction == Qt::RightToLeft)) || vertical; - - if (inverted) - reverse = !reverse; - - int maximum = 2; - int fakePos = 0; - if (bar->minimum == bar->maximum) - maximum = 0; - if (bar->progress == bar->maximum) - fakePos = maximum; - else if (bar->progress > bar->minimum) - fakePos = maximum - 1; - - QRect progressBar; - - if (!indeterminate) { - if (!reverse) - progressBar.setRect(rect.left(), rect.top(), width, rect.height()); - else - progressBar.setRect(rect.right() - width, rect.top(), width, rect.height()); -#ifndef QT_NO_ANIMATION - d->stopAnimation(option->styleObject); -#endif - } else { - Q_D(const QGtkStyle); - int slideWidth = ((rect.width() - 4) * 2) / 3; - int step = 0; -#ifndef QT_NO_ANIMATION - if (QProgressStyleAnimation *animation = qobject_cast<QProgressStyleAnimation*>(d->animation(option->styleObject))) - step = animation->progressStep(slideWidth); - else - d->startAnimation(new QProgressStyleAnimation(d->animationFps, option->styleObject)); -#endif - progressBar.setRect(rect.left() + step, rect.top(), slideWidth / 2, rect.height()); - } - - QString key = QString(QLS("%0")).arg(fakePos); - if (inverted) { - key += QLatin1String("inv"); - gtkPainter->setFlipHorizontal(true); - } - gtkPainter->paintBox(gtkProgressBar, "bar", progressBar, GTK_STATE_SELECTED, GTK_SHADOW_OUT, style, key); - } - - break; - - default: - QCommonStyle::drawControl(element, option, painter, widget); - } -} - -/*! - \reimp -*/ -QRect QGtkStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option, - SubControl subControl, const QWidget *widget) const -{ - Q_D(const QGtkStyle); - - QRect rect = QCommonStyle::subControlRect(control, option, subControl, widget); - if (!d->isThemeAvailable()) - return QCommonStyle::subControlRect(control, option, subControl, widget); - - switch (control) { - case CC_ScrollBar: - break; - case CC_Slider: - if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) { - int tickSize = proxy()->pixelMetric(PM_SliderTickmarkOffset, option, widget); - switch (subControl) { - case SC_SliderHandle: { - if (slider->orientation == Qt::Horizontal) { - rect.setHeight(proxy()->pixelMetric(PM_SliderThickness)); - rect.setWidth(proxy()->pixelMetric(PM_SliderLength)); - int centerY = slider->rect.center().y() - rect.height() / 2; - if (slider->tickPosition & QSlider::TicksAbove) - centerY += tickSize; - if (slider->tickPosition & QSlider::TicksBelow) - centerY -= tickSize; - rect.moveTop(centerY); - } else { - rect.setWidth(proxy()->pixelMetric(PM_SliderThickness)); - rect.setHeight(proxy()->pixelMetric(PM_SliderLength)); - int centerX = slider->rect.center().x() - rect.width() / 2; - if (slider->tickPosition & QSlider::TicksAbove) - centerX += tickSize; - if (slider->tickPosition & QSlider::TicksBelow) - centerX -= tickSize; - rect.moveLeft(centerX); - } - } - break; - case SC_SliderGroove: { - QPoint grooveCenter = slider->rect.center(); - if (slider->orientation == Qt::Horizontal) { - rect.setHeight(7); - if (slider->tickPosition & QSlider::TicksAbove) - grooveCenter.ry() += tickSize; - if (slider->tickPosition & QSlider::TicksBelow) - grooveCenter.ry() -= tickSize; - } else { - rect.setWidth(7); - if (slider->tickPosition & QSlider::TicksAbove) - grooveCenter.rx() += tickSize; - if (slider->tickPosition & QSlider::TicksBelow) - grooveCenter.rx() -= tickSize; - } - rect.moveCenter(grooveCenter); - break; - } - default: - break; - } - } - break; - -#ifndef QT_NO_GROUPBOX - - case CC_GroupBox: - if (const QStyleOptionGroupBox * groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(option)) { - rect = option->rect.adjusted(0, groupBoxTopMargin, 0, -groupBoxBottomMargin); - int topMargin = 0; - int topHeight = 0; - topHeight = 10; - QRect frameRect = rect; - frameRect.setTop(topMargin); - - if (subControl == SC_GroupBoxFrame) - return rect; - else if (subControl == SC_GroupBoxContents) { - int margin = 0; - int leftMarginExtension = 8; - return frameRect.adjusted(leftMarginExtension + margin, margin + topHeight + groupBoxTitleMargin, -margin, -margin); - } - - QFontMetrics fontMetrics = option->fontMetrics; - if (qobject_cast<const QGroupBox *>(widget)) { - //Prepare metrics for a bold font - QFont font = widget->font(); - font.setBold(true); - fontMetrics = QFontMetrics(font); -#ifndef QT_NO_ACCESSIBILITY - } else if (QStyleHelper::isInstanceOf(groupBox->styleObject, QAccessible::Grouping)) { - QVariant var = groupBox->styleObject->property("font"); - if (var.isValid() && var.canConvert<QFont>()) { - QFont font = var.value<QFont>(); - font.setBold(true); - fontMetrics = QFontMetrics(font); - } -#endif // QT_NO_ACCESSIBILITY - } - - QSize textRect = fontMetrics.boundingRect(groupBox->text).size() + QSize(4, 4); - int indicatorWidth = proxy()->pixelMetric(PM_IndicatorWidth, option, widget); - int indicatorHeight = proxy()->pixelMetric(PM_IndicatorHeight, option, widget); - - if (subControl == SC_GroupBoxCheckBox) { - rect.setWidth(indicatorWidth); - rect.setHeight(indicatorHeight); - rect.moveTop((textRect.height() - indicatorHeight) / 2); - - } else if (subControl == SC_GroupBoxLabel) { - if (groupBox->subControls & SC_GroupBoxCheckBox) - rect.adjust(indicatorWidth + 4, 0, 0, 0); - rect.setSize(textRect); - } - rect = visualRect(option->direction, option->rect, rect); - } - - return rect; - -#endif -#ifndef QT_NO_SPINBOX - - case CC_SpinBox: - if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) { - GtkWidget *gtkSpinButton = d->gtkWidget("GtkSpinButton"); - int center = spinbox->rect.height() / 2; - GtkStyle *gtkSpinButtonStyle = d->gtk_widget_get_style(gtkSpinButton); - int xt = spinbox->frame ? gtkSpinButtonStyle->xthickness : 0; - int yt = spinbox->frame ? gtkSpinButtonStyle->ythickness : 0; - int y = yt; - - QSize bs; - bs.setHeight(qMax(8, spinbox->rect.height()/2 - y)); - bs.setWidth(d->getSpinboxArrowSize()); - int x, lx, rx; - x = spinbox->rect.width() - y - bs.width() + 2; - lx = xt; - rx = x - xt; - - switch (subControl) { - - case SC_SpinBoxUp: - if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons) - return QRect(); - rect = QRect(x, xt, bs.width(), center - yt); - break; - - case SC_SpinBoxDown: - if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons) - return QRect(); - rect = QRect(x, center, bs.width(), spinbox->rect.bottom() - center - yt + 1); - break; - - case SC_SpinBoxEditField: - if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons) - rect = QRect(lx, yt, spinbox->rect.width() - 2*xt, spinbox->rect.height() - 2*yt); - else - rect = QRect(lx, yt, rx - qMax(xt - 1, 0), spinbox->rect.height() - 2*yt); - break; - - case SC_SpinBoxFrame: - rect = spinbox->rect; - - default: - break; - } - - rect = visualRect(spinbox->direction, spinbox->rect, rect); - } - - break; - -#endif // Qt_NO_SPINBOX -#ifndef QT_NO_COMBOBOX - - case CC_TitleBar: - if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) { - SubControl sc = subControl; - QRect &ret = rect; - const int indent = 3; - const int controlTopMargin = 3; - const int controlBottomMargin = 3; - const int controlWidthMargin = 2; - const int controlHeight = tb->rect.height() - controlTopMargin - controlBottomMargin ; - const int delta = controlHeight + controlWidthMargin; - int offset = 0; - - bool isMinimized = tb->titleBarState & Qt::WindowMinimized; - bool isMaximized = tb->titleBarState & Qt::WindowMaximized; - - switch (sc) { - case SC_TitleBarLabel: - if (tb->titleBarFlags & (Qt::WindowTitleHint | Qt::WindowSystemMenuHint)) { - ret = tb->rect; - if (tb->titleBarFlags & Qt::WindowSystemMenuHint) - ret.adjust(delta, 0, -delta, 0); - if (tb->titleBarFlags & Qt::WindowMinimizeButtonHint) - ret.adjust(0, 0, -delta, 0); - if (tb->titleBarFlags & Qt::WindowMaximizeButtonHint) - ret.adjust(0, 0, -delta, 0); - if (tb->titleBarFlags & Qt::WindowShadeButtonHint) - ret.adjust(0, 0, -delta, 0); - if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) - ret.adjust(0, 0, -delta, 0); - } - break; - case SC_TitleBarContextHelpButton: - if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) - offset += delta; - case SC_TitleBarMinButton: - if (!isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint)) - offset += delta; - else if (sc == SC_TitleBarMinButton) - break; - case SC_TitleBarNormalButton: - if (isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint)) - offset += delta; - else if (isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint)) - offset += delta; - else if (sc == SC_TitleBarNormalButton) - break; - case SC_TitleBarMaxButton: - if (!isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint)) - offset += delta; - else if (sc == SC_TitleBarMaxButton) - break; - case SC_TitleBarShadeButton: - if (!isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint)) - offset += delta; - else if (sc == SC_TitleBarShadeButton) - break; - case SC_TitleBarUnshadeButton: - if (isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint)) - offset += delta; - else if (sc == SC_TitleBarUnshadeButton) - break; - case SC_TitleBarCloseButton: - if (tb->titleBarFlags & Qt::WindowSystemMenuHint) - offset += delta; - else if (sc == SC_TitleBarCloseButton) - break; - ret.setRect(tb->rect.right() - indent - offset, tb->rect.top() + controlTopMargin, - controlHeight, controlHeight); - break; - case SC_TitleBarSysMenu: - if (tb->titleBarFlags & Qt::WindowSystemMenuHint) { - ret.setRect(tb->rect.left() + controlWidthMargin + indent, tb->rect.top() + controlTopMargin, - controlHeight, controlHeight); - } - break; - default: - break; - } - ret = visualRect(tb->direction, tb->rect, ret); - } - break; - case CC_ComboBox: - if (const QStyleOptionComboBox *box = qstyleoption_cast<const QStyleOptionComboBox *>(option)) { - // We employ the gtk widget to position arrows and separators for us - GtkWidget *gtkCombo = box->editable ? d->gtkWidget("GtkComboBoxEntry") - : d->gtkWidget("GtkComboBox"); - d->gtk_widget_set_direction(gtkCombo, (option->direction == Qt::RightToLeft) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR); - GtkAllocation geometry = {0, 0, qMax(0, option->rect.width()), qMax(0, option->rect.height())}; - d->gtk_widget_size_allocate(gtkCombo, &geometry); - int appears_as_list = !proxy()->styleHint(QStyle::SH_ComboBox_Popup, option, widget); - QHashableLatin1Literal arrowPath("GtkComboBoxEntry.GtkToggleButton"); - if (!box->editable) { - if (appears_as_list) - arrowPath = "GtkComboBox.GtkToggleButton"; - else - arrowPath = "GtkComboBox.GtkToggleButton.GtkHBox.GtkArrow"; - } - - GtkWidget *arrowWidget = d->gtkWidget(arrowPath); - if (!arrowWidget) - return QCommonStyle::subControlRect(control, option, subControl, widget); - - GtkAllocation allocation; - d->gtk_widget_get_allocation(arrowWidget, &allocation); - QRect buttonRect(option->rect.left() + allocation.x, - option->rect.top() + allocation.y, - allocation.width, allocation.height); - - switch (subControl) { - - case SC_ComboBoxArrow: // Note: this indicates the arrowbutton for editable combos - rect = buttonRect; - break; - - case SC_ComboBoxEditField: { - rect = visualRect(option->direction, option->rect, rect); - int xMargin = box->editable ? 1 : 4, yMargin = 2; - GtkStyle *gtkComboStyle = d->gtk_widget_get_style(gtkCombo); - rect.setRect(option->rect.left() + gtkComboStyle->xthickness + xMargin, - option->rect.top() + gtkComboStyle->ythickness + yMargin, - option->rect.width() - buttonRect.width() - 2*(gtkComboStyle->xthickness + xMargin), - option->rect.height() - 2*(gtkComboStyle->ythickness + yMargin)); - rect = visualRect(option->direction, option->rect, rect); - break; - } - - default: - break; - } - } - - break; -#endif // QT_NO_COMBOBOX - - default: - break; - } - - return rect; -} - -/*! - \reimp -*/ -QSize QGtkStyle::sizeFromContents(ContentsType type, const QStyleOption *option, - const QSize &size, const QWidget *widget) const -{ - Q_D(const QGtkStyle); - - QSize newSize = QCommonStyle::sizeFromContents(type, option, size, widget); - if (!d->isThemeAvailable()) - return newSize; - - switch (type) { - case CT_GroupBox: - // Since we use a bold font we have to recalculate base width - if (const QGroupBox *gb = qobject_cast<const QGroupBox*>(widget)) { - QFont font = gb->font(); - font.setBold(true); - QFontMetrics metrics(font); - int baseWidth = metrics.width(gb->title()) + metrics.width(QLatin1Char(' ')); - if (gb->isCheckable()) { - baseWidth += proxy()->pixelMetric(QStyle::PM_IndicatorWidth, option, widget); - baseWidth += proxy()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, option, widget); - } - newSize.setWidth(qMax(baseWidth, newSize.width())); - } - newSize += QSize(4, 1 + groupBoxBottomMargin + groupBoxTopMargin + groupBoxTitleMargin); // Add some space below the groupbox - break; - case CT_ToolButton: - if (const QStyleOptionToolButton *toolbutton = qstyleoption_cast<const QStyleOptionToolButton *>(option)) { - GtkWidget *gtkButton = d->gtkWidget("GtkToolButton.GtkButton"); - GtkStyle *gtkButtonStyle = d->gtk_widget_get_style(gtkButton); - newSize = size + QSize(2 * gtkButtonStyle->xthickness, 2 + 2 * gtkButtonStyle->ythickness); - if (widget && qobject_cast<QToolBar *>(widget->parentWidget())) { - QSize minSize(0, 25); - if (toolbutton->toolButtonStyle != Qt::ToolButtonTextOnly) - minSize = toolbutton->iconSize + QSize(12, 12); - newSize = newSize.expandedTo(minSize); - } - - if (toolbutton->features & QStyleOptionToolButton::HasMenu) - newSize += QSize(6, 0); - } - break; - case CT_SpinBox: - // QSpinBox does some nasty things that depends on CT_LineEdit - newSize = newSize + QSize(0, -d->gtk_widget_get_style(d->gtkWidget("GtkSpinButton"))->ythickness * 2); - break; - case CT_RadioButton: - case CT_CheckBox: - newSize += QSize(0, 1); - break; - case CT_PushButton: - if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) { - if (!btn->icon.isNull() && btn->iconSize.height() > 16) - newSize -= QSize(0, 2); // From cleanlooksstyle - newSize += QSize(0, 1); - GtkWidget *gtkButton = d->gtkWidget("GtkButton"); - gint focusPadding, focusWidth; - d->gtk_widget_style_get(gtkButton, "focus-padding", &focusPadding, NULL); - d->gtk_widget_style_get(gtkButton, "focus-line-width", &focusWidth, NULL); - newSize = size; - GtkStyle *gtkButtonStyle = d->gtk_widget_get_style(gtkButton); - newSize += QSize(2*gtkButtonStyle->xthickness + 4, 2*gtkButtonStyle->ythickness); - newSize += QSize(2*(focusWidth + focusPadding + 2), 2*(focusWidth + focusPadding)); - - GtkWidget *gtkButtonBox = d->gtkWidget("GtkHButtonBox"); - gint minWidth = 85, minHeight = 0; - d->gtk_widget_style_get(gtkButtonBox, "child-min-width", &minWidth, - "child-min-height", &minHeight, NULL); - if (!btn->text.isEmpty() && newSize.width() < minWidth) - newSize.setWidth(minWidth); - if (newSize.height() < minHeight) - newSize.setHeight(minHeight); - } - break; - case CT_Slider: { - GtkWidget *gtkSlider = d->gtkWidget("GtkHScale"); - GtkStyle *gtkSliderStyle = d->gtk_widget_get_style(gtkSlider); - newSize = size + QSize(2*gtkSliderStyle->xthickness, 2*gtkSliderStyle->ythickness); } - break; - case CT_LineEdit: { - GtkWidget *gtkEntry = d->gtkWidget("GtkEntry"); - GtkStyle *gtkEntryStyle = d->gtk_widget_get_style(gtkEntry); - newSize = size + QSize(2*gtkEntryStyle->xthickness, 2 + 2*gtkEntryStyle->ythickness); } - break; - case CT_ItemViewItem: - newSize += QSize(0, 2); - break; - case CT_ComboBox: - if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(option)) { - GtkWidget *gtkCombo = d->gtkWidget("GtkComboBox"); - QRect arrowButtonRect = proxy()->subControlRect(CC_ComboBox, combo, SC_ComboBoxArrow, widget); - GtkStyle *gtkComboStyle = d->gtk_widget_get_style(gtkCombo); - newSize = size + QSize(12 + arrowButtonRect.width() + 2*gtkComboStyle->xthickness, 4 + 2*gtkComboStyle->ythickness); - - if (!(widget && qobject_cast<QToolBar *>(widget->parentWidget()))) - newSize += QSize(0, 2); - } - break; - case CT_TabBarTab: - if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) { - if (!tab->icon.isNull()) - newSize += QSize(6, 0); - } - newSize += QSize(1, 1); - break; - case CT_MenuBarItem: - newSize += QSize(QGtkStylePrivate::menuItemHMargin * 4, QGtkStylePrivate::menuItemVMargin * 2 + 2); - break; - case CT_SizeGrip: - newSize += QSize(4, 4); - break; - case CT_MdiControls: - if (const QStyleOptionComplex *styleOpt = qstyleoption_cast<const QStyleOptionComplex *>(option)) { - int width = 0; - if (styleOpt->subControls & SC_MdiMinButton) - width += 19 + 1; - if (styleOpt->subControls & SC_MdiNormalButton) - width += 19 + 1; - if (styleOpt->subControls & SC_MdiCloseButton) - width += 19 + 1; - newSize = QSize(width, 19); - } else { - newSize = QSize(60, 19); - } - break; - case CT_MenuItem: - if (const QStyleOptionMenuItem *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) { - int w = newSize.width(); - int maxpmw = menuItem->maxIconWidth; - int tabSpacing = 20; - if (menuItem->text.contains(QLatin1Char('\t'))) - w += tabSpacing; - else if (menuItem->menuItemType == QStyleOptionMenuItem::SubMenu) - w += 2 * QGtkStylePrivate::menuArrowHMargin; - else if (menuItem->menuItemType == QStyleOptionMenuItem::DefaultItem) { - // adjust the font and add the difference in size. - // it would be better if the font could be adjusted in the initStyleOption qmenu func!! - QFontMetrics fm(menuItem->font); - QFont fontBold = menuItem->font; - fontBold.setBold(true); - QFontMetrics fmBold(fontBold); - w += fmBold.width(menuItem->text) - fm.width(menuItem->text); - } - - int checkcol = qMax<int>(maxpmw, QGtkStylePrivate::menuCheckMarkWidth); // Windows always shows a check column - w += checkcol; - w += int(QGtkStylePrivate::menuRightBorder) + 10; - - newSize.setWidth(w); - - int textMargin = 8; - if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) { - GtkWidget *gtkMenuSeparator = d->gtkWidget("GtkMenu.GtkSeparatorMenuItem"); - GtkRequisition sizeReq = {0, 0}; - d->gtk_widget_size_request(gtkMenuSeparator, &sizeReq); - newSize = QSize(newSize.width(), sizeReq.height); - break; - } - - GtkWidget *gtkMenuItem = d->gtkWidget("GtkMenu.GtkCheckMenuItem"); - GtkStyle* style = d->gtk_widget_get_style(gtkMenuItem); - - // Note we get the perfect height for the default font since we - // set a fake text label on the gtkMenuItem - // But if custom fonts are used on the widget we need a minimum size - GtkRequisition sizeReq = {0, 0}; - d->gtk_widget_size_request(gtkMenuItem, &sizeReq); - newSize.setHeight(qMax(newSize.height() - 4, sizeReq.height)); - newSize += QSize(textMargin + style->xthickness - 1, 0); - - gint checkSize; - d->gtk_widget_style_get(gtkMenuItem, "indicator-size", &checkSize, NULL); - newSize.setWidth(newSize.width() + qMax(0, checkSize - 20)); - } - break; - default: - break; - } - - return newSize; -} - - -/*! \reimp */ -QPixmap QGtkStyle::standardPixmap(StandardPixmap sp, const QStyleOption *option, - const QWidget *widget) const -{ - Q_D(const QGtkStyle); - - if (!d->isThemeAvailable()) - return QCommonStyle::standardPixmap(sp, option, widget); - - QPixmap pixmap; - switch (sp) { - - case SP_TitleBarNormalButton: { - QImage restoreButton(dock_widget_restore_xpm); - QColor alphaCorner = restoreButton.color(2); - alphaCorner.setAlpha(80); - restoreButton.setColor(2, alphaCorner.rgba()); - alphaCorner.setAlpha(180); - restoreButton.setColor(4, alphaCorner.rgba()); - return QPixmap::fromImage(restoreButton); - } - break; - - case SP_TitleBarCloseButton: // Fall through - case SP_DockWidgetCloseButton: { - - QImage closeButton(dock_widget_close_xpm); - QColor alphaCorner = closeButton.color(2); - alphaCorner.setAlpha(80); - closeButton.setColor(2, alphaCorner.rgba()); - return QPixmap::fromImage(closeButton); - } - break; - - case SP_DialogDiscardButton: - return qt_gtk_get_icon(GTK_STOCK_DELETE); - case SP_DialogOkButton: - return qt_gtk_get_icon(GTK_STOCK_OK); - case SP_DialogCancelButton: - return qt_gtk_get_icon(GTK_STOCK_CANCEL); - case SP_DialogYesButton: - return qt_gtk_get_icon(GTK_STOCK_YES); - case SP_DialogNoButton: - return qt_gtk_get_icon(GTK_STOCK_NO); - case SP_DialogOpenButton: - return qt_gtk_get_icon(GTK_STOCK_OPEN); - case SP_DialogCloseButton: - return qt_gtk_get_icon(GTK_STOCK_CLOSE); - case SP_DialogApplyButton: - return qt_gtk_get_icon(GTK_STOCK_APPLY); - case SP_DialogSaveButton: - return qt_gtk_get_icon(GTK_STOCK_SAVE); - case SP_MessageBoxWarning: - return qt_gtk_get_icon(GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG); - case SP_MessageBoxQuestion: - return qt_gtk_get_icon(GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG); - case SP_MessageBoxInformation: - return qt_gtk_get_icon(GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG); - case SP_MessageBoxCritical: - return qt_gtk_get_icon(GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_DIALOG); - default: - return QCommonStyle::standardPixmap(sp, option, widget); - } - return pixmap; -} - -/*! - \reimp -*/ -QIcon QGtkStyle::standardIcon(StandardPixmap standardIcon, - const QStyleOption *option, - const QWidget *widget) const -{ - Q_D(const QGtkStyle); - - if (!d->isThemeAvailable()) - return QCommonStyle::standardIcon(standardIcon, option, widget); - switch (standardIcon) { - case SP_DialogDiscardButton: - return qt_gtk_get_icon(GTK_STOCK_DELETE); - case SP_DialogOkButton: - return qt_gtk_get_icon(GTK_STOCK_OK); - case SP_DialogCancelButton: - return qt_gtk_get_icon(GTK_STOCK_CANCEL); - case SP_DialogYesButton: - return qt_gtk_get_icon(GTK_STOCK_YES); - case SP_DialogNoButton: - return qt_gtk_get_icon(GTK_STOCK_NO); - case SP_DialogOpenButton: - return qt_gtk_get_icon(GTK_STOCK_OPEN); - case SP_DialogCloseButton: - return qt_gtk_get_icon(GTK_STOCK_CLOSE); - case SP_DialogApplyButton: - return qt_gtk_get_icon(GTK_STOCK_APPLY); - case SP_DialogSaveButton: - return qt_gtk_get_icon(GTK_STOCK_SAVE); - case SP_MessageBoxWarning: - return qt_gtk_get_icon(GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG); - case SP_MessageBoxQuestion: - return qt_gtk_get_icon(GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG); - case SP_MessageBoxInformation: - return qt_gtk_get_icon(GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG); - case SP_MessageBoxCritical: - return qt_gtk_get_icon(GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_DIALOG); - default: - return QCommonStyle::standardIcon(standardIcon, option, widget); - } -} - - -/*! \reimp */ -QRect QGtkStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const -{ - Q_D(const QGtkStyle); - - QRect r = QCommonStyle::subElementRect(element, option, widget); - if (!d->isThemeAvailable()) - return r; - - switch (element) { - case SE_PushButtonFocusRect: - r.adjust(0, 1, 0, -1); - break; - case SE_DockWidgetTitleBarText: { - const QStyleOptionDockWidgetV2 *v2 - = qstyleoption_cast<const QStyleOptionDockWidgetV2*>(option); - bool verticalTitleBar = v2 == 0 ? false : v2->verticalTitleBar; - if (verticalTitleBar) { - r.adjust(0, 0, 0, -4); - } else { - if (option->direction == Qt::LeftToRight) - r.adjust(4, 0, 0, 0); - else - r.adjust(0, 0, -4, 0); - } - - break; - } - case SE_ProgressBarLabel: - case SE_ProgressBarContents: - case SE_ProgressBarGroove: - return option->rect; - case SE_PushButtonContents: - if (!d->gtk_check_version(2, 10, 0)) { - GtkWidget *gtkButton = d->gtkWidget("GtkButton"); - GtkBorder *border = 0; - d->gtk_widget_style_get(gtkButton, "inner-border", &border, NULL); - if (border) { - r = option->rect.adjusted(border->left, border->top, -border->right, -border->bottom); - d->gtk_border_free(border); - } else { - r = option->rect.adjusted(1, 1, -1, -1); - } - r = visualRect(option->direction, option->rect, r); - } - break; - default: - break; - } - - return r; -} - -/*! - \reimp -*/ -QRect QGtkStyle::itemPixmapRect(const QRect &r, int flags, const QPixmap &pixmap) const -{ - return QCommonStyle::itemPixmapRect(r, flags, pixmap); -} - -/*! - \reimp -*/ -void QGtkStyle::drawItemPixmap(QPainter *painter, const QRect &rect, - int alignment, const QPixmap &pixmap) const -{ - QCommonStyle::drawItemPixmap(painter, rect, alignment, pixmap); -} - -/*! - \reimp -*/ -QStyle::SubControl QGtkStyle::hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, - const QPoint &pt, const QWidget *w) const -{ - return QCommonStyle::hitTestComplexControl(cc, opt, pt, w); -} - -/*! - \reimp -*/ -QPixmap QGtkStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, - const QStyleOption *opt) const -{ - return QCommonStyle::generatedIconPixmap(iconMode, pixmap, opt); -} - -/*! - \reimp -*/ -void QGtkStyle::drawItemText(QPainter *painter, const QRect &rect, int alignment, const QPalette &pal, - bool enabled, const QString& text, QPalette::ColorRole textRole) const -{ - return QCommonStyle::drawItemText(painter, rect, alignment, pal, enabled, text, textRole); -} - -QT_END_NAMESPACE - -#endif //!defined(QT_NO_STYLE_QGTK) diff --git a/src/widgets/styles/qgtkstyle_p.cpp b/src/widgets/styles/qgtkstyle_p.cpp deleted file mode 100644 index 00682c1c0f..0000000000 --- a/src/widgets/styles/qgtkstyle_p.cpp +++ /dev/null @@ -1,891 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qgtkstyle_p_p.h" - -// This file is responsible for resolving all GTK functions we use -// dynamically. This is done to avoid link-time dependancy on GTK -// as well as crashes occurring due to usage of the GTK_QT engines -// -// Additionally we create a map of common GTK widgets that we can pass -// to the GTK theme engine as many engines resort to querying the -// actual widget pointers for details that are not covered by the -// state flags - -#include <QtCore/qglobal.h> -#if !defined(QT_NO_STYLE_GTK) - -#include <QtCore/QEvent> -#include <QtCore/QFile> -#include <QtCore/QStringList> -#include <QtCore/QTextStream> -#include <QtCore/QHash> -#include <QtCore/QUrl> -#include <QtCore/QLibrary> -#include <QtCore/QDebug> - -#include <private/qgtk2painter_p.h> -#include <private/qapplication_p.h> -#include <private/qiconloader_p.h> -#include <qpa/qplatformfontdatabase.h> - -#include <QtWidgets/QMenu> -#include <QtWidgets/QStyle> -#include <QtWidgets/QApplication> -#include <QtGui/QPixmapCache> -#include <QtWidgets/QStatusBar> -#include <QtWidgets/QMenuBar> -#include <QtWidgets/QToolBar> -#include <QtWidgets/QToolButton> - -#ifndef Q_OS_MAC -// X11 Includes: - -// the following is necessary to work around breakage in many versions -// of XFree86's Xlib.h still in use -// ### which versions? -#if defined(_XLIB_H_) // crude hack, but... -#error "cannot include <X11/Xlib.h> before this file" -#endif -#define XRegisterIMInstantiateCallback qt_XRegisterIMInstantiateCallback -#define XUnregisterIMInstantiateCallback qt_XUnregisterIMInstantiateCallback -#define XSetIMValues qt_XSetIMValues -#include <X11/Xlib.h> -#undef XRegisterIMInstantiateCallback -#undef XUnregisterIMInstantiateCallback -#undef XSetIMValues -#endif - -QT_BEGIN_NAMESPACE - -Q_GLOBAL_STATIC(QGtkStyleUpdateScheduler, styleScheduler) - -Ptr_gtk_container_forall QGtkStylePrivate::gtk_container_forall = 0; -Ptr_gtk_init QGtkStylePrivate::gtk_init = 0; -Ptr_gtk_style_attach QGtkStylePrivate::gtk_style_attach = 0; -Ptr_gtk_window_new QGtkStylePrivate::gtk_window_new = 0; -Ptr_gtk_widget_destroy QGtkStylePrivate::gtk_widget_destroy = 0; -Ptr_gtk_widget_realize QGtkStylePrivate::gtk_widget_realize = 0; -Ptr_gtk_widget_set_default_direction QGtkStylePrivate::gtk_widget_set_default_direction = 0; -Ptr_gtk_widget_modify_color QGtkStylePrivate::gtk_widget_modify_fg = 0; -Ptr_gtk_widget_modify_color QGtkStylePrivate::gtk_widget_modify_bg = 0; -Ptr_gtk_arrow_new QGtkStylePrivate::gtk_arrow_new = 0; -Ptr_gtk_menu_item_new_with_label QGtkStylePrivate::gtk_menu_item_new_with_label = 0; -Ptr_gtk_check_menu_item_new_with_label QGtkStylePrivate::gtk_check_menu_item_new_with_label = 0; -Ptr_gtk_menu_bar_new QGtkStylePrivate::gtk_menu_bar_new = 0; -Ptr_gtk_menu_new QGtkStylePrivate::gtk_menu_new = 0; -Ptr_gtk_button_new QGtkStylePrivate::gtk_button_new = 0; -Ptr_gtk_tool_button_new QGtkStylePrivate::gtk_tool_button_new = 0; -Ptr_gtk_hbutton_box_new QGtkStylePrivate::gtk_hbutton_box_new = 0; -Ptr_gtk_check_button_new QGtkStylePrivate::gtk_check_button_new = 0; -Ptr_gtk_radio_button_new QGtkStylePrivate::gtk_radio_button_new = 0; -Ptr_gtk_spin_button_new QGtkStylePrivate::gtk_spin_button_new = 0; -Ptr_gtk_frame_new QGtkStylePrivate::gtk_frame_new = 0; -Ptr_gtk_expander_new QGtkStylePrivate::gtk_expander_new = 0; -Ptr_gtk_statusbar_new QGtkStylePrivate::gtk_statusbar_new = 0; -Ptr_gtk_entry_new QGtkStylePrivate::gtk_entry_new = 0; -Ptr_gtk_hscale_new QGtkStylePrivate::gtk_hscale_new = 0; -Ptr_gtk_vscale_new QGtkStylePrivate::gtk_vscale_new = 0; -Ptr_gtk_hscrollbar_new QGtkStylePrivate::gtk_hscrollbar_new = 0; -Ptr_gtk_vscrollbar_new QGtkStylePrivate::gtk_vscrollbar_new = 0; -Ptr_gtk_scrolled_window_new QGtkStylePrivate::gtk_scrolled_window_new = 0; -Ptr_gtk_notebook_new QGtkStylePrivate::gtk_notebook_new = 0; -Ptr_gtk_toolbar_new QGtkStylePrivate::gtk_toolbar_new = 0; -Ptr_gtk_toolbar_insert QGtkStylePrivate::gtk_toolbar_insert = 0; -Ptr_gtk_separator_tool_item_new QGtkStylePrivate::gtk_separator_tool_item_new = 0; -Ptr_gtk_tree_view_new QGtkStylePrivate::gtk_tree_view_new = 0; -Ptr_gtk_combo_box_new QGtkStylePrivate::gtk_combo_box_new = 0; -Ptr_gtk_combo_box_entry_new QGtkStylePrivate::gtk_combo_box_entry_new = 0; -Ptr_gtk_progress_bar_new QGtkStylePrivate::gtk_progress_bar_new = 0; -Ptr_gtk_container_add QGtkStylePrivate::gtk_container_add = 0; -Ptr_gtk_menu_shell_append QGtkStylePrivate::gtk_menu_shell_append = 0; -Ptr_gtk_range_get_adjustment QGtkStylePrivate::gtk_range_get_adjustment = 0; -Ptr_gtk_range_set_adjustment QGtkStylePrivate::gtk_range_set_adjustment = 0; -Ptr_gtk_range_set_inverted QGtkStylePrivate::gtk_range_set_inverted = 0; -Ptr_gtk_icon_factory_lookup_default QGtkStylePrivate::gtk_icon_factory_lookup_default = 0; -Ptr_gtk_icon_theme_get_default QGtkStylePrivate::gtk_icon_theme_get_default = 0; -Ptr_gtk_widget_get_style QGtkStylePrivate::gtk_widget_get_style = 0; -Ptr_gtk_widget_style_get QGtkStylePrivate::gtk_widget_style_get = 0; -Ptr_gtk_icon_set_render_icon QGtkStylePrivate::gtk_icon_set_render_icon = 0; -Ptr_gtk_fixed_new QGtkStylePrivate::gtk_fixed_new = 0; -Ptr_gtk_tree_view_column_new QGtkStylePrivate::gtk_tree_view_column_new = 0; -Ptr_gtk_tree_view_get_column QGtkStylePrivate::gtk_tree_view_get_column = 0; -Ptr_gtk_tree_view_append_column QGtkStylePrivate::gtk_tree_view_append_column = 0; -Ptr_gtk_adjustment_configure QGtkStylePrivate::gtk_adjustment_configure = 0; -Ptr_gtk_adjustment_new QGtkStylePrivate::gtk_adjustment_new = 0; -Ptr_gtk_menu_item_set_submenu QGtkStylePrivate::gtk_menu_item_set_submenu = 0; -Ptr_gtk_settings_get_default QGtkStylePrivate::gtk_settings_get_default = 0; -Ptr_gtk_separator_menu_item_new QGtkStylePrivate::gtk_separator_menu_item_new = 0; -Ptr_gtk_widget_size_allocate QGtkStylePrivate::gtk_widget_size_allocate = 0; -Ptr_gtk_widget_size_request QGtkStylePrivate::gtk_widget_size_request = 0; -Ptr_gtk_widget_set_direction QGtkStylePrivate::gtk_widget_set_direction = 0; -Ptr_gtk_widget_path QGtkStylePrivate::gtk_widget_path = 0; -Ptr_gtk_container_get_type QGtkStylePrivate::gtk_container_get_type = 0; -Ptr_gtk_window_get_type QGtkStylePrivate::gtk_window_get_type = 0; -Ptr_gtk_widget_get_type QGtkStylePrivate::gtk_widget_get_type = 0; -Ptr_gtk_widget_get_parent QGtkStylePrivate::gtk_widget_get_parent = 0; -Ptr_gtk_widget_is_toplevel QGtkStylePrivate::gtk_widget_is_toplevel = 0; -Ptr_gtk_widget_get_toplevel QGtkStylePrivate::gtk_widget_get_toplevel = 0; -Ptr_gtk_rc_get_style_by_paths QGtkStylePrivate::gtk_rc_get_style_by_paths = 0; -Ptr_gtk_check_version QGtkStylePrivate::gtk_check_version = 0; -Ptr_gtk_border_free QGtkStylePrivate::gtk_border_free = 0; -Ptr_gtk_widget_get_allocation QGtkStylePrivate::gtk_widget_get_allocation = 0; -Ptr_gtk_widget_set_allocation QGtkStylePrivate::gtk_widget_set_allocation = 0; -Ptr_gtk_widget_set_can_default QGtkStylePrivate::gtk_widget_set_can_default = 0; -Ptr_gtk_window_set_default QGtkStylePrivate::gtk_window_set_default = 0; - -Ptr_gdk_event_new QGtkStylePrivate::gdk_event_new = 0; -Ptr_gdk_event_free QGtkStylePrivate::gdk_event_free = 0; -Ptr_gtk_widget_send_focus_change QGtkStylePrivate::gtk_widget_send_focus_change = 0; - -Ptr_pango_font_description_get_size QGtkStylePrivate::pango_font_description_get_size = 0; -Ptr_pango_font_description_get_weight QGtkStylePrivate::pango_font_description_get_weight = 0; -Ptr_pango_font_description_get_family QGtkStylePrivate::pango_font_description_get_family = 0; -Ptr_pango_font_description_get_style QGtkStylePrivate::pango_font_description_get_style = 0; - -Ptr_gdk_pixbuf_get_pixels QGtkStylePrivate::gdk_pixbuf_get_pixels = 0; -Ptr_gdk_pixbuf_get_width QGtkStylePrivate::gdk_pixbuf_get_width = 0; -Ptr_gdk_pixbuf_get_height QGtkStylePrivate::gdk_pixbuf_get_height = 0; -Ptr_gdk_pixbuf_new QGtkStylePrivate::gdk_pixbuf_new = 0; -Ptr_gdk_pixbuf_unref QGtkStylePrivate::gdk_pixbuf_unref = 0; -Ptr_gdk_color_free QGtkStylePrivate::gdk_color_free = 0; -Ptr_gdk_x11_window_set_user_time QGtkStylePrivate::gdk_x11_window_set_user_time = 0; -Ptr_gdk_x11_drawable_get_xid QGtkStylePrivate::gdk_x11_drawable_get_xid = 0; -Ptr_gdk_x11_drawable_get_xdisplay QGtkStylePrivate::gdk_x11_drawable_get_xdisplay = 0; - -Ptr_gconf_client_get_default QGtkStylePrivate::gconf_client_get_default = 0; -Ptr_gconf_client_get_string QGtkStylePrivate::gconf_client_get_string = 0; -Ptr_gconf_client_get_bool QGtkStylePrivate::gconf_client_get_bool = 0; - -Ptr_gnome_icon_lookup_sync QGtkStylePrivate::gnome_icon_lookup_sync = 0; -Ptr_gnome_vfs_init QGtkStylePrivate::gnome_vfs_init = 0; - -#ifndef Q_OS_MAC -typedef int (*x11ErrorHandler)(Display*, XErrorEvent*); -#endif - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(QGtkStylePrivate*); - -QT_BEGIN_NAMESPACE - -static void gtkStyleSetCallback(GtkWidget*) -{ - qRegisterMetaType<QGtkStylePrivate *>(); - - // We have to let this function return and complete the event - // loop to ensure that all gtk widgets have been styled before - // updating - QMetaObject::invokeMethod(styleScheduler(), "updateTheme", Qt::QueuedConnection); -} - -static void update_toolbar_style(GtkWidget *gtkToolBar, GParamSpec *, gpointer) -{ - GtkToolbarStyle toolbar_style = GTK_TOOLBAR_ICONS; - g_object_get(gtkToolBar, "toolbar-style", &toolbar_style, NULL); - QWidgetList widgets = QApplication::allWidgets(); - for (int i = 0; i < widgets.size(); ++i) { - QWidget *widget = widgets.at(i); - if (qobject_cast<QToolButton*>(widget)) { - QEvent event(QEvent::StyleChange); - QApplication::sendEvent(widget, &event); - } - } -} - -static QHashableLatin1Literal classPath(GtkWidget *widget) -{ - char *class_path; - QGtkStylePrivate::gtk_widget_path (widget, NULL, &class_path, NULL); - - char *copy = class_path; - if (strncmp(copy, "GtkWindow.", 10) == 0) - copy += 10; - if (strncmp(copy, "GtkFixed.", 9) == 0) - copy += 9; - - copy = strdup(copy); - - g_free(class_path); - - return QHashableLatin1Literal::fromData(copy); -} - - - -bool QGtkStyleFilter::eventFilter(QObject *obj, QEvent *e) -{ - if (e->type() == QEvent::ApplicationPaletteChange) { - // Only do this the first time since this will also - // generate applicationPaletteChange events - if (!qt_app_palettes_hash() || qt_app_palettes_hash()->isEmpty()) { - stylePrivate->applyCustomPaletteHash(); - } - } - return QObject::eventFilter(obj, e); -} - -QList<QGtkStylePrivate *> QGtkStylePrivate::instances; -QGtkStylePrivate::WidgetMap *QGtkStylePrivate::widgetMap = 0; - -QGtkStylePrivate::QGtkStylePrivate() - : QCommonStylePrivate() - , filter(this) -{ - instances.append(this); - animationFps = 60; -} - -QGtkStylePrivate::~QGtkStylePrivate() -{ - instances.removeOne(this); -} - -void QGtkStylePrivate::init() -{ - resolveGtk(); - initGtkWidgets(); -} - -QGtkPainter* QGtkStylePrivate::gtkPainter(QPainter *painter) -{ - // TODO: choose between gtk2 and gtk3 - static QGtk2Painter instance; - instance.reset(painter); - return &instance; -} - -GtkWidget* QGtkStylePrivate::gtkWidget(const QHashableLatin1Literal &path) -{ - GtkWidget *widget = gtkWidgetMap()->value(path); - if (!widget) { - // Theme might have rearranged widget internals - widget = gtkWidgetMap()->value(path); - } - return widget; -} - -GtkStyle* QGtkStylePrivate::gtkStyle(const QHashableLatin1Literal &path) -{ - if (GtkWidget *w = gtkWidgetMap()->value(path)) - return QGtkStylePrivate::gtk_widget_get_style(w); - return 0; -} - -void QGtkStylePrivate::gtkWidgetSetFocus(GtkWidget *widget, bool focus) -{ - if (QGtkStylePrivate::gtk_widget_send_focus_change) { - GdkEvent *event = QGtkStylePrivate::gdk_event_new(GDK_FOCUS_CHANGE); - event->focus_change.type = GDK_FOCUS_CHANGE; - event->focus_change.in = focus; - QGtkStylePrivate::gtk_widget_send_focus_change(widget, event); - QGtkStylePrivate::gdk_event_free(event); - } else { -#if defined(GTK_WIDGET_SET_FLAGS) && defined(GTK_WIDGET_UNSET_FLAGS) - if (focus) - GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS); - else - GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS); -#endif - } -} - -/*! \internal - * Get references to gtk functions after we dynamically load the library. - */ -void QGtkStylePrivate::resolveGtk() const -{ -#ifndef QT_NO_LIBRARY - // enforce the "0" suffix, so we'll open libgtk-x11-2.0.so.0 - QLibrary libgtk(QLS("gtk-x11-2.0"), 0, 0); - - gtk_init = (Ptr_gtk_init)libgtk.resolve("gtk_init"); - gtk_window_new = (Ptr_gtk_window_new)libgtk.resolve("gtk_window_new"); - gtk_style_attach = (Ptr_gtk_style_attach)libgtk.resolve("gtk_style_attach"); - gtk_widget_destroy = (Ptr_gtk_widget_destroy)libgtk.resolve("gtk_widget_destroy"); - gtk_widget_realize = (Ptr_gtk_widget_realize)libgtk.resolve("gtk_widget_realize"); - - gdk_pixbuf_get_pixels = (Ptr_gdk_pixbuf_get_pixels)libgtk.resolve("gdk_pixbuf_get_pixels"); - gdk_pixbuf_get_width = (Ptr_gdk_pixbuf_get_width)libgtk.resolve("gdk_pixbuf_get_width"); - gdk_pixbuf_get_height = (Ptr_gdk_pixbuf_get_height)libgtk.resolve("gdk_pixbuf_get_height"); - gdk_pixbuf_new = (Ptr_gdk_pixbuf_new)libgtk.resolve("gdk_pixbuf_new"); - gdk_pixbuf_unref = (Ptr_gdk_pixbuf_unref)libgtk.resolve("gdk_pixbuf_unref"); - gdk_color_free = (Ptr_gdk_color_free)libgtk.resolve("gdk_color_free"); - gdk_x11_window_set_user_time = (Ptr_gdk_x11_window_set_user_time)libgtk.resolve("gdk_x11_window_set_user_time"); - gdk_x11_drawable_get_xid = (Ptr_gdk_x11_drawable_get_xid)libgtk.resolve("gdk_x11_drawable_get_xid"); - gdk_x11_drawable_get_xdisplay = (Ptr_gdk_x11_drawable_get_xdisplay)libgtk.resolve("gdk_x11_drawable_get_xdisplay"); - - gtk_widget_set_default_direction = (Ptr_gtk_widget_set_default_direction)libgtk.resolve("gtk_widget_set_default_direction"); - gtk_widget_modify_fg = (Ptr_gtk_widget_modify_color)libgtk.resolve("gtk_widget_modify_fg"); - gtk_widget_modify_bg = (Ptr_gtk_widget_modify_color)libgtk.resolve("gtk_widget_modify_bg"); - gtk_arrow_new = (Ptr_gtk_arrow_new)libgtk.resolve("gtk_arrow_new"); - gtk_menu_item_new_with_label = (Ptr_gtk_menu_item_new_with_label)libgtk.resolve("gtk_menu_item_new_with_label"); - gtk_check_menu_item_new_with_label = (Ptr_gtk_check_menu_item_new_with_label)libgtk.resolve("gtk_check_menu_item_new_with_label"); - gtk_menu_bar_new = (Ptr_gtk_menu_bar_new)libgtk.resolve("gtk_menu_bar_new"); - gtk_menu_new = (Ptr_gtk_menu_new)libgtk.resolve("gtk_menu_new"); - gtk_toolbar_new = (Ptr_gtk_toolbar_new)libgtk.resolve("gtk_toolbar_new"); - gtk_separator_tool_item_new = (Ptr_gtk_separator_tool_item_new)libgtk.resolve("gtk_separator_tool_item_new"); - gtk_toolbar_insert = (Ptr_gtk_toolbar_insert)libgtk.resolve("gtk_toolbar_insert"); - gtk_button_new = (Ptr_gtk_button_new)libgtk.resolve("gtk_button_new"); - gtk_tool_button_new = (Ptr_gtk_tool_button_new)libgtk.resolve("gtk_tool_button_new"); - gtk_hbutton_box_new = (Ptr_gtk_hbutton_box_new)libgtk.resolve("gtk_hbutton_box_new"); - gtk_check_button_new = (Ptr_gtk_check_button_new)libgtk.resolve("gtk_check_button_new"); - gtk_radio_button_new = (Ptr_gtk_radio_button_new)libgtk.resolve("gtk_radio_button_new"); - gtk_notebook_new = (Ptr_gtk_notebook_new)libgtk.resolve("gtk_notebook_new"); - gtk_progress_bar_new = (Ptr_gtk_progress_bar_new)libgtk.resolve("gtk_progress_bar_new"); - gtk_spin_button_new = (Ptr_gtk_spin_button_new)libgtk.resolve("gtk_spin_button_new"); - gtk_hscale_new = (Ptr_gtk_hscale_new)libgtk.resolve("gtk_hscale_new"); - gtk_vscale_new = (Ptr_gtk_vscale_new)libgtk.resolve("gtk_vscale_new"); - gtk_hscrollbar_new = (Ptr_gtk_hscrollbar_new)libgtk.resolve("gtk_hscrollbar_new"); - gtk_vscrollbar_new = (Ptr_gtk_vscrollbar_new)libgtk.resolve("gtk_vscrollbar_new"); - gtk_scrolled_window_new = (Ptr_gtk_scrolled_window_new)libgtk.resolve("gtk_scrolled_window_new"); - gtk_menu_shell_append = (Ptr_gtk_menu_shell_append)libgtk.resolve("gtk_menu_shell_append"); - gtk_entry_new = (Ptr_gtk_entry_new)libgtk.resolve("gtk_entry_new"); - gtk_tree_view_new = (Ptr_gtk_tree_view_new)libgtk.resolve("gtk_tree_view_new"); - gtk_combo_box_new = (Ptr_gtk_combo_box_new)libgtk.resolve("gtk_combo_box_new"); - gtk_combo_box_entry_new = (Ptr_gtk_combo_box_entry_new)libgtk.resolve("gtk_combo_box_entry_new"); - gtk_range_get_adjustment = (Ptr_gtk_range_get_adjustment)libgtk.resolve("gtk_range_get_adjustment"); - gtk_range_set_adjustment = (Ptr_gtk_range_set_adjustment)libgtk.resolve("gtk_range_set_adjustment"); - gtk_range_set_inverted = (Ptr_gtk_range_set_inverted)libgtk.resolve("gtk_range_set_inverted"); - gtk_container_add = (Ptr_gtk_container_add)libgtk.resolve("gtk_container_add"); - gtk_icon_factory_lookup_default = (Ptr_gtk_icon_factory_lookup_default)libgtk.resolve("gtk_icon_factory_lookup_default"); - gtk_icon_theme_get_default = (Ptr_gtk_icon_theme_get_default)libgtk.resolve("gtk_icon_theme_get_default"); - gtk_widget_get_style = (Ptr_gtk_widget_get_style)libgtk.resolve("gtk_widget_get_style"); - gtk_widget_style_get = (Ptr_gtk_widget_style_get)libgtk.resolve("gtk_widget_style_get"); - gtk_icon_set_render_icon = (Ptr_gtk_icon_set_render_icon)libgtk.resolve("gtk_icon_set_render_icon"); - gtk_fixed_new = (Ptr_gtk_fixed_new)libgtk.resolve("gtk_fixed_new"); - gtk_tree_view_column_new = (Ptr_gtk_tree_view_column_new)libgtk.resolve("gtk_tree_view_column_new"); - gtk_tree_view_append_column= (Ptr_gtk_tree_view_append_column )libgtk.resolve("gtk_tree_view_append_column"); - gtk_tree_view_get_column = (Ptr_gtk_tree_view_get_column )libgtk.resolve("gtk_tree_view_get_column"); - gtk_adjustment_configure = (Ptr_gtk_adjustment_configure)libgtk.resolve("gtk_adjustment_configure"); - gtk_adjustment_new = (Ptr_gtk_adjustment_new)libgtk.resolve("gtk_adjustment_new"); - gtk_menu_item_set_submenu = (Ptr_gtk_menu_item_set_submenu)libgtk.resolve("gtk_menu_item_set_submenu"); - gtk_settings_get_default = (Ptr_gtk_settings_get_default)libgtk.resolve("gtk_settings_get_default"); - gtk_separator_menu_item_new = (Ptr_gtk_separator_menu_item_new)libgtk.resolve("gtk_separator_menu_item_new"); - gtk_frame_new = (Ptr_gtk_frame_new)libgtk.resolve("gtk_frame_new"); - gtk_expander_new = (Ptr_gtk_expander_new)libgtk.resolve("gtk_expander_new"); - gtk_statusbar_new = (Ptr_gtk_statusbar_new)libgtk.resolve("gtk_statusbar_new"); - gtk_container_forall = (Ptr_gtk_container_forall)libgtk.resolve("gtk_container_forall"); - gtk_widget_size_allocate =(Ptr_gtk_widget_size_allocate)libgtk.resolve("gtk_widget_size_allocate"); - gtk_widget_size_request =(Ptr_gtk_widget_size_request)libgtk.resolve("gtk_widget_size_request"); - gtk_widget_set_direction =(Ptr_gtk_widget_set_direction)libgtk.resolve("gtk_widget_set_direction"); - gtk_widget_path =(Ptr_gtk_widget_path)libgtk.resolve("gtk_widget_path"); - gtk_container_get_type =(Ptr_gtk_container_get_type)libgtk.resolve("gtk_container_get_type"); - gtk_window_get_type =(Ptr_gtk_window_get_type)libgtk.resolve("gtk_window_get_type"); - gtk_widget_get_type =(Ptr_gtk_widget_get_type)libgtk.resolve("gtk_widget_get_type"); - gtk_widget_get_parent =(Ptr_gtk_widget_get_parent)libgtk.resolve("gtk_widget_get_parent"); - gtk_widget_is_toplevel =(Ptr_gtk_widget_is_toplevel)libgtk.resolve("gtk_widget_is_toplevel"); - gtk_widget_get_toplevel =(Ptr_gtk_widget_get_toplevel)libgtk.resolve("gtk_widget_get_toplevel"); - - gtk_rc_get_style_by_paths =(Ptr_gtk_rc_get_style_by_paths)libgtk.resolve("gtk_rc_get_style_by_paths"); - gtk_check_version =(Ptr_gtk_check_version)libgtk.resolve("gtk_check_version"); - gtk_border_free =(Ptr_gtk_border_free)libgtk.resolve("gtk_border_free"); - gtk_widget_get_allocation = (Ptr_gtk_widget_get_allocation)libgtk.resolve("gtk_widget_get_allocation"); - gtk_widget_set_allocation = (Ptr_gtk_widget_set_allocation)libgtk.resolve("gtk_widget_set_allocation"); - - gtk_widget_set_can_default = (Ptr_gtk_widget_set_can_default)libgtk.resolve("gtk_widget_set_can_default"); - gtk_window_set_default = (Ptr_gtk_window_set_default)libgtk.resolve("gtk_window_set_default"); - - gdk_event_new = (Ptr_gdk_event_new)libgtk.resolve("gdk_event_new"); - gdk_event_free = (Ptr_gdk_event_free)libgtk.resolve("gdk_event_free"); - gtk_widget_send_focus_change = (Ptr_gtk_widget_send_focus_change)libgtk.resolve("gtk_widget_send_focus_change"); - - pango_font_description_get_size = (Ptr_pango_font_description_get_size)libgtk.resolve("pango_font_description_get_size"); - pango_font_description_get_weight = (Ptr_pango_font_description_get_weight)libgtk.resolve("pango_font_description_get_weight"); - pango_font_description_get_family = (Ptr_pango_font_description_get_family)libgtk.resolve("pango_font_description_get_family"); - pango_font_description_get_style = (Ptr_pango_font_description_get_style)libgtk.resolve("pango_font_description_get_style"); - - gnome_icon_lookup_sync = (Ptr_gnome_icon_lookup_sync)QLibrary::resolve(QLS("gnomeui-2"), 0, "gnome_icon_lookup_sync"); - gnome_vfs_init= (Ptr_gnome_vfs_init)QLibrary::resolve(QLS("gnomevfs-2"), 0, "gnome_vfs_init"); -#endif // !QT_NO_LIBRARY -} - -/* \internal - * Initializes a number of gtk menu widgets. - * The widgets are cached. - */ -void QGtkStylePrivate::initGtkMenu() const -{ - // Create menubar - GtkWidget *gtkMenuBar = QGtkStylePrivate::gtk_menu_bar_new(); - setupGtkWidget(gtkMenuBar); - - GtkWidget *gtkMenuBarItem = QGtkStylePrivate::gtk_menu_item_new_with_label("X"); - gtk_menu_shell_append((GtkMenuShell*)(gtkMenuBar), gtkMenuBarItem); - gtk_widget_realize(gtkMenuBarItem); - - // Create menu - GtkWidget *gtkMenu = QGtkStylePrivate::gtk_menu_new(); - gtk_menu_item_set_submenu((GtkMenuItem*)(gtkMenuBarItem), gtkMenu); - gtk_widget_realize(gtkMenu); - - GtkWidget *gtkMenuItem = QGtkStylePrivate::gtk_menu_item_new_with_label("X"); - gtk_menu_shell_append((GtkMenuShell*)gtkMenu, gtkMenuItem); - gtk_widget_realize(gtkMenuItem); - - GtkWidget *gtkCheckMenuItem = QGtkStylePrivate::gtk_check_menu_item_new_with_label("X"); - gtk_menu_shell_append((GtkMenuShell*)gtkMenu, gtkCheckMenuItem); - gtk_widget_realize(gtkCheckMenuItem); - - GtkWidget *gtkMenuSeparator = QGtkStylePrivate::gtk_separator_menu_item_new(); - gtk_menu_shell_append((GtkMenuShell*)gtkMenu, gtkMenuSeparator); - - addAllSubWidgets(gtkMenuBar); - addAllSubWidgets(gtkMenu); -} - - -void QGtkStylePrivate::initGtkTreeview() const -{ - GtkWidget *gtkTreeView = gtk_tree_view_new(); - gtk_tree_view_append_column((GtkTreeView*)gtkTreeView, gtk_tree_view_column_new()); - gtk_tree_view_append_column((GtkTreeView*)gtkTreeView, gtk_tree_view_column_new()); - gtk_tree_view_append_column((GtkTreeView*)gtkTreeView, gtk_tree_view_column_new()); - addWidget(gtkTreeView); -} - - -/* \internal - * Initializes a number of gtk widgets that we can later on use to determine some of our styles. - * The widgets are cached. - */ -void QGtkStylePrivate::initGtkWidgets() const -{ - // From gtkmain.c - uid_t ruid = getuid (); - uid_t rgid = getgid (); - uid_t euid = geteuid (); - uid_t egid = getegid (); - if (ruid != euid || rgid != egid) { - qWarning("\nThis process is currently running setuid or setgid.\nGTK+ does not allow this " - "therefore Qt cannot use the GTK+ integration.\nTry launching your app using \'gksudo\', " - "\'kdesudo\' or a similar tool.\n\n" - "See http://www.gtk.org/setuid.html for more information.\n"); - return; - } - - if (QGtkStylePrivate::gtk_init) { -#ifndef Q_OS_MAC - // Gtk will set the Qt error handler so we have to reset it afterwards - x11ErrorHandler qt_x_errhandler = XSetErrorHandler(0); -#endif - QGtkStylePrivate::gtk_init (NULL, NULL); -#ifndef Q_OS_MAC - XSetErrorHandler(qt_x_errhandler); -#endif - - // make a window - GtkWidget* gtkWindow = QGtkStylePrivate::gtk_window_new(GTK_WINDOW_POPUP); - QGtkStylePrivate::gtk_widget_realize(gtkWindow); - QHashableLatin1Literal widgetPath = QHashableLatin1Literal::fromData(strdup("GtkWindow")); - removeWidgetFromMap(widgetPath); - gtkWidgetMap()->insert(widgetPath, gtkWindow); - - - // Make all other widgets. respect the text direction - if (qApp->layoutDirection() == Qt::RightToLeft) - QGtkStylePrivate::gtk_widget_set_default_direction(GTK_TEXT_DIR_RTL); - - if (!gtkWidgetMap()->contains("GtkButton")) { - GtkWidget *gtkButton = QGtkStylePrivate::gtk_button_new(); - addWidget(gtkButton); - g_signal_connect(gtkButton, "style-set", G_CALLBACK(gtkStyleSetCallback), 0); - addWidget(QGtkStylePrivate::gtk_tool_button_new(NULL, "Qt")); - addWidget(QGtkStylePrivate::gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE)); - addWidget(QGtkStylePrivate::gtk_hbutton_box_new()); - addWidget(QGtkStylePrivate::gtk_check_button_new()); - addWidget(QGtkStylePrivate::gtk_radio_button_new(NULL)); - addWidget(QGtkStylePrivate::gtk_combo_box_new()); - addWidget(QGtkStylePrivate::gtk_combo_box_entry_new()); - GtkWidget *entry = QGtkStylePrivate::gtk_entry_new(); - // gtk-im-context-none is supported in gtk+ since 2.19.5 - // and also exists in gtk3 - // http://git.gnome.org/browse/gtk+/tree/gtk/gtkimmulticontext.c?id=2.19.5#n33 - // reason that we don't use gtk-im-context-simple here is, - // gtk-im-context-none has less overhead, and 2.19.5 is - // relatively old. and even for older gtk+, it will fallback - // to gtk-im-context-simple if gtk-im-context-none doesn't - // exists. - g_object_set(entry, "im-module", "gtk-im-context-none", NULL); - addWidget(entry); - addWidget(QGtkStylePrivate::gtk_frame_new(NULL)); - addWidget(QGtkStylePrivate::gtk_expander_new("")); - addWidget(QGtkStylePrivate::gtk_statusbar_new()); - addWidget(QGtkStylePrivate::gtk_hscale_new((QGtkStylePrivate::gtk_adjustment_new(1, 0, 1, 0, 0, 0)))); - addWidget(QGtkStylePrivate::gtk_hscrollbar_new(NULL)); - addWidget(QGtkStylePrivate::gtk_scrolled_window_new(NULL, NULL)); - - initGtkMenu(); - addWidget(QGtkStylePrivate::gtk_notebook_new()); - addWidget(QGtkStylePrivate::gtk_progress_bar_new()); - addWidget(QGtkStylePrivate::gtk_spin_button_new((QGtkStylePrivate::gtk_adjustment_new(1, 0, 1, 0, 0, 0)), 0.1, 3)); - GtkWidget *toolbar = gtk_toolbar_new(); - g_signal_connect (toolbar, "notify::toolbar-style", G_CALLBACK (update_toolbar_style), toolbar); - gtk_toolbar_insert((GtkToolbar*)toolbar, gtk_separator_tool_item_new(), -1); - addWidget(toolbar); - initGtkTreeview(); - addWidget(gtk_vscale_new((QGtkStylePrivate::gtk_adjustment_new(1, 0, 1, 0, 0, 0)))); - addWidget(gtk_vscrollbar_new(NULL)); - } - else // Rebuild map - { - // When styles change subwidgets can get rearranged - // as with the combo box. We need to update the widget map - // to reflect this; - QHash<QHashableLatin1Literal, GtkWidget*> oldMap = *gtkWidgetMap(); - gtkWidgetMap()->clear(); - QHashIterator<QHashableLatin1Literal, GtkWidget*> it(oldMap); - while (it.hasNext()) { - it.next(); - if (!strchr(it.key().data(), '.')) { - addAllSubWidgets(it.value()); - } - free(const_cast<char *>(it.key().data())); - } - } - } else { - qWarning("QGtkStyle could not resolve GTK. Make sure you have installed the proper libraries."); - } -} - -/*! \internal - * destroys all previously buffered widgets. - */ -void QGtkStylePrivate::cleanupGtkWidgets() -{ - if (!widgetMap) - return; - if (widgetMap->contains("GtkWindow")) // Gtk will destroy all children - gtk_widget_destroy(widgetMap->value("GtkWindow")); - for (QHash<QHashableLatin1Literal, GtkWidget *>::const_iterator it = widgetMap->constBegin(); - it != widgetMap->constEnd(); ++it) - free(const_cast<char *>(it.key().data())); -} - -static bool resolveGConf() -{ -#ifndef QT_NO_LIBRARY - if (!QGtkStylePrivate::gconf_client_get_default) { - QGtkStylePrivate::gconf_client_get_default = (Ptr_gconf_client_get_default)QLibrary::resolve(QLS("gconf-2"), 4, "gconf_client_get_default"); - QGtkStylePrivate::gconf_client_get_string = (Ptr_gconf_client_get_string)QLibrary::resolve(QLS("gconf-2"), 4, "gconf_client_get_string"); - QGtkStylePrivate::gconf_client_get_bool = (Ptr_gconf_client_get_bool)QLibrary::resolve(QLS("gconf-2"), 4, "gconf_client_get_bool"); - } -#endif // !QT_NO_LIBRARY - return (QGtkStylePrivate::gconf_client_get_default !=0); -} - -QString QGtkStylePrivate::getGConfString(const QString &value, const QString &fallback) -{ - QString retVal = fallback; - if (resolveGConf()) { -#if !defined(GLIB_VERSION_2_36) - g_type_init(); -#endif - GConfClient* client = gconf_client_get_default(); - GError *err = 0; - char *str = gconf_client_get_string(client, qPrintable(value), &err); - if (!err) { - retVal = QString::fromUtf8(str); - g_free(str); - } - g_object_unref(client); - if (err) - g_error_free (err); - } - return retVal; -} - -bool QGtkStylePrivate::getGConfBool(const QString &key, bool fallback) -{ - bool retVal = fallback; - if (resolveGConf()) { -#if !defined(GLIB_VERSION_2_36) - g_type_init(); -#endif - GConfClient* client = gconf_client_get_default(); - GError *err = 0; - bool result = gconf_client_get_bool(client, qPrintable(key), &err); - g_object_unref(client); - if (!err) - retVal = result; - else - g_error_free (err); - } - return retVal; -} - -QString QGtkStylePrivate::getThemeName() -{ - QString themeName; - // Read the theme name from GtkSettings - GtkSettings *settings = QGtkStylePrivate::gtk_settings_get_default(); - gchararray value; - g_object_get(settings, "gtk-theme-name", &value, NULL); - themeName = QString::fromUtf8(value); - g_free(value); - return themeName; -} - -// Get size of the arrow controls in a GtkSpinButton -int QGtkStylePrivate::getSpinboxArrowSize() const -{ - const int MIN_ARROW_WIDTH = 6; - GtkWidget *spinButton = gtkWidget("GtkSpinButton"); - GtkStyle *style = QGtkStylePrivate::gtk_widget_get_style(spinButton); - gint size = pango_font_description_get_size (style->font_desc); - gint arrow_size; - arrow_size = qMax(PANGO_PIXELS (size), MIN_ARROW_WIDTH) + style->xthickness; - arrow_size += arrow_size%2 + 1; - return arrow_size; -} - - -bool QGtkStylePrivate::isKDE4Session() -{ - static int version = -1; - if (version == -1) - version = qgetenv("KDE_SESSION_VERSION").toInt(); - return (version == 4); -} - -void QGtkStylePrivate::applyCustomPaletteHash() -{ - QPalette menuPal = gtkWidgetPalette("GtkMenu"); - GdkColor gdkBg = QGtkStylePrivate::gtk_widget_get_style(gtkWidget("GtkMenu"))->bg[GTK_STATE_NORMAL]; - QColor bgColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8); - menuPal.setBrush(QPalette::Base, bgColor); - menuPal.setBrush(QPalette::Window, bgColor); - qApp->setPalette(menuPal, "QMenu"); - - QPalette toolbarPal = gtkWidgetPalette("GtkToolbar"); - qApp->setPalette(toolbarPal, "QToolBar"); - - QPalette menuBarPal = gtkWidgetPalette("GtkMenuBar"); - qApp->setPalette(menuBarPal, "QMenuBar"); -} - -/*! \internal - * Returns the gtk Widget that should be used to determine text foreground and background colors. -*/ -GtkWidget* QGtkStylePrivate::getTextColorWidget() const -{ - return gtkWidget("GtkEntry"); -} - -void QGtkStylePrivate::setupGtkWidget(GtkWidget* widget) -{ - if (Q_GTK_IS_WIDGET(widget)) { - GtkWidget *protoLayout = gtkWidgetMap()->value("GtkContainer"); - if (!protoLayout) { - protoLayout = QGtkStylePrivate::gtk_fixed_new(); - QGtkStylePrivate::gtk_container_add((GtkContainer*)(gtkWidgetMap()->value("GtkWindow")), protoLayout); - QHashableLatin1Literal widgetPath = QHashableLatin1Literal::fromData(strdup("GtkContainer")); - gtkWidgetMap()->insert(widgetPath, protoLayout); - } - Q_ASSERT(protoLayout); - - if (!QGtkStylePrivate::gtk_widget_get_parent(widget) && !QGtkStylePrivate::gtk_widget_is_toplevel(widget)) - QGtkStylePrivate::gtk_container_add((GtkContainer*)(protoLayout), widget); - QGtkStylePrivate::gtk_widget_realize(widget); - } -} - -void QGtkStylePrivate::removeWidgetFromMap(const QHashableLatin1Literal &path) -{ - WidgetMap *map = gtkWidgetMap(); - WidgetMap::iterator it = map->find(path); - if (it != map->end()) { - char* keyData = const_cast<char *>(it.key().data()); - map->erase(it); - free(keyData); - } -} - -void QGtkStylePrivate::addWidgetToMap(GtkWidget *widget) -{ - if (Q_GTK_IS_WIDGET(widget)) { - gtk_widget_realize(widget); - QHashableLatin1Literal widgetPath = classPath(widget); - - removeWidgetFromMap(widgetPath); - gtkWidgetMap()->insert(widgetPath, widget); -#ifdef DUMP_GTK_WIDGET_TREE - qWarning("Inserted Gtk Widget: %s", widgetPath.data()); -#endif - } - } - -void QGtkStylePrivate::addAllSubWidgets(GtkWidget *widget, gpointer v) -{ - Q_UNUSED(v); - addWidgetToMap(widget); - if (G_TYPE_CHECK_INSTANCE_TYPE ((widget), gtk_container_get_type())) - gtk_container_forall((GtkContainer*)widget, addAllSubWidgets, NULL); -} - -// Updates window/windowtext palette based on the indicated gtk widget -QPalette QGtkStylePrivate::gtkWidgetPalette(const QHashableLatin1Literal >kWidgetName) const -{ - GtkWidget *gtkWidget = QGtkStylePrivate::gtkWidget(gtkWidgetName); - Q_ASSERT(gtkWidget); - QPalette pal = QApplication::palette(); - GdkColor gdkBg = gtk_widget_get_style(gtkWidget)->bg[GTK_STATE_NORMAL]; - GdkColor gdkText = gtk_widget_get_style(gtkWidget)->fg[GTK_STATE_NORMAL]; - GdkColor gdkDisabledText = gtk_widget_get_style(gtkWidget)->fg[GTK_STATE_INSENSITIVE]; - QColor bgColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8); - QColor textColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); - QColor disabledTextColor(gdkDisabledText.red>>8, gdkDisabledText.green>>8, gdkDisabledText.blue>>8); - pal.setBrush(QPalette::Window, bgColor); - pal.setBrush(QPalette::Button, bgColor); - pal.setBrush(QPalette::All, QPalette::WindowText, textColor); - pal.setBrush(QPalette::Disabled, QPalette::WindowText, disabledTextColor); - pal.setBrush(QPalette::All, QPalette::ButtonText, textColor); - pal.setBrush(QPalette::Disabled, QPalette::ButtonText, disabledTextColor); - return pal; -} - - -void QGtkStyleUpdateScheduler::updateTheme() -{ - static QString oldTheme(QLS("qt_not_set")); - QPixmapCache::clear(); - - QFont font = QGtkStylePrivate::getThemeFont(); - if (QApplication::font() != font) - qApp->setFont(font); - - if (oldTheme != QGtkStylePrivate::getThemeName()) { - oldTheme = QGtkStylePrivate::getThemeName(); - QPalette newPalette = qApp->style()->standardPalette(); - QApplicationPrivate::setSystemPalette(newPalette); - QApplication::setPalette(newPalette); - if (!QGtkStylePrivate::instances.isEmpty()) { - QGtkStylePrivate::instances.last()->initGtkWidgets(); - QGtkStylePrivate::instances.last()->applyCustomPaletteHash(); - } - QList<QWidget*> widgets = QApplication::allWidgets(); - // Notify all widgets that size metrics might have changed - foreach (QWidget *widget, widgets) { - QEvent e(QEvent::StyleChange); - QApplication::sendEvent(widget, &e); - } - } - QIconLoader::instance()->updateSystemTheme(); -} - -void QGtkStylePrivate::addWidget(GtkWidget *widget) -{ - if (widget) { - setupGtkWidget(widget); - addAllSubWidgets(widget); - } -} - - -// Fetch the application font from the pango font description -// contained in the theme. -QFont QGtkStylePrivate::getThemeFont() -{ - QFont font; - GtkStyle *style = gtkStyle(); - if (style && qApp->desktopSettingsAware()) - { - PangoFontDescription *gtk_font = style->font_desc; - font.setPointSizeF((float)(pango_font_description_get_size(gtk_font))/PANGO_SCALE); - - QString family = QString::fromLatin1(pango_font_description_get_family(gtk_font)); - if (!family.isEmpty()) - font.setFamily(family); - - const int weight = pango_font_description_get_weight(gtk_font); - font.setWeight(QPlatformFontDatabase::weightFromInteger(weight)); - - PangoStyle fontstyle = pango_font_description_get_style(gtk_font); - if (fontstyle == PANGO_STYLE_ITALIC) - font.setStyle(QFont::StyleItalic); - else if (fontstyle == PANGO_STYLE_OBLIQUE) - font.setStyle(QFont::StyleOblique); - else - font.setStyle(QFont::StyleNormal); - } - return font; -} - -QIcon QGtkStylePrivate::getFilesystemIcon(const QFileInfo &info) -{ - QIcon icon; - if (isThemeAvailable() && gnome_vfs_init && gnome_icon_lookup_sync) { - gnome_vfs_init(); - GtkIconTheme *theme = gtk_icon_theme_get_default(); - QByteArray fileurl = QUrl::fromLocalFile(info.absoluteFilePath()).toEncoded(); - char * icon_name = gnome_icon_lookup_sync(theme, - NULL, - fileurl.data(), - NULL, - GNOME_ICON_LOOKUP_FLAGS_NONE, - NULL); - QString iconName = QString::fromUtf8(icon_name); - g_free(icon_name); - if (iconName.startsWith(QLatin1Char('/'))) - return QIcon(iconName); - return QIcon::fromTheme(iconName); - } - return icon; -} - -bool operator==(const QHashableLatin1Literal &l1, const QHashableLatin1Literal &l2) -{ - return l1.size() == l2.size() || qstrcmp(l1.data(), l2.data()) == 0; -} - -// copied from qHash.cpp -uint qHash(const QHashableLatin1Literal &key) -{ - int n = key.size(); - const uchar *p = reinterpret_cast<const uchar *>(key.data()); - uint h = 0; - uint g; - - while (n--) { - h = (h << 4) + *p++; - if ((g = (h & 0xf0000000)) != 0) - h ^= g >> 23; - h &= ~g; - } - return h; -} - -QT_END_NAMESPACE - -#endif // !defined(QT_NO_STYLE_GTK) diff --git a/src/widgets/styles/qgtkstyle_p.h b/src/widgets/styles/qgtkstyle_p.h deleted file mode 100644 index 3dcd7bf6ef..0000000000 --- a/src/widgets/styles/qgtkstyle_p.h +++ /dev/null @@ -1,123 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QGTKSTYLE_P_H -#define QGTKSTYLE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qwindowsstyle_p.h> -#include <QtGui/QPalette> -#include <QtGui/QFont> -#include <QtWidgets/QFileDialog> - -QT_BEGIN_NAMESPACE - - -#if !defined(QT_NO_STYLE_GTK) - -class QPainterPath; -class QGtkStylePrivate; - -class QGtkStyle : public QCommonStyle -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QGtkStyle) - -public: - QGtkStyle(); - QGtkStyle(QGtkStylePrivate &dd); - - ~QGtkStyle(); - - QPalette standardPalette() const Q_DECL_OVERRIDE; - - void drawPrimitive(PrimitiveElement element, const QStyleOption *option, - QPainter *painter, const QWidget *widget) const Q_DECL_OVERRIDE; - void drawControl(ControlElement control, const QStyleOption *option, - QPainter *painter, const QWidget *widget) const Q_DECL_OVERRIDE; - void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, - QPainter *painter, const QWidget *widget) const Q_DECL_OVERRIDE; - void drawItemPixmap(QPainter *painter, const QRect &rect, int alignment, - const QPixmap &pixmap) const Q_DECL_OVERRIDE; - void drawItemText(QPainter *painter, const QRect &rect, int alignment, const QPalette &pal, - bool enabled, const QString& text, QPalette::ColorRole textRole) const Q_DECL_OVERRIDE; - - int pixelMetric(PixelMetric metric, const QStyleOption *option = 0, - const QWidget *widget = 0) const Q_DECL_OVERRIDE; - int styleHint(StyleHint hint, const QStyleOption *option, - const QWidget *widget, QStyleHintReturn *returnData) const Q_DECL_OVERRIDE; - - QStyle::SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, - const QPoint &pt, const QWidget *w) const Q_DECL_OVERRIDE; - - QRect subControlRect(ComplexControl control, const QStyleOptionComplex *option, - SubControl subControl, const QWidget *widget) const Q_DECL_OVERRIDE; - QRect subElementRect(SubElement sr, const QStyleOption *opt, const QWidget *w) const Q_DECL_OVERRIDE; - QRect itemPixmapRect(const QRect &r, int flags, const QPixmap &pixmap) const Q_DECL_OVERRIDE; - - - QSize sizeFromContents(ContentsType type, const QStyleOption *option, - const QSize &size, const QWidget *widget) const Q_DECL_OVERRIDE; - QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *option = 0, - const QWidget *widget = 0) const Q_DECL_OVERRIDE; - QPixmap standardPixmap(StandardPixmap sp, const QStyleOption *option, - const QWidget *widget) const Q_DECL_OVERRIDE; - QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, - const QStyleOption *opt) const Q_DECL_OVERRIDE; - - void polish(QWidget *widget) Q_DECL_OVERRIDE; - void polish(QApplication *app) Q_DECL_OVERRIDE; - void polish(QPalette &palette) Q_DECL_OVERRIDE; - - void unpolish(QWidget *widget) Q_DECL_OVERRIDE; - void unpolish(QApplication *app) Q_DECL_OVERRIDE; - - static bool getGConfBool(const QString &key, bool fallback = 0); - static QString getGConfString(const QString &key, const QString &fallback = QString()); -}; - -#endif //!defined(QT_NO_STYLE_QGTK) - -QT_END_NAMESPACE - -#endif //QGTKSTYLE_P_H diff --git a/src/widgets/styles/qgtkstyle_p_p.h b/src/widgets/styles/qgtkstyle_p_p.h deleted file mode 100644 index 4cb03ed833..0000000000 --- a/src/widgets/styles/qgtkstyle_p_p.h +++ /dev/null @@ -1,449 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QGTKSTYLE_P_P_H -#define QGTKSTYLE_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qglobal.h> -#if !defined(QT_NO_STYLE_GTK) - -#include <QtCore/qstring.h> -#include <QtCore/qstringbuilder.h> -#include <QtCore/qcoreapplication.h> - -#include <QtWidgets/QFileDialog> - -#include <private/qgtkstyle_p.h> -#include <private/qcommonstyle_p.h> -#include <private/qgtkglobal_p.h> - -#define Q_GTK_IS_WIDGET(widget) widget && G_TYPE_CHECK_INSTANCE_TYPE ((widget), QGtkStylePrivate::gtk_widget_get_type()) - -QT_BEGIN_NAMESPACE - -class QHashableLatin1Literal -{ -public: - int size() const { return m_size; } - const char *data() const { return m_data; } - -#ifdef __SUNPRO_CC - QHashableLatin1Literal(const char* str) - : m_size(strlen(str)), m_data(str) {} -#else - template <int N> - QHashableLatin1Literal(const char (&str)[N]) - : m_size(N - 1), m_data(str) {} -#endif - - QHashableLatin1Literal(const QHashableLatin1Literal &other) - : m_size(other.m_size), m_data(other.m_data) - {} - - QHashableLatin1Literal &operator=(const QHashableLatin1Literal &other) - { - if (this == &other) - return *this; - *const_cast<int *>(&m_size) = other.m_size; - *const_cast<char **>(&m_data) = const_cast<char *>(other.m_data); - return *this; - } - - QString toString() const { return QString::fromLatin1(m_data, m_size); } - - static QHashableLatin1Literal fromData(const char *str) - { - return QHashableLatin1Literal(str, qstrlen(str)); - } - -private: - QHashableLatin1Literal(const char *str, int length) - : m_size(length), m_data(str) - {} - - const int m_size; - const char *m_data; -}; - -bool operator==(const QHashableLatin1Literal &l1, const QHashableLatin1Literal &l2); -inline bool operator!=(const QHashableLatin1Literal &l1, const QHashableLatin1Literal &l2) { return !operator==(l1, l2); } -uint qHash(const QHashableLatin1Literal &key); - -QT_END_NAMESPACE - -class GConf; -class GConfClient; -typedef struct _XDisplay Display; - -typedef GConfClient* (*Ptr_gconf_client_get_default)(); -typedef char* (*Ptr_gconf_client_get_string)(GConfClient*, const char*, GError **); -typedef bool (*Ptr_gconf_client_get_bool)(GConfClient*, const char*, GError **); - -typedef void (*Ptr_gtk_init)(int *, char ***); -typedef GtkWidget* (*Ptr_gtk_window_new) (GtkWindowType); -typedef GtkStyle* (*Ptr_gtk_style_attach)(GtkStyle *, GdkWindow *); -typedef void (*Ptr_gtk_widget_destroy) (GtkWidget *); -typedef void (*Ptr_gtk_widget_realize) (GtkWidget *); -typedef void (*Ptr_gtk_widget_set_default_direction) (GtkTextDirection); -typedef void (*Ptr_gtk_widget_modify_color)(GtkWidget *widget, GtkStateType state, const GdkColor *color); -typedef GtkWidget* (*Ptr_gtk_arrow_new)(GtkArrowType, GtkShadowType); -typedef GtkWidget* (*Ptr_gtk_menu_item_new_with_label)(const gchar *); -typedef GtkWidget* (*Ptr_gtk_separator_menu_item_new)(void); -typedef GtkWidget* (*Ptr_gtk_check_menu_item_new_with_label)(const gchar *); -typedef GtkWidget* (*Ptr_gtk_menu_bar_new)(void); -typedef GtkWidget* (*Ptr_gtk_menu_new)(void); -typedef GtkWidget* (*Ptr_gtk_combo_box_new)(void); -typedef GtkWidget* (*Ptr_gtk_combo_box_entry_new)(void); -typedef GtkWidget* (*Ptr_gtk_toolbar_new)(void); -typedef GtkWidget* (*Ptr_gtk_spin_button_new)(GtkAdjustment*, double, int); -typedef GtkWidget* (*Ptr_gtk_button_new)(void); -typedef GtkWidget* (*Ptr_gtk_tool_button_new)(GtkWidget *, const gchar *); -typedef GtkWidget* (*Ptr_gtk_hbutton_box_new)(void); -typedef GtkWidget* (*Ptr_gtk_check_button_new)(void); -typedef GtkWidget* (*Ptr_gtk_radio_button_new)(GSList *); -typedef GtkWidget* (*Ptr_gtk_notebook_new)(void); -typedef GtkWidget* (*Ptr_gtk_progress_bar_new)(void); -typedef GtkWidget* (*Ptr_gtk_hscale_new)(GtkAdjustment*); -typedef GtkWidget* (*Ptr_gtk_vscale_new)(GtkAdjustment*); -typedef GtkWidget* (*Ptr_gtk_hscrollbar_new)(GtkAdjustment*); -typedef GtkWidget* (*Ptr_gtk_vscrollbar_new)(GtkAdjustment*); -typedef GtkWidget* (*Ptr_gtk_scrolled_window_new)(GtkAdjustment*, GtkAdjustment*); -typedef gchar* (*Ptr_gtk_check_version)(guint, guint, guint); -typedef GtkToolItem* (*Ptr_gtk_separator_tool_item_new) (void); -typedef GtkWidget* (*Ptr_gtk_entry_new)(void); -typedef GtkWidget* (*Ptr_gtk_tree_view_new)(void); -typedef GtkTreeViewColumn* (*Ptr_gtk_tree_view_get_column)(GtkTreeView *, gint); -typedef GtkWidget* (*Ptr_gtk_frame_new)(const gchar *); -typedef GtkWidget* (*Ptr_gtk_expander_new)(const gchar*); -typedef GtkWidget* (*Ptr_gtk_statusbar_new)(void); -typedef GtkSettings* (*Ptr_gtk_settings_get_default)(void); -typedef GtkAdjustment* (*Ptr_gtk_range_get_adjustment)(GtkRange *); -typedef void (*Ptr_gtk_range_set_adjustment)(GtkRange *, GtkAdjustment *); -typedef void (*Ptr_gtk_range_set_inverted)(GtkRange*, bool); -typedef void (*Ptr_gtk_container_add)(GtkContainer *container, GtkWidget *widget); -typedef GtkIconSet* (*Ptr_gtk_icon_factory_lookup_default) (const gchar*); -typedef GtkIconTheme* (*Ptr_gtk_icon_theme_get_default) (void); -typedef GtkStyle* (*Ptr_gtk_widget_get_style)(GtkWidget *); -typedef void (*Ptr_gtk_widget_style_get)(GtkWidget *, const gchar *first_property_name, ...); -typedef GtkTreeViewColumn* (*Ptr_gtk_tree_view_column_new)(void); -typedef GtkWidget* (*Ptr_gtk_fixed_new)(void); -typedef GdkPixbuf* (*Ptr_gtk_icon_set_render_icon)(GtkIconSet *, GtkStyle *, GtkTextDirection, GtkStateType, GtkIconSize, GtkWidget *,const char *); -typedef void (*Ptr_gtk_tree_view_append_column) (GtkTreeView*, GtkTreeViewColumn*); -typedef void (*Ptr_gtk_adjustment_configure) (GtkAdjustment *, double, double, double, double, double, double); -typedef GtkAdjustment* (*Ptr_gtk_adjustment_new) (double, double, double, double, double, double); -typedef void (*Ptr_gtk_menu_item_set_submenu) (GtkMenuItem *, GtkWidget *); -typedef void (*Ptr_gtk_container_forall) (GtkContainer *, GtkCallback, gpointer); -typedef void (*Ptr_gtk_widget_size_allocate) (GtkWidget *, GtkAllocation*); -typedef void (*Ptr_gtk_widget_size_request) (GtkWidget *widget, GtkRequisition *requisition); -typedef void (*Ptr_gtk_widget_set_direction) (GtkWidget *, GtkTextDirection); -typedef void (*Ptr_gtk_widget_path) (GtkWidget *, guint *, gchar **, gchar**); - -typedef void (*Ptr_gtk_toolbar_insert) (GtkToolbar *toolbar, GtkToolItem *item, int pos); -typedef void (*Ptr_gtk_menu_shell_append)(GtkMenuShell *, GtkWidget *); -typedef GType (*Ptr_gtk_container_get_type) (void); -typedef GType (*Ptr_gtk_window_get_type) (void); -typedef GType (*Ptr_gtk_widget_get_type) (void); -typedef GtkWidget* (*Ptr_gtk_widget_get_parent) (GtkWidget *); -typedef gboolean (*Ptr_gtk_widget_is_toplevel) (GtkWidget *); -typedef GtkWidget* (*Ptr_gtk_widget_get_toplevel) (GtkWidget *); -typedef GtkStyle* (*Ptr_gtk_rc_get_style_by_paths) (GtkSettings *, const char *, const char *, GType); -typedef gint (*Ptr_pango_font_description_get_size) (const PangoFontDescription *); -typedef PangoWeight (*Ptr_pango_font_description_get_weight) (const PangoFontDescription *); -typedef const char* (*Ptr_pango_font_description_get_family) (const PangoFontDescription *); -typedef PangoStyle (*Ptr_pango_font_description_get_style) (const PangoFontDescription *desc); -typedef void (*Ptr_gtk_border_free)(GtkBorder *); -typedef void (*Ptr_gtk_widget_get_allocation) (GtkWidget*, GtkAllocation*); -typedef void (*Ptr_gtk_widget_set_allocation) (GtkWidget*, const GtkAllocation*); - -typedef void (*Ptr_gtk_widget_set_can_default) (GtkWidget*, gboolean); -typedef void (*Ptr_gtk_window_set_default) (GtkWindow*, GtkWidget*); - -typedef GdkEvent* (*Ptr_gdk_event_new) (GdkEventType); -typedef void (*Ptr_gdk_event_free) (GdkEvent*); -typedef void (*Ptr_gtk_widget_send_focus_change) (GtkWidget*, GdkEvent*); - -typedef guchar* (*Ptr_gdk_pixbuf_get_pixels) (const GdkPixbuf *pixbuf); -typedef int (*Ptr_gdk_pixbuf_get_width) (const GdkPixbuf *pixbuf); -typedef void (*Ptr_gdk_color_free) (const GdkColor *); -typedef int (*Ptr_gdk_pixbuf_get_height) (const GdkPixbuf *pixbuf); -typedef GdkPixbuf* (*Ptr_gdk_pixbuf_new) (GdkColorspace colorspace, gboolean has_alpha, - int bits_per_sample, int width, int height); -typedef void (*Ptr_gdk_pixbuf_unref)(GdkPixbuf *); -typedef void (*Ptr_gdk_x11_window_set_user_time) (GdkWindow *window, guint32); -typedef XID (*Ptr_gdk_x11_drawable_get_xid) (GdkDrawable *); -typedef Display* (*Ptr_gdk_x11_drawable_get_xdisplay) ( GdkDrawable *); - - -QT_BEGIN_NAMESPACE - -class QGtkPainter; -class QGtkStylePrivate; - -class QGtkStyleFilter : public QObject -{ -public: - QGtkStyleFilter(QGtkStylePrivate* sp) - : stylePrivate(sp) - {} -private: - QGtkStylePrivate* stylePrivate; - bool eventFilter(QObject *obj, QEvent *e) Q_DECL_OVERRIDE; -}; - -typedef enum { - GNOME_ICON_LOOKUP_FLAGS_NONE = 0, - GNOME_ICON_LOOKUP_FLAGS_EMBEDDING_TEXT = 1<<0, - GNOME_ICON_LOOKUP_FLAGS_SHOW_SMALL_IMAGES_AS_THEMSELVES = 1<<1, - GNOME_ICON_LOOKUP_FLAGS_ALLOW_SVG_AS_THEMSELVES = 1<<2 -} GnomeIconLookupFlags; - -typedef enum { - GNOME_ICON_LOOKUP_RESULT_FLAGS_NONE = 0, - GNOME_ICON_LOOKUP_RESULT_FLAGS_THUMBNAIL = 1<<0 -} GnomeIconLookupResultFlags; - -struct GnomeThumbnailFactory; -typedef gboolean (*Ptr_gnome_vfs_init) (void); -typedef char* (*Ptr_gnome_icon_lookup_sync) ( - GtkIconTheme *icon_theme, - GnomeThumbnailFactory *, - const char *file_uri, - const char *custom_icon, - GnomeIconLookupFlags flags, - GnomeIconLookupResultFlags *result); - -class QGtkStylePrivate : public QCommonStylePrivate -{ - Q_DECLARE_PUBLIC(QGtkStyle) -public: - QGtkStylePrivate(); - ~QGtkStylePrivate(); - - QGtkStyleFilter filter; - - static QGtkPainter* gtkPainter(QPainter *painter = 0); - static GtkWidget* gtkWidget(const QHashableLatin1Literal &path); - static GtkStyle* gtkStyle(const QHashableLatin1Literal &path = QHashableLatin1Literal("GtkWindow")); - static void gtkWidgetSetFocus(GtkWidget *widget, bool focus); - - virtual void resolveGtk() const; - virtual void initGtkMenu() const; - virtual void initGtkTreeview() const; - virtual void initGtkWidgets() const; - - static void cleanupGtkWidgets(); - - static bool isKDE4Session(); - void applyCustomPaletteHash(); - static QFont getThemeFont(); - static bool isThemeAvailable() { return gtkStyle() != 0; } - - static bool getGConfBool(const QString &key, bool fallback = 0); - static QString getGConfString(const QString &key, const QString &fallback = QString()); - - static QString getThemeName(); - virtual int getSpinboxArrowSize() const; - - static QIcon getFilesystemIcon(const QFileInfo &); - - static Ptr_gtk_container_forall gtk_container_forall; - static Ptr_gtk_init gtk_init; - static Ptr_gtk_style_attach gtk_style_attach; - static Ptr_gtk_window_new gtk_window_new; - static Ptr_gtk_widget_destroy gtk_widget_destroy; - static Ptr_gtk_widget_realize gtk_widget_realize; - static Ptr_gtk_widget_set_default_direction gtk_widget_set_default_direction; - static Ptr_gtk_widget_modify_color gtk_widget_modify_fg; - static Ptr_gtk_widget_modify_color gtk_widget_modify_bg; - static Ptr_gtk_menu_item_new_with_label gtk_menu_item_new_with_label; - static Ptr_gtk_arrow_new gtk_arrow_new; - static Ptr_gtk_check_menu_item_new_with_label gtk_check_menu_item_new_with_label; - static Ptr_gtk_menu_bar_new gtk_menu_bar_new; - static Ptr_gtk_menu_new gtk_menu_new; - static Ptr_gtk_expander_new gtk_expander_new; - static Ptr_gtk_button_new gtk_button_new; - static Ptr_gtk_tool_button_new gtk_tool_button_new; - static Ptr_gtk_hbutton_box_new gtk_hbutton_box_new; - static Ptr_gtk_check_button_new gtk_check_button_new; - static Ptr_gtk_radio_button_new gtk_radio_button_new; - static Ptr_gtk_spin_button_new gtk_spin_button_new; - static Ptr_gtk_separator_tool_item_new gtk_separator_tool_item_new; - static Ptr_gtk_toolbar_insert gtk_toolbar_insert; - static Ptr_gtk_frame_new gtk_frame_new; - static Ptr_gtk_statusbar_new gtk_statusbar_new; - static Ptr_gtk_entry_new gtk_entry_new; - static Ptr_gtk_hscale_new gtk_hscale_new; - static Ptr_gtk_vscale_new gtk_vscale_new; - static Ptr_gtk_hscrollbar_new gtk_hscrollbar_new; - static Ptr_gtk_vscrollbar_new gtk_vscrollbar_new; - static Ptr_gtk_scrolled_window_new gtk_scrolled_window_new; - static Ptr_gtk_notebook_new gtk_notebook_new; - static Ptr_gtk_toolbar_new gtk_toolbar_new; - static Ptr_gtk_tree_view_new gtk_tree_view_new; - static Ptr_gtk_tree_view_get_column gtk_tree_view_get_column; - static Ptr_gtk_combo_box_new gtk_combo_box_new; - static Ptr_gtk_combo_box_entry_new gtk_combo_box_entry_new; - static Ptr_gtk_progress_bar_new gtk_progress_bar_new; - static Ptr_gtk_container_add gtk_container_add; - static Ptr_gtk_menu_shell_append gtk_menu_shell_append; - static Ptr_gtk_range_get_adjustment gtk_range_get_adjustment; - static Ptr_gtk_range_set_adjustment gtk_range_set_adjustment; - static Ptr_gtk_range_set_inverted gtk_range_set_inverted; - static Ptr_gtk_icon_factory_lookup_default gtk_icon_factory_lookup_default; - static Ptr_gtk_icon_theme_get_default gtk_icon_theme_get_default; - static Ptr_gtk_widget_get_style gtk_widget_get_style; - static Ptr_gtk_widget_style_get gtk_widget_style_get; - static Ptr_gtk_icon_set_render_icon gtk_icon_set_render_icon; - static Ptr_gtk_fixed_new gtk_fixed_new; - static Ptr_gtk_tree_view_column_new gtk_tree_view_column_new; - static Ptr_gtk_tree_view_append_column gtk_tree_view_append_column; - static Ptr_gtk_adjustment_configure gtk_adjustment_configure; - static Ptr_gtk_adjustment_new gtk_adjustment_new; - static Ptr_gtk_menu_item_set_submenu gtk_menu_item_set_submenu; - static Ptr_gtk_settings_get_default gtk_settings_get_default; - static Ptr_gtk_separator_menu_item_new gtk_separator_menu_item_new; - static Ptr_gtk_widget_size_allocate gtk_widget_size_allocate; - static Ptr_gtk_widget_size_request gtk_widget_size_request; - static Ptr_gtk_widget_set_direction gtk_widget_set_direction; - static Ptr_gtk_widget_path gtk_widget_path; - static Ptr_gtk_container_get_type gtk_container_get_type; - static Ptr_gtk_window_get_type gtk_window_get_type; - static Ptr_gtk_widget_get_type gtk_widget_get_type; - static Ptr_gtk_widget_get_parent gtk_widget_get_parent; - static Ptr_gtk_widget_is_toplevel gtk_widget_is_toplevel; - static Ptr_gtk_widget_get_toplevel gtk_widget_get_toplevel; - static Ptr_gtk_rc_get_style_by_paths gtk_rc_get_style_by_paths; - static Ptr_gtk_check_version gtk_check_version; - static Ptr_gtk_border_free gtk_border_free; - static Ptr_gtk_widget_get_allocation gtk_widget_get_allocation; - static Ptr_gtk_widget_set_allocation gtk_widget_set_allocation; - static Ptr_gtk_widget_set_can_default gtk_widget_set_can_default; - static Ptr_gtk_window_set_default gtk_window_set_default; - - static Ptr_gdk_event_new gdk_event_new; - static Ptr_gdk_event_free gdk_event_free; - static Ptr_gtk_widget_send_focus_change gtk_widget_send_focus_change; - - static Ptr_pango_font_description_get_size pango_font_description_get_size; - static Ptr_pango_font_description_get_weight pango_font_description_get_weight; - static Ptr_pango_font_description_get_family pango_font_description_get_family; - static Ptr_pango_font_description_get_style pango_font_description_get_style; - - static Ptr_gdk_pixbuf_get_pixels gdk_pixbuf_get_pixels; - static Ptr_gdk_pixbuf_get_width gdk_pixbuf_get_width; - static Ptr_gdk_pixbuf_get_height gdk_pixbuf_get_height; - static Ptr_gdk_pixbuf_new gdk_pixbuf_new; - static Ptr_gdk_pixbuf_unref gdk_pixbuf_unref; - static Ptr_gdk_color_free gdk_color_free; - static Ptr_gdk_x11_window_set_user_time gdk_x11_window_set_user_time; - static Ptr_gdk_x11_drawable_get_xid gdk_x11_drawable_get_xid; - static Ptr_gdk_x11_drawable_get_xdisplay gdk_x11_drawable_get_xdisplay; - - static Ptr_gconf_client_get_default gconf_client_get_default; - static Ptr_gconf_client_get_string gconf_client_get_string; - static Ptr_gconf_client_get_bool gconf_client_get_bool; - - static Ptr_gnome_icon_lookup_sync gnome_icon_lookup_sync; - static Ptr_gnome_vfs_init gnome_vfs_init; - - virtual QPalette gtkWidgetPalette(const QHashableLatin1Literal >kWidgetName) const; - -protected: - typedef QHash<QHashableLatin1Literal, GtkWidget*> WidgetMap; - - static inline void destroyWidgetMap() - { - cleanupGtkWidgets(); - delete widgetMap; - widgetMap = 0; - } - - static inline WidgetMap *gtkWidgetMap() - { - if (!widgetMap) { - widgetMap = new WidgetMap(); - qAddPostRoutine(destroyWidgetMap); - } - return widgetMap; - } - - static QStringList extract_filter(const QString &rawFilter); - - virtual GtkWidget* getTextColorWidget() const; - static void setupGtkWidget(GtkWidget* widget); - static void addWidgetToMap(GtkWidget* widget); - static void addAllSubWidgets(GtkWidget *widget, gpointer v = 0); - static void addWidget(GtkWidget *widget); - static void removeWidgetFromMap(const QHashableLatin1Literal &path); - - virtual void init(); - - enum { - menuItemFrame = 2, // menu item frame width - menuItemHMargin = 3, // menu item hor text margin - menuArrowHMargin = 6, // menu arrow horizontal margin - menuItemVMargin = 2, // menu item ver text margin - menuRightBorder = 15, // right border on menus - menuCheckMarkWidth = 12 // checkmarks width on menus - }; - -private: - static QList<QGtkStylePrivate *> instances; - static WidgetMap *widgetMap; - friend class QGtkStyleUpdateScheduler; -}; - -// Helper to ensure that we have polished all our gtk widgets -// before updating our own palettes -class QGtkStyleUpdateScheduler : public QObject -{ - Q_OBJECT -public slots: - void updateTheme(); -}; - -QT_END_NAMESPACE - -#endif // !QT_NO_STYLE_GTK -#endif // QGTKSTYLE_P_P_H diff --git a/src/widgets/styles/qmacstyle_mac.mm b/src/widgets/styles/qmacstyle_mac.mm index 7fc4093e81..c20639d20e 100644 --- a/src/widgets/styles/qmacstyle_mac.mm +++ b/src/widgets/styles/qmacstyle_mac.mm @@ -36,7 +36,7 @@ .../doc/src/qstyles.qdoc. */ -#include <Cocoa/Cocoa.h> +#include <AppKit/AppKit.h> #include "qmacstyle_mac_p.h" #include "qmacstyle_mac_p_p.h" @@ -1110,6 +1110,17 @@ static void qt_drawFocusRingOnPath(CGContextRef cg, NSBezierPath *focusRingPath) CGContextRestoreGState(cg); } +QAquaWidgetSize QMacStylePrivate::effectiveAquaSizeConstrain(const QStyleOption *option, + const QWidget *widg, + QStyle::ContentsType ct, + QSize szHint, QSize *insz) const +{ + QAquaWidgetSize sz = aquaSizeConstrain(option, widg, ct, szHint, insz); + if (sz == QAquaSizeUnknown) + return QAquaSizeLarge; + return sz; +} + QAquaWidgetSize QMacStylePrivate::aquaSizeConstrain(const QStyleOption *option, const QWidget *widg, QStyle::ContentsType ct, QSize szHint, QSize *insz) const { @@ -2475,32 +2486,9 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW ret = 0; break; case PM_TitleBarHeight: - if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) { - HIThemeWindowDrawInfo wdi; - wdi.version = qt_mac_hitheme_version; - wdi.state = kThemeStateActive; - wdi.windowType = QtWinType; - if (tb->titleBarState) - wdi.attributes = kThemeWindowHasFullZoom | kThemeWindowHasCloseBox - | kThemeWindowHasCollapseBox; - else if (tb->titleBarFlags & Qt::WindowSystemMenuHint) - wdi.attributes = kThemeWindowHasCloseBox; - else - wdi.attributes = 0; - wdi.titleHeight = tb->rect.height(); - wdi.titleWidth = tb->rect.width(); - QCFType<HIShapeRef> region; - HIRect hirect = qt_hirectForQRect(tb->rect); - if (hirect.size.width <= 0) - hirect.size.width = 100; - if (hirect.size.height <= 0) - hirect.size.height = 30; - - HIThemeGetWindowShape(&hirect, &wdi, kWindowTitleBarRgn, ®ion); - HIRect rect; - ptrHIShapeGetBounds(region, &rect); - ret = int(rect.size.height); - } + // Always use NSTitledWindowMask since we never need any other type of window here + ret = int([NSWindow frameRectForContentRect:NSZeroRect + styleMask:NSTitledWindowMask].size.height); break; case PM_TabBarTabVSpace: ret = 4; @@ -2530,29 +2518,10 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW } break; case PM_ScrollBarExtent: { - if ([NSScroller preferredScrollerStyle] == NSScrollerStyleOverlay) { - switch (d->aquaSizeConstrain(opt, widget)) { - case QAquaSizeUnknown: - case QAquaSizeLarge: - ret = QSysInfo::macVersion() >= QSysInfo::MV_10_8 ? 16 : 9; - break; - case QAquaSizeMini: - case QAquaSizeSmall: - ret = QSysInfo::macVersion() >= QSysInfo::MV_10_8 ? 14 : 7; - break; - } - break; - } - switch (d->aquaSizeConstrain(opt, widget)) { - case QAquaSizeUnknown: - case QAquaSizeLarge: - GetThemeMetric(kThemeMetricScrollBarWidth, &ret); - break; - case QAquaSizeMini: - case QAquaSizeSmall: - GetThemeMetric(kThemeMetricSmallScrollBarWidth, &ret); - break; - } + const QAquaWidgetSize size = d->effectiveAquaSizeConstrain(opt, widget); + ret = static_cast<SInt32>([NSScroller + scrollerWidthForControlSize:static_cast<NSControlSize>(size) + scrollerStyle:[NSScroller preferredScrollerStyle]]); break; } case PM_IndicatorHeight: { switch (d->aquaSizeConstrain(opt, widget)) { diff --git a/src/widgets/styles/qmacstyle_mac_p_p.h b/src/widgets/styles/qmacstyle_mac_p_p.h index 33818568ec..8e138ea887 100644 --- a/src/widgets/styles/qmacstyle_mac_p_p.h +++ b/src/widgets/styles/qmacstyle_mac_p_p.h @@ -172,6 +172,9 @@ public: QAquaWidgetSize aquaSizeConstrain(const QStyleOption *option, const QWidget *widg, QStyle::ContentsType ct = QStyle::CT_CustomBase, QSize szHint=QSize(-1, -1), QSize *insz = 0) const; + QAquaWidgetSize effectiveAquaSizeConstrain(const QStyleOption *option, const QWidget *widg, + QStyle::ContentsType ct = QStyle::CT_CustomBase, + QSize szHint=QSize(-1, -1), QSize *insz = 0) const; void getSliderInfo(QStyle::ComplexControl cc, const QStyleOptionSlider *slider, HIThemeTrackDrawInfo *tdi, const QWidget *needToRemoveMe) const; inline int animateSpeed(Animates) const { return 33; } diff --git a/src/widgets/styles/qstyle.cpp b/src/widgets/styles/qstyle.cpp index 02c420e55c..9bca11b992 100644 --- a/src/widgets/styles/qstyle.cpp +++ b/src/widgets/styles/qstyle.cpp @@ -691,7 +691,9 @@ void QStyle::drawItemPixmap(QPainter *painter, const QRect &rect, int alignment, \value PE_PanelToolBar The panel for a toolbar. \value PE_PanelTipLabel The panel for a tip label. \value PE_FrameTabBarBase The frame that is drawn for a tab bar, ususally drawn for a tab bar that isn't part of a tab widget. - \value PE_IndicatorTabTear An indicator that a tab is partially scrolled out of the visible tab bar when there are many tabs. + \value PE_IndicatorTabTear Deprecated. Use \l{PE_IndicatorTabTearLeft} instead. + \value PE_IndicatorTabTearLeft An indicator that a tab is partially scrolled out on the left side of the visible tab bar when there are many tabs. + \value PE_IndicatorTabTearRight An indicator that a tab is partially scrolled out on the right side of the visible tab bar when there are many tabs. \value PE_IndicatorColumnViewArrow An arrow in a QColumnView. \value PE_Widget A plain QWidget. @@ -1057,7 +1059,12 @@ void QStyle::drawItemPixmap(QPainter *painter, const QRect &rect, int alignment, \value SE_ItemViewItemCheckIndicator Area for a view item's check mark. - \value SE_TabBarTearIndicator Area for the tear indicator on a tab bar with scroll arrows. + \value SE_TabBarTearIndicator Deprecated. Use SE_TabBarTearIndicatorLeft instead. + \value SE_TabBarTearIndicatorLeft Area for the tear indicator on the left side of a tab bar with scroll arrows. + \value SE_TabBarTearIndicatorRight Area for the tear indicator on the right side of a tab bar with scroll arrows. + + \value SE_TabBarScrollLeftButton Area for the scroll left button on a tab bar with scroll buttons. + \value SE_TabBarScrollRightButton Area for the scroll right button on a tab bar with scroll buttons. \value SE_TreeViewDisclosureItem Area for the actual disclosure item in a tree branch. diff --git a/src/widgets/styles/qstyle.h b/src/widgets/styles/qstyle.h index 1e9d15c993..dad93ec0fc 100644 --- a/src/widgets/styles/qstyle.h +++ b/src/widgets/styles/qstyle.h @@ -134,7 +134,7 @@ public: PE_FrameGroupBox, PE_FrameLineEdit, PE_FrameMenu, - PE_FrameStatusBar, // obsolete + PE_FrameStatusBar, // ### Qt 6: remove PE_FrameStatusBarItem = PE_FrameStatusBar, PE_FrameTabWidget, PE_FrameWindow, @@ -155,7 +155,7 @@ public: PE_IndicatorArrowUp, PE_IndicatorBranch, PE_IndicatorButtonDropDown, - PE_IndicatorViewItemCheck, + PE_IndicatorViewItemCheck, // ### Qt 6: remove PE_IndicatorItemViewItemCheck = PE_IndicatorViewItemCheck, PE_IndicatorCheckBox, PE_IndicatorDockWidgetResizeHandle, @@ -171,6 +171,7 @@ public: PE_IndicatorToolBarSeparator, PE_PanelTipLabel, PE_IndicatorTabTear, + PE_IndicatorTabTearLeft = PE_IndicatorTabTear, PE_PanelScrollAreaCorner, PE_Widget, @@ -186,6 +187,8 @@ public: PE_IndicatorTabClose, PE_PanelMenu, + PE_IndicatorTabTearRight, + // do not add any values below/greater this PE_CustomBase = 0xf000000 }; @@ -298,10 +301,11 @@ public: SE_TabWidgetLeftCorner, SE_TabWidgetRightCorner, - SE_ViewItemCheckIndicator, + SE_ViewItemCheckIndicator, // ### Qt 6: remove SE_ItemViewItemCheckIndicator = SE_ViewItemCheckIndicator, SE_TabBarTearIndicator, + SE_TabBarTearIndicatorLeft = SE_TabBarTearIndicator, SE_TreeViewDisclosureItem, @@ -341,6 +345,10 @@ public: SE_ToolBarHandle, + SE_TabBarScrollLeftButton, + SE_TabBarScrollRightButton, + SE_TabBarTearIndicatorRight, + // do not add any values below/greater than this SE_CustomBase = 0xf0000000 }; @@ -491,9 +499,9 @@ public: PM_DialogButtonsButtonHeight, PM_MdiSubWindowFrameWidth, - PM_MDIFrameWidth = PM_MdiSubWindowFrameWidth, //obsolete + PM_MDIFrameWidth = PM_MdiSubWindowFrameWidth, // ### Qt 6: remove PM_MdiSubWindowMinimizedWidth, - PM_MDIMinimizedWidth = PM_MdiSubWindowMinimizedWidth, //obsolete + PM_MDIMinimizedWidth = PM_MdiSubWindowMinimizedWidth, // ### Qt 6: remove PM_HeaderMargin, PM_HeaderMarkSize, @@ -511,9 +519,9 @@ public: PM_SpinBoxSliderHeight, - PM_DefaultTopLevelMargin, - PM_DefaultChildMargin, - PM_DefaultLayoutSpacing, + PM_DefaultTopLevelMargin, // ### Qt 6: remove + PM_DefaultChildMargin, // ### Qt 6: remove + PM_DefaultLayoutSpacing, // ### Qt 6: remove PM_ToolBarIconSize, PM_ListViewIconSize, @@ -631,7 +639,7 @@ public: SH_ComboBox_Popup, SH_TitleBar_NoBorder, SH_Slider_StopMouseOverSlider, - SH_ScrollBar_StopMouseOverSlider = SH_Slider_StopMouseOverSlider, // obsolete + SH_ScrollBar_StopMouseOverSlider = SH_Slider_StopMouseOverSlider, // ### Qt 6: remove SH_BlinkCursorWhenTextSelected, SH_RichText_FullWidthSelection, SH_Menu_Scrollable, diff --git a/src/widgets/styles/qstylefactory.cpp b/src/widgets/styles/qstylefactory.cpp index 520e303d93..f651521cbd 100644 --- a/src/widgets/styles/qstylefactory.cpp +++ b/src/widgets/styles/qstylefactory.cpp @@ -44,9 +44,6 @@ #include "qandroidstyle_p.h" #endif #endif -#ifndef QT_NO_STYLE_GTK -#include "qgtkstyle_p.h" -#endif #ifndef QT_NO_STYLE_WINDOWSXP #include "qwindowsxpstyle_p.h" #endif @@ -86,7 +83,7 @@ Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, The valid keys can be retrieved using the keys() function. Typically they include "windows" and "fusion". - Depending on the platform, "windowsxp", "windowsvista", "gtk" + Depending on the platform, "windowsxp", "windowsvista" and "macintosh" may be available. Note that keys are case insensitive. @@ -143,11 +140,6 @@ QStyle *QStyleFactory::create(const QString& key) ret = new QAndroidStyle; else #endif -#ifndef QT_NO_STYLE_GTK - if (style == QLatin1String("gtk") || style == QLatin1String("gtk+")) - ret = new QGtkStyle; - else -#endif #ifndef QT_NO_STYLE_MAC if (style.startsWith(QLatin1String("macintosh"))) { ret = new QMacStyle; @@ -210,10 +202,6 @@ QStringList QStyleFactory::keys() if (!list.contains(QLatin1String("Android"))) list << QLatin1String("Android"); #endif -#ifndef QT_NO_STYLE_GTK - if (!list.contains(QLatin1String("GTK+"))) - list << QLatin1String("GTK+"); -#endif #ifndef QT_NO_STYLE_FUSION if (!list.contains(QLatin1String("Fusion"))) list << QLatin1String("Fusion"); diff --git a/src/widgets/styles/qstylesheetstyle.cpp b/src/widgets/styles/qstylesheetstyle.cpp index d3f667748e..0c255f231e 100644 --- a/src/widgets/styles/qstylesheetstyle.cpp +++ b/src/widgets/styles/qstylesheetstyle.cpp @@ -2556,7 +2556,13 @@ void QStyleSheetStyle::setPalette(QWidget *w) { PseudoClass_Enabled, QPalette::Inactive } }; - QPalette p = w->palette(); + const bool useStyleSheetPropagationInWidgetStyles = + QCoreApplication::testAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles); + + QPalette p; + if (!useStyleSheetPropagationInWidgetStyles) + p = w->palette(); + QWidget *ew = embeddedWidget(w); for (int i = 0; i < 3; i++) { @@ -2573,32 +2579,84 @@ void QStyleSheetStyle::setPalette(QWidget *w) rule.configurePalette(&p, map[i].group, ew, ew != w); } - styleSheetCaches->customPaletteWidgets.insert(w, w->palette()); - w->setPalette(p); - if (ew != w) - ew->setPalette(p); + if (!useStyleSheetPropagationInWidgetStyles || p.resolve() != 0) { + QPalette wp = w->palette(); + styleSheetCaches->customPaletteWidgets.insert(w, qMakePair(wp, p.resolve())); + + if (useStyleSheetPropagationInWidgetStyles) { + p = p.resolve(wp); + p.resolve(p.resolve() | wp.resolve()); + } + + w->setPalette(p); + if (ew != w) + ew->setPalette(p); + } } void QStyleSheetStyle::unsetPalette(QWidget *w) { + const bool useStyleSheetPropagationInWidgetStyles = + QCoreApplication::testAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles); + if (styleSheetCaches->customPaletteWidgets.contains(w)) { - QPalette p = styleSheetCaches->customPaletteWidgets.value(w); - w->setPalette(p); + QPair<QPalette, uint> p = styleSheetCaches->customPaletteWidgets.value(w); + styleSheetCaches->customPaletteWidgets.remove(w); + + QPalette original = p.first; + + if (useStyleSheetPropagationInWidgetStyles) { + original.resolve(original.resolve() & p.second); + + QPalette wp = w->palette(); + wp.resolve(wp.resolve() & ~p.second); + wp.resolve(original); + wp.resolve(wp.resolve() | original.resolve()); + original = wp; + } + + w->setPalette(original); QWidget *ew = embeddedWidget(w); if (ew != w) - ew->setPalette(p); - styleSheetCaches->customPaletteWidgets.remove(w); + ew->setPalette(original); } - QVariant oldFont = w->property("_q_styleSheetWidgetFont"); - if (oldFont.isValid()) { - w->setFont(qvariant_cast<QFont>(oldFont)); + + if (useStyleSheetPropagationInWidgetStyles) { + unsetStyleSheetFont(w); + QWidget *ew = embeddedWidget(w); + if (ew != w) + unsetStyleSheetFont(ew); + } else { + QVariant oldFont = w->property("_q_styleSheetWidgetFont"); + if (oldFont.isValid()) { + w->setFont(qvariant_cast<QFont>(oldFont)); + } } + if (styleSheetCaches->autoFillDisabledWidgets.contains(w)) { embeddedWidget(w)->setAutoFillBackground(true); styleSheetCaches->autoFillDisabledWidgets.remove(w); } } +void QStyleSheetStyle::unsetStyleSheetFont(QWidget *w) const +{ + if (styleSheetCaches->customFontWidgets.contains(w)) { + QPair<QFont, uint> f = styleSheetCaches->customFontWidgets.value(w); + styleSheetCaches->customFontWidgets.remove(w); + + QFont original = f.first; + original.resolve(original.resolve() & f.second); + + QFont font = w->font(); + font.resolve(font.resolve() & ~f.second); + font.resolve(original); + font.resolve(font.resolve() | original.resolve()); + + w->setFont(font); + } +} + static void updateObjects(const QList<const QObject *>& objects) { if (!styleSheetCaches->styleRulesCache.isEmpty() || !styleSheetCaches->hasStyleRuleCache.isEmpty() || !styleSheetCaches->renderRulesCache.isEmpty()) { @@ -2658,6 +2716,7 @@ void QStyleSheetStyleCaches::objectDestroyed(QObject *o) hasStyleRuleCache.remove(o); renderRulesCache.remove(o); customPaletteWidgets.remove((const QWidget *)o); + customFontWidgets.remove(static_cast<QWidget *>(o)); styleSheetCache.remove(o); autoFillDisabledWidgets.remove((const QWidget *)o); } @@ -5846,24 +5905,42 @@ void QStyleSheetStyle::updateStyleSheetFont(QWidget* w) const // we should never override it. if (w->objectName() == QLatin1String("qt_fontDialog_sampleEdit")) return; + QWidget *container = containerWidget(w); QRenderRule rule = renderRule(container, PseudoElement_None, PseudoClass_Active | PseudoClass_Enabled | extendedPseudoClass(container)); - QFont font = rule.font.resolve(w->font()); - if ((!w->isWindow() || w->testAttribute(Qt::WA_WindowPropagation)) - && isNaturalChild(w) && qobject_cast<QWidget *>(w->parent())) { + const bool useStyleSheetPropagationInWidgetStyles = + QCoreApplication::testAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles); - font = font.resolve(static_cast<QWidget *>(w->parent())->font()); - } + if (useStyleSheetPropagationInWidgetStyles) { + unsetStyleSheetFont(w); - if (w->data->fnt == font) - return; + if (rule.font.resolve()) { + QFont wf = w->font(); + styleSheetCaches->customFontWidgets.insert(w, qMakePair(wf, rule.font.resolve())); + + QFont font = rule.font.resolve(wf); + font.resolve(wf.resolve() | rule.font.resolve()); + w->setFont(font); + } + } else { + QFont font = rule.font.resolve(w->font()); - w->data->fnt = font; + if ((!w->isWindow() || w->testAttribute(Qt::WA_WindowPropagation)) + && isNaturalChild(w) && qobject_cast<QWidget *>(w->parent())) { - QEvent e(QEvent::FontChange); - QApplication::sendEvent(w, &e); + font = font.resolve(static_cast<QWidget *>(w->parent())->font()); + } + + if (w->data->fnt == font) + return; + + w->data->fnt = font; + + QEvent e(QEvent::FontChange); + QApplication::sendEvent(w, &e); + } } void QStyleSheetStyle::saveWidgetFont(QWidget* w, const QFont& font) const diff --git a/src/widgets/styles/qstylesheetstyle_default.cpp b/src/widgets/styles/qstylesheetstyle_default.cpp index c3d00f1883..a444be006d 100644 --- a/src/widgets/styles/qstylesheetstyle_default.cpp +++ b/src/widgets/styles/qstylesheetstyle_default.cpp @@ -149,8 +149,7 @@ StyleSheet QStyleSheetStyle::getDefaultStyleSheet() const // pixmap based style doesn't support any features bool styleIsPixmapBased = baseStyle()->inherits("QMacStyle") - || baseStyle()->inherits("QWindowsXPStyle") - || baseStyle()->inherits("QGtkStyle"); + || baseStyle()->inherits("QWindowsXPStyle"); /*QLineEdit { diff --git a/src/widgets/styles/qstylesheetstyle_p.h b/src/widgets/styles/qstylesheetstyle_p.h index 9cd8cb889d..dd2a25aed3 100644 --- a/src/widgets/styles/qstylesheetstyle_p.h +++ b/src/widgets/styles/qstylesheetstyle_p.h @@ -42,6 +42,7 @@ #include "QtCore/qhash.h" #include "QtGui/qevent.h" #include "QtCore/qvector.h" +#include "QtCore/qset.h" #include "QtWidgets/qapplication.h" #include "private/qcssparser_p.h" #include "QtGui/qbrush.h" @@ -149,6 +150,7 @@ private: void unsetPalette(QWidget *); void setProperties(QWidget *); void setGeometry(QWidget *); + void unsetStyleSheetFont(QWidget *) const; QVector<QCss::StyleRule> styleRules(const QObject *obj) const; bool hasStyleRule(const QObject *obj, int part) const; @@ -178,9 +180,12 @@ public: QHash<const QObject *, QHash<int, bool> > hasStyleRuleCache; typedef QHash<int, QHash<quint64, QRenderRule> > QRenderRules; QHash<const QObject *, QRenderRules> renderRulesCache; - QHash<const QWidget *, QPalette> customPaletteWidgets; // widgets whose palette we tampered QHash<const void *, QCss::StyleSheet> styleSheetCache; // parsed style sheets QSet<const QWidget *> autoFillDisabledWidgets; + // widgets whose palettes and fonts we have tampered. stored value pair is + // QPair<old widget value, resolve mask of stylesheet value> + QHash<const QWidget *, QPair<QPalette, uint> > customPaletteWidgets; + QHash<const QWidget *, QPair<QFont, uint> > customFontWidgets; }; diff --git a/src/widgets/styles/styles.pri b/src/widgets/styles/styles.pri index 3707090c4c..968429dc1e 100644 --- a/src/widgets/styles/styles.pri +++ b/src/widgets/styles/styles.pri @@ -42,13 +42,6 @@ contains( styles, all ) { !macx:styles -= mac -contains(QT_CONFIG, gtkstyle) { - QMAKE_CXXFLAGS += $$QT_CFLAGS_QGTKSTYLE - LIBS_PRIVATE += $$QT_LIBS_QGTKSTYLE - styles += gtk - CONFIG += x11 -} - contains( styles, mac ) { HEADERS += \ styles/qmacstyle_mac_p.h \ @@ -90,19 +83,6 @@ contains( styles, windows ) { DEFINES += QT_NO_STYLE_WINDOWS } -contains( styles, gtk ) { - HEADERS += styles/qgtkglobal_p.h - HEADERS += styles/qgtkstyle_p.h - HEADERS += styles/qgtkpainter_p.h - HEADERS += styles/qgtk2painter_p.h - HEADERS += styles/qgtkstyle_p_p.h - SOURCES += styles/qgtkstyle.cpp - SOURCES += styles/qgtkpainter.cpp - SOURCES += styles/qgtk2painter.cpp - SOURCES += styles/qgtkstyle_p.cpp -} else { - DEFINES += QT_NO_STYLE_GTK -} contains( styles, fusion ) { HEADERS += styles/qfusionstyle_p.h HEADERS += styles/qfusionstyle_p_p.h diff --git a/src/widgets/util/qflickgesture.cpp b/src/widgets/util/qflickgesture.cpp index c7e0861a0f..6d23977dc0 100644 --- a/src/widgets/util/qflickgesture.cpp +++ b/src/widgets/util/qflickgesture.cpp @@ -40,6 +40,7 @@ #include "qgraphicssceneevent.h" #include "qgraphicsview.h" #include "qscroller.h" +#include <QtGui/qtouchdevice.h> #include "private/qapplication_p.h" #include "private/qevent_p.h" #include "private/qflickgesture_p.h" diff --git a/src/widgets/util/qscroller_mac.mm b/src/widgets/util/qscroller_mac.mm index 07de07de52..51485973b8 100644 --- a/src/widgets/util/qscroller_mac.mm +++ b/src/widgets/util/qscroller_mac.mm @@ -35,7 +35,7 @@ #ifdef Q_DEAD_CODE_FROM_QT4_MAC -#import <Cocoa/Cocoa.h> +#import <AppKit/AppKit.h> #include "qscroller_p.h" diff --git a/src/widgets/util/qsystemtrayicon_x11.cpp b/src/widgets/util/qsystemtrayicon_x11.cpp index 9a69db1bc8..e50b0bd12c 100644 --- a/src/widgets/util/qsystemtrayicon_x11.cpp +++ b/src/widgets/util/qsystemtrayicon_x11.cpp @@ -170,7 +170,9 @@ void QSystemTrayIconSys::mousePressEvent(QMouseEvent *ev) #ifndef QT_NO_CONTEXTMENU if (ev->button() == Qt::RightButton && q->contextMenu()) q->contextMenu()->popup(globalPos); -#endif +#else + Q_UNUSED(globalPos) +#endif // QT_NO_CONTEXTMENU if (QBalloonTip::isBalloonVisible()) { emit q->messageClicked(); diff --git a/src/widgets/widgets/qcalendarwidget.cpp b/src/widgets/widgets/qcalendarwidget.cpp index 48b224fe13..5e6db46a63 100644 --- a/src/widgets/widgets/qcalendarwidget.cpp +++ b/src/widgets/widgets/qcalendarwidget.cpp @@ -53,6 +53,8 @@ #include <qbasictimer.h> #include <qstylepainter.h> +#include <vector> + QT_BEGIN_NAMESPACE enum { @@ -423,6 +425,17 @@ QString QCalendarYearValidator::text(const QDate &date, int repeat) const /////////////////////////////////// +struct SectionToken { + Q_DECL_CONSTEXPR SectionToken(QCalendarDateSectionValidator *v, int rep) + : validator(v), repeat(rep) {} + + QCalendarDateSectionValidator *validator; + int repeat; +}; +} // unnamed namespace +Q_DECLARE_TYPEINFO(SectionToken, Q_PRIMITIVE_TYPE); +namespace { + class QCalendarDateValidator { public: @@ -438,13 +451,6 @@ public: void setLocale(const QLocale &locale); private: - - struct SectionToken { - SectionToken(QCalendarDateSectionValidator *val, int rep) : validator(val), repeat(rep) {} - QCalendarDateSectionValidator *validator; - int repeat; - }; - void toNextToken(); void toPreviousToken(); void applyToDate(); @@ -453,12 +459,12 @@ private: void clear(); QStringList m_separators; - QList<SectionToken *> m_tokens; + std::vector<SectionToken> m_tokens; QCalendarYearValidator m_yearValidator; QCalendarMonthValidator m_monthValidator; QCalendarDayValidator m_dayValidator; - SectionToken *m_currentToken; + int m_currentToken; QDate m_initialDate; QDate m_currentDate; @@ -467,7 +473,7 @@ private: }; QCalendarDateValidator::QCalendarDateValidator() - : m_currentToken(Q_NULLPTR), + : m_currentToken(-1), m_initialDate(QDate::currentDate()), m_currentDate(m_initialDate), m_lastSectionMove(QCalendarDateSectionValidator::ThisSection) @@ -510,17 +516,16 @@ void QCalendarDateValidator::setInitialDate(const QDate &date) QString QCalendarDateValidator::currentText() const { QString str; - QStringListIterator itSep(m_separators); - QListIterator<SectionToken *> itTok(m_tokens); - while (itSep.hasNext()) { - str += itSep.next(); - if (itTok.hasNext()) { - SectionToken *token = itTok.next(); - QCalendarDateSectionValidator *validator = token->validator; - if (m_currentToken == token) - str += validator->text(); + const int numSeps = m_separators.size(); + const int numTokens = int(m_tokens.size()); + for (int i = 0; i < numSeps; ++i) { + str += m_separators.at(i); + if (i < numTokens) { + const SectionToken &token = m_tokens[i]; + if (i == m_currentToken) + str += token.validator->text(); else - str += validator->text(m_currentDate, token->repeat); + str += token.validator->text(m_currentDate, token.repeat); } } return str; @@ -528,14 +533,10 @@ QString QCalendarDateValidator::currentText() const void QCalendarDateValidator::clear() { - QListIterator<SectionToken *> it(m_tokens); - while (it.hasNext()) - delete it.next(); - m_tokens.clear(); m_separators.clear(); - m_currentToken = 0; + m_currentToken = -1; } void QCalendarDateValidator::setFormat(const QString &format) @@ -558,25 +559,25 @@ void QCalendarDateValidator::setFormat(const QString &format) separator += nextChar; quoting = false; } else { - SectionToken *token = 0; + QCalendarDateSectionValidator *validator = 0; if (nextChar == QLatin1Char('d')) { offset = qMin(4, countRepeat(format, pos)); - token = new SectionToken(&m_dayValidator, offset); + validator = &m_dayValidator; } else if (nextChar == QLatin1Char('M')) { offset = qMin(4, countRepeat(format, pos)); - token = new SectionToken(&m_monthValidator, offset); + validator = &m_monthValidator; } else if (nextChar == QLatin1Char('y')) { offset = qMin(4, countRepeat(format, pos)); - token = new SectionToken(&m_yearValidator, offset); + validator = &m_yearValidator; } else { separator += nextChar; } - if (token) { - m_tokens.append(token); + if (validator) { + m_tokens.push_back(SectionToken(validator, offset)); m_separators.append(separator); separator = QString(); - if (!m_currentToken) - m_currentToken = token; + if (m_currentToken < 0) + m_currentToken = int(m_tokens.size()) - 1; } } @@ -595,29 +596,23 @@ void QCalendarDateValidator::applyToDate() void QCalendarDateValidator::toNextToken() { - const int idx = m_tokens.indexOf(m_currentToken); - if (idx == -1) + if (m_currentToken < 0) return; - if (idx + 1 >= m_tokens.count()) - m_currentToken = m_tokens.first(); - else - m_currentToken = m_tokens.at(idx + 1); + ++m_currentToken; + m_currentToken %= m_tokens.size(); } void QCalendarDateValidator::toPreviousToken() { - const int idx = m_tokens.indexOf(m_currentToken); - if (idx == -1) + if (m_currentToken < 0) return; - if (idx - 1 < 0) - m_currentToken = m_tokens.last(); - else - m_currentToken = m_tokens.at(idx - 1); + --m_currentToken; + m_currentToken %= m_tokens.size(); } void QCalendarDateValidator::handleKeyEvent(QKeyEvent *keyEvent) { - if (!m_currentToken) + if (m_currentToken < 0) return; int key = keyEvent->key(); @@ -630,7 +625,7 @@ void QCalendarDateValidator::handleKeyEvent(QKeyEvent *keyEvent) else if (key == Qt::Key_Left) toPreviousToken(); - m_lastSectionMove = m_currentToken->validator->handleKey(key); + m_lastSectionMove = m_tokens[m_currentToken].validator->handleKey(key); applyToDate(); if (m_lastSectionMove == QCalendarDateSectionValidator::NextSection) @@ -821,6 +816,41 @@ void QCalendarTextNavigator::setDateEditAcceptDelay(int delay) class QCalendarView; +// a small helper class that replaces a QMap<Qt::DayOfWeek, T>, +// but requires T to have a member-swap and a default constructor +// which should be cheap (no memory allocations) + +QT_WARNING_PUSH +QT_WARNING_DISABLE_MSVC(4351) // "new behavior: elements of array ... will be default initialized" + +template <typename T> +class StaticDayOfWeekAssociativeArray { + bool contained[7]; + T data[7]; + + static Q_DECL_CONSTEXPR int day2idx(Qt::DayOfWeek day) Q_DECL_NOTHROW { return int(day) - 1; } // alt: day % 7 +public: + Q_DECL_CONSTEXPR StaticDayOfWeekAssociativeArray() Q_DECL_NOEXCEPT_EXPR(noexcept(T())) + : contained(), data() {} + + Q_DECL_CONSTEXPR bool contains(Qt::DayOfWeek day) const Q_DECL_NOTHROW { return contained[day2idx(day)]; } + Q_DECL_CONSTEXPR const T &value(Qt::DayOfWeek day) const Q_DECL_NOTHROW { return data[day2idx(day)]; } + + Q_DECL_RELAXED_CONSTEXPR T &operator[](Qt::DayOfWeek day) Q_DECL_NOTHROW + { + const int idx = day2idx(day); + contained[idx] = true; + return data[idx]; + } + + Q_DECL_RELAXED_CONSTEXPR void insert(Qt::DayOfWeek day, T v) Q_DECL_NOTHROW + { + operator[](day).swap(v); + } +}; + +QT_WARNING_POP + class QCalendarModel : public QAbstractTableModel { Q_OBJECT @@ -899,7 +929,7 @@ public: Qt::DayOfWeek m_firstDay; QCalendarWidget::HorizontalHeaderFormat m_horizontalHeaderFormat; bool m_weekNumbersShown; - QMap<Qt::DayOfWeek, QTextCharFormat> m_dayFormats; + StaticDayOfWeekAssociativeArray<QTextCharFormat> m_dayFormats; QMap<QDate, QTextCharFormat> m_dateFormats; QTextCharFormat m_headerFormat; QCalendarView *m_view; diff --git a/src/widgets/widgets/qcombobox.cpp b/src/widgets/widgets/qcombobox.cpp index ed3af90532..deffad432f 100644 --- a/src/widgets/widgets/qcombobox.cpp +++ b/src/widgets/widgets/qcombobox.cpp @@ -1770,7 +1770,9 @@ void QComboBox::setLineEdit(QLineEdit *edit) delete d->lineEdit; d->lineEdit = edit; +#ifndef QT_NO_IM qt_widget_private(d->lineEdit)->inheritsInputMethodHints = 1; +#endif if (d->lineEdit->parent() != this) d->lineEdit->setParent(this); connect(d->lineEdit, SIGNAL(returnPressed()), this, SLOT(_q_returnPressed())); diff --git a/src/widgets/widgets/qcombobox.h b/src/widgets/widgets/qcombobox.h index 3182a0a3aa..45b601788f 100644 --- a/src/widgets/widgets/qcombobox.h +++ b/src/widgets/widgets/qcombobox.h @@ -229,7 +229,9 @@ protected: #ifndef QT_NO_WHEELEVENT void wheelEvent(QWheelEvent *e) Q_DECL_OVERRIDE; #endif +#ifndef QT_NO_CONTEXTMENU void contextMenuEvent(QContextMenuEvent *e) Q_DECL_OVERRIDE; +#endif // QT_NO_CONTEXTMENU void inputMethodEvent(QInputMethodEvent *) Q_DECL_OVERRIDE; void initStyleOption(QStyleOptionComboBox *option) const; diff --git a/src/widgets/widgets/qgroupbox.cpp b/src/widgets/widgets/qgroupbox.cpp index 0ff5dc8753..f1ebf91ad7 100644 --- a/src/widgets/widgets/qgroupbox.cpp +++ b/src/widgets/widgets/qgroupbox.cpp @@ -707,6 +707,8 @@ void QGroupBox::mousePressEvent(QMouseEvent *event) if (d->checkable && (d->pressedControl & (QStyle::SC_GroupBoxCheckBox | QStyle::SC_GroupBoxLabel))) { d->overCheckBox = true; update(style()->subControlRect(QStyle::CC_GroupBox, &box, QStyle::SC_GroupBoxCheckBox, this)); + } else { + event->ignore(); } } @@ -723,6 +725,8 @@ void QGroupBox::mouseMoveEvent(QMouseEvent *event) if (d->checkable && (d->pressedControl == QStyle::SC_GroupBoxCheckBox || d->pressedControl == QStyle::SC_GroupBoxLabel) && (d->overCheckBox != oldOverCheckBox)) update(style()->subControlRect(QStyle::CC_GroupBox, &box, QStyle::SC_GroupBoxCheckBox, this)); + + event->ignore(); } /*! \reimp */ diff --git a/src/widgets/widgets/qlabel.cpp b/src/widgets/widgets/qlabel.cpp index 55e277026c..eeabd5db38 100644 --- a/src/widgets/widgets/qlabel.cpp +++ b/src/widgets/widgets/qlabel.cpp @@ -882,13 +882,11 @@ void QLabel::mouseReleaseEvent(QMouseEvent *ev) d->sendControlEvent(ev); } +#ifndef QT_NO_CONTEXTMENU /*!\reimp */ void QLabel::contextMenuEvent(QContextMenuEvent *ev) { -#ifdef QT_NO_CONTEXTMENU - Q_UNUSED(ev); -#else Q_D(QLabel); if (!d->isTextLabel) { ev->ignore(); @@ -902,8 +900,8 @@ void QLabel::contextMenuEvent(QContextMenuEvent *ev) ev->accept(); menu->setAttribute(Qt::WA_DeleteOnClose); menu->popup(ev->globalPos()); -#endif } +#endif // QT_NO_CONTEXTMENU /*! \reimp diff --git a/src/widgets/widgets/qlabel.h b/src/widgets/widgets/qlabel.h index 1a3a68db60..6b6e15e0a8 100644 --- a/src/widgets/widgets/qlabel.h +++ b/src/widgets/widgets/qlabel.h @@ -132,7 +132,9 @@ protected: void mousePressEvent(QMouseEvent *ev) Q_DECL_OVERRIDE; void mouseMoveEvent(QMouseEvent *ev) Q_DECL_OVERRIDE; void mouseReleaseEvent(QMouseEvent *ev) Q_DECL_OVERRIDE; +#ifndef QT_NO_CONTEXTMENU void contextMenuEvent(QContextMenuEvent *ev) Q_DECL_OVERRIDE; +#endif // QT_NO_CONTEXTMENU void focusInEvent(QFocusEvent *ev) Q_DECL_OVERRIDE; void focusOutEvent(QFocusEvent *ev) Q_DECL_OVERRIDE; bool focusNextPrevChild(bool next) Q_DECL_OVERRIDE; diff --git a/src/widgets/widgets/qmaccocoaviewcontainer_mac.mm b/src/widgets/widgets/qmaccocoaviewcontainer_mac.mm index a384e41d1b..7dad6604b9 100644 --- a/src/widgets/widgets/qmaccocoaviewcontainer_mac.mm +++ b/src/widgets/widgets/qmaccocoaviewcontainer_mac.mm @@ -31,7 +31,7 @@ ** ****************************************************************************/ -#import <Cocoa/Cocoa.h> +#import <AppKit/AppKit.h> #include "qmaccocoaviewcontainer_mac.h" #include <QtCore/QDebug> diff --git a/src/widgets/widgets/qmacnativewidget_mac.mm b/src/widgets/widgets/qmacnativewidget_mac.mm index 46a43c4110..f855777837 100644 --- a/src/widgets/widgets/qmacnativewidget_mac.mm +++ b/src/widgets/widgets/qmacnativewidget_mac.mm @@ -31,7 +31,7 @@ ** ****************************************************************************/ -#import <Cocoa/Cocoa.h> +#import <AppKit/AppKit.h> #include "qmacnativewidget_mac.h" #include <QtCore/qdebug.h> diff --git a/src/widgets/widgets/qmdiarea.cpp b/src/widgets/widgets/qmdiarea.cpp index e683c48ad3..1864b42a80 100644 --- a/src/widgets/widgets/qmdiarea.cpp +++ b/src/widgets/widgets/qmdiarea.cpp @@ -380,7 +380,7 @@ void IconTiler::rearrange(QList<QWidget *> &widgets, const QRect &domain) const return; const int n = widgets.size(); - const int width = widgets.at(0)->width(); + const int width = qMax(widgets.at(0)->width(), 1); const int height = widgets.at(0)->height(); const int ncols = qMax(domain.width() / width, 1); const int nrows = n / ncols + ((n % ncols) ? 1 : 0); @@ -938,7 +938,7 @@ void QMdiAreaPrivate::rearrange(Rearranger *rearranger) if (!sanityCheck(child, "QMdiArea::rearrange") || !child->isVisible()) continue; if (rearranger->type() == Rearranger::IconTiler) { - if (child->isMinimized() && !child->isShaded() && !(child->windowFlags() & Qt::FramelessWindowHint)) + if (child->isMinimized() && !child->isShaded()) widgets.append(child); } else { if (child->isMinimized() && !child->isShaded()) diff --git a/src/widgets/widgets/qmdisubwindow.cpp b/src/widgets/widgets/qmdisubwindow.cpp index 14aeb73baf..be6cb7c54c 100644 --- a/src/widgets/widgets/qmdisubwindow.cpp +++ b/src/widgets/widgets/qmdisubwindow.cpp @@ -1000,7 +1000,9 @@ void QMdiSubWindowPrivate::removeBaseWidget() q->setWindowModified(false); } lastChildWindowTitle.clear(); - baseWidget->setParent(0); + // QTBUG-47993: parent widget can be reset before this call + if (baseWidget->parentWidget() == q) + baseWidget->setParent(0); baseWidget = 0; isWidgetHiddenByUs = false; } @@ -2653,7 +2655,7 @@ void QMdiSubWindow::showShaded() resize(d->internalMinimumSize); // Hide the internal widget if not already hidden by the user. - if (d->baseWidget && !d->baseWidget->isHidden()) { + if (d->baseWidget && !d->baseWidget->isHidden() && !(windowFlags() & Qt::FramelessWindowHint)) { d->baseWidget->hide(); d->isWidgetHiddenByUs = true; } diff --git a/src/widgets/widgets/qmenu_mac.mm b/src/widgets/widgets/qmenu_mac.mm index 8b29011178..9278099e18 100644 --- a/src/widgets/widgets/qmenu_mac.mm +++ b/src/widgets/widgets/qmenu_mac.mm @@ -32,7 +32,7 @@ ****************************************************************************/ #import <Foundation/Foundation.h> -#import <Cocoa/Cocoa.h> +#import <AppKit/AppKit.h> #include "qmenu.h" #include "qmenubar.h" diff --git a/src/widgets/widgets/qtabbar.cpp b/src/widgets/widgets/qtabbar.cpp index 7ea5455bf7..d2d737059e 100644 --- a/src/widgets/widgets/qtabbar.cpp +++ b/src/widgets/widgets/qtabbar.cpp @@ -348,13 +348,6 @@ void QTabBar::initStyleOption(QStyleOptionTab *option, int tabIndex) const \since 5.2 */ -int QTabBarPrivate::extraWidth() const -{ - Q_Q(const QTabBar); - return 2 * qMax(q->style()->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, 0, q), - QApplication::globalStrut().width()); -} - void QTabBarPrivate::init() { Q_Q(QTabBar); @@ -408,7 +401,6 @@ int QTabBarPrivate::indexAtPos(const QPoint &p) const void QTabBarPrivate::layoutTabs() { Q_Q(QTabBar); - scrollOffset = 0; layoutDirty = false; QSize size = q->size(); int last, available; @@ -508,39 +500,48 @@ void QTabBarPrivate::layoutTabs() } if (useScrollButtons && tabList.count() && last > available) { - int extra = extraWidth(); - if (!vertTabs) { - Qt::LayoutDirection ld = q->layoutDirection(); - QRect arrows = QStyle::visualRect(ld, q->rect(), - QRect(available - extra, 0, extra, size.height())); - int buttonOverlap = q->style()->pixelMetric(QStyle::PM_TabBar_ScrollButtonOverlap, 0, q); - - if (ld == Qt::LeftToRight) { - leftB->setGeometry(arrows.left(), arrows.top(), extra/2, arrows.height()); - rightB->setGeometry(arrows.right() - extra/2 + buttonOverlap, arrows.top(), - extra/2, arrows.height()); - leftB->setArrowType(Qt::LeftArrow); - rightB->setArrowType(Qt::RightArrow); - } else { - rightB->setGeometry(arrows.left(), arrows.top(), extra/2, arrows.height()); - leftB->setGeometry(arrows.right() - extra/2 + buttonOverlap, arrows.top(), - extra/2, arrows.height()); - rightB->setArrowType(Qt::LeftArrow); - leftB->setArrowType(Qt::RightArrow); - } - } else { - QRect arrows = QRect(0, available - extra, size.width(), extra ); - leftB->setGeometry(arrows.left(), arrows.top(), arrows.width(), extra/2); + const QRect scrollRect = normalizedScrollRect(0); + scrollOffset = -scrollRect.left(); + + Q_Q(QTabBar); + QStyleOption opt; + opt.init(q); + QRect scrollButtonLeftRect = q->style()->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, q); + QRect scrollButtonRightRect = q->style()->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, q); + int scrollButtonWidth = q->style()->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, &opt, q); + + // Normally SE_TabBarScrollLeftButton should have the same width as PM_TabBarScrollButtonWidth. + // But if that is not the case, we set the actual button width to PM_TabBarScrollButtonWidth, and + // use the extra space from SE_TabBarScrollLeftButton as margins towards the tabs. + if (vertTabs) { + scrollButtonLeftRect.setHeight(scrollButtonWidth); + scrollButtonRightRect.setY(scrollButtonRightRect.bottom() + 1 - scrollButtonWidth); + scrollButtonRightRect.setHeight(scrollButtonWidth); leftB->setArrowType(Qt::UpArrow); - rightB->setGeometry(arrows.left(), arrows.bottom() - extra/2 + 1, - arrows.width(), extra/2); rightB->setArrowType(Qt::DownArrow); + } else if (q->layoutDirection() == Qt::RightToLeft) { + scrollButtonRightRect.setWidth(scrollButtonWidth); + scrollButtonLeftRect.setX(scrollButtonLeftRect.right() + 1 - scrollButtonWidth); + scrollButtonLeftRect.setWidth(scrollButtonWidth); + leftB->setArrowType(Qt::RightArrow); + rightB->setArrowType(Qt::LeftArrow); + } else { + scrollButtonLeftRect.setWidth(scrollButtonWidth); + scrollButtonRightRect.setX(scrollButtonRightRect.right() + 1 - scrollButtonWidth); + scrollButtonRightRect.setWidth(scrollButtonWidth); + leftB->setArrowType(Qt::LeftArrow); + rightB->setArrowType(Qt::RightArrow); } - leftB->setEnabled(scrollOffset > 0); - rightB->setEnabled(last - scrollOffset >= available - extra); + + leftB->setGeometry(scrollButtonLeftRect); + leftB->setEnabled(false); leftB->show(); + + rightB->setGeometry(scrollButtonRightRect); + rightB->setEnabled(last - scrollOffset > scrollRect.x() + scrollRect.width()); rightB->show(); } else { + scrollOffset = 0; rightB->hide(); leftB->hide(); } @@ -549,6 +550,81 @@ void QTabBarPrivate::layoutTabs() q->tabLayoutChange(); } +QRect QTabBarPrivate::normalizedScrollRect(int index) +{ + // "Normalized scroll rect" means return the free space on the tab bar + // that doesn't overlap with scroll buttons or tear indicators, and + // always return the rect as horizontal Qt::LeftToRight, even if the + // tab bar itself is in a different orientation. + + Q_Q(QTabBar); + QStyleOptionTab opt; + q->initStyleOption(&opt, currentIndex); + opt.rect = q->rect(); + + QRect scrollButtonLeftRect = q->style()->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, q); + QRect scrollButtonRightRect = q->style()->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, q); + QRect tearLeftRect = q->style()->subElementRect(QStyle::SE_TabBarTearIndicatorLeft, &opt, q); + QRect tearRightRect = q->style()->subElementRect(QStyle::SE_TabBarTearIndicatorRight, &opt, q); + + if (verticalTabs(shape)) { + int topEdge, bottomEdge; + bool leftButtonIsOnTop = scrollButtonLeftRect.y() < q->height() / 2; + bool rightButtonIsOnTop = scrollButtonRightRect.y() < q->height() / 2; + + if (leftButtonIsOnTop && rightButtonIsOnTop) { + topEdge = scrollButtonRightRect.bottom() + 1; + bottomEdge = q->height(); + } else if (!leftButtonIsOnTop && !rightButtonIsOnTop) { + topEdge = 0; + bottomEdge = scrollButtonLeftRect.top(); + } else { + topEdge = scrollButtonLeftRect.bottom() + 1; + bottomEdge = scrollButtonRightRect.top(); + } + + bool tearTopVisible = index != 0 && topEdge != -scrollOffset; + bool tearBottomVisible = index != tabList.size() - 1 && bottomEdge != tabList.last().rect.bottom() + 1 - scrollOffset; + if (tearTopVisible && !tearLeftRect.isNull()) + topEdge = tearLeftRect.bottom() + 1; + if (tearBottomVisible && !tearRightRect.isNull()) + bottomEdge = tearRightRect.top(); + + return QRect(topEdge, 0, bottomEdge - topEdge, q->height()); + } else { + if (q->layoutDirection() == Qt::RightToLeft) { + scrollButtonLeftRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), scrollButtonLeftRect); + scrollButtonRightRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), scrollButtonRightRect); + tearLeftRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), tearLeftRect); + tearRightRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), tearRightRect); + } + + int leftEdge, rightEdge; + bool leftButtonIsOnLeftSide = scrollButtonLeftRect.x() < q->width() / 2; + bool rightButtonIsOnLeftSide = scrollButtonRightRect.x() < q->width() / 2; + + if (leftButtonIsOnLeftSide && rightButtonIsOnLeftSide) { + leftEdge = scrollButtonRightRect.right() + 1; + rightEdge = q->width(); + } else if (!leftButtonIsOnLeftSide && !rightButtonIsOnLeftSide) { + leftEdge = 0; + rightEdge = scrollButtonLeftRect.left(); + } else { + leftEdge = scrollButtonLeftRect.right() + 1; + rightEdge = scrollButtonRightRect.left(); + } + + bool tearLeftVisible = index != 0 && leftEdge != -scrollOffset; + bool tearRightVisible = index != tabList.size() - 1 && rightEdge != tabList.last().rect.right() + 1 - scrollOffset; + if (tearLeftVisible && !tearLeftRect.isNull()) + leftEdge = tearLeftRect.right() + 1; + if (tearRightVisible && !tearRightRect.isNull()) + rightEdge = tearRightRect.left(); + + return QRect(leftEdge, 0, rightEdge - leftEdge, q->height()); + } +} + void QTabBarPrivate::makeVisible(int index) { Q_Q(QTabBar); @@ -558,17 +634,24 @@ void QTabBarPrivate::makeVisible(int index) const QRect tabRect = tabList.at(index).rect; const int oldScrollOffset = scrollOffset; const bool horiz = !verticalTabs(shape); - const int available = (horiz ? q->width() : q->height()) - extraWidth(); - const int start = horiz ? tabRect.left() : tabRect.top(); - const int end = horiz ? tabRect.right() : tabRect.bottom(); - if (start < scrollOffset) // too far left - scrollOffset = start - (index ? 8 : 0); - else if (end > scrollOffset + available) // too far right - scrollOffset = end - available + 1; - - leftB->setEnabled(scrollOffset > 0); - const int last = horiz ? tabList.last().rect.right() : tabList.last().rect.bottom(); - rightB->setEnabled(last - scrollOffset >= available); + const int tabStart = horiz ? tabRect.left() : tabRect.top(); + const int tabEnd = horiz ? tabRect.right() : tabRect.bottom(); + const int lastTabEnd = horiz ? tabList.last().rect.right() : tabList.last().rect.bottom(); + const QRect scrollRect = normalizedScrollRect(index); + const int scrolledTabBarStart = qMax(1, scrollRect.left() + scrollOffset); + const int scrolledTabBarEnd = qMin(lastTabEnd - 1, scrollRect.right() + scrollOffset); + + if (tabStart < scrolledTabBarStart) { + // Tab is outside on the left, so scroll left. + scrollOffset = tabStart - scrollRect.left(); + } else if (tabEnd > scrolledTabBarEnd) { + // Tab is outside on the right, so scroll right. + scrollOffset = tabEnd - scrollRect.right(); + } + + leftB->setEnabled(scrollOffset > -scrollRect.left()); + rightB->setEnabled(scrollOffset < lastTabEnd - scrollRect.right()); + if (oldScrollOffset != scrollOffset) { q->update(); layoutWidgets(); @@ -664,39 +747,24 @@ void QTabBarPrivate::_q_scrollTabs() { Q_Q(QTabBar); const QObject *sender = q->sender(); + const bool horizontal = !verticalTabs(shape); + const QRect scrollRect = normalizedScrollRect(); int i = -1; - if (!verticalTabs(shape)) { - if (sender == leftB) { - for (i = tabList.count() - 1; i >= 0; --i) { - if (tabList.at(i).rect.left() - scrollOffset < 0) { - makeVisible(i); - return; - } - } - } else if (sender == rightB) { - int availableWidth = q->width() - extraWidth(); - for (i = 0; i < tabList.count(); ++i) { - if (tabList.at(i).rect.right() - scrollOffset > availableWidth) { - makeVisible(i); - return; - } + + if (sender == leftB) { + for (i = tabList.count() - 1; i >= 0; --i) { + int start = horizontal ? tabList.at(i).rect.left() : tabList.at(i).rect.top(); + if (start < scrollRect.left() + scrollOffset) { + makeVisible(i); + return; } } - } else { // vertical - if (sender == leftB) { - for (i = tabList.count() - 1; i >= 0; --i) { - if (tabList.at(i).rect.top() - scrollOffset < 0) { - makeVisible(i); - return; - } - } - } else if (sender == rightB) { - int available = q->height() - extraWidth(); - for (i = 0; i < tabList.count(); ++i) { - if (tabList.at(i).rect.bottom() - scrollOffset > available) { - makeVisible(i); - return; - } + } else if (sender == rightB) { + for (i = 0; i < tabList.count(); ++i) { + int end = horizontal ? tabList.at(i).rect.right() : tabList.at(i).rect.bottom(); + if (end > scrollRect.right() + scrollOffset) { + makeVisible(i); + return; } } } @@ -1571,13 +1639,15 @@ void QTabBar::paintEvent(QPaintEvent *) QStylePainter p(this); int selected = -1; - int cut = -1; - bool rtl = optTabBase.direction == Qt::RightToLeft; + int cutLeft = -1; + int cutRight = -1; bool vertical = verticalTabs(d->shape); - QStyleOptionTab cutTab; + QStyleOptionTab cutTabLeft; + QStyleOptionTab cutTabRight; selected = d->currentIndex; if (d->dragInProgress) selected = d->pressedIndex; + const QRect scrollRect = d->normalizedScrollRect(); for (int i = 0; i < d->tabList.count(); ++i) optTabBase.tabBarRect |= tabRect(i); @@ -1600,13 +1670,20 @@ void QTabBar::paintEvent(QPaintEvent *) if (!(tab.state & QStyle::State_Enabled)) { tab.palette.setCurrentColorGroup(QPalette::Disabled); } + // If this tab is partially obscured, make a note of it so that we can pass the information // along when we draw the tear. - if (((!vertical && (!rtl && tab.rect.left() < 0)) || (rtl && tab.rect.right() > width())) - || (vertical && tab.rect.top() < 0)) { - cut = i; - cutTab = tab; + QRect tabRect = d->tabList[i].rect; + int tabStart = vertical ? tabRect.top() : tabRect.left(); + int tabEnd = vertical ? tabRect.bottom() : tabRect.right(); + if (tabStart < scrollRect.left() + d->scrollOffset) { + cutLeft = i; + cutTabLeft = tab; + } else if (tabEnd > scrollRect.right() + d->scrollOffset) { + cutRight = i; + cutTabRight = tab; } + // Don't bother drawing a tab if the entire tab is outside of the visible tab bar. if ((!vertical && (tab.rect.right() < 0 || tab.rect.left() > width())) || (vertical && (tab.rect.bottom() < 0 || tab.rect.top() > height()))) @@ -1638,10 +1715,16 @@ void QTabBar::paintEvent(QPaintEvent *) } // Only draw the tear indicator if necessary. Most of the time we don't need too. - if (d->leftB->isVisible() && cut >= 0) { - cutTab.rect = rect(); - cutTab.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicator, &cutTab, this); - p.drawPrimitive(QStyle::PE_IndicatorTabTear, cutTab); + if (d->leftB->isVisible() && cutLeft >= 0) { + cutTabLeft.rect = rect(); + cutTabLeft.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicatorLeft, &cutTabLeft, this); + p.drawPrimitive(QStyle::PE_IndicatorTabTearLeft, cutTabLeft); + } + + if (d->rightB->isVisible() && cutRight >= 0) { + cutTabRight.rect = rect(); + cutTabRight.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicatorRight, &cutTabRight, this); + p.drawPrimitive(QStyle::PE_IndicatorTabTearRight, cutTabRight); } } diff --git a/src/widgets/widgets/qtabbar_p.h b/src/widgets/widgets/qtabbar_p.h index 38a3c138cc..e6929d5b52 100644 --- a/src/widgets/widgets/qtabbar_p.h +++ b/src/widgets/widgets/qtabbar_p.h @@ -150,7 +150,6 @@ public: int calculateNewPosition(int from, int to, int index) const; void slide(int from, int to); void init(); - int extraWidth() const; Tab *at(int index); const Tab *at(int index) const; @@ -178,6 +177,7 @@ public: bool isTabInMacUnifiedToolbarArea() const; void setupMovableTab(); void autoHideTabs(); + QRect normalizedScrollRect(int index = -1); void makeVisible(int index); QSize iconSize; diff --git a/src/widgets/widgets/qtextedit.cpp b/src/widgets/widgets/qtextedit.cpp index 7439005b92..ba6db2971a 100644 --- a/src/widgets/widgets/qtextedit.cpp +++ b/src/widgets/widgets/qtextedit.cpp @@ -1060,6 +1060,8 @@ bool QTextEdit::event(QEvent *e) || e->type() == QEvent::ToolTip) { d->sendControlEvent(e); } +#else + Q_UNUSED(d) #endif // QT_NO_CONTEXTMENU #ifdef QT_KEYPAD_NAVIGATION if (e->type() == QEvent::EnterEditFocus || e->type() == QEvent::LeaveEditFocus) { |