annotate widgets/TextAbbrev.cpp @ 1551:e79731086b0f

Fixes to NoteLayer, particularly to calculation of vertical scale when model unit is not Hz. To avoid inconsistency we now behave as if the unit is always Hz from the point of view of the external API and display, converting at the point where we obtain values from the events themselves. Also various fixes to editing.
author Chris Cannam
date Thu, 21 Nov 2019 14:02:57 +0000
parents d39db4673676
children
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@1478 41 // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
Chris@1478 42 // replacement (horizontalAdvance) was only added in Qt 5.11
Chris@1478 43 // which is too new for us
Chris@1478 44 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Chris@1478 45
Chris@376 46 int width = metrics.width(ellipsis);
Chris@376 47 return width * 2;
Chris@376 48 }
Chris@376 49
Chris@376 50 QString
Chris@376 51 TextAbbrev::abbreviateTo(QString text, int characters, Policy policy,
Chris@376 52 QString ellipsis)
Chris@376 53 {
Chris@376 54 switch (policy) {
Chris@376 55
Chris@376 56 case ElideEnd:
Chris@376 57 case ElideEndAndCommonPrefixes:
Chris@376 58 text = text.left(characters) + ellipsis;
Chris@376 59 break;
Chris@376 60
Chris@376 61 case ElideStart:
Chris@376 62 text = ellipsis + text.right(characters);
Chris@376 63 break;
Chris@376 64
Chris@376 65 case ElideMiddle:
Chris@376 66 if (characters > 2) {
Chris@376 67 text = text.left(characters/2 + 1) + ellipsis
Chris@376 68 + text.right(characters - (characters/2 + 1));
Chris@376 69 } else {
Chris@376 70 text = text.left(characters) + ellipsis;
Chris@376 71 }
Chris@376 72 break;
Chris@376 73 }
Chris@376 74
Chris@376 75 return text;
Chris@376 76 }
Chris@376 77
Chris@376 78 QString
Chris@376 79 TextAbbrev::abbreviate(QString text, int maxLength, Policy policy, bool fuzzy,
Chris@376 80 QString ellipsis)
Chris@376 81 {
Chris@376 82 if (ellipsis == "") ellipsis = getDefaultEllipsis();
Chris@376 83 int fl = (fuzzy ? getFuzzLength(ellipsis) : 0);
Chris@376 84 if (maxLength <= ellipsis.length()) maxLength = ellipsis.length() + 1;
Chris@376 85 if (text.length() <= maxLength + fl) return text;
Chris@376 86
Chris@376 87 int truncated = maxLength - ellipsis.length();
Chris@376 88 return abbreviateTo(text, truncated, policy, ellipsis);
Chris@376 89 }
Chris@376 90
Chris@376 91 QString
Chris@376 92 TextAbbrev::abbreviate(QString text,
Chris@376 93 const QFontMetrics &metrics, int &maxWidth,
Chris@376 94 Policy policy, QString ellipsis)
Chris@376 95 {
Chris@376 96 if (ellipsis == "") ellipsis = getDefaultEllipsis();
Chris@376 97
Chris@376 98 int tw = metrics.width(text);
Chris@376 99
Chris@376 100 if (tw <= maxWidth) {
Chris@376 101 maxWidth = tw;
Chris@376 102 return text;
Chris@376 103 }
Chris@376 104
Chris@376 105 int truncated = text.length();
Chris@376 106 QString original = text;
Chris@376 107
Chris@376 108 while (tw > maxWidth && truncated > 1) {
Chris@376 109
Chris@376 110 truncated--;
Chris@376 111
Chris@376 112 if (truncated > ellipsis.length()) {
Chris@376 113 text = abbreviateTo(original, truncated, policy, ellipsis);
Chris@376 114 } else {
Chris@376 115 break;
Chris@376 116 }
Chris@376 117
Chris@376 118 tw = metrics.width(text);
Chris@376 119 }
Chris@376 120
Chris@376 121 maxWidth = tw;
Chris@376 122 return text;
Chris@376 123 }
Chris@376 124
Chris@376 125 QStringList
Chris@376 126 TextAbbrev::abbreviate(const QStringList &texts, int maxLength,
Chris@376 127 Policy policy, bool fuzzy, QString ellipsis)
Chris@376 128 {
Chris@376 129 if (policy == ElideEndAndCommonPrefixes &&
Chris@376 130 texts.size() > 1) {
Chris@376 131
Chris@376 132 if (ellipsis == "") ellipsis = getDefaultEllipsis();
Chris@376 133 int fl = (fuzzy ? getFuzzLength(ellipsis) : 0);
Chris@376 134 if (maxLength <= ellipsis.length()) maxLength = ellipsis.length() + 1;
Chris@376 135
Chris@376 136 int maxOrigLength = 0;
Chris@376 137 for (int i = 0; i < texts.size(); ++i) {
Chris@376 138 int len = texts[i].length();
Chris@376 139 if (len > maxOrigLength) maxOrigLength = len;
Chris@376 140 }
Chris@376 141 if (maxOrigLength <= maxLength + fl) return texts;
Chris@376 142
Chris@376 143 return abbreviate(elidePrefixes
Chris@376 144 (texts, maxOrigLength - maxLength, ellipsis),
Chris@376 145 maxLength, ElideEnd, fuzzy, ellipsis);
Chris@376 146 }
Chris@376 147
Chris@376 148 QStringList results;
Chris@376 149 for (int i = 0; i < texts.size(); ++i) {
Chris@376 150 results.push_back
Chris@376 151 (abbreviate(texts[i], maxLength, policy, fuzzy, ellipsis));
Chris@376 152 }
Chris@376 153 return results;
Chris@376 154 }
Chris@376 155
Chris@376 156 QStringList
Chris@376 157 TextAbbrev::abbreviate(const QStringList &texts, const QFontMetrics &metrics,
Chris@376 158 int &maxWidth, Policy policy, QString ellipsis)
Chris@376 159 {
Chris@376 160 if (policy == ElideEndAndCommonPrefixes &&
Chris@376 161 texts.size() > 1) {
Chris@376 162
Chris@376 163 if (ellipsis == "") ellipsis = getDefaultEllipsis();
Chris@376 164
Chris@376 165 int maxOrigWidth = 0;
Chris@376 166 for (int i = 0; i < texts.size(); ++i) {
Chris@376 167 int w = metrics.width(texts[i]);
Chris@376 168 if (w > maxOrigWidth) maxOrigWidth = w;
Chris@376 169 }
Chris@376 170
Chris@376 171 return abbreviate(elidePrefixes(texts, metrics,
Chris@376 172 maxOrigWidth - maxWidth, ellipsis),
Chris@376 173 metrics, maxWidth, ElideEnd, ellipsis);
Chris@376 174 }
Chris@376 175
Chris@376 176 QStringList results;
Chris@376 177 int maxAbbrWidth = 0;
Chris@376 178 for (int i = 0; i < texts.size(); ++i) {
Chris@376 179 int width = maxWidth;
Chris@376 180 QString abbr = abbreviate(texts[i], metrics, width, policy, ellipsis);
Chris@376 181 if (width > maxAbbrWidth) maxAbbrWidth = width;
Chris@376 182 results.push_back(abbr);
Chris@376 183 }
Chris@376 184 maxWidth = maxAbbrWidth;
Chris@376 185 return results;
Chris@376 186 }
Chris@376 187
Chris@376 188 QStringList
Chris@376 189 TextAbbrev::elidePrefixes(const QStringList &texts,
Chris@376 190 int targetReduction,
Chris@376 191 QString ellipsis)
Chris@376 192 {
Chris@376 193 if (texts.empty()) return texts;
Chris@376 194 int plen = getPrefixLength(texts);
Chris@376 195 int fl = getFuzzLength(ellipsis);
Chris@376 196 if (plen < fl) return texts;
Chris@376 197
Chris@376 198 QString prefix = texts[0].left(plen);
Chris@376 199 int truncated = plen;
Chris@376 200 if (plen >= targetReduction + fl) {
Chris@376 201 truncated = plen - targetReduction;
Chris@376 202 } else {
Chris@376 203 truncated = fl;
Chris@376 204 }
Chris@376 205 prefix = abbreviate(prefix, truncated, ElideEnd, false, ellipsis);
Chris@376 206
Chris@376 207 QStringList results;
Chris@376 208 for (int i = 0; i < texts.size(); ++i) {
Chris@376 209 results.push_back
Chris@376 210 (prefix + texts[i].right(texts[i].length() - plen));
Chris@376 211 }
Chris@376 212 return results;
Chris@376 213 }
Chris@376 214
Chris@376 215 QStringList
Chris@376 216 TextAbbrev::elidePrefixes(const QStringList &texts,
Chris@376 217 const QFontMetrics &metrics,
Chris@376 218 int targetWidthReduction,
Chris@376 219 QString ellipsis)
Chris@376 220 {
Chris@376 221 if (texts.empty()) return texts;
Chris@376 222 int plen = getPrefixLength(texts);
Chris@376 223 int fl = getFuzzLength(ellipsis);
Chris@376 224 if (plen < fl) return texts;
Chris@376 225
Chris@376 226 QString prefix = texts[0].left(plen);
Chris@376 227 int pwid = metrics.width(prefix);
Chris@376 228 int twid = pwid - targetWidthReduction;
Chris@376 229 if (twid < metrics.width(ellipsis) * 2) twid = metrics.width(ellipsis) * 2;
Chris@376 230 prefix = abbreviate(prefix, metrics, twid, ElideEnd, ellipsis);
Chris@376 231
Chris@376 232 QStringList results;
Chris@376 233 for (int i = 0; i < texts.size(); ++i) {
Chris@376 234 results.push_back
Chris@376 235 (prefix + texts[i].right(texts[i].length() - plen));
Chris@376 236 }
Chris@376 237 return results;
Chris@376 238 }
Chris@376 239
Chris@376 240 static bool
Chris@376 241 havePrefix(QString prefix, const QStringList &texts)
Chris@376 242 {
Chris@376 243 for (int i = 1; i < texts.size(); ++i) {
Chris@376 244 if (!texts[i].startsWith(prefix)) return false;
Chris@376 245 }
Chris@376 246 return true;
Chris@376 247 }
Chris@376 248
Chris@376 249 int
Chris@376 250 TextAbbrev::getPrefixLength(const QStringList &texts)
Chris@376 251 {
Chris@376 252 QString reference = texts[0];
Chris@376 253
Chris@376 254 if (reference == "" || havePrefix(reference, texts)) {
Chris@376 255 return reference.length();
Chris@376 256 }
Chris@376 257
Chris@376 258 int candidate = reference.length();
Chris@376 259 QString splitChars(";:,./#-!()$_+=[]{}\\");
Chris@376 260
Chris@376 261 while (--candidate > 1) {
Chris@376 262 if (splitChars.contains(reference[candidate])) {
Chris@376 263 if (havePrefix(reference.left(candidate), texts)) {
Chris@376 264 break;
Chris@376 265 }
Chris@376 266 }
Chris@376 267 }
Chris@376 268
Chris@587 269 // SVDEBUG << "TextAbbrev::getPrefixLength: prefix length is " << candidate << endl;
Chris@376 270 // for (int i = 0; i < texts.size(); ++i) {
Chris@682 271 // cerr << texts[i].left(candidate) << "|" << texts[i].right(texts[i].length() - candidate) << endl;
Chris@376 272 // }
Chris@376 273
Chris@376 274 return candidate;
Chris@376 275 }
Chris@376 276