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"
29 #include <wx/postscrp.h>
32 #define BASELINESTRETCH 12
35 static const char *_t
[] = { "invalid", "text", "cmd", "icon",
39 wxLayoutObjectBase::Debug(void)
42 cerr
<< _t
[GetType()] << ": size=" << GetSize(&bl
).x
<< ","
43 << GetSize(&bl
).y
<< " bl=" << bl
;
46 # define VAR(x) cerr << #x"=" << x << endl;
47 # define DBG_POINT(p) cerr << #p << ": " << p.x << ',' << p.y << endl
48 # define TRACE(f) cerr << #f":" << endl;
55 //-------------------------- wxLayoutObjectText
57 wxLayoutObjectText::wxLayoutObjectText(const String
&txt
)
66 wxLayoutObjectText::GetSize(CoordType
*baseLine
) const
68 if(baseLine
) *baseLine
= m_BaseLine
;
69 return wxPoint(m_Width
, m_Height
);
73 wxLayoutObjectText::Draw(wxDC
&dc
, wxPoint position
, CoordType baseLine
,
77 dc
.GetTextExtent(Str(m_Text
),&m_Width
, &m_Height
, &descent
);
78 //FIXME: wxGTK does not set descent to a descent value yet.
80 descent
= (2*m_Height
)/10; // crude fix
81 m_BaseLine
= m_Height
- descent
;
82 position
.y
+= baseLine
-m_BaseLine
;
84 dc
.DrawText(Str(m_Text
),position
.x
,position
.y
);
85 # ifdef WXLAYOUT_DEBUG
86 // dc.DrawRectangle(position.x, position.y, m_Width, m_Height);
92 wxLayoutObjectText::Debug(void)
94 wxLayoutObjectBase::Debug();
95 cerr
<< " `" << m_Text
<< '\'';
99 //-------------------------- wxLayoutObjectIcon
101 wxLayoutObjectIcon::wxLayoutObjectIcon(wxIcon
*icon
)
107 wxLayoutObjectIcon::Draw(wxDC
&dc
, wxPoint position
, CoordType baseLine
,
110 position
.y
+= baseLine
- m_Icon
->GetHeight();
112 dc
.DrawIcon(m_Icon
,position
.x
,position
.y
);
116 wxLayoutObjectIcon::GetSize(CoordType
*baseLine
) const
119 *baseLine
= m_Icon
->GetHeight();
120 return wxPoint(m_Icon
->GetWidth(), m_Icon
->GetHeight());
123 //-------------------------- wxLayoutObjectCmd
126 wxLayoutObjectCmd::wxLayoutObjectCmd(int size
, int family
, int style
, int
127 weight
, bool underline
,
128 wxColour
const *fg
, wxColour
const *bg
)
131 m_font
= new wxFont(size
,family
,style
,weight
,underline
);
136 wxLayoutObjectCmd::~wxLayoutObjectCmd()
142 wxLayoutObjectCmd::GetStyle(void) const
144 wxLayoutStyleInfo
*si
= new wxLayoutStyleInfo();
147 si
->size
= m_font
->GetPointSize();
148 si
->family
= m_font
->GetFamily();
149 si
->style
= m_font
->GetStyle();
150 si
->underline
= m_font
->GetUnderlined();
151 si
->weight
= m_font
->GetWeight();
153 si
->fg_red
= m_ColourFG
->Red();
154 si
->fg_green
= m_ColourFG
->Green();
155 si
->fg_blue
= m_ColourFG
->Blue();
156 si
->bg_red
= m_ColourBG
->Red();
157 si
->bg_green
= m_ColourBG
->Green();
158 si
->bg_blue
= m_ColourBG
->Blue();
164 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint position
, CoordType lineHeight
,
168 // this get called even when draw==false, so that recalculation
169 // uses right font sizes
172 dc
.SetTextForeground(*m_ColourFG
);
174 dc
.SetTextBackground(*m_ColourBG
);
177 //-------------------------- wxwxLayoutList
179 wxLayoutList::wxLayoutList()
181 m_DefaultSetting
= NULL
;
185 wxLayoutList::~wxLayoutList()
188 delete m_DefaultSetting
;
193 wxLayoutList::LineBreak(void)
195 Insert(new wxLayoutObjectLineBreak
);
196 m_CursorPosition
.x
= 0; m_CursorPosition
.y
++;
200 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
201 int underline
, wxColour
const *fg
,
204 if(family
!= -1) m_FontFamily
= family
;
205 if(size
!= -1) m_FontPtSize
= size
;
206 if(style
!= -1) m_FontStyle
= style
;
207 if(weight
!= -1) m_FontWeight
= weight
;
208 if(underline
!= -1) m_FontUnderline
= underline
;
210 if(fg
!= NULL
) m_ColourFG
= fg
;
211 if(bg
!= NULL
) m_ColourBG
= bg
;
214 new wxLayoutObjectCmd(m_FontPtSize
,m_FontFamily
,m_FontStyle
,m_FontWeight
,m_FontUnderline
,
215 m_ColourFG
, m_ColourBG
));
219 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
220 int underline
, char const *fg
, char const *bg
)
227 cfg
= wxTheColourDatabase
->FindColour(fg
);
229 cbg
= wxTheColourDatabase
->FindColour(bg
);
231 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
235 /// for access by wxLayoutWindow:
237 wxLayoutList::GetSize(CoordType
*max_x
, CoordType
*max_y
,
238 CoordType
*lineHeight
)
240 wxASSERT(max_x
); wxASSERT(max_y
); wxASSERT(lineHeight
);
243 *lineHeight
= m_LineHeight
;
247 wxLayoutList::Draw(wxDC
&dc
, bool findObject
, wxPoint
const &findCoords
)
249 wxLayoutObjectList::iterator i
;
251 // in case we need to look for an object
252 wxLayoutObjectBase
*foundObject
= NULL
;
254 // first object in current line
255 wxLayoutObjectList::iterator headOfLine
;
257 // do we need to recalculate current line?
258 bool recalculate
= false;
260 // do we calculate or draw? Either true or false.
262 // drawing parameters:
263 wxPoint position
= wxPoint(0,0);
264 wxPoint position_HeadOfLine
;
265 CoordType baseLine
= m_FontPtSize
;
266 CoordType baseLineSkip
= (BASELINESTRETCH
* baseLine
)/10;
268 // we trace the objects' cursor positions so we can draw the cursor
269 wxPoint cursor
= wxPoint(0,0);
270 // the cursor position inside the object
271 CoordType cursorOffset
= 0;
272 // object under cursor
273 wxLayoutObjectList::iterator cursorObject
= FindCurrentObject(&cursorOffset
);
275 // queried from each object:
276 wxPoint size
= wxPoint(0,0);
277 CoordType objBaseLine
= baseLine
;
278 wxLayoutObjectType type
;
281 wxLayoutObjectText
*tobj
= NULL
;
284 // this is needed for printing to a printer:
285 // only interesting for printer/PS output
286 int pageWidth
, pageHeight
; //GetSize() still needs int at the moment
289 int top
, bottom
, left
, right
;
294 dc
.IsKindOf(CLASSINFO(wxPrinterDC
)) ||
296 dc
.IsKindOf(CLASSINFO(wxPostScriptDC
)))
298 VAR(wxThePrintSetupData
);
300 dc
.GetSize(&pageWidth
, &pageHeight
);
301 dc
.StartDoc(_("Printing..."));
303 margins
.top
= (1*pageHeight
)/10; // 10%
304 margins
.bottom
= (9*pageHeight
)/10; // 90%
305 margins
.left
= (1*pageWidth
)/10;
306 margins
.right
= (9*pageWidth
)/10;
310 margins
.top
= 0; margins
.left
= 0;
314 position
.y
= margins
.right
;
315 position
.x
= margins
.left
;
317 VAR(findObject
); VAR(findCoords
.x
); VAR(findCoords
.y
);
318 // if the cursorobject is a cmd, we need to find the first
320 while(cursorObject
!= end()
321 && (*cursorObject
)->GetType() == WXLO_TYPE_CMD
)
324 headOfLine
= begin();
325 position_HeadOfLine
= position
;
327 // setting up the default:
328 dc
.SetTextForeground( *wxBLACK
);
329 dc
.SetTextBackground( *wxWHITE
);
330 dc
.SetBackgroundMode( wxSOLID
); // to enable setting of text background
331 dc
.SetFont( *wxNORMAL_FONT
);
334 //FIXME: who frees the brush, how long does it need to exist?
336 m_DefaultSetting
->Draw(dc
,wxPoint(0,0),0,true);
338 // we calculate everything for drawing a line, then rewind to the
339 // begin of line and actually draw it
347 type
= (*i
)->GetType();
349 // to initialise sizes of objects, we need to call Draw
350 (*i
)->Draw(dc
, position
, baseLine
, draw
);
352 // update coordinates for next object:
353 size
= (*i
)->GetSize(&objBaseLine
);
354 if(findObject
&& draw
) // we need to look for an object
356 if(findCoords
.y
>= position
.y
357 && findCoords
.y
<= position
.y
+size
.y
358 && findCoords
.x
>= position
.x
359 && findCoords
.x
<= position
.x
+size
.x
)
362 findObject
= false; // speeds things up
366 if(m_Editable
&& draw
&& i
== cursorObject
)
368 if(type
== WXLO_TYPE_TEXT
) // special treatment
370 long descent
= 0l; long width
, height
;
371 tobj
= (wxLayoutObjectText
*)*i
;
372 String str
= tobj
->GetText();
373 VAR(m_CursorPosition
.x
); VAR(cursor
.x
);
374 str
= str
.substr(0, cursorOffset
);
376 dc
.GetTextExtent(Str(str
), &width
,&height
, &descent
);
378 VAR(width
); VAR(descent
);
379 if(width
< 1) width
= 1;
380 dc
.DrawLine(position
.x
+width
,
381 position
.y
+(baseLineSkip
-height
),
382 position
.x
+width
, position
.y
+baseLineSkip
);
386 if(type
== WXLO_TYPE_LINEBREAK
)
387 dc
.DrawLine(0, position
.y
+baseLineSkip
, 0, position
.y
+2*baseLineSkip
);
393 dc
.DrawLine(position
.x
, position
.y
, position
.x
, position
.y
+baseLineSkip
);
395 dc
.DrawLine(position
.x
, position
.y
, position
.x
, position
.y
+size
.y
);
398 dc
.DrawRectangle(position
.x
, position
.y
, size
.x
, size
.y
);
403 // calculate next object's position:
404 position
.x
+= size
.x
;
405 if(position
.x
> m_MaxX
)
408 // do we need to increase the line's height?
409 if(size
.y
> baseLineSkip
)
411 baseLineSkip
= size
.y
;
414 if(objBaseLine
> baseLine
)
416 baseLine
= objBaseLine
;
420 // now check whether we have finished handling this line:
421 if(type
== WXLO_TYPE_LINEBREAK
|| i
== tail())
423 if(recalculate
) // do this line again
425 position
.x
= position_HeadOfLine
.x
;
430 if(! draw
) // finished calculating sizes
432 // if the this line needs to go onto a new page, we need
433 // to change pages before drawing it:
434 if(margins
.bottom
!= -1 && position
.y
> margins
.bottom
)
437 position_HeadOfLine
.y
= margins
.top
;
440 // do this line again, this time drawing it
441 position
= position_HeadOfLine
;
446 else // we have drawn a line, so continue calculating next one
450 // is it a linebreak?
451 if(type
== WXLO_TYPE_LINEBREAK
|| i
== tail())
453 position
.x
= margins
.left
;
454 position
.y
+= baseLineSkip
;
455 baseLine
= m_FontPtSize
;
456 objBaseLine
= baseLine
; // not all objects set it
457 baseLineSkip
= (BASELINESTRETCH
* baseLine
)/10;
460 position_HeadOfLine
= position
;
469 #ifdef WXLAYOUT_DEBUG
471 wxLayoutList::Debug(void)
474 wxLayoutObjectList::iterator i
;
477 "------------------------debug start-------------------------" << endl
;
478 for(i
= begin(); i
!= end(); i
++)
484 "-----------------------debug end----------------------------"
486 // show current object:
488 << m_CursorPosition
.x
<< ','
489 << m_CursorPosition
.y
;
491 i
= FindCurrentObject(&offs
);
492 cerr
<< " line length: " << GetLineLength(i
) << " ";
495 cerr
<< "<<no object found>>" << endl
;
496 return; // FIXME we should set cursor position to maximum allowed
499 if((*i
)->GetType() == WXLO_TYPE_TEXT
)
501 cerr
<< " \"" << ((wxLayoutObjectText
*)(*i
))->GetText() << "\", offs: "
505 cerr
<< ' ' << _t
[(*i
)->GetType()] << endl
;
510 /******************** editing stuff ********************/
512 // don't change this, I know how to optimise this and will do it real
515 wxLayoutObjectList::iterator
516 wxLayoutList::FindObjectCursor(wxPoint
const &cpos
, CoordType
*offset
)
518 wxPoint cursor
= wxPoint(0,0); // runs along the objects
520 wxLayoutObjectList::iterator i
;
522 #ifdef WXLAYOUT_DEBUG
523 cerr
<< "Looking for object at " << cpos
.x
<< ',' << cpos
.y
<<
526 for(i
= begin(); i
!= end() && cursor
.y
<= cpos
.y
; i
++)
529 if((*i
)->GetType() == WXLO_TYPE_LINEBREAK
)
531 if(cpos
.y
== cursor
.y
&& i
!= begin())
534 if(offset
) *offset
= i
!= end() ? (*i
)->CountPositions() : 0;
537 cursor
.x
= 0; cursor
.y
++;
540 cursor
.x
+= (width
= (*i
)->CountPositions());
541 if(cursor
.y
== cpos
.y
&& (cursor
.x
> cpos
.x
||
542 ((*i
)->GetType() != WXLO_TYPE_CMD
&& cursor
.x
== cpos
.x
))
546 *offset
= cpos
.x
-(cursor
.x
-width
); // 0==cursor before
548 #ifdef WXLAYOUT_DEBUG
549 cerr
<< " found object at " << cursor
.x
-width
<< ',' <<
550 cursor
.y
<< ", type:" << _t
[(*i
)->GetType()] <<endl
;
555 #ifdef WXLAYOUT_DEBUG
556 cerr
<< " not found" << endl
;
558 return end(); // not found
561 wxLayoutObjectList::iterator
562 wxLayoutList::FindCurrentObject(CoordType
*offset
)
564 wxLayoutObjectList::iterator obj
= end();
566 obj
= FindObjectCursor(m_CursorPosition
, offset
);
567 if(obj
== end()) // not ideal yet
570 if(obj
!= end()) // tail really exists
571 *offset
= (*obj
)->CountPositions(); // at the end of it
577 wxLayoutList::MoveCursor(int dx
, int dy
)
579 CoordType offs
, lineLength
;
580 wxLayoutObjectList::iterator i
;
583 if(dy
> 0 && m_CursorPosition
.y
< m_MaxLine
)
584 m_CursorPosition
.y
+= dy
;
585 else if(dy
< 0 && m_CursorPosition
.y
> 0)
586 m_CursorPosition
.y
+= dy
; // dy is negative
587 if(m_CursorPosition
.y
< 0)
588 m_CursorPosition
.y
= 0;
589 else if (m_CursorPosition
.y
> m_MaxLine
)
590 m_CursorPosition
.y
= m_MaxLine
;
594 i
= FindCurrentObject(&offs
);
595 lineLength
= GetLineLength(i
);
596 if(m_CursorPosition
.x
< lineLength
)
598 m_CursorPosition
.x
++;
604 if(m_CursorPosition
.y
< m_MaxLine
)
606 m_CursorPosition
.y
++;
607 m_CursorPosition
.x
= 0;
611 break; // cannot move there
616 if(m_CursorPosition
.x
> 0)
618 m_CursorPosition
.x
--;
623 if(m_CursorPosition
.y
> 0)
625 m_CursorPosition
.y
--;
626 m_CursorPosition
.x
= 0;
627 i
= FindCurrentObject(&offs
);
628 lineLength
= GetLineLength(i
);
629 m_CursorPosition
.x
= lineLength
;
634 break; // cannot move left any more
638 i
= FindCurrentObject(&offs
);
639 lineLength
= GetLineLength(i
);
640 if(m_CursorPosition
.x
> lineLength
)
641 m_CursorPosition
.x
= lineLength
;
643 #ifdef WXLAYOUT_DEBUG
644 i
= FindCurrentObject(&offs
);
646 << m_CursorPosition
.x
<< ','
647 << m_CursorPosition
.y
;
651 cerr
<< "<<no object found>>" << endl
;
652 return; // FIXME we should set cursor position to maximum allowed
655 if((*i
)->GetType() == WXLO_TYPE_TEXT
)
657 cerr
<< " \"" << ((wxLayoutObjectText
*)(*i
))->GetText() << "\", offs: "
661 cerr
<< ' ' << _t
[(*i
)->GetType()] << endl
;
666 wxLayoutList::Delete(CoordType count
)
676 wxLayoutObjectList::iterator i
;
680 i
= FindCurrentObject(&offs
);
683 #ifdef WXLAYOUT_DEBUG
684 cerr
<< "trying to delete: " << _t
[(*i
)->GetType()] << endl
;
686 if((*i
)->GetType() == WXLO_TYPE_LINEBREAK
)
688 if((*i
)->GetType() == WXLO_TYPE_TEXT
)
690 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
691 len
= tobj
->CountPositions();
692 // If we find the end of a text object, this means that we
693 // have to delete from the object following it.
697 if(i
!= end() && (*i
)->GetType() == WXLO_TYPE_TEXT
)
699 offs
= 0; // delete from begin of next string
700 tobj
= (wxLayoutObjectText
*)*i
;
701 len
= tobj
->CountPositions();
709 if(len
<= count
) // delete this object
718 tobj
->GetText().erase(offs
,len
);
719 return; // we are done
722 else // delete the object
724 len
= (*i
)->CountPositions();
725 erase(i
); // after this, i is the iterator for the following object
732 while(count
&& i
!= end());
736 wxLayoutList::Insert(wxLayoutObjectBase
*obj
)
740 wxLayoutObjectList::iterator i
= FindCurrentObject(&offs
);
748 // do we have to split a text object?
749 if((*i
)->GetType() == WXLO_TYPE_TEXT
&& offs
!= 0 && offs
!= (*i
)->CountPositions())
751 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
752 #ifdef WXLAYOUT_DEBUG
753 cerr
<< "text: '" << tobj
->GetText() << "'" << endl
;
756 String left
= tobj
->GetText().substr(0,offs
); // get part before cursor
758 tobj
->GetText() = tobj
->GetText().substr(offs
,(*i
)->CountPositions()-offs
); // keeps the right half
759 VAR(tobj
->GetText());
761 insert(i
,new wxLayoutObjectText(left
)); // inserts before
765 wxLayoutObjectList::iterator j
= i
; // we want to apend after this object
773 m_CursorPosition
.x
+= obj
->CountPositions();
774 if(obj
->GetType() == WXLO_TYPE_LINEBREAK
)
779 wxLayoutList::Insert(String
const &text
)
781 wxLayoutObjectText
*tobj
= NULL
;
788 wxLayoutObjectList::iterator i
= FindCurrentObject(&offs
);
790 if(i
!= end() && (*i
)->GetType() == WXLO_TYPE_TEXT
)
791 { // insert into an existing text object:
792 TRACE(inserting into existing object
);
793 tobj
= (wxLayoutObjectText
*)*i
;
795 tobj
->GetText().insert(offs
,text
);
797 else // check whether the previous object is text:
799 wxLayoutObjectList::iterator j
= i
;
801 TRACE(checking previous object
);
802 if(0 && j
!= end() && (*j
)->GetType() == WXLO_TYPE_TEXT
)
804 tobj
= (wxLayoutObjectText
*)*i
;
806 tobj
->GetText()+=text
;
808 else // insert a new text object
810 TRACE(creating
new object
);
811 Insert(new wxLayoutObjectText(text
)); //FIXME not too optimal, slow
812 return; // position gets incremented in Insert(obj)
815 m_CursorPosition
.x
+= strlen(text
.c_str());
819 wxLayoutList::GetLineLength(wxLayoutObjectList::iterator i
)
826 // search backwards for beginning of line:
827 while(i
!= begin() && (*i
)->GetType() != WXLO_TYPE_LINEBREAK
)
829 if((*i
)->GetType() == WXLO_TYPE_LINEBREAK
)
831 // now we can start counting:
832 while(i
!= end() && (*i
)->GetType() != WXLO_TYPE_LINEBREAK
)
834 len
+= (*i
)->CountPositions();
841 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
842 int underline
, char const *fg
, char const *bg
)
844 wxLayoutObjectList::iterator i
= begin();
846 while(i
!= end()) // == while valid
851 m_FontUnderline
= false;
852 m_FontFamily
= family
;
854 m_FontWeight
= weight
;
855 m_ColourFG
= wxTheColourDatabase
->FindColour(fg
);
856 m_ColourBG
= wxTheColourDatabase
->FindColour(bg
);
858 if(! m_ColourFG
) m_ColourFG
= wxBLACK
;
859 if(! m_ColourBG
) m_ColourBG
= wxWHITE
;
861 m_Position
= wxPoint(0,0);
862 m_CursorPosition
= wxPoint(0,0);
864 m_LineHeight
= (BASELINESTRETCH
*m_FontPtSize
)/10;
865 m_MaxX
= 0; m_MaxY
= 0;
869 delete m_DefaultSetting
;
870 m_DefaultSetting
= new
871 wxLayoutObjectCmd(m_FontPtSize
,m_FontFamily
,m_FontStyle
,
872 m_FontWeight
,m_FontUnderline
,
873 m_ColourFG
, m_ColourBG
);