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) {
65 if (data.text.at(i).unicode()==0x0A) {
74 * Updates the Inserts (letters) of this text. Called when the
75 * text or it's data, position, alignment, .. changes.
76 * This method also updates the usedTextWidth / usedTextHeight property.
78 void RS_Text::update()
80 RS_DEBUG->print("RS_Text::update");
91 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) {
114 switch (data.text.at(i).unicode()) {
117 updateAddLine(oneLine, lineCounter++);
118 oneLine = new RS_EntityContainer(this);
119 letterPos = Vector(0.0, -9.0);
128 // code (e.g. \S, \P, ..)
130 int ch = data.text.at(i).unicode();
133 updateAddLine(oneLine, lineCounter++);
134 oneLine = new RS_EntityContainer(this);
135 letterPos = Vector(0.0, -9.0);
141 //letterPos += letterSpace;
145 while (data.text.at(i).unicode()!='^' &&
146 //data.text.at(i).unicode()!='/' &&
147 data.text.at(i).unicode()!='\\' &&
148 //data.text.at(i).unicode()!='#' &&
149 i<(int)data.text.length()) {
150 up += data.text.at(i);
156 if (data.text.at(i-1).unicode()=='^' &&
157 data.text.at(i).unicode()==' ') {
162 while (data.text.at(i).unicode()!=';' &&
163 i<(int)data.text.length()) {
164 dw += data.text.at(i);
172 RS_TextData(letterPos + Vector(0.0,9.0),
173 4.0, 100.0, RS2::VAlignTop,
175 RS2::LeftToRight, RS2::Exact,
178 upper->setLayer(NULL);
179 upper->setPen(RS_Pen(RS2::FlagInvalid));
180 oneLine->addEntity(upper);
185 RS_TextData(letterPos+Vector(0.0,4.0),
186 4.0, 100.0, RS2::VAlignTop,
188 RS2::LeftToRight, RS2::Exact,
191 lower->setLayer(NULL);
192 lower->setPen(RS_Pen(RS2::FlagInvalid));
193 oneLine->addEntity(lower);
196 upper->calculateBorders();
197 lower->calculateBorders();
199 double w1 = upper->getSize().x;
200 double w2 = lower->getSize().x;
203 letterPos += Vector(w1, 0.0);
205 letterPos += Vector(w2, 0.0);
207 letterPos += letterSpace;
219 if (font->findLetter(QString(data.text.at(i))) != NULL) {
221 RS_DEBUG->print("RS_Text::update: insert a "
222 "letter at pos: %f/%f", letterPos.x, letterPos.y);
224 RS_InsertData d(QString(data.text.at(i)),
228 1,1, Vector(0.0,0.0),
229 font->getLetterList(), RS2::NoUpdate);
231 RS_Insert* letter = new RS_Insert(this, d);
233 letter->setPen(RS_Pen(RS2::FlagInvalid));
234 letter->setLayer(NULL);
236 letter->forcedCalculateBorders();
239 //letterWidth = Vector(letter->getSize().x, 0.0);
241 letterWidth = Vector(letter->getMax().x-letterPos.x, 0.0);
243 oneLine->addEntity(letter);
245 // next letter position:
246 letterPos += letterWidth;
247 letterPos += letterSpace;
254 updateAddLine(oneLine, lineCounter);
255 usedTextHeight -= data.height*data.lineSpacingFactor*1.6
257 forcedCalculateBorders();
259 RS_DEBUG->print("RS_Text::update: OK");
263 * Used internally by update() to add a text line created with
264 * default values and alignment to this text container.
266 * @param textLine The text line.
267 * @param lineCounter Line number.
269 void RS_Text::updateAddLine(RS_EntityContainer* textLine, int lineCounter)
271 RS_DEBUG->print("RS_Text::updateAddLine: width: %f", textLine->getSize().x);
273 //textLine->forcedCalculateBorders();
274 //RS_DEBUG->print("RS_Text::updateAddLine: width 2: %f", textLine->getSize().x);
276 // Move to correct line position:
277 textLine->move(Vector(0.0, -9.0 * lineCounter
278 * data.lineSpacingFactor * 1.6));
280 textLine->forcedCalculateBorders();
281 Vector textSize = textLine->getSize();
283 RS_DEBUG->print("RS_Text::updateAddLine: width 2: %f", textSize.x);
286 switch (data.halign) {
287 case RS2::HAlignCenter:
288 RS_DEBUG->print("RS_Text::updateAddLine: move by: %f", -textSize.x/2.0);
289 textLine->move(Vector(-textSize.x/2.0, 0.0));
292 case RS2::HAlignRight:
293 textLine->move(Vector(-textSize.x, 0.0));
301 double vSize = getNumberOfLines()*9.0*data.lineSpacingFactor*1.6
302 - (9.0*data.lineSpacingFactor*1.6 - 9.0);
304 switch (data.valign) {
305 case RS2::VAlignMiddle:
306 textLine->move(Vector(0.0, vSize/2.0));
309 case RS2::VAlignBottom:
310 textLine->move(Vector(0.0, vSize));
318 textLine->scale(Vector(0.0,0.0),
319 Vector(data.height/9.0, data.height/9.0));
321 textLine->forcedCalculateBorders();
323 // Update actual text size (before rotating, after scaling!):
324 if (textLine->getSize().x>usedTextWidth) {
325 usedTextWidth = textLine->getSize().x;
328 usedTextHeight += data.height*data.lineSpacingFactor*1.6;
331 textLine->rotate(Vector(0.0,0.0), data.angle);
334 textLine->move(data.insertionPoint);
335 textLine->setPen(RS_Pen(RS2::FlagInvalid));
336 textLine->setLayer(NULL);
337 textLine->forcedCalculateBorders();
342 Vector RS_Text::getInsertionPoint()
344 return data.insertionPoint;
347 double RS_Text::getHeight()
352 void RS_Text::setHeight(double h)
357 double RS_Text::getWidth()
363 * Sets the alignment from an int.
365 * @param a 1: top left ... 9: bottom right
367 void RS_Text::setAlignment(int a)
373 data.halign = RS2::HAlignLeft;
376 data.halign = RS2::HAlignCenter;
379 data.halign = RS2::HAlignRight;
383 switch ((int)ceil(a / 3.0))
387 data.valign = RS2::VAlignTop;
390 data.valign = RS2::VAlignMiddle;
393 data.valign = RS2::VAlignBottom;
399 * Gets the alignment as an int.
401 * @return 1: top left ... 9: bottom right
403 int RS_Text::getAlignment()
405 if (data.valign == RS2::VAlignTop)
407 if (data.halign == RS2::HAlignLeft)
411 else if (data.halign == RS2::HAlignCenter)
415 else if (data.halign == RS2::HAlignRight)
420 else if (data.valign == RS2::VAlignMiddle)
422 if (data.halign == RS2::HAlignLeft)
426 else if (data.halign == RS2::HAlignCenter)
430 else if (data.halign == RS2::HAlignRight)
435 else if (data.valign == RS2::VAlignBottom)
437 if (data.halign == RS2::HAlignLeft)
441 else if (data.halign == RS2::HAlignCenter)
445 else if (data.halign == RS2::HAlignRight)
454 RS2::VAlign RS_Text::getVAlign()
459 void RS_Text::setVAlign(RS2::VAlign va)
464 RS2::HAlign RS_Text::getHAlign()
469 void RS_Text::setHAlign(RS2::HAlign ha)
474 RS2::TextDrawingDirection RS_Text::getDrawingDirection()
476 return data.drawingDirection;
479 RS2::TextLineSpacingStyle RS_Text::getLineSpacingStyle()
481 return data.lineSpacingStyle;
484 void RS_Text::setLineSpacingFactor(double f)
486 data.lineSpacingFactor = f;
489 double RS_Text::getLineSpacingFactor()
491 return data.lineSpacingFactor;
495 * Sets a new text. The entities representing the
498 void RS_Text::setText(const QString & t)
502 // handle some special flags embedded in the text:
503 if (data.text.left(4) == "\\A0;")
505 data.text = data.text.mid(4);
506 data.valign = RS2::VAlignBottom;
508 else if (data.text.left(4) == "\\A1;")
510 data.text = data.text.mid(4);
511 data.valign = RS2::VAlignMiddle;
513 else if (data.text.left(4) == "\\A2;")
515 data.text = data.text.mid(4);
516 data.valign = RS2::VAlignTop;
519 if (data.updateMode == RS2::Update)
522 //calculateBorders();
526 QString RS_Text::getText()
531 void RS_Text::setStyle(const QString & s)
536 QString RS_Text::getStyle()
541 void RS_Text::setAngle(double a)
546 double RS_Text::getAngle()
551 double RS_Text::getUsedTextWidth()
553 return usedTextWidth;
556 double RS_Text::getUsedTextHeight()
558 return usedTextHeight;
561 /*virtual*/ double RS_Text::getLength()
566 VectorSolutions RS_Text::getRefPoints()
568 VectorSolutions ret(data.insertionPoint);
572 Vector RS_Text::getNearestRef(const Vector & coord, double * dist)
574 //return getRefPoints().getClosest(coord, dist);
575 return RS_Entity::getNearestRef(coord, dist);
578 void RS_Text::move(Vector offset)
580 data.insertionPoint.move(offset);
584 void RS_Text::rotate(Vector center, double angle)
586 data.insertionPoint.rotate(center, angle);
587 data.angle = RS_Math::correctAngle(data.angle+angle);
591 void RS_Text::scale(Vector center, Vector factor)
593 data.insertionPoint.scale(center, factor);
594 data.width*=factor.x;
595 data.height*=factor.x;
599 void RS_Text::mirror(Vector axisPoint1, Vector axisPoint2)
601 data.insertionPoint.mirror(axisPoint1, axisPoint2);
602 //double ang = axisPoint1.angleTo(axisPoint2);
603 bool readable = RS_Math::isAngleReadable(data.angle);
606 vec.setPolar(1.0, data.angle);
607 vec.mirror(Vector(0.0,0.0), axisPoint2-axisPoint1);
608 data.angle = vec.angle();
611 data.angle = RS_Math::makeAngleReadable(data.angle, readable, &corr);
614 if (data.halign==RS2::HAlignLeft) {
615 data.halign=RS2::HAlignRight;
616 } else if (data.halign==RS2::HAlignRight) {
617 data.halign=RS2::HAlignLeft;
620 if (data.valign==RS2::VAlignTop) {
621 data.valign=RS2::VAlignBottom;
622 } else if (data.valign==RS2::VAlignBottom) {
623 data.valign=RS2::VAlignTop;
629 bool RS_Text::hasEndpointsWithinWindow(Vector /*v1*/, Vector /*v2*/)
637 * Implementations must stretch the given range of the entity
638 * by the given offset.
640 void RS_Text::stretch(Vector firstCorner,
644 if (getMin().isInWindow(firstCorner, secondCorner) &&
645 getMax().isInWindow(firstCorner, secondCorner)) {
654 * Dumps the point's data to stdout.
656 std::ostream& operator << (std::ostream& os, const RS_Text& p) {
657 os << " Text: " << p.getData() << "\n";