annotate textabbrev.cpp @ 53:3c46b2ac45d3

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