annotate base/TextAbbrev.cpp @ 287:557e00480279

* Fix #1755108 end of file not clearly shown and #1754808 crash when trying to retrieve remote file from absent server
author Chris Cannam
date Mon, 13 Aug 2007 14:51:35 +0000
parents 20097c32d15d
children 2c1e57ad86e7
rev   line source
Chris@286 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@286 2
Chris@286 3 /*
Chris@286 4 Sonic Visualiser
Chris@286 5 An audio file viewer and annotation editor.
Chris@286 6 Centre for Digital Music, Queen Mary, University of London.
Chris@286 7 This file copyright 2006-2007 Chris Cannam and QMUL.
Chris@286 8
Chris@286 9 This program is free software; you can redistribute it and/or
Chris@286 10 modify it under the terms of the GNU General Public License as
Chris@286 11 published by the Free Software Foundation; either version 2 of the
Chris@286 12 License, or (at your option) any later version. See the file
Chris@286 13 COPYING included with this distribution for more information.
Chris@286 14 */
Chris@286 15
Chris@286 16 #include "TextAbbrev.h"
Chris@286 17
Chris@286 18 #include <QFontMetrics>
Chris@286 19 #include <QApplication>
Chris@286 20
Chris@286 21 QString
Chris@286 22 TextAbbrev::getDefaultEllipsis()
Chris@286 23 {
Chris@286 24 return "...";
Chris@286 25 }
Chris@286 26
Chris@286 27 int
Chris@286 28 TextAbbrev::getFuzzLength(QString ellipsis)
Chris@286 29 {
Chris@286 30 int len = ellipsis.length();
Chris@286 31 if (len < 3) return len + 3;
Chris@286 32 else if (len > 5) return len + 5;
Chris@286 33 else return len * 2;
Chris@286 34 }
Chris@286 35
Chris@286 36 int
Chris@286 37 TextAbbrev::getFuzzWidth(const QFontMetrics &metrics, QString ellipsis)
Chris@286 38 {
Chris@286 39 int width = metrics.width(ellipsis);
Chris@286 40 return width * 2;
Chris@286 41 }
Chris@286 42
Chris@286 43 QString
Chris@286 44 TextAbbrev::abbreviateTo(QString text, int characters, Policy policy,
Chris@286 45 QString ellipsis)
Chris@286 46 {
Chris@286 47 switch (policy) {
Chris@286 48
Chris@286 49 case ElideEnd:
Chris@286 50 case ElideEndAndCommonPrefixes:
Chris@286 51 text = text.left(characters) + ellipsis;
Chris@286 52 break;
Chris@286 53
Chris@286 54 case ElideStart:
Chris@286 55 text = ellipsis + text.right(characters);
Chris@286 56 break;
Chris@286 57
Chris@286 58 case ElideMiddle:
Chris@286 59 if (characters > 2) {
Chris@286 60 text = text.left(characters/2 + 1) + ellipsis
Chris@286 61 + text.right(characters - (characters/2 + 1));
Chris@286 62 } else {
Chris@286 63 text = text.left(characters) + ellipsis;
Chris@286 64 }
Chris@286 65 break;
Chris@286 66 }
Chris@286 67
Chris@286 68 return text;
Chris@286 69 }
Chris@286 70
Chris@286 71 QString
Chris@286 72 TextAbbrev::abbreviate(QString text, int maxLength, Policy policy, bool fuzzy,
Chris@286 73 QString ellipsis)
Chris@286 74 {
Chris@286 75 if (ellipsis == "") ellipsis = getDefaultEllipsis();
Chris@286 76 int fl = (fuzzy ? getFuzzLength(ellipsis) : 0);
Chris@286 77 if (maxLength <= ellipsis.length()) maxLength = ellipsis.length() + 1;
Chris@286 78 if (text.length() <= maxLength + fl) return text;
Chris@286 79
Chris@286 80 int truncated = maxLength - ellipsis.length();
Chris@286 81 return abbreviateTo(text, truncated, policy, ellipsis);
Chris@286 82 }
Chris@286 83
Chris@286 84 QString
Chris@286 85 TextAbbrev::abbreviate(QString text,
Chris@286 86 const QFontMetrics &metrics, int &maxWidth,
Chris@286 87 Policy policy, QString ellipsis)
Chris@286 88 {
Chris@286 89 if (ellipsis == "") ellipsis = getDefaultEllipsis();
Chris@286 90
Chris@286 91 int tw = metrics.width(text);
Chris@286 92
Chris@286 93 if (tw <= maxWidth) {
Chris@286 94 maxWidth = tw;
Chris@286 95 return text;
Chris@286 96 }
Chris@286 97
Chris@286 98 int truncated = text.length();
Chris@286 99 QString original = text;
Chris@286 100
Chris@286 101 while (tw > maxWidth && truncated > 1) {
Chris@286 102
Chris@286 103 truncated--;
Chris@286 104
Chris@286 105 if (truncated > ellipsis.length()) {
Chris@286 106 text = abbreviateTo(original, truncated, policy, ellipsis);
Chris@286 107 } else {
Chris@286 108 break;
Chris@286 109 }
Chris@286 110
Chris@286 111 tw = metrics.width(text);
Chris@286 112 }
Chris@286 113
Chris@286 114 maxWidth = tw;
Chris@286 115 return text;
Chris@286 116 }
Chris@286 117
Chris@286 118 QStringList
Chris@286 119 TextAbbrev::abbreviate(const QStringList &texts, int maxLength,
Chris@286 120 Policy policy, bool fuzzy, QString ellipsis)
Chris@286 121 {
Chris@286 122 if (policy == ElideEndAndCommonPrefixes &&
Chris@286 123 texts.size() > 1) {
Chris@286 124
Chris@286 125 if (ellipsis == "") ellipsis = getDefaultEllipsis();
Chris@286 126 int fl = (fuzzy ? getFuzzLength(ellipsis) : 0);
Chris@286 127 if (maxLength <= ellipsis.length()) maxLength = ellipsis.length() + 1;
Chris@286 128
Chris@286 129 int maxOrigLength = 0;
Chris@286 130 for (int i = 0; i < texts.size(); ++i) {
Chris@286 131 int len = texts[i].length();
Chris@286 132 if (len > maxOrigLength) maxOrigLength = len;
Chris@286 133 }
Chris@286 134 if (maxOrigLength <= maxLength + fl) return texts;
Chris@286 135
Chris@286 136 return abbreviate(elidePrefixes
Chris@286 137 (texts, maxOrigLength - maxLength, ellipsis),
Chris@286 138 maxLength, ElideEnd, fuzzy, ellipsis);
Chris@286 139 }
Chris@286 140
Chris@286 141 QStringList results;
Chris@286 142 for (int i = 0; i < texts.size(); ++i) {
Chris@286 143 results.push_back
Chris@286 144 (abbreviate(texts[i], maxLength, policy, fuzzy, ellipsis));
Chris@286 145 }
Chris@286 146 return results;
Chris@286 147 }
Chris@286 148
Chris@286 149 QStringList
Chris@286 150 TextAbbrev::abbreviate(const QStringList &texts, const QFontMetrics &metrics,
Chris@286 151 int &maxWidth, Policy policy, QString ellipsis)
Chris@286 152 {
Chris@286 153 if (policy == ElideEndAndCommonPrefixes &&
Chris@286 154 texts.size() > 1) {
Chris@286 155
Chris@286 156 if (ellipsis == "") ellipsis = getDefaultEllipsis();
Chris@286 157
Chris@286 158 int maxOrigWidth = 0;
Chris@286 159 for (int i = 0; i < texts.size(); ++i) {
Chris@286 160 int w = metrics.width(texts[i]);
Chris@286 161 if (w > maxOrigWidth) maxOrigWidth = w;
Chris@286 162 }
Chris@286 163
Chris@286 164 return abbreviate(elidePrefixes(texts, metrics,
Chris@286 165 maxOrigWidth - maxWidth, ellipsis),
Chris@286 166 metrics, maxWidth, ElideEnd, ellipsis);
Chris@286 167 }
Chris@286 168
Chris@286 169 QStringList results;
Chris@286 170 int maxAbbrWidth = 0;
Chris@286 171 for (int i = 0; i < texts.size(); ++i) {
Chris@286 172 int width = maxWidth;
Chris@286 173 QString abbr = abbreviate(texts[i], metrics, width, policy, ellipsis);
Chris@286 174 if (width > maxAbbrWidth) maxAbbrWidth = width;
Chris@286 175 results.push_back(abbr);
Chris@286 176 }
Chris@286 177 maxWidth = maxAbbrWidth;
Chris@286 178 return results;
Chris@286 179 }
Chris@286 180
Chris@286 181 QStringList
Chris@286 182 TextAbbrev::elidePrefixes(const QStringList &texts,
Chris@286 183 int targetReduction,
Chris@286 184 QString ellipsis)
Chris@286 185 {
Chris@286 186 if (texts.empty()) return texts;
Chris@286 187 int plen = getPrefixLength(texts);
Chris@286 188 int fl = getFuzzLength(ellipsis);
Chris@286 189 if (plen < fl) return texts;
Chris@286 190
Chris@286 191 QString prefix = texts[0].left(plen);
Chris@286 192 int truncated = plen;
Chris@286 193 if (plen >= targetReduction + fl) {
Chris@286 194 truncated = plen - targetReduction;
Chris@286 195 } else {
Chris@286 196 truncated = fl;
Chris@286 197 }
Chris@286 198 prefix = abbreviate(prefix, truncated, ElideEnd, false, ellipsis);
Chris@286 199
Chris@286 200 QStringList results;
Chris@286 201 for (int i = 0; i < texts.size(); ++i) {
Chris@286 202 results.push_back
Chris@286 203 (prefix + texts[i].right(texts[i].length() - plen));
Chris@286 204 }
Chris@286 205 return results;
Chris@286 206 }
Chris@286 207
Chris@286 208 QStringList
Chris@286 209 TextAbbrev::elidePrefixes(const QStringList &texts,
Chris@286 210 const QFontMetrics &metrics,
Chris@286 211 int targetWidthReduction,
Chris@286 212 QString ellipsis)
Chris@286 213 {
Chris@286 214 if (texts.empty()) return texts;
Chris@286 215 int plen = getPrefixLength(texts);
Chris@286 216 int fl = getFuzzLength(ellipsis);
Chris@286 217 if (plen < fl) return texts;
Chris@286 218
Chris@286 219 QString prefix = texts[0].left(plen);
Chris@286 220 int pwid = metrics.width(prefix);
Chris@286 221 int twid = pwid - targetWidthReduction;
Chris@286 222 if (twid < metrics.width(ellipsis) * 2) twid = metrics.width(ellipsis) * 2;
Chris@286 223 prefix = abbreviate(prefix, metrics, twid, ElideEnd, ellipsis);
Chris@286 224
Chris@286 225 QStringList results;
Chris@286 226 for (int i = 0; i < texts.size(); ++i) {
Chris@286 227 results.push_back
Chris@286 228 (prefix + texts[i].right(texts[i].length() - plen));
Chris@286 229 }
Chris@286 230 return results;
Chris@286 231 }
Chris@286 232
Chris@286 233 static bool
Chris@286 234 havePrefix(QString prefix, const QStringList &texts)
Chris@286 235 {
Chris@286 236 for (int i = 1; i < texts.size(); ++i) {
Chris@286 237 if (!texts[i].startsWith(prefix)) return false;
Chris@286 238 }
Chris@286 239 return true;
Chris@286 240 }
Chris@286 241
Chris@286 242 int
Chris@286 243 TextAbbrev::getPrefixLength(const QStringList &texts)
Chris@286 244 {
Chris@286 245 QString reference = texts[0];
Chris@286 246
Chris@286 247 if (reference == "" || havePrefix(reference, texts)) {
Chris@286 248 return reference.length();
Chris@286 249 }
Chris@286 250
Chris@286 251 int candidate = reference.length();
Chris@286 252 QString splitChars(";:,./#-!()$_+=[]{}\\");
Chris@286 253
Chris@286 254 while (--candidate > 1) {
Chris@286 255 if (splitChars.contains(reference[candidate])) {
Chris@286 256 if (havePrefix(reference.left(candidate - 1), texts)) {
Chris@286 257 break;
Chris@286 258 }
Chris@286 259 }
Chris@286 260 }
Chris@286 261
Chris@286 262 return candidate;
Chris@286 263 }
Chris@286 264