comparison src/textabbrev.cpp @ 370:b9c153e00e84

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