File indexing completed on 2024-12-08 06:46:40
0001 /*************************************************************************** 0002 cmaptext.cpp 0003 ------------------- 0004 begin : Sat Mar 10 2001 0005 copyright : (C) 2001 by Kmud Developer Team 0006 email : kmud-devel@kmud.de 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 "cmaptext.h" 0019 0020 #include <qfontmetrics.h> 0021 #include <qfont.h> 0022 #include <qstring.h> 0023 #include <qcolor.h> 0024 #include <qpoint.h> 0025 #include <QDebug> 0026 0027 #include "cmapmanager.h" 0028 #include "cmaplevel.h" 0029 0030 0031 CMapText::CMapText(QString str,QFont f,QColor col,CMapManager *manager,QPoint pos,CMapLevel *level) : CMapElement(manager,level) 0032 { 0033 m_font = f; 0034 setRect(QRect(pos,pos)); 0035 setText(str); 0036 0037 setColor(col); 0038 0039 m_linkElement = nullptr; 0040 0041 getZone()->m_text_id_count=getZone()->m_text_id_count+1; 0042 m_ID = getZone()->m_text_id_count; 0043 setCursor(QPoint(0,1)); 0044 0045 if (level) 0046 level->getTextList()->append(this); 0047 } 0048 0049 CMapText::CMapText(QString str,CMapManager *manager,QPoint pos,CMapLevel *level) : CMapElement(manager,level) 0050 { 0051 m_font = manager->getMapData()->defaultTextFont; 0052 setRect(QRect(pos,pos)); 0053 setText(str); 0054 setColor(Qt::black); 0055 m_linkElement = nullptr; 0056 0057 getZone()->m_text_id_count=getZone()->m_text_id_count+1; 0058 m_ID = getZone()->m_text_id_count; 0059 setCursor(QPoint(0,1)); 0060 0061 if (level) 0062 level->getTextList()->append(this); 0063 } 0064 0065 CMapText::~CMapText() 0066 { 0067 if (m_linkElement) 0068 { 0069 if (m_linkElement->getElementType()==ROOM) 0070 { 0071 ((CMapRoom *)m_linkElement)->textRemove(); 0072 } 0073 0074 if (m_linkElement->getElementType()==ZONE) 0075 { 0076 ((CMapZone *)m_linkElement)->textRemove(); 0077 } 0078 } 0079 getLevel()->getTextList()->removeAll(this); 0080 } 0081 0082 void CMapText::setLevel(CMapLevel *level) 0083 { 0084 if (getLevel()) getLevel()->getTextList()->removeAll(this); 0085 level->getTextList()->append(this); 0086 CMapElement::setLevel(level); 0087 } 0088 0089 /** This is used to return the actual cords in the view of the cursor 0090 * @return the cords */ 0091 QPoint CMapText::getCursorCords(void) 0092 { 0093 return m_cursorOffset + getLowPos(); 0094 } 0095 0096 void CMapText::dragPaint(QPoint offset,QPainter *p,CMapZone *zone) 0097 { 0098 QPoint pos(getX()+offset.x(),getY()+offset.y()); 0099 paintElementResize(p,pos,getSize(),zone); 0100 } 0101 0102 void CMapText::lowerPaint(QPainter *,CMapZone *) 0103 { 0104 // Do nothing as the text is not visiable on the lower level 0105 } 0106 0107 void CMapText::higherPaint(QPainter *,CMapZone *) 0108 { 0109 // Do nothing as the text is not visiable on the upper level 0110 } 0111 0112 /** This is used to convert a Actual size to it's closet font size 0113 * @param size The actual size of the font 0114 * @param font The text font 0115 * @return The closet matching font size */ 0116 int CMapText::getActualToFontSize(QSize size,QFont font,QStringList *text) 0117 { 0118 QFont tmpFont = font; 0119 int result = 1; 0120 tmpFont.setPointSize(result); 0121 QSize fontSize = QSize(1,1); 0122 0123 //FIXME_jp: Check this does not go into infinate loop because there is 0124 // no upper limit check. 0125 while (fontSize.width() < size.width() && fontSize.height() < size.height()) 0126 { 0127 result+=10; 0128 tmpFont.setPointSize(result); 0129 QFontMetrics fm(tmpFont); 0130 int tmpWidth = 0; 0131 for (QStringList::iterator it = text->begin(); it != text->end(); ++it) 0132 { 0133 if (fm.horizontalAdvance(*it) > tmpWidth) 0134 tmpWidth = fm.horizontalAdvance(*it); 0135 } 0136 0137 fontSize = QSize(tmpWidth,fm.height()); 0138 } 0139 0140 while ((fontSize.width() > size.width() || fontSize.height() > size.height()) && result > 1) 0141 { 0142 result--; 0143 tmpFont.setPointSize(result); 0144 QFontMetrics fm(tmpFont); 0145 int tmpWidth = 0; 0146 for (QStringList::iterator it = text->begin(); it != text->end(); ++it) 0147 { 0148 if (fm.horizontalAdvance(*it) > tmpWidth) 0149 tmpWidth = fm.horizontalAdvance(*it); 0150 } 0151 0152 fontSize = QSize(tmpWidth,fm.height()); 0153 } 0154 0155 return result; 0156 } 0157 0158 /** Used to paint the text on the map 0159 * @param p The painter used to paint the text 0160 * @param zone The zone the text is being painted in 0161 */ 0162 void CMapText::paint(QPainter *p,CMapZone *zone) 0163 { 0164 CMapElement::paint(p,zone); 0165 0166 if (getEditMode()) 0167 { 0168 p->save(); 0169 0170 p->translate(getX(),getY()); 0171 0172 QFontMetrics fm(m_font); 0173 0174 p->setPen(getManager()->getMapData()->defaultTextColor); 0175 p->setBrush(getManager()->getMapData()->defaultTextColor); 0176 0177 int x = m_cursorOffset.x(); 0178 int y1 = m_cursorOffset.y(); 0179 int y2 = m_cursorOffset.y()-fm.height(); 0180 p->drawLine(x ,y1,x ,y2 ); 0181 0182 p->restore(); 0183 } 0184 } 0185 0186 /** Used to paint the element at a given location and size 0187 * @param p The painer to paint the element to 0188 * @param pos The position to paint the elmenet 0189 * @param size The size the element should be draw 0190 * @param zone The current zone being viewed */ 0191 void CMapText::paintElementResize(QPainter *p,QPoint pos,QSize size,CMapZone *) 0192 { 0193 paintText(p,m_col,pos,m_font,&m_text,size); 0194 } 0195 0196 /** This is used to paint the text. It is capable of painting multiline 0197 * of text. It is static so that it can be called with out creating a instance of CMapText 0198 * @param p The painter used to paint the text 0199 * @param col The color to paint the text 0200 * @param pos The position to paint the text 0201 * @param font The Font used to paint the text 0202 * @param text A point to the text list 0203 * @param size The size of the text */ 0204 void CMapText::paintText(QPainter *p,QColor col,QPoint pos,QFont font,QStringList *text,QSize size) 0205 { 0206 QFont tmpFont = font; 0207 int fontSize = getActualToFontSize(size,font,text); 0208 tmpFont.setPointSize(fontSize); 0209 0210 p->save(); 0211 p->translate(pos.x(),pos.y()); 0212 paintText(p,col,QPoint(0,0),tmpFont,text); 0213 p->restore(); 0214 } 0215 0216 /** This is used to paint the text. It is capable of painting multiline of text */ 0217 void CMapText::paintText(QPainter *p,QColor col,QPoint pos,QFont font,QStringList *text) 0218 { 0219 QFontMetrics fm(font); 0220 pos.setY(pos.y()+fm.height()-fm.descent()); 0221 0222 p->setPen(col); 0223 p->setBrush(col); 0224 0225 p->setFont(font); 0226 0227 for (QStringList::iterator it = text->begin(); it != text->end(); ++it) 0228 { 0229 p->drawText(pos,*it); 0230 0231 pos.setY(pos.y()+fm.height()); 0232 } 0233 } 0234 0235 /** This is used to set the cursor position within the text element 0236 * @param pos The cursor position 0237 */ 0238 void CMapText::setCursor(QPoint pos) 0239 { 0240 if (pos.y() > (int)m_text.count()) 0241 { 0242 // Set the cursor to the last position 0243 m_cursorPos.setX(QString(m_text.last()).length()); 0244 m_cursorPos.setY(m_text.count()); 0245 } 0246 else 0247 { 0248 m_cursorPos = pos; 0249 } 0250 0251 setActualCursorPosition(); 0252 } 0253 0254 /** This is used to conver a offset from the element orgin into a cursor position 0255 * @param offset The offset of the curosr */ 0256 QPoint CMapText::convertOffsetToCursor(QPoint offset) 0257 { 0258 QFontMetrics fm(m_font); 0259 0260 int y = (int)(offset.y() / fm.height()); 0261 int x = 0; 0262 0263 QString s = m_text.at(y); 0264 0265 if (s.length()>0) 0266 { 0267 bool found = false; 0268 0269 for (int pos = 0 ; pos <=(int)s.length() ; pos ++) 0270 { 0271 int charWidth =fm.horizontalAdvance(s.left(pos)); 0272 if (charWidth>offset.x()) 0273 { 0274 x = pos -1; 0275 found = true; 0276 break; 0277 } 0278 } 0279 0280 if (!found) 0281 { 0282 x = (int)fm.horizontalAdvance(s); 0283 } 0284 } 0285 0286 return QPoint(x,y+1); 0287 } 0288 0289 /** This is used to convert mouse cordinates into a cursor position */ 0290 QPoint CMapText::convertPosToCursor(QPoint mousePos) 0291 { 0292 mousePos-=getLowPos(); 0293 0294 return convertOffsetToCursor(mousePos); 0295 } 0296 0297 /** This method is called when the element looses it's edit mode */ 0298 void CMapText::editModeUnsetEvent(void) 0299 { 0300 qDebug() << "CMapText::editModeUnsetEvent"; 0301 //FIXME_jp: Make sure elements are deleted 0302 0303 if (m_text.count()==0) 0304 emit deleteElement((CMapElement *)this,true); 0305 else 0306 if (QString(m_text.first()).trimmed().length()==0 && m_text.count() == 1) 0307 emit deleteElement((CMapElement *)this,true); 0308 0309 setTextSize(); 0310 0311 // Update any elements linked with this one (zones/rooms with labels) 0312 updateLinkElements(); 0313 } 0314 0315 /** This method is called when the element gains it's edit mode */ 0316 void CMapText::editModeSetEvent(void) 0317 { 0318 m_orgText = getText(); 0319 } 0320 0321 /** This method is called to insert a string at the cursor position */ 0322 void CMapText::insertString(QString s) 0323 { 0324 QFontMetrics fm(m_font); 0325 QString str = m_text.at(m_cursorPos.y()-1); 0326 QString newStr; 0327 0328 if ((int)str.length()>m_cursorPos.x()) 0329 { 0330 newStr = str.left(m_cursorPos.x()) + s + str.right(str.length() - (m_cursorPos.x())); 0331 } 0332 else 0333 { 0334 newStr = str.left(m_cursorPos.x()) + s; 0335 } 0336 0337 m_text.removeAt(m_cursorPos.y()-1); 0338 m_text.insert(m_cursorPos.y()-1,newStr); 0339 m_cursorPos.setX(m_cursorPos.x()+s.length()); 0340 setTextSize(); 0341 } 0342 0343 /** This will delete a character behind the cursor */ 0344 void CMapText::deleteChar(void) 0345 { 0346 0347 QString str = m_text.at(m_cursorPos.y()-1); 0348 if ((int)str.length()>m_cursorPos.x()) 0349 { 0350 str.remove(m_cursorPos.x(),1); 0351 m_text.removeAt(m_cursorPos.y()-1); 0352 m_text.insert(m_cursorPos.y()-1,str); 0353 } 0354 else 0355 { 0356 if (m_cursorPos.y() < (int)m_text.count()) 0357 { 0358 QString str2 = m_text.at(m_cursorPos.y()); 0359 m_text.removeAt(m_cursorPos.y()); 0360 m_text.removeAt(m_cursorPos.y()-1); 0361 m_text.insert(m_cursorPos.y()-1,str + str2); 0362 } 0363 } 0364 } 0365 0366 /** This will delete a character infront of the cursor */ 0367 void CMapText::backspace(void) 0368 { 0369 QFontMetrics fm(m_font); 0370 0371 if (m_cursorPos.x()==0) 0372 { 0373 if (m_cursorPos.y()>1) 0374 { 0375 QString str = m_text.at(m_cursorPos.y()-1); 0376 m_text.removeAt(m_cursorPos.y()-1); 0377 QString str2 = m_text.at(m_cursorPos.y()-2); 0378 m_text.removeAt(m_cursorPos.y()-2); 0379 m_text.insert(m_cursorPos.y()-2,str2 + str); 0380 setCursor(QPoint(str2.length(),m_cursorPos.y()-1)); 0381 } 0382 } 0383 else 0384 { 0385 QString str = m_text.at(m_cursorPos.y()-1); 0386 if (str.length()>0) 0387 { 0388 str.remove(m_cursorPos.x()-1,1); 0389 m_text.removeAt(m_cursorPos.y()-1); 0390 m_text.insert(m_cursorPos.y()-1,str); 0391 m_cursorPos.setX(m_cursorPos.x()-1); 0392 setActualCursorPosition(); 0393 /* 0394 for (char *s = text.frist();s!=0; s= text.next()) 0395 { 0396 } 0397 if (getWidth() == oldWidth()) 0398 */ 0399 } 0400 } 0401 } 0402 0403 /** this will insert a caridge return at the cursor */ 0404 void CMapText::insertCR(void) 0405 { 0406 QString str = m_text.at(m_cursorPos.y()-1); 0407 m_text.removeAt(m_cursorPos.y()-1); 0408 m_text.insert(m_cursorPos.y()-1,str.left(m_cursorPos.x())); 0409 m_text.insert(m_cursorPos.y(),str.right(str.length() - m_cursorPos.x())); 0410 m_cursorPos.setX(0); 0411 m_cursorPos.setY(m_cursorPos.y()+1); 0412 setActualCursorPosition(); 0413 } 0414 0415 /** Move the cursor up */ 0416 void CMapText::cursorUp(void) 0417 { 0418 if (m_cursorPos.y()>1) 0419 { 0420 QFontMetrics fm(m_font); 0421 int y = m_cursorOffset.y()-fm.height()-fm.height(); 0422 setCursor(convertOffsetToCursor(QPoint(m_cursorOffset.x(),y))); 0423 } 0424 } 0425 0426 /** Move the cursor down */ 0427 void CMapText::cursorDown(void) 0428 { 0429 if (m_cursorPos.y()<(int)m_text.count()) 0430 { 0431 QFontMetrics fm(m_font); 0432 int y = m_cursorOffset.y(); 0433 0434 QPoint p = convertOffsetToCursor(QPoint(m_cursorOffset.x(),y)); 0435 0436 setCursor(p); 0437 } 0438 } 0439 0440 /** Move the cursor right */ 0441 void CMapText::cursorRight(void) 0442 { 0443 int x = m_cursorPos.x(); 0444 QString str = m_text.at(m_cursorPos.y()-1); 0445 if (x<(int)str.length()) 0446 { 0447 m_cursorPos.setX(x+1); 0448 setActualCursorPosition(); 0449 } 0450 } 0451 0452 /** Move the cursor left */ 0453 void CMapText::cursorLeft(void) 0454 { 0455 int x = m_cursorPos.x(); 0456 if (x>0) 0457 { 0458 m_cursorPos.setX(x-1); 0459 setActualCursorPosition(); 0460 } 0461 } 0462 0463 /** Move the cursor to the end */ 0464 void CMapText::cursorEnd(void) 0465 { 0466 QString str = m_text.at(m_cursorPos.y()-1); 0467 m_cursorPos.setX(str.length()); 0468 setActualCursorPosition(); 0469 } 0470 0471 0472 /** This method is called to restore the orginal text of the element. When editing, if 0473 * esc is pressed, then this method is called to restore the text to the value it had 0474 * before editing */ 0475 void CMapText::restoreText(void) 0476 { 0477 setText(m_orgText); 0478 } 0479 0480 /** This used internaly to calculate the offset of the cursor */ 0481 void CMapText::setActualCursorPosition(void) 0482 { 0483 QFontMetrics fm(m_font); 0484 int y = fm.height() * m_cursorPos.y(); 0485 int x = 0; 0486 if ((m_text.count() >= m_cursorPos.y()) && (m_cursorPos.y() > 0)) { 0487 QString s = m_text.at(m_cursorPos.y()-1); 0488 x = fm.horizontalAdvance(s.left(m_cursorPos.x())); 0489 } 0490 0491 m_cursorOffset.setX(x); 0492 m_cursorOffset.setY(y); 0493 } 0494 0495 /** This is used to get the cursor position 0496 * @return The cursor position 0497 */ 0498 QPoint CMapText::getCursor(void) 0499 { 0500 return m_cursorPos; 0501 } 0502 0503 /** This method is over ridden from CMapElement::paint. It is used 0504 * draw the text on the map. 0505 * @param p A pointer to the paint device the text is to be drawn on 0506 */ 0507 void CMapText::setColor(QColor color) 0508 { 0509 m_col = color; 0510 } 0511 0512 /** This is used to return the color of the text 0513 * @return The color of the text 0514 */ 0515 QColor CMapText::getColor(void) 0516 { 0517 return m_col; 0518 } 0519 0520 /** Used to set the font of the text. This font is used when drawing the text. 0521 * @param f The required font of the text 0522 */ 0523 void CMapText::setFont(QFont f) 0524 { 0525 m_font = f; 0526 0527 setTextSize(); 0528 } 0529 0530 /** This menthod will return the font used to display the text 0531 * @return The font used to display text 0532 */ 0533 QFont CMapText::getFont(void) 0534 { 0535 return m_font; 0536 } 0537 0538 /** Gets the text of the text element 0539 * @return The text 0540 */ 0541 QString CMapText::getText(void) 0542 { 0543 return m_text.join ("\n"); 0544 } 0545 0546 /** Sets the text of the text element 0547 * @param str The new string that the text element should be set to 0548 */ 0549 void CMapText::setText(QString str) 0550 { 0551 stringToList(str,&m_text); 0552 0553 setTextSize(); 0554 } 0555 0556 /** This method is used to convert a text string into a text list 0557 * @param str The string of text 0558 * @param textList The list to add the text to 0559 */ 0560 void CMapText::stringToList(QString str,QStringList *textList) 0561 { 0562 textList->clear(); 0563 if (str.isEmpty()) 0564 { 0565 textList->append(""); 0566 } 0567 else 0568 { 0569 int oldIndex = 0; 0570 int index = str.indexOf('\n'); 0571 0572 while(index!=-1) 0573 { 0574 textList->append(str.mid(oldIndex,index-oldIndex )); 0575 0576 oldIndex = index + 1; 0577 index = str.indexOf('\n', oldIndex); 0578 } 0579 0580 textList->append(str.right(str.length()-oldIndex)); 0581 } 0582 0583 } 0584 0585 /** This method is used to update the size of the text element */ 0586 void CMapText::setTextSize(void) 0587 { 0588 QFontMetrics fm(m_font); 0589 int width = 0; 0590 int height = 0; 0591 0592 for (QStringList::iterator it = m_text.begin(); it != m_text.end(); ++it) 0593 { 0594 if (fm.horizontalAdvance(*it)>width) 0595 width = fm.horizontalAdvance(*it); 0596 0597 height+=fm.height(); 0598 } 0599 0600 if (height < 10) 0601 height = 10; 0602 0603 if (width < 10) 0604 width = 10; 0605 0606 QRect rect = getRect(); 0607 rect.setWidth(width); 0608 rect.setHeight(height); 0609 setRect(rect); 0610 setActualCursorPosition(); 0611 } 0612 0613 void CMapText::updateLinkElements(void) 0614 { 0615 if (m_linkElement) 0616 { 0617 if (m_linkElement->getElementType()==ROOM) 0618 { 0619 CMapRoom *room = (CMapRoom *)m_linkElement; 0620 room->setLabel(QString(m_text.first())); 0621 room->setLabelPosition(room->getLabelPosition()); 0622 } 0623 if (m_linkElement->getElementType()==ZONE) 0624 { 0625 CMapZone *zone = (CMapZone *)m_linkElement; 0626 zone->setLabel(QString(m_text.first())); 0627 zone->setLabelPosition(zone->getLabelPosition()); 0628 } 0629 } 0630 } 0631 0632 /** This is used to create a new copy of the text element and return 0633 * a pointer to the new text element 0634 * @return A pointer to the copy of the text element. 0635 */ 0636 CMapElement *CMapText::copy(void) 0637 { 0638 CMapText *newText = new CMapText (getText(),getFont(),getColor(),getManager(),getLowPos(),getLevel()); 0639 return newText; 0640 } 0641 0642 /** Used to load the properties of the element from a list of properties */ 0643 void CMapText::loadProperties(KConfigGroup properties) 0644 { 0645 CMapElement::loadProperties(properties); 0646 0647 setText(properties.readEntry("Text",getText())); 0648 QColor color=getColor(); 0649 color=properties.readEntry("Color",color); 0650 setColor(color); 0651 QFont font = getFont(); 0652 font = properties.readEntry("Font",font); 0653 setFont(font); 0654 0655 if (properties.hasKey("LinkedType")) 0656 { 0657 CMapLevel *level = getManager()->findLevel(properties.readEntry("LinkedLevel",-1)); 0658 0659 if (level) 0660 { 0661 elementTyp typ = (elementTyp)properties.readEntry("LinkedType",(int)OTHER); 0662 if (typ == ROOM) 0663 { 0664 CMapRoom *room = level->findRoom(properties.readEntry("LinkedID",-1)); 0665 room->setLabelPosition((CMapRoom::labelPosTyp)properties.readEntry("LabelPos",(int)CMapRoom::HIDE),this); 0666 } 0667 } 0668 } 0669 0670 setTextID(properties.readEntry("TextID",m_ID)); 0671 } 0672 0673 /** Used to save the properties of the element to a list of properties */ 0674 void CMapText::saveProperties(KConfigGroup properties) 0675 { 0676 CMapElement::saveProperties(properties); 0677 properties.writeEntry("Text",getText()); 0678 properties.writeEntry("Color",getColor()); 0679 properties.writeEntry("Font",getFont()); 0680 properties.writeEntry("TextID",getTextID()); 0681 0682 if (m_linkElement) 0683 { 0684 properties.writeEntry("LinkedType",(int)m_linkElement->getElementType()); 0685 0686 if (m_linkElement->getElementType()==ROOM) 0687 { 0688 CMapRoom *room = (CMapRoom *)m_linkElement; 0689 properties.writeEntry("LinkedLevel",room->getLevel()->getLevelID()); 0690 properties.writeEntry("LinkedID",room->getRoomID()); 0691 properties.writeEntry("LabelPos",(int)room->getLabelPosition()); 0692 } 0693 } 0694 } 0695 0696 /** Used to save the element as an XML object 0697 * @param properties The XML object to save the properties too 0698 * @param doc The XML Document */ 0699 void CMapText::saveQDomElement(QDomDocument *doc,QDomElement *properties) 0700 { 0701 writeColor(doc,properties,"Color",getColor()); 0702 0703 CMapElement::saveQDomElement(doc,properties); 0704 properties->setAttribute("Text",getText()); 0705 properties->setAttribute("Font",getFont().toString()); 0706 properties->setAttribute("TextID",getTextID()); 0707 } 0708 0709 /** Used to load the properties from a XML object 0710 * @param properties The XML object to load the properties from */ 0711 void CMapText::loadQDomElement(QDomElement *properties) 0712 { 0713 CMapElement::loadQDomElement(properties); 0714 0715 setColor(readColor(properties,"Color",getColor())); 0716 setText(properties->attribute("Text",getText())); 0717 setTextID(readInt(properties,"TextID",getTextID())); 0718 0719 QFont font; 0720 font.fromString(properties->attribute("Font")); 0721 setFont(font); 0722 } 0723 0724 void CMapText::setTextID(unsigned int id) 0725 { 0726 if (id > getZone()->m_text_id_count) 0727 getZone()->m_text_id_count = id; 0728 0729 m_ID = id; 0730 } 0731