annotate textabbrev.cpp @ 145:644bd31e8301

* Include the uncommitted item in general graph layout (in case it is not at the head, when other items will need to avoid it)
author Chris Cannam
date Wed, 01 Dec 2010 17:41:14 +0000
parents f583e44d9d31
children 8fd71f570884
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@57 8 Copyright (c) 2010 Chris Cannam
Chris@57 9 Copyright (c) 2010 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