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"
19 # include "gui/wxllist.h"
25 # include "iostream.h"
28 # include <wx/print.h>
34 /// This should never really get created
35 #define WXLLIST_TEMPFILE "__wxllist.tmp"
39 # define TypewxString(t) g_aTypewxStrings[t]
40 # define WXLO_DEBUG(x) wxLogDebug x
42 static const char *g_aTypewxStrings
[] =
44 "invalid", "text", "cmd", "icon"
47 wxLayoutObject::Debug(void)
49 WXLO_DEBUG(("%s",g_aTypewxStrings
[GetType()]));
52 # define TypewxString(t) ""
53 # define WXLO_DEBUG(x)
57 /// Cursors smaller than this disappear in XOR drawing mode
58 #define WXLO_MINIMUM_CURSOR_WIDTH 4
60 /// Use this character to estimate a cursor size when none is available.
61 #define WXLO_CURSORCHAR "E"
63 /// Helper function, allows me to compare to wxPoints
64 bool operator ==(wxPoint
const &p1
, wxPoint
const &p2
)
66 return p1
.x
== p2
.x
&& p1
.y
== p2
.y
;
69 /// Helper function, 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
;
76 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
80 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
82 wxLayoutObjectText::wxLayoutObjectText(const wxString
&txt
)
92 wxLayoutObjectText::Copy(void)
94 wxLayoutObjectText
*obj
= new wxLayoutObjectText(m_Text
);
95 obj
->m_Width
= m_Width
;
96 obj
->m_Height
= m_Height
;
98 obj
->m_Bottom
= m_Bottom
;
99 obj
->SetUserData(m_UserData
);
104 wxLayoutObjectText::GetSize(CoordType
*top
, CoordType
*bottom
) const
107 *top
= m_Top
; *bottom
= m_Bottom
;
108 return wxPoint(m_Width
, m_Height
);
112 wxLayoutObjectText::Draw(wxDC
&dc
, wxPoint
const &coords
)
114 dc
.DrawText(m_Text
, coords
.x
, coords
.y
-m_Top
);
118 wxLayoutObjectText::GetOffsetScreen(wxDC
&dc
, CoordType xpos
) const
122 maxlen
= m_Text
.Length();
125 height
, descent
= 0l;
127 if(xpos
== 0) return 0; // easy
129 while(width
< xpos
&& offs
< maxlen
)
131 dc
.GetTextExtent(m_Text
.substr(0,offs
),
132 &width
, &height
, &descent
);
135 /* We have to substract 1 to compensate for the offs++, and another
136 one because we don't want to position the cursor behind the
137 object what we clicked on, but before - otherwise it looks
139 return (xpos
> 2) ? offs
-2 : 0;
143 wxLayoutObjectText::Layout(wxDC
&dc
)
147 dc
.GetTextExtent(m_Text
,&m_Width
, &m_Height
, &descent
);
149 m_Top
= m_Height
- m_Bottom
;
152 #ifdef WXLAYOUT_DEBUG
154 wxLayoutObjectText::Debug(void)
156 wxLayoutObject::Debug();
157 WXLO_DEBUG((" `%s`", m_Text
.c_str()));
161 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
165 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
167 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
const &icon
)
169 m_Icon
= new wxBitmap(icon
);
173 wxLayoutObjectIcon::Copy(void)
175 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon(new
177 obj
->SetUserData(m_UserData
);
181 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
*icon
)
187 wxLayoutObjectIcon::Draw(wxDC
&dc
, wxPoint
const &coords
)
189 dc
.DrawBitmap(*m_Icon
, coords
.x
, coords
.y
-m_Icon
->GetHeight());
193 wxLayoutObjectIcon::Layout(wxDC
& /* dc */)
198 wxLayoutObjectIcon::GetSize(CoordType
*top
, CoordType
*bottom
) const
200 *top
= m_Icon
->GetHeight();
202 return wxPoint(m_Icon
->GetWidth(), m_Icon
->GetHeight());
207 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
211 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
213 wxLayoutObjectCmd::wxLayoutObjectCmd(int size
, int family
, int style
, int
214 weight
, bool underline
,
215 wxColour
const *fg
, wxColour
const *bg
)
218 m_font
= new wxFont(size
,family
,style
,weight
,underline
);
224 wxLayoutObjectCmd::Copy(void)
226 wxLayoutStyleInfo si
;
229 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd(
230 si
.size
, si
.family
, si
.style
, si
.weight
, si
.underline
,
231 m_ColourFG
, m_ColourBG
);
232 obj
->SetUserData(m_UserData
);
237 wxLayoutObjectCmd::~wxLayoutObjectCmd()
243 wxLayoutObjectCmd::GetStyle(wxLayoutStyleInfo
*si
) const
245 si
->size
= m_font
->GetPointSize();
246 si
->family
= m_font
->GetFamily();
247 si
->style
= m_font
->GetStyle();
248 si
->underline
= m_font
->GetUnderlined();
249 si
->weight
= m_font
->GetWeight();
251 si
->fg_red
= m_ColourFG
->Red();
252 si
->fg_green
= m_ColourFG
->Green();
253 si
->fg_blue
= m_ColourFG
->Blue();
254 si
->bg_red
= m_ColourBG
->Red();
255 si
->bg_green
= m_ColourBG
->Green();
256 si
->bg_blue
= m_ColourBG
->Blue();
260 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint
const & /* coords */)
265 dc
.SetTextForeground(*m_ColourFG
);
267 dc
.SetTextBackground(*m_ColourBG
);
271 wxLayoutObjectCmd::Layout(wxDC
&dc
)
273 // this get called, so that recalculation uses right font sizes
274 Draw(dc
, wxPoint(0,0));
278 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
280 The wxLayoutLine object
282 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
284 wxLayoutLine::wxLayoutLine(wxLayoutLine
*prev
)
292 RecalculatePosition();
295 m_LineNumber
= m_Previous
->GetLineNumber()+1;
296 m_Next
= m_Previous
->GetNextLine();
297 m_Previous
->m_Next
= this;
298 m_Height
= m_Previous
->GetHeight();
302 m_Next
->m_Previous
= this;
303 m_Next
->MoveLines(+1);
304 m_Next
->RecalculatePositions(1);
308 wxLayoutLine::~wxLayoutLine()
310 // kbList cleans itself
314 wxLayoutLine::RecalculatePosition(void)
317 m_Position
= m_Previous
->GetPosition() +
318 wxPoint(0,m_Previous
->GetHeight());
320 m_Position
= wxPoint(0,0);
325 wxLayoutLine::RecalculatePositions(int recurse
)
327 wxASSERT(recurse
>= 0);
328 wxPoint pos
= m_Position
;
329 CoordType height
= m_Height
;
331 // WXLO_TRACE("RecalculatePositions()");
332 RecalculatePosition();
337 if(m_Next
) m_Next
->RecalculatePositions(--recurse
);
339 else if(pos
!= m_Position
|| m_Height
!= height
)
340 if(m_Next
) m_Next
->RecalculatePositions();
344 wxLayoutObjectList::iterator
345 wxLayoutLine::FindObject(CoordType xpos
, CoordType
*offset
) const
349 wxLayoutObjectList::iterator i
;
350 CoordType x
= 0, len
;
353 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
355 len
= (**i
).GetLength();
356 if( x
<= xpos
&& xpos
<= x
+ len
)
361 x
+= (**i
).GetLength();
366 wxLayoutObjectList::iterator
367 wxLayoutLine::FindObjectScreen(wxDC
&dc
, CoordType xpos
, CoordType
*cxpos
) const
371 wxLayoutObjectList::iterator i
;
372 CoordType x
= 0, cx
= 0, width
;
374 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
377 width
= (**i
).GetWidth();
378 if( x
<= xpos
&& xpos
<= x
+ width
)
380 *cxpos
= cx
+ (**i
).GetOffsetScreen(dc
, xpos
-x
);
381 wxLogDebug("wxLayoutLine::FindObjectScreen: cursor xpos = %ld", *cxpos
);
384 x
+= (**i
).GetWidth();
385 cx
+= (**i
).GetLength();
387 // behind last object:
389 return m_ObjectList
.tail();
393 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
396 wxASSERT(obj
!= NULL
);
398 wxLOiterator i
= FindObject(xpos
, &offset
);
401 if(xpos
== 0 ) // aha, empty line!
403 m_ObjectList
.push_back(obj
);
404 m_Length
+= obj
->GetLength();
411 CoordType len
= (**i
).GetLength();
412 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
413 { // insert before this object
414 m_ObjectList
.insert(i
,obj
);
415 m_Length
+= obj
->GetLength();
420 if( i
== m_ObjectList
.tail()) // last object?
421 m_ObjectList
.push_back(obj
);
423 { // insert after current object
425 m_ObjectList
.insert(i
,obj
);
427 m_Length
+= obj
->GetLength();
430 /* Otherwise we need to split the current object.
431 Fortunately this can only be a text object. */
432 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
433 wxString left
, right
;
434 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
435 left
= tobj
->GetText().substr(0,offset
);
436 right
= tobj
->GetText().substr(offset
,len
-offset
);
437 // current text object gets set to right half
438 tobj
->GetText() = right
; // set new text
439 // before it we insert the new object
440 m_ObjectList
.insert(i
,obj
);
441 m_Length
+= obj
->GetLength();
442 // and before that we insert the left half
443 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
448 wxLayoutLine::Insert(CoordType xpos
, wxString text
)
452 wxLOiterator i
= FindObject(xpos
, &offset
);
453 if(i
!= NULLIT
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
455 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
456 tobj
->GetText().insert(offset
, text
);
457 m_Length
+= text
.Length();
462 return Insert(xpos
, new wxLayoutObjectText(text
));
466 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
468 CoordType offset
, len
;
472 wxLOiterator i
= FindObject(xpos
, &offset
);
475 if(i
== NULLIT
) return npos
;
476 // now delete from that object:
477 if((**i
).GetType() != WXLO_TYPE_TEXT
)
479 if(offset
!= 0) // at end of line after a non-text object
482 len
= (**i
).GetLength();
485 m_ObjectList
.erase(i
);
489 // tidy up: remove empty text objects
490 if((**i
).GetLength() == 0)
492 m_ObjectList
.erase(i
);
496 CoordType max
= (**i
).GetLength() - offset
;
497 if(npos
< max
) max
= npos
;
500 if(xpos
== GetLength())
503 { // at the end of an object
504 // move to begin of next object:
506 continue; // start over
511 if(offset
== 0 && max
== (**i
).GetLength())
512 m_ObjectList
.erase(i
); // remove the whole object
514 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
521 wxLayoutLine::DeleteWord(CoordType xpos
)
526 wxLOiterator i
= FindObject(xpos
, &offset
);
530 if(i
== NULLIT
) return false;
531 if((**i
).GetType() != WXLO_TYPE_TEXT
)
533 // This should only happen when at end of line, behind a non-text
535 if(offset
== (**i
).GetLength()) return false;
536 m_Length
-= (**i
).GetLength(); // -1
537 m_ObjectList
.erase(i
);
538 return true; // we are done
542 if(offset
== (**i
).GetLength()) // at end of object
547 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
549 wxString str
= tobj
->GetText();
550 str
= str
.substr(offset
,str
.Length()-offset
);
551 // Find out how many positions we need to delete:
552 // 1. eat leading space
553 while(isspace(str
[count
])) count
++;
554 // 2. eat the word itself:
555 while(isalnum(str
[count
])) count
++;
557 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
558 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
563 wxASSERT(0); // we should never arrive here
567 wxLayoutLine::DeleteLine(bool update
)
569 if(m_Next
) m_Next
->m_Previous
= m_Previous
;
570 if(m_Previous
) m_Previous
->m_Next
= m_Next
;
573 m_Next
->MoveLines(-1);
574 m_Next
->RecalculatePositions(1);
576 wxLayoutLine
*next
= m_Next
;
582 wxLayoutLine::Draw(wxDC
&dc
, const wxPoint
& offset
) const
584 wxLayoutObjectList::iterator i
;
585 wxPoint pos
= offset
;
586 pos
= pos
+ GetPosition();
590 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
593 pos
.x
+= (**i
).GetWidth();
598 wxLayoutLine::Layout(wxDC
&dc
, wxPoint
*cursorPos
,
602 wxLayoutObjectList::iterator i
;
605 oldHeight
= m_Height
;
607 topHeight
, bottomHeight
; // above and below baseline
610 objTopHeight
, objBottomHeight
;
613 m_Height
= 0; m_BaseLine
= 0;
615 topHeight
= 0; bottomHeight
= 0;
617 bool cursorFound
= false;
621 *cursorPos
= m_Position
;
624 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
627 size
= (**i
).GetSize(&objTopHeight
, &objBottomHeight
);
629 if(cursorPos
&& ! cursorFound
)
630 { // we need to check whether the text cursor is here
631 len
= (**i
).GetLength();
632 if(count
<= cx
&& count
+len
> cx
)
634 if((**i
).GetType() == WXLO_TYPE_TEXT
)
636 len
= cx
- count
; // pos in object
637 CoordType width
, height
, descent
;
638 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
639 &width
, &height
, &descent
);
640 cursorPos
->x
+= width
;
641 cursorPos
->y
= m_Position
.y
;
643 if(len
< (**i
).GetLength())
644 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
646 str
= WXLO_CURSORCHAR
;
647 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
648 wxASSERT(cursorSize
);
649 // Just in case some joker inserted an empty string object:
650 if(width
== 0) width
= WXLO_MINIMUM_CURSOR_WIDTH
;
651 if(height
== 0) height
= objHeight
;
652 cursorSize
->x
= width
;
653 cursorSize
->y
= height
;
654 cursorFound
= true; // no more checks
657 { // on some other object
658 CoordType top
, bottom
; // unused
659 *cursorSize
= (**i
).GetSize(&top
,&bottom
);
660 cursorPos
->y
= m_Position
.y
;
661 cursorFound
= true; // no more checks
667 cursorPos
->x
+= (**i
).GetWidth();
672 if(objHeight
> m_Height
) m_Height
= objHeight
;
673 if(objTopHeight
> topHeight
) topHeight
= objTopHeight
;
674 if(objBottomHeight
> bottomHeight
) bottomHeight
= objBottomHeight
;
676 if(topHeight
+ bottomHeight
> m_Height
) m_Height
=
677 topHeight
+bottomHeight
;
678 m_BaseLine
= topHeight
;
682 if(GetPreviousLine()) // empty line
684 m_Height
= GetPreviousLine()->GetHeight();
685 m_BaseLine
= GetPreviousLine()->m_BaseLine
;
689 CoordType width
, height
, descent
;
690 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
692 m_BaseLine
= m_Height
- descent
;
697 // tell next line about coordinate change
698 if(m_Next
&& objHeight
!= oldHeight
)
699 m_Next
->RecalculatePositions();
703 // this might be the case if the cursor is at the end of the
704 // line or on a command object:
705 if(cursorSize
->y
< WXLO_MINIMUM_CURSOR_WIDTH
)
709 cursorSize
->y
= m_BaseLine
;
710 if(cursorSize
->x
< WXLO_MINIMUM_CURSOR_WIDTH
) cursorSize
->x
= WXLO_MINIMUM_CURSOR_WIDTH
;
714 CoordType width
, height
, descent
;
715 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
716 cursorSize
->x
= width
;
717 cursorSize
->y
= height
;
720 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
721 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
727 wxLayoutLine::Break(CoordType xpos
)
732 { // insert an empty line before this one
733 wxLayoutLine
*prev
= new wxLayoutLine(m_Previous
);
734 if(m_Previous
== NULL
)
735 { // We were in first line, need to link in new empty line
739 m_Previous
->m_Height
= GetHeight(); // this is a wild guess
743 m_Next
->RecalculatePositions(1);
748 wxLOiterator i
= FindObject(xpos
, &offset
);
750 // must be at the end of the line then
751 return new wxLayoutLine(this);
754 wxLayoutLine
*newLine
= new wxLayoutLine(this);
755 // split object at i:
756 if((**i
).GetType() == WXLO_TYPE_TEXT
&& offset
!= 0)
758 wxString left
, right
;
759 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
760 left
= tobj
->GetText().substr(0,offset
);
761 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
762 // current text object gets set to left half
763 tobj
->GetText() = left
; // set new text
764 newLine
->Append(new wxLayoutObjectText(right
));
765 m_Length
-= right
.Length();
766 i
++; // don't move this object to the new list
770 i
++; // move objects from here to new list
772 while(i
!= m_ObjectList
.end())
775 m_Length
-= (**i
).GetLength();
776 m_ObjectList
.remove(i
); // remove without deleting it
779 m_Next
->RecalculatePositions(2);
785 wxLayoutLine::MergeNextLine(void)
787 wxASSERT(GetNextLine());
788 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
791 for(i
= list
.begin(); i
!= list
.end();)
794 list
.remove(i
); // remove without deleting it
796 wxASSERT(list
.empty());
797 wxLayoutLine
*oldnext
= GetNextLine();
798 SetNext(GetNextLine()->GetNextLine());
800 RecalculatePositions(1);
804 wxLayoutLine::GetWrapPosition(CoordType column
)
807 wxLOiterator i
= FindObject(column
, &offset
);
808 if(i
== NULLIT
) return -1; // cannot wrap
810 // go backwards through the list and look for space in text objects
813 if((**i
).GetType() == WXLO_TYPE_TEXT
)
817 if( isspace(((wxLayoutObjectText
*)*i
)->GetText()[offset
]))
824 }while(offset
!= -1);
825 i
--; // move on to previous object
829 column
-= (**i
).GetLength();
833 offset
= (**i
).GetLength();
835 /* If we reached the begin of the list and have more than one
836 object, that one is longer than the margin, so break behind
839 i
= m_ObjectList
.begin();
840 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
842 pos
+= (**i
).GetLength();
845 if(i
== NULLIT
) return -1; //why should this happen?
846 pos
+= (**i
).GetLength();
848 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
850 pos
+= (**i
).GetLength();
853 if(i
== NULLIT
) return -1; //this is possible, if there is only one text object
854 // now we are at the second text object:
855 pos
-= (**i
).GetLength();
856 return pos
; // in front of it
860 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
862 The wxLayoutList object
864 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
866 wxLayoutList::wxLayoutList()
868 m_DefaultSetting
= NULL
;
873 wxLayoutList::~wxLayoutList()
879 wxLayoutList::Empty(void)
882 m_FirstLine
= m_FirstLine
->DeleteLine(false);
884 m_CursorPos
= wxPoint(0,0);
885 m_CursorScreenPos
= wxPoint(0,0);
886 m_CursorSize
= wxPoint(0,0);
887 m_FirstLine
= new wxLayoutLine(NULL
); // empty first line
888 m_CursorLine
= m_FirstLine
;
893 wxLayoutList::InternalClear(void)
898 delete m_DefaultSetting
;
899 m_DefaultSetting
= NULL
;
904 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
905 int underline
, wxColour
const *fg
,
908 if(family
!= -1) m_FontFamily
= family
;
909 if(size
!= -1) m_FontPtSize
= size
;
910 if(style
!= -1) m_FontStyle
= style
;
911 if(weight
!= -1) m_FontWeight
= weight
;
912 if(underline
!= -1) m_FontUnderline
= underline
!= 0;
914 if(fg
!= NULL
) m_ColourFG
= fg
;
915 if(bg
!= NULL
) m_ColourBG
= bg
;
918 new wxLayoutObjectCmd(m_FontPtSize
,m_FontFamily
,m_FontStyle
,m_FontWeight
,m_FontUnderline
,
919 m_ColourFG
, m_ColourBG
));
923 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
924 int underline
, char const *fg
, char const *bg
)
932 cfg
= wxTheColourDatabase
->FindColour(fg
);
934 cbg
= wxTheColourDatabase
->FindColour(bg
);
936 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
940 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
941 int /* underline */, char const *fg
, char const *bg
)
947 m_FontUnderline
= false;
948 m_FontFamily
= family
;
950 m_FontWeight
= weight
;
951 m_ColourFG
= wxTheColourDatabase
->FindColour(fg
);
952 m_ColourBG
= wxTheColourDatabase
->FindColour(bg
);
954 if(! m_ColourFG
) m_ColourFG
= wxBLACK
;
955 if(! m_ColourBG
) m_ColourBG
= wxWHITE
;
958 delete m_DefaultSetting
;
960 m_DefaultSetting
= new
961 wxLayoutObjectCmd(m_FontPtSize
,m_FontFamily
,m_FontStyle
,
962 m_FontWeight
,m_FontUnderline
,
963 m_ColourFG
, m_ColourBG
);
969 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
971 wxLayoutLine
*line
= m_FirstLine
;
972 while(line
&& line
->GetLineNumber() != p
.y
)
973 line
= line
->GetNextLine();
974 if(line
&& line
->GetLineNumber() == p
.y
) // found it
978 CoordType len
= line
->GetLength();
994 wxLayoutList::MoveCursorVertically(int n
)
998 if(m_CursorLine
== m_FirstLine
) return false;
999 while(n
< 0 && m_CursorLine
)
1001 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1007 m_CursorLine
= m_FirstLine
;
1013 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1014 m_CursorPos
.x
= m_CursorLine
->GetLength();
1020 wxLayoutLine
*last
= m_CursorLine
;
1021 if(! m_CursorLine
->GetNextLine()) return false;
1022 while(n
> 0 && m_CursorLine
)
1026 m_CursorLine
= m_CursorLine
->GetNextLine();
1030 m_CursorLine
= last
;
1036 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1037 m_CursorPos
.x
= m_CursorLine
->GetLength();
1044 wxLayoutList::MoveCursorHorizontally(int n
)
1049 if(m_CursorPos
.x
== 0) // at begin of line
1051 if(! MoveCursorVertically(-1))
1053 MoveCursorToEndOfLine();
1058 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1059 m_CursorPos
.x
-= move
; n
+= move
;
1064 int len
= m_CursorLine
->GetLength();
1065 if(m_CursorPos
.x
== len
) // at end of line
1067 if(! MoveCursorVertically(1))
1069 MoveCursorToBeginOfLine();
1074 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1075 m_CursorPos
.x
+= move
;
1082 wxLayoutList::Insert(wxString
const &text
)
1084 wxASSERT(m_CursorLine
);
1085 m_CursorLine
->Insert(m_CursorPos
.x
, text
);
1086 m_CursorPos
.x
+= text
.Length();
1091 wxLayoutList::Insert(wxLayoutObject
*obj
)
1093 wxASSERT(m_CursorLine
);
1094 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
1095 m_CursorPos
.x
+= obj
->GetLength();
1100 wxLayoutList::LineBreak(void)
1102 wxASSERT(m_CursorLine
);
1104 bool setFirst
= (m_CursorLine
== m_FirstLine
&& m_CursorPos
.x
== 0);
1105 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
);
1106 if(setFirst
) // we were at beginning of first line
1107 m_FirstLine
= m_CursorLine
->GetPreviousLine();
1114 wxLayoutList::WrapLine(CoordType column
)
1116 if(m_CursorPos
.x
<= column
|| column
< 1)
1117 return false; // do nothing yet
1120 CoordType xpos
= m_CursorLine
->GetWrapPosition(column
);
1122 return false; // cannot break line
1124 CoordType newpos
= m_CursorPos
.x
- xpos
- 1;
1125 m_CursorPos
.x
= xpos
;
1127 Delete(1); // delete the space
1128 m_CursorPos
.x
= newpos
;
1134 wxLayoutList::Delete(CoordType npos
)
1136 wxASSERT(m_CursorLine
);
1140 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
1143 // More to delete, continue on next line.
1144 // First, check if line is empty:
1145 if(m_CursorLine
->GetLength() == 0)
1146 { // in this case, updating could probably be optimised
1148 wxASSERT(DeleteLines(1) == 0);
1157 // Need to join next line
1158 if(! m_CursorLine
->GetNextLine())
1162 m_CursorLine
->MergeNextLine();
1172 wxLayoutList::DeleteLines(int n
)
1174 wxASSERT(m_CursorLine
);
1178 if(!m_CursorLine
->GetNextLine())
1179 { // we cannot delete this line, but we can clear it
1180 MoveCursorToBeginOfLine();
1181 DeleteToEndOfLine();
1185 line
= m_CursorLine
;
1186 m_CursorLine
= m_CursorLine
->DeleteLine(true);
1188 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
1189 wxASSERT(m_FirstLine
);
1190 wxASSERT(m_CursorLine
);
1192 m_CursorLine
->RecalculatePositions(2);
1197 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
) const
1199 wxLayoutLine
*line
= m_FirstLine
;
1201 // first, make sure everything is calculated - this might not be
1202 // needed, optimise it later
1203 m_DefaultSetting
->Layout(dc
);
1206 line
->RecalculatePosition(); // so we don't need to do it all the time
1207 // little condition to speed up redrawing:
1208 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1209 line
= line
->GetNextLine();
1214 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
) const
1216 wxLayoutLine
*line
= m_FirstLine
;
1218 // first, make sure everything is calculated - this might not be
1219 // needed, optimise it later
1220 m_DefaultSetting
->Layout(dc
);
1223 if(line
== m_CursorLine
)
1224 line
->Layout(dc
, (wxPoint
*)&m_CursorScreenPos
, (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
1227 // little condition to speed up redrawing:
1228 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1229 line
= line
->GetNextLine();
1231 // can only be 0 if we are on the first line and have no next line
1232 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
1233 m_CursorLine
->GetNextLine() == NULL
&&
1234 m_CursorLine
== m_FirstLine
));
1238 wxLayoutList::Draw(wxDC
&dc
, wxPoint
const &offset
,
1239 CoordType top
, CoordType bottom
) const
1241 wxLayoutLine
*line
= m_FirstLine
;
1244 m_DefaultSetting
->Draw(dc
, wxPoint(0,0));
1247 // only draw if between top and bottom:
1248 if((top
== -1 || line
->GetPosition().y
>= top
))
1249 line
->Draw(dc
, offset
);
1250 // little condition to speed up redrawing:
1251 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1252 line
= line
->GetNextLine();
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
));
1261 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
, wxPoint
*cursorPos
)
1263 // First, find the right line:
1264 wxLayoutLine
*line
= m_FirstLine
;
1267 // we need to run a layout here to get font sizes right :-(
1268 m_DefaultSetting
->Layout(dc
);
1271 p
= line
->GetPosition();
1272 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
1275 line
= line
->GetNextLine();
1277 if(line
== NULL
) return NULL
; // not found
1278 if(cursorPos
) cursorPos
->y
= line
->GetLineNumber();
1279 // Now, find the object in the line:
1280 wxLOiterator i
= line
->FindObjectScreen(dc
, pos
.x
, & cursorPos
->x
);
1281 return (i
== NULLIT
) ? NULL
: *i
;
1286 wxLayoutList::GetSize(void) const
1289 *line
= m_FirstLine
,
1292 return wxPoint(0,0);
1299 if(line
->GetWidth() > max
.x
) max
.x
= line
->GetWidth();
1301 line
= line
->GetNextLine();
1304 max
.y
= last
->GetPosition().y
+ last
->GetHeight();
1309 wxLayoutList::DrawCursor(wxDC
&dc
, bool active
, wxPoint
const &translate
)
1312 coords
= m_CursorScreenPos
;
1313 coords
.x
+= translate
.x
;
1314 coords
.y
+= translate
.y
;
1316 #ifdef WXLAYOUT_DEBUG
1317 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
1318 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
1319 (long)coords
.x
, (long)coords
.y
,
1320 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
1321 (long)m_CursorLine
->GetLineNumber(),
1322 (long)m_CursorLine
->GetLength()));
1325 dc
.SetBrush(*wxBLACK_BRUSH
);
1326 dc
.SetLogicalFunction(wxXOR
);
1327 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
1328 dc
.SetLogicalFunction(wxXOR
);
1330 dc
.DrawRectangle(coords
.x
, coords
.y
, m_CursorSize
.x
,
1333 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
1334 coords
.x
+m_CursorSize
.x
, coords
.y
+m_CursorSize
.y
-1);
1335 dc
.SetLogicalFunction(wxCOPY
);
1336 dc
.SetBrush(wxNullBrush
);
1341 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1345 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1347 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
1348 wxString
const & title
)
1356 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
1358 // The following bit is taken from the printing sample, let's see
1359 // whether it works for us.
1361 /* You might use THIS code to set the printer DC to ROUGHLY reflect
1362 * the screen text size. This page also draws lines of actual length 5cm
1365 // Get the logical pixels per inch of screen and printer
1366 int ppiScreenX
, ppiScreenY
;
1367 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
1368 int ppiPrinterX
, ppiPrinterY
;
1369 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
1371 if(ppiScreenX
== 0) // not yet set, need to guess
1376 if(ppiPrinterX
== 0) // not yet set, need to guess
1382 // This scales the DC so that the printout roughly represents the
1383 // the screen scaling. The text point size _should_ be the right size
1384 // but in fact is too small for some reason. This is a detail that will
1385 // need to be addressed at some point but can be fudged for the
1387 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
1389 // Now we have to check in case our real page size is reduced
1390 // (e.g. because we're drawing to a print preview memory DC)
1391 int pageWidth
, pageHeight
;
1393 dc
->GetSize(&w
, &h
);
1394 GetPageSizePixels(&pageWidth
, &pageHeight
);
1395 if(pageWidth
!= 0) // doesn't work always
1397 // If printer pageWidth == current DC width, then this doesn't
1398 // change. But w might be the preview bitmap width, so scale down.
1399 scale
= scale
* (float)(w
/(float)pageWidth
);
1401 dc
->SetUserScale(scale
, scale
);
1405 bool wxLayoutPrintout::OnPrintPage(int page
)
1414 top
= (page
- 1)*m_PrintoutHeight
;
1415 bottom
= top
+ m_PrintoutHeight
;
1416 // SetDeviceOrigin() doesn't work here, so we need to manually
1417 // translate all coordinates.
1418 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
1419 m_llist
->Draw(*dc
, translate
, top
, bottom
);
1426 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
1428 /* We allocate a temporary wxDC for printing, so that we can
1429 determine the correct paper size and scaling. We don't actually
1430 print anything on it. */
1432 wxPrinterDC
psdc("","",WXLLIST_TEMPFILE
,false);
1434 wxPostScriptDC
psdc(WXLLIST_TEMPFILE
,false);
1437 float scale
= ScaleDC(&psdc
);
1439 psdc
.GetSize(&m_PageWidth
, &m_PageHeight
);
1440 // This sets a left/top origin of 10% and 20%:
1441 m_Offset
= wxPoint(m_PageWidth
/10, m_PageHeight
/20);
1443 // This is the length of the printable area.
1444 m_PrintoutHeight
= m_PageHeight
- (int) (m_PageHeight
* 0.1);
1445 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
1448 m_NumOfPages
= (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
) + 0.5);
1450 // This is a crude hack to get it right for very small
1451 // printouts. No idea why this is required, I thought +0.5 would do
1453 if(m_NumOfPages
== 0 && m_llist
->GetSize().y
> 0)
1456 *maxPage
= m_NumOfPages
;
1459 *selPageTo
= m_NumOfPages
;
1460 wxRemoveFile(WXLLIST_TEMPFILE
);
1463 bool wxLayoutPrintout::HasPage(int pageNum
)
1465 return pageNum
<= m_NumOfPages
;
1469 Stupid wxWindows doesn't draw proper ellipses, so we comment this
1470 out. It's a waste of paper anyway.
1474 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
1475 wxPoint topleft
, wxPoint bottomright
,
1478 // make backups of all essential parameters
1479 const wxBrush
& brush
= dc
.GetBrush();
1480 const wxPen
& pen
= dc
.GetPen();
1481 const wxFont
& font
= dc
.GetFont();
1483 dc
.SetBrush(*wxWHITE_BRUSH
);
1484 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
1485 dc
.DrawRoundedRectangle(topleft
.x
,
1486 topleft
.y
,bottomright
.x
-topleft
.x
,
1487 bottomright
.y
-topleft
.y
);
1488 dc
.SetBrush(*wxBLACK_BRUSH
);
1489 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
1490 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
1494 page
= "9999/9999 "; // many pages...
1496 dc
.GetTextExtent(page
,&w
,&h
);
1497 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
1498 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
1499 dc
.GetTextExtent("XXXX", &w
,&h
);
1500 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);