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 // (C) 2010 Underground Software
8 // JLH = James L. Hammons <jlhamm@acm.org>
11 // --- ---------- -----------------------------------------------------------
12 // JLH 05/27/2010 Added this text. :-)
18 #include "rs_fontlist.h"
19 #include "rs_insert.h"
24 RS_Text::RS_Text(RS_EntityContainer * parent, const RS_TextData & d): RS_EntityContainer(parent), data(d)
31 /*virtual*/ RS_Text::~RS_Text()
35 /*virtual*/ RS_Entity * RS_Text::clone()
37 RS_Text * t = new RS_Text(*this);
38 #warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
39 // t->entities.setAutoDelete(entities.autoDelete());
45 /** @return RS2::EntityText */
46 /*virtual*/ RS2::EntityType RS_Text::rtti() const
48 return RS2::EntityText;
51 /** @return Copy of data that defines the text. */
52 RS_TextData RS_Text::getData() const
58 * @return Number of lines in this text entity.
60 int RS_Text::getNumberOfLines()
64 for(int i=0; i<(int)data.text.length(); ++i)
66 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.
80 void RS_Text::update()
82 RS_DEBUG->print("RS_Text::update");
92 RS_Font * font = RS_FONTLIST->requestFont(data.style);
97 Vector letterPos = Vector(0.0, -9.0);
98 Vector letterSpace = Vector(font->getLetterSpacing(), 0.0);
99 Vector space = Vector(font->getWordSpacing(), 0.0);
102 // Every single text line gets stored in this entity container
103 // so we can move the whole line around easely:
104 RS_EntityContainer * oneLine = new RS_EntityContainer(this);
106 // First every text line is created with
107 // alignement: top left
110 // Rotation, scaling and centering is done later
113 for(int i=0; i<(int)data.text.length(); ++i)
115 switch (data.text.at(i).unicode())
119 updateAddLine(oneLine, lineCounter++);
120 oneLine = new RS_EntityContainer(this);
121 letterPos = Vector(0.0, -9.0);
131 // code (e.g. \S, \P, ..)
133 int ch = data.text.at(i).unicode();
138 updateAddLine(oneLine, lineCounter++);
139 oneLine = new RS_EntityContainer(this);
140 letterPos = Vector(0.0, -9.0);
147 //letterPos += letterSpace;
151 while (data.text.at(i).unicode() != '^'
152 && data.text.at(i).unicode() != '\\'
153 && i < (int)data.text.length())
155 up += data.text.at(i);
161 if (data.text.at(i - 1).unicode() == '^'
162 && data.text.at(i).unicode() == ' ')
168 while (data.text.at(i).unicode() != ';' && i < (int)data.text.length())
170 dw += data.text.at(i);
175 RS_Text * upper = new RS_Text(oneLine,
176 RS_TextData(letterPos + Vector(0.0, 9.0),
177 4.0, 100.0, RS2::VAlignTop,
179 RS2::LeftToRight, RS2::Exact,
182 upper->setLayer(NULL);
183 upper->setPen(RS_Pen(RS2::FlagInvalid));
184 oneLine->addEntity(upper);
186 RS_Text * lower = new RS_Text(oneLine,
187 RS_TextData(letterPos+Vector(0.0, 4.0),
188 4.0, 100.0, RS2::VAlignTop,
190 RS2::LeftToRight, RS2::Exact,
193 lower->setLayer(NULL);
194 lower->setPen(RS_Pen(RS2::FlagInvalid));
195 oneLine->addEntity(lower);
198 upper->calculateBorders();
199 lower->calculateBorders();
201 double w1 = upper->getSize().x;
202 double w2 = lower->getSize().x;
205 letterPos += Vector(w1, 0.0);
207 letterPos += Vector(w2, 0.0);
209 letterPos += letterSpace;
222 if (font->findLetter(QString(data.text.at(i))) != NULL)
224 RS_DEBUG->print("RS_Text::update: insert a "
225 "letter at pos: %f/%f", letterPos.x, letterPos.y);
227 RS_InsertData d(QString(data.text.at(i)), letterPos, Vector(1.0, 1.0),
228 0.0, 1,1, Vector(0.0, 0.0), font->getLetterList(), RS2::NoUpdate);
230 RS_Insert * letter = new RS_Insert(this, d);
231 letter->setPen(RS_Pen(RS2::FlagInvalid));
232 letter->setLayer(NULL);
234 letter->forcedCalculateBorders();
237 //letterWidth = Vector(letter->getSize().x, 0.0);
239 Vector letterWidth = Vector(letter->getMax().x - letterPos.x, 0.0);
241 oneLine->addEntity(letter);
243 // next letter position:
244 letterPos += letterWidth;
245 letterPos += letterSpace;
252 updateAddLine(oneLine, lineCounter);
253 usedTextHeight -= data.height * data.lineSpacingFactor * 1.6 - data.height;
254 forcedCalculateBorders();
256 RS_DEBUG->print("RS_Text::update: OK");
260 * Used internally by update() to add a text line created with
261 * default values and alignment to this text container.
263 * @param textLine The text line.
264 * @param lineCounter Line number.
266 void RS_Text::updateAddLine(RS_EntityContainer* textLine, int lineCounter)
268 RS_DEBUG->print("RS_Text::updateAddLine: width: %f", textLine->getSize().x);
270 //textLine->forcedCalculateBorders();
271 //RS_DEBUG->print("RS_Text::updateAddLine: width 2: %f", textLine->getSize().x);
273 // Move to correct line position:
274 textLine->move(Vector(0.0, -9.0 * lineCounter * data.lineSpacingFactor * 1.6));
276 textLine->forcedCalculateBorders();
277 Vector textSize = textLine->getSize();
279 RS_DEBUG->print("RS_Text::updateAddLine: width 2: %f", textSize.x);
284 case RS2::HAlignCenter:
285 RS_DEBUG->print("RS_Text::updateAddLine: move by: %f", -textSize.x / 2.0);
286 textLine->move(Vector(-textSize.x / 2.0, 0.0));
289 case RS2::HAlignRight:
290 textLine->move(Vector(-textSize.x, 0.0));
298 double vSize = getNumberOfLines() * 9.0 * data.lineSpacingFactor * 1.6 - (9.0 * data.lineSpacingFactor * 1.6 - 9.0);
302 case RS2::VAlignMiddle:
303 textLine->move(Vector(0.0, vSize / 2.0));
306 case RS2::VAlignBottom:
307 textLine->move(Vector(0.0, vSize));
315 textLine->scale(Vector(0.0, 0.0), Vector(data.height / 9.0, data.height / 9.0));
317 textLine->forcedCalculateBorders();
319 // Update actual text size (before rotating, after scaling!):
320 if (textLine->getSize().x > usedTextWidth)
321 usedTextWidth = textLine->getSize().x;
323 usedTextHeight += data.height * data.lineSpacingFactor * 1.6;
326 textLine->rotate(Vector(0.0, 0.0), data.angle);
329 textLine->move(data.insertionPoint);
330 textLine->setPen(RS_Pen(RS2::FlagInvalid));
331 textLine->setLayer(NULL);
332 textLine->forcedCalculateBorders();
337 Vector RS_Text::getInsertionPoint()
339 return data.insertionPoint;
342 double RS_Text::getHeight()
347 void RS_Text::setHeight(double h)
352 double RS_Text::getWidth()
358 * Sets the alignment from an int.
360 * @param a 1: top left ... 9: bottom right
362 void RS_Text::setAlignment(int a)
368 data.halign = RS2::HAlignLeft;
371 data.halign = RS2::HAlignCenter;
374 data.halign = RS2::HAlignRight;
378 switch ((int)ceil(a / 3.0))
382 data.valign = RS2::VAlignTop;
385 data.valign = RS2::VAlignMiddle;
388 data.valign = RS2::VAlignBottom;
394 * Gets the alignment as an int.
396 * @return 1: top left ... 9: bottom right
398 int RS_Text::getAlignment()
400 if (data.valign == RS2::VAlignTop)
402 if (data.halign == RS2::HAlignLeft)
406 else if (data.halign == RS2::HAlignCenter)
410 else if (data.halign == RS2::HAlignRight)
415 else if (data.valign == RS2::VAlignMiddle)
417 if (data.halign == RS2::HAlignLeft)
421 else if (data.halign == RS2::HAlignCenter)
425 else if (data.halign == RS2::HAlignRight)
430 else if (data.valign == RS2::VAlignBottom)
432 if (data.halign == RS2::HAlignLeft)
436 else if (data.halign == RS2::HAlignCenter)
440 else if (data.halign == RS2::HAlignRight)
449 RS2::VAlign RS_Text::getVAlign()
454 void RS_Text::setVAlign(RS2::VAlign va)
459 RS2::HAlign RS_Text::getHAlign()
464 void RS_Text::setHAlign(RS2::HAlign ha)
469 RS2::TextDrawingDirection RS_Text::getDrawingDirection()
471 return data.drawingDirection;
474 RS2::TextLineSpacingStyle RS_Text::getLineSpacingStyle()
476 return data.lineSpacingStyle;
479 void RS_Text::setLineSpacingFactor(double f)
481 data.lineSpacingFactor = f;
484 double RS_Text::getLineSpacingFactor()
486 return data.lineSpacingFactor;
490 * Sets a new text. The entities representing the
493 void RS_Text::setText(const QString & t)
497 // handle some special flags embedded in the text:
498 if (data.text.left(4) == "\\A0;")
500 data.text = data.text.mid(4);
501 data.valign = RS2::VAlignBottom;
503 else if (data.text.left(4) == "\\A1;")
505 data.text = data.text.mid(4);
506 data.valign = RS2::VAlignMiddle;
508 else if (data.text.left(4) == "\\A2;")
510 data.text = data.text.mid(4);
511 data.valign = RS2::VAlignTop;
514 if (data.updateMode == RS2::Update)
517 //calculateBorders();
521 QString RS_Text::getText()
526 void RS_Text::setStyle(const QString & s)
531 QString RS_Text::getStyle()
536 void RS_Text::setAngle(double a)
541 double RS_Text::getAngle()
546 double RS_Text::getUsedTextWidth()
548 return usedTextWidth;
551 double RS_Text::getUsedTextHeight()
553 return usedTextHeight;
556 /*virtual*/ double RS_Text::getLength()
561 VectorSolutions RS_Text::getRefPoints()
563 VectorSolutions ret(data.insertionPoint);
567 Vector RS_Text::getNearestRef(const Vector & coord, double * dist)
569 //return getRefPoints().getClosest(coord, dist);
570 return RS_Entity::getNearestRef(coord, dist);
573 void RS_Text::move(Vector offset)
575 data.insertionPoint.move(offset);
579 void RS_Text::rotate(Vector center, double angle)
581 data.insertionPoint.rotate(center, angle);
582 data.angle = RS_Math::correctAngle(data.angle + angle);
586 void RS_Text::scale(Vector center, Vector factor)
588 data.insertionPoint.scale(center, factor);
589 data.width *= factor.x;
590 data.height *= factor.x;
594 void RS_Text::mirror(Vector axisPoint1, Vector axisPoint2)
596 data.insertionPoint.mirror(axisPoint1, axisPoint2);
597 //double ang = axisPoint1.angleTo(axisPoint2);
598 bool readable = RS_Math::isAngleReadable(data.angle);
601 vec.setPolar(1.0, data.angle);
602 vec.mirror(Vector(0.0, 0.0), axisPoint2 - axisPoint1);
603 data.angle = vec.angle();
606 data.angle = RS_Math::makeAngleReadable(data.angle, readable, &corr);
610 if (data.halign == RS2::HAlignLeft)
612 data.halign = RS2::HAlignRight;
614 else if (data.halign == RS2::HAlignRight)
616 data.halign = RS2::HAlignLeft;
621 if (data.valign == RS2::VAlignTop)
623 data.valign = RS2::VAlignBottom;
625 else if (data.valign == RS2::VAlignBottom)
627 data.valign = RS2::VAlignTop;
634 bool RS_Text::hasEndpointsWithinWindow(Vector /*v1*/, Vector /*v2*/)
640 * Implementations must stretch the given range of the entity
641 * by the given offset.
643 void RS_Text::stretch(Vector firstCorner, Vector secondCorner, Vector offset)
645 if (getMin().isInWindow(firstCorner, secondCorner)
646 && getMax().isInWindow(firstCorner, secondCorner))
651 * Dumps the point's data to stdout.
653 std::ostream & operator<<(std::ostream & os, const RS_Text & p)
655 os << " Text: " << p.getData() << "\n";