3 // Part of the Architektonas Project
4 // Originally part of QCad Community Edition by Andrew Mustun
5 // Extensively rewritten and refactored by James L. Hammons
6 // Portions copyright (C) 2001-2003 RibbonSoft
7 // Copyright (C) 2010 Underground Software
8 // See the README and GPLv2 files for licensing and warranty information
10 // JLH = James L. Hammons <jlhamm@acm.org>
13 // --- ---------- -----------------------------------------------------------
14 // JLH 05/27/2010 Added this text. :-)
26 Text::Text(EntityContainer * parent, const TextData & d): EntityContainer(parent), data(d)
33 /*virtual*/ Text::~Text()
37 /*virtual*/ Entity * Text::clone()
39 Text * t = new Text(*this);
40 #warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
41 // t->entities.setAutoDelete(entities.autoDelete());
47 /** @return RS2::EntityText */
48 /*virtual*/ RS2::EntityType Text::rtti() const
50 return RS2::EntityText;
53 /** @return Copy of data that defines the text. */
54 TextData Text::getData() const
60 * @return Number of lines in this text entity.
62 int Text::getNumberOfLines()
66 for(int i=0; i<(int)data.text.length(); i++)
68 if (data.text.at(i).unicode() == 0x0A)
76 * Updates the Inserts (letters) of this text. Called when the
77 * text or it's data, position, alignment, .. changes.
78 * This method also updates the usedTextWidth / usedTextHeight property.
83 printf("Text::update(): ");
85 DEBUG->print("Text::update");
89 printf("isUndone=%s, ", (isUndone() ? "true" : "false"));
98 printf("data.style=\"%s\", ", data.style.toAscii().data());
100 Font * font = FONTLIST->requestFont(data.style);
103 printf("font=%08X\n", (unsigned int)font);
108 Vector letterPos = Vector(0.0, -9.0);
109 Vector letterSpace = Vector(font->getLetterSpacing(), 0.0);
110 Vector space = Vector(font->getWordSpacing(), 0.0);
113 // Every single text line gets stored in this entity container
114 // so we can move the whole line around easely:
115 EntityContainer * oneLine = new EntityContainer(this);
117 // First every text line is created with
118 // alignement: top left
121 // Rotation, scaling and centering is done later
124 for(int i=0; i<(int)data.text.length(); ++i)
126 switch (data.text.at(i).unicode())
130 updateAddLine(oneLine, lineCounter++);
131 oneLine = new EntityContainer(this);
132 letterPos = Vector(0.0, -9.0);
142 // code (e.g. \S, \P, ..)
144 int ch = data.text.at(i).unicode();
149 updateAddLine(oneLine, lineCounter++);
150 oneLine = new EntityContainer(this);
151 letterPos = Vector(0.0, -9.0);
158 //letterPos += letterSpace;
162 while (data.text.at(i).unicode() != '^'
163 && data.text.at(i).unicode() != '\\'
164 && i < (int)data.text.length())
166 up += data.text.at(i);
172 if (data.text.at(i - 1).unicode() == '^'
173 && data.text.at(i).unicode() == ' ')
179 while (data.text.at(i).unicode() != ';' && i < (int)data.text.length())
181 dw += data.text.at(i);
186 Text * upper = new Text(oneLine,
187 TextData(letterPos + Vector(0.0, 9.0), 4.0, 100.0,
188 RS2::VAlignTop, RS2::HAlignLeft, RS2::LeftToRight,
189 RS2::Exact, 1.0, up, data.style, 0.0, RS2::Update));
190 upper->setLayer(NULL);
191 upper->setPen(Pen(RS2::FlagInvalid));
192 oneLine->addEntity(upper);
194 Text * lower = new Text(oneLine,
195 TextData(letterPos+Vector(0.0, 4.0), 4.0, 100.0,
196 RS2::VAlignTop, RS2::HAlignLeft, RS2::LeftToRight,
197 RS2::Exact, 1.0, dw, data.style, 0.0, RS2::Update));
198 lower->setLayer(NULL);
199 lower->setPen(Pen(RS2::FlagInvalid));
200 oneLine->addEntity(lower);
203 upper->calculateBorders();
204 lower->calculateBorders();
206 double w1 = upper->getSize().x;
207 double w2 = lower->getSize().x;
210 letterPos += Vector(w1, 0.0);
212 letterPos += Vector(w2, 0.0);
214 letterPos += letterSpace;
226 // Regular text works because the .style is "normallatin1" while the dimensions are done
227 // using "standard"... So that's why it doesn't work.
228 // "standard" exists, but it seems that no characters are loaded from it...
230 //printf("--> default: pos=%d, char=%04X, font size=%d\n", i, data.text.at(i).unicode(), font->getLetterList()->count());
232 //#warning "!!! font->findLetter() not working correctly !!!"
233 if (font->findLetter(QString(data.text.at(i))))
235 //We ain't getting here:
237 About to draw TEXT entity... TEXT="107.25"
238 Dimension::updateCreateDimensionLine()...
240 Text::update(): isUndone=false, font=094C1020
241 --> default: pos=0, char=0031
242 --> default: pos=1, char=0030
243 --> default: pos=2, char=0037
244 --> default: pos=3, char=002E
245 --> default: pos=4, char=0032
246 --> default: pos=5, char=0035
248 //printf(" char=%s\n", QString(data.text.at(i)).toAscii().data());
249 DEBUG->print("Text::update: insert a letter at pos: %f/%f", letterPos.x, letterPos.y);
250 InsertData d(QString(data.text.at(i)), letterPos, Vector(1.0, 1.0),
251 0.0, 1, 1, Vector(0.0, 0.0), font->getLetterList(), RS2::NoUpdate);
253 Insert * letter = new Insert(this, d);
254 letter->setPen(Pen(RS2::FlagInvalid));
255 letter->setLayer(NULL);
257 letter->forcedCalculateBorders();
260 //letterWidth = Vector(letter->getSize().x, 0.0);
262 Vector letterWidth = Vector(letter->getMax().x - letterPos.x, 0.0);
263 oneLine->addEntity(letter);
265 // next letter position:
266 letterPos += letterWidth;
267 letterPos += letterSpace;
274 updateAddLine(oneLine, lineCounter);
275 usedTextHeight -= data.height * data.lineSpacingFactor * 1.6 - data.height;
276 forcedCalculateBorders();
278 DEBUG->print("Text::update: OK");
282 * Used internally by update() to add a text line created with
283 * default values and alignment to this text container.
285 * @param textLine The text line.
286 * @param lineCounter Line number.
288 void Text::updateAddLine(EntityContainer* textLine, int lineCounter)
290 DEBUG->print("Text::updateAddLine: width: %f", textLine->getSize().x);
292 //textLine->forcedCalculateBorders();
293 //DEBUG->print("Text::updateAddLine: width 2: %f", textLine->getSize().x);
295 // Move to correct line position:
296 textLine->move(Vector(0.0, -9.0 * lineCounter * data.lineSpacingFactor * 1.6));
298 textLine->forcedCalculateBorders();
299 Vector textSize = textLine->getSize();
301 DEBUG->print("Text::updateAddLine: width 2: %f", textSize.x);
306 case RS2::HAlignCenter:
307 DEBUG->print("Text::updateAddLine: move by: %f", -textSize.x / 2.0);
308 textLine->move(Vector(-textSize.x / 2.0, 0.0));
311 case RS2::HAlignRight:
312 textLine->move(Vector(-textSize.x, 0.0));
320 double vSize = getNumberOfLines() * 9.0 * data.lineSpacingFactor * 1.6 - (9.0 * data.lineSpacingFactor * 1.6 - 9.0);
324 case RS2::VAlignMiddle:
325 textLine->move(Vector(0.0, vSize / 2.0));
328 case RS2::VAlignBottom:
329 textLine->move(Vector(0.0, vSize));
337 textLine->scale(Vector(0.0, 0.0), Vector(data.height / 9.0, data.height / 9.0));
339 textLine->forcedCalculateBorders();
341 // Update actual text size (before rotating, after scaling!):
342 if (textLine->getSize().x > usedTextWidth)
343 usedTextWidth = textLine->getSize().x;
345 usedTextHeight += data.height * data.lineSpacingFactor * 1.6;
348 textLine->rotate(Vector(0.0, 0.0), data.angle);
351 textLine->move(data.insertionPoint);
352 textLine->setPen(Pen(RS2::FlagInvalid));
353 textLine->setLayer(NULL);
354 textLine->forcedCalculateBorders();
359 Vector Text::getInsertionPoint()
361 return data.insertionPoint;
364 double Text::getHeight()
369 void Text::setHeight(double h)
374 double Text::getWidth()
380 * Sets the alignment from an int.
382 * @param a 1: top left ... 9: bottom right
384 void Text::setAlignment(int a)
390 data.halign = RS2::HAlignLeft;
393 data.halign = RS2::HAlignCenter;
396 data.halign = RS2::HAlignRight;
400 switch ((int)ceil(a / 3.0))
404 data.valign = RS2::VAlignTop;
407 data.valign = RS2::VAlignMiddle;
410 data.valign = RS2::VAlignBottom;
416 * Gets the alignment as an int.
418 * @return 1: top left ... 9: bottom right
420 int Text::getAlignment()
422 if (data.valign == RS2::VAlignTop)
424 if (data.halign == RS2::HAlignLeft)
428 else if (data.halign == RS2::HAlignCenter)
432 else if (data.halign == RS2::HAlignRight)
437 else if (data.valign == RS2::VAlignMiddle)
439 if (data.halign == RS2::HAlignLeft)
443 else if (data.halign == RS2::HAlignCenter)
447 else if (data.halign == RS2::HAlignRight)
452 else if (data.valign == RS2::VAlignBottom)
454 if (data.halign == RS2::HAlignLeft)
458 else if (data.halign == RS2::HAlignCenter)
462 else if (data.halign == RS2::HAlignRight)
471 RS2::VAlign Text::getVAlign()
476 void Text::setVAlign(RS2::VAlign va)
481 RS2::HAlign Text::getHAlign()
486 void Text::setHAlign(RS2::HAlign ha)
491 RS2::TextDrawingDirection Text::getDrawingDirection()
493 return data.drawingDirection;
496 RS2::TextLineSpacingStyle Text::getLineSpacingStyle()
498 return data.lineSpacingStyle;
501 void Text::setLineSpacingFactor(double f)
503 data.lineSpacingFactor = f;
506 double Text::getLineSpacingFactor()
508 return data.lineSpacingFactor;
512 * Sets a new text. The entities representing the
515 void Text::setText(const QString & t)
519 // handle some special flags embedded in the text:
520 if (data.text.left(4) == "\\A0;")
522 data.text = data.text.mid(4);
523 data.valign = RS2::VAlignBottom;
525 else if (data.text.left(4) == "\\A1;")
527 data.text = data.text.mid(4);
528 data.valign = RS2::VAlignMiddle;
530 else if (data.text.left(4) == "\\A2;")
532 data.text = data.text.mid(4);
533 data.valign = RS2::VAlignTop;
536 if (data.updateMode == RS2::Update)
539 //calculateBorders();
543 QString Text::getText()
548 void Text::setStyle(const QString & s)
553 QString Text::getStyle()
558 void Text::setAngle(double a)
563 double Text::getAngle()
568 double Text::getUsedTextWidth()
570 return usedTextWidth;
573 double Text::getUsedTextHeight()
575 return usedTextHeight;
578 /*virtual*/ double Text::getLength()
583 VectorSolutions Text::getRefPoints()
585 VectorSolutions ret(data.insertionPoint);
589 Vector Text::getNearestRef(const Vector & coord, double * dist)
591 return Entity::getNearestRef(coord, dist);
594 void Text::move(Vector offset)
596 data.insertionPoint.move(offset);
600 void Text::rotate(Vector center, double angle)
602 data.insertionPoint.rotate(center, angle);
603 data.angle = Math::correctAngle(data.angle + angle);
607 void Text::scale(Vector center, Vector factor)
609 data.insertionPoint.scale(center, factor);
610 data.width *= factor.x;
611 data.height *= factor.x;
615 void Text::mirror(Vector axisPoint1, Vector axisPoint2)
617 data.insertionPoint.mirror(axisPoint1, axisPoint2);
618 //double ang = axisPoint1.angleTo(axisPoint2);
619 bool readable = Math::isAngleReadable(data.angle);
622 vec.setPolar(1.0, data.angle);
623 vec.mirror(Vector(0.0, 0.0), axisPoint2 - axisPoint1);
624 data.angle = vec.angle();
627 data.angle = Math::makeAngleReadable(data.angle, readable, &corr);
631 if (data.halign == RS2::HAlignLeft)
633 data.halign = RS2::HAlignRight;
635 else if (data.halign == RS2::HAlignRight)
637 data.halign = RS2::HAlignLeft;
642 if (data.valign == RS2::VAlignTop)
644 data.valign = RS2::VAlignBottom;
646 else if (data.valign == RS2::VAlignBottom)
648 data.valign = RS2::VAlignTop;
655 bool Text::hasEndpointsWithinWindow(Vector /*v1*/, Vector /*v2*/)
661 * Implementations must stretch the given range of the entity
662 * by the given offset.
664 void Text::stretch(Vector firstCorner, Vector secondCorner, Vector offset)
666 if (getMin().isInWindow(firstCorner, secondCorner)
667 && getMax().isInWindow(firstCorner, secondCorner))
672 * Dumps the point's data to stdout.
674 std::ostream & operator<<(std::ostream & os, const Text & p)
676 os << " Text: " << p.getData() << "\n";