File indexing completed on 2025-01-05 03:56:44
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2006-04-19 0007 * Description : A tab to display general item information 0008 * 0009 * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * SPDX-FileCopyrightText: 2013 by Michael G. Hansen <mike at mghansen dot de> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #include "itempropertiestab_p.h" 0017 0018 namespace Digikam 0019 { 0020 0021 typedef QPair<QString, QVariant> PathValuePair; 0022 0023 static bool naturalLessThan(const PathValuePair& a, const PathValuePair& b) 0024 { 0025 return (QCollator().compare(a.first, b.first) < 0); 0026 } 0027 0028 QStringList ItemPropertiesTab::shortenedTagPaths(const QStringList& tagPaths, QList<QVariant>* identifiers) 0029 { 0030 QList<PathValuePair> tagsSorted; 0031 0032 if (identifiers) 0033 { 0034 for (int i = 0 ; i < tagPaths.size() ; ++i) 0035 { 0036 tagsSorted << PathValuePair(tagPaths.at(i), (*identifiers).at(i)); 0037 } 0038 } 0039 else 0040 { 0041 for (int i = 0 ; i < tagPaths.size() ; ++i) 0042 { 0043 tagsSorted << PathValuePair(tagPaths.at(i), QVariant()); 0044 } 0045 } 0046 0047 std::stable_sort(tagsSorted.begin(), tagsSorted.end(), naturalLessThan); 0048 0049 if (identifiers) 0050 { 0051 identifiers->clear(); 0052 } 0053 0054 QStringList tagsShortened; 0055 QString previous; 0056 0057 Q_FOREACH (const PathValuePair& pair, tagsSorted) 0058 { 0059 const QString& tagPath = pair.first; 0060 QString shortenedPath = tagPath; 0061 QStringList currentPath = tagPath.split(QLatin1Char('/'), QT_SKIP_EMPTY_PARTS); 0062 QStringList previousPath = previous.split(QLatin1Char('/'), QT_SKIP_EMPTY_PARTS); 0063 int depth; 0064 0065 for (depth = 0 ; (depth < currentPath.size()) && (depth < previousPath.size()) ; ++depth) 0066 { 0067 if (currentPath.at(depth) != previousPath.at(depth)) 0068 { 0069 break; 0070 } 0071 } 0072 0073 if (depth) 0074 { 0075 QString indent; 0076 indent.fill(QLatin1Char(' '), qMin(depth, 5)); 0077 /* 0078 indent += QChar(0x2026); 0079 */ 0080 shortenedPath = indent + tagPath.section(QLatin1Char('/'), depth); 0081 } 0082 0083 shortenedPath.replace(QLatin1Char('/'), QLatin1String(" / ")); 0084 tagsShortened << shortenedPath; 0085 previous = tagPath; 0086 0087 if (identifiers) 0088 { 0089 (*identifiers) << pair.second; 0090 } 0091 } 0092 0093 return tagsShortened; 0094 } 0095 0096 void ItemPropertiesTab::shortenedMakeInfo(QString& make) 0097 { 0098 make.remove(QLatin1String(" CORPORATION"), Qt::CaseInsensitive); // from Nikon, Pentax, and Olympus 0099 make.remove(QLatin1String("EASTMAN "), Qt::CaseInsensitive); // from Kodak 0100 make.remove(QLatin1String(" COMPANY"), Qt::CaseInsensitive); // from Kodak 0101 make.remove(QLatin1String(" OPTICAL CO.,LTD"), Qt::CaseInsensitive); // from Olympus 0102 make.remove(QLatin1String(" IMAGING CORP."), Qt::CaseInsensitive); // from Olympus 0103 make.remove(QLatin1String(" Techwin co.,Ltd."), Qt::CaseInsensitive); // from Samsung 0104 make.remove(QLatin1String(" Electric Co.,Ltd."), Qt::CaseInsensitive); // from Sanyo 0105 make.remove(QLatin1String(" Electric Co.,Ltd"), Qt::CaseInsensitive); // from Sanyo 0106 make.remove(QLatin1String(" COMPUTER CO.,LTD."), Qt::CaseInsensitive); // from Casio 0107 make.remove(QLatin1String(" COMPUTER CO.,LTD"), Qt::CaseInsensitive); // from Casio 0108 make.remove(QLatin1String(" Co., Ltd."), Qt::CaseInsensitive); // from Minolta 0109 make.remove(QLatin1String(" Co.,Ltd."), Qt::CaseInsensitive); // from Minolta 0110 make = make.trimmed(); 0111 } 0112 0113 void ItemPropertiesTab::shortenedModelInfo(QString& model) 0114 { 0115 model.remove(QLatin1String("Canon "), Qt::CaseInsensitive); 0116 model.remove(QLatin1String("NIKON "), Qt::CaseInsensitive); 0117 model.remove(QLatin1String("PENTAX "), Qt::CaseInsensitive); 0118 model.remove(QLatin1String(" DIGITAL"), Qt::CaseInsensitive); // from Canon 0119 model.remove(QLatin1String("KODAK "), Qt::CaseInsensitive); 0120 model.remove(QLatin1String(" CAMERA"), Qt::CaseInsensitive); // from Kodak 0121 model = model.trimmed(); 0122 } 0123 0124 /** 0125 * Find rational approximation to given real number 0126 * 0127 * val : double value to convert as humain readable fraction 0128 * num : fraction numerator 0129 * den : fraction denominator 0130 * maxden : the maximum denominator allowed 0131 * 0132 * This function return approximation error of the fraction 0133 * 0134 * Based on the theory of continued fractions 0135 * if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...))) 0136 * Then best approximation is found by truncating this series 0137 * wwith some adjustments in the last term. 0138 * 0139 * Note the fraction can be recovered as the first column of the matrix 0140 * ( a1 1 ) ( a2 1 ) ( a3 1 ) ... 0141 * ( 1 0 ) ( 1 0 ) ( 1 0 ) 0142 * Instead of keeping the sequence of continued fraction terms, 0143 * we just keep the last partial product of these matrices. 0144 * 0145 * Details: stackoverflow.com/questions/95727/how-to-convert-floats-to-human-readable-fractions 0146 * 0147 */ 0148 double ItemPropertiesTab::doubleToHumanReadableFraction(double val, long* num, long* den, long maxden) 0149 { 0150 double x = val; 0151 long m[2][2]; 0152 long ai; 0153 0154 // Initialize matrix 0155 0156 m[0][0] = m[1][1] = 1; 0157 m[0][1] = m[1][0] = 0; 0158 0159 // Loop finding terms until denominator gets too big 0160 0161 while (m[1][0] * (ai = (long)x) + m[1][1] <= maxden) 0162 { 0163 long t = m[0][0] * ai + m[0][1]; 0164 m[0][1] = m[0][0]; 0165 m[0][0] = t; 0166 t = m[1][0] * ai + m[1][1]; 0167 m[1][1] = m[1][0]; 0168 m[1][0] = t; 0169 0170 if (x == (double)ai) 0171 { 0172 break; // division by zero 0173 } 0174 0175 x = 1 / (x - (double)ai); 0176 0177 if (x > (double)0x7FFFFFFF) 0178 { 0179 break; // representation failure 0180 } 0181 } 0182 0183 // Now remaining x is between 0 and 1/ai 0184 // Approx as either 0 or 1/m where m is max that will fit in maxden 0185 0186 *num = m[0][0]; 0187 *den = m[1][0]; 0188 0189 // Return approximation error 0190 0191 return (val - ((double)m[0][0] / (double)m[1][0])); 0192 } 0193 0194 bool ItemPropertiesTab::aspectRatioToString(int width, int height, QString& arString) 0195 { 0196 if ((width == 0) || (height == 0)) 0197 { 0198 return false; 0199 } 0200 0201 double ratio = (double)qMax(width, height) / (double)qMin(width, height); 0202 long num = 0; 0203 long den = 0; 0204 0205 doubleToHumanReadableFraction(ratio, &num, &den, 10); 0206 0207 double aratio = (double)qMax(num, den) / (double)qMin(num, den); 0208 0209 arString = i18nc("@info: width : height (Aspect Ratio)", "%1:%2 (%3)", 0210 (width > height) ? num : den, 0211 (width > height) ? den : num, 0212 QLocale().toString(aratio, 'g', 2)); 0213 0214 return true; 0215 } 0216 0217 QString ItemPropertiesTab::permissionsString(const QFileInfo& fi) 0218 { 0219 QString str; 0220 QFile::Permissions perms = fi.permissions(); 0221 0222 str.append(fi.isSymLink() ? QLatin1String("l") : QLatin1String("-")); 0223 0224 str.append((perms & QFileDevice::ReadOwner) ? QLatin1String("r") : QLatin1String("-")); 0225 str.append((perms & QFileDevice::WriteOwner) ? QLatin1String("w") : QLatin1String("-")); 0226 str.append((perms & QFileDevice::ExeOwner) ? QLatin1String("x") : QLatin1String("-")); 0227 0228 str.append((perms & QFileDevice::ReadGroup) ? QLatin1String("r") : QLatin1String("-")); 0229 str.append((perms & QFileDevice::WriteGroup) ? QLatin1String("w") : QLatin1String("-")); 0230 str.append((perms & QFileDevice::ExeGroup) ? QLatin1String("x") : QLatin1String("-")); 0231 0232 str.append((perms & QFileDevice::ReadOther) ? QLatin1String("r") : QLatin1String("-")); 0233 str.append((perms & QFileDevice::WriteOther) ? QLatin1String("w") : QLatin1String("-")); 0234 str.append((perms & QFileDevice::ExeOther) ? QLatin1String("x") : QLatin1String("-")); 0235 0236 return str; 0237 } 0238 0239 QString ItemPropertiesTab::humanReadableBytesCount(qint64 bytes, bool si) 0240 { 0241 int unit = si ? 1000 : 1024; 0242 QString byteStr = i18nc("@info: unit file size in bytes", "B"); 0243 QString ret = QString::number(bytes); 0244 0245 if (bytes >= unit) 0246 { 0247 int exp = (int)(qLn(bytes) / qLn(unit)); 0248 QString pre = QString(si ? QLatin1String("kMGTPEZY") 0249 : QLatin1String("KMGTPEZY")).at(exp-1) + (si ? QLatin1String("") 0250 : QLatin1String("i")); 0251 ret = QString().asprintf("%.1f %s", bytes / qPow(unit, exp), pre.toUtf8().constData()); 0252 } 0253 0254 return (QString::fromUtf8("%1%2").arg(ret).arg(byteStr)); 0255 } 0256 0257 } // namespace Digikam