annotate src/textabbrev.cpp @ 400:07eaf4e6003a

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