annotate widgets/TextAbbrev.cpp @ 522:0db823c49ed6

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