1 /*-*- c++ -*-********************************************************
2 * wxFTCanvas: a canvas for editing formatted text *
4 * (C) 1998 by Karsten Ballüder (Ballueder@usa.net) *
7 *******************************************************************/
10 - each Object knows its size and how to draw itself
11 - the list is responsible for calculating positions
12 - the draw coordinates for each object are the top left corner
13 - coordinates only get calculated when things get redrawn
14 - during redraw each line gets iterated over twice, first just
15 calculating baselines and positions, second to actually draw it
16 - the cursor position is the position before an object, i.e. if the
17 buffer starts with a text-object, cursor 0,0 is just before the
22 #pragma implementation "wxllist.h"
28 #define VAR(x) cerr << #x"=" << x << endl;
29 #define DBG_POINT(p) cerr << #p << ": " << p.x << ',' << p.y << endl
30 #define TRACE(f) cerr << #f":" << endl;
33 static const char *_t
[] = { "invalid", "text", "cmd", "icon",
37 wxLayoutObjectBase::Debug(void)
40 cerr
<< _t
[GetType()] << ": size=" << GetSize(&bl
).x
<< ","
41 << GetSize(&bl
).y
<< " bl=" << bl
;
45 //-------------------------- wxLayoutObjectText
47 wxLayoutObjectText::wxLayoutObjectText(const wxString
&txt
)
56 wxLayoutObjectText::GetSize(CoordType
*baseLine
) const
58 if(baseLine
) *baseLine
= m_BaseLine
;
59 return wxPoint(m_Width
, m_Height
);
63 wxLayoutObjectText::Draw(wxDC
&dc
, wxPoint position
, CoordType baseLine
,
67 dc
.GetTextExtent(m_Text
,&m_Width
, &m_Height
, &descent
);
68 //FIXME: wxGTK does not set descent to a descent value yet.
70 descent
= (2*m_Height
)/10; // crude fix
71 m_BaseLine
= m_Height
- descent
;
72 position
.y
+= baseLine
-m_BaseLine
;
74 dc
.DrawText(m_Text
,position
.x
,position
.y
);
76 // dc.DrawRectangle(position.x, position.y, m_Width, m_Height);
82 wxLayoutObjectText::Debug(void)
84 wxLayoutObjectBase::Debug();
85 cerr
<< " `" << m_Text
<< '\'';
89 //-------------------------- wxLayoutObjectIcon
91 wxLayoutObjectIcon::wxLayoutObjectIcon(wxIcon
*icon
)
97 wxLayoutObjectIcon::Draw(wxDC
&dc
, wxPoint position
, CoordType baseLine
,
100 position
.y
+= baseLine
- m_Icon
->GetHeight();
102 dc
.DrawIcon(m_Icon
,position
.x
,position
.y
);
106 wxLayoutObjectIcon::GetSize(CoordType
*baseLine
) const
109 *baseLine
= m_Icon
->GetHeight();
110 return wxPoint(m_Icon
->GetWidth(), m_Icon
->GetHeight());
113 //-------------------------- wxLayoutObjectCmd
116 wxLayoutObjectCmd::wxLayoutObjectCmd(int size
, int family
, int style
, int
117 weight
, bool underline
,
118 wxColour
const *fg
, wxColour
const *bg
)
121 m_font
= new wxFont(size
,family
,style
,weight
,underline
);
126 wxLayoutObjectCmd::~wxLayoutObjectCmd()
132 wxLayoutObjectCmd::GetStyle(void) const
134 wxLayoutStyleInfo
*si
= new wxLayoutStyleInfo();
137 si
->size
= m_font
->GetPointSize();
138 si
->family
= m_font
->GetFamily();
139 si
->style
= m_font
->GetStyle();
140 si
->underline
= m_font
->GetUnderlined();
141 si
->weight
= m_font
->GetWeight();
143 si
->fg_red
= m_ColourFG
->Red();
144 si
->fg_green
= m_ColourFG
->Green();
145 si
->fg_blue
= m_ColourFG
->Blue();
146 si
->bg_red
= m_ColourBG
->Red();
147 si
->bg_green
= m_ColourBG
->Green();
148 si
->bg_blue
= m_ColourBG
->Blue();
154 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint position
, CoordType lineHeight
,
158 // this get called even when draw==false, so that recalculation
159 // uses right font sizes
162 dc
.SetTextForeground(*m_ColourFG
);
164 dc
.SetTextBackground(*m_ColourBG
);
167 //-------------------------- wxwxLayoutList
169 wxLayoutList::wxLayoutList()
174 wxLayoutList::~wxLayoutList()
180 wxLayoutList::LineBreak(void)
182 Insert(new wxLayoutObjectLineBreak
);
183 m_CursorPosition
.x
= 0; m_CursorPosition
.y
++;
187 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
188 int underline
, wxColour
const *fg
,
191 if(family
!= -1) m_FontFamily
= family
;
192 if(size
!= -1) m_FontPtSize
= size
;
193 if(style
!= -1) m_FontStyle
= style
;
194 if(weight
!= -1) m_FontWeight
= weight
;
195 if(underline
!= -1) m_FontUnderline
= underline
;
197 if(fg
!= NULL
) m_ColourFG
= fg
;
198 if(bg
!= NULL
) m_ColourBG
= bg
;
201 new wxLayoutObjectCmd(m_FontPtSize
,m_FontFamily
,m_FontStyle
,m_FontWeight
,m_FontUnderline
,
202 m_ColourFG
, m_ColourBG
));
206 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
207 int underline
, char const *fg
, char const *bg
)
214 cfg
= wxTheColourDatabase
->FindColour(fg
);
216 cbg
= wxTheColourDatabase
->FindColour(bg
);
218 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
222 /// for access by wxLayoutWindow:
224 wxLayoutList::GetSize(CoordType
*max_x
, CoordType
*max_y
,
225 CoordType
*lineHeight
)
227 wxASSERT(max_x
); wxASSERT(max_y
); wxASSERT(lineHeight
);
230 *lineHeight
= m_LineHeight
;
234 wxLayoutList::Draw(wxDC
&dc
, bool findObject
, wxPoint
const &findCoords
)
236 wxLayoutObjectList::iterator i
;
238 // in case we need to look for an object
239 wxLayoutObjectBase
*foundObject
= NULL
;
241 // first object in current line
242 wxLayoutObjectList::iterator headOfLine
;
244 // do we need to recalculate current line?
245 bool recalculate
= false;
247 // do we calculate or draw? Either true or false.
249 // drawing parameters:
250 wxPoint position
= wxPoint(0,0);
251 wxPoint position_HeadOfLine
;
252 CoordType baseLine
= m_FontPtSize
;
253 CoordType baseLineSkip
= (12 * baseLine
)/10;
255 // we trace the objects' cursor positions so we can draw the cursor
256 wxPoint cursor
= wxPoint(0,0);
257 // the cursor position inside the object
258 CoordType cursorOffset
= 0;
259 // object under cursor
260 wxLayoutObjectList::iterator cursorObject
= FindCurrentObject(&cursorOffset
);
262 // queried from each object:
263 wxPoint size
= wxPoint(0,0);
264 CoordType objBaseLine
= baseLine
;
265 wxLayoutObjectType type
;
267 VAR(findObject
); VAR(findCoords
.x
); VAR(findCoords
.y
);
268 // if the cursorobject is a cmd, we need to find the first
270 while(cursorObject
!= end()
271 && (*cursorObject
)->GetType() == WXLO_TYPE_CMD
)
274 headOfLine
= begin();
275 position_HeadOfLine
= position
;
277 // setting up the default:
278 dc
.SetTextForeground( *wxBLACK
);
279 dc
.SetFont( *wxNORMAL_FONT
);
281 // we calculate everything for drawing a line, then rewind to the
282 // begin of line and actually draw it
290 type
= (*i
)->GetType();
292 // to initialise sizes of objects, we need to call Draw
293 (*i
)->Draw(dc
, position
, baseLine
, draw
);
295 // update coordinates for next object:
296 size
= (*i
)->GetSize(&objBaseLine
);
297 if(findObject
&& draw
) // we need to look for an object
299 if(findCoords
.y
>= position
.y
300 && findCoords
.y
<= position
.y
+size
.y
301 && findCoords
.x
>= position
.x
302 && findCoords
.x
<= position
.x
+size
.x
)
305 findObject
= false; // speeds things up
309 if(m_Editable
&& draw
&& i
== cursorObject
)
311 if(type
== WXLO_TYPE_TEXT
) // special treatment
313 long descent
= 0l; long width
, height
;
314 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
315 wxString str
= tobj
->GetText();
316 VAR(m_CursorPosition
.x
); VAR(cursor
.x
);
317 str
= str
.substr(0, cursorOffset
);
319 dc
.GetTextExtent(str
, &width
,&height
, &descent
);
321 VAR(width
); VAR(descent
);
322 dc
.DrawLine(position
.x
+width
,
323 position
.y
+(baseLineSkip
-height
),
324 position
.x
+width
, position
.y
+baseLineSkip
);
328 if(type
== WXLO_TYPE_LINEBREAK
)
329 dc
.DrawLine(0, position
.y
+baseLineSkip
, 0, position
.y
+2*baseLineSkip
);
335 dc
.DrawLine(position
.x
, position
.y
, position
.x
, position
.y
+baseLineSkip
);
337 dc
.DrawLine(position
.x
, position
.y
, position
.x
, position
.y
+size
.y
);
340 dc
.DrawRectangle(position
.x
, position
.y
, size
.x
, size
.y
);
345 // calculate next object's position:
346 position
.x
+= size
.x
;
348 // do we need to increase the line's height?
349 if(size
.y
> baseLineSkip
)
351 baseLineSkip
= size
.y
;
354 if(objBaseLine
> baseLine
)
356 baseLine
= objBaseLine
;
360 // now check whether we have finished handling this line:
361 if(type
== WXLO_TYPE_LINEBREAK
|| i
== tail())
363 if(recalculate
) // do this line again
365 position
.x
= position_HeadOfLine
.x
;
370 if(! draw
) // finished calculating sizes
371 { // do this line again, this time drawing it
372 position
= position_HeadOfLine
;
377 else // we have drawn a line, so continue calculating next one
381 if(position
.x
+size
.x
> m_MaxX
)
382 m_MaxX
= position
.x
+size
.x
;
383 // is it a linebreak?
384 if(type
== WXLO_TYPE_LINEBREAK
|| i
== tail())
387 position
.y
+= baseLineSkip
;
388 baseLine
= m_FontPtSize
;
389 baseLineSkip
= (12 * baseLine
)/10;
392 position_HeadOfLine
= position
;
402 wxLayoutList::Debug(void)
405 wxLayoutObjectList::iterator i
;
408 "------------------------debug start-------------------------" << endl
;
409 for(i
= begin(); i
!= end(); i
++)
415 "-----------------------debug end----------------------------"
417 // show current object:
419 << m_CursorPosition
.x
<< ','
420 << m_CursorPosition
.y
;
422 i
= FindCurrentObject(&offs
);
423 cerr
<< " line length: " << GetLineLength(i
) << " ";
426 cerr
<< "<<no object found>>" << endl
;
427 return; // FIXME we should set cursor position to maximum allowed
430 if((*i
)->GetType() == WXLO_TYPE_TEXT
)
432 cerr
<< " \"" << ((wxLayoutObjectText
*)(*i
))->GetText() << "\", offs: "
436 cerr
<< ' ' << _t
[(*i
)->GetType()] << endl
;
441 /******************** editing stuff ********************/
443 wxLayoutObjectList::iterator
444 wxLayoutList::FindObjectCursor(wxPoint
const &cpos
, CoordType
*offset
)
446 wxPoint cursor
= wxPoint(0,0); // runs along the objects
448 wxLayoutObjectList::iterator i
;
451 cerr
<< "Looking for object at " << cpos
.x
<< ',' << cpos
.y
<<
454 for(i
= begin(); i
!= end() && cursor
.y
<= cpos
.y
; i
++)
457 if((*i
)->GetType() == WXLO_TYPE_LINEBREAK
)
459 if(cpos
.y
== cursor
.y
)
463 *offset
= (*i
)->CountPositions();
466 cursor
.x
= 0; cursor
.y
++;
469 cursor
.x
+= (width
= (*i
)->CountPositions());
470 if(cursor
.y
== cpos
.y
&& (cursor
.x
> cpos
.x
||
471 ((*i
)->GetType() != WXLO_TYPE_CMD
&& cursor
.x
== cpos
.x
))
475 *offset
= cpos
.x
-(cursor
.x
-width
); // 0==cursor before
478 cerr
<< " found object at " << cursor
.x
-width
<< ',' <<
479 cursor
.y
<< ", type:" << _t
[(*i
)->GetType()] <<endl
;
485 cerr
<< " not found" << endl
;
487 return end(); // not found
490 wxLayoutObjectList::iterator
491 wxLayoutList::FindCurrentObject(CoordType
*offset
)
493 wxLayoutObjectList::iterator obj
= end();
495 obj
= FindObjectCursor(m_CursorPosition
, offset
);
496 if(obj
== end()) // not ideal yet
499 if(obj
!= end()) // tail really exists
500 *offset
= (*obj
)->CountPositions(); // at the end of it
506 wxLayoutList::MoveCursor(int dx
, int dy
)
508 CoordType offs
, lineLength
;
509 wxLayoutObjectList::iterator i
;
512 if(dy
> 0 && m_CursorPosition
.y
< m_MaxLine
)
513 m_CursorPosition
.y
+= dy
;
514 else if(dy
< 0 && m_CursorPosition
.y
> 0)
515 m_CursorPosition
.y
+= dy
; // dy is negative
516 if(m_CursorPosition
.y
< 0)
517 m_CursorPosition
.y
= 0;
518 else if (m_CursorPosition
.y
> m_MaxLine
)
519 m_CursorPosition
.y
= m_MaxLine
;
523 i
= FindCurrentObject(&offs
);
524 lineLength
= GetLineLength(i
);
525 if(m_CursorPosition
.x
< lineLength
)
527 m_CursorPosition
.x
++;
533 if(m_CursorPosition
.y
< m_MaxLine
)
535 m_CursorPosition
.y
++;
536 m_CursorPosition
.x
= 0;
540 break; // cannot move there
545 if(m_CursorPosition
.x
> 0)
547 m_CursorPosition
.x
--;
552 if(m_CursorPosition
.y
> 0)
554 m_CursorPosition
.y
--;
555 m_CursorPosition
.x
= 0;
556 i
= FindCurrentObject(&offs
);
557 lineLength
= GetLineLength(i
);
558 m_CursorPosition
.x
= lineLength
;
563 break; // cannot move left any more
567 i
= FindCurrentObject(&offs
);
568 lineLength
= GetLineLength(i
);
569 if(m_CursorPosition
.x
> lineLength
)
570 m_CursorPosition
.x
= lineLength
;
573 i
= FindCurrentObject(&offs
);
575 << m_CursorPosition
.x
<< ','
576 << m_CursorPosition
.y
;
580 cerr
<< "<<no object found>>" << endl
;
581 return; // FIXME we should set cursor position to maximum allowed
584 if((*i
)->GetType() == WXLO_TYPE_TEXT
)
586 cerr
<< " \"" << ((wxLayoutObjectText
*)(*i
))->GetText() << "\", offs: "
590 cerr
<< ' ' << _t
[(*i
)->GetType()] << endl
;
595 wxLayoutList::Delete(CoordType count
)
605 wxLayoutObjectList::iterator i
;
609 i
= FindCurrentObject(&offs
);
613 cerr
<< "trying to delete: " << _t
[(*i
)->GetType()] << endl
;
615 if((*i
)->GetType() == WXLO_TYPE_LINEBREAK
)
617 if((*i
)->GetType() == WXLO_TYPE_TEXT
)
619 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
620 len
= tobj
->CountPositions();
621 // If we find the end of a text object, this means that we
622 // have to delete from the object following it.
626 if((*i
)->GetType() == WXLO_TYPE_TEXT
)
628 offs
= 0; // delete from begin of next string
629 tobj
= (wxLayoutObjectText
*)*i
;
630 len
= tobj
->CountPositions();
638 if(len
<= count
) // delete this object
647 tobj
->GetText().erase(offs
,len
);
648 return; // we are done
651 else // delete the object
653 len
= (*i
)->CountPositions();
654 erase(i
); // after this, i is the iterator for the following object
661 while(count
&& i
!= end());
665 wxLayoutList::Insert(wxLayoutObjectBase
*obj
)
669 wxLayoutObjectList::iterator i
= FindCurrentObject(&offs
);
677 // do we have to split a text object?
678 if((*i
)->GetType() == WXLO_TYPE_TEXT
&& offs
!= 0 && offs
!= (*i
)->CountPositions())
680 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
682 cerr
<< "text: '" << tobj
->GetText() << "'" << endl
;
685 wxString left
= tobj
->GetText().substr(0,offs
); // get part before cursor
687 tobj
->GetText() = tobj
->GetText().substr(offs
,(*i
)->CountPositions()-offs
); // keeps the right half
688 VAR(tobj
->GetText());
690 insert(i
,new wxLayoutObjectText(left
)); // inserts before
694 wxLayoutObjectList::iterator j
= i
; // we want to apend after this object
702 m_CursorPosition
.x
+= obj
->CountPositions();
703 if(obj
->GetType() == WXLO_TYPE_LINEBREAK
)
708 wxLayoutList::Insert(wxString
const &text
)
716 wxLayoutObjectList::iterator i
= FindCurrentObject(&offs
);
718 if(i
!= end() && (*i
)->GetType() == WXLO_TYPE_TEXT
)
719 { // insert into an existing text object:
720 TRACE(inserting into existing object
);
721 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
722 tobj
->GetText().insert(offs
,text
);
726 // check whether the previous object is text:
727 wxLayoutObjectList::iterator j
= i
;
729 TRACE(checking previous object
);
730 if(0 && j
!= end() && (*j
)->GetType() == WXLO_TYPE_TEXT
)
732 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
733 tobj
->GetText()+=text
;
735 else // insert a new text object
737 TRACE(creating
new object
);
738 Insert(new wxLayoutObjectText(text
)); //FIXME not too optimal, slow
739 return; // position gets incremented in Insert(obj)
742 m_CursorPosition
.x
+= strlen(text
.c_str());
746 wxLayoutList::GetLineLength(wxLayoutObjectList::iterator i
)
753 // search backwards for beginning of line:
754 while(i
!= begin() && (*i
)->GetType() != WXLO_TYPE_LINEBREAK
)
756 if((*i
)->GetType() == WXLO_TYPE_LINEBREAK
)
758 // now we can start counting:
759 while(i
!= end() && (*i
)->GetType() != WXLO_TYPE_LINEBREAK
)
761 len
+= (*i
)->CountPositions();
768 wxLayoutList::Clear(void)
770 wxLayoutObjectList::iterator i
= begin();
772 while(i
!= end()) // == while valid
777 m_FontUnderline
= false;
778 m_FontFamily
= wxDEFAULT
;
779 m_FontStyle
= wxNORMAL
;
780 m_FontWeight
= wxNORMAL
;
781 m_ColourFG
= wxTheColourDatabase
->FindColour("BLACK");
782 m_ColourBG
= wxTheColourDatabase
->FindColour("WHITE");
784 m_Position
= wxPoint(0,0);
785 m_CursorPosition
= wxPoint(0,0);
787 m_LineHeight
= (12*m_FontPtSize
)/10;
788 m_MaxX
= 0; m_MaxY
= 0;