File indexing completed on 2024-12-22 04:17:34
0001 /*************************************************************************** 0002 labelrenderer.cpp 0003 ------------------ 0004 begin : Jun 17 2005 0005 copyright : (C) 2005 by The University of Toronto 0006 email : netterfield@astro.utoronto.ca 0007 ***************************************************************************/ 0008 0009 /*************************************************************************** 0010 * * 0011 * This program is free software; you can redistribute it and/or modify * 0012 * it under the terms of the GNU General Public License as published by * 0013 * the Free Software Foundation; either version 2 of the License, or * 0014 * (at your option) any later version. * 0015 * * 0016 ***************************************************************************/ 0017 0018 #include "labelrenderer.h" 0019 0020 #include "enodes.h" 0021 #include "datacollection.h" 0022 #include "ksttimers.h" 0023 #include "labelparser.h" 0024 #include "document.h" 0025 #include "objectstore.h" 0026 #include "application.h" 0027 #include "applicationsettings.h" 0028 0029 #include <QDebug> 0030 #include <cstdio> 0031 #include <time.h> 0032 0033 const double subscript_scale = 0.60; 0034 const double subscript_drop = 0.16; 0035 const double superscript_scale = 0.60; 0036 const double superscript_raise = 0.44; 0037 0038 namespace Label { 0039 0040 static QString FormattedNumber(double val, QString format_in) { 0041 const int strlen = 100; 0042 char cstring[strlen]; 0043 0044 QByteArray format = format_in.toLatin1(); 0045 0046 if (format_in.startsWith('T')) { 0047 time_t time = val; 0048 struct tm timestruct; 0049 gmtime_r(&time, ×truct); 0050 strftime(cstring, strlen, format.data()+1, ×truct); 0051 } else { 0052 snprintf(cstring, strlen, format.data(), val); // not using QString::asprintf() because it isn't in qt4. 0053 } 0054 return QString(cstring); 0055 } 0056 0057 void renderLabel(RenderContext& rc, Label::Chunk *fi, bool cache, bool draw) { 0058 int oldSize = rc.size = rc.fontSize(); 0059 int oldY = rc.y; 0060 int oldX = rc.x; 0061 bool boldFont = rc.font().bold(); 0062 bool italicFont = rc.font().italic(); 0063 0064 QColor default_color = fi->attributes.color; 0065 0066 Kst::Document *doc = kstApp->mainWindow()->document(); 0067 Q_ASSERT(doc); 0068 Kst::ObjectStore *store = doc->objectStore(); 0069 Q_ASSERT(store); 0070 0071 while (fi) { 0072 if (fi->vOffset != Label::Chunk::None) { 0073 if (fi->vOffset == Label::Chunk::Up) { 0074 rc.size = int(double(rc.size)*superscript_scale); 0075 rc.y -= int(superscript_raise * rc.fontHeight()); 0076 } else { // Down 0077 rc.size = int(double(rc.size)*subscript_scale); 0078 rc.y += int(subscript_drop * rc.fontHeight()); 0079 } 0080 if (rc.size<Kst::ApplicationSettings::self()->minimumFontSize()) { 0081 rc.size = Kst::ApplicationSettings::self()->minimumFontSize(); 0082 } 0083 } 0084 QFont f = rc.font(); 0085 if (rc.fontSize() != rc.size) { 0086 f.setPointSizeF(rc.size); 0087 } 0088 0089 f.setBold(fi->attributes.bold || boldFont); 0090 f.setItalic(fi->attributes.italic || italicFont); 0091 f.setUnderline(fi->attributes.underline); 0092 f.setOverline(fi->attributes.overline); 0093 0094 QPen pen = rc.pen; 0095 if (fi->attributes.color.isValid()) { 0096 pen.setColor(fi->attributes.color); 0097 } else if (default_color.isValid()) { 0098 pen.setColor(default_color); 0099 } 0100 if (draw && rc.p) { 0101 rc.p->setPen(pen); 0102 } 0103 0104 rc.setFont(f); 0105 0106 if (fi->linebreak) { 0107 rc.x = oldX; 0108 rc.y += rc.fontAscent() + rc.fontDescent() + 1; 0109 fi = fi->next; 0110 rc.lines++; 0111 continue; 0112 } 0113 0114 if (/*!rc.substitute*/ 0 && (fi->scalar || fi->vector)) { 0115 // FIXME: dead code, I think... 0116 QString txt = QString('[') + fi->text + ']'; 0117 if (draw && rc.p) { 0118 rc.p->drawText(rc.x, rc.y, txt); 0119 } 0120 if (cache) { 0121 rc.addToCache(QPointF(rc.x, rc.y), txt, f, pen); 0122 } 0123 rc.x += rc.fontWidth(txt); 0124 } else if (fi->scalar) { 0125 // do scalar/string/fit substitution 0126 QString txt; 0127 if (!fi->text.isEmpty() && fi->text[0] == '=') { 0128 // Parse and evaluate as an equation 0129 bool ok = false; 0130 const QString s = fi->text.mid(1); 0131 const double eqResult(Equations::interpret(store, s.toLatin1(), &ok, s.length())); 0132 if (fi->formated) { 0133 txt = FormattedNumber(eqResult, fi->format); 0134 } else { 0135 txt = QString::number(eqResult, 'g', rc.precision); 0136 } 0137 } else { 0138 Kst::ObjectPtr op = store->retrieveObject(fi->text); 0139 Kst::ScalarPtr scp = Kst::kst_cast<Kst::Scalar>(op); 0140 if (scp) { 0141 KstReadLocker l(scp); 0142 if (fi->formated) { 0143 txt = FormattedNumber(scp->value(), fi->format); 0144 0145 } else { 0146 txt = QString::number(scp->value(), 'g', rc.precision); 0147 } 0148 if (cache) { 0149 rc.addObject(scp); 0150 } 0151 } else { 0152 Kst::StringPtr stp = Kst::kst_cast<Kst::String>(op); 0153 if (stp) { 0154 KstReadLocker l(stp); 0155 txt = stp->value(); 0156 if (cache) { 0157 rc.addObject(stp); 0158 } 0159 } 0160 } 0161 } 0162 if (draw && rc.p) { 0163 rc.p->drawText(rc.x, rc.y, txt); 0164 } 0165 if (cache) { 0166 rc.addToCache(QPointF(rc.x, rc.y), txt, f, pen); 0167 } 0168 rc.x += rc.fontWidth(txt); 0169 } else if (fi->vector) { 0170 QString txt; 0171 Kst::VectorPtr vp = Kst::kst_cast<Kst::Vector>(store->retrieveObject(fi->text)); 0172 if (vp) { 0173 if (!fi->expression.isEmpty()) { 0174 if (cache) { 0175 rc.addObject(vp); 0176 } 0177 // Parse and evaluate as an equation 0178 bool ok = false; 0179 // FIXME: make more efficient: cache the parsed equation 0180 const double idx = Equations::interpret(store, fi->expression.toLatin1(), &ok, fi->expression.length()); 0181 if (ok) { 0182 KstReadLocker l(vp); 0183 const double vVal(vp->value()[int(idx)]); 0184 if (fi->formated) { 0185 txt = FormattedNumber(vVal, fi->format); 0186 } else { 0187 txt = QString::number(vVal, 'g', rc.precision); 0188 } 0189 0190 if (cache) { 0191 rc.addObject(vp); 0192 } 0193 } else { 0194 txt = "NAN"; 0195 } 0196 } 0197 } 0198 if (draw && rc.p) { 0199 rc.p->drawText(rc.x, rc.y, txt); 0200 } 0201 if (cache) { 0202 rc.addToCache(QPointF(rc.x, rc.y), txt, f, pen); 0203 } 0204 rc.x += rc.fontWidth(txt); 0205 } else if (fi->tab) { 0206 const int tabWidth = rc.fontWidth("MMMM"); 0207 const int toSkip = tabWidth - (rc.x - rc.xStart) % tabWidth; 0208 if (draw && rc.p && (fi->attributes.underline || fi->attributes.overline)) { 0209 const int spaceWidth = rc.fontWidth(" "); 0210 const int spacesToSkip = tabWidth / spaceWidth + (tabWidth % spaceWidth > 0 ? 1 : 0); 0211 QString txt(QString().fill(' ', spacesToSkip)); 0212 rc.p->drawText(rc.x, rc.y, txt); 0213 if (cache) { 0214 rc.addToCache(QPointF(rc.x, rc.y), txt, f, pen); 0215 } 0216 } 0217 rc.x += toSkip; 0218 } else { 0219 if (draw && rc.p) { 0220 #ifdef BENCHMARK 0221 QTime t; 0222 t.start(); 0223 #endif 0224 rc.p->drawText(rc.x, rc.y, fi->text); 0225 0226 #ifdef BENCHMARK 0227 qDebug() << "Renderer did draw, time: " << t.elapsed(); 0228 #endif 0229 } 0230 if (cache) { 0231 rc.addToCache(QPointF(rc.x, rc.y), fi->text, f, pen); 0232 } 0233 rc.x += rc.fontWidth(fi->text); 0234 } 0235 0236 int xNext = rc.x; 0237 if (fi->group) { 0238 renderLabel(rc, fi->group, true, draw); 0239 xNext = rc.x; 0240 } 0241 0242 if (fi->up) { 0243 int xPrev = rc.x; 0244 renderLabel(rc, fi->up, true, draw); 0245 xNext = qMax(xNext, rc.x); 0246 rc.x = xPrev; 0247 } 0248 0249 if (fi->down) { 0250 renderLabel(rc, fi->down, true, draw); 0251 xNext = qMax(xNext, rc.x); 0252 } 0253 0254 rc.x = xNext; 0255 rc.xMax = qMax(rc.xMax, rc.x); 0256 0257 fi = fi->next; 0258 } 0259 0260 QFont f = rc.font(); 0261 if (rc.fontSize() != oldSize) { 0262 f.setPointSizeF(oldSize); 0263 rc.setFont(f); 0264 } 0265 0266 rc.size = oldSize; 0267 rc.y = oldY; 0268 } 0269 0270 void paintLabel(RenderContext& rc, QPainter *p) { 0271 if (p) { 0272 foreach (const RenderedText &text, rc.cachedText) { 0273 p->save(); 0274 p->setPen(text.pen); 0275 p->setFont(text.font); 0276 p->drawText(text.location, text.text); 0277 p->restore(); 0278 } 0279 } 0280 } 0281 0282 } 0283 0284 // vim: ts=2 sw=2 et