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"
17 #include "wx/wxprec.h"
24 # include "gui/wxllist.h"
30 # include "iostream.h"
33 # include <wx/print.h>
39 /// This should never really get created
40 #define WXLLIST_TEMPFILE "__wxllist.tmp"
44 # define TypewxString(t) g_aTypewxStrings[t]
45 # define WXLO_DEBUG(x) wxLogDebug x
47 static const char *g_aTypewxStrings
[] =
49 "invalid", "text", "cmd", "icon"
52 wxLayoutObject::Debug(void)
54 WXLO_DEBUG(("%s",g_aTypewxStrings
[GetType()]));
57 # define TypewxString(t) ""
58 # define WXLO_DEBUG(x)
62 /// Cursors smaller than this disappear in XOR drawing mode
63 #define WXLO_MINIMUM_CURSOR_WIDTH 4
65 /// Use this character to estimate a cursor size when none is available.
66 #define WXLO_CURSORCHAR "E"
67 /** @name Helper functions */
69 /// allows me to compare to wxPoints
70 bool operator ==(wxPoint
const &p1
, wxPoint
const &p2
)
72 return p1
.x
== p2
.x
&& p1
.y
== p2
.y
;
75 /// allows me to compare to wxPoints
76 bool operator !=(wxPoint
const &p1
, wxPoint
const &p2
)
78 return p1
.x
!= p2
.x
|| p1
.y
!= p2
.y
;
81 /// grows a wxRect so that it includes the given point
83 static void GrowRect(wxRect
&r
, const wxPoint
& p
)
87 else if(r
.x
+ r
.width
< p
.x
)
92 else if(r
.y
+ r
.height
< p
.y
)
97 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
101 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
103 wxLayoutObjectText::wxLayoutObjectText(const wxString
&txt
)
113 wxLayoutObjectText::Copy(void)
115 wxLayoutObjectText
*obj
= new wxLayoutObjectText(m_Text
);
116 obj
->m_Width
= m_Width
;
117 obj
->m_Height
= m_Height
;
119 obj
->m_Bottom
= m_Bottom
;
120 obj
->SetUserData(m_UserData
);
125 wxLayoutObjectText::GetSize(CoordType
*top
, CoordType
*bottom
) const
128 *top
= m_Top
; *bottom
= m_Bottom
;
129 return wxPoint(m_Width
, m_Height
);
133 wxLayoutObjectText::Draw(wxDC
&dc
, wxPoint
const &coords
)
135 dc
.DrawText(m_Text
, coords
.x
, coords
.y
-m_Top
);
139 wxLayoutObjectText::GetOffsetScreen(wxDC
&dc
, CoordType xpos
) const
143 maxlen
= m_Text
.Length();
146 height
, descent
= 0l;
148 if(xpos
== 0) return 0; // easy
150 while(width
< xpos
&& offs
< maxlen
)
152 dc
.GetTextExtent(m_Text
.substr(0,offs
),
153 &width
, &height
, &descent
);
156 /* We have to substract 1 to compensate for the offs++, and another
157 one because we don't want to position the cursor behind the
158 object what we clicked on, but before - otherwise it looks
160 return (xpos
> 2) ? offs
-2 : 0;
164 wxLayoutObjectText::Layout(wxDC
&dc
)
168 dc
.GetTextExtent(m_Text
,&m_Width
, &m_Height
, &descent
);
170 m_Top
= m_Height
- m_Bottom
;
173 #ifdef WXLAYOUT_DEBUG
175 wxLayoutObjectText::Debug(void)
177 wxLayoutObject::Debug();
178 WXLO_DEBUG((" `%s`", m_Text
.c_str()));
182 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
186 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
188 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
const &icon
)
190 m_Icon
= new wxBitmap(icon
);
194 wxLayoutObjectIcon::Copy(void)
196 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon(new
198 obj
->SetUserData(m_UserData
);
202 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
*icon
)
208 wxLayoutObjectIcon::Draw(wxDC
&dc
, wxPoint
const &coords
)
210 dc
.DrawBitmap(*m_Icon
, coords
.x
, coords
.y
-m_Icon
->GetHeight(),
211 (m_Icon
->GetMask() == NULL
) ? FALSE
: TRUE
);
215 wxLayoutObjectIcon::Layout(wxDC
& /* dc */)
220 wxLayoutObjectIcon::GetSize(CoordType
*top
, CoordType
*bottom
) const
222 *top
= m_Icon
->GetHeight();
224 return wxPoint(m_Icon
->GetWidth(), m_Icon
->GetHeight());
229 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
233 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
235 wxLayoutObjectCmd::wxLayoutObjectCmd(int size
, int family
, int style
, int
236 weight
, bool underline
,
237 wxColour
const *fg
, wxColour
const *bg
)
240 m_font
= new wxFont(size
,family
,style
,weight
,underline
);
246 wxLayoutObjectCmd::Copy(void)
248 wxLayoutStyleInfo si
;
251 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd(
252 si
.size
, si
.family
, si
.style
, si
.weight
, si
.underline
,
253 m_ColourFG
, m_ColourBG
);
254 obj
->SetUserData(m_UserData
);
259 wxLayoutObjectCmd::~wxLayoutObjectCmd()
265 wxLayoutObjectCmd::GetStyle(wxLayoutStyleInfo
*si
) const
267 si
->size
= m_font
->GetPointSize();
268 si
->family
= m_font
->GetFamily();
269 si
->style
= m_font
->GetStyle();
270 si
->underline
= m_font
->GetUnderlined();
271 si
->weight
= m_font
->GetWeight();
273 si
->fg_red
= m_ColourFG
->Red();
274 si
->fg_green
= m_ColourFG
->Green();
275 si
->fg_blue
= m_ColourFG
->Blue();
276 si
->bg_red
= m_ColourBG
->Red();
277 si
->bg_green
= m_ColourBG
->Green();
278 si
->bg_blue
= m_ColourBG
->Blue();
282 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint
const & /* coords */)
287 dc
.SetTextForeground(*m_ColourFG
);
289 dc
.SetTextBackground(*m_ColourBG
);
293 wxLayoutObjectCmd::Layout(wxDC
&dc
)
295 // this get called, so that recalculation uses right font sizes
296 Draw(dc
, wxPoint(0,0));
300 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
302 The wxLayoutLine object
304 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
306 wxLayoutLine::wxLayoutLine(wxLayoutLine
*prev
)
309 m_Width
= m_Height
= 0;
314 RecalculatePosition();
317 m_LineNumber
= m_Previous
->GetLineNumber()+1;
318 m_Next
= m_Previous
->GetNextLine();
319 m_Previous
->m_Next
= this;
320 m_Height
= m_Previous
->GetHeight();
324 m_Next
->m_Previous
= this;
325 m_Next
->MoveLines(+1);
326 m_Next
->RecalculatePositions(1);
330 wxLayoutLine::~wxLayoutLine()
332 // kbList cleans itself
336 wxLayoutLine::RecalculatePosition(void)
339 m_Position
= m_Previous
->GetPosition() +
340 wxPoint(0,m_Previous
->GetHeight());
342 m_Position
= wxPoint(0,0);
347 wxLayoutLine::RecalculatePositions(int recurse
)
349 wxASSERT(recurse
>= 0);
350 wxPoint pos
= m_Position
;
351 CoordType height
= m_Height
;
353 // WXLO_TRACE("RecalculatePositions()");
354 RecalculatePosition();
359 if(m_Next
) m_Next
->RecalculatePositions(--recurse
);
361 else if(pos
!= m_Position
|| m_Height
!= height
)
362 if(m_Next
) m_Next
->RecalculatePositions();
366 wxLayoutObjectList::iterator
367 wxLayoutLine::FindObject(CoordType xpos
, CoordType
*offset
) const
371 wxLayoutObjectList::iterator i
;
372 CoordType x
= 0, len
;
375 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
377 len
= (**i
).GetLength();
378 if( x
<= xpos
&& xpos
<= x
+ len
)
383 x
+= (**i
).GetLength();
388 wxLayoutObjectList::iterator
389 wxLayoutLine::FindObjectScreen(wxDC
&dc
, CoordType xpos
, CoordType
*cxpos
) const
393 wxLayoutObjectList::iterator i
;
394 CoordType x
= 0, cx
= 0, width
;
396 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
399 width
= (**i
).GetWidth();
400 if( x
<= xpos
&& xpos
<= x
+ width
)
402 *cxpos
= cx
+ (**i
).GetOffsetScreen(dc
, xpos
-x
);
403 wxLogDebug("wxLayoutLine::FindObjectScreen: cursor xpos = %ld", *cxpos
);
406 x
+= (**i
).GetWidth();
407 cx
+= (**i
).GetLength();
409 // behind last object:
411 return m_ObjectList
.tail();
415 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
418 wxASSERT(obj
!= NULL
);
420 wxLOiterator i
= FindObject(xpos
, &offset
);
423 if(xpos
== 0 ) // aha, empty line!
425 m_ObjectList
.push_back(obj
);
426 m_Length
+= obj
->GetLength();
433 CoordType len
= (**i
).GetLength();
434 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
435 { // insert before this object
436 m_ObjectList
.insert(i
,obj
);
437 m_Length
+= obj
->GetLength();
442 if( i
== m_ObjectList
.tail()) // last object?
443 m_ObjectList
.push_back(obj
);
445 { // insert after current object
447 m_ObjectList
.insert(i
,obj
);
449 m_Length
+= obj
->GetLength();
452 /* Otherwise we need to split the current object.
453 Fortunately this can only be a text object. */
454 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
455 wxString left
, right
;
456 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
457 left
= tobj
->GetText().substr(0,offset
);
458 right
= tobj
->GetText().substr(offset
,len
-offset
);
459 // current text object gets set to right half
460 tobj
->GetText() = right
; // set new text
461 // before it we insert the new object
462 m_ObjectList
.insert(i
,obj
);
463 m_Length
+= obj
->GetLength();
464 // and before that we insert the left half
465 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
470 wxLayoutLine::Insert(CoordType xpos
, wxString text
)
474 wxLOiterator i
= FindObject(xpos
, &offset
);
475 if(i
!= NULLIT
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
477 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
478 tobj
->GetText().insert(offset
, text
);
479 m_Length
+= text
.Length();
484 return Insert(xpos
, new wxLayoutObjectText(text
));
488 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
490 CoordType offset
, len
;
494 wxLOiterator i
= FindObject(xpos
, &offset
);
497 if(i
== NULLIT
) return npos
;
498 // now delete from that object:
499 if((**i
).GetType() != WXLO_TYPE_TEXT
)
501 if(offset
!= 0) // at end of line after a non-text object
504 len
= (**i
).GetLength();
507 m_ObjectList
.erase(i
);
511 // tidy up: remove empty text objects
512 if((**i
).GetLength() == 0)
514 m_ObjectList
.erase(i
);
518 CoordType max
= (**i
).GetLength() - offset
;
519 if(npos
< max
) max
= npos
;
522 if(xpos
== GetLength())
525 { // at the end of an object
526 // move to begin of next object:
528 continue; // start over
533 if(offset
== 0 && max
== (**i
).GetLength())
534 m_ObjectList
.erase(i
); // remove the whole object
536 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
543 wxLayoutLine::DeleteWord(CoordType xpos
)
548 wxLOiterator i
= FindObject(xpos
, &offset
);
552 if(i
== NULLIT
) return false;
553 if((**i
).GetType() != WXLO_TYPE_TEXT
)
555 // This should only happen when at end of line, behind a non-text
557 if(offset
== (**i
).GetLength()) return false;
558 m_Length
-= (**i
).GetLength(); // -1
559 m_ObjectList
.erase(i
);
560 return true; // we are done
564 if(offset
== (**i
).GetLength()) // at end of object
569 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
571 wxString str
= tobj
->GetText();
572 str
= str
.substr(offset
,str
.Length()-offset
);
573 // Find out how many positions we need to delete:
574 // 1. eat leading space
575 while(isspace(str
.c_str()[count
])) count
++;
576 // 2. eat the word itself:
577 while(isalnum(str
.c_str()[count
])) count
++;
579 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
580 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
585 wxASSERT(0); // we should never arrive here
589 wxLayoutLine::DeleteLine(bool update
)
591 if(m_Next
) m_Next
->m_Previous
= m_Previous
;
592 if(m_Previous
) m_Previous
->m_Next
= m_Next
;
595 m_Next
->MoveLines(-1);
596 m_Next
->RecalculatePositions(1);
598 wxLayoutLine
*next
= m_Next
;
604 wxLayoutLine::Draw(wxDC
&dc
, const wxPoint
& offset
) const
606 wxLayoutObjectList::iterator i
;
607 wxPoint pos
= offset
;
608 pos
= pos
+ GetPosition();
612 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
615 pos
.x
+= (**i
).GetWidth();
620 wxLayoutLine::Layout(wxDC
&dc
, wxPoint
*cursorPos
,
624 wxLayoutObjectList::iterator i
;
627 oldHeight
= m_Height
;
629 topHeight
, bottomHeight
; // above and below baseline
632 objTopHeight
, objBottomHeight
;
635 m_Height
= 0; m_BaseLine
= 0;
637 topHeight
= 0; bottomHeight
= 0;
639 bool cursorFound
= false;
643 *cursorPos
= m_Position
;
646 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
649 size
= (**i
).GetSize(&objTopHeight
, &objBottomHeight
);
651 if(cursorPos
&& ! cursorFound
)
652 { // we need to check whether the text cursor is here
653 len
= (**i
).GetLength();
654 if(count
<= cx
&& count
+len
> cx
)
656 if((**i
).GetType() == WXLO_TYPE_TEXT
)
658 len
= cx
- count
; // pos in object
659 CoordType width
, height
, descent
;
660 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
661 &width
, &height
, &descent
);
662 cursorPos
->x
+= width
;
663 cursorPos
->y
= m_Position
.y
;
665 if(len
< (**i
).GetLength())
666 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
668 str
= WXLO_CURSORCHAR
;
669 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
670 wxASSERT(cursorSize
);
671 // Just in case some joker inserted an empty string object:
672 if(width
== 0) width
= WXLO_MINIMUM_CURSOR_WIDTH
;
673 if(height
== 0) height
= objHeight
;
674 cursorSize
->x
= width
;
675 cursorSize
->y
= height
;
676 cursorFound
= true; // no more checks
679 { // on some other object
680 CoordType top
, bottom
; // unused
681 *cursorSize
= (**i
).GetSize(&top
,&bottom
);
682 cursorPos
->y
= m_Position
.y
;
683 cursorFound
= true; // no more checks
689 cursorPos
->x
+= (**i
).GetWidth();
694 if(objHeight
> m_Height
) m_Height
= objHeight
;
695 if(objTopHeight
> topHeight
) topHeight
= objTopHeight
;
696 if(objBottomHeight
> bottomHeight
) bottomHeight
= objBottomHeight
;
698 if(topHeight
+ bottomHeight
> m_Height
) m_Height
=
699 topHeight
+bottomHeight
;
700 m_BaseLine
= topHeight
;
704 if(GetPreviousLine()) // empty line
706 m_Height
= GetPreviousLine()->GetHeight();
707 m_BaseLine
= GetPreviousLine()->m_BaseLine
;
711 CoordType width
, height
, descent
;
712 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
714 m_BaseLine
= m_Height
- descent
;
719 // tell next line about coordinate change
720 if(m_Next
&& objHeight
!= oldHeight
)
721 m_Next
->RecalculatePositions();
723 // We need to check whether we found a valid cursor size:
726 // this might be the case if the cursor is at the end of the
727 // line or on a command object:
728 if(cursorSize
->y
< WXLO_MINIMUM_CURSOR_WIDTH
)
732 cursorSize
->y
= m_BaseLine
;
733 if(cursorSize
->x
< WXLO_MINIMUM_CURSOR_WIDTH
) cursorSize
->x
= WXLO_MINIMUM_CURSOR_WIDTH
;
737 CoordType width
, height
, descent
;
738 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
739 cursorSize
->x
= width
;
740 cursorSize
->y
= height
;
743 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
744 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
750 wxLayoutLine::Break(CoordType xpos
)
755 { // insert an empty line before this one
756 wxLayoutLine
*prev
= new wxLayoutLine(m_Previous
);
757 if(m_Previous
== NULL
)
758 { // We were in first line, need to link in new empty line
762 m_Previous
->m_Height
= GetHeight(); // this is a wild guess
766 m_Next
->RecalculatePositions(1);
771 wxLOiterator i
= FindObject(xpos
, &offset
);
773 // must be at the end of the line then
774 return new wxLayoutLine(this);
777 wxLayoutLine
*newLine
= new wxLayoutLine(this);
778 // split object at i:
779 if((**i
).GetType() == WXLO_TYPE_TEXT
&& offset
!= 0)
781 wxString left
, right
;
782 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
783 left
= tobj
->GetText().substr(0,offset
);
784 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
785 // current text object gets set to left half
786 tobj
->GetText() = left
; // set new text
787 newLine
->Append(new wxLayoutObjectText(right
));
788 m_Length
-= right
.Length();
789 i
++; // don't move this object to the new list
793 i
++; // move objects from here to new list
795 while(i
!= m_ObjectList
.end())
798 m_Length
-= (**i
).GetLength();
799 m_ObjectList
.remove(i
); // remove without deleting it
802 m_Next
->RecalculatePositions(2);
808 wxLayoutLine::MergeNextLine(void)
810 wxASSERT(GetNextLine());
811 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
814 for(i
= list
.begin(); i
!= list
.end();)
817 list
.remove(i
); // remove without deleting it
819 wxASSERT(list
.empty());
820 wxLayoutLine
*oldnext
= GetNextLine();
821 SetNext(GetNextLine()->GetNextLine());
823 RecalculatePositions(1);
827 wxLayoutLine::GetWrapPosition(CoordType column
)
830 wxLOiterator i
= FindObject(column
, &offset
);
831 if(i
== NULLIT
) return -1; // cannot wrap
833 // go backwards through the list and look for space in text objects
836 if((**i
).GetType() == WXLO_TYPE_TEXT
)
840 if( isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
847 }while(offset
!= -1);
848 i
--; // move on to previous object
852 column
-= (**i
).GetLength();
856 offset
= (**i
).GetLength();
858 /* If we reached the begin of the list and have more than one
859 object, that one is longer than the margin, so break behind
862 i
= m_ObjectList
.begin();
863 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
865 pos
+= (**i
).GetLength();
868 if(i
== NULLIT
) return -1; //why should this happen?
869 pos
+= (**i
).GetLength();
871 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
873 pos
+= (**i
).GetLength();
876 if(i
== NULLIT
) return -1; //this is possible, if there is only one text object
877 // now we are at the second text object:
878 pos
-= (**i
).GetLength();
879 return pos
; // in front of it
883 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
885 The wxLayoutList object
887 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
889 wxLayoutList::wxLayoutList()
891 m_DefaultSetting
= NULL
;
893 InvalidateUpdateRect();
897 wxLayoutList::~wxLayoutList()
900 m_FirstLine
->DeleteLine(false);
904 wxLayoutList::Empty(void)
907 m_FirstLine
= m_FirstLine
->DeleteLine(false);
909 m_CursorPos
= wxPoint(0,0);
910 m_CursorScreenPos
= wxPoint(0,0);
911 m_CursorSize
= wxPoint(0,0);
912 m_FirstLine
= new wxLayoutLine(NULL
); // empty first line
913 m_CursorLine
= m_FirstLine
;
918 wxLayoutList::InternalClear(void)
923 delete m_DefaultSetting
;
924 m_DefaultSetting
= NULL
;
929 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
930 int underline
, wxColour
const *fg
,
933 if(family
!= -1) m_FontFamily
= family
;
934 if(size
!= -1) m_FontPtSize
= size
;
935 if(style
!= -1) m_FontStyle
= style
;
936 if(weight
!= -1) m_FontWeight
= weight
;
937 if(underline
!= -1) m_FontUnderline
= underline
!= 0;
939 if(fg
!= NULL
) m_ColourFG
= fg
;
940 if(bg
!= NULL
) m_ColourBG
= bg
;
943 new wxLayoutObjectCmd(m_FontPtSize
,m_FontFamily
,m_FontStyle
,m_FontWeight
,m_FontUnderline
,
944 m_ColourFG
, m_ColourBG
));
948 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
949 int underline
, char const *fg
, char const *bg
)
957 cfg
= wxTheColourDatabase
->FindColour(fg
);
959 cbg
= wxTheColourDatabase
->FindColour(bg
);
961 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
965 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
966 int /* underline */, char const *fg
, char const *bg
)
972 m_FontUnderline
= false;
973 m_FontFamily
= family
;
975 m_FontWeight
= weight
;
976 m_ColourFG
= wxTheColourDatabase
->FindColour(fg
);
977 m_ColourBG
= wxTheColourDatabase
->FindColour(bg
);
979 if(! m_ColourFG
) m_ColourFG
= wxBLACK
;
980 if(! m_ColourBG
) m_ColourBG
= wxWHITE
;
983 delete m_DefaultSetting
;
985 m_DefaultSetting
= new
986 wxLayoutObjectCmd(m_FontPtSize
,m_FontFamily
,m_FontStyle
,
987 m_FontWeight
,m_FontUnderline
,
988 m_ColourFG
, m_ColourBG
);
994 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
996 wxLayoutLine
*line
= m_FirstLine
;
997 while(line
&& line
->GetLineNumber() != p
.y
)
998 line
= line
->GetNextLine();
999 if(line
&& line
->GetLineNumber() == p
.y
) // found it
1001 m_CursorPos
.y
= p
.y
;
1002 m_CursorLine
= line
;
1003 CoordType len
= line
->GetLength();
1006 m_CursorPos
.x
= p
.x
;
1011 m_CursorPos
.x
= len
;
1019 wxLayoutList::MoveCursorVertically(int n
)
1021 if(n
< 0) // move up
1023 if(m_CursorLine
== m_FirstLine
) return false;
1024 while(n
< 0 && m_CursorLine
)
1026 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1032 m_CursorLine
= m_FirstLine
;
1038 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1039 m_CursorPos
.x
= m_CursorLine
->GetLength();
1045 wxLayoutLine
*last
= m_CursorLine
;
1046 if(! m_CursorLine
->GetNextLine()) return false;
1047 while(n
> 0 && m_CursorLine
)
1051 m_CursorLine
= m_CursorLine
->GetNextLine();
1055 m_CursorLine
= last
;
1061 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1062 m_CursorPos
.x
= m_CursorLine
->GetLength();
1069 wxLayoutList::MoveCursorHorizontally(int n
)
1074 if(m_CursorPos
.x
== 0) // at begin of line
1076 if(! MoveCursorVertically(-1))
1078 MoveCursorToEndOfLine();
1083 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1084 m_CursorPos
.x
-= move
; n
+= move
;
1089 int len
= m_CursorLine
->GetLength();
1090 if(m_CursorPos
.x
== len
) // at end of line
1092 if(! MoveCursorVertically(1))
1094 MoveCursorToBeginOfLine();
1099 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1100 m_CursorPos
.x
+= move
;
1107 wxLayoutList::Insert(wxString
const &text
)
1109 wxASSERT(m_CursorLine
);
1110 m_CursorLine
->Insert(m_CursorPos
.x
, text
);
1111 m_CursorPos
.x
+= text
.Length();
1116 wxLayoutList::Insert(wxLayoutObject
*obj
)
1118 wxASSERT(m_CursorLine
);
1119 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
1120 m_CursorPos
.x
+= obj
->GetLength();
1125 wxLayoutList::LineBreak(void)
1127 wxASSERT(m_CursorLine
);
1129 bool setFirst
= (m_CursorLine
== m_FirstLine
&& m_CursorPos
.x
== 0);
1130 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
);
1131 if(setFirst
) // we were at beginning of first line
1132 m_FirstLine
= m_CursorLine
->GetPreviousLine();
1139 wxLayoutList::WrapLine(CoordType column
)
1141 if(m_CursorPos
.x
<= column
|| column
< 1)
1142 return false; // do nothing yet
1145 CoordType xpos
= m_CursorLine
->GetWrapPosition(column
);
1147 return false; // cannot break line
1149 CoordType newpos
= m_CursorPos
.x
- xpos
- 1;
1150 m_CursorPos
.x
= xpos
;
1152 Delete(1); // delete the space
1153 m_CursorPos
.x
= newpos
;
1159 wxLayoutList::Delete(CoordType npos
)
1161 wxASSERT(m_CursorLine
);
1165 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
1168 // More to delete, continue on next line.
1169 // First, check if line is empty:
1170 if(m_CursorLine
->GetLength() == 0)
1171 { // in this case, updating could probably be optimised
1173 wxASSERT(DeleteLines(1) == 0);
1182 // Need to join next line
1183 if(! m_CursorLine
->GetNextLine())
1187 m_CursorLine
->MergeNextLine();
1197 wxLayoutList::DeleteLines(int n
)
1199 wxASSERT(m_CursorLine
);
1203 if(!m_CursorLine
->GetNextLine())
1204 { // we cannot delete this line, but we can clear it
1205 MoveCursorToBeginOfLine();
1206 DeleteToEndOfLine();
1210 line
= m_CursorLine
;
1211 m_CursorLine
= m_CursorLine
->DeleteLine(true);
1213 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
1214 wxASSERT(m_FirstLine
);
1215 wxASSERT(m_CursorLine
);
1217 m_CursorLine
->RecalculatePositions(2);
1222 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
) const
1224 wxLayoutLine
*line
= m_FirstLine
;
1226 // first, make sure everything is calculated - this might not be
1227 // needed, optimise it later
1228 m_DefaultSetting
->Layout(dc
);
1231 line
->RecalculatePosition(); // so we don't need to do it all the time
1232 // little condition to speed up redrawing:
1233 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1234 line
= line
->GetNextLine();
1239 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
) const
1241 wxLayoutLine
*line
= m_FirstLine
;
1243 // first, make sure everything is calculated - this might not be
1244 // needed, optimise it later
1245 m_DefaultSetting
->Layout(dc
);
1248 if(line
== m_CursorLine
)
1249 line
->Layout(dc
, (wxPoint
*)&m_CursorScreenPos
, (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
1252 // little condition to speed up redrawing:
1253 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1254 line
= line
->GetNextLine();
1257 ///FIXME: disabled for now
1259 // can only be 0 if we are on the first line and have no next line
1260 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
1261 m_CursorLine
->GetNextLine() == NULL
&&
1262 m_CursorLine
== m_FirstLine
));
1267 wxLayoutList::Draw(wxDC
&dc
, wxPoint
const &offset
,
1268 CoordType top
, CoordType bottom
) const
1270 wxLayoutLine
*line
= m_FirstLine
;
1273 m_DefaultSetting
->Draw(dc
, wxPoint(0,0));
1274 wxBrush
*brush
= new wxBrush(*m_ColourBG
, wxSOLID
);
1275 dc
.SetBrush(*brush
);
1280 // only draw if between top and bottom:
1281 if((top
== -1 || line
->GetPosition().y
>= top
))
1282 line
->Draw(dc
, offset
);
1283 // little condition to speed up redrawing:
1284 if(bottom
!= -1 && line
->GetPosition().y
+ line
->GetHeight() > bottom
) break;
1285 line
= line
->GetNextLine();
1287 // can only be 0 if we are on the first line and have no next line
1288 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
1289 m_CursorLine
->GetNextLine() == NULL
&&
1290 m_CursorLine
== m_FirstLine
));
1294 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
, wxPoint
*cursorPos
)
1296 // First, find the right line:
1297 wxLayoutLine
*line
= m_FirstLine
;
1300 // we need to run a layout here to get font sizes right :-(
1301 m_DefaultSetting
->Layout(dc
);
1304 p
= line
->GetPosition();
1305 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
1308 line
= line
->GetNextLine();
1310 if(line
== NULL
) return NULL
; // not found
1311 if(cursorPos
) cursorPos
->y
= line
->GetLineNumber();
1312 // Now, find the object in the line:
1313 wxLOiterator i
= line
->FindObjectScreen(dc
, pos
.x
, & cursorPos
->x
);
1314 return (i
== NULLIT
) ? NULL
: *i
;
1319 wxLayoutList::GetSize(void) const
1322 *line
= m_FirstLine
,
1325 return wxPoint(0,0);
1327 wxPoint
maxPoint(0,0);
1332 if(line
->GetWidth() > maxPoint
.x
)
1333 maxPoint
.x
= line
->GetWidth();
1335 line
= line
->GetNextLine();
1338 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
1343 wxLayoutList::DrawCursor(wxDC
&dc
, bool active
, wxPoint
const &translate
)
1346 coords
= m_CursorScreenPos
;
1347 coords
.x
+= translate
.x
;
1348 coords
.y
+= translate
.y
;
1350 #ifdef WXLAYOUT_DEBUG
1351 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
1352 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
1353 (long)coords
.x
, (long)coords
.y
,
1354 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
1355 (long)m_CursorLine
->GetLineNumber(),
1356 (long)m_CursorLine
->GetLength()));
1359 dc
.SetBrush(*wxBLACK_BRUSH
);
1360 dc
.SetLogicalFunction(wxXOR
);
1361 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
1363 dc
.DrawRectangle(coords
.x
, coords
.y
, m_CursorSize
.x
,
1366 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
1367 coords
.x
+m_CursorSize
.x
, coords
.y
+m_CursorSize
.y
-1);
1368 dc
.SetLogicalFunction(wxCOPY
);
1369 //dc.SetBrush(wxNullBrush);
1372 /** Called by the objects to update the update rectangle.
1373 @param p a point to include in it
1376 wxLayoutList::SetUpdateRect(const wxPoint
&p
)
1378 if(m_UpdateRectValid
)
1379 GrowRect(m_UpdateRect
, p
);
1382 m_UpdateRect
.x
= p
.x
;
1383 m_UpdateRect
.y
= p
.y
;
1384 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
1385 m_UpdateRect
.height
= 4;// wxGTK :-)
1386 m_UpdateRectValid
= true;
1391 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1395 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1397 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
1398 wxString
const & title
)
1406 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
1408 // The following bit is taken from the printing sample, let's see
1409 // whether it works for us.
1411 /* You might use THIS code to set the printer DC to ROUGHLY reflect
1412 * the screen text size. This page also draws lines of actual length 5cm
1415 // Get the logical pixels per inch of screen and printer
1416 int ppiScreenX
, ppiScreenY
;
1417 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
1418 int ppiPrinterX
, ppiPrinterY
;
1419 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
1421 if(ppiScreenX
== 0) // not yet set, need to guess
1426 if(ppiPrinterX
== 0) // not yet set, need to guess
1432 // This scales the DC so that the printout roughly represents the
1433 // the screen scaling. The text point size _should_ be the right size
1434 // but in fact is too small for some reason. This is a detail that will
1435 // need to be addressed at some point but can be fudged for the
1437 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
1439 // Now we have to check in case our real page size is reduced
1440 // (e.g. because we're drawing to a print preview memory DC)
1441 int pageWidth
, pageHeight
;
1443 dc
->GetSize(&w
, &h
);
1444 GetPageSizePixels(&pageWidth
, &pageHeight
);
1445 if(pageWidth
!= 0) // doesn't work always
1447 // If printer pageWidth == current DC width, then this doesn't
1448 // change. But w might be the preview bitmap width, so scale down.
1449 scale
= scale
* (float)(w
/(float)pageWidth
);
1451 dc
->SetUserScale(scale
, scale
);
1455 bool wxLayoutPrintout::OnPrintPage(int page
)
1464 top
= (page
- 1)*m_PrintoutHeight
;
1465 bottom
= top
+ m_PrintoutHeight
;
1466 // SetDeviceOrigin() doesn't work here, so we need to manually
1467 // translate all coordinates.
1468 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
1469 m_llist
->Draw(*dc
, translate
, top
, bottom
);
1476 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
1478 /* We allocate a temporary wxDC for printing, so that we can
1479 determine the correct paper size and scaling. We don't actually
1480 print anything on it. */
1482 wxPrinterDC
psdc("","",WXLLIST_TEMPFILE
,false);
1484 wxPostScriptDC
psdc(WXLLIST_TEMPFILE
,false);
1487 float scale
= ScaleDC(&psdc
);
1489 psdc
.GetSize(&m_PageWidth
, &m_PageHeight
);
1490 // This sets a left/top origin of 15% and 20%:
1491 m_Offset
= wxPoint((15*m_PageWidth
)/100, m_PageHeight
/20);
1493 // This is the length of the printable area.
1494 m_PrintoutHeight
= m_PageHeight
- (int) (m_PageHeight
* 0.15);
1495 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
1499 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
1502 *maxPage
= m_NumOfPages
;
1505 *selPageTo
= m_NumOfPages
;
1506 wxRemoveFile(WXLLIST_TEMPFILE
);
1509 bool wxLayoutPrintout::HasPage(int pageNum
)
1511 return pageNum
<= m_NumOfPages
;
1515 Stupid wxWindows doesn't draw proper ellipses, so we comment this
1516 out. It's a waste of paper anyway.
1520 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
1521 wxPoint topleft
, wxPoint bottomright
,
1524 // make backups of all essential parameters
1525 const wxBrush
& brush
= dc
.GetBrush();
1526 const wxPen
& pen
= dc
.GetPen();
1527 const wxFont
& font
= dc
.GetFont();
1529 dc
.SetBrush(*wxWHITE_BRUSH
);
1530 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
1531 dc
.DrawRoundedRectangle(topleft
.x
,
1532 topleft
.y
,bottomright
.x
-topleft
.x
,
1533 bottomright
.y
-topleft
.y
);
1534 dc
.SetBrush(*wxBLACK_BRUSH
);
1535 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
1536 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
1540 page
= "9999/9999 "; // many pages...
1542 dc
.GetTextExtent(page
,&w
,&h
);
1543 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
1544 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
1545 dc
.GetTextExtent("XXXX", &w
,&h
);
1546 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);