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"
15 # include "gui/wxllist.h"
21 # include "iostream.h"
24 # include <wx/print.h>
30 /// This should never really get created
31 #define WXLLIST_TEMPFILE "__wxllist.tmp"
35 # define TypewxString(t) g_aTypewxStrings[t]
36 # define WXLO_DEBUG(x) wxLogDebug x
38 static const char *g_aTypewxStrings
[] =
40 "invalid", "text", "cmd", "icon"
43 wxLayoutObject::Debug(void)
45 WXLO_DEBUG(("%s",g_aTypewxStrings
[GetType()]));
48 # define TypewxString(t) ""
49 # define WXLO_DEBUG(x)
53 /// Cursors smaller than this disappear in XOR drawing mode
54 #define WXLO_MINIMUM_CURSOR_WIDTH 4
56 /// Use this character to estimate a cursor size when none is available.
57 #define WXLO_CURSORCHAR "E"
58 /** @name Helper functions */
60 /// allows me to compare to wxPoints
61 bool operator ==(wxPoint
const &p1
, wxPoint
const &p2
)
63 return p1
.x
== p2
.x
&& p1
.y
== p2
.y
;
66 /// allows me to compare to wxPoints
67 bool operator !=(wxPoint
const &p1
, wxPoint
const &p2
)
69 return p1
.x
!= p2
.x
|| p1
.y
!= p2
.y
;
72 /// grows a wxRect so that it includes the given point
74 static void GrowRect(wxRect
&r
, const wxPoint
& p
)
78 else if(r
.x
+ r
.width
< p
.x
)
83 else if(r
.y
+ r
.height
< p
.y
)
88 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
92 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
94 wxLayoutObjectText::wxLayoutObjectText(const wxString
&txt
)
104 wxLayoutObjectText::Copy(void)
106 wxLayoutObjectText
*obj
= new wxLayoutObjectText(m_Text
);
107 obj
->m_Width
= m_Width
;
108 obj
->m_Height
= m_Height
;
110 obj
->m_Bottom
= m_Bottom
;
111 obj
->SetUserData(m_UserData
);
116 wxLayoutObjectText::GetSize(CoordType
*top
, CoordType
*bottom
) const
119 *top
= m_Top
; *bottom
= m_Bottom
;
120 return wxPoint(m_Width
, m_Height
);
124 wxLayoutObjectText::Draw(wxDC
&dc
, wxPoint
const &coords
)
126 dc
.DrawText(m_Text
, coords
.x
, coords
.y
-m_Top
);
130 wxLayoutObjectText::GetOffsetScreen(wxDC
&dc
, CoordType xpos
) const
134 maxlen
= m_Text
.Length();
137 height
, descent
= 0l;
139 if(xpos
== 0) return 0; // easy
141 while(width
< xpos
&& offs
< maxlen
)
143 dc
.GetTextExtent(m_Text
.substr(0,offs
),
144 &width
, &height
, &descent
);
147 /* We have to substract 1 to compensate for the offs++, and another
148 one because we don't want to position the cursor behind the
149 object what we clicked on, but before - otherwise it looks
151 return (xpos
> 2) ? offs
-2 : 0;
155 wxLayoutObjectText::Layout(wxDC
&dc
)
159 dc
.GetTextExtent(m_Text
,&m_Width
, &m_Height
, &descent
);
161 m_Top
= m_Height
- m_Bottom
;
164 #ifdef WXLAYOUT_DEBUG
166 wxLayoutObjectText::Debug(void)
168 wxLayoutObject::Debug();
169 WXLO_DEBUG((" `%s`", m_Text
.c_str()));
173 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
177 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
179 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
const &icon
)
181 m_Icon
= new wxBitmap(icon
);
185 wxLayoutObjectIcon::Copy(void)
187 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon(new
189 obj
->SetUserData(m_UserData
);
193 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
*icon
)
199 wxLayoutObjectIcon::Draw(wxDC
&dc
, wxPoint
const &coords
)
201 dc
.DrawBitmap(*m_Icon
, coords
.x
, coords
.y
-m_Icon
->GetHeight());
205 wxLayoutObjectIcon::Layout(wxDC
& /* dc */)
210 wxLayoutObjectIcon::GetSize(CoordType
*top
, CoordType
*bottom
) const
212 *top
= m_Icon
->GetHeight();
214 return wxPoint(m_Icon
->GetWidth(), m_Icon
->GetHeight());
219 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
223 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
225 wxLayoutObjectCmd::wxLayoutObjectCmd(int size
, int family
, int style
, int
226 weight
, bool underline
,
227 wxColour
const *fg
, wxColour
const *bg
)
230 m_font
= new wxFont(size
,family
,style
,weight
,underline
);
236 wxLayoutObjectCmd::Copy(void)
238 wxLayoutStyleInfo si
;
241 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd(
242 si
.size
, si
.family
, si
.style
, si
.weight
, si
.underline
,
243 m_ColourFG
, m_ColourBG
);
244 obj
->SetUserData(m_UserData
);
249 wxLayoutObjectCmd::~wxLayoutObjectCmd()
255 wxLayoutObjectCmd::GetStyle(wxLayoutStyleInfo
*si
) const
257 si
->size
= m_font
->GetPointSize();
258 si
->family
= m_font
->GetFamily();
259 si
->style
= m_font
->GetStyle();
260 si
->underline
= m_font
->GetUnderlined();
261 si
->weight
= m_font
->GetWeight();
263 si
->fg_red
= m_ColourFG
->Red();
264 si
->fg_green
= m_ColourFG
->Green();
265 si
->fg_blue
= m_ColourFG
->Blue();
266 si
->bg_red
= m_ColourBG
->Red();
267 si
->bg_green
= m_ColourBG
->Green();
268 si
->bg_blue
= m_ColourBG
->Blue();
272 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint
const & /* coords */)
277 dc
.SetTextForeground(*m_ColourFG
);
279 dc
.SetTextBackground(*m_ColourBG
);
283 wxLayoutObjectCmd::Layout(wxDC
&dc
)
285 // this get called, so that recalculation uses right font sizes
286 Draw(dc
, wxPoint(0,0));
290 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
292 The wxLayoutLine object
294 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
296 wxLayoutLine::wxLayoutLine(wxLayoutLine
*prev
)
304 RecalculatePosition();
307 m_LineNumber
= m_Previous
->GetLineNumber()+1;
308 m_Next
= m_Previous
->GetNextLine();
309 m_Previous
->m_Next
= this;
310 m_Height
= m_Previous
->GetHeight();
314 m_Next
->m_Previous
= this;
315 m_Next
->MoveLines(+1);
316 m_Next
->RecalculatePositions(1);
320 wxLayoutLine::~wxLayoutLine()
322 // kbList cleans itself
326 wxLayoutLine::RecalculatePosition(void)
329 m_Position
= m_Previous
->GetPosition() +
330 wxPoint(0,m_Previous
->GetHeight());
332 m_Position
= wxPoint(0,0);
337 wxLayoutLine::RecalculatePositions(int recurse
)
339 wxASSERT(recurse
>= 0);
340 wxPoint pos
= m_Position
;
341 CoordType height
= m_Height
;
343 // WXLO_TRACE("RecalculatePositions()");
344 RecalculatePosition();
349 if(m_Next
) m_Next
->RecalculatePositions(--recurse
);
351 else if(pos
!= m_Position
|| m_Height
!= height
)
352 if(m_Next
) m_Next
->RecalculatePositions();
356 wxLayoutObjectList::iterator
357 wxLayoutLine::FindObject(CoordType xpos
, CoordType
*offset
) const
361 wxLayoutObjectList::iterator i
;
362 CoordType x
= 0, len
;
365 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
367 len
= (**i
).GetLength();
368 if( x
<= xpos
&& xpos
<= x
+ len
)
373 x
+= (**i
).GetLength();
378 wxLayoutObjectList::iterator
379 wxLayoutLine::FindObjectScreen(wxDC
&dc
, CoordType xpos
, CoordType
*cxpos
) const
383 wxLayoutObjectList::iterator i
;
384 CoordType x
= 0, cx
= 0, width
;
386 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
389 width
= (**i
).GetWidth();
390 if( x
<= xpos
&& xpos
<= x
+ width
)
392 *cxpos
= cx
+ (**i
).GetOffsetScreen(dc
, xpos
-x
);
393 wxLogDebug("wxLayoutLine::FindObjectScreen: cursor xpos = %ld", *cxpos
);
396 x
+= (**i
).GetWidth();
397 cx
+= (**i
).GetLength();
399 // behind last object:
401 return m_ObjectList
.tail();
405 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
408 wxASSERT(obj
!= NULL
);
410 wxLOiterator i
= FindObject(xpos
, &offset
);
413 if(xpos
== 0 ) // aha, empty line!
415 m_ObjectList
.push_back(obj
);
416 m_Length
+= obj
->GetLength();
423 CoordType len
= (**i
).GetLength();
424 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
425 { // insert before this object
426 m_ObjectList
.insert(i
,obj
);
427 m_Length
+= obj
->GetLength();
432 if( i
== m_ObjectList
.tail()) // last object?
433 m_ObjectList
.push_back(obj
);
435 { // insert after current object
437 m_ObjectList
.insert(i
,obj
);
439 m_Length
+= obj
->GetLength();
442 /* Otherwise we need to split the current object.
443 Fortunately this can only be a text object. */
444 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
445 wxString left
, right
;
446 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
447 left
= tobj
->GetText().substr(0,offset
);
448 right
= tobj
->GetText().substr(offset
,len
-offset
);
449 // current text object gets set to right half
450 tobj
->GetText() = right
; // set new text
451 // before it we insert the new object
452 m_ObjectList
.insert(i
,obj
);
453 m_Length
+= obj
->GetLength();
454 // and before that we insert the left half
455 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
460 wxLayoutLine::Insert(CoordType xpos
, wxString text
)
464 wxLOiterator i
= FindObject(xpos
, &offset
);
465 if(i
!= NULLIT
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
467 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
468 tobj
->GetText().insert(offset
, text
);
469 m_Length
+= text
.Length();
474 return Insert(xpos
, new wxLayoutObjectText(text
));
478 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
480 CoordType offset
, len
;
484 wxLOiterator i
= FindObject(xpos
, &offset
);
487 if(i
== NULLIT
) return npos
;
488 // now delete from that object:
489 if((**i
).GetType() != WXLO_TYPE_TEXT
)
491 if(offset
!= 0) // at end of line after a non-text object
494 len
= (**i
).GetLength();
497 m_ObjectList
.erase(i
);
501 // tidy up: remove empty text objects
502 if((**i
).GetLength() == 0)
504 m_ObjectList
.erase(i
);
508 CoordType max
= (**i
).GetLength() - offset
;
509 if(npos
< max
) max
= npos
;
512 if(xpos
== GetLength())
515 { // at the end of an object
516 // move to begin of next object:
518 continue; // start over
523 if(offset
== 0 && max
== (**i
).GetLength())
524 m_ObjectList
.erase(i
); // remove the whole object
526 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
533 wxLayoutLine::DeleteWord(CoordType xpos
)
538 wxLOiterator i
= FindObject(xpos
, &offset
);
542 if(i
== NULLIT
) return false;
543 if((**i
).GetType() != WXLO_TYPE_TEXT
)
545 // This should only happen when at end of line, behind a non-text
547 if(offset
== (**i
).GetLength()) return false;
548 m_Length
-= (**i
).GetLength(); // -1
549 m_ObjectList
.erase(i
);
550 return true; // we are done
554 if(offset
== (**i
).GetLength()) // at end of object
559 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
561 wxString str
= tobj
->GetText();
562 str
= str
.substr(offset
,str
.Length()-offset
);
563 // Find out how many positions we need to delete:
564 // 1. eat leading space
565 while(isspace(str
[count
])) count
++;
566 // 2. eat the word itself:
567 while(isalnum(str
[count
])) count
++;
569 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
570 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
575 wxASSERT(0); // we should never arrive here
579 wxLayoutLine::DeleteLine(bool update
)
581 if(m_Next
) m_Next
->m_Previous
= m_Previous
;
582 if(m_Previous
) m_Previous
->m_Next
= m_Next
;
585 m_Next
->MoveLines(-1);
586 m_Next
->RecalculatePositions(1);
588 wxLayoutLine
*next
= m_Next
;
594 wxLayoutLine::Draw(wxDC
&dc
, const wxPoint
& offset
) const
596 wxLayoutObjectList::iterator i
;
597 wxPoint pos
= offset
;
598 pos
= pos
+ GetPosition();
602 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
605 pos
.x
+= (**i
).GetWidth();
610 wxLayoutLine::Layout(wxDC
&dc
, wxPoint
*cursorPos
,
614 wxLayoutObjectList::iterator i
;
617 oldHeight
= m_Height
;
619 topHeight
, bottomHeight
; // above and below baseline
622 objTopHeight
, objBottomHeight
;
625 m_Height
= 0; m_BaseLine
= 0;
627 topHeight
= 0; bottomHeight
= 0;
629 bool cursorFound
= false;
633 *cursorPos
= m_Position
;
636 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
639 size
= (**i
).GetSize(&objTopHeight
, &objBottomHeight
);
641 if(cursorPos
&& ! cursorFound
)
642 { // we need to check whether the text cursor is here
643 len
= (**i
).GetLength();
644 if(count
<= cx
&& count
+len
> cx
)
646 if((**i
).GetType() == WXLO_TYPE_TEXT
)
648 len
= cx
- count
; // pos in object
649 CoordType width
, height
, descent
;
650 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
651 &width
, &height
, &descent
);
652 cursorPos
->x
+= width
;
653 cursorPos
->y
= m_Position
.y
;
655 if(len
< (**i
).GetLength())
656 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
658 str
= WXLO_CURSORCHAR
;
659 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
660 wxASSERT(cursorSize
);
661 // Just in case some joker inserted an empty string object:
662 if(width
== 0) width
= WXLO_MINIMUM_CURSOR_WIDTH
;
663 if(height
== 0) height
= objHeight
;
664 cursorSize
->x
= width
;
665 cursorSize
->y
= height
;
666 cursorFound
= true; // no more checks
669 { // on some other object
670 CoordType top
, bottom
; // unused
671 *cursorSize
= (**i
).GetSize(&top
,&bottom
);
672 cursorPos
->y
= m_Position
.y
;
673 cursorFound
= true; // no more checks
679 cursorPos
->x
+= (**i
).GetWidth();
684 if(objHeight
> m_Height
) m_Height
= objHeight
;
685 if(objTopHeight
> topHeight
) topHeight
= objTopHeight
;
686 if(objBottomHeight
> bottomHeight
) bottomHeight
= objBottomHeight
;
688 if(topHeight
+ bottomHeight
> m_Height
) m_Height
=
689 topHeight
+bottomHeight
;
690 m_BaseLine
= topHeight
;
694 if(GetPreviousLine()) // empty line
696 m_Height
= GetPreviousLine()->GetHeight();
697 m_BaseLine
= GetPreviousLine()->m_BaseLine
;
701 CoordType width
, height
, descent
;
702 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
704 m_BaseLine
= m_Height
- descent
;
709 // tell next line about coordinate change
710 if(m_Next
&& objHeight
!= oldHeight
)
711 m_Next
->RecalculatePositions();
713 // We need to check whether we found a valid cursor size:
716 // this might be the case if the cursor is at the end of the
717 // line or on a command object:
718 if(cursorSize
->y
< WXLO_MINIMUM_CURSOR_WIDTH
)
722 cursorSize
->y
= m_BaseLine
;
723 if(cursorSize
->x
< WXLO_MINIMUM_CURSOR_WIDTH
) cursorSize
->x
= WXLO_MINIMUM_CURSOR_WIDTH
;
727 CoordType width
, height
, descent
;
728 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
729 cursorSize
->x
= width
;
730 cursorSize
->y
= height
;
733 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
734 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
740 wxLayoutLine::Break(CoordType xpos
)
745 { // insert an empty line before this one
746 wxLayoutLine
*prev
= new wxLayoutLine(m_Previous
);
747 if(m_Previous
== NULL
)
748 { // We were in first line, need to link in new empty line
752 m_Previous
->m_Height
= GetHeight(); // this is a wild guess
756 m_Next
->RecalculatePositions(1);
761 wxLOiterator i
= FindObject(xpos
, &offset
);
763 // must be at the end of the line then
764 return new wxLayoutLine(this);
767 wxLayoutLine
*newLine
= new wxLayoutLine(this);
768 // split object at i:
769 if((**i
).GetType() == WXLO_TYPE_TEXT
&& offset
!= 0)
771 wxString left
, right
;
772 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
773 left
= tobj
->GetText().substr(0,offset
);
774 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
775 // current text object gets set to left half
776 tobj
->GetText() = left
; // set new text
777 newLine
->Append(new wxLayoutObjectText(right
));
778 m_Length
-= right
.Length();
779 i
++; // don't move this object to the new list
783 i
++; // move objects from here to new list
785 while(i
!= m_ObjectList
.end())
788 m_Length
-= (**i
).GetLength();
789 m_ObjectList
.remove(i
); // remove without deleting it
792 m_Next
->RecalculatePositions(2);
798 wxLayoutLine::MergeNextLine(void)
800 wxASSERT(GetNextLine());
801 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
804 for(i
= list
.begin(); i
!= list
.end();)
807 list
.remove(i
); // remove without deleting it
809 wxASSERT(list
.empty());
810 wxLayoutLine
*oldnext
= GetNextLine();
811 SetNext(GetNextLine()->GetNextLine());
813 RecalculatePositions(1);
817 wxLayoutLine::GetWrapPosition(CoordType column
)
820 wxLOiterator i
= FindObject(column
, &offset
);
821 if(i
== NULLIT
) return -1; // cannot wrap
823 // go backwards through the list and look for space in text objects
826 if((**i
).GetType() == WXLO_TYPE_TEXT
)
830 if( isspace(((wxLayoutObjectText
*)*i
)->GetText()[(size_t)offset
]))
837 }while(offset
!= -1);
838 i
--; // move on to previous object
842 column
-= (**i
).GetLength();
846 offset
= (**i
).GetLength();
848 /* If we reached the begin of the list and have more than one
849 object, that one is longer than the margin, so break behind
852 i
= m_ObjectList
.begin();
853 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
855 pos
+= (**i
).GetLength();
858 if(i
== NULLIT
) return -1; //why should this happen?
859 pos
+= (**i
).GetLength();
861 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
863 pos
+= (**i
).GetLength();
866 if(i
== NULLIT
) return -1; //this is possible, if there is only one text object
867 // now we are at the second text object:
868 pos
-= (**i
).GetLength();
869 return pos
; // in front of it
873 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
875 The wxLayoutList object
877 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
879 wxLayoutList::wxLayoutList()
881 m_DefaultSetting
= NULL
;
883 InvalidateUpdateRect();
887 wxLayoutList::~wxLayoutList()
893 wxLayoutList::Empty(void)
896 m_FirstLine
= m_FirstLine
->DeleteLine(false);
898 m_CursorPos
= wxPoint(0,0);
899 m_CursorScreenPos
= wxPoint(0,0);
900 m_CursorSize
= wxPoint(0,0);
901 m_FirstLine
= new wxLayoutLine(NULL
); // empty first line
902 m_CursorLine
= m_FirstLine
;
907 wxLayoutList::InternalClear(void)
912 delete m_DefaultSetting
;
913 m_DefaultSetting
= NULL
;
918 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
919 int underline
, wxColour
const *fg
,
922 if(family
!= -1) m_FontFamily
= family
;
923 if(size
!= -1) m_FontPtSize
= size
;
924 if(style
!= -1) m_FontStyle
= style
;
925 if(weight
!= -1) m_FontWeight
= weight
;
926 if(underline
!= -1) m_FontUnderline
= underline
!= 0;
928 if(fg
!= NULL
) m_ColourFG
= fg
;
929 if(bg
!= NULL
) m_ColourBG
= bg
;
932 new wxLayoutObjectCmd(m_FontPtSize
,m_FontFamily
,m_FontStyle
,m_FontWeight
,m_FontUnderline
,
933 m_ColourFG
, m_ColourBG
));
937 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
938 int underline
, char const *fg
, char const *bg
)
946 cfg
= wxTheColourDatabase
->FindColour(fg
);
948 cbg
= wxTheColourDatabase
->FindColour(bg
);
950 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
954 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
955 int /* underline */, char const *fg
, char const *bg
)
961 m_FontUnderline
= false;
962 m_FontFamily
= family
;
964 m_FontWeight
= weight
;
965 m_ColourFG
= wxTheColourDatabase
->FindColour(fg
);
966 m_ColourBG
= wxTheColourDatabase
->FindColour(bg
);
968 if(! m_ColourFG
) m_ColourFG
= wxBLACK
;
969 if(! m_ColourBG
) m_ColourBG
= wxWHITE
;
972 delete m_DefaultSetting
;
974 m_DefaultSetting
= new
975 wxLayoutObjectCmd(m_FontPtSize
,m_FontFamily
,m_FontStyle
,
976 m_FontWeight
,m_FontUnderline
,
977 m_ColourFG
, m_ColourBG
);
983 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
985 wxLayoutLine
*line
= m_FirstLine
;
986 while(line
&& line
->GetLineNumber() != p
.y
)
987 line
= line
->GetNextLine();
988 if(line
&& line
->GetLineNumber() == p
.y
) // found it
992 CoordType len
= line
->GetLength();
1000 m_CursorPos
.x
= len
;
1008 wxLayoutList::MoveCursorVertically(int n
)
1010 if(n
< 0) // move up
1012 if(m_CursorLine
== m_FirstLine
) return false;
1013 while(n
< 0 && m_CursorLine
)
1015 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1021 m_CursorLine
= m_FirstLine
;
1027 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1028 m_CursorPos
.x
= m_CursorLine
->GetLength();
1034 wxLayoutLine
*last
= m_CursorLine
;
1035 if(! m_CursorLine
->GetNextLine()) return false;
1036 while(n
> 0 && m_CursorLine
)
1040 m_CursorLine
= m_CursorLine
->GetNextLine();
1044 m_CursorLine
= last
;
1050 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1051 m_CursorPos
.x
= m_CursorLine
->GetLength();
1058 wxLayoutList::MoveCursorHorizontally(int n
)
1063 if(m_CursorPos
.x
== 0) // at begin of line
1065 if(! MoveCursorVertically(-1))
1067 MoveCursorToEndOfLine();
1072 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1073 m_CursorPos
.x
-= move
; n
+= move
;
1078 int len
= m_CursorLine
->GetLength();
1079 if(m_CursorPos
.x
== len
) // at end of line
1081 if(! MoveCursorVertically(1))
1083 MoveCursorToBeginOfLine();
1088 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1089 m_CursorPos
.x
+= move
;
1096 wxLayoutList::Insert(wxString
const &text
)
1098 wxASSERT(m_CursorLine
);
1099 m_CursorLine
->Insert(m_CursorPos
.x
, text
);
1100 m_CursorPos
.x
+= text
.Length();
1105 wxLayoutList::Insert(wxLayoutObject
*obj
)
1107 wxASSERT(m_CursorLine
);
1108 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
1109 m_CursorPos
.x
+= obj
->GetLength();
1114 wxLayoutList::LineBreak(void)
1116 wxASSERT(m_CursorLine
);
1118 bool setFirst
= (m_CursorLine
== m_FirstLine
&& m_CursorPos
.x
== 0);
1119 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
);
1120 if(setFirst
) // we were at beginning of first line
1121 m_FirstLine
= m_CursorLine
->GetPreviousLine();
1128 wxLayoutList::WrapLine(CoordType column
)
1130 if(m_CursorPos
.x
<= column
|| column
< 1)
1131 return false; // do nothing yet
1134 CoordType xpos
= m_CursorLine
->GetWrapPosition(column
);
1136 return false; // cannot break line
1138 CoordType newpos
= m_CursorPos
.x
- xpos
- 1;
1139 m_CursorPos
.x
= xpos
;
1141 Delete(1); // delete the space
1142 m_CursorPos
.x
= newpos
;
1148 wxLayoutList::Delete(CoordType npos
)
1150 wxASSERT(m_CursorLine
);
1154 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
1157 // More to delete, continue on next line.
1158 // First, check if line is empty:
1159 if(m_CursorLine
->GetLength() == 0)
1160 { // in this case, updating could probably be optimised
1162 wxASSERT(DeleteLines(1) == 0);
1171 // Need to join next line
1172 if(! m_CursorLine
->GetNextLine())
1176 m_CursorLine
->MergeNextLine();
1186 wxLayoutList::DeleteLines(int n
)
1188 wxASSERT(m_CursorLine
);
1192 if(!m_CursorLine
->GetNextLine())
1193 { // we cannot delete this line, but we can clear it
1194 MoveCursorToBeginOfLine();
1195 DeleteToEndOfLine();
1199 line
= m_CursorLine
;
1200 m_CursorLine
= m_CursorLine
->DeleteLine(true);
1202 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
1203 wxASSERT(m_FirstLine
);
1204 wxASSERT(m_CursorLine
);
1206 m_CursorLine
->RecalculatePositions(2);
1211 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
) const
1213 wxLayoutLine
*line
= m_FirstLine
;
1215 // first, make sure everything is calculated - this might not be
1216 // needed, optimise it later
1217 m_DefaultSetting
->Layout(dc
);
1220 line
->RecalculatePosition(); // so we don't need to do it all the time
1221 // little condition to speed up redrawing:
1222 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1223 line
= line
->GetNextLine();
1228 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
) const
1230 wxLayoutLine
*line
= m_FirstLine
;
1232 // first, make sure everything is calculated - this might not be
1233 // needed, optimise it later
1234 m_DefaultSetting
->Layout(dc
);
1237 if(line
== m_CursorLine
)
1238 line
->Layout(dc
, (wxPoint
*)&m_CursorScreenPos
, (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
1241 // little condition to speed up redrawing:
1242 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1243 line
= line
->GetNextLine();
1246 ///FIXME: disabled for now
1248 // can only be 0 if we are on the first line and have no next line
1249 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
1250 m_CursorLine
->GetNextLine() == NULL
&&
1251 m_CursorLine
== m_FirstLine
));
1256 wxLayoutList::Draw(wxDC
&dc
, wxPoint
const &offset
,
1257 CoordType top
, CoordType bottom
) const
1259 wxLayoutLine
*line
= m_FirstLine
;
1262 m_DefaultSetting
->Draw(dc
, wxPoint(0,0));
1263 wxBrush
*brush
= new wxBrush(*m_ColourBG
, wxSOLID
);
1264 dc
.SetBrush(*brush
);
1269 // only draw if between top and bottom:
1270 if((top
== -1 || line
->GetPosition().y
>= top
))
1271 line
->Draw(dc
, offset
);
1272 // little condition to speed up redrawing:
1273 if(bottom
!= -1 && line
->GetPosition().y
+ line
->GetHeight() > bottom
) break;
1274 line
= line
->GetNextLine();
1276 // can only be 0 if we are on the first line and have no next line
1277 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
1278 m_CursorLine
->GetNextLine() == NULL
&&
1279 m_CursorLine
== m_FirstLine
));
1283 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
, wxPoint
*cursorPos
)
1285 // First, find the right line:
1286 wxLayoutLine
*line
= m_FirstLine
;
1289 // we need to run a layout here to get font sizes right :-(
1290 m_DefaultSetting
->Layout(dc
);
1293 p
= line
->GetPosition();
1294 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
1297 line
= line
->GetNextLine();
1299 if(line
== NULL
) return NULL
; // not found
1300 if(cursorPos
) cursorPos
->y
= line
->GetLineNumber();
1301 // Now, find the object in the line:
1302 wxLOiterator i
= line
->FindObjectScreen(dc
, pos
.x
, & cursorPos
->x
);
1303 return (i
== NULLIT
) ? NULL
: *i
;
1308 wxLayoutList::GetSize(void) const
1311 *line
= m_FirstLine
,
1314 return wxPoint(0,0);
1316 wxPoint
maxPoint(0,0);
1321 if(line
->GetWidth() > maxPoint
.x
)
1322 maxPoint
.x
= line
->GetWidth();
1324 line
= line
->GetNextLine();
1327 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
1332 wxLayoutList::DrawCursor(wxDC
&dc
, bool active
, wxPoint
const &translate
)
1335 coords
= m_CursorScreenPos
;
1336 coords
.x
+= translate
.x
;
1337 coords
.y
+= translate
.y
;
1339 #ifdef WXLAYOUT_DEBUG
1340 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
1341 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
1342 (long)coords
.x
, (long)coords
.y
,
1343 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
1344 (long)m_CursorLine
->GetLineNumber(),
1345 (long)m_CursorLine
->GetLength()));
1348 dc
.SetBrush(*wxBLACK_BRUSH
);
1349 dc
.SetLogicalFunction(wxXOR
);
1350 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
1352 dc
.DrawRectangle(coords
.x
, coords
.y
, m_CursorSize
.x
,
1355 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
1356 coords
.x
+m_CursorSize
.x
, coords
.y
+m_CursorSize
.y
-1);
1357 dc
.SetLogicalFunction(wxCOPY
);
1358 //dc.SetBrush(wxNullBrush);
1361 /** Called by the objects to update the update rectangle.
1362 @param p a point to include in it
1365 wxLayoutList::SetUpdateRect(const wxPoint
&p
)
1367 if(m_UpdateRectValid
)
1368 GrowRect(m_UpdateRect
, p
);
1371 m_UpdateRect
.x
= p
.x
;
1372 m_UpdateRect
.y
= p
.y
;
1373 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
1374 m_UpdateRect
.height
= 4;// wxGTK :-)
1375 m_UpdateRectValid
= true;
1380 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1384 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1386 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
1387 wxString
const & title
)
1395 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
1397 // The following bit is taken from the printing sample, let's see
1398 // whether it works for us.
1400 /* You might use THIS code to set the printer DC to ROUGHLY reflect
1401 * the screen text size. This page also draws lines of actual length 5cm
1404 // Get the logical pixels per inch of screen and printer
1405 int ppiScreenX
, ppiScreenY
;
1406 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
1407 int ppiPrinterX
, ppiPrinterY
;
1408 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
1410 if(ppiScreenX
== 0) // not yet set, need to guess
1415 if(ppiPrinterX
== 0) // not yet set, need to guess
1421 // This scales the DC so that the printout roughly represents the
1422 // the screen scaling. The text point size _should_ be the right size
1423 // but in fact is too small for some reason. This is a detail that will
1424 // need to be addressed at some point but can be fudged for the
1426 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
1428 // Now we have to check in case our real page size is reduced
1429 // (e.g. because we're drawing to a print preview memory DC)
1430 int pageWidth
, pageHeight
;
1432 dc
->GetSize(&w
, &h
);
1433 GetPageSizePixels(&pageWidth
, &pageHeight
);
1434 if(pageWidth
!= 0) // doesn't work always
1436 // If printer pageWidth == current DC width, then this doesn't
1437 // change. But w might be the preview bitmap width, so scale down.
1438 scale
= scale
* (float)(w
/(float)pageWidth
);
1440 dc
->SetUserScale(scale
, scale
);
1444 bool wxLayoutPrintout::OnPrintPage(int page
)
1453 top
= (page
- 1)*m_PrintoutHeight
;
1454 bottom
= top
+ m_PrintoutHeight
;
1455 // SetDeviceOrigin() doesn't work here, so we need to manually
1456 // translate all coordinates.
1457 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
1458 m_llist
->Draw(*dc
, translate
, top
, bottom
);
1465 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
1467 /* We allocate a temporary wxDC for printing, so that we can
1468 determine the correct paper size and scaling. We don't actually
1469 print anything on it. */
1471 wxPrinterDC
psdc("","",WXLLIST_TEMPFILE
,false);
1473 wxPostScriptDC
psdc(WXLLIST_TEMPFILE
,false);
1476 float scale
= ScaleDC(&psdc
);
1478 psdc
.GetSize(&m_PageWidth
, &m_PageHeight
);
1479 // This sets a left/top origin of 10% and 20%:
1480 m_Offset
= wxPoint(m_PageWidth
/10, m_PageHeight
/20);
1482 // This is the length of the printable area.
1483 m_PrintoutHeight
= m_PageHeight
- (int) (m_PageHeight
* 0.1);
1484 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
1488 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
1491 *maxPage
= m_NumOfPages
;
1494 *selPageTo
= m_NumOfPages
;
1495 wxRemoveFile(WXLLIST_TEMPFILE
);
1498 bool wxLayoutPrintout::HasPage(int pageNum
)
1500 return pageNum
<= m_NumOfPages
;
1504 Stupid wxWindows doesn't draw proper ellipses, so we comment this
1505 out. It's a waste of paper anyway.
1509 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
1510 wxPoint topleft
, wxPoint bottomright
,
1513 // make backups of all essential parameters
1514 const wxBrush
& brush
= dc
.GetBrush();
1515 const wxPen
& pen
= dc
.GetPen();
1516 const wxFont
& font
= dc
.GetFont();
1518 dc
.SetBrush(*wxWHITE_BRUSH
);
1519 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
1520 dc
.DrawRoundedRectangle(topleft
.x
,
1521 topleft
.y
,bottomright
.x
-topleft
.x
,
1522 bottomright
.y
-topleft
.y
);
1523 dc
.SetBrush(*wxBLACK_BRUSH
);
1524 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
1525 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
1529 page
= "9999/9999 "; // many pages...
1531 dc
.GetTextExtent(page
,&w
,&h
);
1532 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
1533 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
1534 dc
.GetTextExtent("XXXX", &w
,&h
);
1535 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);