1 /*-*- c++ -*-********************************************************
2 * wxllist: wxLayoutList, a layout engine for text and graphics *
4 * (C) 1998-1999 by Karsten Ballüder (Ballueder@usa.net) *
7 *******************************************************************/
14 #pragma implementation "wxllist.h"
20 #include "wx/wxprec.h"
26 # include "gui/wxllist.h"
32 # include "iostream.h"
35 # include <wx/print.h>
41 /// This should never really get created
42 #define WXLLIST_TEMPFILE "__wxllist.tmp"
46 # define TypewxString(t) g_aTypewxStrings[t]
47 # define WXLO_DEBUG(x) wxLogDebug x
49 static const char *g_aTypewxStrings
[] =
51 "invalid", "text", "cmd", "icon"
54 wxLayoutObject::Debug(void)
56 WXLO_DEBUG(("%s",g_aTypewxStrings
[GetType()]));
59 # define TypewxString(t) ""
60 # define WXLO_DEBUG(x)
64 /// Cursors smaller than this disappear in XOR drawing mode
65 #define WXLO_MINIMUM_CURSOR_WIDTH 4
67 /// Use this character to estimate a cursor size when none is available.
68 #define WXLO_CURSORCHAR "E"
69 /** @name Helper functions */
71 /// allows me to compare to wxPoints
72 bool operator ==(wxPoint
const &p1
, wxPoint
const &p2
)
74 return p1
.x
== p2
.x
&& p1
.y
== p2
.y
;
77 /// allows me to compare to wxPoints
78 bool operator !=(wxPoint
const &p1
, wxPoint
const &p2
)
80 return p1
.x
!= p2
.x
|| p1
.y
!= p2
.y
;
83 /// allows me to compare to wxPoints
84 bool operator <=(wxPoint
const &p1
, wxPoint
const &p2
)
86 return p1
.y
< p2
.y
|| (p1
.y
== p2
.y
&& p1
.x
<= p2
.x
);
89 /// grows a wxRect so that it includes the given point
92 void GrowRect(wxRect
&r
, const wxPoint
& p
)
96 else if(r
.x
+ r
.width
< p
.x
)
101 else if(r
.y
+ r
.height
< p
.y
)
102 r
.height
= p
.y
- r
.y
;
105 /// returns true if the point is in the rectangle
107 bool Contains(const wxRect
&r
, const wxPoint
&p
)
109 return r
.x
<= p
.x
&& r
.y
<= p
.y
&& (r
.x
+r
.width
) >= p
.x
&& (r
.y
+ r
.height
) >= p
.y
;
113 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
117 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
119 wxLayoutObjectText::wxLayoutObjectText(const wxString
&txt
)
129 wxLayoutObjectText::Copy(void)
131 wxLayoutObjectText
*obj
= new wxLayoutObjectText(m_Text
);
132 obj
->m_Width
= m_Width
;
133 obj
->m_Height
= m_Height
;
135 obj
->m_Bottom
= m_Bottom
;
136 obj
->SetUserData(m_UserData
);
141 wxLayoutObjectText::GetSize(CoordType
*top
, CoordType
*bottom
) const
144 *top
= m_Top
; *bottom
= m_Bottom
;
145 return wxPoint(m_Width
, m_Height
);
149 wxLayoutObjectText::Draw(wxDC
&dc
, wxPoint
const &coords
)
151 dc
.DrawText(m_Text
, coords
.x
, coords
.y
-m_Top
);
155 wxLayoutObjectText::GetOffsetScreen(wxDC
&dc
, CoordType xpos
) const
159 maxlen
= m_Text
.Length();
162 height
, descent
= 0l;
164 if(xpos
== 0) return 0; // easy
166 while(width
< xpos
&& offs
< maxlen
)
168 dc
.GetTextExtent(m_Text
.substr(0,offs
),
169 &width
, &height
, &descent
);
172 /* We have to substract 1 to compensate for the offs++, and another
173 one because we don't want to position the cursor behind the
174 object what we clicked on, but before - otherwise it looks
176 return (xpos
> 2) ? offs
-2 : 0;
180 wxLayoutObjectText::Layout(wxDC
&dc
)
184 dc
.GetTextExtent(m_Text
,&m_Width
, &m_Height
, &descent
);
186 m_Top
= m_Height
- m_Bottom
;
189 #ifdef WXLAYOUT_DEBUG
191 wxLayoutObjectText::Debug(void)
193 wxLayoutObject::Debug();
194 WXLO_DEBUG((" `%s`", m_Text
.c_str()));
198 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
202 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
204 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
const &icon
)
206 m_Icon
= new wxBitmap(icon
);
210 wxLayoutObjectIcon::Copy(void)
212 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon(new
214 obj
->SetUserData(m_UserData
);
218 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
*icon
)
224 wxLayoutObjectIcon::Draw(wxDC
&dc
, wxPoint
const &coords
)
226 dc
.DrawBitmap(*m_Icon
, coords
.x
, coords
.y
-m_Icon
->GetHeight(),
227 (m_Icon
->GetMask() == NULL
) ? FALSE
: TRUE
);
231 wxLayoutObjectIcon::Layout(wxDC
& /* dc */)
236 wxLayoutObjectIcon::GetSize(CoordType
*top
, CoordType
*bottom
) const
238 *top
= m_Icon
->GetHeight();
240 return wxPoint(m_Icon
->GetWidth(), m_Icon
->GetHeight());
245 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
249 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
251 wxLayoutObjectCmd::wxLayoutObjectCmd(int size
, int family
, int style
, int
252 weight
, bool underline
,
253 wxColour
&fg
, wxColour
&bg
)
256 m_font
= new wxFont(size
,family
,style
,weight
,underline
);
262 wxLayoutObjectCmd::Copy(void)
264 wxLayoutStyleInfo si
;
267 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd(
268 si
.size
, si
.family
, si
.style
, si
.weight
, si
.underline
,
269 m_ColourFG
, m_ColourBG
);
270 obj
->SetUserData(m_UserData
);
275 wxLayoutObjectCmd::~wxLayoutObjectCmd()
281 wxLayoutObjectCmd::GetStyle(wxLayoutStyleInfo
*si
) const
283 si
->size
= m_font
->GetPointSize();
284 si
->family
= m_font
->GetFamily();
285 si
->style
= m_font
->GetStyle();
286 si
->underline
= m_font
->GetUnderlined();
287 si
->weight
= m_font
->GetWeight();
289 si
->fg_red
= m_ColourFG
.Red();
290 si
->fg_green
= m_ColourFG
.Green();
291 si
->fg_blue
= m_ColourFG
.Blue();
292 si
->bg_red
= m_ColourBG
.Red();
293 si
->bg_green
= m_ColourBG
.Green();
294 si
->bg_blue
= m_ColourBG
.Blue();
298 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint
const & /* coords */)
302 dc
.SetTextForeground(m_ColourFG
);
303 dc
.SetTextBackground(m_ColourBG
);
307 wxLayoutObjectCmd::Layout(wxDC
&dc
)
309 // this get called, so that recalculation uses right font sizes
310 Draw(dc
, wxPoint(0,0));
314 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
316 The wxLayoutLine object
318 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
320 wxLayoutLine::wxLayoutLine(wxLayoutLine
*prev
, wxLayoutList
*llist
)
323 m_Width
= m_Height
= 0;
328 RecalculatePosition(llist
);
331 m_LineNumber
= m_Previous
->GetLineNumber()+1;
332 m_Next
= m_Previous
->GetNextLine();
333 m_Previous
->m_Next
= this;
334 m_Height
= m_Previous
->GetHeight();
338 m_Next
->m_Previous
= this;
339 m_Next
->MoveLines(+1);
340 m_Next
->RecalculatePositions(1,llist
);
344 wxLayoutLine::~wxLayoutLine()
346 // kbList cleans itself
350 wxLayoutLine::RecalculatePosition(wxLayoutList
*llist
)
353 m_Position
= m_Previous
->GetPosition() +
354 wxPoint(0,m_Previous
->GetHeight());
356 m_Position
= wxPoint(0,0);
357 llist
->SetUpdateRect(m_Position
);
362 wxLayoutLine::RecalculatePositions(int recurse
, wxLayoutList
*llist
)
364 wxASSERT(recurse
>= 0);
365 wxPoint pos
= m_Position
;
366 CoordType height
= m_Height
;
368 // WXLO_TRACE("RecalculatePositions()");
369 RecalculatePosition(llist
);
373 m_Next
->RecalculatePositions(--recurse
, llist
);
374 else if(pos
!= m_Position
|| m_Height
!= height
)
375 m_Next
->RecalculatePositions(0, llist
);
379 wxLayoutObjectList::iterator
380 wxLayoutLine::FindObject(CoordType xpos
, CoordType
*offset
) const
384 wxLayoutObjectList::iterator
387 CoordType x
= 0, len
;
389 /* We search through the objects. As we don't like returning the
390 object that the cursor is behind, we just remember such an
391 object in "found" so we can return it if there is really no
392 further object following it. */
393 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
395 len
= (**i
).GetLength();
396 if( x
<= xpos
&& xpos
<= x
+ len
)
399 if(xpos
== x
+ len
) // is there another object behind?
401 else // we are really inside this object
404 x
+= (**i
).GetLength();
406 return found
; // ==NULL if really none found
409 wxLayoutObjectList::iterator
410 wxLayoutLine::FindObjectScreen(wxDC
&dc
, CoordType xpos
, CoordType
*cxpos
) const
414 wxLayoutObjectList::iterator i
;
415 CoordType x
= 0, cx
= 0, width
;
417 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
420 width
= (**i
).GetWidth();
421 if( x
<= xpos
&& xpos
<= x
+ width
)
423 *cxpos
= cx
+ (**i
).GetOffsetScreen(dc
, xpos
-x
);
424 wxLogDebug("wxLayoutLine::FindObjectScreen: cursor xpos = %ld", *cxpos
);
427 x
+= (**i
).GetWidth();
428 cx
+= (**i
).GetLength();
430 // behind last object:
432 return m_ObjectList
.tail();
436 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
439 wxASSERT(obj
!= NULL
);
441 wxLOiterator i
= FindObject(xpos
, &offset
);
444 if(xpos
== 0 ) // aha, empty line!
446 m_ObjectList
.push_back(obj
);
447 m_Length
+= obj
->GetLength();
454 CoordType len
= (**i
).GetLength();
455 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
456 { // insert before this object
457 m_ObjectList
.insert(i
,obj
);
458 m_Length
+= obj
->GetLength();
463 if( i
== m_ObjectList
.tail()) // last object?
464 m_ObjectList
.push_back(obj
);
466 { // insert after current object
468 m_ObjectList
.insert(i
,obj
);
470 m_Length
+= obj
->GetLength();
473 /* Otherwise we need to split the current object.
474 Fortunately this can only be a text object. */
475 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
476 wxString left
, right
;
477 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
478 left
= tobj
->GetText().substr(0,offset
);
479 right
= tobj
->GetText().substr(offset
,len
-offset
);
480 // current text object gets set to right half
481 tobj
->GetText() = right
; // set new text
482 // before it we insert the new object
483 m_ObjectList
.insert(i
,obj
);
484 m_Length
+= obj
->GetLength();
485 // and before that we insert the left half
486 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
491 wxLayoutLine::Insert(CoordType xpos
, wxString text
)
495 wxLOiterator i
= FindObject(xpos
, &offset
);
496 if(i
!= NULLIT
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
498 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
499 tobj
->GetText().insert(offset
, text
);
500 m_Length
+= text
.Length();
505 return Insert(xpos
, new wxLayoutObjectText(text
));
509 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
511 CoordType offset
, len
;
515 wxLOiterator i
= FindObject(xpos
, &offset
);
518 if(i
== NULLIT
) return npos
;
519 // now delete from that object:
520 if((**i
).GetType() != WXLO_TYPE_TEXT
)
522 if(offset
!= 0) // at end of line after a non-text object
525 len
= (**i
).GetLength();
528 m_ObjectList
.erase(i
);
532 // tidy up: remove empty text objects
533 if((**i
).GetLength() == 0)
535 m_ObjectList
.erase(i
);
539 CoordType max
= (**i
).GetLength() - offset
;
540 if(npos
< max
) max
= npos
;
543 if(xpos
== GetLength())
546 { // at the end of an object
547 // move to begin of next object:
549 continue; // start over
554 if(offset
== 0 && max
== (**i
).GetLength())
555 m_ObjectList
.erase(i
); // remove the whole object
557 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
564 wxLayoutLine::DeleteWord(CoordType xpos
)
569 wxLOiterator i
= FindObject(xpos
, &offset
);
573 if(i
== NULLIT
) return false;
574 if((**i
).GetType() != WXLO_TYPE_TEXT
)
576 // This should only happen when at end of line, behind a non-text
578 if(offset
== (**i
).GetLength()) return false;
579 m_Length
-= (**i
).GetLength(); // -1
580 m_ObjectList
.erase(i
);
581 return true; // we are done
585 if(offset
== (**i
).GetLength()) // at end of object
590 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
592 wxString str
= tobj
->GetText();
593 str
= str
.substr(offset
,str
.Length()-offset
);
594 // Find out how many positions we need to delete:
595 // 1. eat leading space
596 while(isspace(str
.c_str()[count
])) count
++;
597 // 2. eat the word itself:
598 while(isalnum(str
.c_str()[count
])) count
++;
600 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
601 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
606 wxASSERT(0); // we should never arrive here
610 wxLayoutLine::DeleteLine(bool update
, wxLayoutList
*llist
)
612 if(m_Next
) m_Next
->m_Previous
= m_Previous
;
613 if(m_Previous
) m_Previous
->m_Next
= m_Next
;
616 m_Next
->MoveLines(-1);
617 m_Next
->RecalculatePositions(1, llist
);
619 wxLayoutLine
*next
= m_Next
;
625 wxLayoutLine::Draw(wxDC
&dc
,
627 const wxPoint
& offset
) const
629 wxLayoutObjectList::iterator i
;
630 wxPoint pos
= offset
;
631 pos
= pos
+ GetPosition();
635 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
638 pos
.x
+= (**i
).GetWidth();
643 wxLayoutLine::Layout(wxDC
&dc
,
649 wxLayoutObjectList::iterator i
;
652 oldHeight
= m_Height
;
654 topHeight
, bottomHeight
; // above and below baseline
657 objTopHeight
, objBottomHeight
;
660 m_Height
= 0; m_BaseLine
= 0;
662 topHeight
= 0; bottomHeight
= 0;
664 bool cursorFound
= false;
668 *cursorPos
= m_Position
;
669 if(cursorSize
) *cursorSize
= wxPoint(0,0);
672 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
675 size
= (**i
).GetSize(&objTopHeight
, &objBottomHeight
);
677 if(cursorPos
&& ! cursorFound
)
678 { // we need to check whether the text cursor is here
679 len
= (**i
).GetLength();
680 if(count
<= cx
&& count
+len
> cx
)
682 if((**i
).GetType() == WXLO_TYPE_TEXT
)
684 len
= cx
- count
; // pos in object
685 CoordType width
, height
, descent
;
686 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
687 &width
, &height
, &descent
);
688 cursorPos
->x
+= width
;
689 cursorPos
->y
= m_Position
.y
;
691 if(len
< (**i
).GetLength())
692 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
694 str
= WXLO_CURSORCHAR
;
695 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
696 wxASSERT(cursorSize
);
697 // Just in case some joker inserted an empty string object:
698 if(width
== 0) width
= WXLO_MINIMUM_CURSOR_WIDTH
;
699 if(height
== 0) height
= objHeight
;
700 cursorSize
->x
= width
;
701 cursorSize
->y
= height
;
702 cursorFound
= true; // no more checks
705 { // on some other object
706 CoordType top
, bottom
; // unused
707 *cursorSize
= (**i
).GetSize(&top
,&bottom
);
708 cursorPos
->y
= m_Position
.y
;
709 cursorFound
= true; // no more checks
715 cursorPos
->x
+= (**i
).GetWidth();
720 if(objHeight
> m_Height
) m_Height
= objHeight
;
721 if(objTopHeight
> topHeight
) topHeight
= objTopHeight
;
722 if(objBottomHeight
> bottomHeight
) bottomHeight
= objBottomHeight
;
724 if(topHeight
+ bottomHeight
> m_Height
) m_Height
=
725 topHeight
+bottomHeight
;
726 m_BaseLine
= topHeight
;
730 if(GetPreviousLine()) // empty line
732 m_Height
= GetPreviousLine()->GetHeight();
733 m_BaseLine
= GetPreviousLine()->m_BaseLine
;
737 CoordType width
, height
, descent
;
738 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
740 m_BaseLine
= m_Height
- descent
;
745 // tell next line about coordinate change
746 if(m_Next
&& objHeight
!= oldHeight
)
747 m_Next
->RecalculatePositions(0, llist
);
749 // We need to check whether we found a valid cursor size:
752 // this might be the case if the cursor is at the end of the
753 // line or on a command object:
754 if(cursorSize
->y
< WXLO_MINIMUM_CURSOR_WIDTH
)
756 CoordType width
, height
, descent
;
757 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
758 cursorSize
->x
= width
;
759 cursorSize
->y
= height
;
761 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
762 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
768 wxLayoutLine::Break(CoordType xpos
, wxLayoutList
*llist
)
773 { // insert an empty line before this one
774 wxLayoutLine
*prev
= new wxLayoutLine(m_Previous
, llist
);
775 if(m_Previous
== NULL
)
776 { // We were in first line, need to link in new empty line
780 m_Previous
->m_Height
= GetHeight(); // this is a wild guess
784 m_Next
->RecalculatePositions(1, llist
);
789 wxLOiterator i
= FindObject(xpos
, &offset
);
791 // must be at the end of the line then
792 return new wxLayoutLine(this, llist
);
795 wxLayoutLine
*newLine
= new wxLayoutLine(this, llist
);
796 // split object at i:
797 if((**i
).GetType() == WXLO_TYPE_TEXT
&& offset
!= 0)
799 wxString left
, right
;
800 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
801 left
= tobj
->GetText().substr(0,offset
);
802 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
803 // current text object gets set to left half
804 tobj
->GetText() = left
; // set new text
805 newLine
->Append(new wxLayoutObjectText(right
));
806 m_Length
-= right
.Length();
807 i
++; // don't move this object to the new list
811 i
++; // move objects from here to new list
813 while(i
!= m_ObjectList
.end())
816 m_Length
-= (**i
).GetLength();
817 m_ObjectList
.remove(i
); // remove without deleting it
820 m_Next
->RecalculatePositions(2, llist
);
826 wxLayoutLine::MergeNextLine(wxLayoutList
*llist
)
828 wxASSERT(GetNextLine());
829 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
832 for(i
= list
.begin(); i
!= list
.end();)
835 list
.remove(i
); // remove without deleting it
837 wxASSERT(list
.empty());
838 wxLayoutLine
*oldnext
= GetNextLine();
839 SetNext(GetNextLine()->GetNextLine());
841 RecalculatePositions(1, llist
);
845 wxLayoutLine::GetWrapPosition(CoordType column
)
848 wxLOiterator i
= FindObject(column
, &offset
);
849 if(i
== NULLIT
) return -1; // cannot wrap
851 // go backwards through the list and look for space in text objects
854 if((**i
).GetType() == WXLO_TYPE_TEXT
)
858 if( isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
865 }while(offset
!= -1);
866 i
--; // move on to previous object
870 column
-= (**i
).GetLength();
874 offset
= (**i
).GetLength();
876 /* If we reached the begin of the list and have more than one
877 object, that one is longer than the margin, so break behind
880 i
= m_ObjectList
.begin();
881 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
883 pos
+= (**i
).GetLength();
886 if(i
== NULLIT
) return -1; //why should this happen?
887 pos
+= (**i
).GetLength();
889 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
891 pos
+= (**i
).GetLength();
894 if(i
== NULLIT
) return -1; //this is possible, if there is only one text object
895 // now we are at the second text object:
896 pos
-= (**i
).GetLength();
897 return pos
; // in front of it
901 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
903 The wxLayoutList object
905 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
907 wxLayoutList::wxLayoutList()
909 m_DefaultSetting
= NULL
;
911 m_ColourFG
= *wxBLACK
;
912 m_ColourBG
= *wxWHITE
;
913 InvalidateUpdateRect();
917 wxLayoutList::~wxLayoutList()
920 m_FirstLine
->DeleteLine(false, this);
924 wxLayoutList::Empty(void)
927 m_FirstLine
= m_FirstLine
->DeleteLine(false, this);
929 m_CursorPos
= wxPoint(0,0);
930 m_CursorScreenPos
= wxPoint(0,0);
931 m_CursorSize
= wxPoint(0,0);
932 m_FirstLine
= new wxLayoutLine(NULL
, this); // empty first line
933 m_CursorLine
= m_FirstLine
;
934 InvalidateUpdateRect();
939 wxLayoutList::InternalClear(void)
944 delete m_DefaultSetting
;
945 m_DefaultSetting
= NULL
;
950 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
951 int underline
, wxColour
*fg
,
954 if(family
!= -1) m_FontFamily
= family
;
955 if(size
!= -1) m_FontPtSize
= size
;
956 if(style
!= -1) m_FontStyle
= style
;
957 if(weight
!= -1) m_FontWeight
= weight
;
958 if(underline
!= -1) m_FontUnderline
= underline
!= 0;
960 if(fg
!= NULL
) m_ColourFG
= *fg
;
961 if(bg
!= NULL
) m_ColourBG
= *bg
;
964 new wxLayoutObjectCmd(m_FontPtSize
,m_FontFamily
,m_FontStyle
,m_FontWeight
,m_FontUnderline
,
965 m_ColourFG
, m_ColourBG
));
969 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
970 int underline
, char const *fg
, char const *bg
)
978 cfg
= wxTheColourDatabase
->FindColour(fg
);
980 cbg
= wxTheColourDatabase
->FindColour(bg
);
982 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
986 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
987 int /* underline */, wxColour
*fg
, wxColour
*bg
)
993 m_FontUnderline
= false;
994 m_FontFamily
= family
;
996 m_FontWeight
= weight
;
997 if(fg
) m_ColourFG
= *fg
;
998 if(bg
) m_ColourBG
= *bg
;
1000 m_ColourFG
= *wxBLACK
;
1001 m_ColourBG
= *wxWHITE
;
1003 if(m_DefaultSetting
)
1004 delete m_DefaultSetting
;
1006 m_DefaultSetting
= new
1007 wxLayoutObjectCmd(m_FontPtSize
,m_FontFamily
,m_FontStyle
,
1008 m_FontWeight
,m_FontUnderline
,
1009 m_ColourFG
, m_ColourBG
);
1015 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
1017 SetUpdateRect(m_CursorScreenPos
);
1018 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1019 wxLayoutLine
*line
= m_FirstLine
;
1020 while(line
&& line
->GetLineNumber() != p
.y
)
1021 line
= line
->GetNextLine();
1022 if(line
&& line
->GetLineNumber() == p
.y
) // found it
1024 m_CursorPos
.y
= p
.y
;
1025 m_CursorLine
= line
;
1026 CoordType len
= line
->GetLength();
1029 m_CursorPos
.x
= p
.x
;
1034 m_CursorPos
.x
= len
;
1042 wxLayoutList::MoveCursorVertically(int n
)
1044 SetUpdateRect(m_CursorScreenPos
);
1045 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1047 if(n
< 0) // move up
1049 if(m_CursorLine
== m_FirstLine
) return false;
1050 while(n
< 0 && m_CursorLine
)
1052 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1058 m_CursorLine
= m_FirstLine
;
1064 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1065 m_CursorPos
.x
= m_CursorLine
->GetLength();
1071 wxLayoutLine
*last
= m_CursorLine
;
1072 if(! m_CursorLine
->GetNextLine()) return false;
1073 while(n
> 0 && m_CursorLine
)
1077 m_CursorLine
= m_CursorLine
->GetNextLine();
1081 m_CursorLine
= last
;
1087 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1088 m_CursorPos
.x
= m_CursorLine
->GetLength();
1096 wxLayoutList::MoveCursorHorizontally(int n
)
1098 SetUpdateRect(m_CursorScreenPos
);
1099 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1103 if(m_CursorPos
.x
== 0) // at begin of line
1105 if(! MoveCursorVertically(-1))
1107 MoveCursorToEndOfLine();
1112 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1113 m_CursorPos
.x
-= move
; n
+= move
;
1118 int len
= m_CursorLine
->GetLength();
1119 if(m_CursorPos
.x
== len
) // at end of line
1121 if(! MoveCursorVertically(1))
1123 MoveCursorToBeginOfLine();
1128 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1129 m_CursorPos
.x
+= move
;
1136 wxLayoutList::Insert(wxString
const &text
)
1138 wxASSERT(m_CursorLine
);
1139 SetUpdateRect(m_CursorScreenPos
);
1140 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1141 m_CursorLine
->Insert(m_CursorPos
.x
, text
);
1142 m_CursorPos
.x
+= text
.Length();
1147 wxLayoutList::Insert(wxLayoutObject
*obj
)
1149 wxASSERT(m_CursorLine
);
1150 SetUpdateRect(m_CursorScreenPos
);
1151 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1152 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
1153 m_CursorPos
.x
+= obj
->GetLength();
1158 wxLayoutList::LineBreak(void)
1160 wxASSERT(m_CursorLine
);
1161 bool setFirst
= (m_CursorLine
== m_FirstLine
&& m_CursorPos
.x
== 0);
1162 SetUpdateRect(m_CursorScreenPos
);
1163 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1164 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
, this);
1165 if(setFirst
) // we were at beginning of first line
1166 m_FirstLine
= m_CursorLine
->GetPreviousLine();
1173 wxLayoutList::WrapLine(CoordType column
)
1175 if(m_CursorPos
.x
<= column
|| column
< 1)
1176 return false; // do nothing yet
1179 CoordType xpos
= m_CursorLine
->GetWrapPosition(column
);
1181 return false; // cannot break line
1183 CoordType newpos
= m_CursorPos
.x
- xpos
- 1;
1184 m_CursorPos
.x
= xpos
;
1185 SetUpdateRect(m_CursorScreenPos
);
1186 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1188 Delete(1); // delete the space
1189 m_CursorPos
.x
= newpos
;
1195 wxLayoutList::Delete(CoordType npos
)
1197 wxASSERT(m_CursorLine
);
1198 SetUpdateRect(m_CursorScreenPos
);
1199 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1203 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
1206 // More to delete, continue on next line.
1207 // First, check if line is empty:
1208 if(m_CursorLine
->GetLength() == 0)
1209 { // in this case, updating could probably be optimised
1211 wxASSERT(DeleteLines(1) == 0);
1220 // Need to join next line
1221 if(! m_CursorLine
->GetNextLine())
1225 m_CursorLine
->MergeNextLine(this);
1235 wxLayoutList::DeleteLines(int n
)
1237 wxASSERT(m_CursorLine
);
1239 SetUpdateRect(m_CursorScreenPos
);
1240 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1243 if(!m_CursorLine
->GetNextLine())
1244 { // we cannot delete this line, but we can clear it
1245 MoveCursorToBeginOfLine();
1246 DeleteToEndOfLine();
1250 line
= m_CursorLine
;
1251 m_CursorLine
= m_CursorLine
->DeleteLine(true, this);
1253 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
1254 wxASSERT(m_FirstLine
);
1255 wxASSERT(m_CursorLine
);
1257 m_CursorLine
->RecalculatePositions(2, this);
1262 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
)
1264 wxLayoutLine
*line
= m_FirstLine
;
1266 // first, make sure everything is calculated - this might not be
1267 // needed, optimise it later
1268 m_DefaultSetting
->Layout(dc
);
1271 line
->RecalculatePosition(this); // so we don't need to do it all the time
1272 // little condition to speed up redrawing:
1273 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1274 line
= line
->GetNextLine();
1279 wxLayoutList::UpdateCursorScreenPos(wxDC
&dc
)
1281 wxASSERT(m_CursorLine
);
1282 m_CursorLine
->Layout(dc
, this, (wxPoint
*)&m_CursorScreenPos
, (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
1286 wxLayoutList::GetCursorScreenPos(wxDC
&dc
)
1288 UpdateCursorScreenPos(dc
);
1289 return m_CursorScreenPos
;
1293 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
)
1295 wxLayoutLine
*line
= m_FirstLine
;
1297 // first, make sure everything is calculated - this might not be
1298 // needed, optimise it later
1299 m_DefaultSetting
->Layout(dc
);
1302 if(line
== m_CursorLine
)
1303 line
->Layout(dc
, this, (wxPoint
*)&m_CursorScreenPos
, (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
1305 line
->Layout(dc
, this);
1306 // little condition to speed up redrawing:
1307 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1308 line
= line
->GetNextLine();
1311 ///FIXME: disabled for now
1313 // can only be 0 if we are on the first line and have no next line
1314 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
1315 m_CursorLine
->GetNextLine() == NULL
&&
1316 m_CursorLine
== m_FirstLine
));
1318 SetUpdateRect(m_CursorScreenPos
);
1319 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1323 wxLayoutList::Draw(wxDC
&dc
, wxPoint
const &offset
,
1324 CoordType top
, CoordType bottom
)
1326 wxLayoutLine
*line
= m_FirstLine
;
1329 m_DefaultSetting
->Draw(dc
, wxPoint(0,0));
1330 wxBrush
brush(m_ColourBG
, wxSOLID
);
1335 // only draw if between top and bottom:
1336 if((top
== -1 || line
->GetPosition().y
>= top
))
1337 line
->Draw(dc
, this, offset
);
1338 // little condition to speed up redrawing:
1339 if(bottom
!= -1 && line
->GetPosition().y
+ line
->GetHeight() > bottom
) break;
1340 line
= line
->GetNextLine();
1342 // can only be 0 if we are on the first line and have no next line
1343 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
1344 m_CursorLine
->GetNextLine() == NULL
&&
1345 m_CursorLine
== m_FirstLine
));
1346 InvalidateUpdateRect();
1350 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
, wxPoint
*cursorPos
)
1352 // First, find the right line:
1353 wxLayoutLine
*line
= m_FirstLine
;
1356 // we need to run a layout here to get font sizes right :-(
1357 m_DefaultSetting
->Layout(dc
);
1360 p
= line
->GetPosition();
1361 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
1363 line
->Layout(dc
, this);
1364 line
= line
->GetNextLine();
1366 if(line
== NULL
) return NULL
; // not found
1367 if(cursorPos
) cursorPos
->y
= line
->GetLineNumber();
1368 // Now, find the object in the line:
1369 wxLOiterator i
= line
->FindObjectScreen(dc
, pos
.x
, & cursorPos
->x
);
1370 return (i
== NULLIT
) ? NULL
: *i
;
1375 wxLayoutList::GetSize(void) const
1378 *line
= m_FirstLine
,
1381 return wxPoint(0,0);
1383 wxPoint
maxPoint(0,0);
1388 if(line
->GetWidth() > maxPoint
.x
)
1389 maxPoint
.x
= line
->GetWidth();
1391 line
= line
->GetNextLine();
1394 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
1399 wxLayoutList::DrawCursor(wxDC
&dc
, bool active
, wxPoint
const &translate
)
1402 coords
= m_CursorScreenPos
;
1403 coords
.x
+= translate
.x
;
1404 coords
.y
+= translate
.y
;
1406 #ifdef WXLAYOUT_DEBUG
1407 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
1408 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
1409 (long)coords
.x
, (long)coords
.y
,
1410 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
1411 (long)m_CursorLine
->GetLineNumber(),
1412 (long)m_CursorLine
->GetLength()));
1415 dc
.SetBrush(*wxBLACK_BRUSH
);
1416 dc
.SetLogicalFunction(wxXOR
);
1417 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
1419 dc
.DrawRectangle(coords
.x
, coords
.y
, m_CursorSize
.x
,
1422 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
1423 coords
.x
+m_CursorSize
.x
, coords
.y
+m_CursorSize
.y
-1);
1424 dc
.SetLogicalFunction(wxCOPY
);
1425 //dc.SetBrush(wxNullBrush);
1428 /** Called by the objects to update the update rectangle.
1429 @param p a point to include in it
1432 wxLayoutList::SetUpdateRect(const wxPoint
&p
)
1434 if(m_UpdateRectValid
)
1435 GrowRect(m_UpdateRect
, p
);
1438 m_UpdateRect
.x
= p
.x
;
1439 m_UpdateRect
.y
= p
.y
;
1440 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
1441 m_UpdateRect
.height
= 4;// wxGTK :-)
1442 m_UpdateRectValid
= true;
1447 wxLayoutList::StartSelection(void)
1449 wxLogDebug("Starting selection at %ld/%ld", m_CursorPos
.x
, m_CursorPos
.y
);
1450 m_Selection
.m_CursorA
= m_CursorPos
;
1454 wxLayoutList::EndSelection(void)
1456 wxLogDebug("Ending selection at %ld/%ld", m_CursorPos
.x
, m_CursorPos
.y
);
1457 m_Selection
.m_CursorB
= m_CursorPos
;
1461 wxLayoutList::IsSelected(const wxPoint
&cursor
)
1463 return m_Selection
.m_CursorA
<= cursor
1464 && cursor
<= m_Selection
.m_CursorB
;
1468 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1472 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1474 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
1475 wxString
const & title
)
1483 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
1485 // The following bit is taken from the printing sample, let's see
1486 // whether it works for us.
1488 /* You might use THIS code to set the printer DC to ROUGHLY reflect
1489 * the screen text size. This page also draws lines of actual length 5cm
1492 // Get the logical pixels per inch of screen and printer
1493 int ppiScreenX
, ppiScreenY
;
1494 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
1495 int ppiPrinterX
, ppiPrinterY
;
1496 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
1498 if(ppiScreenX
== 0) // not yet set, need to guess
1503 if(ppiPrinterX
== 0) // not yet set, need to guess
1509 // This scales the DC so that the printout roughly represents the
1510 // the screen scaling. The text point size _should_ be the right size
1511 // but in fact is too small for some reason. This is a detail that will
1512 // need to be addressed at some point but can be fudged for the
1514 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
1516 // Now we have to check in case our real page size is reduced
1517 // (e.g. because we're drawing to a print preview memory DC)
1518 int pageWidth
, pageHeight
;
1520 dc
->GetSize(&w
, &h
);
1521 GetPageSizePixels(&pageWidth
, &pageHeight
);
1522 if(pageWidth
!= 0) // doesn't work always
1524 // If printer pageWidth == current DC width, then this doesn't
1525 // change. But w might be the preview bitmap width, so scale down.
1526 scale
= scale
* (float)(w
/(float)pageWidth
);
1528 dc
->SetUserScale(scale
, scale
);
1532 bool wxLayoutPrintout::OnPrintPage(int page
)
1541 top
= (page
- 1)*m_PrintoutHeight
;
1542 bottom
= top
+ m_PrintoutHeight
;
1543 // SetDeviceOrigin() doesn't work here, so we need to manually
1544 // translate all coordinates.
1545 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
1546 m_llist
->Draw(*dc
, translate
, top
, bottom
);
1553 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
1555 /* We allocate a temporary wxDC for printing, so that we can
1556 determine the correct paper size and scaling. We don't actually
1557 print anything on it. */
1559 wxPrinterDC
psdc("","",WXLLIST_TEMPFILE
,false);
1561 wxPostScriptDC
psdc(WXLLIST_TEMPFILE
,false);
1564 float scale
= ScaleDC(&psdc
);
1566 psdc
.GetSize(&m_PageWidth
, &m_PageHeight
);
1567 // This sets a left/top origin of 15% and 20%:
1568 m_Offset
= wxPoint((15*m_PageWidth
)/100, m_PageHeight
/20);
1570 // This is the length of the printable area.
1571 m_PrintoutHeight
= m_PageHeight
- (int) (m_PageHeight
* 0.15);
1572 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
1576 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
1579 *maxPage
= m_NumOfPages
;
1582 *selPageTo
= m_NumOfPages
;
1583 wxRemoveFile(WXLLIST_TEMPFILE
);
1586 bool wxLayoutPrintout::HasPage(int pageNum
)
1588 return pageNum
<= m_NumOfPages
;
1592 Stupid wxWindows doesn't draw proper ellipses, so we comment this
1593 out. It's a waste of paper anyway.
1597 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
1598 wxPoint topleft
, wxPoint bottomright
,
1601 // make backups of all essential parameters
1602 const wxBrush
& brush
= dc
.GetBrush();
1603 const wxPen
& pen
= dc
.GetPen();
1604 const wxFont
& font
= dc
.GetFont();
1606 dc
.SetBrush(*wxWHITE_BRUSH
);
1607 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
1608 dc
.DrawRoundedRectangle(topleft
.x
,
1609 topleft
.y
,bottomright
.x
-topleft
.x
,
1610 bottomright
.y
-topleft
.y
);
1611 dc
.SetBrush(*wxBLACK_BRUSH
);
1612 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
1613 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
1617 page
= "9999/9999 "; // many pages...
1619 dc
.GetTextExtent(page
,&w
,&h
);
1620 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
1621 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
1622 dc
.GetTextExtent("XXXX", &w
,&h
);
1623 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);