1 /*-*- c++ -*-********************************************************
2 * wxllist: wxLayoutList, a layout engine for text and graphics *
4 * (C) 1998-1999 by Karsten Ballüder (Ballueder@usa.net) *
7 *******************************************************************/
10 #pragma implementation "wxllist.h"
13 #include "wx/wxprec.h"
21 # include "gui/wxllist.h"
26 # include "iostream.h"
29 # include <wx/print.h>
35 /// This should never really get created
36 #define WXLLIST_TEMPFILE "__wxllist.tmp"
40 # define TypewxString(t) g_aTypewxStrings[t]
41 # define WXLO_DEBUG(x) wxLogDebug x
43 static const char *g_aTypewxStrings
[] =
45 "invalid", "text", "cmd", "icon"
48 wxLayoutObject::Debug(void)
50 WXLO_DEBUG(("%s",g_aTypewxStrings
[GetType()]));
53 # define TypewxString(t) ""
54 # define WXLO_DEBUG(x)
58 /// Cursors smaller than this disappear in XOR drawing mode
59 #define WXLO_MINIMUM_CURSOR_WIDTH 4
61 /// Use this character to estimate a cursor size when none is available.
62 #define WXLO_CURSORCHAR "E"
63 /** @name Helper functions */
65 /// allows me to compare to wxPoints
66 bool operator ==(wxPoint
const &p1
, wxPoint
const &p2
)
68 return p1
.x
== p2
.x
&& p1
.y
== p2
.y
;
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 /// grows a wxRect so that it includes the given point
79 static void GrowRect(wxRect
&r
, const wxPoint
& p
)
83 else if(r
.x
+ r
.width
< p
.x
)
88 else if(r
.y
+ r
.height
< p
.y
)
93 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
97 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
99 wxLayoutObjectText::wxLayoutObjectText(const wxString
&txt
)
109 wxLayoutObjectText::Copy(void)
111 wxLayoutObjectText
*obj
= new wxLayoutObjectText(m_Text
);
112 obj
->m_Width
= m_Width
;
113 obj
->m_Height
= m_Height
;
115 obj
->m_Bottom
= m_Bottom
;
116 obj
->SetUserData(m_UserData
);
121 wxLayoutObjectText::GetSize(CoordType
*top
, CoordType
*bottom
) const
124 *top
= m_Top
; *bottom
= m_Bottom
;
125 return wxPoint(m_Width
, m_Height
);
129 wxLayoutObjectText::Draw(wxDC
&dc
, wxPoint
const &coords
)
131 dc
.DrawText(m_Text
, coords
.x
, coords
.y
-m_Top
);
135 wxLayoutObjectText::GetOffsetScreen(wxDC
&dc
, CoordType xpos
) const
139 maxlen
= m_Text
.Length();
142 height
, descent
= 0l;
144 if(xpos
== 0) return 0; // easy
146 while(width
< xpos
&& offs
< maxlen
)
148 dc
.GetTextExtent(m_Text
.substr(0,offs
),
149 &width
, &height
, &descent
);
152 /* We have to substract 1 to compensate for the offs++, and another
153 one because we don't want to position the cursor behind the
154 object what we clicked on, but before - otherwise it looks
156 return (xpos
> 2) ? offs
-2 : 0;
160 wxLayoutObjectText::Layout(wxDC
&dc
)
164 dc
.GetTextExtent(m_Text
,&m_Width
, &m_Height
, &descent
);
166 m_Top
= m_Height
- m_Bottom
;
169 #ifdef WXLAYOUT_DEBUG
171 wxLayoutObjectText::Debug(void)
173 wxLayoutObject::Debug();
174 WXLO_DEBUG((" `%s`", m_Text
.c_str()));
178 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
182 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
184 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
const &icon
)
186 m_Icon
= new wxBitmap(icon
);
190 wxLayoutObjectIcon::Copy(void)
192 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon(new
194 obj
->SetUserData(m_UserData
);
198 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
*icon
)
204 wxLayoutObjectIcon::Draw(wxDC
&dc
, wxPoint
const &coords
)
206 dc
.DrawBitmap(*m_Icon
, coords
.x
, coords
.y
-m_Icon
->GetHeight(),
207 (m_Icon
->GetMask() == NULL
) ? FALSE
: TRUE
);
211 wxLayoutObjectIcon::Layout(wxDC
& /* dc */)
216 wxLayoutObjectIcon::GetSize(CoordType
*top
, CoordType
*bottom
) const
218 *top
= m_Icon
->GetHeight();
220 return wxPoint(m_Icon
->GetWidth(), m_Icon
->GetHeight());
225 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
229 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
231 wxLayoutObjectCmd::wxLayoutObjectCmd(int size
, int family
, int style
, int
232 weight
, bool underline
,
233 wxColour
const *fg
, wxColour
const *bg
)
236 m_font
= new wxFont(size
,family
,style
,weight
,underline
);
242 wxLayoutObjectCmd::Copy(void)
244 wxLayoutStyleInfo si
;
247 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd(
248 si
.size
, si
.family
, si
.style
, si
.weight
, si
.underline
,
249 m_ColourFG
, m_ColourBG
);
250 obj
->SetUserData(m_UserData
);
255 wxLayoutObjectCmd::~wxLayoutObjectCmd()
261 wxLayoutObjectCmd::GetStyle(wxLayoutStyleInfo
*si
) const
263 si
->size
= m_font
->GetPointSize();
264 si
->family
= m_font
->GetFamily();
265 si
->style
= m_font
->GetStyle();
266 si
->underline
= m_font
->GetUnderlined();
267 si
->weight
= m_font
->GetWeight();
269 si
->fg_red
= m_ColourFG
->Red();
270 si
->fg_green
= m_ColourFG
->Green();
271 si
->fg_blue
= m_ColourFG
->Blue();
272 si
->bg_red
= m_ColourBG
->Red();
273 si
->bg_green
= m_ColourBG
->Green();
274 si
->bg_blue
= m_ColourBG
->Blue();
278 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint
const & /* coords */)
283 dc
.SetTextForeground(*m_ColourFG
);
285 dc
.SetTextBackground(*m_ColourBG
);
289 wxLayoutObjectCmd::Layout(wxDC
&dc
)
291 // this get called, so that recalculation uses right font sizes
292 Draw(dc
, wxPoint(0,0));
296 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
298 The wxLayoutLine object
300 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
302 wxLayoutLine::wxLayoutLine(wxLayoutLine
*prev
)
310 RecalculatePosition();
313 m_LineNumber
= m_Previous
->GetLineNumber()+1;
314 m_Next
= m_Previous
->GetNextLine();
315 m_Previous
->m_Next
= this;
316 m_Height
= m_Previous
->GetHeight();
320 m_Next
->m_Previous
= this;
321 m_Next
->MoveLines(+1);
322 m_Next
->RecalculatePositions(1);
326 wxLayoutLine::~wxLayoutLine()
328 // kbList cleans itself
332 wxLayoutLine::RecalculatePosition(void)
335 m_Position
= m_Previous
->GetPosition() +
336 wxPoint(0,m_Previous
->GetHeight());
338 m_Position
= wxPoint(0,0);
343 wxLayoutLine::RecalculatePositions(int recurse
)
345 wxASSERT(recurse
>= 0);
346 wxPoint pos
= m_Position
;
347 CoordType height
= m_Height
;
349 // WXLO_TRACE("RecalculatePositions()");
350 RecalculatePosition();
355 if(m_Next
) m_Next
->RecalculatePositions(--recurse
);
357 else if(pos
!= m_Position
|| m_Height
!= height
)
358 if(m_Next
) m_Next
->RecalculatePositions();
362 wxLayoutObjectList::iterator
363 wxLayoutLine::FindObject(CoordType xpos
, CoordType
*offset
) const
367 wxLayoutObjectList::iterator i
;
368 CoordType x
= 0, len
;
371 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
373 len
= (**i
).GetLength();
374 if( x
<= xpos
&& xpos
<= x
+ len
)
379 x
+= (**i
).GetLength();
384 wxLayoutObjectList::iterator
385 wxLayoutLine::FindObjectScreen(wxDC
&dc
, CoordType xpos
, CoordType
*cxpos
) const
389 wxLayoutObjectList::iterator i
;
390 CoordType x
= 0, cx
= 0, width
;
392 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
395 width
= (**i
).GetWidth();
396 if( x
<= xpos
&& xpos
<= x
+ width
)
398 *cxpos
= cx
+ (**i
).GetOffsetScreen(dc
, xpos
-x
);
399 wxLogDebug("wxLayoutLine::FindObjectScreen: cursor xpos = %ld", *cxpos
);
402 x
+= (**i
).GetWidth();
403 cx
+= (**i
).GetLength();
405 // behind last object:
407 return m_ObjectList
.tail();
411 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
414 wxASSERT(obj
!= NULL
);
416 wxLOiterator i
= FindObject(xpos
, &offset
);
419 if(xpos
== 0 ) // aha, empty line!
421 m_ObjectList
.push_back(obj
);
422 m_Length
+= obj
->GetLength();
429 CoordType len
= (**i
).GetLength();
430 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
431 { // insert before this object
432 m_ObjectList
.insert(i
,obj
);
433 m_Length
+= obj
->GetLength();
438 if( i
== m_ObjectList
.tail()) // last object?
439 m_ObjectList
.push_back(obj
);
441 { // insert after current object
443 m_ObjectList
.insert(i
,obj
);
445 m_Length
+= obj
->GetLength();
448 /* Otherwise we need to split the current object.
449 Fortunately this can only be a text object. */
450 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
451 wxString left
, right
;
452 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
453 left
= tobj
->GetText().substr(0,offset
);
454 right
= tobj
->GetText().substr(offset
,len
-offset
);
455 // current text object gets set to right half
456 tobj
->GetText() = right
; // set new text
457 // before it we insert the new object
458 m_ObjectList
.insert(i
,obj
);
459 m_Length
+= obj
->GetLength();
460 // and before that we insert the left half
461 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
466 wxLayoutLine::Insert(CoordType xpos
, wxString text
)
470 wxLOiterator i
= FindObject(xpos
, &offset
);
471 if(i
!= NULLIT
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
473 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
474 tobj
->GetText().insert(offset
, text
);
475 m_Length
+= text
.Length();
480 return Insert(xpos
, new wxLayoutObjectText(text
));
484 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
486 CoordType offset
, len
;
490 wxLOiterator i
= FindObject(xpos
, &offset
);
493 if(i
== NULLIT
) return npos
;
494 // now delete from that object:
495 if((**i
).GetType() != WXLO_TYPE_TEXT
)
497 if(offset
!= 0) // at end of line after a non-text object
500 len
= (**i
).GetLength();
503 m_ObjectList
.erase(i
);
507 // tidy up: remove empty text objects
508 if((**i
).GetLength() == 0)
510 m_ObjectList
.erase(i
);
514 CoordType max
= (**i
).GetLength() - offset
;
515 if(npos
< max
) max
= npos
;
518 if(xpos
== GetLength())
521 { // at the end of an object
522 // move to begin of next object:
524 continue; // start over
529 if(offset
== 0 && max
== (**i
).GetLength())
530 m_ObjectList
.erase(i
); // remove the whole object
532 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
539 wxLayoutLine::DeleteWord(CoordType xpos
)
544 wxLOiterator i
= FindObject(xpos
, &offset
);
548 if(i
== NULLIT
) return false;
549 if((**i
).GetType() != WXLO_TYPE_TEXT
)
551 // This should only happen when at end of line, behind a non-text
553 if(offset
== (**i
).GetLength()) return false;
554 m_Length
-= (**i
).GetLength(); // -1
555 m_ObjectList
.erase(i
);
556 return true; // we are done
560 if(offset
== (**i
).GetLength()) // at end of object
565 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
567 wxString str
= tobj
->GetText();
568 str
= str
.substr(offset
,str
.Length()-offset
);
569 // Find out how many positions we need to delete:
570 // 1. eat leading space
571 while(isspace(str
[count
])) count
++;
572 // 2. eat the word itself:
573 while(isalnum(str
[count
])) count
++;
575 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
576 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
581 wxASSERT(0); // we should never arrive here
585 wxLayoutLine::DeleteLine(bool update
)
587 if(m_Next
) m_Next
->m_Previous
= m_Previous
;
588 if(m_Previous
) m_Previous
->m_Next
= m_Next
;
591 m_Next
->MoveLines(-1);
592 m_Next
->RecalculatePositions(1);
594 wxLayoutLine
*next
= m_Next
;
600 wxLayoutLine::Draw(wxDC
&dc
, const wxPoint
& offset
) const
602 wxLayoutObjectList::iterator i
;
603 wxPoint pos
= offset
;
604 pos
= pos
+ GetPosition();
608 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
611 pos
.x
+= (**i
).GetWidth();
616 wxLayoutLine::Layout(wxDC
&dc
, wxPoint
*cursorPos
,
620 wxLayoutObjectList::iterator i
;
623 oldHeight
= m_Height
;
625 topHeight
, bottomHeight
; // above and below baseline
628 objTopHeight
, objBottomHeight
;
631 m_Height
= 0; m_BaseLine
= 0;
633 topHeight
= 0; bottomHeight
= 0;
635 bool cursorFound
= false;
639 *cursorPos
= m_Position
;
642 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
645 size
= (**i
).GetSize(&objTopHeight
, &objBottomHeight
);
647 if(cursorPos
&& ! cursorFound
)
648 { // we need to check whether the text cursor is here
649 len
= (**i
).GetLength();
650 if(count
<= cx
&& count
+len
> cx
)
652 if((**i
).GetType() == WXLO_TYPE_TEXT
)
654 len
= cx
- count
; // pos in object
655 CoordType width
, height
, descent
;
656 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
657 &width
, &height
, &descent
);
658 cursorPos
->x
+= width
;
659 cursorPos
->y
= m_Position
.y
;
661 if(len
< (**i
).GetLength())
662 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
664 str
= WXLO_CURSORCHAR
;
665 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
666 wxASSERT(cursorSize
);
667 // Just in case some joker inserted an empty string object:
668 if(width
== 0) width
= WXLO_MINIMUM_CURSOR_WIDTH
;
669 if(height
== 0) height
= objHeight
;
670 cursorSize
->x
= width
;
671 cursorSize
->y
= height
;
672 cursorFound
= true; // no more checks
675 { // on some other object
676 CoordType top
, bottom
; // unused
677 *cursorSize
= (**i
).GetSize(&top
,&bottom
);
678 cursorPos
->y
= m_Position
.y
;
679 cursorFound
= true; // no more checks
685 cursorPos
->x
+= (**i
).GetWidth();
690 if(objHeight
> m_Height
) m_Height
= objHeight
;
691 if(objTopHeight
> topHeight
) topHeight
= objTopHeight
;
692 if(objBottomHeight
> bottomHeight
) bottomHeight
= objBottomHeight
;
694 if(topHeight
+ bottomHeight
> m_Height
) m_Height
=
695 topHeight
+bottomHeight
;
696 m_BaseLine
= topHeight
;
700 if(GetPreviousLine()) // empty line
702 m_Height
= GetPreviousLine()->GetHeight();
703 m_BaseLine
= GetPreviousLine()->m_BaseLine
;
707 CoordType width
, height
, descent
;
708 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
710 m_BaseLine
= m_Height
- descent
;
715 // tell next line about coordinate change
716 if(m_Next
&& objHeight
!= oldHeight
)
717 m_Next
->RecalculatePositions();
719 // We need to check whether we found a valid cursor size:
722 // this might be the case if the cursor is at the end of the
723 // line or on a command object:
724 if(cursorSize
->y
< WXLO_MINIMUM_CURSOR_WIDTH
)
728 cursorSize
->y
= m_BaseLine
;
729 if(cursorSize
->x
< WXLO_MINIMUM_CURSOR_WIDTH
) cursorSize
->x
= WXLO_MINIMUM_CURSOR_WIDTH
;
733 CoordType width
, height
, descent
;
734 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
735 cursorSize
->x
= width
;
736 cursorSize
->y
= height
;
739 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
740 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
746 wxLayoutLine::Break(CoordType xpos
)
751 { // insert an empty line before this one
752 wxLayoutLine
*prev
= new wxLayoutLine(m_Previous
);
753 if(m_Previous
== NULL
)
754 { // We were in first line, need to link in new empty line
758 m_Previous
->m_Height
= GetHeight(); // this is a wild guess
762 m_Next
->RecalculatePositions(1);
767 wxLOiterator i
= FindObject(xpos
, &offset
);
769 // must be at the end of the line then
770 return new wxLayoutLine(this);
773 wxLayoutLine
*newLine
= new wxLayoutLine(this);
774 // split object at i:
775 if((**i
).GetType() == WXLO_TYPE_TEXT
&& offset
!= 0)
777 wxString left
, right
;
778 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
779 left
= tobj
->GetText().substr(0,offset
);
780 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
781 // current text object gets set to left half
782 tobj
->GetText() = left
; // set new text
783 newLine
->Append(new wxLayoutObjectText(right
));
784 m_Length
-= right
.Length();
785 i
++; // don't move this object to the new list
789 i
++; // move objects from here to new list
791 while(i
!= m_ObjectList
.end())
794 m_Length
-= (**i
).GetLength();
795 m_ObjectList
.remove(i
); // remove without deleting it
798 m_Next
->RecalculatePositions(2);
804 wxLayoutLine::MergeNextLine(void)
806 wxASSERT(GetNextLine());
807 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
810 for(i
= list
.begin(); i
!= list
.end();)
813 list
.remove(i
); // remove without deleting it
815 wxASSERT(list
.empty());
816 wxLayoutLine
*oldnext
= GetNextLine();
817 SetNext(GetNextLine()->GetNextLine());
819 RecalculatePositions(1);
823 wxLayoutLine::GetWrapPosition(CoordType column
)
826 wxLOiterator i
= FindObject(column
, &offset
);
827 if(i
== NULLIT
) return -1; // cannot wrap
829 // go backwards through the list and look for space in text objects
832 if((**i
).GetType() == WXLO_TYPE_TEXT
)
836 if( isspace(((wxLayoutObjectText
*)*i
)->GetText()[(size_t)offset
]))
843 }while(offset
!= -1);
844 i
--; // move on to previous object
848 column
-= (**i
).GetLength();
852 offset
= (**i
).GetLength();
854 /* If we reached the begin of the list and have more than one
855 object, that one is longer than the margin, so break behind
858 i
= m_ObjectList
.begin();
859 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
861 pos
+= (**i
).GetLength();
864 if(i
== NULLIT
) return -1; //why should this happen?
865 pos
+= (**i
).GetLength();
867 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
869 pos
+= (**i
).GetLength();
872 if(i
== NULLIT
) return -1; //this is possible, if there is only one text object
873 // now we are at the second text object:
874 pos
-= (**i
).GetLength();
875 return pos
; // in front of it
879 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
881 The wxLayoutList object
883 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
885 wxLayoutList::wxLayoutList()
887 m_DefaultSetting
= NULL
;
889 InvalidateUpdateRect();
893 wxLayoutList::~wxLayoutList()
899 wxLayoutList::Empty(void)
902 m_FirstLine
= m_FirstLine
->DeleteLine(false);
904 m_CursorPos
= wxPoint(0,0);
905 m_CursorScreenPos
= wxPoint(0,0);
906 m_CursorSize
= wxPoint(0,0);
907 m_FirstLine
= new wxLayoutLine(NULL
); // empty first line
908 m_CursorLine
= m_FirstLine
;
913 wxLayoutList::InternalClear(void)
918 delete m_DefaultSetting
;
919 m_DefaultSetting
= NULL
;
924 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
925 int underline
, wxColour
const *fg
,
928 if(family
!= -1) m_FontFamily
= family
;
929 if(size
!= -1) m_FontPtSize
= size
;
930 if(style
!= -1) m_FontStyle
= style
;
931 if(weight
!= -1) m_FontWeight
= weight
;
932 if(underline
!= -1) m_FontUnderline
= underline
!= 0;
934 if(fg
!= NULL
) m_ColourFG
= fg
;
935 if(bg
!= NULL
) m_ColourBG
= bg
;
938 new wxLayoutObjectCmd(m_FontPtSize
,m_FontFamily
,m_FontStyle
,m_FontWeight
,m_FontUnderline
,
939 m_ColourFG
, m_ColourBG
));
943 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
944 int underline
, char const *fg
, char const *bg
)
952 cfg
= wxTheColourDatabase
->FindColour(fg
);
954 cbg
= wxTheColourDatabase
->FindColour(bg
);
956 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
960 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
961 int /* underline */, char const *fg
, char const *bg
)
967 m_FontUnderline
= false;
968 m_FontFamily
= family
;
970 m_FontWeight
= weight
;
971 m_ColourFG
= wxTheColourDatabase
->FindColour(fg
);
972 m_ColourBG
= wxTheColourDatabase
->FindColour(bg
);
974 if(! m_ColourFG
) m_ColourFG
= wxBLACK
;
975 if(! m_ColourBG
) m_ColourBG
= wxWHITE
;
978 delete m_DefaultSetting
;
980 m_DefaultSetting
= new
981 wxLayoutObjectCmd(m_FontPtSize
,m_FontFamily
,m_FontStyle
,
982 m_FontWeight
,m_FontUnderline
,
983 m_ColourFG
, m_ColourBG
);
989 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
991 wxLayoutLine
*line
= m_FirstLine
;
992 while(line
&& line
->GetLineNumber() != p
.y
)
993 line
= line
->GetNextLine();
994 if(line
&& line
->GetLineNumber() == p
.y
) // found it
998 CoordType len
= line
->GetLength();
1001 m_CursorPos
.x
= p
.x
;
1006 m_CursorPos
.x
= len
;
1014 wxLayoutList::MoveCursorVertically(int n
)
1016 if(n
< 0) // move up
1018 if(m_CursorLine
== m_FirstLine
) return false;
1019 while(n
< 0 && m_CursorLine
)
1021 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1027 m_CursorLine
= m_FirstLine
;
1033 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1034 m_CursorPos
.x
= m_CursorLine
->GetLength();
1040 wxLayoutLine
*last
= m_CursorLine
;
1041 if(! m_CursorLine
->GetNextLine()) return false;
1042 while(n
> 0 && m_CursorLine
)
1046 m_CursorLine
= m_CursorLine
->GetNextLine();
1050 m_CursorLine
= last
;
1056 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1057 m_CursorPos
.x
= m_CursorLine
->GetLength();
1064 wxLayoutList::MoveCursorHorizontally(int n
)
1069 if(m_CursorPos
.x
== 0) // at begin of line
1071 if(! MoveCursorVertically(-1))
1073 MoveCursorToEndOfLine();
1078 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1079 m_CursorPos
.x
-= move
; n
+= move
;
1084 int len
= m_CursorLine
->GetLength();
1085 if(m_CursorPos
.x
== len
) // at end of line
1087 if(! MoveCursorVertically(1))
1089 MoveCursorToBeginOfLine();
1094 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1095 m_CursorPos
.x
+= move
;
1102 wxLayoutList::Insert(wxString
const &text
)
1104 wxASSERT(m_CursorLine
);
1105 m_CursorLine
->Insert(m_CursorPos
.x
, text
);
1106 m_CursorPos
.x
+= text
.Length();
1111 wxLayoutList::Insert(wxLayoutObject
*obj
)
1113 wxASSERT(m_CursorLine
);
1114 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
1115 m_CursorPos
.x
+= obj
->GetLength();
1120 wxLayoutList::LineBreak(void)
1122 wxASSERT(m_CursorLine
);
1124 bool setFirst
= (m_CursorLine
== m_FirstLine
&& m_CursorPos
.x
== 0);
1125 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
);
1126 if(setFirst
) // we were at beginning of first line
1127 m_FirstLine
= m_CursorLine
->GetPreviousLine();
1134 wxLayoutList::WrapLine(CoordType column
)
1136 if(m_CursorPos
.x
<= column
|| column
< 1)
1137 return false; // do nothing yet
1140 CoordType xpos
= m_CursorLine
->GetWrapPosition(column
);
1142 return false; // cannot break line
1144 CoordType newpos
= m_CursorPos
.x
- xpos
- 1;
1145 m_CursorPos
.x
= xpos
;
1147 Delete(1); // delete the space
1148 m_CursorPos
.x
= newpos
;
1154 wxLayoutList::Delete(CoordType npos
)
1156 wxASSERT(m_CursorLine
);
1160 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
1163 // More to delete, continue on next line.
1164 // First, check if line is empty:
1165 if(m_CursorLine
->GetLength() == 0)
1166 { // in this case, updating could probably be optimised
1168 wxASSERT(DeleteLines(1) == 0);
1177 // Need to join next line
1178 if(! m_CursorLine
->GetNextLine())
1182 m_CursorLine
->MergeNextLine();
1192 wxLayoutList::DeleteLines(int n
)
1194 wxASSERT(m_CursorLine
);
1198 if(!m_CursorLine
->GetNextLine())
1199 { // we cannot delete this line, but we can clear it
1200 MoveCursorToBeginOfLine();
1201 DeleteToEndOfLine();
1205 line
= m_CursorLine
;
1206 m_CursorLine
= m_CursorLine
->DeleteLine(true);
1208 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
1209 wxASSERT(m_FirstLine
);
1210 wxASSERT(m_CursorLine
);
1212 m_CursorLine
->RecalculatePositions(2);
1217 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
) const
1219 wxLayoutLine
*line
= m_FirstLine
;
1221 // first, make sure everything is calculated - this might not be
1222 // needed, optimise it later
1223 m_DefaultSetting
->Layout(dc
);
1226 line
->RecalculatePosition(); // so we don't need to do it all the time
1227 // little condition to speed up redrawing:
1228 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1229 line
= line
->GetNextLine();
1234 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
) const
1236 wxLayoutLine
*line
= m_FirstLine
;
1238 // first, make sure everything is calculated - this might not be
1239 // needed, optimise it later
1240 m_DefaultSetting
->Layout(dc
);
1243 if(line
== m_CursorLine
)
1244 line
->Layout(dc
, (wxPoint
*)&m_CursorScreenPos
, (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
1247 // little condition to speed up redrawing:
1248 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1249 line
= line
->GetNextLine();
1252 ///FIXME: disabled for now
1254 // can only be 0 if we are on the first line and have no next line
1255 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
1256 m_CursorLine
->GetNextLine() == NULL
&&
1257 m_CursorLine
== m_FirstLine
));
1262 wxLayoutList::Draw(wxDC
&dc
, wxPoint
const &offset
,
1263 CoordType top
, CoordType bottom
) const
1265 wxLayoutLine
*line
= m_FirstLine
;
1268 m_DefaultSetting
->Draw(dc
, wxPoint(0,0));
1269 wxBrush
*brush
= new wxBrush(*m_ColourBG
, wxSOLID
);
1270 dc
.SetBrush(*brush
);
1275 // only draw if between top and bottom:
1276 if((top
== -1 || line
->GetPosition().y
>= top
))
1277 line
->Draw(dc
, offset
);
1278 // little condition to speed up redrawing:
1279 if(bottom
!= -1 && line
->GetPosition().y
+ line
->GetHeight() > bottom
) break;
1280 line
= line
->GetNextLine();
1282 // can only be 0 if we are on the first line and have no next line
1283 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
1284 m_CursorLine
->GetNextLine() == NULL
&&
1285 m_CursorLine
== m_FirstLine
));
1289 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
, wxPoint
*cursorPos
)
1291 // First, find the right line:
1292 wxLayoutLine
*line
= m_FirstLine
;
1295 // we need to run a layout here to get font sizes right :-(
1296 m_DefaultSetting
->Layout(dc
);
1299 p
= line
->GetPosition();
1300 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
1303 line
= line
->GetNextLine();
1305 if(line
== NULL
) return NULL
; // not found
1306 if(cursorPos
) cursorPos
->y
= line
->GetLineNumber();
1307 // Now, find the object in the line:
1308 wxLOiterator i
= line
->FindObjectScreen(dc
, pos
.x
, & cursorPos
->x
);
1309 return (i
== NULLIT
) ? NULL
: *i
;
1314 wxLayoutList::GetSize(void) const
1317 *line
= m_FirstLine
,
1320 return wxPoint(0,0);
1322 wxPoint
maxPoint(0,0);
1327 if(line
->GetWidth() > maxPoint
.x
)
1328 maxPoint
.x
= line
->GetWidth();
1330 line
= line
->GetNextLine();
1333 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
1338 wxLayoutList::DrawCursor(wxDC
&dc
, bool active
, wxPoint
const &translate
)
1341 coords
= m_CursorScreenPos
;
1342 coords
.x
+= translate
.x
;
1343 coords
.y
+= translate
.y
;
1345 #ifdef WXLAYOUT_DEBUG
1346 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
1347 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
1348 (long)coords
.x
, (long)coords
.y
,
1349 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
1350 (long)m_CursorLine
->GetLineNumber(),
1351 (long)m_CursorLine
->GetLength()));
1354 dc
.SetBrush(*wxBLACK_BRUSH
);
1355 dc
.SetLogicalFunction(wxXOR
);
1356 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
1358 dc
.DrawRectangle(coords
.x
, coords
.y
, m_CursorSize
.x
,
1361 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
1362 coords
.x
+m_CursorSize
.x
, coords
.y
+m_CursorSize
.y
-1);
1363 dc
.SetLogicalFunction(wxCOPY
);
1364 //dc.SetBrush(wxNullBrush);
1367 /** Called by the objects to update the update rectangle.
1368 @param p a point to include in it
1371 wxLayoutList::SetUpdateRect(const wxPoint
&p
)
1373 if(m_UpdateRectValid
)
1374 GrowRect(m_UpdateRect
, p
);
1377 m_UpdateRect
.x
= p
.x
;
1378 m_UpdateRect
.y
= p
.y
;
1379 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
1380 m_UpdateRect
.height
= 4;// wxGTK :-)
1381 m_UpdateRectValid
= true;
1386 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1390 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1392 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
1393 wxString
const & title
)
1401 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
1403 // The following bit is taken from the printing sample, let's see
1404 // whether it works for us.
1406 /* You might use THIS code to set the printer DC to ROUGHLY reflect
1407 * the screen text size. This page also draws lines of actual length 5cm
1410 // Get the logical pixels per inch of screen and printer
1411 int ppiScreenX
, ppiScreenY
;
1412 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
1413 int ppiPrinterX
, ppiPrinterY
;
1414 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
1416 if(ppiScreenX
== 0) // not yet set, need to guess
1421 if(ppiPrinterX
== 0) // not yet set, need to guess
1427 // This scales the DC so that the printout roughly represents the
1428 // the screen scaling. The text point size _should_ be the right size
1429 // but in fact is too small for some reason. This is a detail that will
1430 // need to be addressed at some point but can be fudged for the
1432 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
1434 // Now we have to check in case our real page size is reduced
1435 // (e.g. because we're drawing to a print preview memory DC)
1436 int pageWidth
, pageHeight
;
1438 dc
->GetSize(&w
, &h
);
1439 GetPageSizePixels(&pageWidth
, &pageHeight
);
1440 if(pageWidth
!= 0) // doesn't work always
1442 // If printer pageWidth == current DC width, then this doesn't
1443 // change. But w might be the preview bitmap width, so scale down.
1444 scale
= scale
* (float)(w
/(float)pageWidth
);
1446 dc
->SetUserScale(scale
, scale
);
1450 bool wxLayoutPrintout::OnPrintPage(int page
)
1459 top
= (page
- 1)*m_PrintoutHeight
;
1460 bottom
= top
+ m_PrintoutHeight
;
1461 // SetDeviceOrigin() doesn't work here, so we need to manually
1462 // translate all coordinates.
1463 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
1464 m_llist
->Draw(*dc
, translate
, top
, bottom
);
1471 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
1473 /* We allocate a temporary wxDC for printing, so that we can
1474 determine the correct paper size and scaling. We don't actually
1475 print anything on it. */
1477 wxPrinterDC
psdc("","",WXLLIST_TEMPFILE
,false);
1479 wxPostScriptDC
psdc(WXLLIST_TEMPFILE
,false);
1482 float scale
= ScaleDC(&psdc
);
1484 psdc
.GetSize(&m_PageWidth
, &m_PageHeight
);
1485 // This sets a left/top origin of 10% and 5%:
1486 m_Offset
= wxPoint(m_PageWidth
/10, m_PageHeight
/20);
1488 // This is the length of the printable area.
1489 m_PrintoutHeight
= m_PageHeight
- (int) (m_PageHeight
* 0.15);
1490 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
1494 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
1497 *maxPage
= m_NumOfPages
;
1500 *selPageTo
= m_NumOfPages
;
1501 wxRemoveFile(WXLLIST_TEMPFILE
);
1504 bool wxLayoutPrintout::HasPage(int pageNum
)
1506 return pageNum
<= m_NumOfPages
;
1510 Stupid wxWindows doesn't draw proper ellipses, so we comment this
1511 out. It's a waste of paper anyway.
1515 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
1516 wxPoint topleft
, wxPoint bottomright
,
1519 // make backups of all essential parameters
1520 const wxBrush
& brush
= dc
.GetBrush();
1521 const wxPen
& pen
= dc
.GetPen();
1522 const wxFont
& font
= dc
.GetFont();
1524 dc
.SetBrush(*wxWHITE_BRUSH
);
1525 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
1526 dc
.DrawRoundedRectangle(topleft
.x
,
1527 topleft
.y
,bottomright
.x
-topleft
.x
,
1528 bottomright
.y
-topleft
.y
);
1529 dc
.SetBrush(*wxBLACK_BRUSH
);
1530 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
1531 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
1535 page
= "9999/9999 "; // many pages...
1537 dc
.GetTextExtent(page
,&w
,&h
);
1538 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
1539 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
1540 dc
.GetTextExtent("XXXX", &w
,&h
);
1541 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);