File indexing completed on 2024-04-21 04:03:10

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