annotate base/TextAbbrev.cpp @ 316:3a6725f285d6

* Make RemoteFile far more pervasive, and use it for local files as well so that we can handle both transparently. Make it shallow copy with reference counting, so it can be used by value without having to worry about the cache file lifetime. Use RemoteFile for MainWindow file-open functions, etc
author Chris Cannam
date Thu, 18 Oct 2007 15:31:20 +0000
parents 2c1e57ad86e7
children
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@294 21 #include <iostream>
Chris@294 22
Chris@286 23 QString
Chris@286 24 TextAbbrev::getDefaultEllipsis()
Chris@286 25 {
Chris@286 26 return "...";
Chris@286 27 }
Chris@286 28
Chris@286 29 int
Chris@286 30 TextAbbrev::getFuzzLength(QString ellipsis)
Chris@286 31 {
Chris@286 32 int len = ellipsis.length();
Chris@286 33 if (len < 3) return len + 3;
Chris@286 34 else if (len > 5) return len + 5;
Chris@286 35 else return len * 2;
Chris@286 36 }
Chris@286 37
Chris@286 38 int
Chris@286 39 TextAbbrev::getFuzzWidth(const QFontMetrics &metrics, QString ellipsis)
Chris@286 40 {
Chris@286 41 int width = metrics.width(ellipsis);
Chris@286 42 return width * 2;
Chris@286 43 }
Chris@286 44
Chris@286 45 QString
Chris@286 46 TextAbbrev::abbreviateTo(QString text, int characters, Policy policy,
Chris@286 47 QString ellipsis)
Chris@286 48 {
Chris@286 49 switch (policy) {
Chris@286 50
Chris@286 51 case ElideEnd:
Chris@286 52 case ElideEndAndCommonPrefixes:
Chris@286 53 text = text.left(characters) + ellipsis;
Chris@286 54 break;
Chris@286 55
Chris@286 56 case ElideStart:
Chris@286 57 text = ellipsis + text.right(characters);
Chris@286 58 break;
Chris@286 59
Chris@286 60 case ElideMiddle:
Chris@286 61 if (characters > 2) {
Chris@286 62 text = text.left(characters/2 + 1) + ellipsis
Chris@286 63 + text.right(characters - (characters/2 + 1));
Chris@286 64 } else {
Chris@286 65 text = text.left(characters) + ellipsis;
Chris@286 66 }
Chris@286 67 break;
Chris@286 68 }
Chris@286 69
Chris@286 70 return text;
Chris@286 71 }
Chris@286 72
Chris@286 73 QString
Chris@286 74 TextAbbrev::abbreviate(QString text, int maxLength, Policy policy, bool fuzzy,
Chris@286 75 QString ellipsis)
Chris@286 76 {
Chris@286 77 if (ellipsis == "") ellipsis = getDefaultEllipsis();
Chris@286 78 int fl = (fuzzy ? getFuzzLength(ellipsis) : 0);
Chris@286 79 if (maxLength <= ellipsis.length()) maxLength = ellipsis.length() + 1;
Chris@286 80 if (text.length() <= maxLength + fl) return text;
Chris@286 81
Chris@286 82 int truncated = maxLength - ellipsis.length();
Chris@286 83 return abbreviateTo(text, truncated, policy, ellipsis);
Chris@286 84 }
Chris@286 85
Chris@286 86 QString
Chris@286 87 TextAbbrev::abbreviate(QString text,
Chris@286 88 const QFontMetrics &metrics, int &maxWidth,
Chris@286 89 Policy policy, QString ellipsis)
Chris@286 90 {
Chris@286 91 if (ellipsis == "") ellipsis = getDefaultEllipsis();
Chris@286 92
Chris@286 93 int tw = metrics.width(text);
Chris@286 94
Chris@286 95 if (tw <= maxWidth) {
Chris@286 96 maxWidth = tw;
Chris@286 97 return text;
Chris@286 98 }
Chris@286 99
Chris@286 100 int truncated = text.length();
Chris@286 101 QString original = text;
Chris@286 102
Chris@286 103 while (tw > maxWidth && truncated > 1) {
Chris@286 104
Chris@286 105 truncated--;
Chris@286 106
Chris@286 107 if (truncated > ellipsis.length()) {
Chris@286 108 text = abbreviateTo(original, truncated, policy, ellipsis);
Chris@286 109 } else {
Chris@286 110 break;
Chris@286 111 }
Chris@286 112
Chris@286 113 tw = metrics.width(text);
Chris@286 114 }
Chris@286 115
Chris@286 116 maxWidth = tw;
Chris@286 117 return text;
Chris@286 118 }
Chris@286 119
Chris@286 120 QStringList
Chris@286 121 TextAbbrev::abbreviate(const QStringList &texts, int maxLength,
Chris@286 122 Policy policy, bool fuzzy, QString ellipsis)
Chris@286 123 {
Chris@286 124 if (policy == ElideEndAndCommonPrefixes &&
Chris@286 125 texts.size() > 1) {
Chris@286 126
Chris@286 127 if (ellipsis == "") ellipsis = getDefaultEllipsis();
Chris@286 128 int fl = (fuzzy ? getFuzzLength(ellipsis) : 0);
Chris@286 129 if (maxLength <= ellipsis.length()) maxLength = ellipsis.length() + 1;
Chris@286 130
Chris@286 131 int maxOrigLength = 0;
Chris@286 132 for (int i = 0; i < texts.size(); ++i) {
Chris@286 133 int len = texts[i].length();
Chris@286 134 if (len > maxOrigLength) maxOrigLength = len;
Chris@286 135 }
Chris@286 136 if (maxOrigLength <= maxLength + fl) return texts;
Chris@286 137
Chris@286 138 return abbreviate(elidePrefixes
Chris@286 139 (texts, maxOrigLength - maxLength, ellipsis),
Chris@286 140 maxLength, ElideEnd, fuzzy, ellipsis);
Chris@286 141 }
Chris@286 142
Chris@286 143 QStringList results;
Chris@286 144 for (int i = 0; i < texts.size(); ++i) {
Chris@286 145 results.push_back
Chris@286 146 (abbreviate(texts[i], maxLength, policy, fuzzy, ellipsis));
Chris@286 147 }
Chris@286 148 return results;
Chris@286 149 }
Chris@286 150
Chris@286 151 QStringList
Chris@286 152 TextAbbrev::abbreviate(const QStringList &texts, const QFontMetrics &metrics,
Chris@286 153 int &maxWidth, Policy policy, QString ellipsis)
Chris@286 154 {
Chris@286 155 if (policy == ElideEndAndCommonPrefixes &&
Chris@286 156 texts.size() > 1) {
Chris@286 157
Chris@286 158 if (ellipsis == "") ellipsis = getDefaultEllipsis();
Chris@286 159
Chris@286 160 int maxOrigWidth = 0;
Chris@286 161 for (int i = 0; i < texts.size(); ++i) {
Chris@286 162 int w = metrics.width(texts[i]);
Chris@286 163 if (w > maxOrigWidth) maxOrigWidth = w;
Chris@286 164 }
Chris@286 165
Chris@286 166 return abbreviate(elidePrefixes(texts, metrics,
Chris@286 167 maxOrigWidth - maxWidth, ellipsis),
Chris@286 168 metrics, maxWidth, ElideEnd, ellipsis);
Chris@286 169 }
Chris@286 170
Chris@286 171 QStringList results;
Chris@286 172 int maxAbbrWidth = 0;
Chris@286 173 for (int i = 0; i < texts.size(); ++i) {
Chris@286 174 int width = maxWidth;
Chris@286 175 QString abbr = abbreviate(texts[i], metrics, width, policy, ellipsis);
Chris@286 176 if (width > maxAbbrWidth) maxAbbrWidth = width;
Chris@286 177 results.push_back(abbr);
Chris@286 178 }
Chris@286 179 maxWidth = maxAbbrWidth;
Chris@286 180 return results;
Chris@286 181 }
Chris@286 182
Chris@286 183 QStringList
Chris@286 184 TextAbbrev::elidePrefixes(const QStringList &texts,
Chris@286 185 int targetReduction,
Chris@286 186 QString ellipsis)
Chris@286 187 {
Chris@286 188 if (texts.empty()) return texts;
Chris@286 189 int plen = getPrefixLength(texts);
Chris@286 190 int fl = getFuzzLength(ellipsis);
Chris@286 191 if (plen < fl) return texts;
Chris@286 192
Chris@286 193 QString prefix = texts[0].left(plen);
Chris@286 194 int truncated = plen;
Chris@286 195 if (plen >= targetReduction + fl) {
Chris@286 196 truncated = plen - targetReduction;
Chris@286 197 } else {
Chris@286 198 truncated = fl;
Chris@286 199 }
Chris@286 200 prefix = abbreviate(prefix, truncated, ElideEnd, false, ellipsis);
Chris@286 201
Chris@286 202 QStringList results;
Chris@286 203 for (int i = 0; i < texts.size(); ++i) {
Chris@286 204 results.push_back
Chris@286 205 (prefix + texts[i].right(texts[i].length() - plen));
Chris@286 206 }
Chris@286 207 return results;
Chris@286 208 }
Chris@286 209
Chris@286 210 QStringList
Chris@286 211 TextAbbrev::elidePrefixes(const QStringList &texts,
Chris@286 212 const QFontMetrics &metrics,
Chris@286 213 int targetWidthReduction,
Chris@286 214 QString ellipsis)
Chris@286 215 {
Chris@286 216 if (texts.empty()) return texts;
Chris@286 217 int plen = getPrefixLength(texts);
Chris@286 218 int fl = getFuzzLength(ellipsis);
Chris@286 219 if (plen < fl) return texts;
Chris@286 220
Chris@286 221 QString prefix = texts[0].left(plen);
Chris@286 222 int pwid = metrics.width(prefix);
Chris@286 223 int twid = pwid - targetWidthReduction;
Chris@286 224 if (twid < metrics.width(ellipsis) * 2) twid = metrics.width(ellipsis) * 2;
Chris@286 225 prefix = abbreviate(prefix, metrics, twid, ElideEnd, ellipsis);
Chris@286 226
Chris@286 227 QStringList results;
Chris@286 228 for (int i = 0; i < texts.size(); ++i) {
Chris@286 229 results.push_back
Chris@286 230 (prefix + texts[i].right(texts[i].length() - plen));
Chris@286 231 }
Chris@286 232 return results;
Chris@286 233 }
Chris@286 234
Chris@286 235 static bool
Chris@286 236 havePrefix(QString prefix, const QStringList &texts)
Chris@286 237 {
Chris@286 238 for (int i = 1; i < texts.size(); ++i) {
Chris@286 239 if (!texts[i].startsWith(prefix)) return false;
Chris@286 240 }
Chris@286 241 return true;
Chris@286 242 }
Chris@286 243
Chris@286 244 int
Chris@286 245 TextAbbrev::getPrefixLength(const QStringList &texts)
Chris@286 246 {
Chris@286 247 QString reference = texts[0];
Chris@286 248
Chris@286 249 if (reference == "" || havePrefix(reference, texts)) {
Chris@286 250 return reference.length();
Chris@286 251 }
Chris@286 252
Chris@286 253 int candidate = reference.length();
Chris@286 254 QString splitChars(";:,./#-!()$_+=[]{}\\");
Chris@286 255
Chris@286 256 while (--candidate > 1) {
Chris@286 257 if (splitChars.contains(reference[candidate])) {
Chris@294 258 if (havePrefix(reference.left(candidate), texts)) {
Chris@286 259 break;
Chris@286 260 }
Chris@286 261 }
Chris@286 262 }
Chris@286 263
Chris@294 264 // std::cerr << "TextAbbrev::getPrefixLength: prefix length is " << candidate << std::endl;
Chris@294 265 // for (int i = 0; i < texts.size(); ++i) {
Chris@294 266 // std::cerr << texts[i].left(candidate).toStdString() << "|" << texts[i].right(texts[i].length() - candidate).toStdString() << std::endl;
Chris@294 267 // }
Chris@294 268
Chris@286 269 return candidate;
Chris@286 270 }
Chris@286 271