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, &timestruct);
0050     strftime(cstring, strlen, format.data()+1, &timestruct);
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