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