annotate base/TextAbbrev.cpp @ 340:516819f2b97b

* Add Erase tool and mode * Add icons for Normalize buttons in property boxes, and for Show Peaks * Add support for velocity in notes -- not yet reflected in display or editable in the note edit dialog, but they are imported from MIDI, played, and exported * Begin work on making pastes align pasted times (subtler than I thought)
author Chris Cannam
date Fri, 23 Nov 2007 16:48:23 +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