1 /*-*- c++ -*-********************************************************
2 * wxllist: wxLayoutList, a layout engine for text and graphics *
4 * (C) 1998-1999 by Karsten Ballüder (Ballueder@usa.net) *
7 *******************************************************************/
14 #pragma implementation "wxllist.h"
20 #include "wx/wxprec.h"
26 # include "gui/wxllist.h"
32 # include "iostream.h"
35 # include <wx/print.h>
41 /// This should never really get created
42 #define WXLLIST_TEMPFILE "__wxllist.tmp"
46 # define TypewxString(t) g_aTypewxStrings[t]
47 # define WXLO_DEBUG(x) wxLogDebug x
49 static const char *g_aTypewxStrings
[] =
51 "invalid", "text", "cmd", "icon"
54 wxLayoutObject::Debug(void)
56 WXLO_DEBUG(("%s",g_aTypewxStrings
[GetType()]));
59 # define TypewxString(t) ""
60 # define WXLO_DEBUG(x)
64 /// Cursors smaller than this disappear in XOR drawing mode
65 #define WXLO_MINIMUM_CURSOR_WIDTH 4
67 /// Use this character to estimate a cursor size when none is available.
68 #define WXLO_CURSORCHAR "E"
69 /** @name Helper functions */
71 /// allows me to compare to wxPoints
72 bool operator ==(wxPoint
const &p1
, wxPoint
const &p2
)
74 return p1
.x
== p2
.x
&& p1
.y
== p2
.y
;
77 /// allows me to compare to wxPoints
78 bool operator !=(wxPoint
const &p1
, wxPoint
const &p2
)
80 return p1
.x
!= p2
.x
|| p1
.y
!= p2
.y
;
83 /// allows me to compare to wxPoints
84 bool operator <=(wxPoint
const &p1
, wxPoint
const &p2
)
86 return p1
.y
< p2
.y
|| (p1
.y
== p2
.y
&& p1
.x
<= p2
.x
);
89 /// grows a wxRect so that it includes the given point
92 void GrowRect(wxRect
&r
, CoordType x
, CoordType y
)
96 else if(r
.x
+ r
.width
< x
)
101 else if(r
.y
+ r
.height
< y
)
105 /// returns true if the point is in the rectangle
107 bool Contains(const wxRect
&r
, const wxPoint
&p
)
109 return r
.x
<= p
.x
&& r
.y
<= p
.y
&& (r
.x
+r
.width
) >= p
.x
&& (r
.y
+ r
.height
) >= p
.y
;
113 /// Starts highlighting the selection
115 inline void StartHighlighting(wxDC
&dc
)
117 dc
.SetBrush(*wxBLACK_BRUSH
);
118 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
119 dc
.SetLogicalFunction(wxINVERT
);
122 /// Ends highlighting the selection
124 inline void EndHighlighting(wxDC
&dc
)
126 dc
.SetLogicalFunction(wxCOPY
);
130 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
134 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
136 wxLayoutObjectText::wxLayoutObjectText(const wxString
&txt
)
146 wxLayoutObjectText::Copy(void)
148 wxLayoutObjectText
*obj
= new wxLayoutObjectText(m_Text
);
149 obj
->m_Width
= m_Width
;
150 obj
->m_Height
= m_Height
;
152 obj
->m_Bottom
= m_Bottom
;
153 obj
->SetUserData(m_UserData
);
158 wxLayoutObjectText::GetSize(CoordType
*top
, CoordType
*bottom
) const
161 *top
= m_Top
; *bottom
= m_Bottom
;
162 return wxPoint(m_Width
, m_Height
);
166 wxLayoutObjectText::Draw(wxDC
&dc
, wxPoint
const &coords
,
167 CoordType begin
, CoordType end
)
170 dc
.DrawText(m_Text
, coords
.x
, coords
.y
-m_Top
);
173 // highlight the bit between begin and len
177 ypos
= coords
.y
-m_Top
;
178 long width
, height
, descent
;
180 str
= m_Text
.Mid(0, begin
);
181 dc
.DrawText(str
, xpos
, ypos
);
182 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
184 StartHighlighting(dc
);
185 str
= m_Text
.Mid(begin
, end
-begin
);
186 dc
.DrawText(str
, xpos
, ypos
);
187 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
189 dc
.SetLogicalFunction(wxCOPY
);
190 str
= m_Text
.Mid(end
, m_Text
.Length()-end
);
191 dc
.DrawText(str
, xpos
, ypos
);
196 wxLayoutObjectText::GetOffsetScreen(wxDC
&dc
, CoordType xpos
) const
200 maxlen
= m_Text
.Length();
203 height
, descent
= 0l;
205 if(xpos
== 0) return 0; // easy
207 while(width
< xpos
&& offs
< maxlen
)
209 dc
.GetTextExtent(m_Text
.substr(0,offs
),
210 &width
, &height
, &descent
);
213 /* We have to substract 1 to compensate for the offs++, and another
214 one because we don't want to position the cursor behind the
215 object what we clicked on, but before - otherwise it looks
217 return (xpos
> 2) ? offs
-2 : 0;
221 wxLayoutObjectText::Layout(wxDC
&dc
)
225 dc
.GetTextExtent(m_Text
,&m_Width
, &m_Height
, &descent
);
227 m_Top
= m_Height
- m_Bottom
;
230 #ifdef WXLAYOUT_DEBUG
232 wxLayoutObjectText::Debug(void)
234 wxLayoutObject::Debug();
235 WXLO_DEBUG((" `%s`", m_Text
.c_str()));
239 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
243 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
245 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
const &icon
)
247 m_Icon
= new wxBitmap(icon
);
251 wxLayoutObjectIcon::Copy(void)
253 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon(new
255 obj
->SetUserData(m_UserData
);
259 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
*icon
)
265 wxLayoutObjectIcon::Draw(wxDC
&dc
, wxPoint
const &coords
,
266 CoordType begin
, CoordType
/* len */)
269 StartHighlighting(dc
);
271 dc
.DrawBitmap(*m_Icon
, coords
.x
, coords
.y
-m_Icon
->GetHeight(),
272 (m_Icon
->GetMask() == NULL
) ? FALSE
: TRUE
);
276 wxLayoutObjectIcon::Layout(wxDC
& /* dc */)
281 wxLayoutObjectIcon::GetSize(CoordType
*top
, CoordType
*bottom
) const
283 *top
= m_Icon
->GetHeight();
285 return wxPoint(m_Icon
->GetWidth(), m_Icon
->GetHeight());
290 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
294 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
296 wxLayoutObjectCmd::wxLayoutObjectCmd(int size
, int family
, int style
, int
297 weight
, bool underline
,
298 wxColour
&fg
, wxColour
&bg
)
301 m_font
= new wxFont(size
,family
,style
,weight
,underline
);
307 wxLayoutObjectCmd::Copy(void)
309 wxLayoutStyleInfo si
;
312 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd(
313 si
.size
, si
.family
, si
.style
, si
.weight
, si
.underline
,
314 m_ColourFG
, m_ColourBG
);
315 obj
->SetUserData(m_UserData
);
320 wxLayoutObjectCmd::~wxLayoutObjectCmd()
326 wxLayoutObjectCmd::GetStyle(wxLayoutStyleInfo
*si
) const
328 si
->size
= m_font
->GetPointSize();
329 si
->family
= m_font
->GetFamily();
330 si
->style
= m_font
->GetStyle();
331 si
->underline
= m_font
->GetUnderlined();
332 si
->weight
= m_font
->GetWeight();
334 si
->fg_red
= m_ColourFG
.Red();
335 si
->fg_green
= m_ColourFG
.Green();
336 si
->fg_blue
= m_ColourFG
.Blue();
337 si
->bg_red
= m_ColourBG
.Red();
338 si
->bg_green
= m_ColourBG
.Green();
339 si
->bg_blue
= m_ColourBG
.Blue();
343 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint
const & /* coords */,
344 CoordType begin
, CoordType
/* len */)
348 dc
.SetTextForeground(m_ColourFG
);
349 dc
.SetTextBackground(m_ColourBG
);
353 wxLayoutObjectCmd::Layout(wxDC
&dc
)
355 // this get called, so that recalculation uses right font sizes
356 Draw(dc
, wxPoint(0,0));
360 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
362 The wxLayoutLine object
364 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
366 wxLayoutLine::wxLayoutLine(wxLayoutLine
*prev
, wxLayoutList
*llist
)
369 m_Width
= m_Height
= 0;
374 RecalculatePosition(llist
);
377 m_LineNumber
= m_Previous
->GetLineNumber()+1;
378 m_Next
= m_Previous
->GetNextLine();
379 m_Previous
->m_Next
= this;
380 m_Height
= m_Previous
->GetHeight();
384 m_Next
->m_Previous
= this;
385 m_Next
->MoveLines(+1);
386 m_Next
->RecalculatePositions(1,llist
);
390 wxLayoutLine::~wxLayoutLine()
392 // kbList cleans itself
396 wxLayoutLine::RecalculatePosition(wxLayoutList
*llist
)
399 m_Position
= m_Previous
->GetPosition() +
400 wxPoint(0,m_Previous
->GetHeight());
402 m_Position
= wxPoint(0,0);
403 llist
->SetUpdateRect(m_Position
);
408 wxLayoutLine::RecalculatePositions(int recurse
, wxLayoutList
*llist
)
410 wxASSERT(recurse
>= 0);
411 wxPoint pos
= m_Position
;
412 CoordType height
= m_Height
;
414 // WXLO_TRACE("RecalculatePositions()");
415 RecalculatePosition(llist
);
419 m_Next
->RecalculatePositions(--recurse
, llist
);
420 else if(pos
!= m_Position
|| m_Height
!= height
)
421 m_Next
->RecalculatePositions(0, llist
);
425 wxLayoutObjectList::iterator
426 wxLayoutLine::FindObject(CoordType xpos
, CoordType
*offset
) const
430 wxLayoutObjectList::iterator
433 CoordType x
= 0, len
;
435 /* We search through the objects. As we don't like returning the
436 object that the cursor is behind, we just remember such an
437 object in "found" so we can return it if there is really no
438 further object following it. */
439 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
441 len
= (**i
).GetLength();
442 if( x
<= xpos
&& xpos
<= x
+ len
)
445 if(xpos
== x
+ len
) // is there another object behind?
447 else // we are really inside this object
450 x
+= (**i
).GetLength();
452 return found
; // ==NULL if really none found
455 wxLayoutObjectList::iterator
456 wxLayoutLine::FindObjectScreen(wxDC
&dc
,
457 CoordType xpos
, CoordType
*cxpos
,
462 wxLayoutObjectList::iterator i
;
463 CoordType x
= 0, cx
= 0, width
;
465 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
468 width
= (**i
).GetWidth();
469 if( x
<= xpos
&& xpos
<= x
+ width
)
471 *cxpos
= cx
+ (**i
).GetOffsetScreen(dc
, xpos
-x
);
472 wxLogDebug("wxLayoutLine::FindObjectScreen: cursor xpos = %ld", *cxpos
);
473 if(found
) *found
= true;
476 x
+= (**i
).GetWidth();
477 cx
+= (**i
).GetLength();
479 // behind last object:
481 if(found
) *found
= false;
482 return m_ObjectList
.tail();
486 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
489 wxASSERT(obj
!= NULL
);
491 wxLOiterator i
= FindObject(xpos
, &offset
);
494 if(xpos
== 0 ) // aha, empty line!
496 m_ObjectList
.push_back(obj
);
497 m_Length
+= obj
->GetLength();
504 CoordType len
= (**i
).GetLength();
505 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
506 { // insert before this object
507 m_ObjectList
.insert(i
,obj
);
508 m_Length
+= obj
->GetLength();
513 if( i
== m_ObjectList
.tail()) // last object?
514 m_ObjectList
.push_back(obj
);
516 { // insert after current object
518 m_ObjectList
.insert(i
,obj
);
520 m_Length
+= obj
->GetLength();
523 /* Otherwise we need to split the current object.
524 Fortunately this can only be a text object. */
525 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
526 wxString left
, right
;
527 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
528 left
= tobj
->GetText().substr(0,offset
);
529 right
= tobj
->GetText().substr(offset
,len
-offset
);
530 // current text object gets set to right half
531 tobj
->GetText() = right
; // set new text
532 // before it we insert the new object
533 m_ObjectList
.insert(i
,obj
);
534 m_Length
+= obj
->GetLength();
535 // and before that we insert the left half
536 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
541 wxLayoutLine::Insert(CoordType xpos
, wxString text
)
545 wxLOiterator i
= FindObject(xpos
, &offset
);
546 if(i
!= NULLIT
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
548 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
549 tobj
->GetText().insert(offset
, text
);
550 m_Length
+= text
.Length();
555 return Insert(xpos
, new wxLayoutObjectText(text
));
559 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
561 CoordType offset
, len
;
565 wxLOiterator i
= FindObject(xpos
, &offset
);
568 if(i
== NULLIT
) return npos
;
569 // now delete from that object:
570 if((**i
).GetType() != WXLO_TYPE_TEXT
)
572 if(offset
!= 0) // at end of line after a non-text object
575 len
= (**i
).GetLength();
578 m_ObjectList
.erase(i
);
582 // tidy up: remove empty text objects
583 if((**i
).GetLength() == 0)
585 m_ObjectList
.erase(i
);
589 CoordType max
= (**i
).GetLength() - offset
;
590 if(npos
< max
) max
= npos
;
593 if(xpos
== GetLength())
596 { // at the end of an object
597 // move to begin of next object:
599 continue; // start over
604 if(offset
== 0 && max
== (**i
).GetLength())
605 m_ObjectList
.erase(i
); // remove the whole object
607 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
614 wxLayoutLine::DeleteWord(CoordType xpos
)
619 wxLOiterator i
= FindObject(xpos
, &offset
);
623 if(i
== NULLIT
) return false;
624 if((**i
).GetType() != WXLO_TYPE_TEXT
)
626 // This should only happen when at end of line, behind a non-text
628 if(offset
== (**i
).GetLength()) return false;
629 m_Length
-= (**i
).GetLength(); // -1
630 m_ObjectList
.erase(i
);
631 return true; // we are done
635 if(offset
== (**i
).GetLength()) // at end of object
640 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
642 wxString str
= tobj
->GetText();
643 str
= str
.substr(offset
,str
.Length()-offset
);
644 // Find out how many positions we need to delete:
645 // 1. eat leading space
646 while(isspace(str
.c_str()[count
])) count
++;
647 // 2. eat the word itself:
648 while(isalnum(str
.c_str()[count
])) count
++;
650 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
651 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
656 wxASSERT(0); // we should never arrive here
660 wxLayoutLine::DeleteLine(bool update
, wxLayoutList
*llist
)
662 if(m_Next
) m_Next
->m_Previous
= m_Previous
;
663 if(m_Previous
) m_Previous
->m_Next
= m_Next
;
666 m_Next
->MoveLines(-1);
667 m_Next
->RecalculatePositions(1, llist
);
669 wxLayoutLine
*next
= m_Next
;
675 wxLayoutLine::Draw(wxDC
&dc
,
677 const wxPoint
& offset
) const
679 wxLayoutObjectList::iterator i
;
680 wxPoint pos
= offset
;
681 pos
= pos
+ GetPosition();
685 CoordType xpos
= 0; // cursorpos, lenght of line
687 CoordType from
, to
, tempto
;
688 int highlight
= llist
->IsSelected(this, &from
, &to
);
689 if(highlight
== 1) // we need to draw the whole line inverted!
690 StartHighlighting(dc
);
694 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
696 if(highlight
== -1) // partially highlight line
698 // parts of the line need highlighting
699 tempto
= xpos
+(**i
).GetLength();
700 if(tempto
>= from
&& tempto
<= to
)
703 if(tempto
> (**i
).GetLength())
704 tempto
= (**i
).GetLength();
705 (**i
).Draw(dc
, pos
, from
-xpos
, to
);
712 pos
.x
+= (**i
).GetWidth();
713 xpos
+= (**i
).GetLength();
718 wxLayoutLine::Layout(wxDC
&dc
,
724 wxLayoutObjectList::iterator i
;
727 oldHeight
= m_Height
;
729 topHeight
, bottomHeight
; // above and below baseline
732 objTopHeight
, objBottomHeight
;
735 m_Height
= 0; m_BaseLine
= 0;
737 topHeight
= 0; bottomHeight
= 0;
739 bool cursorFound
= false;
743 *cursorPos
= m_Position
;
744 if(cursorSize
) *cursorSize
= wxPoint(0,0);
747 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
750 size
= (**i
).GetSize(&objTopHeight
, &objBottomHeight
);
752 if(cursorPos
&& ! cursorFound
)
753 { // we need to check whether the text cursor is here
754 len
= (**i
).GetLength();
755 if(count
<= cx
&& count
+len
> cx
)
757 if((**i
).GetType() == WXLO_TYPE_TEXT
)
759 len
= cx
- count
; // pos in object
760 CoordType width
, height
, descent
;
761 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
762 &width
, &height
, &descent
);
763 cursorPos
->x
+= width
;
764 cursorPos
->y
= m_Position
.y
;
766 if(len
< (**i
).GetLength())
767 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
769 str
= WXLO_CURSORCHAR
;
770 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
771 wxASSERT(cursorSize
);
772 // Just in case some joker inserted an empty string object:
773 if(width
== 0) width
= WXLO_MINIMUM_CURSOR_WIDTH
;
774 if(height
== 0) height
= objHeight
;
775 cursorSize
->x
= width
;
776 cursorSize
->y
= height
;
777 cursorFound
= true; // no more checks
780 { // on some other object
781 CoordType top
, bottom
; // unused
782 *cursorSize
= (**i
).GetSize(&top
,&bottom
);
783 cursorPos
->y
= m_Position
.y
;
784 cursorFound
= true; // no more checks
790 cursorPos
->x
+= (**i
).GetWidth();
795 if(objHeight
> m_Height
) m_Height
= objHeight
;
796 if(objTopHeight
> topHeight
) topHeight
= objTopHeight
;
797 if(objBottomHeight
> bottomHeight
) bottomHeight
= objBottomHeight
;
799 if(topHeight
+ bottomHeight
> m_Height
) m_Height
=
800 topHeight
+bottomHeight
;
801 m_BaseLine
= topHeight
;
805 if(GetPreviousLine()) // empty line
807 m_Height
= GetPreviousLine()->GetHeight();
808 m_BaseLine
= GetPreviousLine()->m_BaseLine
;
812 CoordType width
, height
, descent
;
813 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
815 m_BaseLine
= m_Height
- descent
;
820 // tell next line about coordinate change
821 if(m_Next
&& objHeight
!= oldHeight
)
822 m_Next
->RecalculatePositions(0, llist
);
824 // We need to check whether we found a valid cursor size:
827 // this might be the case if the cursor is at the end of the
828 // line or on a command object:
829 if(cursorSize
->y
< WXLO_MINIMUM_CURSOR_WIDTH
)
831 CoordType width
, height
, descent
;
832 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
833 cursorSize
->x
= width
;
834 cursorSize
->y
= height
;
836 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
837 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
843 wxLayoutLine::Break(CoordType xpos
, wxLayoutList
*llist
)
848 { // insert an empty line before this one
849 wxLayoutLine
*prev
= new wxLayoutLine(m_Previous
, llist
);
850 if(m_Previous
== NULL
)
851 { // We were in first line, need to link in new empty line
855 m_Previous
->m_Height
= GetHeight(); // this is a wild guess
859 m_Next
->RecalculatePositions(1, llist
);
864 wxLOiterator i
= FindObject(xpos
, &offset
);
866 // must be at the end of the line then
867 return new wxLayoutLine(this, llist
);
870 wxLayoutLine
*newLine
= new wxLayoutLine(this, llist
);
871 // split object at i:
872 if((**i
).GetType() == WXLO_TYPE_TEXT
&& offset
!= 0)
874 wxString left
, right
;
875 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
876 left
= tobj
->GetText().substr(0,offset
);
877 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
878 // current text object gets set to left half
879 tobj
->GetText() = left
; // set new text
880 newLine
->Append(new wxLayoutObjectText(right
));
881 m_Length
-= right
.Length();
882 i
++; // don't move this object to the new list
886 i
++; // move objects from here to new list
888 while(i
!= m_ObjectList
.end())
891 m_Length
-= (**i
).GetLength();
892 m_ObjectList
.remove(i
); // remove without deleting it
895 m_Next
->RecalculatePositions(2, llist
);
901 wxLayoutLine::MergeNextLine(wxLayoutList
*llist
)
903 wxASSERT(GetNextLine());
904 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
907 for(i
= list
.begin(); i
!= list
.end();)
910 list
.remove(i
); // remove without deleting it
912 wxASSERT(list
.empty());
913 wxLayoutLine
*oldnext
= GetNextLine();
914 SetNext(GetNextLine()->GetNextLine());
916 RecalculatePositions(1, llist
);
920 wxLayoutLine::GetWrapPosition(CoordType column
)
923 wxLOiterator i
= FindObject(column
, &offset
);
924 if(i
== NULLIT
) return -1; // cannot wrap
926 // go backwards through the list and look for space in text objects
929 if((**i
).GetType() == WXLO_TYPE_TEXT
)
933 if( isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
940 }while(offset
!= -1);
941 i
--; // move on to previous object
945 column
-= (**i
).GetLength();
949 offset
= (**i
).GetLength();
951 /* If we reached the begin of the list and have more than one
952 object, that one is longer than the margin, so break behind
955 i
= m_ObjectList
.begin();
956 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
958 pos
+= (**i
).GetLength();
961 if(i
== NULLIT
) return -1; //why should this happen?
962 pos
+= (**i
).GetLength();
964 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
966 pos
+= (**i
).GetLength();
969 if(i
== NULLIT
) return -1; //this is possible, if there is only one text object
970 // now we are at the second text object:
971 pos
-= (**i
).GetLength();
972 return pos
; // in front of it
976 #ifdef WXLAYOUT_DEBUG
978 wxLayoutLine::Debug(void)
981 wxPoint pos
= GetPosition();
982 tmp
.Printf("Line %ld, Pos (%ld,%ld), Height %ld",
983 (long int) GetLineNumber(),
984 (long int) pos
.x
, (long int) pos
.y
,
985 (long int) GetHeight());
991 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
993 The wxLayoutList object
995 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
997 wxLayoutList::wxLayoutList()
999 m_DefaultSetting
= NULL
;
1001 m_ColourFG
= *wxBLACK
;
1002 m_ColourBG
= *wxWHITE
;
1003 InvalidateUpdateRect();
1007 wxLayoutList::~wxLayoutList()
1010 m_FirstLine
->DeleteLine(false, this);
1014 wxLayoutList::Empty(void)
1017 m_FirstLine
= m_FirstLine
->DeleteLine(false, this);
1019 m_CursorPos
= wxPoint(0,0);
1020 m_CursorScreenPos
= wxPoint(0,0);
1021 m_CursorSize
= wxPoint(0,0);
1022 m_FirstLine
= new wxLayoutLine(NULL
, this); // empty first line
1023 m_CursorLine
= m_FirstLine
;
1024 InvalidateUpdateRect();
1029 wxLayoutList::InternalClear(void)
1032 if(m_DefaultSetting
)
1034 delete m_DefaultSetting
;
1035 m_DefaultSetting
= NULL
;
1040 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1041 int underline
, wxColour
*fg
,
1044 if(family
!= -1) m_FontFamily
= family
;
1045 if(size
!= -1) m_FontPtSize
= size
;
1046 if(style
!= -1) m_FontStyle
= style
;
1047 if(weight
!= -1) m_FontWeight
= weight
;
1048 if(underline
!= -1) m_FontUnderline
= underline
!= 0;
1050 if(fg
!= NULL
) m_ColourFG
= *fg
;
1051 if(bg
!= NULL
) m_ColourBG
= *bg
;
1054 new wxLayoutObjectCmd(m_FontPtSize
,m_FontFamily
,m_FontStyle
,m_FontWeight
,m_FontUnderline
,
1055 m_ColourFG
, m_ColourBG
));
1059 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1060 int underline
, char const *fg
, char const *bg
)
1068 cfg
= wxTheColourDatabase
->FindColour(fg
);
1070 cbg
= wxTheColourDatabase
->FindColour(bg
);
1072 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
1076 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
1077 int /* underline */, wxColour
*fg
, wxColour
*bg
)
1082 m_FontPtSize
= size
;
1083 m_FontUnderline
= false;
1084 m_FontFamily
= family
;
1085 m_FontStyle
= style
;
1086 m_FontWeight
= weight
;
1087 if(fg
) m_ColourFG
= *fg
;
1088 if(bg
) m_ColourBG
= *bg
;
1090 m_ColourFG
= *wxBLACK
;
1091 m_ColourBG
= *wxWHITE
;
1093 if(m_DefaultSetting
)
1094 delete m_DefaultSetting
;
1096 m_DefaultSetting
= new
1097 wxLayoutObjectCmd(m_FontPtSize
,m_FontFamily
,m_FontStyle
,
1098 m_FontWeight
,m_FontUnderline
,
1099 m_ColourFG
, m_ColourBG
);
1105 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
1107 SetUpdateRect(m_CursorScreenPos
);
1108 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1109 wxLayoutLine
*line
= m_FirstLine
;
1110 while(line
&& line
->GetLineNumber() != p
.y
)
1111 line
= line
->GetNextLine();
1112 if(line
&& line
->GetLineNumber() == p
.y
) // found it
1114 m_CursorPos
.y
= p
.y
;
1115 m_CursorLine
= line
;
1116 CoordType len
= line
->GetLength();
1119 m_CursorPos
.x
= p
.x
;
1124 m_CursorPos
.x
= len
;
1132 wxLayoutList::MoveCursorVertically(int n
)
1134 SetUpdateRect(m_CursorScreenPos
);
1135 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1137 if(n
< 0) // move up
1139 if(m_CursorLine
== m_FirstLine
) return false;
1140 while(n
< 0 && m_CursorLine
)
1142 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1148 m_CursorLine
= m_FirstLine
;
1154 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1155 m_CursorPos
.x
= m_CursorLine
->GetLength();
1161 wxLayoutLine
*last
= m_CursorLine
;
1162 if(! m_CursorLine
->GetNextLine()) return false;
1163 while(n
> 0 && m_CursorLine
)
1167 m_CursorLine
= m_CursorLine
->GetNextLine();
1171 m_CursorLine
= last
;
1177 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1178 m_CursorPos
.x
= m_CursorLine
->GetLength();
1186 wxLayoutList::MoveCursorHorizontally(int n
)
1188 SetUpdateRect(m_CursorScreenPos
);
1189 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1193 if(m_CursorPos
.x
== 0) // at begin of line
1195 if(! MoveCursorVertically(-1))
1197 MoveCursorToEndOfLine();
1202 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1203 m_CursorPos
.x
-= move
; n
+= move
;
1208 int len
= m_CursorLine
->GetLength();
1209 if(m_CursorPos
.x
== len
) // at end of line
1211 if(! MoveCursorVertically(1))
1213 MoveCursorToBeginOfLine();
1218 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1219 m_CursorPos
.x
+= move
;
1226 wxLayoutList::Insert(wxString
const &text
)
1228 wxASSERT(m_CursorLine
);
1229 SetUpdateRect(m_CursorScreenPos
);
1230 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1231 m_CursorLine
->Insert(m_CursorPos
.x
, text
);
1232 m_CursorPos
.x
+= text
.Length();
1237 wxLayoutList::Insert(wxLayoutObject
*obj
)
1239 wxASSERT(m_CursorLine
);
1240 SetUpdateRect(m_CursorScreenPos
);
1241 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1242 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
1243 m_CursorPos
.x
+= obj
->GetLength();
1248 wxLayoutList::LineBreak(void)
1250 wxASSERT(m_CursorLine
);
1251 bool setFirst
= (m_CursorLine
== m_FirstLine
&& m_CursorPos
.x
== 0);
1252 SetUpdateRect(m_CursorScreenPos
);
1253 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1254 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
, this);
1255 if(setFirst
) // we were at beginning of first line
1256 m_FirstLine
= m_CursorLine
->GetPreviousLine();
1263 wxLayoutList::WrapLine(CoordType column
)
1265 if(m_CursorPos
.x
<= column
|| column
< 1)
1266 return false; // do nothing yet
1269 CoordType xpos
= m_CursorLine
->GetWrapPosition(column
);
1271 return false; // cannot break line
1273 CoordType newpos
= m_CursorPos
.x
- xpos
- 1;
1274 m_CursorPos
.x
= xpos
;
1275 SetUpdateRect(m_CursorScreenPos
);
1276 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1278 Delete(1); // delete the space
1279 m_CursorPos
.x
= newpos
;
1285 wxLayoutList::Delete(CoordType npos
)
1287 wxASSERT(m_CursorLine
);
1288 SetUpdateRect(m_CursorScreenPos
);
1289 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1293 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
1296 // More to delete, continue on next line.
1297 // First, check if line is empty:
1298 if(m_CursorLine
->GetLength() == 0)
1299 { // in this case, updating could probably be optimised
1301 wxASSERT(DeleteLines(1) == 0);
1310 // Need to join next line
1311 if(! m_CursorLine
->GetNextLine())
1315 m_CursorLine
->MergeNextLine(this);
1325 wxLayoutList::DeleteLines(int n
)
1327 wxASSERT(m_CursorLine
);
1329 SetUpdateRect(m_CursorScreenPos
);
1330 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1333 if(!m_CursorLine
->GetNextLine())
1334 { // we cannot delete this line, but we can clear it
1335 MoveCursorToBeginOfLine();
1336 DeleteToEndOfLine();
1340 line
= m_CursorLine
;
1341 m_CursorLine
= m_CursorLine
->DeleteLine(true, this);
1343 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
1344 wxASSERT(m_FirstLine
);
1345 wxASSERT(m_CursorLine
);
1347 m_CursorLine
->RecalculatePositions(2, this);
1352 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
)
1354 wxLayoutLine
*line
= m_FirstLine
;
1356 // first, make sure everything is calculated - this might not be
1357 // needed, optimise it later
1358 m_DefaultSetting
->Layout(dc
);
1361 line
->RecalculatePosition(this); // so we don't need to do it all the time
1362 // little condition to speed up redrawing:
1363 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1364 line
= line
->GetNextLine();
1369 wxLayoutList::UpdateCursorScreenPos(wxDC
&dc
)
1371 wxASSERT(m_CursorLine
);
1372 m_CursorLine
->Layout(dc
, this, (wxPoint
*)&m_CursorScreenPos
, (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
1376 wxLayoutList::GetCursorScreenPos(wxDC
&dc
)
1378 UpdateCursorScreenPos(dc
);
1379 return m_CursorScreenPos
;
1383 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
)
1385 wxLayoutLine
*line
= m_FirstLine
;
1387 // first, make sure everything is calculated - this might not be
1388 // needed, optimise it later
1389 m_DefaultSetting
->Layout(dc
);
1392 if(line
== m_CursorLine
)
1393 line
->Layout(dc
, this, (wxPoint
*)&m_CursorScreenPos
, (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
1395 line
->Layout(dc
, this);
1396 // little condition to speed up redrawing:
1397 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1398 line
= line
->GetNextLine();
1401 ///FIXME: disabled for now
1403 // can only be 0 if we are on the first line and have no next line
1404 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
1405 m_CursorLine
->GetNextLine() == NULL
&&
1406 m_CursorLine
== m_FirstLine
));
1408 SetUpdateRect(m_CursorScreenPos
);
1409 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1413 wxLayoutList::Draw(wxDC
&dc
,
1414 wxPoint
const &offset
,
1418 wxLayoutLine
*line
= m_FirstLine
;
1421 m_DefaultSetting
->Draw(dc
, wxPoint(0,0));
1422 wxBrush
brush(m_ColourBG
, wxSOLID
);
1427 // only draw if between top and bottom:
1428 if((top
== -1 || line
->GetPosition().y
+ line
->GetHeight() >= top
))
1429 line
->Draw(dc
, this, offset
);
1430 // little condition to speed up redrawing:
1431 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1432 line
= line
->GetNextLine();
1434 // can only be 0 if we are on the first line and have no next line
1435 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
1436 m_CursorLine
->GetNextLine() == NULL
&&
1437 m_CursorLine
== m_FirstLine
));
1438 InvalidateUpdateRect();
1442 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
,
1446 // First, find the right line:
1447 wxLayoutLine
*line
= m_FirstLine
;
1450 // we need to run a layout here to get font sizes right :-(
1451 m_DefaultSetting
->Layout(dc
);
1454 p
= line
->GetPosition();
1455 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
1457 line
->Layout(dc
, this);
1458 line
= line
->GetNextLine();
1462 if(found
) *found
= false;
1463 return NULL
; // not found
1465 if(cursorPos
) cursorPos
->y
= line
->GetLineNumber();
1466 // Now, find the object in the line:
1467 wxLOiterator i
= line
->FindObjectScreen(dc
, pos
.x
,
1468 cursorPos
? & cursorPos
->x
: NULL
,
1470 return (i
== NULLIT
) ? NULL
: *i
;
1475 wxLayoutList::GetSize(void) const
1478 *line
= m_FirstLine
,
1481 return wxPoint(0,0);
1483 wxPoint
maxPoint(0,0);
1488 if(line
->GetWidth() > maxPoint
.x
)
1489 maxPoint
.x
= line
->GetWidth();
1491 line
= line
->GetNextLine();
1494 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
1499 wxLayoutList::DrawCursor(wxDC
&dc
, bool active
, wxPoint
const &translate
)
1502 coords
= m_CursorScreenPos
;
1503 coords
.x
+= translate
.x
;
1504 coords
.y
+= translate
.y
;
1506 #ifdef WXLAYOUT_DEBUG
1507 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
1508 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
1509 (long)coords
.x
, (long)coords
.y
,
1510 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
1511 (long)m_CursorLine
->GetLineNumber(),
1512 (long)m_CursorLine
->GetLength()));
1515 dc
.SetBrush(*wxBLACK_BRUSH
);
1516 dc
.SetLogicalFunction(wxXOR
);
1517 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
1519 dc
.DrawRectangle(coords
.x
, coords
.y
, m_CursorSize
.x
,
1522 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
1523 coords
.x
+m_CursorSize
.x
, coords
.y
+m_CursorSize
.y
-1);
1524 dc
.SetLogicalFunction(wxCOPY
);
1525 //dc.SetBrush(wxNullBrush);
1529 wxLayoutList::SetUpdateRect(CoordType x
, CoordType y
)
1531 if(m_UpdateRectValid
)
1532 GrowRect(m_UpdateRect
, x
, y
);
1537 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
1538 m_UpdateRect
.height
= 4;// wxGTK :-)
1539 m_UpdateRectValid
= true;
1544 wxLayoutList::StartSelection(void)
1546 wxLogDebug("Starting selection at %ld/%ld", m_CursorPos
.x
, m_CursorPos
.y
);
1547 m_Selection
.m_CursorA
= m_CursorPos
;
1548 m_Selection
.m_selecting
= true;
1549 m_Selection
.m_valid
= false;
1553 wxLayoutList::EndSelection(void)
1555 wxLogDebug("Ending selection at %ld/%ld", m_CursorPos
.x
, m_CursorPos
.y
);
1556 m_Selection
.m_CursorB
= m_CursorPos
;
1557 m_Selection
.m_selecting
= false;
1558 m_Selection
.m_valid
= true;
1560 // We always want m_CursorA <= m_CursorB!
1561 if(! (m_Selection
.m_CursorA
<= m_Selection
.m_CursorB
))
1563 wxPoint help
= m_Selection
.m_CursorB
;
1564 m_Selection
.m_CursorB
= m_Selection
.m_CursorA
;
1565 m_Selection
.m_CursorA
= help
;
1570 wxLayoutList::IsSelecting(void)
1572 return m_Selection
.m_selecting
;
1576 wxLayoutList::IsSelected(const wxPoint
&cursor
)
1578 return m_Selection
.m_CursorA
<= cursor
1579 && cursor
<= m_Selection
.m_CursorB
;
1583 /** Tests whether this layout line is selected and needs
1585 @param line to test for
1586 @return 0 = not selected, 1 = fully selected, -1 = partially
1590 wxLayoutList::IsSelected(const wxLayoutLine
*line
, CoordType
*from
,
1593 wxASSERT(line
); wxASSERT(to
); wxASSERT(from
);
1595 CoordType y
= line
->GetLineNumber();
1596 if(m_Selection
.m_CursorA
.y
< y
&& m_Selection
.m_CursorB
.y
> y
)
1598 else if(m_Selection
.m_CursorA
.y
== y
)
1600 *from
= m_Selection
.m_CursorA
.x
;
1601 if(m_Selection
.m_CursorB
.y
== y
)
1602 *to
= m_Selection
.m_CursorB
.x
;
1604 *to
= line
->GetLength();
1607 else if(m_Selection
.m_CursorB
.y
== y
)
1609 *to
= m_Selection
.m_CursorB
.x
;
1610 if(m_Selection
.m_CursorA
.y
== y
)
1611 *from
= m_Selection
.m_CursorA
.x
;
1620 #ifdef WXLAYOUT_DEBUG
1623 wxLayoutList::Debug(void)
1628 for(line
= m_FirstLine
;
1630 line
= line
->GetNextLine())
1637 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1641 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1643 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
1644 wxString
const & title
)
1651 wxLayoutPrintout::~wxLayoutPrintout()
1656 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
1658 // The following bit is taken from the printing sample, let's see
1659 // whether it works for us.
1661 /* You might use THIS code to set the printer DC to ROUGHLY reflect
1662 * the screen text size. This page also draws lines of actual length 5cm
1665 // Get the logical pixels per inch of screen and printer
1666 int ppiScreenX
, ppiScreenY
;
1667 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
1668 int ppiPrinterX
, ppiPrinterY
;
1669 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
1671 if(ppiScreenX
== 0) // not yet set, need to guess
1676 if(ppiPrinterX
== 0) // not yet set, need to guess
1682 // This scales the DC so that the printout roughly represents the
1683 // the screen scaling. The text point size _should_ be the right size
1684 // but in fact is too small for some reason. This is a detail that will
1685 // need to be addressed at some point but can be fudged for the
1687 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
1689 // Now we have to check in case our real page size is reduced
1690 // (e.g. because we're drawing to a print preview memory DC)
1691 int pageWidth
, pageHeight
;
1693 dc
->GetSize(&w
, &h
);
1694 GetPageSizePixels(&pageWidth
, &pageHeight
);
1695 if(pageWidth
!= 0) // doesn't work always
1697 // If printer pageWidth == current DC width, then this doesn't
1698 // change. But w might be the preview bitmap width, so scale down.
1699 scale
= scale
* (float)(w
/(float)pageWidth
);
1701 dc
->SetUserScale(scale
, scale
);
1705 bool wxLayoutPrintout::OnPrintPage(int page
)
1714 top
= (page
- 1)*m_PrintoutHeight
;
1715 bottom
= top
+ m_PrintoutHeight
;
1716 // SetDeviceOrigin() doesn't work here, so we need to manually
1717 // translate all coordinates.
1718 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
1719 m_llist
->Draw(*dc
, translate
, top
, bottom
);
1726 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
1728 /* We allocate a temporary wxDC for printing, so that we can
1729 determine the correct paper size and scaling. We don't actually
1730 print anything on it. */
1732 wxPrinterDC
psdc("","",WXLLIST_TEMPFILE
,false);
1734 wxPostScriptDC
psdc(WXLLIST_TEMPFILE
,false);
1737 float scale
= ScaleDC(&psdc
);
1739 psdc
.GetSize(&m_PageWidth
, &m_PageHeight
);
1740 // This sets a left/top origin of 15% and 20%:
1741 m_Offset
= wxPoint((15*m_PageWidth
)/100, m_PageHeight
/20);
1743 // This is the length of the printable area.
1744 m_PrintoutHeight
= m_PageHeight
- (int) (m_PageHeight
* 0.15);
1745 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
1749 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
1752 *maxPage
= m_NumOfPages
;
1755 *selPageTo
= m_NumOfPages
;
1756 wxRemoveFile(WXLLIST_TEMPFILE
);
1759 bool wxLayoutPrintout::HasPage(int pageNum
)
1761 return pageNum
<= m_NumOfPages
;
1765 Stupid wxWindows doesn't draw proper ellipses, so we comment this
1766 out. It's a waste of paper anyway.
1770 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
1771 wxPoint topleft
, wxPoint bottomright
,
1774 // make backups of all essential parameters
1775 const wxBrush
& brush
= dc
.GetBrush();
1776 const wxPen
& pen
= dc
.GetPen();
1777 const wxFont
& font
= dc
.GetFont();
1779 dc
.SetBrush(*wxWHITE_BRUSH
);
1780 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
1781 dc
.DrawRoundedRectangle(topleft
.x
,
1782 topleft
.y
,bottomright
.x
-topleft
.x
,
1783 bottomright
.y
-topleft
.y
);
1784 dc
.SetBrush(*wxBLACK_BRUSH
);
1785 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
1786 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
1790 page
= "9999/9999 "; // many pages...
1792 dc
.GetTextExtent(page
,&w
,&h
);
1793 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
1794 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
1795 dc
.GetTextExtent("XXXX", &w
,&h
);
1796 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);