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 char *g_aTypeStrings
[] =
78 "invalid", "text", "cmd", "icon"
81 wxLayoutObject::DebugDump(void) 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(void)
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
*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("test ", &a
, &b
, &c
);
350 dc
.GetTextExtent("test", &d
, &e
, &f
);
354 dc
.GetTextExtent(" ", &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(void) 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("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(void)
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
*wxllist
,
476 CoordType begin
, CoordType
/* 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(void)
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 int red
, green
, blue
;
635 ReadString(tmp
, istr
);
639 ReadString(tmp
, istr
);
643 ReadString(tmp
, istr
);
647 obj
->m_StyleInfo
->m_fg
= wxColour(red
, green
, blue
);
650 if(obj
->m_StyleInfo
->m_bg_valid
)
652 int red
, green
, blue
;
653 ReadString(tmp
, istr
);
657 ReadString(tmp
, istr
);
661 ReadString(tmp
, istr
);
665 obj
->m_StyleInfo
->m_bg
= wxColour(red
, green
, blue
);
672 wxLayoutObjectCmd::~wxLayoutObjectCmd()
678 wxLayoutObjectCmd::GetStyle(void) const
684 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint
const & /* coords */,
685 wxLayoutList
*wxllist
,
686 CoordType begin
, CoordType
/* 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
857 wxString
const *text
;
859 for(wxLOiterator i
= m_ObjectList
.begin(); i
!= m_ObjectList
.end(); i
++)
861 if(cpos
>= xpos
) // search from here!
863 if((**i
).GetType() == WXLO_TYPE_TEXT
)
865 text
= & ((wxLayoutObjectText
*)(*i
))->GetText();
866 relpos
= text
->Find(needle
);
867 if(relpos
>= cpos
-xpos
) // -1 if not found
872 cpos
+= (**i
).GetLength();
875 return -1; // not found
879 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
882 wxASSERT(obj
!= NULL
);
887 wxLOiterator i
= FindObject(xpos
, &offset
);
890 if(xpos
== 0 ) // aha, empty line!
892 m_ObjectList
.push_back(obj
);
893 m_Length
+= obj
->GetLength();
900 CoordType len
= (**i
).GetLength();
901 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
902 { // insert before this object
903 m_ObjectList
.insert(i
,obj
);
904 m_Length
+= obj
->GetLength();
909 if( i
== m_ObjectList
.tail()) // last object?
910 m_ObjectList
.push_back(obj
);
912 { // insert after current object
914 m_ObjectList
.insert(i
,obj
);
916 m_Length
+= obj
->GetLength();
919 /* Otherwise we need to split the current object.
920 Fortunately this can only be a text object. */
921 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
922 wxString left
, right
;
923 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
924 left
= tobj
->GetText().substr(0,offset
);
925 right
= tobj
->GetText().substr(offset
,len
-offset
);
926 // current text object gets set to right half
927 tobj
->GetText() = right
; // set new text
928 // before it we insert the new object
929 m_ObjectList
.insert(i
,obj
);
930 m_Length
+= obj
->GetLength();
931 // and before that we insert the left half
932 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
937 wxLayoutLine::Insert(CoordType xpos
, const wxString
& text
)
944 wxLOiterator i
= FindObject(xpos
, &offset
);
945 if(i
!= NULLIT
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
947 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
948 tobj
->GetText().insert(offset
, text
);
949 m_Length
+= text
.Length();
953 if ( !Insert(xpos
, new wxLayoutObjectText(text
)) )
961 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
963 CoordType offset
, len
;
968 wxLOiterator i
= FindObject(xpos
, &offset
);
971 if(i
== NULLIT
) return npos
;
972 // now delete from that object:
973 if((**i
).GetType() != WXLO_TYPE_TEXT
)
975 if(offset
!= 0) // at end of line after a non-text object
978 len
= (**i
).GetLength();
981 m_ObjectList
.erase(i
);
985 // tidy up: remove empty text objects
986 if((**i
).GetLength() == 0)
988 m_ObjectList
.erase(i
);
992 CoordType max
= (**i
).GetLength() - offset
;
993 if(npos
< max
) max
= npos
;
996 if(xpos
== GetLength())
999 { // at the end of an object
1000 // move to begin of next object:
1002 continue; // start over
1007 if(offset
== 0 && max
== (**i
).GetLength())
1008 m_ObjectList
.erase(i
); // remove the whole object
1010 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
1018 wxLayoutLine::DeleteWord(CoordType xpos
)
1020 wxASSERT(xpos
>= 0);
1024 wxLOiterator i
= FindObject(xpos
, &offset
);
1028 if(i
== NULLIT
) return false;
1029 if((**i
).GetType() != WXLO_TYPE_TEXT
)
1031 // This should only happen when at end of line, behind a non-text
1033 if(offset
== (**i
).GetLength()) return false;
1034 m_Length
-= (**i
).GetLength(); // -1
1035 m_ObjectList
.erase(i
);
1036 return true; // we are done
1040 if(offset
== (**i
).GetLength()) // at end of object
1046 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
1048 wxString str
= tobj
->GetText();
1049 str
= str
.substr(offset
,str
.Length()-offset
);
1050 // Find out how many positions we need to delete:
1051 // 1. eat leading space
1052 while(isspace(str
.c_str()[count
])) count
++;
1053 // 2. eat the word itself:
1054 while(isalnum(str
.c_str()[count
])) count
++;
1056 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
1057 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
1064 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
1105 CoordType from
, to
, tempto
;
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
1119 tempto
= xpos
+(**i
).GetLength();
1120 (**i
).Draw(dc
, pos
, llist
, from
-xpos
, to
-xpos
);
1123 (**i
).Draw(dc
, pos
, llist
);
1124 pos
.x
+= (**i
).GetWidth();
1125 xpos
+= (**i
).GetLength();
1130 This function does all the recalculation, that is, it should only be
1131 called from within wxLayoutList::Layout(), as it uses the current
1132 list's styleinfo and updates it.
1135 wxLayoutLine::Layout(wxDC
&dc
,
1136 wxLayoutList
*llist
,
1138 wxPoint
*cursorSize
,
1139 wxLayoutStyleInfo
*cursorStyle
,
1141 bool suppressSIupdate
)
1143 wxLayoutObjectList::iterator i
;
1145 // when a line becomes dirty, we redraw it from the place where it was
1146 // changed till the end of line (because the following wxLayoutObjects are
1147 // moved when the preceding one changes) - calculate the update rectangle.
1148 CoordType updateTop
= m_Position
.y
,
1150 updateWidth
= m_Width
,
1151 updateHeight
= m_Height
;
1155 bottomHeight
= 0; // above and below baseline
1157 objTopHeight
, objBottomHeight
; // above and below baseline
1161 CoordType heightOld
= m_Height
;
1167 bool cursorFound
= false;
1169 RecalculatePosition(llist
);
1173 *cursorPos
= m_Position
;
1174 if(cursorSize
) *cursorSize
= wxPoint(0,0);
1177 m_StyleInfo
= llist
->GetStyleInfo(); // save current style
1178 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
1180 wxLayoutObject
*obj
= *i
;
1181 obj
->Layout(dc
, llist
);
1182 wxPoint sizeObj
= obj
->GetSize(&objTopHeight
, &objBottomHeight
);
1184 if(cursorPos
&& ! cursorFound
)
1186 // we need to check whether the text cursor is here
1187 len
= obj
->GetLength();
1188 if(count
<= cx
&& count
+len
> cx
)
1190 if(obj
->GetType() == WXLO_TYPE_TEXT
)
1192 len
= cx
- count
; // pos in object
1193 CoordType width
, height
, descent
;
1194 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
1195 &width
, &height
, &descent
);
1196 cursorPos
->x
+= width
;
1197 cursorPos
->y
= m_Position
.y
;
1199 if(len
< obj
->GetLength())
1200 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
1202 str
= WXLO_CURSORCHAR
;
1203 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
1205 if(cursorStyle
) // set style info
1206 *cursorStyle
= llist
->GetStyleInfo();
1209 // Just in case some joker inserted an empty string object:
1211 width
= WXLO_MINIMUM_CURSOR_WIDTH
;
1214 cursorSize
->x
= width
;
1215 cursorSize
->y
= height
;
1218 cursorFound
= true; // no more checks
1222 // on some other object
1223 CoordType top
, bottom
; // unused
1225 *cursorSize
= obj
->GetSize(&top
,&bottom
);
1226 cursorPos
->y
= m_Position
.y
;
1227 cursorFound
= true; // no more checks
1233 cursorPos
->x
+= obj
->GetWidth();
1237 m_Width
+= sizeObj
.x
;
1238 if(sizeObj
.y
> m_Height
)
1240 m_Height
= sizeObj
.y
;
1243 if(objTopHeight
> topHeight
)
1244 topHeight
= objTopHeight
;
1245 if(objBottomHeight
> bottomHeight
)
1246 bottomHeight
= objBottomHeight
;
1251 if ( updateHeight
< m_Height
)
1252 updateHeight
= m_Height
;
1253 if ( updateWidth
< m_Width
)
1254 updateWidth
= m_Width
;
1256 // update all line if we don't know where to start from
1257 if ( updateLeft
== -1 )
1260 llist
->SetUpdateRect(updateLeft
, updateTop
);
1261 llist
->SetUpdateRect(updateLeft
+ updateWidth
+ MSW_CORRECTION
,
1262 updateTop
+ updateHeight
+ MSW_CORRECTION
);
1265 if(topHeight
+ bottomHeight
> m_Height
)
1267 m_Height
= topHeight
+bottomHeight
;
1270 m_BaseLine
= topHeight
;
1274 CoordType width
, height
, descent
;
1275 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1277 m_BaseLine
= m_Height
- descent
;
1280 // tell next line about coordinate change
1281 if(m_Next
&& m_Height
!= heightOld
)
1283 m_Next
->MarkDirty();
1286 // We need to check whether we found a valid cursor size:
1287 if(cursorPos
&& cursorSize
)
1289 // this might be the case if the cursor is at the end of the
1290 // line or on a command object:
1291 if(cursorSize
->x
< WXLO_MINIMUM_CURSOR_WIDTH
)
1293 CoordType width
, height
, descent
;
1294 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1295 cursorSize
->x
= width
;
1296 cursorSize
->y
= height
;
1298 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
1299 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
1306 wxLayoutLine::Break(CoordType xpos
, wxLayoutList
*llist
)
1308 wxASSERT(xpos
>= 0);
1313 wxLOiterator i
= FindObject(xpos
, &offset
);
1315 // must be at the end of the line then
1316 return new wxLayoutLine(this, llist
);
1319 wxLayoutLine
*newLine
= new wxLayoutLine(this, llist
);
1320 // split object at i:
1321 if((**i
).GetType() == WXLO_TYPE_TEXT
1323 && offset
!= (**i
).GetLength() )
1325 wxString left
, right
;
1326 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
1327 left
= tobj
->GetText().substr(0,offset
);
1328 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
1329 // current text object gets set to left half
1330 tobj
->GetText() = left
; // set new text
1331 newLine
->Append(new wxLayoutObjectText(right
));
1332 m_Length
-= right
.Length();
1333 i
++; // don't move this object to the new list
1338 i
++; // move objects from here to new list
1341 while(i
!= m_ObjectList
.end())
1343 wxLayoutObject
*obj
= *i
;
1344 newLine
->Append(obj
);
1345 m_Length
-= obj
->GetLength();
1347 m_ObjectList
.remove(i
); // remove without deleting it
1350 m_Next
->MarkDirty();
1355 wxLayoutLine::Wrap(CoordType wrapmargin
, wxLayoutList
*llist
)
1357 if(GetLength() < wrapmargin
)
1358 return FALSE
; // nothing to do
1360 // find the object which covers the wrapmargin:
1362 wxLOiterator i
= FindObject(wrapmargin
, &offset
);
1363 wxCHECK_MSG( i
!= NULLIT
, FALSE
,
1364 wxT("Cannot find object covering wrapmargin."));
1366 // from this object on, the rest of the line must be copied to the
1368 wxLOiterator copyObject
= NULLIT
;
1369 // if we split a text-object, we must pre-pend some text to the
1370 // next line later on, remember it here:
1371 wxString prependText
= "";
1372 // we might need to adjust the cursor position later, so remember it
1373 size_t xpos
= llist
->GetCursorPos().x
;
1374 // by how much did we shorten the current line:
1376 // remember cursor location of object
1377 size_t objectCursorPos
= 0;
1379 size_t breakpos
= offset
;
1381 if( (**i
).GetType() != WXLO_TYPE_TEXT
)
1383 // break before a non-text object
1388 bool foundSpace
= FALSE
;
1391 // while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT)
1393 // try to find a suitable place to split the object:
1394 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
1395 if((**i
).GetType() == WXLO_TYPE_TEXT
1396 && tobj
->GetText().Length() >= breakpos
)
1400 foundSpace
= isspace(tobj
->GetText()[breakpos
]) != 0;
1404 while ( breakpos
-- > 0 );
1411 if(! foundSpace
) // breakpos == 0!
1413 if(i
== m_ObjectList
.begin())
1414 return FALSE
; // could not break line
1418 while(i
!= m_ObjectList
.begin()
1419 && (**i
).GetType() != WXLO_TYPE_TEXT
)
1423 breakpos
= (**i
).GetLength();
1426 }while(! foundSpace
);
1427 // before we actually break the object, we need to know at which
1428 // cursorposition it starts, so we can restore the cursor if needed:
1429 if( this == llist
->GetCursorLine() && xpos
>= breakpos
)
1431 for(wxLOiterator j
= m_ObjectList
.begin();
1432 j
!= NULLIT
&& j
!= i
; j
++)
1433 objectCursorPos
+= (**j
).GetLength();
1435 // now we know where to break it:
1436 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
1437 shorter
= tobj
->GetLength() - breakpos
;
1438 // remember text to copy from this object
1439 prependText
= tobj
->GetText().Mid(breakpos
+1);
1440 tobj
->SetText(tobj
->GetText().Left(breakpos
));
1441 // copy every following object:
1442 copyObject
= i
; copyObject
++;
1445 // make sure there is an empty m_Next line:
1446 (void) new wxLayoutLine(this, llist
);
1448 // We need to move this and all following objects to the next
1449 // line. Starting from the end of line, to keep the order right.
1450 if(copyObject
!= NULLIT
)
1453 for(j
= m_ObjectList
.tail(); j
!= copyObject
; j
--)
1454 m_Next
->Prepend(*j
);
1455 m_Next
->Prepend(*copyObject
);
1456 // and now remove them from this list:
1457 while( copyObject
!= m_ObjectList
.end() )
1459 shorter
+= (**copyObject
).GetLength();
1460 m_ObjectList
.remove(copyObject
); // remove without deleting it
1463 m_Length
-= shorter
;
1465 if(prependText
.Length() > 0)
1466 m_Next
->Insert(0, prependText
);
1468 // do we need to adjust the cursor position?
1469 if( this == llist
->GetCursorLine() && xpos
>= breakpos
)
1471 xpos
= objectCursorPos
+ (xpos
- objectCursorPos
- breakpos
-
1472 ((xpos
> breakpos
) ? 1 : 0 ));
1473 wxASSERT(xpos
>= 0);
1474 llist
->MoveCursorTo( wxPoint( xpos
, m_Next
->GetLineNumber()) );
1476 return TRUE
; // we wrapped the line
1480 wxLayoutLine::ReNumber(void)
1482 CoordType lineNo
= m_Previous
? m_Previous
->m_LineNumber
+1 : 0;
1483 m_LineNumber
= lineNo
++;
1485 for(wxLayoutLine
*next
= GetNextLine();
1486 next
; next
= next
->GetNextLine())
1487 next
->m_LineNumber
= lineNo
++;
1491 wxLayoutLine::MergeNextLine(wxLayoutList
*llist
)
1493 wxCHECK_RET( GetNextLine(),
1494 wxT("wxLayout internal error: no next line to merge"));
1495 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
1498 MarkDirty(GetWidth());
1500 wxLayoutObject
*last
= NULL
;
1501 for(i
= list
.begin(); i
!= list
.end();)
1503 wxLayoutObject
*current
= *i
;
1505 // merge text objects together for efficiency
1506 if ( last
&& last
->GetType() == WXLO_TYPE_TEXT
&&
1507 current
->GetType() == WXLO_TYPE_TEXT
)
1509 wxLayoutObjectText
*textObj
= (wxLayoutObjectText
*)last
;
1510 wxString
text(textObj
->GetText());
1511 text
+= ((wxLayoutObjectText
*)current
)->GetText();
1512 textObj
->SetText(text
);
1514 list
.erase(i
); // remove and delete it
1518 // just append the object "as was"
1521 list
.remove(i
); // remove without deleting it
1524 wxASSERT(list
.empty());
1526 wxLayoutLine
*oldnext
= GetNextLine();
1527 wxLayoutLine
*nextLine
= oldnext
->GetNextLine();
1531 nextLine
->ReNumber();
1535 // this is now done in Delete(), but if this function is ever called
1536 // from elsewhere, we might have to move refresh code back here (in
1537 // order not to duplicate it)
1539 wxPoint
pos(oldnext
->GetPosition());
1540 llist
->SetUpdateRect(pos
);
1541 llist
->SetUpdateRect(pos
.x
+ oldnext
->GetWidth() + MSW_CORRECTION
,
1542 pos
.y
+ oldnext
->GetHeight() + MSW_CORRECTION
);
1546 llist
->DecNumLines();
1552 wxLayoutLine::GetWrapPosition(CoordType column
)
1555 wxLOiterator i
= FindObject(column
, &offset
);
1556 if(i
== NULLIT
) return -1; // cannot wrap
1558 // go backwards through the list and look for space in text objects
1561 if((**i
).GetType() == WXLO_TYPE_TEXT
)
1565 if(isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1572 }while(offset
!= -1);
1573 i
--; // move on to previous object
1577 column
-= (**i
).GetLength();
1581 offset
= (**i
).GetLength();
1582 }while(i
!= NULLIT
);
1583 /* If we reached the begin of the list and have more than one
1584 object, that one is longer than the margin, so break behind
1587 i
= m_ObjectList
.begin();
1588 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1590 pos
+= (**i
).GetLength();
1593 if(i
== NULLIT
) return -1; //why should this happen?
1595 // now we are behind the one long text object and need to find the
1596 // first space in it
1597 for(offset
= 0; offset
< (**i
).GetLength(); offset
++)
1598 if( isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1602 pos
+= (**i
).GetLength();
1607 #ifdef WXLAYOUT_DEBUG
1609 wxLayoutLine::Debug(void) const
1611 wxPoint pos
= GetPosition();
1612 WXLO_DEBUG((wxT("Line %ld, Pos (%ld,%ld), Height %ld, BL %ld, Font: %d"),
1613 (long int) GetLineNumber(),
1614 (long int) pos
.x
, (long int) pos
.y
,
1615 (long int) GetHeight(),
1616 (long int) m_BaseLine
,
1617 (int) m_StyleInfo
.family
));
1618 if(m_ObjectList
.begin() != NULLIT
)
1620 WXLO_DEBUG(((**m_ObjectList
.begin()).DebugDump().c_str()));
1627 wxLayoutLine::Copy(wxLayoutList
*llist
,
1631 CoordType firstOffset
, lastOffset
;
1633 if(to
== -1) to
= GetLength();
1634 if(from
== to
) return;
1636 wxLOiterator first
= FindObject(from
, &firstOffset
);
1637 wxLOiterator last
= FindObject(to
, &lastOffset
);
1639 // Common special case: only one object
1640 if( first
!= NULLIT
&& last
!= NULLIT
&& *first
== *last
)
1642 if( (**first
).GetType() == WXLO_TYPE_TEXT
)
1644 llist
->Insert(new wxLayoutObjectText(
1645 ((wxLayoutObjectText
1646 *)*first
)->GetText().substr(firstOffset
,
1647 lastOffset
-firstOffset
))
1651 else // what can we do?
1653 if(lastOffset
> firstOffset
) // i.e. +1 :-)
1654 llist
->Insert( (**first
).Copy() );
1659 // If we reach here, we can safely copy the whole first object from
1660 // the firstOffset position on:
1661 if((**first
).GetType() == WXLO_TYPE_TEXT
&& firstOffset
!= 0)
1663 llist
->Insert(new wxLayoutObjectText(
1664 ((wxLayoutObjectText
*)*first
)->GetText().substr(firstOffset
))
1667 else if(firstOffset
== 0)
1668 llist
->Insert( (**first
).Copy() );
1669 // else nothing to copy :-(
1671 // Now we copy all objects before the last one:
1672 wxLOiterator i
= first
; i
++;
1673 for( ; i
!= last
; i
++)
1674 llist
->Insert( (**i
).Copy() );
1676 // And now the last object:
1679 if( (**last
).GetType() == WXLO_TYPE_TEXT
)
1681 llist
->Insert(new wxLayoutObjectText(
1682 ((wxLayoutObjectText
*)*last
)->GetText().substr(0,lastOffset
))
1686 llist
->Insert( (**last
).Copy() );
1691 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1693 The wxLayoutList object
1695 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1697 wxLayoutList::wxLayoutList()
1699 #ifdef WXLAYOUT_USE_CARET
1701 #endif // WXLAYOUT_USE_CARET
1705 SetAutoFormatting(TRUE
);
1706 ForceTotalLayout(TRUE
); // for the first time, do all
1707 InvalidateUpdateRect();
1711 wxLayoutList::~wxLayoutList()
1713 SetAutoFormatting(FALSE
);
1716 m_FirstLine
->DeleteLine(false, this);
1718 wxASSERT_MSG( m_numLines
== 0, wxT("line count calculation broken"));
1722 wxLayoutList::Empty(void)
1725 m_FirstLine
= m_FirstLine
->DeleteLine(false, this);
1727 m_CursorPos
= wxPoint(0,0);
1728 m_CursorScreenPos
= wxPoint(0,0);
1729 m_CursorSize
= wxPoint(0,0);
1730 m_movedCursor
= true;
1731 m_FirstLine
= new wxLayoutLine(NULL
, this); // empty first line
1732 m_CursorLine
= m_FirstLine
;
1733 InvalidateUpdateRect();
1738 wxLayoutList::InternalClear(void)
1740 m_Selection
.m_selecting
= false;
1741 m_Selection
.m_valid
= false;
1743 m_DefaultStyleInfo
.family
= wxSWISS
;
1744 m_DefaultStyleInfo
.size
= WXLO_DEFAULTFONTSIZE
;
1745 m_DefaultStyleInfo
.style
= wxNORMAL
;
1746 m_DefaultStyleInfo
.weight
= wxNORMAL
;
1747 m_DefaultStyleInfo
.underline
= 0;
1748 m_DefaultStyleInfo
.m_fg_valid
= TRUE
;
1749 m_DefaultStyleInfo
.m_fg
= *wxBLACK
;
1750 m_DefaultStyleInfo
.m_bg_valid
= TRUE
;
1751 m_DefaultStyleInfo
.m_bg
= *wxWHITE
;
1753 m_CurrentStyleInfo
= m_DefaultStyleInfo
;
1754 m_CursorStyleInfo
= m_DefaultStyleInfo
;
1758 wxLayoutList::Read(wxString
&istr
)
1760 /* In order to handle input of formatted string "nicely", we need
1761 to restore our current font settings after the string. So first
1762 of all, we create a StyleInfo structure with our current
1764 wxLayoutStyleInfo current_si
= GetStyleInfo();
1766 while(istr
.Length())
1768 // check for a linebreak:
1770 tmp
= istr
.BeforeFirst('\n');
1771 long l
= WXLO_TYPE_INVALID
;
1775 if(type
== WXLO_TYPE_LINEBREAK
)
1778 istr
= istr
.AfterFirst('\n');
1782 wxLayoutObject
*obj
= wxLayoutObject::Read(istr
);
1787 /* Now we use the current_si to restore our last font settings: */
1788 Insert(new wxLayoutObjectCmd(current_si
));
1793 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1794 int underline
, wxColour
*fg
,
1797 if(family
!= -1) m_CurrentStyleInfo
.family
= family
;
1798 if(size
!= -1) m_CurrentStyleInfo
.size
= size
;
1799 if(style
!= -1) m_CurrentStyleInfo
.style
= style
;
1800 if(weight
!= -1) m_CurrentStyleInfo
.weight
= weight
;
1801 if(underline
!= -1) m_CurrentStyleInfo
.underline
= underline
!= 0;
1802 if(fg
) m_CurrentStyleInfo
.m_fg
= *fg
;
1803 if(bg
) m_CurrentStyleInfo
.m_bg
= *bg
;
1805 new wxLayoutObjectCmd(
1806 m_CurrentStyleInfo
.family
,
1807 m_CurrentStyleInfo
.size
,
1808 m_CurrentStyleInfo
.style
,
1809 m_CurrentStyleInfo
.weight
,
1810 m_CurrentStyleInfo
.underline
,
1815 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1816 int underline
, char const *fg
, char const *bg
)
1824 cfg
= wxTheColourDatabase
->FindColour(fg
);
1826 cbg
= wxTheColourDatabase
->FindColour(bg
);
1828 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
1832 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
1833 int underline
, wxColour
*fg
, wxColour
*bg
)
1836 m_DefaultStyleInfo
= wxLayoutStyleInfo(family
, size
, style
, weight
,
1838 m_CurrentStyleInfo
= m_DefaultStyleInfo
;
1840 // Empty() should be called after we set m_DefaultStyleInfo because
1841 // otherwise the style info for the first line (created in Empty()) would be
1847 wxLayoutList::FindText(const wxString
&needle
, const wxPoint
&cpos
) const
1852 for(line
= m_FirstLine
;
1854 line
= line
->GetNextLine())
1856 if(line
->GetLineNumber() >= cpos
.y
)
1858 xpos
= line
->FindText(needle
,
1859 (line
->GetLineNumber() == cpos
.y
) ?
1862 return wxPoint(xpos
, line
->GetLineNumber());
1865 return wxPoint(-1,-1);
1870 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
1872 AddCursorPosToUpdateRect();
1874 wxPoint cursorPosOld
= m_CursorPos
;
1876 wxLayoutLine
*line
= m_FirstLine
;
1877 while(line
&& line
->GetLineNumber() != p
.y
)
1878 line
= line
->GetNextLine();
1879 if(line
&& line
->GetLineNumber() == p
.y
) // found it
1881 m_CursorPos
.y
= p
.y
;
1882 m_CursorLine
= line
;
1883 CoordType len
= line
->GetLength();
1886 m_CursorPos
.x
= p
.x
;
1890 m_CursorPos
.x
= len
;
1894 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1896 return m_CursorPos
== p
;
1900 wxLayoutList::MoveCursorVertically(int n
)
1902 AddCursorPosToUpdateRect();
1904 wxPoint cursorPosOld
= m_CursorPos
;
1907 if(n
< 0) // move up
1909 if(m_CursorLine
== m_FirstLine
) return false;
1910 while(n
< 0 && m_CursorLine
)
1912 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1918 m_CursorLine
= m_FirstLine
;
1924 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1925 m_CursorPos
.x
= m_CursorLine
->GetLength();
1931 wxLayoutLine
*last
= m_CursorLine
;
1932 if(! m_CursorLine
->GetNextLine()) return false;
1933 while(n
> 0 && m_CursorLine
)
1937 last
= m_CursorLine
;
1938 m_CursorLine
= m_CursorLine
->GetNextLine();
1942 m_CursorLine
= last
;
1948 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1949 m_CursorPos
.x
= m_CursorLine
->GetLength();
1954 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1960 wxLayoutList::MoveCursorHorizontally(int n
)
1962 AddCursorPosToUpdateRect();
1964 wxPoint cursorPosOld
= m_CursorPos
;
1969 if(m_CursorPos
.x
== 0) // at begin of line
1971 if(! MoveCursorVertically(-1))
1973 MoveCursorToEndOfLine();
1978 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1979 m_CursorPos
.x
-= move
; n
+= move
;
1984 int len
= m_CursorLine
->GetLength();
1985 if(m_CursorPos
.x
== len
) // at end of line
1987 if(! MoveCursorVertically(1))
1989 MoveCursorToBeginOfLine();
1994 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1995 m_CursorPos
.x
+= move
;
1999 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
2005 wxLayoutList::MoveCursorWord(int n
, bool untilNext
)
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 substracted 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
)
2216 for(wxLayoutLine
*line
= llist
->GetFirstLine();
2218 line
= line
->GetNextLine()
2221 for(wxLOiterator i
= line
->GetFirstObject();
2231 wxLayoutList::LineBreak(void)
2233 wxASSERT(m_CursorLine
);
2235 AddCursorPosToUpdateRect();
2237 wxPoint
position(m_CursorLine
->GetPosition());
2240 width
= m_CursorLine
->GetWidth(),
2241 height
= m_CursorLine
->GetHeight();
2243 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
, this);
2244 if(m_CursorLine
->GetPreviousLine() == NULL
)
2245 m_FirstLine
= m_CursorLine
;
2249 // The following code will produce a height which is guaranteed to
2250 // be too high: old lineheight + the height of both new lines.
2251 // We can probably drop the old line height and start with height =
2253 wxLayoutLine
*prev
= m_CursorLine
->GetPreviousLine();
2255 height
+= prev
->GetHeight();
2256 height
+= m_CursorLine
->GetHeight();
2258 m_movedCursor
= true;
2260 SetUpdateRect(position
);
2261 SetUpdateRect(position
.x
+ width
+ MSW_CORRECTION
,
2262 position
.y
+ height
+ MSW_CORRECTION
);
2268 wxLayoutList::WrapLine(CoordType column
)
2270 return m_CursorLine
->Wrap(column
, this);
2274 wxLayoutList::WrapAll(CoordType column
)
2276 wxLayoutLine
*line
= m_FirstLine
;
2282 rc
&= line
->Wrap(column
, this);
2283 line
= line
->GetNextLine();
2289 wxLayoutList::Delete(CoordType npos
)
2291 wxCHECK_MSG(m_CursorLine
, false, wxT("can't delete in non existing line"));
2296 AddCursorPosToUpdateRect();
2298 // were other lines appended to this one (this is important to know because
2299 // this means that our width _increased_ as the result of deletion)
2300 bool wasMerged
= false;
2302 // the size of the region to update
2303 CoordType totalHeight
= m_CursorLine
->GetHeight(),
2304 totalWidth
= m_CursorLine
->GetWidth();
2309 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
2313 // More to delete, continue on next line.
2315 // First, check if line is empty:
2316 if(m_CursorLine
->GetLength() == 0)
2318 // in this case, updating could probably be optimised
2320 wxASSERT(DeleteLines(1) == 0);
2329 // Need to join next line
2330 if(! m_CursorLine
->GetNextLine())
2335 wxLayoutLine
*next
= m_CursorLine
->GetNextLine();
2338 totalHeight
+= next
->GetHeight();
2339 totalWidth
+= next
->GetWidth();
2341 m_CursorLine
->MergeNextLine(this);
2346 wxFAIL_MSG(wxT("can't delete all this"));
2356 // we need to update the whole tail of the line and the lines which
2360 wxPoint
position(m_CursorLine
->GetPosition());
2361 SetUpdateRect(position
);
2362 SetUpdateRect(position
.x
+ totalWidth
+ MSW_CORRECTION
,
2363 position
.y
+ totalHeight
+ MSW_CORRECTION
);
2370 wxLayoutList::DeleteLines(int n
)
2372 wxASSERT(m_CursorLine
);
2375 AddCursorPosToUpdateRect();
2379 if(!m_CursorLine
->GetNextLine())
2380 { // we cannot delete this line, but we can clear it
2381 MoveCursorToBeginOfLine();
2382 DeleteToEndOfLine();
2384 m_CursorLine
->MarkDirty();
2388 line
= m_CursorLine
;
2389 m_CursorLine
= m_CursorLine
->DeleteLine(true, this);
2391 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
2392 wxASSERT(m_FirstLine
);
2393 wxASSERT(m_CursorLine
);
2396 m_CursorLine
->MarkDirty();
2401 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
)
2405 wxLayoutLine
*line
= m_FirstLine
;
2407 // first, make sure everything is calculated - this might not be
2408 // needed, optimise it later
2409 ApplyStyle(m_DefaultStyleInfo
, dc
);
2412 line
->RecalculatePosition(this); // so we don't need to do it all the time
2413 // little condition to speed up redrawing:
2414 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
2415 line
= line
->GetNextLine();
2420 wxLayoutList::GetCursorScreenPos(void) const
2422 return m_CursorScreenPos
;
2426 Is called before each Draw(). Now, it will re-layout all lines which
2430 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
, bool forceAll
,
2431 wxPoint
*cpos
, wxPoint
*csize
)
2433 // first, make sure everything is calculated - this might not be
2434 // needed, optimise it later
2435 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
;
2494 cursorReached
= TRUE
;
2498 if(cpos
&& line
->GetLineNumber() == cpos
->y
)
2500 line
->Layout(dc
, this,
2502 csize
, NULL
, cpos
->x
);
2503 cursorReached
= TRUE
;
2506 line
->Layout(dc
, this);
2509 line
= line
->GetNextLine();
2512 #ifndef WXLAYOUT_USE_CARET
2513 // can only be 0 if we are on the first line and have no next line
2514 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
2515 m_CursorLine
->GetNextLine() == NULL
&&
2516 m_CursorLine
== m_FirstLine
));
2517 #endif // WXLAYOUT_USE_CARET
2518 AddCursorPosToUpdateRect();
2522 wxLayoutList::GetScreenPos(wxDC
&dc
, const wxPoint
&cpos
, wxPoint
*csize
)
2525 Layout(dc
, -1, false, &pos
, csize
);
2530 wxLayoutList::Draw(wxDC
&dc
,
2531 wxPoint
const &offset
,
2536 wxLayoutLine
*line
= m_FirstLine
;
2538 if ( m_Selection
.m_discarded
)
2540 // calculate them if we don't have them already
2541 if ( !m_Selection
.HasValidScreenCoords() )
2543 m_Selection
.m_ScreenA
= GetScreenPos(dc
, m_Selection
.m_CursorA
);
2544 m_Selection
.m_ScreenB
= GetScreenPos(dc
, m_Selection
.m_CursorB
);
2547 // invalidate the area which was previousle selected - and which is not
2548 // selected any more
2549 SetUpdateRect(m_Selection
.m_ScreenA
);
2550 SetUpdateRect(m_Selection
.m_ScreenB
);
2552 m_Selection
.m_discarded
= false;
2555 /* This call to Layout() will re-calculate and update all lines
2560 ApplyStyle(m_DefaultStyleInfo
, dc
);
2561 wxBrush
brush(m_CurrentStyleInfo
.m_bg
, wxSOLID
);
2563 dc
.SetBackgroundMode(wxTRANSPARENT
);
2567 // only draw if between top and bottom:
2569 line
->GetPosition().y
+ line
->GetHeight() > top
))
2571 ApplyStyle(line
->GetStyleInfo(), dc
);
2572 // little condition to speed up redrawing:
2574 && line
->GetPosition().y
2575 +(clipStrictly
? line
->GetHeight() : 0) >= bottom
)
2577 line
->Draw(dc
, this, offset
);
2579 line
= line
->GetNextLine();
2581 InvalidateUpdateRect();
2583 WXLO_DEBUG((wxT("Selection is %s : %ld,%ld/%ld,%ld"),
2584 m_Selection
.m_valid
? wxT("valid") : wxT("invalid"),
2585 m_Selection
.m_CursorA
.x
, m_Selection
.m_CursorA
.y
,
2586 m_Selection
.m_CursorB
.x
, m_Selection
.m_CursorB
.y
));
2590 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
,
2594 // First, find the right line:
2596 *line
= m_FirstLine
,
2597 *lastline
= m_FirstLine
;
2600 ApplyStyle(m_DefaultStyleInfo
, dc
);
2603 p
= line
->GetPosition();
2604 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
2607 line
= line
->GetNextLine();
2610 bool didFind
= line
!= NULL
;
2614 // use the last line:
2619 cursorPos
->y
= line
->GetLineNumber();
2621 bool foundinline
= true;
2624 // Now, find the object in the line:
2629 i
= line
->FindObjectScreen(dc
, this,
2636 i
= line
->FindObjectScreen(dc
, this,
2641 *found
= didFind
&& foundinline
;
2643 return (i
== NULLIT
) ? NULL
: *i
;
2648 wxLayoutList::GetSize(void) const
2651 *line
= m_FirstLine
,
2654 return wxPoint(0,0);
2656 wxPoint
maxPoint(0,0);
2661 if(line
->GetWidth() > maxPoint
.x
)
2662 maxPoint
.x
= line
->GetWidth();
2664 line
= line
->GetNextLine();
2667 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
2669 // if the line was just added, its height would be 0 and we can't call
2670 // Layout() from here because we don't have a dc and we might be not drawing
2671 // at all, besides... So take the cursor height by default (taking 0 is bad
2672 // because then the scrollbars won't be resized and the new line won't be
2674 if ( last
->IsDirty() )
2676 if ( last
->GetHeight() == 0 )
2677 maxPoint
.y
+= m_CursorSize
.y
;
2678 if ( last
->GetWidth() == 0 && maxPoint
.x
< m_CursorSize
.x
)
2679 maxPoint
.x
= m_CursorSize
.x
;
2687 wxLayoutList::DrawCursor(wxDC
&dc
, bool active
, wxPoint
const &translate
)
2689 if ( m_movedCursor
)
2690 m_movedCursor
= false;
2692 wxPoint
coords(m_CursorScreenPos
);
2693 coords
+= translate
;
2695 #ifdef WXLAYOUT_DEBUG
2696 WXLO_DEBUG((wxT("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld"),
2697 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
2698 (long)coords
.x
, (long)coords
.y
,
2699 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
2700 (long)m_CursorLine
->GetLineNumber(),
2701 (long)m_CursorLine
->GetLength()));
2703 wxLogStatus(wxT("Cursor is at (%d, %d)"), m_CursorPos
.x
, m_CursorPos
.y
);
2706 #ifdef WXLAYOUT_USE_CARET
2707 m_caret
->Move(coords
);
2708 #else // !WXLAYOUT_USE_CARET
2710 wxASSERT(m_CursorSize
.x
>= WXLO_MINIMUM_CURSOR_WIDTH
);
2711 dc
.SetBrush(*wxWHITE_BRUSH
);
2712 //FIXME: wxGTK XOR is borken at the moment!!!dc.SetLogicalFunction(wxXOR);
2713 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
2716 dc
.SetLogicalFunction(wxXOR
);
2717 dc
.DrawRectangle(coords
.x
, coords
.y
,
2718 m_CursorSize
.x
, m_CursorSize
.y
);
2719 SetUpdateRect(coords
.x
, coords
.y
);
2720 SetUpdateRect(coords
.x
+m_CursorSize
.x
,
2721 coords
.y
+m_CursorSize
.y
);
2725 dc
.SetLogicalFunction(wxCOPY
);
2726 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
2727 coords
.x
, coords
.y
);
2728 SetUpdateRect(coords
.x
, coords
.y
+m_CursorSize
.y
-1);
2729 SetUpdateRect(coords
.x
, coords
.y
);
2731 dc
.SetLogicalFunction(wxCOPY
);
2732 //dc.SetBrush(wxNullBrush);
2733 #endif // WXLAYOUT_USE_CARET/!WXLAYOUT_USE_CARET
2737 wxLayoutList::SetUpdateRect(CoordType x
, CoordType y
)
2739 if(m_UpdateRectValid
)
2740 GrowRect(m_UpdateRect
, x
, y
);
2745 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
2746 m_UpdateRect
.height
= 4;// wxGTK :-)
2747 m_UpdateRectValid
= true;
2752 wxLayoutList::StartSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2754 wxPoint
cpos(cposOrig
);
2757 WXLO_DEBUG((wxT("Starting selection at %ld/%ld"), cpos
.x
, cpos
.y
));
2758 m_Selection
.m_CursorA
= cpos
;
2759 m_Selection
.m_CursorB
= cpos
;
2760 m_Selection
.m_ScreenA
= spos
;
2761 m_Selection
.m_ScreenB
= spos
;
2762 m_Selection
.m_selecting
= true;
2763 m_Selection
.m_valid
= false;
2767 wxLayoutList::ContinueSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2769 wxPoint
cpos(cposOrig
);
2773 wxASSERT(m_Selection
.m_selecting
== true);
2774 wxASSERT(m_Selection
.m_valid
== false);
2775 WXLO_DEBUG((wxT("Continuing selection at %ld/%ld"), cpos
.x
, cpos
.y
));
2777 m_Selection
.m_ScreenB
= spos
;
2778 m_Selection
.m_CursorB
= cpos
;
2782 wxLayoutList::EndSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2784 wxPoint
cpos(cposOrig
);
2785 if(cpos
.x
== -1) cpos
= m_CursorPos
;
2786 ContinueSelection(cpos
, spos
);
2787 WXLO_DEBUG((wxT("Ending selection at %ld/%ld"), cpos
.x
, cpos
.y
));
2788 // we always want m_CursorA <= m_CursorB!
2789 if( m_Selection
.m_CursorA
> m_Selection
.m_CursorB
)
2791 // exchange the start/end points
2792 wxPoint help
= m_Selection
.m_CursorB
;
2793 m_Selection
.m_CursorB
= m_Selection
.m_CursorA
;
2794 m_Selection
.m_CursorA
= help
;
2796 help
= m_Selection
.m_ScreenB
;
2797 m_Selection
.m_ScreenB
= m_Selection
.m_ScreenA
;
2798 m_Selection
.m_ScreenA
= help
;
2800 m_Selection
.m_selecting
= false;
2801 m_Selection
.m_valid
= true;
2802 /// In case we just clicked somewhere, the selection will have zero
2803 /// size, so we discard it immediately.
2804 if(m_Selection
.m_CursorA
== m_Selection
.m_CursorB
)
2809 wxLayoutList::DiscardSelection()
2811 if ( !HasSelection() )
2814 m_Selection
.m_valid
=
2815 m_Selection
.m_selecting
= false;
2816 m_Selection
.m_discarded
= true;
2820 wxLayoutList::IsSelecting(void) const
2822 return m_Selection
.m_selecting
;
2826 wxLayoutList::IsSelected(const wxPoint
&cursor
) const
2828 if ( !HasSelection() )
2832 (m_Selection
.m_CursorA
<= cursor
2833 && cursor
<= m_Selection
.m_CursorB
)
2834 || (m_Selection
.m_CursorB
<= cursor
2835 && cursor
<= m_Selection
.m_CursorA
)
2840 /** Tests whether this layout line is selected and needs
2842 @param line to test for
2843 @return 0 = not selected, 1 = fully selected, -1 = partially
2847 wxLayoutList::IsSelected(const wxLayoutLine
*line
, CoordType
*from
,
2850 wxASSERT(line
); wxASSERT(to
); wxASSERT(from
);
2852 if(! m_Selection
.m_valid
&& ! m_Selection
.m_selecting
)
2855 CoordType y
= line
->GetLineNumber();
2857 (m_Selection
.m_CursorA
.y
< y
&& m_Selection
.m_CursorB
.y
> y
)
2858 || (m_Selection
.m_CursorB
.y
< y
&& m_Selection
.m_CursorA
.y
> y
)
2861 else if(m_Selection
.m_CursorA
.y
== y
)
2863 *from
= m_Selection
.m_CursorA
.x
;
2864 if(m_Selection
.m_CursorB
.y
== y
)
2865 *to
= m_Selection
.m_CursorB
.x
;
2868 if(m_Selection
.m_CursorB
> m_Selection
.m_CursorA
)
2869 *to
= line
->GetLength();
2875 CoordType help
= *to
;
2881 else if(m_Selection
.m_CursorB
.y
== y
)
2883 *to
= m_Selection
.m_CursorB
.x
;
2884 if(m_Selection
.m_CursorA
.y
== y
)
2885 *from
= m_Selection
.m_CursorA
.x
;
2888 if(m_Selection
.m_CursorB
> m_Selection
.m_CursorA
)
2891 *from
= line
->GetLength();
2895 CoordType help
= *to
;
2906 wxLayoutList::DeleteSelection(void)
2908 if(! m_Selection
.m_valid
)
2911 m_Selection
.m_valid
= false;
2913 // Only delete part of the current line?
2914 if(m_Selection
.m_CursorA
.y
== m_Selection
.m_CursorB
.y
)
2916 MoveCursorTo(m_Selection
.m_CursorA
);
2917 Delete(m_Selection
.m_CursorB
.x
- m_Selection
.m_CursorA
.x
);
2921 // We now know that the two lines are different:
2924 * firstLine
= GetLine(m_Selection
.m_CursorA
.y
),
2925 * lastLine
= GetLine(m_Selection
.m_CursorB
.y
);
2926 // be a bit paranoid:
2927 if(! firstLine
|| ! lastLine
)
2930 // First, delete what's left of this line:
2931 MoveCursorTo(m_Selection
.m_CursorA
);
2932 DeleteToEndOfLine();
2934 wxLayoutLine
*prevLine
= firstLine
->GetPreviousLine(),
2935 *nextLine
= firstLine
->GetNextLine();
2936 while(nextLine
&& nextLine
!= lastLine
)
2937 nextLine
= nextLine
->DeleteLine(false, this);
2939 // Now nextLine = lastLine;
2940 Delete(1); // This joins firstLine and nextLine
2941 Delete(m_Selection
.m_CursorB
.x
); // This deletes the first x positions
2943 // Recalculate the line positions and numbers but notice that firstLine
2944 // might not exist any more - it could be deleted by Delete(1) above
2945 wxLayoutLine
*firstLine2
= prevLine
? prevLine
->GetNextLine() : m_FirstLine
;
2946 firstLine2
->MarkDirty();
2949 /// Starts highlighting the selection
2951 wxLayoutList::StartHighlighting(wxDC
&dc
)
2954 dc
.SetTextForeground(m_CurrentStyleInfo
.m_bg
);
2955 dc
.SetTextBackground(m_CurrentStyleInfo
.m_fg
);
2956 dc
.SetBackgroundMode(wxSOLID
);
2960 /// Ends highlighting the selection
2962 wxLayoutList::EndHighlighting(wxDC
&dc
)
2965 dc
.SetTextForeground(m_CurrentStyleInfo
.m_fg
);
2966 dc
.SetTextBackground(m_CurrentStyleInfo
.m_bg
);
2967 dc
.SetBackgroundMode(wxTRANSPARENT
);
2973 wxLayoutList::GetLine(CoordType index
) const
2975 wxASSERT_MSG( (0 <= index
) && (index
< (CoordType
)m_numLines
),
2976 wxT("invalid index") );
2979 CoordType n
= index
;
2981 CoordType lineNo
= 0;
2984 for ( line
= m_FirstLine
; line
&& n
-- > 0; line
=
2985 line
->GetNextLine() )
2988 wxASSERT(line
->GetLineNumber() == lineNo
);
2995 // should be the right one
2996 wxASSERT( line
->GetLineNumber() == index
);
3004 wxLayoutList::Copy(const wxPoint
&from
,
3011 for(firstLine
= m_FirstLine
;
3012 firstLine
&& firstLine
->GetLineNumber() < from
.y
;
3013 firstLine
=firstLine
->GetNextLine())
3015 if(!firstLine
|| firstLine
->GetLineNumber() != from
.y
)
3018 for(lastLine
= m_FirstLine
;
3019 lastLine
&& lastLine
->GetLineNumber() < to
.y
;
3020 lastLine
=lastLine
->GetNextLine())
3022 if(!lastLine
|| lastLine
->GetLineNumber() != to
.y
)
3027 wxLayoutLine
*tmp
= firstLine
;
3028 firstLine
= lastLine
;
3032 wxLayoutList
*llist
= new wxLayoutList();
3034 if(firstLine
== lastLine
)
3036 firstLine
->Copy(llist
, from
.x
, to
.x
);
3040 // Extract objects from first line
3041 firstLine
->Copy(llist
, from
.x
);
3043 // Extract all lines between
3044 for(wxLayoutLine
*line
= firstLine
->GetNextLine();
3046 line
= line
->GetNextLine())
3051 // Extract objects from last line
3052 lastLine
->Copy(llist
, 0, to
.x
);
3058 wxLayoutList::GetSelection(wxLayoutDataObject
*wxlo
, bool invalidate
)
3060 if(! m_Selection
.m_valid
)
3062 if(m_Selection
.m_selecting
)
3068 if(invalidate
) m_Selection
.m_valid
= false;
3070 wxLayoutList
*llist
= Copy( m_Selection
.m_CursorA
,
3071 m_Selection
.m_CursorB
);
3073 if(llist
&& wxlo
) // export as data object, too
3077 wxLayoutExportObject
*exp
;
3078 wxLayoutExportStatus
status(llist
);
3079 while((exp
= wxLayoutExport( &status
, WXLO_EXPORT_AS_OBJECTS
)) != NULL
)
3081 if(exp
->type
== WXLO_EXPORT_EMPTYLINE
)
3082 string
<< (int) WXLO_TYPE_LINEBREAK
<< '\n';
3084 exp
->content
.object
->Write(string
);
3087 wxlo
->SetLayoutData(string
);
3094 #define COPY_SI(what) if(si.what != -1) { m_CurrentStyleInfo.what = si.what; fontChanged = TRUE; }
3097 wxLayoutList::ApplyStyle(wxLayoutStyleInfo
const &si
, wxDC
&dc
)
3099 bool fontChanged
= FALSE
;
3106 dc
.SetFont( m_FontCache
.GetFont(m_CurrentStyleInfo
) );
3110 m_CurrentStyleInfo
.m_fg
= si
.m_fg
;
3111 m_CurrentStyleInfo
.m_fg_valid
= true;
3112 dc
.SetTextForeground(m_CurrentStyleInfo
.m_fg
);
3116 m_CurrentStyleInfo
.m_bg
= si
.m_bg
;
3117 m_CurrentStyleInfo
.m_bg_valid
= true;
3118 dc
.SetTextBackground(m_CurrentStyleInfo
.m_bg
);
3123 #ifdef WXLAYOUT_DEBUG
3126 wxLayoutList::Debug(void)
3128 WXLO_DEBUG((wxT("Cursor is in line %d, screen pos = (%d, %d)"),
3129 m_CursorLine
->GetLineNumber(),
3130 m_CursorScreenPos
.x
, m_CursorScreenPos
.y
));
3133 for(line
= m_FirstLine
; line
; line
= line
->GetNextLine())
3142 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3146 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
3148 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
3149 wxString
const & title
)
3154 // remove any highlighting which could interfere with printing:
3155 m_llist
->StartSelection();
3156 m_llist
->EndSelection();
3157 // force a full layout of the list:
3158 m_llist
->ForceTotalLayout();
3159 // layout is called in ScaleDC() when we have a DC
3162 wxLayoutPrintout::~wxLayoutPrintout()
3167 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
3169 // The following bit is taken from the printing sample, let's see
3170 // whether it works for us.
3172 /* You might use THIS code to set the printer DC to ROUGHLY reflect
3173 * the screen text size. This page also draws lines of actual length 5cm
3176 // Get the logical pixels per inch of screen and printer
3177 int ppiScreenX
, ppiScreenY
;
3178 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
3179 int ppiPrinterX
, ppiPrinterY
;
3180 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
3182 if(ppiScreenX
== 0) // not yet set, need to guess
3187 if(ppiPrinterX
== 0) // not yet set, need to guess
3193 // This scales the DC so that the printout roughly represents the
3194 // the screen scaling. The text point size _should_ be the right size
3195 // but in fact is too small for some reason. This is a detail that will
3196 // need to be addressed at some point but can be fudged for the
3198 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
3200 // Now we have to check in case our real page size is reduced
3201 // (e.g. because we're drawing to a print preview memory DC)
3202 int pageWidth
, pageHeight
;
3204 dc
->GetSize(&w
, &h
);
3205 GetPageSizePixels(&pageWidth
, &pageHeight
);
3206 if(pageWidth
!= 0) // doesn't work always
3208 // If printer pageWidth == current DC width, then this doesn't
3209 // change. But w might be the preview bitmap width, so scale down.
3210 scale
= scale
* (float)(w
/(float)pageWidth
);
3212 dc
->SetUserScale(scale
, scale
);
3216 bool wxLayoutPrintout::OnPrintPage(int page
)
3225 top
= (page
- 1)*m_PrintoutHeight
;
3226 bottom
= top
+ m_PrintoutHeight
;
3228 WXLO_DEBUG((wxT("OnPrintPage(%d) printing from %d to %d"), page
, top
,
3230 // SetDeviceOrigin() doesn't work here, so we need to manually
3231 // translate all coordinates.
3232 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
3233 m_llist
->Draw(*dc
, translate
, top
, bottom
, TRUE
/* clip strictly
3241 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
3243 /* We allocate a temporary wxDC for printing, so that we can
3244 determine the correct paper size and scaling. We don't actually
3245 print anything on it. */
3246 #if defined(__WXMSW__)
3247 wxPrinterDC
*psdc
= new wxPrinterDC("","",WXLLIST_TEMPFILE
,false);
3249 wxPostScriptDC
*psdc
= new wxPostScriptDC(WXLLIST_TEMPFILE
,false);
3252 psdc
->StartDoc(m_title
);
3253 // before we draw anything, me must make sure the list is properly
3255 m_llist
->Layout(*psdc
);
3257 float scale
= ScaleDC(psdc
);
3259 psdc
->GetSize(&m_PageWidth
, &m_PageHeight
);
3261 // This sets a left/top origin of 15% and 5%:
3262 m_Offset
= wxPoint((15*m_PageWidth
)/100, (5*m_PageHeight
)/100);
3264 // This is the length of the printable area.
3265 m_PrintoutHeight
= m_PageHeight
- 2*m_Offset
.y
;
3266 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
3269 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
3272 *maxPage
= m_NumOfPages
;
3275 *selPageTo
= m_NumOfPages
;
3278 wxRemoveFile(WXLLIST_TEMPFILE
);
3281 bool wxLayoutPrintout::HasPage(int pageNum
)
3283 return pageNum
<= m_NumOfPages
;
3287 Stupid wxWindows doesn't draw proper ellipses, so we comment this
3288 out. It's a waste of paper anyway.
3292 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
3293 wxPoint topleft
, wxPoint bottomright
,
3296 // make backups of all essential parameters
3297 const wxBrush
& brush
= dc
.GetBrush();
3298 const wxPen
& pen
= dc
.GetPen();
3299 const wxFont
& font
= dc
.GetFont();
3301 dc
.SetBrush(*wxWHITE_BRUSH
);
3302 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
3303 dc
.DrawRoundedRectangle(topleft
.x
,
3304 topleft
.y
,bottomright
.x
-topleft
.x
,
3305 bottomright
.y
-topleft
.y
);
3306 dc
.SetBrush(*wxBLACK_BRUSH
);
3307 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
3308 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
3312 page
= "9999/9999 "; // many pages...
3314 dc
.GetTextExtent(page
,&w
,&h
);
3315 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
3316 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
3317 dc
.GetTextExtent("XXXX", &w
,&h
);
3318 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);
3329 wxFontCache::GetFont(int family
, int size
, int style
, int weight
,
3332 for(wxFCEList::iterator i
= m_FontList
.begin();
3333 i
!= m_FontList
.end(); i
++)
3334 if( (**i
).Matches(family
, size
, style
, weight
, underline
) )
3335 return (**i
).GetFont();
3337 wxFontCacheEntry
*fce
= new wxFontCacheEntry(family
, size
, style
,
3339 m_FontList
.push_back(fce
);
3340 return fce
->GetFont();