1 /*-*- c++ -*-********************************************************
2 * wxllist: wxLayoutList, a layout engine for text and graphics *
4 * (C) 1998-2000 by Karsten Ballüder (Ballueder@gmx.net) *
7 *******************************************************************/
13 Layout() recalculates the objects, sizes, etc.
14 Draw() just draws them with the current settings, without
15 re-layout()ing them again
17 Each line has its own wxLayoutStyleInfo structure which gets updated
18 from within Layout(). Thanks to this, we don't need to re-layout all
19 lines if we want to draw one, but can just use its styleinfo to set
24 #include "wx/wxprec.h"
34 # include "gui/wxllist.h"
35 # include "gui/wxlparser.h"
36 # define SHOW_SELECTIONS 1
39 # include "wxlparser.h"
40 # define SHOW_SELECTIONS 1
52 # include "wx/print.h"
54 # include "wx/filefn.h"
57 #ifdef WXLAYOUT_USE_CARET
58 # include "wx/caret.h"
59 #endif // WXLAYOUT_USE_CARET
64 /// This should never really get created
65 #define WXLLIST_TEMPFILE "__wxllist.tmp"
69 # define TypeString(t) g_aTypeStrings[t]
70 # define WXLO_DEBUG(x) wxLogDebug x
72 static const wxChar
*g_aTypeStrings
[] =
74 _T("invalid"), _T("text"), _T("cmd"), _T("icon")
77 wxLayoutObject::DebugDump() const
80 str
.Printf(wxT("%s"), g_aTypeStrings
[GetType()]);
84 # define TypeString(t) ""
85 # define WXLO_DEBUG(x)
89 // FIXME under MSW, this constant is needed to make the thing properly redraw
90 // itself - I don't know where the size calculation error is and I can't
91 // waste time looking for it right now. Search for occurences of
92 // MSW_CORRECTION to find all the places where I did it.
94 static const int MSW_CORRECTION
= 10;
96 static const int MSW_CORRECTION
= 0;
99 /// Cursors smaller than this disappear in XOR drawing mode
100 #define WXLO_MINIMUM_CURSOR_WIDTH 4
102 /// Use this character to estimate a cursor size when none is available.
103 #define WXLO_CURSORCHAR "E"
104 /** @name Helper functions */
106 /// allows me to compare to wxPoints
107 bool operator <=(wxPoint
const &p1
, wxPoint
const &p2
)
109 return p1
.y
< p2
.y
|| (p1
.y
== p2
.y
&& p1
.x
<= p2
.x
);
113 The following STAY HERE until we have a working wxGTK again!!!
115 #ifndef wxWANTS_CHARS
116 /// allows me to compare to wxPoints
117 bool operator ==(wxPoint
const &p1
, wxPoint
const &p2
)
119 return p1
.x
== p2
.x
&& p1
.y
== p2
.y
;
122 /// allows me to compare to wxPoints
123 bool operator !=(wxPoint
const &p1
, wxPoint
const &p2
)
125 return p1
.x
!= p2
.x
|| p1
.y
!= p2
.y
;
128 wxPoint
& operator += (wxPoint
&p1
, wxPoint
const &p2
)
136 /// allows me to compare to wxPoints
137 bool operator>(wxPoint
const &p1
, wxPoint
const &p2
)
142 /// grows a wxRect so that it includes the given point
145 void GrowRect(wxRect
&r
, CoordType x
, CoordType y
)
149 else if(r
.x
+ r
.width
< x
)
154 else if(r
.y
+ r
.height
< y
)
160 /// returns true if the point is in the rectangle
162 bool Contains(const wxRect
&r
, const wxPoint
&p
)
164 return r
.x
<= p
.x
&& r
.y
<= p
.y
&& (r
.x
+r
.width
) >= p
.x
&& (r
.y
+ r
.height
) >= p
.y
;
173 void ReadString(wxString
&to
, wxString
&from
)
176 const wxChar
*cptr
= from
.c_str();
177 while(*cptr
&& *cptr
!= wxT('\n'))
188 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
192 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
196 wxLayoutObject::Read(wxString
&istr
)
199 ReadString(tmp
, istr
);
200 long l
= WXLO_TYPE_INVALID
;
207 return wxLayoutObjectText::Read(istr
);
209 return wxLayoutObjectCmd::Read(istr
);
211 return wxLayoutObjectIcon::Read(istr
);
217 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
221 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
223 wxLayoutObjectText::wxLayoutObjectText(const wxString
&txt
)
233 wxLayoutObjectText::Copy()
235 wxLayoutObjectText
*obj
= new wxLayoutObjectText(m_Text
);
236 obj
->m_Width
= m_Width
;
237 obj
->m_Height
= m_Height
;
239 obj
->m_Bottom
= m_Bottom
;
240 obj
->SetUserData(m_UserData
);
246 wxLayoutObjectText::Write(wxString
&ostr
)
248 ostr
<< (int) WXLO_TYPE_TEXT
<< '\n'
253 wxLayoutObjectText::Read(wxString
&istr
)
256 ReadString(text
, istr
);
258 return new wxLayoutObjectText(text
);
262 wxLayoutObjectText::GetSize(CoordType
*top
, CoordType
*bottom
) const
265 *top
= m_Top
; *bottom
= m_Bottom
;
266 return wxPoint(m_Width
, m_Height
);
270 wxLayoutObjectText::Draw(wxDC
&dc
, wxPoint
const &coords
,
271 wxLayoutList
*wxllist
,
272 CoordType begin
, CoordType end
)
276 // draw the whole object normally
277 dc
.DrawText(m_Text
, coords
.x
, coords
.y
-m_Top
);
281 // highlight the bit between begin and len
284 ypos
= coords
.y
-m_Top
;
285 long width
, height
, descent
;
287 if(begin
< 0) begin
= 0;
288 if( end
> (signed)m_Text
.Length() )
289 end
= m_Text
.Length();
291 wxString str
= m_Text
.Mid(0, begin
);
292 dc
.DrawText(str
, xpos
, ypos
);
293 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
295 wxllist
->StartHighlighting(dc
);
296 str
= m_Text
.Mid(begin
, end
-begin
);
297 dc
.DrawText(str
, xpos
, ypos
);
298 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
300 wxllist
->EndHighlighting(dc
);
301 str
= m_Text
.Mid(end
, m_Text
.Length()-end
);
302 dc
.DrawText(str
, xpos
, ypos
);
307 wxLayoutObjectText::GetOffsetScreen(wxDC
&dc
, CoordType xpos
) const
311 maxlen
= m_Text
.Length();
314 height
, descent
= 0l;
316 if(xpos
== 0) return 0; // easy
318 while(width
< xpos
&& offs
< maxlen
)
320 dc
.GetTextExtent(m_Text
.substr(0,offs
),
321 &width
, &height
, &descent
);
324 /* We have to subtract 1 to compensate for the offs++, and another
325 one because we don't want to position the cursor behind the
326 object what we clicked on, but before - otherwise it looks
328 return (xpos
> 2) ? offs
-2 : 0;
332 wxLayoutObjectText::Layout(wxDC
&dc
, class wxLayoutList
*WXUNUSED(llist
))
336 // now this is done in wxLayoutLine::Layout(), but this code might be
337 // reenabled later - in principle, it's more efficient
339 CoordType widthOld
= m_Width
,
340 heightOld
= m_Height
;
344 CoordType a
,b
,c
,d
,e
,f
;
345 dc
.GetTextExtent(_T("test "), &a
, &b
, &c
);
346 dc
.GetTextExtent(_T("test"), &d
, &e
, &f
);
350 dc
.GetTextExtent(_T(" "), &d
, &e
, &f
);
353 dc
.GetTextExtent(m_Text
, &m_Width
, &m_Height
, &descent
);
356 if ( widthOld
!= m_Width
|| heightOld
!= m_Height
)
358 // as the text length changed, it must be refreshed
359 wxLayoutLine
*line
= GetLine();
361 wxCHECK_RET( line
, "wxLayoutObjectText can't refresh itself" );
363 // as our size changed, we need to repaint the part which was appended
364 wxPoint
position(line
->GetPosition());
366 // this is not the most efficient way (we repaint the whole line), but
367 // it's not too slow and is *simple*
368 if ( widthOld
< m_Width
)
370 if ( heightOld
< m_Height
)
371 heightOld
= m_Height
;
373 llist
->SetUpdateRect(position
.x
+ widthOld
+ MSW_CORRECTION
,
374 position
.y
+ heightOld
+ MSW_CORRECTION
);
379 m_Top
= m_Height
- m_Bottom
;
383 #ifdef WXLAYOUT_DEBUG
385 wxLayoutObjectText::DebugDump() const
388 str
= wxLayoutObject::DebugDump();
390 str2
.Printf(wxT(" `%s`"), m_Text
.c_str());
395 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
399 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
401 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
const &icon
)
405 wxFAIL_MSG(wxT("invalid icon"));
413 // FIXME ugly, ugly, ugly - but the only way to avoid slicing
414 m_Icon
= icon
.GetHBITMAP() ? new wxBitmap(icon
)
415 : new wxBitmap(wxBitmap((const wxBitmap
&)icon
));
417 m_Icon
= new wxBitmap(icon
);
423 wxLayoutObjectIcon::Write(wxString
&ostr
)
425 /* Exports icon through a temporary file. */
427 wxString file
= wxGetTempFileName(_T("wxloexport"));
429 ostr
<< (int) WXLO_TYPE_ICON
<< '\n'
431 m_Icon
->SaveFile(file
, WXLO_BITMAP_FORMAT
);
435 wxLayoutObjectIcon::Read(wxString
&istr
)
438 ReadString(file
, istr
);
440 if(! wxFileExists(file
))
442 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon
;
444 if(!obj
->m_Icon
->LoadFile(file
, WXLO_BITMAP_FORMAT
))
454 wxLayoutObjectIcon::Copy()
456 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon(new
458 obj
->SetUserData(m_UserData
);
462 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
*icon
)
466 m_Icon
= new wxBitmap
;
470 wxLayoutObjectIcon::Draw(wxDC
&dc
, wxPoint
const &coords
,
471 wxLayoutList
*WXUNUSED(wxllist
),
472 CoordType
WXUNUSED(begin
), CoordType
WXUNUSED(len
) )
474 dc
.DrawBitmap(*m_Icon
, coords
.x
, coords
.y
-m_Icon
->GetHeight(),
475 (m_Icon
->GetMask() == NULL
) ? false : true);
479 wxLayoutObjectIcon::Layout(wxDC
& /* dc */, class wxLayoutList
* )
484 wxLayoutObjectIcon::GetSize(CoordType
*top
, CoordType
*bottom
) const
486 *top
= m_Icon
->GetHeight();
488 return wxPoint(m_Icon
->GetWidth(), m_Icon
->GetHeight());
493 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
497 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
500 wxLayoutStyleInfo::wxLayoutStyleInfo(int ifamily
,
512 underline
= iul
!= 0;
514 m_fg_valid
= fg
!= 0;
515 m_bg_valid
= bg
!= 0;
516 m_fg
= m_fg_valid
? *fg
: *wxBLACK
;
517 m_bg
= m_bg_valid
? *bg
: *wxWHITE
;
520 #define COPY_SI_(what) if(right.what != -1) what = right.what;
523 wxLayoutStyleInfo::operator=(const wxLayoutStyleInfo
&right
)
530 if(right
.m_fg_valid
) m_fg
= right
.m_fg
;
531 if(right
.m_bg_valid
) m_bg
= right
.m_bg
;
535 wxLayoutObjectCmd::wxLayoutObjectCmd(int family
, int size
, int style
, int
536 weight
, int underline
,
537 wxColour
*fg
, wxColour
*bg
)
540 m_StyleInfo
= new wxLayoutStyleInfo(family
, size
,style
,weight
,underline
,fg
,bg
);
543 wxLayoutObjectCmd::wxLayoutObjectCmd(const wxLayoutStyleInfo
&si
)
546 m_StyleInfo
= new wxLayoutStyleInfo
;
551 wxLayoutObjectCmd::Copy()
553 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd(
558 m_StyleInfo
->underline
,
559 m_StyleInfo
->m_fg_valid
?
560 &m_StyleInfo
->m_fg
: NULL
,
561 m_StyleInfo
->m_bg_valid
?
562 &m_StyleInfo
->m_bg
: NULL
);
563 obj
->SetUserData(m_UserData
);
568 wxLayoutObjectCmd::Write(wxString
&ostr
)
570 ostr
<< (int) WXLO_TYPE_CMD
<< '\n'
571 << (int) m_StyleInfo
->family
<< '\n'
572 << (int) m_StyleInfo
->size
<< '\n'
573 << (int) m_StyleInfo
->style
<< '\n'
574 << (int) m_StyleInfo
->weight
<< '\n'
575 << (int) m_StyleInfo
->underline
<< '\n'
576 << (int) m_StyleInfo
->m_fg_valid
<< '\n'
577 << (int) m_StyleInfo
->m_bg_valid
<< '\n';
578 if(m_StyleInfo
->m_fg_valid
)
580 ostr
<< (int) m_StyleInfo
->m_fg
.Red() << '\n'
581 << (int) m_StyleInfo
->m_fg
.Green() << '\n'
582 << (int) m_StyleInfo
->m_fg
.Blue() << '\n';
584 if(m_StyleInfo
->m_bg_valid
)
586 ostr
<< (int) m_StyleInfo
->m_bg
.Red() << '\n'
587 << (int) m_StyleInfo
->m_bg
.Green() << '\n'
588 << (int) m_StyleInfo
->m_bg
.Blue() << '\n';
593 wxLayoutObjectCmd::Read(wxString
&istr
)
595 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd
;
599 ReadString(tmp
, istr
);
601 obj
->m_StyleInfo
->family
= (int) l
;
604 ReadString(tmp
, istr
);
606 obj
->m_StyleInfo
->size
= (int) l
;
608 ReadString(tmp
, istr
);
610 obj
->m_StyleInfo
->style
= (int) l
;
612 ReadString(tmp
, istr
);
614 obj
->m_StyleInfo
->weight
= (int) l
;
616 ReadString(tmp
, istr
);
618 obj
->m_StyleInfo
->underline
= (int) l
;
620 ReadString(tmp
, istr
);
622 obj
->m_StyleInfo
->m_fg_valid
= (int) l
;
624 ReadString(tmp
, istr
);
626 obj
->m_StyleInfo
->m_bg_valid
= (int) l
;
628 if(obj
->m_StyleInfo
->m_fg_valid
)
630 unsigned char red
, green
, blue
;
631 ReadString(tmp
, istr
);
633 red
= (unsigned char) l
;
635 ReadString(tmp
, istr
);
637 green
= (unsigned char) l
;
639 ReadString(tmp
, istr
);
641 blue
= (unsigned char) l
;
643 obj
->m_StyleInfo
->m_fg
= wxColour(red
, green
, blue
);
646 if(obj
->m_StyleInfo
->m_bg_valid
)
648 unsigned char red
, green
, blue
;
649 ReadString(tmp
, istr
);
651 red
= (unsigned char) l
;
653 ReadString(tmp
, istr
);
655 green
= (unsigned char) l
;
657 ReadString(tmp
, istr
);
659 blue
= (unsigned char) l
;
661 obj
->m_StyleInfo
->m_bg
= wxColour(red
, green
, blue
);
668 wxLayoutObjectCmd::~wxLayoutObjectCmd()
674 wxLayoutObjectCmd::GetStyle() const
680 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint
const & WXUNUSED(coords
),
681 wxLayoutList
*wxllist
,
682 CoordType
WXUNUSED(begin
), CoordType
WXUNUSED(len
))
684 wxASSERT(m_StyleInfo
);
685 wxllist
->ApplyStyle(*m_StyleInfo
, dc
);
689 wxLayoutObjectCmd::Layout(wxDC
&dc
, class wxLayoutList
* llist
)
691 // this get called, so that recalculation uses right font sizes
692 Draw(dc
, wxPoint(0,0), llist
);
696 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
698 The wxLayoutLine object
700 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
702 wxLayoutLine::wxLayoutLine(wxLayoutLine
*prev
, wxLayoutList
*llist
)
704 m_Width
= m_Height
= 0;
713 RecalculatePosition(llist
);
718 m_LineNumber
= m_Previous
->GetLineNumber() + 1;
719 m_Next
= m_Previous
->GetNextLine();
720 m_Previous
->m_Next
= this;
725 m_Next
->m_Previous
= this;
729 m_StyleInfo
= llist
->GetDefaultStyleInfo();
731 llist
->IncNumLines();
735 wxLayoutLine::RecalculatePosition(wxLayoutList
*llist
)
737 wxASSERT(m_Previous
|| GetLineNumber() == 0);
739 wxPoint
posOld(m_Position
);
743 m_Position
= m_Previous
->GetPosition();
744 m_Position
.y
+= m_Previous
->GetHeight();
747 m_Position
= wxPoint(0,0);
749 if ( m_Position
!= posOld
)
751 // the whole line moved and must be repainted
752 llist
->SetUpdateRect(m_Position
);
753 llist
->SetUpdateRect(m_Position
.x
+ GetWidth() + MSW_CORRECTION
,
754 m_Position
.y
+ GetHeight() + MSW_CORRECTION
);
755 llist
->SetUpdateRect(posOld
);
756 llist
->SetUpdateRect(posOld
.x
+ GetWidth() + MSW_CORRECTION
,
757 posOld
.y
+ GetHeight() + MSW_CORRECTION
);
764 wxLayoutObjectList::iterator
765 wxLayoutLine::FindObject(CoordType xpos
, CoordType
*offset
) const
769 wxLayoutObjectList::iterator
773 CoordType x
= 0, len
;
775 /* We search through the objects. As we don't like returning the
776 object that the cursor is behind, we just remember such an
777 object in "found" so we can return it if there is really no
778 further object following it. */
779 for(i
= m_ObjectList
.begin(); i
!= nulled
; i
++)
781 len
= (**i
).GetLength();
782 if( x
<= xpos
&& xpos
<= x
+ len
)
785 if(xpos
== x
+ len
) // is there another object behind?
787 else // we are really inside this object
790 x
+= (**i
).GetLength();
792 return found
; // ==NULL if really none found
795 wxLayoutObjectList::iterator
796 wxLayoutLine::FindObjectScreen(wxDC
&dc
, wxLayoutList
*llist
,
797 CoordType xpos
, CoordType
*cxpos
,
802 llist
->ApplyStyle(GetStyleInfo(), dc
);
804 wxLayoutObjectList::iterator i
, nulled(NULL
);
805 CoordType x
= 0, cx
= 0, width
;
807 for(i
= m_ObjectList
.begin(); i
!= nulled
; i
++)
809 wxLayoutObject
*obj
= *i
;
810 if ( obj
->GetType() == WXLO_TYPE_CMD
)
812 // this will set the correct font for the objects which follow
813 obj
->Layout(dc
, llist
);
816 width
= obj
->GetWidth();
817 if( x
<= xpos
&& xpos
<= x
+ width
)
819 *cxpos
= cx
+ obj
->GetOffsetScreen(dc
, xpos
-x
);
826 x
+= obj
->GetWidth();
827 cx
+= obj
->GetLength();
830 // behind last object:
835 return m_ObjectList
.tail();
838 /** Finds text in this line.
839 @param needle the text to find
840 @param xpos the position where to start the search
841 @return the cursoor coord where it was found or -1
844 wxLayoutLine::FindText(const wxString
&needle
, CoordType xpos
) const
847 wxString
const *text
;
849 for(wxLOiterator i
= m_ObjectList
.begin(); i
!= m_ObjectList
.end(); i
++)
851 if(cpos
>= xpos
) // search from here!
853 if((**i
).GetType() == WXLO_TYPE_TEXT
)
855 text
= & ((wxLayoutObjectText
*)(*i
))->GetText();
856 int relpos
= text
->Find(needle
);
857 if(relpos
>= cpos
-xpos
) // -1 if not found
862 cpos
+= (**i
).GetLength();
865 return -1; // not found
869 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
872 wxASSERT(obj
!= NULL
);
877 wxLOiterator i
= FindObject(xpos
, &offset
);
878 wxLayoutObjectList::iterator
nulled(NULL
);
881 if(xpos
== 0 ) // aha, empty line!
883 m_ObjectList
.push_back(obj
);
884 m_Length
+= obj
->GetLength();
891 CoordType len
= (**i
).GetLength();
892 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
893 { // insert before this object
894 m_ObjectList
.insert(i
,obj
);
895 m_Length
+= obj
->GetLength();
900 if( i
== m_ObjectList
.tail()) // last object?
901 m_ObjectList
.push_back(obj
);
903 { // insert after current object
905 m_ObjectList
.insert(i
,obj
);
907 m_Length
+= obj
->GetLength();
910 /* Otherwise we need to split the current object.
911 Fortunately this can only be a text object. */
912 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
913 wxString left
, right
;
914 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
915 left
= tobj
->GetText().substr(0,offset
);
916 right
= tobj
->GetText().substr(offset
,len
-offset
);
917 // current text object gets set to right half
918 tobj
->GetText() = right
; // set new text
919 // before it we insert the new object
920 m_ObjectList
.insert(i
,obj
);
921 m_Length
+= obj
->GetLength();
922 // and before that we insert the left half
923 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
928 wxLayoutLine::Insert(CoordType xpos
, const wxString
& text
)
935 wxLOiterator i
= FindObject(xpos
, &offset
);
936 wxLayoutObjectList::iterator
nulled(NULL
);
937 if(i
!= nulled
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
939 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
940 tobj
->GetText().insert(offset
, text
);
941 m_Length
+= text
.Length();
945 if ( !Insert(xpos
, new wxLayoutObjectText(text
)) )
953 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
955 CoordType offset
, len
;
960 wxLOiterator i
= FindObject(xpos
, &offset
);
961 wxLayoutObjectList::iterator
nulled(NULL
);
964 if(i
== nulled
) return npos
;
965 // now delete from that object:
966 if((**i
).GetType() != WXLO_TYPE_TEXT
)
968 if(offset
!= 0) // at end of line after a non-text object
971 len
= (**i
).GetLength();
974 m_ObjectList
.erase(i
);
978 // tidy up: remove empty text objects
979 if((**i
).GetLength() == 0)
981 m_ObjectList
.erase(i
);
985 CoordType max
= (**i
).GetLength() - offset
;
986 if(npos
< max
) max
= npos
;
989 if(xpos
== GetLength())
992 { // at the end of an object
993 // move to begin of next object:
995 continue; // start over
1000 if(offset
== 0 && max
== (**i
).GetLength())
1001 m_ObjectList
.erase(i
); // remove the whole object
1003 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
1011 wxLayoutLine::DeleteWord(CoordType xpos
)
1013 wxASSERT(xpos
>= 0);
1017 wxLOiterator i
= FindObject(xpos
, &offset
);
1018 wxLayoutObjectList::iterator
nulled(NULL
);
1021 if(i
== nulled
) return false;
1022 if((**i
).GetType() != WXLO_TYPE_TEXT
)
1024 // This should only happen when at end of line, behind a non-text
1026 if(offset
== (**i
).GetLength()) return false;
1027 m_Length
-= (**i
).GetLength(); // -1
1028 m_ObjectList
.erase(i
);
1029 return true; // we are done
1033 if(offset
== (**i
).GetLength()) // at end of object
1039 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
1041 wxString str
= tobj
->GetText();
1042 str
= str
.substr(offset
,str
.Length()-offset
);
1043 // Find out how many positions we need to delete:
1044 // 1. eat leading space
1045 while(isspace(str
.c_str()[count
])) count
++;
1046 // 2. eat the word itself:
1047 while(isalnum(str
.c_str()[count
])) count
++;
1049 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
1050 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
1058 wxFAIL_MSG(wxT("unreachable"));
1063 wxLayoutLine::DeleteLine(bool update
, wxLayoutList
*llist
)
1065 // maintain linked list integrity
1067 m_Next
->m_Previous
= m_Previous
;
1069 m_Previous
->m_Next
= m_Next
;
1071 // get the line numbers right again
1072 if ( update
&& m_Next
)
1077 // we can't use m_Next after "delete this", so we must save this pointer
1079 wxLayoutLine
*next
= m_Next
;
1082 llist
->DecNumLines();
1088 wxLayoutLine::Draw(wxDC
&dc
,
1089 wxLayoutList
*llist
,
1090 const wxPoint
& offset
) const
1092 wxLayoutObjectList::iterator i
, nulled(NULL
);
1093 wxPoint pos
= offset
;
1094 pos
= pos
+ GetPosition();
1096 pos
.y
+= m_BaseLine
;
1098 CoordType xpos
= 0; // cursorpos, length of line
1102 int highlight
= llist
->IsSelected(this, &from
, &to
);
1103 // WXLO_DEBUG(("highlight=%d", highlight ));
1104 if(highlight
== 1) // we need to draw the whole line inverted!
1105 llist
->StartHighlighting(dc
);
1107 llist
->EndHighlighting(dc
);
1109 for(i
= m_ObjectList
.begin(); i
!= nulled
; i
++)
1111 if(highlight
== -1) // partially highlight line
1113 // parts of the line need highlighting
1115 // Next line commented, code has no effect
1116 // xpos+(**i).GetLength();
1117 (**i
).Draw(dc
, pos
, llist
, from
-xpos
, to
-xpos
);
1120 (**i
).Draw(dc
, pos
, llist
);
1121 pos
.x
+= (**i
).GetWidth();
1122 xpos
+= (**i
).GetLength();
1127 This function does all the recalculation, that is, it should only be
1128 called from within wxLayoutList::Layout(), as it uses the current
1129 list's styleinfo and updates it.
1132 wxLayoutLine::Layout(wxDC
&dc
,
1133 wxLayoutList
*llist
,
1135 wxPoint
*cursorSize
,
1136 wxLayoutStyleInfo
*cursorStyle
,
1138 bool WXUNUSED(suppressSIupdate
))
1140 wxLayoutObjectList::iterator i
, nulled(NULL
);
1142 // when a line becomes dirty, we redraw it from the place where it was
1143 // changed till the end of line (because the following wxLayoutObjects are
1144 // moved when the preceding one changes) - calculate the update rectangle.
1145 CoordType updateTop
= m_Position
.y
,
1147 updateWidth
= m_Width
,
1148 updateHeight
= m_Height
;
1152 bottomHeight
= 0; // above and below baseline
1154 objTopHeight
, objBottomHeight
; // above and below baseline
1158 CoordType heightOld
= m_Height
;
1164 bool cursorFound
= false;
1166 RecalculatePosition(llist
);
1170 *cursorPos
= m_Position
;
1171 if(cursorSize
) *cursorSize
= wxPoint(0,0);
1174 m_StyleInfo
= llist
->GetStyleInfo(); // save current style
1175 for(i
= m_ObjectList
.begin(); i
!= nulled
; i
++)
1177 wxLayoutObject
*obj
= *i
;
1178 obj
->Layout(dc
, llist
);
1179 wxPoint sizeObj
= obj
->GetSize(&objTopHeight
, &objBottomHeight
);
1181 if(cursorPos
&& ! cursorFound
)
1183 // we need to check whether the text cursor is here
1184 len
= obj
->GetLength();
1185 if(count
<= cx
&& count
+len
> cx
)
1187 if(obj
->GetType() == WXLO_TYPE_TEXT
)
1189 len
= cx
- count
; // pos in object
1190 CoordType width
, height
, descent
;
1191 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
1192 &width
, &height
, &descent
);
1193 cursorPos
->x
+= width
;
1194 cursorPos
->y
= m_Position
.y
;
1196 if(len
< obj
->GetLength())
1197 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
1199 str
= _T(WXLO_CURSORCHAR
);
1200 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
1202 if(cursorStyle
) // set style info
1203 *cursorStyle
= llist
->GetStyleInfo();
1206 // Just in case some joker inserted an empty string object:
1208 width
= WXLO_MINIMUM_CURSOR_WIDTH
;
1211 cursorSize
->x
= width
;
1212 cursorSize
->y
= height
;
1215 cursorFound
= true; // no more checks
1219 // on some other object
1220 CoordType top
, bottom
; // unused
1222 *cursorSize
= obj
->GetSize(&top
,&bottom
);
1223 cursorPos
->y
= m_Position
.y
;
1224 cursorFound
= true; // no more checks
1230 cursorPos
->x
+= obj
->GetWidth();
1234 m_Width
+= sizeObj
.x
;
1235 if(sizeObj
.y
> m_Height
)
1237 m_Height
= sizeObj
.y
;
1240 if(objTopHeight
> topHeight
)
1241 topHeight
= objTopHeight
;
1242 if(objBottomHeight
> bottomHeight
)
1243 bottomHeight
= objBottomHeight
;
1248 if ( updateHeight
< m_Height
)
1249 updateHeight
= m_Height
;
1250 if ( updateWidth
< m_Width
)
1251 updateWidth
= m_Width
;
1253 // update all line if we don't know where to start from
1254 if ( updateLeft
== -1 )
1257 llist
->SetUpdateRect(updateLeft
, updateTop
);
1258 llist
->SetUpdateRect(updateLeft
+ updateWidth
+ MSW_CORRECTION
,
1259 updateTop
+ updateHeight
+ MSW_CORRECTION
);
1262 if(topHeight
+ bottomHeight
> m_Height
)
1264 m_Height
= topHeight
+bottomHeight
;
1267 m_BaseLine
= topHeight
;
1271 CoordType width
, height
, descent
;
1272 dc
.GetTextExtent(_T(WXLO_CURSORCHAR
), &width
, &height
, &descent
);
1274 m_BaseLine
= m_Height
- descent
;
1277 // tell next line about coordinate change
1278 if(m_Next
&& m_Height
!= heightOld
)
1280 m_Next
->MarkDirty();
1283 // We need to check whether we found a valid cursor size:
1284 if(cursorPos
&& cursorSize
)
1286 // this might be the case if the cursor is at the end of the
1287 // line or on a command object:
1288 if(cursorSize
->x
< WXLO_MINIMUM_CURSOR_WIDTH
)
1290 CoordType width
, height
, descent
;
1291 dc
.GetTextExtent(_T(WXLO_CURSORCHAR
), &width
, &height
, &descent
);
1292 cursorSize
->x
= width
;
1293 cursorSize
->y
= height
;
1295 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
1296 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
1303 wxLayoutLine::Break(CoordType xpos
, wxLayoutList
*llist
)
1305 wxASSERT(xpos
>= 0);
1310 wxLOiterator i
= FindObject(xpos
, &offset
);
1311 wxLayoutObjectList::iterator
nulled(NULL
);
1313 // must be at the end of the line then
1314 return new wxLayoutLine(this, llist
);
1317 wxLayoutLine
*newLine
= new wxLayoutLine(this, llist
);
1318 // split object at i:
1319 if((**i
).GetType() == WXLO_TYPE_TEXT
1321 && offset
!= (**i
).GetLength() )
1323 wxString left
, right
;
1324 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
1325 left
= tobj
->GetText().substr(0,offset
);
1326 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
1327 // current text object gets set to left half
1328 tobj
->GetText() = left
; // set new text
1329 newLine
->Append(new wxLayoutObjectText(right
));
1330 m_Length
-= right
.Length();
1331 i
++; // don't move this object to the new list
1336 i
++; // move objects from here to new list
1339 while(i
!= m_ObjectList
.end())
1341 wxLayoutObject
*obj
= *i
;
1342 newLine
->Append(obj
);
1343 m_Length
-= obj
->GetLength();
1345 m_ObjectList
.remove(i
); // remove without deleting it
1348 m_Next
->MarkDirty();
1353 wxLayoutLine::Wrap(CoordType wrapmargin
, wxLayoutList
*llist
)
1355 wxLayoutObjectList::iterator
nulled(NULL
);
1356 if(GetLength() < wrapmargin
)
1357 return false; // nothing to do
1359 // find the object which covers the wrapmargin:
1361 wxLOiterator i
= FindObject(wrapmargin
, &offset
);
1362 wxCHECK_MSG( i
!= nulled
, false,
1363 wxT("Cannot find object covering wrapmargin."));
1365 // from this object on, the rest of the line must be copied to the
1367 wxLOiterator copyObject
= nulled
;
1368 // if we split a text-object, we must pre-pend some text to the
1369 // next line later on, remember it here:
1370 wxString prependText
= _T("");
1371 // we might need to adjust the cursor position later, so remember it
1372 size_t xpos
= llist
->GetCursorPos().x
;
1373 // by how much did we shorten the current line:
1375 // remember cursor location of object
1376 size_t objectCursorPos
= 0;
1378 size_t breakpos
= offset
;
1380 if( (**i
).GetType() != WXLO_TYPE_TEXT
)
1382 // break before a non-text object
1387 bool foundSpace
= false;
1390 // while(i != nulled && (**i).GetType() != WXLO_TYPE_TEXT)
1392 // try to find a suitable place to split the object:
1393 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
1394 if((**i
).GetType() == WXLO_TYPE_TEXT
1395 && tobj
->GetText().Length() >= breakpos
)
1399 foundSpace
= isspace(tobj
->GetText()[breakpos
]) != 0;
1403 while ( breakpos
-- > 0 );
1410 if(! foundSpace
) // breakpos == 0!
1412 if(i
== m_ObjectList
.begin())
1413 return false; // could not break line
1417 while(i
!= m_ObjectList
.begin()
1418 && (**i
).GetType() != WXLO_TYPE_TEXT
)
1422 breakpos
= (**i
).GetLength();
1425 }while(! foundSpace
);
1426 // before we actually break the object, we need to know at which
1427 // cursorposition it starts, so we can restore the cursor if needed:
1428 if( this == llist
->GetCursorLine() && xpos
>= breakpos
)
1430 for(wxLOiterator j
= m_ObjectList
.begin();
1431 j
!= nulled
&& j
!= i
; j
++)
1432 objectCursorPos
+= (**j
).GetLength();
1434 // now we know where to break it:
1435 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
1436 shorter
= tobj
->GetLength() - breakpos
;
1437 // remember text to copy from this object
1438 prependText
= tobj
->GetText().Mid(breakpos
+1);
1439 tobj
->SetText(tobj
->GetText().Left(breakpos
));
1440 // copy every following object:
1441 copyObject
= i
; copyObject
++;
1444 // make sure there is an empty m_Next line:
1445 (void) new wxLayoutLine(this, llist
);
1447 // We need to move this and all following objects to the next
1448 // line. Starting from the end of line, to keep the order right.
1449 if(copyObject
!= nulled
)
1452 for(j
= m_ObjectList
.tail(); j
!= copyObject
; j
--)
1453 m_Next
->Prepend(*j
);
1454 m_Next
->Prepend(*copyObject
);
1455 // and now remove them from this list:
1456 while( copyObject
!= m_ObjectList
.end() )
1458 shorter
+= (**copyObject
).GetLength();
1459 m_ObjectList
.remove(copyObject
); // remove without deleting it
1462 m_Length
-= shorter
;
1464 if(prependText
.Length() > 0)
1465 m_Next
->Insert(0, prependText
);
1467 // do we need to adjust the cursor position?
1468 if( this == llist
->GetCursorLine() && xpos
>= breakpos
)
1470 xpos
= objectCursorPos
+ (xpos
- objectCursorPos
- breakpos
-
1471 ((xpos
> breakpos
) ? 1 : 0 ));
1473 // this assert is useless when xpos has unsigned type
1474 wxASSERT(xpos
>= 0);
1476 llist
->MoveCursorTo( wxPoint( xpos
, m_Next
->GetLineNumber()) );
1478 return true; // we wrapped the line
1482 wxLayoutLine::ReNumber()
1484 CoordType lineNo
= m_Previous
? m_Previous
->m_LineNumber
+1 : 0;
1485 m_LineNumber
= lineNo
++;
1487 for(wxLayoutLine
*next
= GetNextLine();
1488 next
; next
= next
->GetNextLine())
1489 next
->m_LineNumber
= lineNo
++;
1493 wxLayoutLine::MergeNextLine(wxLayoutList
*llist
)
1495 wxCHECK_RET( GetNextLine(),
1496 wxT("wxLayout internal error: no next line to merge"));
1497 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
1500 MarkDirty(GetWidth());
1502 wxLayoutObject
*last
= NULL
;
1503 for(i
= list
.begin(); i
!= list
.end();)
1505 wxLayoutObject
*current
= *i
;
1507 // merge text objects together for efficiency
1508 if ( last
&& last
->GetType() == WXLO_TYPE_TEXT
&&
1509 current
->GetType() == WXLO_TYPE_TEXT
)
1511 wxLayoutObjectText
*textObj
= (wxLayoutObjectText
*)last
;
1512 wxString
text(textObj
->GetText());
1513 text
+= ((wxLayoutObjectText
*)current
)->GetText();
1514 textObj
->SetText(text
);
1516 list
.erase(i
); // remove and delete it
1520 // just append the object "as was"
1523 list
.remove(i
); // remove without deleting it
1526 wxASSERT(list
.empty());
1528 wxLayoutLine
*oldnext
= GetNextLine();
1529 wxLayoutLine
*nextLine
= oldnext
->GetNextLine();
1533 nextLine
->ReNumber();
1537 // this is now done in Delete(), but if this function is ever called
1538 // from elsewhere, we might have to move refresh code back here (in
1539 // order not to duplicate it)
1541 wxPoint
pos(oldnext
->GetPosition());
1542 llist
->SetUpdateRect(pos
);
1543 llist
->SetUpdateRect(pos
.x
+ oldnext
->GetWidth() + MSW_CORRECTION
,
1544 pos
.y
+ oldnext
->GetHeight() + MSW_CORRECTION
);
1548 llist
->DecNumLines();
1554 wxLayoutLine::GetWrapPosition(CoordType column
)
1557 wxLOiterator i
= FindObject(column
, &offset
);
1558 wxLayoutObjectList::iterator
nulled(NULL
);
1559 if(i
== nulled
) return -1; // cannot wrap
1561 // go backwards through the list and look for space in text objects
1564 if((**i
).GetType() == WXLO_TYPE_TEXT
)
1568 if(isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1575 }while(offset
!= -1);
1576 i
--; // move on to previous object
1580 column
-= (**i
).GetLength();
1584 offset
= (**i
).GetLength();
1585 }while(i
!= nulled
);
1586 /* If we reached the begin of the list and have more than one
1587 object, that one is longer than the margin, so break behind
1590 i
= m_ObjectList
.begin();
1591 while(i
!= nulled
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1593 pos
+= (**i
).GetLength();
1596 if(i
== nulled
) return -1; //why should this happen?
1598 // now we are behind the one long text object and need to find the
1599 // first space in it
1600 for(offset
= 0; offset
< (**i
).GetLength(); offset
++)
1601 if( isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1605 pos
+= (**i
).GetLength();
1610 #ifdef WXLAYOUT_DEBUG
1612 wxLayoutLine::Debug() const
1614 wxLayoutObjectList::iterator
nulled(NULL
);
1615 wxPoint pos
= GetPosition();
1616 WXLO_DEBUG((wxT("Line %ld, Pos (%ld,%ld), Height %ld, BL %ld, Font: %d"),
1617 (long int) GetLineNumber(),
1618 (long int) pos
.x
, (long int) pos
.y
,
1619 (long int) GetHeight(),
1620 (long int) m_BaseLine
,
1621 (int) m_StyleInfo
.family
));
1622 if(m_ObjectList
.begin() != nulled
)
1624 WXLO_DEBUG(((**m_ObjectList
.begin()).DebugDump().c_str()));
1631 wxLayoutLine::Copy(wxLayoutList
*llist
,
1635 wxLayoutObjectList::iterator
nulled(NULL
);
1636 CoordType firstOffset
, lastOffset
;
1638 if(to
== -1) to
= GetLength();
1639 if(from
== to
) return;
1641 wxLOiterator first
= FindObject(from
, &firstOffset
);
1642 wxLOiterator last
= FindObject(to
, &lastOffset
);
1644 // Common special case: only one object
1645 if( first
!= nulled
&& last
!= nulled
&& *first
== *last
)
1647 if( (**first
).GetType() == WXLO_TYPE_TEXT
)
1649 llist
->Insert(new wxLayoutObjectText(
1650 ((wxLayoutObjectText
1651 *)*first
)->GetText().substr(firstOffset
,
1652 lastOffset
-firstOffset
))
1656 else // what can we do?
1658 if(lastOffset
> firstOffset
) // i.e. +1 :-)
1659 llist
->Insert( (**first
).Copy() );
1664 // If we reach here, we can safely copy the whole first object from
1665 // the firstOffset position on:
1666 if((**first
).GetType() == WXLO_TYPE_TEXT
&& firstOffset
!= 0)
1668 llist
->Insert(new wxLayoutObjectText(
1669 ((wxLayoutObjectText
*)*first
)->GetText().substr(firstOffset
))
1672 else if(firstOffset
== 0)
1673 llist
->Insert( (**first
).Copy() );
1674 // else nothing to copy :-(
1676 // Now we copy all objects before the last one:
1677 wxLOiterator i
= first
; i
++;
1678 for( ; i
!= last
; i
++)
1679 llist
->Insert( (**i
).Copy() );
1681 // And now the last object:
1684 if( (**last
).GetType() == WXLO_TYPE_TEXT
)
1686 llist
->Insert(new wxLayoutObjectText(
1687 ((wxLayoutObjectText
*)*last
)->GetText().substr(0,lastOffset
))
1691 llist
->Insert( (**last
).Copy() );
1696 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1698 The wxLayoutList object
1700 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1702 wxLayoutList::wxLayoutList()
1704 #ifdef WXLAYOUT_USE_CARET
1706 #endif // WXLAYOUT_USE_CARET
1710 SetAutoFormatting(true);
1711 ForceTotalLayout(true); // for the first time, do all
1712 InvalidateUpdateRect();
1716 wxLayoutList::~wxLayoutList()
1718 SetAutoFormatting(false);
1721 m_FirstLine
->DeleteLine(false, this);
1723 wxASSERT_MSG( m_numLines
== 0, wxT("line count calculation broken"));
1727 wxLayoutList::Empty()
1730 m_FirstLine
= m_FirstLine
->DeleteLine(false, this);
1732 m_CursorPos
= wxPoint(0,0);
1733 m_CursorScreenPos
= wxPoint(0,0);
1734 m_CursorSize
= wxPoint(0,0);
1735 m_movedCursor
= true;
1736 m_FirstLine
= new wxLayoutLine(NULL
, this); // empty first line
1737 m_CursorLine
= m_FirstLine
;
1738 InvalidateUpdateRect();
1743 wxLayoutList::InternalClear()
1745 m_Selection
.m_selecting
= false;
1746 m_Selection
.m_valid
= false;
1748 m_DefaultStyleInfo
.family
= wxSWISS
;
1749 m_DefaultStyleInfo
.size
= WXLO_DEFAULTFONTSIZE
;
1750 m_DefaultStyleInfo
.style
= wxNORMAL
;
1751 m_DefaultStyleInfo
.weight
= wxNORMAL
;
1752 m_DefaultStyleInfo
.underline
= 0;
1753 m_DefaultStyleInfo
.m_fg_valid
= true;
1754 m_DefaultStyleInfo
.m_fg
= *wxBLACK
;
1755 m_DefaultStyleInfo
.m_bg_valid
= true;
1756 m_DefaultStyleInfo
.m_bg
= *wxWHITE
;
1758 m_CurrentStyleInfo
= m_DefaultStyleInfo
;
1759 m_CursorStyleInfo
= m_DefaultStyleInfo
;
1763 wxLayoutList::Read(wxString
&istr
)
1765 /* In order to handle input of formatted string "nicely", we need
1766 to restore our current font settings after the string. So first
1767 of all, we create a StyleInfo structure with our current
1769 wxLayoutStyleInfo current_si
= GetStyleInfo();
1771 while(istr
.Length())
1773 // check for a linebreak:
1775 tmp
= istr
.BeforeFirst('\n');
1776 long l
= WXLO_TYPE_INVALID
;
1780 if(type
== WXLO_TYPE_LINEBREAK
)
1783 istr
= istr
.AfterFirst('\n');
1787 wxLayoutObject
*obj
= wxLayoutObject::Read(istr
);
1792 /* Now we use the current_si to restore our last font settings: */
1793 Insert(new wxLayoutObjectCmd(current_si
));
1798 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1799 int underline
, wxColour
*fg
,
1802 if(family
!= -1) m_CurrentStyleInfo
.family
= family
;
1803 if(size
!= -1) m_CurrentStyleInfo
.size
= size
;
1804 if(style
!= -1) m_CurrentStyleInfo
.style
= style
;
1805 if(weight
!= -1) m_CurrentStyleInfo
.weight
= weight
;
1806 if(underline
!= -1) m_CurrentStyleInfo
.underline
= underline
!= 0;
1807 if(fg
) m_CurrentStyleInfo
.m_fg
= *fg
;
1808 if(bg
) m_CurrentStyleInfo
.m_bg
= *bg
;
1810 new wxLayoutObjectCmd(
1811 m_CurrentStyleInfo
.family
,
1812 m_CurrentStyleInfo
.size
,
1813 m_CurrentStyleInfo
.style
,
1814 m_CurrentStyleInfo
.weight
,
1815 m_CurrentStyleInfo
.underline
,
1820 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1821 int underline
, wxChar
const *fg
, wxChar
const *bg
)
1824 wxColour cfg
= wxTheColourDatabase
->Find((fg
)?fg
:wxT("BLACK"));
1825 wxColour cbg
= wxTheColourDatabase
->Find((bg
)?bg
:wxT("WHITE"));
1827 SetFont(family
,size
,style
,weight
,underline
,&cfg
,&cbg
);
1831 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
1832 int underline
, wxColour
*fg
, wxColour
*bg
)
1835 m_DefaultStyleInfo
= wxLayoutStyleInfo(family
, size
, style
, weight
,
1837 m_CurrentStyleInfo
= m_DefaultStyleInfo
;
1839 // Empty() should be called after we set m_DefaultStyleInfo because
1840 // otherwise the style info for the first line (created in Empty()) would be
1846 wxLayoutList::FindText(const wxString
&needle
, const wxPoint
&cpos
) const
1851 for(line
= m_FirstLine
;
1853 line
= line
->GetNextLine())
1855 if(line
->GetLineNumber() >= cpos
.y
)
1857 xpos
= line
->FindText(needle
,
1858 (line
->GetLineNumber() == cpos
.y
) ?
1861 return wxPoint(xpos
, line
->GetLineNumber());
1864 return wxPoint(-1,-1);
1869 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
1871 AddCursorPosToUpdateRect();
1873 wxPoint cursorPosOld
= m_CursorPos
;
1875 wxLayoutLine
*line
= m_FirstLine
;
1876 while(line
&& line
->GetLineNumber() != p
.y
)
1877 line
= line
->GetNextLine();
1878 if(line
&& line
->GetLineNumber() == p
.y
) // found it
1880 m_CursorPos
.y
= p
.y
;
1881 m_CursorLine
= line
;
1882 CoordType len
= line
->GetLength();
1885 m_CursorPos
.x
= p
.x
;
1889 m_CursorPos
.x
= len
;
1893 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1895 return m_CursorPos
== p
;
1899 wxLayoutList::MoveCursorVertically(int n
)
1901 AddCursorPosToUpdateRect();
1903 wxPoint cursorPosOld
= m_CursorPos
;
1906 if(n
< 0) // move up
1908 if(m_CursorLine
== m_FirstLine
) return false;
1909 while(n
< 0 && m_CursorLine
)
1911 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1917 m_CursorLine
= m_FirstLine
;
1923 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1924 m_CursorPos
.x
= m_CursorLine
->GetLength();
1930 wxLayoutLine
*last
= m_CursorLine
;
1931 if(! m_CursorLine
->GetNextLine()) return false;
1932 while(n
> 0 && m_CursorLine
)
1936 last
= m_CursorLine
;
1937 m_CursorLine
= m_CursorLine
->GetNextLine();
1941 m_CursorLine
= last
;
1947 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1948 m_CursorPos
.x
= m_CursorLine
->GetLength();
1953 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1959 wxLayoutList::MoveCursorHorizontally(int n
)
1961 AddCursorPosToUpdateRect();
1963 wxPoint cursorPosOld
= m_CursorPos
;
1968 if(m_CursorPos
.x
== 0) // at begin of line
1970 if(! MoveCursorVertically(-1))
1972 MoveCursorToEndOfLine();
1977 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1978 m_CursorPos
.x
-= move
; n
+= move
;
1983 int len
= m_CursorLine
->GetLength();
1984 if(m_CursorPos
.x
== len
) // at end of line
1986 if(! MoveCursorVertically(1))
1988 MoveCursorToBeginOfLine();
1993 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1994 m_CursorPos
.x
+= move
;
1998 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
2004 wxLayoutList::MoveCursorWord(int n
, bool untilNext
)
2006 wxLayoutObjectList::iterator
nulled(NULL
);
2007 wxCHECK_MSG( m_CursorLine
, false, wxT("no current line") );
2008 wxCHECK_MSG( n
== -1 || n
== +1, false, wxT("not implemented yet") );
2010 CoordType moveDistance
= 0;
2012 wxLayoutLine
*lineCur
= m_CursorLine
;
2013 for ( wxLOiterator i
= lineCur
->FindObject(m_CursorPos
.x
, &offset
);
2021 // moving forward, pass to the first object of the next line
2023 lineCur
= lineCur
->GetNextLine();
2025 i
= lineCur
->GetFirstObject();
2029 // moving backwards, pass to the last object of the prev line
2031 lineCur
= lineCur
->GetPreviousLine();
2033 i
= lineCur
->GetLastObject();
2038 // moved to the end/beginning of text
2045 wxLayoutObject
*obj
= *i
;
2049 // calculate offset: we are either at the very beginning or the very
2050 // end of the object, so it isn't very difficult (the only time when
2051 // offset is != -1 is for the very first iteration when its value is
2052 // returned by FindObject)
2056 offset
= obj
->GetLength();
2059 if( obj
->GetType() != WXLO_TYPE_TEXT
)
2061 // any visible non text objects count as one word
2062 if ( obj
->IsVisibleObject() )
2066 moveDistance
+= obj
->GetLength();
2071 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)obj
;
2073 bool canAdvance
= true;
2075 if ( offset
== tobj
->GetLength() )
2080 // can't move further in this text object
2083 // still should move over the object border
2087 else if ( offset
> 0 )
2089 // offset is off by 1, make it a valid index
2096 const wxString
& text
= tobj
->GetText();
2097 const wxChar
*start
= text
.c_str();
2098 const wxChar
*end
= start
+ text
.length();
2099 const wxChar
*p
= start
+ offset
;
2107 // to the beginning/end of the next/prev word
2108 while ( p
>= start
&& p
< end
&& isspace(*p
) )
2113 // go to the end/beginning of the word (in a broad sense...)
2114 while ( p
>= start
&& p
< end
&& !isspace(*p
) )
2123 // now advance to the beginning of the next word
2124 while ( isspace(*p
) && p
< end
)
2130 // in these 2 cases we took 1 char too much
2131 if ( (p
< start
) || isspace(*p
) )
2137 CoordType moveDelta
= p
- start
- offset
;
2138 if ( (n
< 0) && (offset
== tobj
->GetLength() - 1) )
2140 // because we subtracted 1 from offset in this case above, now
2141 // compensate for it
2145 if ( moveDelta
!= 0 )
2147 moveDistance
+= moveDelta
;
2154 // except for the first iteration, offset is calculated in the beginning
2159 MoveCursorHorizontally(moveDistance
);
2165 wxLayoutList::Insert(wxString
const &text
)
2167 wxASSERT(m_CursorLine
);
2168 wxASSERT_MSG( text
.Find(wxT('\n')) == wxNOT_FOUND
,
2169 wxT("use wxLayoutImportText!") );
2174 AddCursorPosToUpdateRect();
2176 wxASSERT(m_CursorLine
->GetLength() >= m_CursorPos
.x
);
2178 if ( !m_CursorLine
->Insert(m_CursorPos
.x
, text
) )
2180 m_CursorPos
.x
+= text
.Length();
2182 m_movedCursor
= true;
2185 m_CursorLine
->MarkDirty();
2191 wxLayoutList::Insert(wxLayoutObject
*obj
)
2193 wxASSERT(m_CursorLine
);
2196 m_CursorLine
= GetFirstLine();
2198 AddCursorPosToUpdateRect();
2200 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
2201 m_CursorPos
.x
+= obj
->GetLength();
2202 m_movedCursor
= true;
2205 m_CursorLine
->MarkDirty();
2211 wxLayoutList::Insert(wxLayoutList
*llist
)
2213 wxLayoutObjectList::iterator
nulled(NULL
);
2217 for(wxLayoutLine
*line
= llist
->GetFirstLine();
2219 line
= line
->GetNextLine()
2222 for(wxLOiterator i
= line
->GetFirstObject();
2232 wxLayoutList::LineBreak()
2234 wxASSERT(m_CursorLine
);
2236 AddCursorPosToUpdateRect();
2238 wxPoint
position(m_CursorLine
->GetPosition());
2241 width
= m_CursorLine
->GetWidth(),
2242 height
= m_CursorLine
->GetHeight();
2244 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
, this);
2245 if(m_CursorLine
->GetPreviousLine() == NULL
)
2246 m_FirstLine
= m_CursorLine
;
2250 // The following code will produce a height which is guaranteed to
2251 // be too high: old lineheight + the height of both new lines.
2252 // We can probably drop the old line height and start with height =
2254 wxLayoutLine
*prev
= m_CursorLine
->GetPreviousLine();
2256 height
+= prev
->GetHeight();
2257 height
+= m_CursorLine
->GetHeight();
2259 m_movedCursor
= true;
2261 SetUpdateRect(position
);
2262 SetUpdateRect(position
.x
+ width
+ MSW_CORRECTION
,
2263 position
.y
+ height
+ MSW_CORRECTION
);
2269 wxLayoutList::WrapLine(CoordType column
)
2271 return m_CursorLine
->Wrap(column
, this);
2275 wxLayoutList::WrapAll(CoordType column
)
2277 wxLayoutLine
*line
= m_FirstLine
;
2283 rc
&= line
->Wrap(column
, this);
2284 line
= line
->GetNextLine();
2290 wxLayoutList::Delete(CoordType npos
)
2292 wxCHECK_MSG(m_CursorLine
, false, wxT("can't delete in non existing line"));
2297 AddCursorPosToUpdateRect();
2299 // were other lines appended to this one (this is important to know because
2300 // this means that our width _increased_ as the result of deletion)
2301 bool wasMerged
= false;
2303 // the size of the region to update
2304 CoordType totalHeight
= m_CursorLine
->GetHeight(),
2305 totalWidth
= m_CursorLine
->GetWidth();
2310 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
2314 // More to delete, continue on next line.
2316 // First, check if line is empty:
2317 if(m_CursorLine
->GetLength() == 0)
2319 // in this case, updating could probably be optimised
2321 wxASSERT(DeleteLines(1) == 0);
2330 // Need to join next line
2331 if(! m_CursorLine
->GetNextLine())
2336 wxLayoutLine
*next
= m_CursorLine
->GetNextLine();
2339 totalHeight
+= next
->GetHeight();
2340 totalWidth
+= next
->GetWidth();
2342 m_CursorLine
->MergeNextLine(this);
2347 wxFAIL_MSG(wxT("can't delete all this"));
2357 // we need to update the whole tail of the line and the lines which
2361 wxPoint
position(m_CursorLine
->GetPosition());
2362 SetUpdateRect(position
);
2363 SetUpdateRect(position
.x
+ totalWidth
+ MSW_CORRECTION
,
2364 position
.y
+ totalHeight
+ MSW_CORRECTION
);
2371 wxLayoutList::DeleteLines(int n
)
2373 wxASSERT(m_CursorLine
);
2376 AddCursorPosToUpdateRect();
2380 if(!m_CursorLine
->GetNextLine())
2381 { // we cannot delete this line, but we can clear it
2382 MoveCursorToBeginOfLine();
2383 DeleteToEndOfLine();
2385 m_CursorLine
->MarkDirty();
2389 line
= m_CursorLine
;
2390 m_CursorLine
= m_CursorLine
->DeleteLine(true, this);
2392 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
2393 wxASSERT(m_FirstLine
);
2394 wxASSERT(m_CursorLine
);
2397 m_CursorLine
->MarkDirty();
2402 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
)
2406 wxLayoutLine
*line
= m_FirstLine
;
2408 // first, make sure everything is calculated - this might not be
2409 // needed, optimise it later
2410 ApplyStyle(m_DefaultStyleInfo
, dc
);
2413 line
->RecalculatePosition(this); // so we don't need to do it all the time
2414 // little condition to speed up redrawing:
2415 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
2416 line
= line
->GetNextLine();
2421 wxLayoutList::GetCursorScreenPos() const
2423 return m_CursorScreenPos
;
2427 Is called before each Draw(). Now, it will re-layout all lines which
2431 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
, bool forceAll
,
2432 wxPoint
*cpos
, wxPoint
*csize
)
2434 // first, make sure everything is calculated - this might not be
2435 // needed, optimise it later
2436 ApplyStyle(m_DefaultStyleInfo
, dc
);
2445 ForceTotalLayout(false);
2448 // If one line was dirty, we need to re-calculate all
2449 // following lines, too.
2450 bool wasDirty
= forceAll
;
2451 // we need to layout until we reach at least the cursor line,
2452 // otherwise we won't be able to scroll to it
2453 bool cursorReached
= false;
2454 wxLayoutLine
*line
= m_FirstLine
;
2458 ApplyStyle(line
->GetStyleInfo(), dc
);
2460 // if any previous line was dirty, we need to layout all
2463 // go on until we find the cursorline
2465 // layout dirty lines:
2467 // always layout the cursor line toupdate the cursor
2468 // position and size:
2469 || line
== m_CursorLine
2470 // or if it's the line we are asked to look for:
2471 || (cpos
&& line
->GetLineNumber() == cpos
->y
)
2472 // layout at least the desired region:
2474 || (line
->GetPosition().y
<= bottom
)
2480 // The following Layout() calls will update our
2481 // m_CurrentStyleInfo if needed.
2482 if(line
== m_CursorLine
)
2484 line
->Layout(dc
, this,
2485 (wxPoint
*)&m_CursorScreenPos
,
2486 (wxPoint
*)&m_CursorSize
,
2489 // we cannot layout the line twice, so copy the coords:
2490 if(cpos
&& line
->GetLineNumber() == cpos
->y
)
2492 *cpos
= m_CursorScreenPos
;
2494 *csize
= m_CursorSize
;
2497 cursorReached
= true;
2501 if(cpos
&& line
->GetLineNumber() == cpos
->y
)
2503 line
->Layout(dc
, this,
2505 csize
, NULL
, cpos
->x
);
2506 cursorReached
= true;
2509 line
->Layout(dc
, this);
2513 line
= line
->GetNextLine();
2516 #ifndef WXLAYOUT_USE_CARET
2517 // can only be 0 if we are on the first line and have no next line
2518 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
2519 m_CursorLine
->GetNextLine() == NULL
&&
2520 m_CursorLine
== m_FirstLine
));
2521 #endif // WXLAYOUT_USE_CARET
2523 AddCursorPosToUpdateRect();
2527 wxLayoutList::GetScreenPos(wxDC
&dc
, const wxPoint
&cpos
, wxPoint
*csize
)
2530 Layout(dc
, -1, false, &pos
, csize
);
2535 wxLayoutList::Draw(wxDC
&dc
,
2536 wxPoint
const &offset
,
2541 wxLayoutLine
*line
= m_FirstLine
;
2543 if ( m_Selection
.m_discarded
)
2545 // calculate them if we don't have them already
2546 if ( !m_Selection
.HasValidScreenCoords() )
2548 m_Selection
.m_ScreenA
= GetScreenPos(dc
, m_Selection
.m_CursorA
);
2549 m_Selection
.m_ScreenB
= GetScreenPos(dc
, m_Selection
.m_CursorB
);
2552 // invalidate the area which was previousle selected - and which is not
2553 // selected any more
2554 SetUpdateRect(m_Selection
.m_ScreenA
);
2555 SetUpdateRect(m_Selection
.m_ScreenB
);
2557 m_Selection
.m_discarded
= false;
2560 /* This call to Layout() will re-calculate and update all lines
2565 ApplyStyle(m_DefaultStyleInfo
, dc
);
2566 wxBrush
brush(m_CurrentStyleInfo
.m_bg
, wxSOLID
);
2568 dc
.SetBackgroundMode(wxTRANSPARENT
);
2572 // only draw if between top and bottom:
2574 line
->GetPosition().y
+ line
->GetHeight() > top
))
2576 ApplyStyle(line
->GetStyleInfo(), dc
);
2577 // little condition to speed up redrawing:
2579 && line
->GetPosition().y
2580 +(clipStrictly
? line
->GetHeight() : 0) >= bottom
)
2583 line
->Draw(dc
, this, offset
);
2586 line
= line
->GetNextLine();
2589 InvalidateUpdateRect();
2591 WXLO_DEBUG((wxT("Selection is %s : %d,%d/%d,%d"),
2592 m_Selection
.m_valid
? wxT("valid") : wxT("invalid"),
2593 m_Selection
.m_CursorA
.x
, m_Selection
.m_CursorA
.y
,
2594 m_Selection
.m_CursorB
.x
, m_Selection
.m_CursorB
.y
));
2598 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
,
2599 wxPoint
*cursorPos
, bool *found
)
2601 wxLayoutObjectList::iterator
nulled(NULL
);
2602 // First, find the right line:
2604 *line
= m_FirstLine
,
2605 *lastline
= m_FirstLine
;
2608 ApplyStyle(m_DefaultStyleInfo
, dc
);
2611 p
= line
->GetPosition();
2612 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
2615 line
= line
->GetNextLine();
2618 bool didFind
= line
!= NULL
;
2622 // use the last line:
2627 cursorPos
->y
= line
->GetLineNumber();
2629 bool foundinline
= true;
2632 // Now, find the object in the line:
2637 i
= line
->FindObjectScreen(dc
, this,
2644 i
= line
->FindObjectScreen(dc
, this,
2650 *found
= didFind
&& foundinline
;
2652 return (i
== nulled
) ? NULL
: *i
;
2657 wxLayoutList::GetSize() const
2660 *line
= m_FirstLine
,
2663 return wxPoint(0,0);
2665 wxPoint
maxPoint(0,0);
2670 if(line
->GetWidth() > maxPoint
.x
)
2671 maxPoint
.x
= line
->GetWidth();
2673 line
= line
->GetNextLine();
2676 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
2678 // if the line was just added, its height would be 0 and we can't call
2679 // Layout() from here because we don't have a dc and we might be not drawing
2680 // at all, besides... So take the cursor height by default (taking 0 is bad
2681 // because then the scrollbars won't be resized and the new line won't be
2683 if ( last
->IsDirty() )
2685 if ( last
->GetHeight() == 0 )
2686 maxPoint
.y
+= m_CursorSize
.y
;
2687 if ( last
->GetWidth() == 0 && maxPoint
.x
< m_CursorSize
.x
)
2688 maxPoint
.x
= m_CursorSize
.x
;
2696 wxLayoutList::DrawCursor(wxDC
&
2697 #ifdef WXLAYOUT_USE_CARET
2703 #ifdef WXLAYOUT_USE_CARET
2708 , wxPoint
const &translate
)
2710 if ( m_movedCursor
)
2711 m_movedCursor
= false;
2713 wxPoint
coords(m_CursorScreenPos
);
2714 coords
+= translate
;
2716 #ifdef WXLAYOUT_DEBUG
2717 WXLO_DEBUG((wxT("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld"),
2718 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
2719 (long)coords
.x
, (long)coords
.y
,
2720 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
2721 (long)m_CursorLine
->GetLineNumber(),
2722 (long)m_CursorLine
->GetLength()));
2724 wxLogStatus(wxT("Cursor is at (%d, %d)"), m_CursorPos
.x
, m_CursorPos
.y
);
2727 #ifdef WXLAYOUT_USE_CARET
2728 m_caret
->Move(coords
);
2729 #else // !WXLAYOUT_USE_CARET
2731 wxASSERT(m_CursorSize
.x
>= WXLO_MINIMUM_CURSOR_WIDTH
);
2732 dc
.SetBrush(*wxWHITE_BRUSH
);
2733 //FIXME: wxGTK XOR is borken at the moment!!!dc.SetLogicalFunction(wxXOR);
2734 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
2737 dc
.SetLogicalFunction(wxXOR
);
2738 dc
.DrawRectangle(coords
.x
, coords
.y
,
2739 m_CursorSize
.x
, m_CursorSize
.y
);
2740 SetUpdateRect(coords
.x
, coords
.y
);
2741 SetUpdateRect(coords
.x
+m_CursorSize
.x
,
2742 coords
.y
+m_CursorSize
.y
);
2746 dc
.SetLogicalFunction(wxCOPY
);
2747 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
2748 coords
.x
, coords
.y
);
2749 SetUpdateRect(coords
.x
, coords
.y
+m_CursorSize
.y
-1);
2750 SetUpdateRect(coords
.x
, coords
.y
);
2753 dc
.SetLogicalFunction(wxCOPY
);
2754 //dc.SetBrush(wxNullBrush);
2755 #endif // WXLAYOUT_USE_CARET/!WXLAYOUT_USE_CARET
2759 wxLayoutList::SetUpdateRect(CoordType x
, CoordType y
)
2761 if(m_UpdateRectValid
)
2763 GrowRect(m_UpdateRect
, x
, y
);
2769 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
2770 m_UpdateRect
.height
= 4;// wxGTK :-)
2771 m_UpdateRectValid
= true;
2776 wxLayoutList::StartSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2778 wxPoint
cpos(cposOrig
);
2782 WXLO_DEBUG((wxT("Starting selection at %d/%d"), cpos
.x
, cpos
.y
));
2784 m_Selection
.m_CursorA
= cpos
;
2785 m_Selection
.m_CursorB
= cpos
;
2786 m_Selection
.m_ScreenA
= spos
;
2787 m_Selection
.m_ScreenB
= spos
;
2788 m_Selection
.m_selecting
= true;
2789 m_Selection
.m_valid
= false;
2793 wxLayoutList::ContinueSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2795 wxPoint
cpos(cposOrig
);
2799 wxASSERT(m_Selection
.m_selecting
== true);
2800 wxASSERT(m_Selection
.m_valid
== false);
2801 WXLO_DEBUG((wxT("Continuing selection at %d/%d"), cpos
.x
, cpos
.y
));
2803 m_Selection
.m_ScreenB
= spos
;
2804 m_Selection
.m_CursorB
= cpos
;
2808 wxLayoutList::EndSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2810 wxPoint
cpos(cposOrig
);
2812 if(cpos
.x
== -1) cpos
= m_CursorPos
;
2814 ContinueSelection(cpos
, spos
);
2816 WXLO_DEBUG((wxT("Ending selection at %d/%d"), cpos
.x
, cpos
.y
));
2818 // we always want m_CursorA <= m_CursorB!
2819 if( m_Selection
.m_CursorA
> m_Selection
.m_CursorB
)
2821 // exchange the start/end points
2822 wxPoint help
= m_Selection
.m_CursorB
;
2823 m_Selection
.m_CursorB
= m_Selection
.m_CursorA
;
2824 m_Selection
.m_CursorA
= help
;
2826 help
= m_Selection
.m_ScreenB
;
2827 m_Selection
.m_ScreenB
= m_Selection
.m_ScreenA
;
2828 m_Selection
.m_ScreenA
= help
;
2831 m_Selection
.m_selecting
= false;
2832 m_Selection
.m_valid
= true;
2833 /// In case we just clicked somewhere, the selection will have zero
2834 /// size, so we discard it immediately.
2835 if(m_Selection
.m_CursorA
== m_Selection
.m_CursorB
)
2842 wxLayoutList::DiscardSelection()
2844 if ( !HasSelection() )
2847 m_Selection
.m_valid
=
2848 m_Selection
.m_selecting
= false;
2849 m_Selection
.m_discarded
= true;
2853 wxLayoutList::IsSelecting() const
2855 return m_Selection
.m_selecting
;
2859 wxLayoutList::IsSelected(const wxPoint
&cursor
) const
2861 if ( !HasSelection() )
2865 (m_Selection
.m_CursorA
<= cursor
2866 && cursor
<= m_Selection
.m_CursorB
)
2867 || (m_Selection
.m_CursorB
<= cursor
2868 && cursor
<= m_Selection
.m_CursorA
)
2873 /** Tests whether this layout line is selected and needs
2875 @param line to test for
2876 @return 0 = not selected, 1 = fully selected, -1 = partially
2880 wxLayoutList::IsSelected(const wxLayoutLine
*line
, CoordType
*from
,
2883 wxASSERT(line
); wxASSERT(to
); wxASSERT(from
);
2885 if(! m_Selection
.m_valid
&& ! m_Selection
.m_selecting
)
2888 CoordType y
= line
->GetLineNumber();
2889 if ( (m_Selection
.m_CursorA
.y
< y
&& m_Selection
.m_CursorB
.y
> y
)
2890 || (m_Selection
.m_CursorB
.y
< y
&& m_Selection
.m_CursorA
.y
> y
) )
2894 else if (m_Selection
.m_CursorA
.y
== y
)
2896 *from
= m_Selection
.m_CursorA
.x
;
2897 if(m_Selection
.m_CursorB
.y
== y
)
2899 *to
= m_Selection
.m_CursorB
.x
;
2903 if(m_Selection
.m_CursorB
> m_Selection
.m_CursorA
)
2904 *to
= line
->GetLength();
2911 CoordType help
= *to
;
2918 else if (m_Selection
.m_CursorB
.y
== y
)
2920 *to
= m_Selection
.m_CursorB
.x
;
2921 if (m_Selection
.m_CursorA
.y
== y
)
2923 *from
= m_Selection
.m_CursorA
.x
;
2927 if(m_Selection
.m_CursorB
> m_Selection
.m_CursorA
)
2930 *from
= line
->GetLength();
2935 CoordType help
= *to
;
2948 wxLayoutList::DeleteSelection()
2950 if (! m_Selection
.m_valid
)
2953 m_Selection
.m_valid
= false;
2955 // Only delete part of the current line?
2956 if (m_Selection
.m_CursorA
.y
== m_Selection
.m_CursorB
.y
)
2958 MoveCursorTo(m_Selection
.m_CursorA
);
2959 Delete(m_Selection
.m_CursorB
.x
- m_Selection
.m_CursorA
.x
);
2963 // We now know that the two lines are different:
2966 * firstLine
= GetLine(m_Selection
.m_CursorA
.y
),
2967 * lastLine
= GetLine(m_Selection
.m_CursorB
.y
);
2969 // be a bit paranoid:
2970 if(! firstLine
|| ! lastLine
)
2973 // First, delete what's left of this line:
2974 MoveCursorTo(m_Selection
.m_CursorA
);
2975 DeleteToEndOfLine();
2977 wxLayoutLine
*prevLine
= firstLine
->GetPreviousLine(),
2978 *nextLine
= firstLine
->GetNextLine();
2980 while(nextLine
&& nextLine
!= lastLine
)
2982 nextLine
= nextLine
->DeleteLine(false, this);
2985 // Now nextLine = lastLine;
2986 Delete(1); // This joins firstLine and nextLine
2987 Delete(m_Selection
.m_CursorB
.x
); // This deletes the first x positions
2989 // Recalculate the line positions and numbers but notice that firstLine
2990 // might not exist any more - it could be deleted by Delete(1) above
2991 wxLayoutLine
*firstLine2
= prevLine
? prevLine
->GetNextLine() : m_FirstLine
;
2992 firstLine2
->MarkDirty();
2995 /// Starts highlighting the selection
2997 wxLayoutList::StartHighlighting(wxDC
&dc
)
3000 dc
.SetTextForeground(m_CurrentStyleInfo
.m_bg
);
3001 dc
.SetTextBackground(m_CurrentStyleInfo
.m_fg
);
3002 dc
.SetBackgroundMode(wxSOLID
);
3006 /// Ends highlighting the selection
3008 wxLayoutList::EndHighlighting(wxDC
&dc
)
3011 dc
.SetTextForeground(m_CurrentStyleInfo
.m_fg
);
3012 dc
.SetTextBackground(m_CurrentStyleInfo
.m_bg
);
3013 dc
.SetBackgroundMode(wxTRANSPARENT
);
3019 wxLayoutList::GetLine(CoordType index
) const
3021 wxASSERT_MSG( (0 <= index
) && (index
< (CoordType
)m_numLines
),
3022 wxT("invalid index") );
3025 CoordType n
= index
;
3027 CoordType lineNo
= 0;
3030 for ( line
= m_FirstLine
; line
&& n
-- > 0; line
= line
->GetNextLine() )
3033 wxASSERT(line
->GetLineNumber() == lineNo
);
3040 // should be the right one
3041 wxASSERT( line
->GetLineNumber() == index
);
3049 wxLayoutList::Copy(const wxPoint
&from
,
3056 for(firstLine
= m_FirstLine
;
3057 firstLine
&& firstLine
->GetLineNumber() < from
.y
;
3058 firstLine
=firstLine
->GetNextLine())
3061 if(!firstLine
|| firstLine
->GetLineNumber() != from
.y
)
3064 for(lastLine
= m_FirstLine
;
3065 lastLine
&& lastLine
->GetLineNumber() < to
.y
;
3066 lastLine
=lastLine
->GetNextLine())
3069 if(!lastLine
|| lastLine
->GetLineNumber() != to
.y
)
3074 wxLayoutLine
*tmp
= firstLine
;
3075 firstLine
= lastLine
;
3079 wxLayoutList
*llist
= new wxLayoutList();
3081 if(firstLine
== lastLine
)
3083 firstLine
->Copy(llist
, from
.x
, to
.x
);
3087 // Extract objects from first line
3088 firstLine
->Copy(llist
, from
.x
);
3090 // Extract all lines between
3091 for ( wxLayoutLine
*line
= firstLine
->GetNextLine();
3093 line
= line
->GetNextLine() )
3099 // Extract objects from last line
3100 lastLine
->Copy(llist
, 0, to
.x
);
3107 wxLayoutList::GetSelection(wxLayoutDataObject
*wxlo
, bool invalidate
)
3109 if(! m_Selection
.m_valid
)
3111 if(m_Selection
.m_selecting
)
3117 if(invalidate
) m_Selection
.m_valid
= false;
3119 wxLayoutList
*llist
= Copy( m_Selection
.m_CursorA
,
3120 m_Selection
.m_CursorB
);
3122 if(llist
&& wxlo
) // export as data object, too
3126 wxLayoutExportObject
*exp
;
3127 wxLayoutExportStatus
status(llist
);
3128 while((exp
= wxLayoutExport( &status
, WXLO_EXPORT_AS_OBJECTS
)) != NULL
)
3130 if(exp
->type
== WXLO_EXPORT_EMPTYLINE
)
3131 string
<< (int) WXLO_TYPE_LINEBREAK
<< '\n';
3133 exp
->content
.object
->Write(string
);
3137 wxlo
->SetLayoutData(string
);
3145 #define COPY_SI(what) if(si.what != -1) { m_CurrentStyleInfo.what = si.what; fontChanged = true; }
3148 wxLayoutList::ApplyStyle(wxLayoutStyleInfo
const &si
, wxDC
&dc
)
3150 bool fontChanged
= false;
3157 dc
.SetFont( m_FontCache
.GetFont(m_CurrentStyleInfo
) );
3161 m_CurrentStyleInfo
.m_fg
= si
.m_fg
;
3162 m_CurrentStyleInfo
.m_fg_valid
= true;
3163 dc
.SetTextForeground(m_CurrentStyleInfo
.m_fg
);
3168 m_CurrentStyleInfo
.m_bg
= si
.m_bg
;
3169 m_CurrentStyleInfo
.m_bg_valid
= true;
3170 dc
.SetTextBackground(m_CurrentStyleInfo
.m_bg
);
3175 #ifdef WXLAYOUT_DEBUG
3178 wxLayoutList::Debug()
3180 WXLO_DEBUG((wxT("Cursor is in line %d, screen pos = (%d, %d)"),
3181 (int)m_CursorLine
->GetLineNumber(),
3182 m_CursorScreenPos
.x
, m_CursorScreenPos
.y
));
3185 for(line
= m_FirstLine
; line
; line
= line
->GetNextLine())
3194 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3198 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
3200 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
3201 wxString
const & title
)
3206 // remove any highlighting which could interfere with printing:
3207 m_llist
->StartSelection();
3208 m_llist
->EndSelection();
3209 // force a full layout of the list:
3210 m_llist
->ForceTotalLayout();
3211 // layout is called in ScaleDC() when we have a DC
3215 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
3217 // The following bit is taken from the printing sample, let's see
3218 // whether it works for us.
3220 /* You might use THIS code to set the printer DC to ROUGHLY reflect
3221 * the screen text size. This page also draws lines of actual length 5cm
3225 // Get the logical pixels per inch of screen and printer
3226 int ppiScreenX
, ppiScreenY
;
3227 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
3228 int ppiPrinterX
, ppiPrinterY
;
3229 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
3231 if(ppiScreenX
== 0) // not yet set, need to guess
3236 wxUnusedVar(ppiScreenY
);
3238 if(ppiPrinterX
== 0) // not yet set, need to guess
3243 wxUnusedVar(ppiPrinterY
);
3245 // This scales the DC so that the printout roughly represents the
3246 // the screen scaling. The text point size _should_ be the right size
3247 // but in fact is too small for some reason. This is a detail that will
3248 // need to be addressed at some point but can be fudged for the
3250 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
3252 // Now we have to check in case our real page size is reduced
3253 // (e.g. because we're drawing to a print preview memory DC)
3254 int pageWidth
, pageHeight
;
3256 dc
->GetSize(&w
, &h
);
3257 GetPageSizePixels(&pageWidth
, &pageHeight
);
3258 wxUnusedVar(pageHeight
);
3259 if(pageWidth
!= 0) // doesn't work always
3261 // If printer pageWidth == current DC width, then this doesn't
3262 // change. But w might be the preview bitmap width, so scale down.
3263 scale
= scale
* (float)(w
/(float)pageWidth
);
3266 dc
->SetUserScale(scale
, scale
);
3270 bool wxLayoutPrintout::OnPrintPage(int page
)
3279 top
= (page
- 1)*m_PrintoutHeight
;
3280 bottom
= top
+ m_PrintoutHeight
;
3282 WXLO_DEBUG((wxT("OnPrintPage(%d) printing from %d to %d"), page
, top
,
3285 // SetDeviceOrigin() doesn't work here, so we need to manually
3286 // translate all coordinates.
3287 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
3288 m_llist
->Draw(*dc
, translate
, top
, bottom
, true /* clip strictly */);
3297 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
3299 /* We allocate a temporary wxDC for printing, so that we can
3300 determine the correct paper size and scaling. We don't actually
3301 print anything on it. */
3302 #if defined(__WXMSW__) || defined(__WXMAC__)
3303 wxPrinterDC
*psdc
= new wxPrinterDC(wxEmptyString
,wxEmptyString
,_T(WXLLIST_TEMPFILE
),false);
3306 data
.SetFilename(WXLLIST_TEMPFILE
);
3307 wxPostScriptDC
*psdc
= new wxPostScriptDC(data
);
3310 psdc
->StartDoc(m_title
);
3311 // before we draw anything, me must make sure the list is properly
3313 m_llist
->Layout(*psdc
);
3315 float scale
= ScaleDC(psdc
);
3317 psdc
->GetSize(&m_PageWidth
, &m_PageHeight
);
3319 // This sets a left/top origin of 15% and 5%:
3320 m_Offset
= wxPoint((15*m_PageWidth
)/100, (5*m_PageHeight
)/100);
3322 // This is the length of the printable area.
3323 m_PrintoutHeight
= m_PageHeight
- 2*m_Offset
.y
;
3324 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
3327 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
3330 *maxPage
= m_NumOfPages
;
3333 *selPageTo
= m_NumOfPages
;
3336 wxRemoveFile(_T(WXLLIST_TEMPFILE
));
3339 bool wxLayoutPrintout::HasPage(int pageNum
)
3341 return pageNum
<= m_NumOfPages
;
3345 Stupid wxWidgets doesn't draw proper ellipses, so we comment this
3346 out. It's a waste of paper anyway.
3350 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
3351 wxPoint topleft
, wxPoint bottomright
,
3354 // make backups of all essential parameters
3355 const wxBrush
& brush
= dc
.GetBrush();
3356 const wxPen
& pen
= dc
.GetPen();
3357 const wxFont
& font
= dc
.GetFont();
3359 dc
.SetBrush(*wxWHITE_BRUSH
);
3360 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
3361 dc
.DrawRoundedRectangle(topleft
.x
,
3362 topleft
.y
,bottomright
.x
-topleft
.x
,
3363 bottomright
.y
-topleft
.y
);
3364 dc
.SetBrush(*wxBLACK_BRUSH
);
3365 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
3366 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
3370 page
= "9999/9999 "; // many pages...
3372 dc
.GetTextExtent(page
,&w
,&h
);
3373 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
3374 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
3375 dc
.GetTextExtent("XXXX", &w
,&h
);
3376 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);
3387 wxFontCache::GetFont(int family
, int size
, int style
, int weight
,
3390 for(wxFCEList::iterator i
= m_FontList
.begin();
3391 i
!= m_FontList
.end(); i
++)
3392 if( (**i
).Matches(family
, size
, style
, weight
, underline
) )
3393 return (**i
).GetFont();
3395 wxFontCacheEntry
*fce
= new wxFontCacheEntry(family
, size
, style
,
3397 m_FontList
.push_back(fce
);
3398 return fce
->GetFont();