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
25 # pragma implementation "wxllist.h"
28 #include "wx/wxprec.h"
38 # include "gui/wxllist.h"
39 # include "gui/wxlparser.h"
40 # define SHOW_SELECTIONS 1
43 # include "wxlparser.h"
44 # define SHOW_SELECTIONS 1
56 # include "wx/print.h"
58 # include "wx/filefn.h"
61 #ifdef WXLAYOUT_USE_CARET
62 # include "wx/caret.h"
63 #endif // WXLAYOUT_USE_CARET
68 /// This should never really get created
69 #define WXLLIST_TEMPFILE "__wxllist.tmp"
73 # define TypeString(t) g_aTypeStrings[t]
74 # define WXLO_DEBUG(x) wxLogDebug x
76 static const wxChar
*g_aTypeStrings
[] =
78 _T("invalid"), _T("text"), _T("cmd"), _T("icon")
81 wxLayoutObject::DebugDump() const
84 str
.Printf(wxT("%s"), g_aTypeStrings
[GetType()]);
88 # define TypeString(t) ""
89 # define WXLO_DEBUG(x)
93 // FIXME under MSW, this constant is needed to make the thing properly redraw
94 // itself - I don't know where the size calculation error is and I can't
95 // waste time looking for it right now. Search for occurences of
96 // MSW_CORRECTION to find all the places where I did it.
98 static const int MSW_CORRECTION
= 10;
100 static const int MSW_CORRECTION
= 0;
103 /// Cursors smaller than this disappear in XOR drawing mode
104 #define WXLO_MINIMUM_CURSOR_WIDTH 4
106 /// Use this character to estimate a cursor size when none is available.
107 #define WXLO_CURSORCHAR "E"
108 /** @name Helper functions */
110 /// allows me to compare to wxPoints
111 bool operator <=(wxPoint
const &p1
, wxPoint
const &p2
)
113 return p1
.y
< p2
.y
|| (p1
.y
== p2
.y
&& p1
.x
<= p2
.x
);
117 The following STAY HERE until we have a working wxGTK again!!!
119 #ifndef wxWANTS_CHARS
120 /// allows me to compare to wxPoints
121 bool operator ==(wxPoint
const &p1
, wxPoint
const &p2
)
123 return p1
.x
== p2
.x
&& p1
.y
== p2
.y
;
126 /// allows me to compare to wxPoints
127 bool operator !=(wxPoint
const &p1
, wxPoint
const &p2
)
129 return p1
.x
!= p2
.x
|| p1
.y
!= p2
.y
;
132 wxPoint
& operator += (wxPoint
&p1
, wxPoint
const &p2
)
140 /// allows me to compare to wxPoints
141 bool operator>(wxPoint
const &p1
, wxPoint
const &p2
)
146 /// grows a wxRect so that it includes the given point
149 void GrowRect(wxRect
&r
, CoordType x
, CoordType y
)
153 else if(r
.x
+ r
.width
< x
)
158 else if(r
.y
+ r
.height
< y
)
164 /// returns true if the point is in the rectangle
166 bool Contains(const wxRect
&r
, const wxPoint
&p
)
168 return r
.x
<= p
.x
&& r
.y
<= p
.y
&& (r
.x
+r
.width
) >= p
.x
&& (r
.y
+ r
.height
) >= p
.y
;
177 void ReadString(wxString
&to
, wxString
&from
)
180 const wxChar
*cptr
= from
.c_str();
181 while(*cptr
&& *cptr
!= wxT('\n'))
192 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
196 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
200 wxLayoutObject::Read(wxString
&istr
)
203 ReadString(tmp
, istr
);
204 long l
= WXLO_TYPE_INVALID
;
211 return wxLayoutObjectText::Read(istr
);
213 return wxLayoutObjectCmd::Read(istr
);
215 return wxLayoutObjectIcon::Read(istr
);
221 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
225 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
227 wxLayoutObjectText::wxLayoutObjectText(const wxString
&txt
)
237 wxLayoutObjectText::Copy()
239 wxLayoutObjectText
*obj
= new wxLayoutObjectText(m_Text
);
240 obj
->m_Width
= m_Width
;
241 obj
->m_Height
= m_Height
;
243 obj
->m_Bottom
= m_Bottom
;
244 obj
->SetUserData(m_UserData
);
250 wxLayoutObjectText::Write(wxString
&ostr
)
252 ostr
<< (int) WXLO_TYPE_TEXT
<< '\n'
257 wxLayoutObjectText::Read(wxString
&istr
)
260 ReadString(text
, istr
);
262 return new wxLayoutObjectText(text
);
266 wxLayoutObjectText::GetSize(CoordType
*top
, CoordType
*bottom
) const
269 *top
= m_Top
; *bottom
= m_Bottom
;
270 return wxPoint(m_Width
, m_Height
);
274 wxLayoutObjectText::Draw(wxDC
&dc
, wxPoint
const &coords
,
275 wxLayoutList
*wxllist
,
276 CoordType begin
, CoordType end
)
280 // draw the whole object normally
281 dc
.DrawText(m_Text
, coords
.x
, coords
.y
-m_Top
);
285 // highlight the bit between begin and len
288 ypos
= coords
.y
-m_Top
;
289 long width
, height
, descent
;
291 if(begin
< 0) begin
= 0;
292 if( end
> (signed)m_Text
.Length() )
293 end
= m_Text
.Length();
295 wxString str
= m_Text
.Mid(0, begin
);
296 dc
.DrawText(str
, xpos
, ypos
);
297 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
299 wxllist
->StartHighlighting(dc
);
300 str
= m_Text
.Mid(begin
, end
-begin
);
301 dc
.DrawText(str
, xpos
, ypos
);
302 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
304 wxllist
->EndHighlighting(dc
);
305 str
= m_Text
.Mid(end
, m_Text
.Length()-end
);
306 dc
.DrawText(str
, xpos
, ypos
);
311 wxLayoutObjectText::GetOffsetScreen(wxDC
&dc
, CoordType xpos
) const
315 maxlen
= m_Text
.Length();
318 height
, descent
= 0l;
320 if(xpos
== 0) return 0; // easy
322 while(width
< xpos
&& offs
< maxlen
)
324 dc
.GetTextExtent(m_Text
.substr(0,offs
),
325 &width
, &height
, &descent
);
328 /* We have to substract 1 to compensate for the offs++, and another
329 one because we don't want to position the cursor behind the
330 object what we clicked on, but before - otherwise it looks
332 return (xpos
> 2) ? offs
-2 : 0;
336 wxLayoutObjectText::Layout(wxDC
&dc
, class wxLayoutList
*WXUNUSED(llist
))
340 // now this is done in wxLayoutLine::Layout(), but this code might be
341 // reenabled later - in principle, it's more efficient
343 CoordType widthOld
= m_Width
,
344 heightOld
= m_Height
;
348 CoordType a
,b
,c
,d
,e
,f
;
349 dc
.GetTextExtent(_T("test "), &a
, &b
, &c
);
350 dc
.GetTextExtent(_T("test"), &d
, &e
, &f
);
354 dc
.GetTextExtent(_T(" "), &d
, &e
, &f
);
357 dc
.GetTextExtent(m_Text
, &m_Width
, &m_Height
, &descent
);
360 if ( widthOld
!= m_Width
|| heightOld
!= m_Height
)
362 // as the text length changed, it must be refreshed
363 wxLayoutLine
*line
= GetLine();
365 wxCHECK_RET( line
, "wxLayoutObjectText can't refresh itself" );
367 // as our size changed, we need to repaint the part which was appended
368 wxPoint
position(line
->GetPosition());
370 // this is not the most efficient way (we repaint the whole line), but
371 // it's not too slow and is *simple*
372 if ( widthOld
< m_Width
)
374 if ( heightOld
< m_Height
)
375 heightOld
= m_Height
;
377 llist
->SetUpdateRect(position
.x
+ widthOld
+ MSW_CORRECTION
,
378 position
.y
+ heightOld
+ MSW_CORRECTION
);
383 m_Top
= m_Height
- m_Bottom
;
387 #ifdef WXLAYOUT_DEBUG
389 wxLayoutObjectText::DebugDump() const
392 str
= wxLayoutObject::DebugDump();
394 str2
.Printf(wxT(" `%s`"), m_Text
.c_str());
399 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
403 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
405 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
const &icon
)
409 wxFAIL_MSG(wxT("invalid icon"));
417 // FIXME ugly, ugly, ugly - but the only way to avoid slicing
418 m_Icon
= icon
.GetHBITMAP() ? new wxBitmap(icon
)
419 : new wxBitmap(wxBitmap((const wxBitmap
&)icon
));
421 m_Icon
= new wxBitmap(icon
);
427 wxLayoutObjectIcon::Write(wxString
&ostr
)
429 /* Exports icon through a temporary file. */
431 wxString file
= wxGetTempFileName(_T("wxloexport"));
433 ostr
<< (int) WXLO_TYPE_ICON
<< '\n'
435 m_Icon
->SaveFile(file
, WXLO_BITMAP_FORMAT
);
439 wxLayoutObjectIcon::Read(wxString
&istr
)
442 ReadString(file
, istr
);
444 if(! wxFileExists(file
))
446 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon
;
448 if(!obj
->m_Icon
->LoadFile(file
, WXLO_BITMAP_FORMAT
))
458 wxLayoutObjectIcon::Copy()
460 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon(new
462 obj
->SetUserData(m_UserData
);
466 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
*icon
)
470 m_Icon
= new wxBitmap
;
474 wxLayoutObjectIcon::Draw(wxDC
&dc
, wxPoint
const &coords
,
475 wxLayoutList
*WXUNUSED(wxllist
),
476 CoordType
WXUNUSED(begin
), CoordType
WXUNUSED(len
) )
478 dc
.DrawBitmap(*m_Icon
, coords
.x
, coords
.y
-m_Icon
->GetHeight(),
479 (m_Icon
->GetMask() == NULL
) ? false : true);
483 wxLayoutObjectIcon::Layout(wxDC
& /* dc */, class wxLayoutList
* )
488 wxLayoutObjectIcon::GetSize(CoordType
*top
, CoordType
*bottom
) const
490 *top
= m_Icon
->GetHeight();
492 return wxPoint(m_Icon
->GetWidth(), m_Icon
->GetHeight());
497 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
501 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
504 wxLayoutStyleInfo::wxLayoutStyleInfo(int ifamily
,
516 underline
= iul
!= 0;
518 m_fg_valid
= fg
!= 0;
519 m_bg_valid
= bg
!= 0;
520 m_fg
= m_fg_valid
? *fg
: *wxBLACK
;
521 m_bg
= m_bg_valid
? *bg
: *wxWHITE
;
524 #define COPY_SI_(what) if(right.what != -1) what = right.what;
527 wxLayoutStyleInfo::operator=(const wxLayoutStyleInfo
&right
)
534 if(right
.m_fg_valid
) m_fg
= right
.m_fg
;
535 if(right
.m_bg_valid
) m_bg
= right
.m_bg
;
539 wxLayoutObjectCmd::wxLayoutObjectCmd(int family
, int size
, int style
, int
540 weight
, int underline
,
541 wxColour
*fg
, wxColour
*bg
)
544 m_StyleInfo
= new wxLayoutStyleInfo(family
, size
,style
,weight
,underline
,fg
,bg
);
547 wxLayoutObjectCmd::wxLayoutObjectCmd(const wxLayoutStyleInfo
&si
)
550 m_StyleInfo
= new wxLayoutStyleInfo
;
555 wxLayoutObjectCmd::Copy()
557 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd(
562 m_StyleInfo
->underline
,
563 m_StyleInfo
->m_fg_valid
?
564 &m_StyleInfo
->m_fg
: NULL
,
565 m_StyleInfo
->m_bg_valid
?
566 &m_StyleInfo
->m_bg
: NULL
);
567 obj
->SetUserData(m_UserData
);
572 wxLayoutObjectCmd::Write(wxString
&ostr
)
574 ostr
<< (int) WXLO_TYPE_CMD
<< '\n'
575 << (int) m_StyleInfo
->family
<< '\n'
576 << (int) m_StyleInfo
->size
<< '\n'
577 << (int) m_StyleInfo
->style
<< '\n'
578 << (int) m_StyleInfo
->weight
<< '\n'
579 << (int) m_StyleInfo
->underline
<< '\n'
580 << (int) m_StyleInfo
->m_fg_valid
<< '\n'
581 << (int) m_StyleInfo
->m_bg_valid
<< '\n';
582 if(m_StyleInfo
->m_fg_valid
)
584 ostr
<< (int) m_StyleInfo
->m_fg
.Red() << '\n'
585 << (int) m_StyleInfo
->m_fg
.Green() << '\n'
586 << (int) m_StyleInfo
->m_fg
.Blue() << '\n';
588 if(m_StyleInfo
->m_bg_valid
)
590 ostr
<< (int) m_StyleInfo
->m_bg
.Red() << '\n'
591 << (int) m_StyleInfo
->m_bg
.Green() << '\n'
592 << (int) m_StyleInfo
->m_bg
.Blue() << '\n';
597 wxLayoutObjectCmd::Read(wxString
&istr
)
599 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd
;
603 ReadString(tmp
, istr
);
605 obj
->m_StyleInfo
->family
= (int) l
;
608 ReadString(tmp
, istr
);
610 obj
->m_StyleInfo
->size
= (int) l
;
612 ReadString(tmp
, istr
);
614 obj
->m_StyleInfo
->style
= (int) l
;
616 ReadString(tmp
, istr
);
618 obj
->m_StyleInfo
->weight
= (int) l
;
620 ReadString(tmp
, istr
);
622 obj
->m_StyleInfo
->underline
= (int) l
;
624 ReadString(tmp
, istr
);
626 obj
->m_StyleInfo
->m_fg_valid
= (int) l
;
628 ReadString(tmp
, istr
);
630 obj
->m_StyleInfo
->m_bg_valid
= (int) l
;
632 if(obj
->m_StyleInfo
->m_fg_valid
)
634 unsigned char red
, green
, blue
;
635 ReadString(tmp
, istr
);
637 red
= (unsigned char) l
;
639 ReadString(tmp
, istr
);
641 green
= (unsigned char) l
;
643 ReadString(tmp
, istr
);
645 blue
= (unsigned char) l
;
647 obj
->m_StyleInfo
->m_fg
= wxColour(red
, green
, blue
);
650 if(obj
->m_StyleInfo
->m_bg_valid
)
652 unsigned char red
, green
, blue
;
653 ReadString(tmp
, istr
);
655 red
= (unsigned char) l
;
657 ReadString(tmp
, istr
);
659 green
= (unsigned char) l
;
661 ReadString(tmp
, istr
);
663 blue
= (unsigned char) l
;
665 obj
->m_StyleInfo
->m_bg
= wxColour(red
, green
, blue
);
672 wxLayoutObjectCmd::~wxLayoutObjectCmd()
678 wxLayoutObjectCmd::GetStyle() const
684 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint
const & WXUNUSED(coords
),
685 wxLayoutList
*wxllist
,
686 CoordType
WXUNUSED(begin
), CoordType
WXUNUSED(len
))
688 wxASSERT(m_StyleInfo
);
689 wxllist
->ApplyStyle(*m_StyleInfo
, dc
);
693 wxLayoutObjectCmd::Layout(wxDC
&dc
, class wxLayoutList
* llist
)
695 // this get called, so that recalculation uses right font sizes
696 Draw(dc
, wxPoint(0,0), llist
);
700 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
702 The wxLayoutLine object
704 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
706 wxLayoutLine::wxLayoutLine(wxLayoutLine
*prev
, wxLayoutList
*llist
)
708 m_Width
= m_Height
= 0;
717 RecalculatePosition(llist
);
722 m_LineNumber
= m_Previous
->GetLineNumber() + 1;
723 m_Next
= m_Previous
->GetNextLine();
724 m_Previous
->m_Next
= this;
729 m_Next
->m_Previous
= this;
733 m_StyleInfo
= llist
->GetDefaultStyleInfo();
735 llist
->IncNumLines();
738 wxLayoutLine::~wxLayoutLine()
740 // kbList cleans itself
744 wxLayoutLine::RecalculatePosition(wxLayoutList
*llist
)
746 wxASSERT(m_Previous
|| GetLineNumber() == 0);
748 wxPoint
posOld(m_Position
);
752 m_Position
= m_Previous
->GetPosition();
753 m_Position
.y
+= m_Previous
->GetHeight();
756 m_Position
= wxPoint(0,0);
758 if ( m_Position
!= posOld
)
760 // the whole line moved and must be repainted
761 llist
->SetUpdateRect(m_Position
);
762 llist
->SetUpdateRect(m_Position
.x
+ GetWidth() + MSW_CORRECTION
,
763 m_Position
.y
+ GetHeight() + MSW_CORRECTION
);
764 llist
->SetUpdateRect(posOld
);
765 llist
->SetUpdateRect(posOld
.x
+ GetWidth() + MSW_CORRECTION
,
766 posOld
.y
+ GetHeight() + MSW_CORRECTION
);
773 wxLayoutObjectList::iterator
774 wxLayoutLine::FindObject(CoordType xpos
, CoordType
*offset
) const
778 wxLayoutObjectList::iterator
781 CoordType x
= 0, len
;
783 /* We search through the objects. As we don't like returning the
784 object that the cursor is behind, we just remember such an
785 object in "found" so we can return it if there is really no
786 further object following it. */
787 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
789 len
= (**i
).GetLength();
790 if( x
<= xpos
&& xpos
<= x
+ len
)
793 if(xpos
== x
+ len
) // is there another object behind?
795 else // we are really inside this object
798 x
+= (**i
).GetLength();
800 return found
; // ==NULL if really none found
803 wxLayoutObjectList::iterator
804 wxLayoutLine::FindObjectScreen(wxDC
&dc
, wxLayoutList
*llist
,
805 CoordType xpos
, CoordType
*cxpos
,
810 llist
->ApplyStyle(GetStyleInfo(), dc
);
812 wxLayoutObjectList::iterator i
;
813 CoordType x
= 0, cx
= 0, width
;
815 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
817 wxLayoutObject
*obj
= *i
;
818 if ( obj
->GetType() == WXLO_TYPE_CMD
)
820 // this will set the correct font for the objects which follow
821 obj
->Layout(dc
, llist
);
824 width
= obj
->GetWidth();
825 if( x
<= xpos
&& xpos
<= x
+ width
)
827 *cxpos
= cx
+ obj
->GetOffsetScreen(dc
, xpos
-x
);
834 x
+= obj
->GetWidth();
835 cx
+= obj
->GetLength();
838 // behind last object:
843 return m_ObjectList
.tail();
846 /** Finds text in this line.
847 @param needle the text to find
848 @param xpos the position where to start the search
849 @return the cursoor coord where it was found or -1
852 wxLayoutLine::FindText(const wxString
&needle
, CoordType xpos
) const
855 wxString
const *text
;
857 for(wxLOiterator i
= m_ObjectList
.begin(); i
!= m_ObjectList
.end(); i
++)
859 if(cpos
>= xpos
) // search from here!
861 if((**i
).GetType() == WXLO_TYPE_TEXT
)
863 text
= & ((wxLayoutObjectText
*)(*i
))->GetText();
864 int relpos
= text
->Find(needle
);
865 if(relpos
>= cpos
-xpos
) // -1 if not found
870 cpos
+= (**i
).GetLength();
873 return -1; // not found
877 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
880 wxASSERT(obj
!= NULL
);
885 wxLOiterator i
= FindObject(xpos
, &offset
);
888 if(xpos
== 0 ) // aha, empty line!
890 m_ObjectList
.push_back(obj
);
891 m_Length
+= obj
->GetLength();
898 CoordType len
= (**i
).GetLength();
899 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
900 { // insert before this object
901 m_ObjectList
.insert(i
,obj
);
902 m_Length
+= obj
->GetLength();
907 if( i
== m_ObjectList
.tail()) // last object?
908 m_ObjectList
.push_back(obj
);
910 { // insert after current object
912 m_ObjectList
.insert(i
,obj
);
914 m_Length
+= obj
->GetLength();
917 /* Otherwise we need to split the current object.
918 Fortunately this can only be a text object. */
919 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
920 wxString left
, right
;
921 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
922 left
= tobj
->GetText().substr(0,offset
);
923 right
= tobj
->GetText().substr(offset
,len
-offset
);
924 // current text object gets set to right half
925 tobj
->GetText() = right
; // set new text
926 // before it we insert the new object
927 m_ObjectList
.insert(i
,obj
);
928 m_Length
+= obj
->GetLength();
929 // and before that we insert the left half
930 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
935 wxLayoutLine::Insert(CoordType xpos
, const wxString
& text
)
942 wxLOiterator i
= FindObject(xpos
, &offset
);
943 if(i
!= NULLIT
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
945 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
946 tobj
->GetText().insert(offset
, text
);
947 m_Length
+= text
.Length();
951 if ( !Insert(xpos
, new wxLayoutObjectText(text
)) )
959 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
961 CoordType offset
, len
;
966 wxLOiterator i
= FindObject(xpos
, &offset
);
969 if(i
== NULLIT
) return npos
;
970 // now delete from that object:
971 if((**i
).GetType() != WXLO_TYPE_TEXT
)
973 if(offset
!= 0) // at end of line after a non-text object
976 len
= (**i
).GetLength();
979 m_ObjectList
.erase(i
);
983 // tidy up: remove empty text objects
984 if((**i
).GetLength() == 0)
986 m_ObjectList
.erase(i
);
990 CoordType max
= (**i
).GetLength() - offset
;
991 if(npos
< max
) max
= npos
;
994 if(xpos
== GetLength())
997 { // at the end of an object
998 // move to begin of next object:
1000 continue; // start over
1005 if(offset
== 0 && max
== (**i
).GetLength())
1006 m_ObjectList
.erase(i
); // remove the whole object
1008 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
1016 wxLayoutLine::DeleteWord(CoordType xpos
)
1018 wxASSERT(xpos
>= 0);
1022 wxLOiterator i
= FindObject(xpos
, &offset
);
1026 if(i
== NULLIT
) return false;
1027 if((**i
).GetType() != WXLO_TYPE_TEXT
)
1029 // This should only happen when at end of line, behind a non-text
1031 if(offset
== (**i
).GetLength()) return false;
1032 m_Length
-= (**i
).GetLength(); // -1
1033 m_ObjectList
.erase(i
);
1034 return true; // we are done
1038 if(offset
== (**i
).GetLength()) // at end of object
1044 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
1046 wxString str
= tobj
->GetText();
1047 str
= str
.substr(offset
,str
.Length()-offset
);
1048 // Find out how many positions we need to delete:
1049 // 1. eat leading space
1050 while(isspace(str
.c_str()[count
])) count
++;
1051 // 2. eat the word itself:
1052 while(isalnum(str
.c_str()[count
])) count
++;
1054 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
1055 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
1063 wxFAIL_MSG(wxT("unreachable"));
1068 wxLayoutLine::DeleteLine(bool update
, wxLayoutList
*llist
)
1070 // maintain linked list integrity
1072 m_Next
->m_Previous
= m_Previous
;
1074 m_Previous
->m_Next
= m_Next
;
1076 // get the line numbers right again
1077 if ( update
&& m_Next
)
1082 // we can't use m_Next after "delete this", so we must save this pointer
1084 wxLayoutLine
*next
= m_Next
;
1087 llist
->DecNumLines();
1093 wxLayoutLine::Draw(wxDC
&dc
,
1094 wxLayoutList
*llist
,
1095 const wxPoint
& offset
) const
1097 wxLayoutObjectList::iterator i
;
1098 wxPoint pos
= offset
;
1099 pos
= pos
+ GetPosition();
1101 pos
.y
+= m_BaseLine
;
1103 CoordType xpos
= 0; // cursorpos, lenght of line
1107 int highlight
= llist
->IsSelected(this, &from
, &to
);
1108 // WXLO_DEBUG(("highlight=%d", highlight ));
1109 if(highlight
== 1) // we need to draw the whole line inverted!
1110 llist
->StartHighlighting(dc
);
1112 llist
->EndHighlighting(dc
);
1114 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
1116 if(highlight
== -1) // partially highlight line
1118 // parts of the line need highlighting
1120 // Next line commented, code has no effect
1121 // xpos+(**i).GetLength();
1122 (**i
).Draw(dc
, pos
, llist
, from
-xpos
, to
-xpos
);
1125 (**i
).Draw(dc
, pos
, llist
);
1126 pos
.x
+= (**i
).GetWidth();
1127 xpos
+= (**i
).GetLength();
1132 This function does all the recalculation, that is, it should only be
1133 called from within wxLayoutList::Layout(), as it uses the current
1134 list's styleinfo and updates it.
1137 wxLayoutLine::Layout(wxDC
&dc
,
1138 wxLayoutList
*llist
,
1140 wxPoint
*cursorSize
,
1141 wxLayoutStyleInfo
*cursorStyle
,
1143 bool WXUNUSED(suppressSIupdate
))
1145 wxLayoutObjectList::iterator i
;
1147 // when a line becomes dirty, we redraw it from the place where it was
1148 // changed till the end of line (because the following wxLayoutObjects are
1149 // moved when the preceding one changes) - calculate the update rectangle.
1150 CoordType updateTop
= m_Position
.y
,
1152 updateWidth
= m_Width
,
1153 updateHeight
= m_Height
;
1157 bottomHeight
= 0; // above and below baseline
1159 objTopHeight
, objBottomHeight
; // above and below baseline
1163 CoordType heightOld
= m_Height
;
1169 bool cursorFound
= false;
1171 RecalculatePosition(llist
);
1175 *cursorPos
= m_Position
;
1176 if(cursorSize
) *cursorSize
= wxPoint(0,0);
1179 m_StyleInfo
= llist
->GetStyleInfo(); // save current style
1180 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
1182 wxLayoutObject
*obj
= *i
;
1183 obj
->Layout(dc
, llist
);
1184 wxPoint sizeObj
= obj
->GetSize(&objTopHeight
, &objBottomHeight
);
1186 if(cursorPos
&& ! cursorFound
)
1188 // we need to check whether the text cursor is here
1189 len
= obj
->GetLength();
1190 if(count
<= cx
&& count
+len
> cx
)
1192 if(obj
->GetType() == WXLO_TYPE_TEXT
)
1194 len
= cx
- count
; // pos in object
1195 CoordType width
, height
, descent
;
1196 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
1197 &width
, &height
, &descent
);
1198 cursorPos
->x
+= width
;
1199 cursorPos
->y
= m_Position
.y
;
1201 if(len
< obj
->GetLength())
1202 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
1204 str
= _T(WXLO_CURSORCHAR
);
1205 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
1207 if(cursorStyle
) // set style info
1208 *cursorStyle
= llist
->GetStyleInfo();
1211 // Just in case some joker inserted an empty string object:
1213 width
= WXLO_MINIMUM_CURSOR_WIDTH
;
1216 cursorSize
->x
= width
;
1217 cursorSize
->y
= height
;
1220 cursorFound
= true; // no more checks
1224 // on some other object
1225 CoordType top
, bottom
; // unused
1227 *cursorSize
= obj
->GetSize(&top
,&bottom
);
1228 cursorPos
->y
= m_Position
.y
;
1229 cursorFound
= true; // no more checks
1235 cursorPos
->x
+= obj
->GetWidth();
1239 m_Width
+= sizeObj
.x
;
1240 if(sizeObj
.y
> m_Height
)
1242 m_Height
= sizeObj
.y
;
1245 if(objTopHeight
> topHeight
)
1246 topHeight
= objTopHeight
;
1247 if(objBottomHeight
> bottomHeight
)
1248 bottomHeight
= objBottomHeight
;
1253 if ( updateHeight
< m_Height
)
1254 updateHeight
= m_Height
;
1255 if ( updateWidth
< m_Width
)
1256 updateWidth
= m_Width
;
1258 // update all line if we don't know where to start from
1259 if ( updateLeft
== -1 )
1262 llist
->SetUpdateRect(updateLeft
, updateTop
);
1263 llist
->SetUpdateRect(updateLeft
+ updateWidth
+ MSW_CORRECTION
,
1264 updateTop
+ updateHeight
+ MSW_CORRECTION
);
1267 if(topHeight
+ bottomHeight
> m_Height
)
1269 m_Height
= topHeight
+bottomHeight
;
1272 m_BaseLine
= topHeight
;
1276 CoordType width
, height
, descent
;
1277 dc
.GetTextExtent(_T(WXLO_CURSORCHAR
), &width
, &height
, &descent
);
1279 m_BaseLine
= m_Height
- descent
;
1282 // tell next line about coordinate change
1283 if(m_Next
&& m_Height
!= heightOld
)
1285 m_Next
->MarkDirty();
1288 // We need to check whether we found a valid cursor size:
1289 if(cursorPos
&& cursorSize
)
1291 // this might be the case if the cursor is at the end of the
1292 // line or on a command object:
1293 if(cursorSize
->x
< WXLO_MINIMUM_CURSOR_WIDTH
)
1295 CoordType width
, height
, descent
;
1296 dc
.GetTextExtent(_T(WXLO_CURSORCHAR
), &width
, &height
, &descent
);
1297 cursorSize
->x
= width
;
1298 cursorSize
->y
= height
;
1300 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
1301 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
1308 wxLayoutLine::Break(CoordType xpos
, wxLayoutList
*llist
)
1310 wxASSERT(xpos
>= 0);
1315 wxLOiterator i
= FindObject(xpos
, &offset
);
1317 // must be at the end of the line then
1318 return new wxLayoutLine(this, llist
);
1321 wxLayoutLine
*newLine
= new wxLayoutLine(this, llist
);
1322 // split object at i:
1323 if((**i
).GetType() == WXLO_TYPE_TEXT
1325 && offset
!= (**i
).GetLength() )
1327 wxString left
, right
;
1328 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
1329 left
= tobj
->GetText().substr(0,offset
);
1330 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
1331 // current text object gets set to left half
1332 tobj
->GetText() = left
; // set new text
1333 newLine
->Append(new wxLayoutObjectText(right
));
1334 m_Length
-= right
.Length();
1335 i
++; // don't move this object to the new list
1340 i
++; // move objects from here to new list
1343 while(i
!= m_ObjectList
.end())
1345 wxLayoutObject
*obj
= *i
;
1346 newLine
->Append(obj
);
1347 m_Length
-= obj
->GetLength();
1349 m_ObjectList
.remove(i
); // remove without deleting it
1352 m_Next
->MarkDirty();
1357 wxLayoutLine::Wrap(CoordType wrapmargin
, wxLayoutList
*llist
)
1359 if(GetLength() < wrapmargin
)
1360 return false; // nothing to do
1362 // find the object which covers the wrapmargin:
1364 wxLOiterator i
= FindObject(wrapmargin
, &offset
);
1365 wxCHECK_MSG( i
!= NULLIT
, false,
1366 wxT("Cannot find object covering wrapmargin."));
1368 // from this object on, the rest of the line must be copied to the
1370 wxLOiterator copyObject
= NULLIT
;
1371 // if we split a text-object, we must pre-pend some text to the
1372 // next line later on, remember it here:
1373 wxString prependText
= _T("");
1374 // we might need to adjust the cursor position later, so remember it
1375 size_t xpos
= llist
->GetCursorPos().x
;
1376 // by how much did we shorten the current line:
1378 // remember cursor location of object
1379 size_t objectCursorPos
= 0;
1381 size_t breakpos
= offset
;
1383 if( (**i
).GetType() != WXLO_TYPE_TEXT
)
1385 // break before a non-text object
1390 bool foundSpace
= false;
1393 // while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT)
1395 // try to find a suitable place to split the object:
1396 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
1397 if((**i
).GetType() == WXLO_TYPE_TEXT
1398 && tobj
->GetText().Length() >= breakpos
)
1402 foundSpace
= isspace(tobj
->GetText()[breakpos
]) != 0;
1406 while ( breakpos
-- > 0 );
1413 if(! foundSpace
) // breakpos == 0!
1415 if(i
== m_ObjectList
.begin())
1416 return false; // could not break line
1420 while(i
!= m_ObjectList
.begin()
1421 && (**i
).GetType() != WXLO_TYPE_TEXT
)
1425 breakpos
= (**i
).GetLength();
1428 }while(! foundSpace
);
1429 // before we actually break the object, we need to know at which
1430 // cursorposition it starts, so we can restore the cursor if needed:
1431 if( this == llist
->GetCursorLine() && xpos
>= breakpos
)
1433 for(wxLOiterator j
= m_ObjectList
.begin();
1434 j
!= NULLIT
&& j
!= i
; j
++)
1435 objectCursorPos
+= (**j
).GetLength();
1437 // now we know where to break it:
1438 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
1439 shorter
= tobj
->GetLength() - breakpos
;
1440 // remember text to copy from this object
1441 prependText
= tobj
->GetText().Mid(breakpos
+1);
1442 tobj
->SetText(tobj
->GetText().Left(breakpos
));
1443 // copy every following object:
1444 copyObject
= i
; copyObject
++;
1447 // make sure there is an empty m_Next line:
1448 (void) new wxLayoutLine(this, llist
);
1450 // We need to move this and all following objects to the next
1451 // line. Starting from the end of line, to keep the order right.
1452 if(copyObject
!= NULLIT
)
1455 for(j
= m_ObjectList
.tail(); j
!= copyObject
; j
--)
1456 m_Next
->Prepend(*j
);
1457 m_Next
->Prepend(*copyObject
);
1458 // and now remove them from this list:
1459 while( copyObject
!= m_ObjectList
.end() )
1461 shorter
+= (**copyObject
).GetLength();
1462 m_ObjectList
.remove(copyObject
); // remove without deleting it
1465 m_Length
-= shorter
;
1467 if(prependText
.Length() > 0)
1468 m_Next
->Insert(0, prependText
);
1470 // do we need to adjust the cursor position?
1471 if( this == llist
->GetCursorLine() && xpos
>= breakpos
)
1473 xpos
= objectCursorPos
+ (xpos
- objectCursorPos
- breakpos
-
1474 ((xpos
> breakpos
) ? 1 : 0 ));
1476 // this assert is useless when xpos has unsigned type
1477 wxASSERT(xpos
>= 0);
1479 llist
->MoveCursorTo( wxPoint( xpos
, m_Next
->GetLineNumber()) );
1481 return true; // we wrapped the line
1485 wxLayoutLine::ReNumber()
1487 CoordType lineNo
= m_Previous
? m_Previous
->m_LineNumber
+1 : 0;
1488 m_LineNumber
= lineNo
++;
1490 for(wxLayoutLine
*next
= GetNextLine();
1491 next
; next
= next
->GetNextLine())
1492 next
->m_LineNumber
= lineNo
++;
1496 wxLayoutLine::MergeNextLine(wxLayoutList
*llist
)
1498 wxCHECK_RET( GetNextLine(),
1499 wxT("wxLayout internal error: no next line to merge"));
1500 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
1503 MarkDirty(GetWidth());
1505 wxLayoutObject
*last
= NULL
;
1506 for(i
= list
.begin(); i
!= list
.end();)
1508 wxLayoutObject
*current
= *i
;
1510 // merge text objects together for efficiency
1511 if ( last
&& last
->GetType() == WXLO_TYPE_TEXT
&&
1512 current
->GetType() == WXLO_TYPE_TEXT
)
1514 wxLayoutObjectText
*textObj
= (wxLayoutObjectText
*)last
;
1515 wxString
text(textObj
->GetText());
1516 text
+= ((wxLayoutObjectText
*)current
)->GetText();
1517 textObj
->SetText(text
);
1519 list
.erase(i
); // remove and delete it
1523 // just append the object "as was"
1526 list
.remove(i
); // remove without deleting it
1529 wxASSERT(list
.empty());
1531 wxLayoutLine
*oldnext
= GetNextLine();
1532 wxLayoutLine
*nextLine
= oldnext
->GetNextLine();
1536 nextLine
->ReNumber();
1540 // this is now done in Delete(), but if this function is ever called
1541 // from elsewhere, we might have to move refresh code back here (in
1542 // order not to duplicate it)
1544 wxPoint
pos(oldnext
->GetPosition());
1545 llist
->SetUpdateRect(pos
);
1546 llist
->SetUpdateRect(pos
.x
+ oldnext
->GetWidth() + MSW_CORRECTION
,
1547 pos
.y
+ oldnext
->GetHeight() + MSW_CORRECTION
);
1551 llist
->DecNumLines();
1557 wxLayoutLine::GetWrapPosition(CoordType column
)
1560 wxLOiterator i
= FindObject(column
, &offset
);
1561 if(i
== NULLIT
) return -1; // cannot wrap
1563 // go backwards through the list and look for space in text objects
1566 if((**i
).GetType() == WXLO_TYPE_TEXT
)
1570 if(isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1577 }while(offset
!= -1);
1578 i
--; // move on to previous object
1582 column
-= (**i
).GetLength();
1586 offset
= (**i
).GetLength();
1587 }while(i
!= NULLIT
);
1588 /* If we reached the begin of the list and have more than one
1589 object, that one is longer than the margin, so break behind
1592 i
= m_ObjectList
.begin();
1593 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1595 pos
+= (**i
).GetLength();
1598 if(i
== NULLIT
) return -1; //why should this happen?
1600 // now we are behind the one long text object and need to find the
1601 // first space in it
1602 for(offset
= 0; offset
< (**i
).GetLength(); offset
++)
1603 if( isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1607 pos
+= (**i
).GetLength();
1612 #ifdef WXLAYOUT_DEBUG
1614 wxLayoutLine::Debug() const
1616 wxPoint pos
= GetPosition();
1617 WXLO_DEBUG((wxT("Line %ld, Pos (%ld,%ld), Height %ld, BL %ld, Font: %d"),
1618 (long int) GetLineNumber(),
1619 (long int) pos
.x
, (long int) pos
.y
,
1620 (long int) GetHeight(),
1621 (long int) m_BaseLine
,
1622 (int) m_StyleInfo
.family
));
1623 if(m_ObjectList
.begin() != NULLIT
)
1625 WXLO_DEBUG(((**m_ObjectList
.begin()).DebugDump().c_str()));
1632 wxLayoutLine::Copy(wxLayoutList
*llist
,
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
!= NULLIT
&& last
!= NULLIT
&& *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 wxCHECK_MSG( m_CursorLine
, false, wxT("no current line") );
2007 wxCHECK_MSG( n
== -1 || n
== +1, false, wxT("not implemented yet") );
2009 CoordType moveDistance
= 0;
2011 wxLayoutLine
*lineCur
= m_CursorLine
;
2012 for ( wxLOiterator i
= lineCur
->FindObject(m_CursorPos
.x
, &offset
);
2020 // moving forward, pass to the first object of the next line
2022 lineCur
= lineCur
->GetNextLine();
2024 i
= lineCur
->GetFirstObject();
2028 // moving backwards, pass to the last object of the prev line
2030 lineCur
= lineCur
->GetPreviousLine();
2032 i
= lineCur
->GetLastObject();
2037 // moved to the end/beginning of text
2044 wxLayoutObject
*obj
= *i
;
2048 // calculate offset: we are either at the very beginning or the very
2049 // end of the object, so it isn't very difficult (the only time when
2050 // offset is != -1 is for the very first iteration when its value is
2051 // returned by FindObject)
2055 offset
= obj
->GetLength();
2058 if( obj
->GetType() != WXLO_TYPE_TEXT
)
2060 // any visible non text objects count as one word
2061 if ( obj
->IsVisibleObject() )
2065 moveDistance
+= obj
->GetLength();
2070 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)obj
;
2072 bool canAdvance
= true;
2074 if ( offset
== tobj
->GetLength() )
2079 // can't move further in this text object
2082 // still should move over the object border
2086 else if ( offset
> 0 )
2088 // offset is off by 1, make it a valid index
2095 const wxString
& text
= tobj
->GetText();
2096 const wxChar
*start
= text
.c_str();
2097 const wxChar
*end
= start
+ text
.length();
2098 const wxChar
*p
= start
+ offset
;
2106 // to the beginning/end of the next/prev word
2107 while ( p
>= start
&& p
< end
&& isspace(*p
) )
2112 // go to the end/beginning of the word (in a broad sense...)
2113 while ( p
>= start
&& p
< end
&& !isspace(*p
) )
2122 // now advance to the beginning of the next word
2123 while ( isspace(*p
) && p
< end
)
2129 // in these 2 cases we took 1 char too much
2130 if ( (p
< start
) || isspace(*p
) )
2136 CoordType moveDelta
= p
- start
- offset
;
2137 if ( (n
< 0) && (offset
== tobj
->GetLength() - 1) )
2139 // because we substracted 1 from offset in this case above, now
2140 // compensate for it
2144 if ( moveDelta
!= 0 )
2146 moveDistance
+= moveDelta
;
2153 // except for the first iteration, offset is calculated in the beginning
2158 MoveCursorHorizontally(moveDistance
);
2164 wxLayoutList::Insert(wxString
const &text
)
2166 wxASSERT(m_CursorLine
);
2167 wxASSERT_MSG( text
.Find(wxT('\n')) == wxNOT_FOUND
,
2168 wxT("use wxLayoutImportText!") );
2173 AddCursorPosToUpdateRect();
2175 wxASSERT(m_CursorLine
->GetLength() >= m_CursorPos
.x
);
2177 if ( !m_CursorLine
->Insert(m_CursorPos
.x
, text
) )
2179 m_CursorPos
.x
+= text
.Length();
2181 m_movedCursor
= true;
2184 m_CursorLine
->MarkDirty();
2190 wxLayoutList::Insert(wxLayoutObject
*obj
)
2192 wxASSERT(m_CursorLine
);
2195 m_CursorLine
= GetFirstLine();
2197 AddCursorPosToUpdateRect();
2199 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
2200 m_CursorPos
.x
+= obj
->GetLength();
2201 m_movedCursor
= true;
2204 m_CursorLine
->MarkDirty();
2210 wxLayoutList::Insert(wxLayoutList
*llist
)
2215 for(wxLayoutLine
*line
= llist
->GetFirstLine();
2217 line
= line
->GetNextLine()
2220 for(wxLOiterator i
= line
->GetFirstObject();
2230 wxLayoutList::LineBreak()
2232 wxASSERT(m_CursorLine
);
2234 AddCursorPosToUpdateRect();
2236 wxPoint
position(m_CursorLine
->GetPosition());
2239 width
= m_CursorLine
->GetWidth(),
2240 height
= m_CursorLine
->GetHeight();
2242 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
, this);
2243 if(m_CursorLine
->GetPreviousLine() == NULL
)
2244 m_FirstLine
= m_CursorLine
;
2248 // The following code will produce a height which is guaranteed to
2249 // be too high: old lineheight + the height of both new lines.
2250 // We can probably drop the old line height and start with height =
2252 wxLayoutLine
*prev
= m_CursorLine
->GetPreviousLine();
2254 height
+= prev
->GetHeight();
2255 height
+= m_CursorLine
->GetHeight();
2257 m_movedCursor
= true;
2259 SetUpdateRect(position
);
2260 SetUpdateRect(position
.x
+ width
+ MSW_CORRECTION
,
2261 position
.y
+ height
+ MSW_CORRECTION
);
2267 wxLayoutList::WrapLine(CoordType column
)
2269 return m_CursorLine
->Wrap(column
, this);
2273 wxLayoutList::WrapAll(CoordType column
)
2275 wxLayoutLine
*line
= m_FirstLine
;
2281 rc
&= line
->Wrap(column
, this);
2282 line
= line
->GetNextLine();
2288 wxLayoutList::Delete(CoordType npos
)
2290 wxCHECK_MSG(m_CursorLine
, false, wxT("can't delete in non existing line"));
2295 AddCursorPosToUpdateRect();
2297 // were other lines appended to this one (this is important to know because
2298 // this means that our width _increased_ as the result of deletion)
2299 bool wasMerged
= false;
2301 // the size of the region to update
2302 CoordType totalHeight
= m_CursorLine
->GetHeight(),
2303 totalWidth
= m_CursorLine
->GetWidth();
2308 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
2312 // More to delete, continue on next line.
2314 // First, check if line is empty:
2315 if(m_CursorLine
->GetLength() == 0)
2317 // in this case, updating could probably be optimised
2319 wxASSERT(DeleteLines(1) == 0);
2328 // Need to join next line
2329 if(! m_CursorLine
->GetNextLine())
2334 wxLayoutLine
*next
= m_CursorLine
->GetNextLine();
2337 totalHeight
+= next
->GetHeight();
2338 totalWidth
+= next
->GetWidth();
2340 m_CursorLine
->MergeNextLine(this);
2345 wxFAIL_MSG(wxT("can't delete all this"));
2355 // we need to update the whole tail of the line and the lines which
2359 wxPoint
position(m_CursorLine
->GetPosition());
2360 SetUpdateRect(position
);
2361 SetUpdateRect(position
.x
+ totalWidth
+ MSW_CORRECTION
,
2362 position
.y
+ totalHeight
+ MSW_CORRECTION
);
2369 wxLayoutList::DeleteLines(int n
)
2371 wxASSERT(m_CursorLine
);
2374 AddCursorPosToUpdateRect();
2378 if(!m_CursorLine
->GetNextLine())
2379 { // we cannot delete this line, but we can clear it
2380 MoveCursorToBeginOfLine();
2381 DeleteToEndOfLine();
2383 m_CursorLine
->MarkDirty();
2387 line
= m_CursorLine
;
2388 m_CursorLine
= m_CursorLine
->DeleteLine(true, this);
2390 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
2391 wxASSERT(m_FirstLine
);
2392 wxASSERT(m_CursorLine
);
2395 m_CursorLine
->MarkDirty();
2400 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
)
2404 wxLayoutLine
*line
= m_FirstLine
;
2406 // first, make sure everything is calculated - this might not be
2407 // needed, optimise it later
2408 ApplyStyle(m_DefaultStyleInfo
, dc
);
2411 line
->RecalculatePosition(this); // so we don't need to do it all the time
2412 // little condition to speed up redrawing:
2413 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
2414 line
= line
->GetNextLine();
2419 wxLayoutList::GetCursorScreenPos() const
2421 return m_CursorScreenPos
;
2425 Is called before each Draw(). Now, it will re-layout all lines which
2429 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
, bool forceAll
,
2430 wxPoint
*cpos
, wxPoint
*csize
)
2432 // first, make sure everything is calculated - this might not be
2433 // needed, optimise it later
2434 ApplyStyle(m_DefaultStyleInfo
, dc
);
2443 ForceTotalLayout(false);
2446 // If one line was dirty, we need to re-calculate all
2447 // following lines, too.
2448 bool wasDirty
= forceAll
;
2449 // we need to layout until we reach at least the cursor line,
2450 // otherwise we won't be able to scroll to it
2451 bool cursorReached
= false;
2452 wxLayoutLine
*line
= m_FirstLine
;
2456 ApplyStyle(line
->GetStyleInfo(), dc
);
2458 // if any previous line was dirty, we need to layout all
2461 // go on until we find the cursorline
2463 // layout dirty lines:
2465 // always layout the cursor line toupdate the cursor
2466 // position and size:
2467 || line
== m_CursorLine
2468 // or if it's the line we are asked to look for:
2469 || (cpos
&& line
->GetLineNumber() == cpos
->y
)
2470 // layout at least the desired region:
2472 || (line
->GetPosition().y
<= bottom
)
2478 // The following Layout() calls will update our
2479 // m_CurrentStyleInfo if needed.
2480 if(line
== m_CursorLine
)
2482 line
->Layout(dc
, this,
2483 (wxPoint
*)&m_CursorScreenPos
,
2484 (wxPoint
*)&m_CursorSize
,
2487 // we cannot layout the line twice, so copy the coords:
2488 if(cpos
&& line
->GetLineNumber() == cpos
->y
)
2490 *cpos
= m_CursorScreenPos
;
2492 *csize
= m_CursorSize
;
2495 cursorReached
= true;
2499 if(cpos
&& line
->GetLineNumber() == cpos
->y
)
2501 line
->Layout(dc
, this,
2503 csize
, NULL
, cpos
->x
);
2504 cursorReached
= true;
2507 line
->Layout(dc
, this);
2511 line
= line
->GetNextLine();
2514 #ifndef WXLAYOUT_USE_CARET
2515 // can only be 0 if we are on the first line and have no next line
2516 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
2517 m_CursorLine
->GetNextLine() == NULL
&&
2518 m_CursorLine
== m_FirstLine
));
2519 #endif // WXLAYOUT_USE_CARET
2521 AddCursorPosToUpdateRect();
2525 wxLayoutList::GetScreenPos(wxDC
&dc
, const wxPoint
&cpos
, wxPoint
*csize
)
2528 Layout(dc
, -1, false, &pos
, csize
);
2533 wxLayoutList::Draw(wxDC
&dc
,
2534 wxPoint
const &offset
,
2539 wxLayoutLine
*line
= m_FirstLine
;
2541 if ( m_Selection
.m_discarded
)
2543 // calculate them if we don't have them already
2544 if ( !m_Selection
.HasValidScreenCoords() )
2546 m_Selection
.m_ScreenA
= GetScreenPos(dc
, m_Selection
.m_CursorA
);
2547 m_Selection
.m_ScreenB
= GetScreenPos(dc
, m_Selection
.m_CursorB
);
2550 // invalidate the area which was previousle selected - and which is not
2551 // selected any more
2552 SetUpdateRect(m_Selection
.m_ScreenA
);
2553 SetUpdateRect(m_Selection
.m_ScreenB
);
2555 m_Selection
.m_discarded
= false;
2558 /* This call to Layout() will re-calculate and update all lines
2563 ApplyStyle(m_DefaultStyleInfo
, dc
);
2564 wxBrush
brush(m_CurrentStyleInfo
.m_bg
, wxSOLID
);
2566 dc
.SetBackgroundMode(wxTRANSPARENT
);
2570 // only draw if between top and bottom:
2572 line
->GetPosition().y
+ line
->GetHeight() > top
))
2574 ApplyStyle(line
->GetStyleInfo(), dc
);
2575 // little condition to speed up redrawing:
2577 && line
->GetPosition().y
2578 +(clipStrictly
? line
->GetHeight() : 0) >= bottom
)
2581 line
->Draw(dc
, this, offset
);
2584 line
= line
->GetNextLine();
2587 InvalidateUpdateRect();
2589 WXLO_DEBUG((wxT("Selection is %s : %d,%d/%d,%d"),
2590 m_Selection
.m_valid
? wxT("valid") : wxT("invalid"),
2591 m_Selection
.m_CursorA
.x
, m_Selection
.m_CursorA
.y
,
2592 m_Selection
.m_CursorB
.x
, m_Selection
.m_CursorB
.y
));
2596 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
,
2597 wxPoint
*cursorPos
, bool *found
)
2599 // First, find the right line:
2601 *line
= m_FirstLine
,
2602 *lastline
= m_FirstLine
;
2605 ApplyStyle(m_DefaultStyleInfo
, dc
);
2608 p
= line
->GetPosition();
2609 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
2612 line
= line
->GetNextLine();
2615 bool didFind
= line
!= NULL
;
2619 // use the last line:
2624 cursorPos
->y
= line
->GetLineNumber();
2626 bool foundinline
= true;
2629 // Now, find the object in the line:
2634 i
= line
->FindObjectScreen(dc
, this,
2641 i
= line
->FindObjectScreen(dc
, this,
2647 *found
= didFind
&& foundinline
;
2649 return (i
== NULLIT
) ? NULL
: *i
;
2654 wxLayoutList::GetSize() const
2657 *line
= m_FirstLine
,
2660 return wxPoint(0,0);
2662 wxPoint
maxPoint(0,0);
2667 if(line
->GetWidth() > maxPoint
.x
)
2668 maxPoint
.x
= line
->GetWidth();
2670 line
= line
->GetNextLine();
2673 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
2675 // if the line was just added, its height would be 0 and we can't call
2676 // Layout() from here because we don't have a dc and we might be not drawing
2677 // at all, besides... So take the cursor height by default (taking 0 is bad
2678 // because then the scrollbars won't be resized and the new line won't be
2680 if ( last
->IsDirty() )
2682 if ( last
->GetHeight() == 0 )
2683 maxPoint
.y
+= m_CursorSize
.y
;
2684 if ( last
->GetWidth() == 0 && maxPoint
.x
< m_CursorSize
.x
)
2685 maxPoint
.x
= m_CursorSize
.x
;
2693 wxLayoutList::DrawCursor(wxDC
&
2694 #ifdef WXLAYOUT_USE_CARET
2700 #ifdef WXLAYOUT_USE_CARET
2705 , wxPoint
const &translate
)
2707 if ( m_movedCursor
)
2708 m_movedCursor
= false;
2710 wxPoint
coords(m_CursorScreenPos
);
2711 coords
+= translate
;
2713 #ifdef WXLAYOUT_DEBUG
2714 WXLO_DEBUG((wxT("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld"),
2715 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
2716 (long)coords
.x
, (long)coords
.y
,
2717 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
2718 (long)m_CursorLine
->GetLineNumber(),
2719 (long)m_CursorLine
->GetLength()));
2721 wxLogStatus(wxT("Cursor is at (%d, %d)"), m_CursorPos
.x
, m_CursorPos
.y
);
2724 #ifdef WXLAYOUT_USE_CARET
2725 m_caret
->Move(coords
);
2726 #else // !WXLAYOUT_USE_CARET
2728 wxASSERT(m_CursorSize
.x
>= WXLO_MINIMUM_CURSOR_WIDTH
);
2729 dc
.SetBrush(*wxWHITE_BRUSH
);
2730 //FIXME: wxGTK XOR is borken at the moment!!!dc.SetLogicalFunction(wxXOR);
2731 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
2734 dc
.SetLogicalFunction(wxXOR
);
2735 dc
.DrawRectangle(coords
.x
, coords
.y
,
2736 m_CursorSize
.x
, m_CursorSize
.y
);
2737 SetUpdateRect(coords
.x
, coords
.y
);
2738 SetUpdateRect(coords
.x
+m_CursorSize
.x
,
2739 coords
.y
+m_CursorSize
.y
);
2743 dc
.SetLogicalFunction(wxCOPY
);
2744 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
2745 coords
.x
, coords
.y
);
2746 SetUpdateRect(coords
.x
, coords
.y
+m_CursorSize
.y
-1);
2747 SetUpdateRect(coords
.x
, coords
.y
);
2750 dc
.SetLogicalFunction(wxCOPY
);
2751 //dc.SetBrush(wxNullBrush);
2752 #endif // WXLAYOUT_USE_CARET/!WXLAYOUT_USE_CARET
2756 wxLayoutList::SetUpdateRect(CoordType x
, CoordType y
)
2758 if(m_UpdateRectValid
)
2760 GrowRect(m_UpdateRect
, x
, y
);
2766 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
2767 m_UpdateRect
.height
= 4;// wxGTK :-)
2768 m_UpdateRectValid
= true;
2773 wxLayoutList::StartSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2775 wxPoint
cpos(cposOrig
);
2779 WXLO_DEBUG((wxT("Starting selection at %d/%d"), cpos
.x
, cpos
.y
));
2781 m_Selection
.m_CursorA
= cpos
;
2782 m_Selection
.m_CursorB
= cpos
;
2783 m_Selection
.m_ScreenA
= spos
;
2784 m_Selection
.m_ScreenB
= spos
;
2785 m_Selection
.m_selecting
= true;
2786 m_Selection
.m_valid
= false;
2790 wxLayoutList::ContinueSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2792 wxPoint
cpos(cposOrig
);
2796 wxASSERT(m_Selection
.m_selecting
== true);
2797 wxASSERT(m_Selection
.m_valid
== false);
2798 WXLO_DEBUG((wxT("Continuing selection at %d/%d"), cpos
.x
, cpos
.y
));
2800 m_Selection
.m_ScreenB
= spos
;
2801 m_Selection
.m_CursorB
= cpos
;
2805 wxLayoutList::EndSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2807 wxPoint
cpos(cposOrig
);
2809 if(cpos
.x
== -1) cpos
= m_CursorPos
;
2811 ContinueSelection(cpos
, spos
);
2813 WXLO_DEBUG((wxT("Ending selection at %d/%d"), cpos
.x
, cpos
.y
));
2815 // we always want m_CursorA <= m_CursorB!
2816 if( m_Selection
.m_CursorA
> m_Selection
.m_CursorB
)
2818 // exchange the start/end points
2819 wxPoint help
= m_Selection
.m_CursorB
;
2820 m_Selection
.m_CursorB
= m_Selection
.m_CursorA
;
2821 m_Selection
.m_CursorA
= help
;
2823 help
= m_Selection
.m_ScreenB
;
2824 m_Selection
.m_ScreenB
= m_Selection
.m_ScreenA
;
2825 m_Selection
.m_ScreenA
= help
;
2828 m_Selection
.m_selecting
= false;
2829 m_Selection
.m_valid
= true;
2830 /// In case we just clicked somewhere, the selection will have zero
2831 /// size, so we discard it immediately.
2832 if(m_Selection
.m_CursorA
== m_Selection
.m_CursorB
)
2839 wxLayoutList::DiscardSelection()
2841 if ( !HasSelection() )
2844 m_Selection
.m_valid
=
2845 m_Selection
.m_selecting
= false;
2846 m_Selection
.m_discarded
= true;
2850 wxLayoutList::IsSelecting() const
2852 return m_Selection
.m_selecting
;
2856 wxLayoutList::IsSelected(const wxPoint
&cursor
) const
2858 if ( !HasSelection() )
2862 (m_Selection
.m_CursorA
<= cursor
2863 && cursor
<= m_Selection
.m_CursorB
)
2864 || (m_Selection
.m_CursorB
<= cursor
2865 && cursor
<= m_Selection
.m_CursorA
)
2870 /** Tests whether this layout line is selected and needs
2872 @param line to test for
2873 @return 0 = not selected, 1 = fully selected, -1 = partially
2877 wxLayoutList::IsSelected(const wxLayoutLine
*line
, CoordType
*from
,
2880 wxASSERT(line
); wxASSERT(to
); wxASSERT(from
);
2882 if(! m_Selection
.m_valid
&& ! m_Selection
.m_selecting
)
2885 CoordType y
= line
->GetLineNumber();
2886 if ( (m_Selection
.m_CursorA
.y
< y
&& m_Selection
.m_CursorB
.y
> y
)
2887 || (m_Selection
.m_CursorB
.y
< y
&& m_Selection
.m_CursorA
.y
> y
) )
2891 else if (m_Selection
.m_CursorA
.y
== y
)
2893 *from
= m_Selection
.m_CursorA
.x
;
2894 if(m_Selection
.m_CursorB
.y
== y
)
2896 *to
= m_Selection
.m_CursorB
.x
;
2900 if(m_Selection
.m_CursorB
> m_Selection
.m_CursorA
)
2901 *to
= line
->GetLength();
2908 CoordType help
= *to
;
2915 else if (m_Selection
.m_CursorB
.y
== y
)
2917 *to
= m_Selection
.m_CursorB
.x
;
2918 if (m_Selection
.m_CursorA
.y
== y
)
2920 *from
= m_Selection
.m_CursorA
.x
;
2924 if(m_Selection
.m_CursorB
> m_Selection
.m_CursorA
)
2927 *from
= line
->GetLength();
2932 CoordType help
= *to
;
2945 wxLayoutList::DeleteSelection()
2947 if (! m_Selection
.m_valid
)
2950 m_Selection
.m_valid
= false;
2952 // Only delete part of the current line?
2953 if (m_Selection
.m_CursorA
.y
== m_Selection
.m_CursorB
.y
)
2955 MoveCursorTo(m_Selection
.m_CursorA
);
2956 Delete(m_Selection
.m_CursorB
.x
- m_Selection
.m_CursorA
.x
);
2960 // We now know that the two lines are different:
2963 * firstLine
= GetLine(m_Selection
.m_CursorA
.y
),
2964 * lastLine
= GetLine(m_Selection
.m_CursorB
.y
);
2966 // be a bit paranoid:
2967 if(! firstLine
|| ! lastLine
)
2970 // First, delete what's left of this line:
2971 MoveCursorTo(m_Selection
.m_CursorA
);
2972 DeleteToEndOfLine();
2974 wxLayoutLine
*prevLine
= firstLine
->GetPreviousLine(),
2975 *nextLine
= firstLine
->GetNextLine();
2977 while(nextLine
&& nextLine
!= lastLine
)
2979 nextLine
= nextLine
->DeleteLine(false, this);
2982 // Now nextLine = lastLine;
2983 Delete(1); // This joins firstLine and nextLine
2984 Delete(m_Selection
.m_CursorB
.x
); // This deletes the first x positions
2986 // Recalculate the line positions and numbers but notice that firstLine
2987 // might not exist any more - it could be deleted by Delete(1) above
2988 wxLayoutLine
*firstLine2
= prevLine
? prevLine
->GetNextLine() : m_FirstLine
;
2989 firstLine2
->MarkDirty();
2992 /// Starts highlighting the selection
2994 wxLayoutList::StartHighlighting(wxDC
&dc
)
2997 dc
.SetTextForeground(m_CurrentStyleInfo
.m_bg
);
2998 dc
.SetTextBackground(m_CurrentStyleInfo
.m_fg
);
2999 dc
.SetBackgroundMode(wxSOLID
);
3003 /// Ends highlighting the selection
3005 wxLayoutList::EndHighlighting(wxDC
&dc
)
3008 dc
.SetTextForeground(m_CurrentStyleInfo
.m_fg
);
3009 dc
.SetTextBackground(m_CurrentStyleInfo
.m_bg
);
3010 dc
.SetBackgroundMode(wxTRANSPARENT
);
3016 wxLayoutList::GetLine(CoordType index
) const
3018 wxASSERT_MSG( (0 <= index
) && (index
< (CoordType
)m_numLines
),
3019 wxT("invalid index") );
3022 CoordType n
= index
;
3024 CoordType lineNo
= 0;
3027 for ( line
= m_FirstLine
; line
&& n
-- > 0; line
= line
->GetNextLine() )
3030 wxASSERT(line
->GetLineNumber() == lineNo
);
3037 // should be the right one
3038 wxASSERT( line
->GetLineNumber() == index
);
3046 wxLayoutList::Copy(const wxPoint
&from
,
3053 for(firstLine
= m_FirstLine
;
3054 firstLine
&& firstLine
->GetLineNumber() < from
.y
;
3055 firstLine
=firstLine
->GetNextLine())
3058 if(!firstLine
|| firstLine
->GetLineNumber() != from
.y
)
3061 for(lastLine
= m_FirstLine
;
3062 lastLine
&& lastLine
->GetLineNumber() < to
.y
;
3063 lastLine
=lastLine
->GetNextLine())
3066 if(!lastLine
|| lastLine
->GetLineNumber() != to
.y
)
3071 wxLayoutLine
*tmp
= firstLine
;
3072 firstLine
= lastLine
;
3076 wxLayoutList
*llist
= new wxLayoutList();
3078 if(firstLine
== lastLine
)
3080 firstLine
->Copy(llist
, from
.x
, to
.x
);
3084 // Extract objects from first line
3085 firstLine
->Copy(llist
, from
.x
);
3087 // Extract all lines between
3088 for ( wxLayoutLine
*line
= firstLine
->GetNextLine();
3090 line
= line
->GetNextLine() )
3096 // Extract objects from last line
3097 lastLine
->Copy(llist
, 0, to
.x
);
3104 wxLayoutList::GetSelection(wxLayoutDataObject
*wxlo
, bool invalidate
)
3106 if(! m_Selection
.m_valid
)
3108 if(m_Selection
.m_selecting
)
3114 if(invalidate
) m_Selection
.m_valid
= false;
3116 wxLayoutList
*llist
= Copy( m_Selection
.m_CursorA
,
3117 m_Selection
.m_CursorB
);
3119 if(llist
&& wxlo
) // export as data object, too
3123 wxLayoutExportObject
*exp
;
3124 wxLayoutExportStatus
status(llist
);
3125 while((exp
= wxLayoutExport( &status
, WXLO_EXPORT_AS_OBJECTS
)) != NULL
)
3127 if(exp
->type
== WXLO_EXPORT_EMPTYLINE
)
3128 string
<< (int) WXLO_TYPE_LINEBREAK
<< '\n';
3130 exp
->content
.object
->Write(string
);
3134 wxlo
->SetLayoutData(string
);
3142 #define COPY_SI(what) if(si.what != -1) { m_CurrentStyleInfo.what = si.what; fontChanged = true; }
3145 wxLayoutList::ApplyStyle(wxLayoutStyleInfo
const &si
, wxDC
&dc
)
3147 bool fontChanged
= false;
3154 dc
.SetFont( m_FontCache
.GetFont(m_CurrentStyleInfo
) );
3158 m_CurrentStyleInfo
.m_fg
= si
.m_fg
;
3159 m_CurrentStyleInfo
.m_fg_valid
= true;
3160 dc
.SetTextForeground(m_CurrentStyleInfo
.m_fg
);
3165 m_CurrentStyleInfo
.m_bg
= si
.m_bg
;
3166 m_CurrentStyleInfo
.m_bg_valid
= true;
3167 dc
.SetTextBackground(m_CurrentStyleInfo
.m_bg
);
3172 #ifdef WXLAYOUT_DEBUG
3175 wxLayoutList::Debug()
3177 WXLO_DEBUG((wxT("Cursor is in line %d, screen pos = (%d, %d)"),
3178 (int)m_CursorLine
->GetLineNumber(),
3179 m_CursorScreenPos
.x
, m_CursorScreenPos
.y
));
3182 for(line
= m_FirstLine
; line
; line
= line
->GetNextLine())
3191 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3195 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
3197 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
3198 wxString
const & title
)
3203 // remove any highlighting which could interfere with printing:
3204 m_llist
->StartSelection();
3205 m_llist
->EndSelection();
3206 // force a full layout of the list:
3207 m_llist
->ForceTotalLayout();
3208 // layout is called in ScaleDC() when we have a DC
3211 wxLayoutPrintout::~wxLayoutPrintout()
3216 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
3218 // The following bit is taken from the printing sample, let's see
3219 // whether it works for us.
3221 /* You might use THIS code to set the printer DC to ROUGHLY reflect
3222 * the screen text size. This page also draws lines of actual length 5cm
3226 // Get the logical pixels per inch of screen and printer
3227 int ppiScreenX
, ppiScreenY
;
3228 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
3229 int ppiPrinterX
, ppiPrinterY
;
3230 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
3232 if(ppiScreenX
== 0) // not yet set, need to guess
3237 wxUnusedVar(ppiScreenY
);
3239 if(ppiPrinterX
== 0) // not yet set, need to guess
3244 wxUnusedVar(ppiPrinterY
);
3246 // This scales the DC so that the printout roughly represents the
3247 // the screen scaling. The text point size _should_ be the right size
3248 // but in fact is too small for some reason. This is a detail that will
3249 // need to be addressed at some point but can be fudged for the
3251 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
3253 // Now we have to check in case our real page size is reduced
3254 // (e.g. because we're drawing to a print preview memory DC)
3255 int pageWidth
, pageHeight
;
3257 dc
->GetSize(&w
, &h
);
3258 GetPageSizePixels(&pageWidth
, &pageHeight
);
3259 wxUnusedVar(pageHeight
);
3260 if(pageWidth
!= 0) // doesn't work always
3262 // If printer pageWidth == current DC width, then this doesn't
3263 // change. But w might be the preview bitmap width, so scale down.
3264 scale
= scale
* (float)(w
/(float)pageWidth
);
3267 dc
->SetUserScale(scale
, scale
);
3271 bool wxLayoutPrintout::OnPrintPage(int page
)
3280 top
= (page
- 1)*m_PrintoutHeight
;
3281 bottom
= top
+ m_PrintoutHeight
;
3283 WXLO_DEBUG((wxT("OnPrintPage(%d) printing from %d to %d"), page
, top
,
3286 // SetDeviceOrigin() doesn't work here, so we need to manually
3287 // translate all coordinates.
3288 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
3289 m_llist
->Draw(*dc
, translate
, top
, bottom
, true /* clip strictly */);
3298 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
3300 /* We allocate a temporary wxDC for printing, so that we can
3301 determine the correct paper size and scaling. We don't actually
3302 print anything on it. */
3303 #if defined(__WXMSW__)
3304 wxPrinterDC
*psdc
= new wxPrinterDC(wxEmptyString
,wxEmptyString
,_T(WXLLIST_TEMPFILE
),false);
3306 wxPostScriptDC
*psdc
= new wxPostScriptDC(WXLLIST_TEMPFILE
,false);
3309 psdc
->StartDoc(m_title
);
3310 // before we draw anything, me must make sure the list is properly
3312 m_llist
->Layout(*psdc
);
3314 float scale
= ScaleDC(psdc
);
3316 psdc
->GetSize(&m_PageWidth
, &m_PageHeight
);
3318 // This sets a left/top origin of 15% and 5%:
3319 m_Offset
= wxPoint((15*m_PageWidth
)/100, (5*m_PageHeight
)/100);
3321 // This is the length of the printable area.
3322 m_PrintoutHeight
= m_PageHeight
- 2*m_Offset
.y
;
3323 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
3326 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
3329 *maxPage
= m_NumOfPages
;
3332 *selPageTo
= m_NumOfPages
;
3335 wxRemoveFile(_T(WXLLIST_TEMPFILE
));
3338 bool wxLayoutPrintout::HasPage(int pageNum
)
3340 return pageNum
<= m_NumOfPages
;
3344 Stupid wxWidgets doesn't draw proper ellipses, so we comment this
3345 out. It's a waste of paper anyway.
3349 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
3350 wxPoint topleft
, wxPoint bottomright
,
3353 // make backups of all essential parameters
3354 const wxBrush
& brush
= dc
.GetBrush();
3355 const wxPen
& pen
= dc
.GetPen();
3356 const wxFont
& font
= dc
.GetFont();
3358 dc
.SetBrush(*wxWHITE_BRUSH
);
3359 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
3360 dc
.DrawRoundedRectangle(topleft
.x
,
3361 topleft
.y
,bottomright
.x
-topleft
.x
,
3362 bottomright
.y
-topleft
.y
);
3363 dc
.SetBrush(*wxBLACK_BRUSH
);
3364 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
3365 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
3369 page
= "9999/9999 "; // many pages...
3371 dc
.GetTextExtent(page
,&w
,&h
);
3372 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
3373 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
3374 dc
.GetTextExtent("XXXX", &w
,&h
);
3375 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);
3386 wxFontCache::GetFont(int family
, int size
, int style
, int weight
,
3389 for(wxFCEList::iterator i
= m_FontList
.begin();
3390 i
!= m_FontList
.end(); i
++)
3391 if( (**i
).Matches(family
, size
, style
, weight
, underline
) )
3392 return (**i
).GetFont();
3394 wxFontCacheEntry
*fce
= new wxFontCacheEntry(family
, size
, style
,
3396 m_FontList
.push_back(fce
);
3397 return fce
->GetFont();