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 subtract 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();
739 wxLayoutLine::RecalculatePosition(wxLayoutList
*llist
)
741 wxASSERT(m_Previous
|| GetLineNumber() == 0);
743 wxPoint
posOld(m_Position
);
747 m_Position
= m_Previous
->GetPosition();
748 m_Position
.y
+= m_Previous
->GetHeight();
751 m_Position
= wxPoint(0,0);
753 if ( m_Position
!= posOld
)
755 // the whole line moved and must be repainted
756 llist
->SetUpdateRect(m_Position
);
757 llist
->SetUpdateRect(m_Position
.x
+ GetWidth() + MSW_CORRECTION
,
758 m_Position
.y
+ GetHeight() + MSW_CORRECTION
);
759 llist
->SetUpdateRect(posOld
);
760 llist
->SetUpdateRect(posOld
.x
+ GetWidth() + MSW_CORRECTION
,
761 posOld
.y
+ GetHeight() + MSW_CORRECTION
);
768 wxLayoutObjectList::iterator
769 wxLayoutLine::FindObject(CoordType xpos
, CoordType
*offset
) const
773 wxLayoutObjectList::iterator
777 CoordType x
= 0, len
;
779 /* We search through the objects. As we don't like returning the
780 object that the cursor is behind, we just remember such an
781 object in "found" so we can return it if there is really no
782 further object following it. */
783 for(i
= m_ObjectList
.begin(); i
!= nulled
; i
++)
785 len
= (**i
).GetLength();
786 if( x
<= xpos
&& xpos
<= x
+ len
)
789 if(xpos
== x
+ len
) // is there another object behind?
791 else // we are really inside this object
794 x
+= (**i
).GetLength();
796 return found
; // ==NULL if really none found
799 wxLayoutObjectList::iterator
800 wxLayoutLine::FindObjectScreen(wxDC
&dc
, wxLayoutList
*llist
,
801 CoordType xpos
, CoordType
*cxpos
,
806 llist
->ApplyStyle(GetStyleInfo(), dc
);
808 wxLayoutObjectList::iterator i
, nulled(NULL
);
809 CoordType x
= 0, cx
= 0, width
;
811 for(i
= m_ObjectList
.begin(); i
!= nulled
; i
++)
813 wxLayoutObject
*obj
= *i
;
814 if ( obj
->GetType() == WXLO_TYPE_CMD
)
816 // this will set the correct font for the objects which follow
817 obj
->Layout(dc
, llist
);
820 width
= obj
->GetWidth();
821 if( x
<= xpos
&& xpos
<= x
+ width
)
823 *cxpos
= cx
+ obj
->GetOffsetScreen(dc
, xpos
-x
);
830 x
+= obj
->GetWidth();
831 cx
+= obj
->GetLength();
834 // behind last object:
839 return m_ObjectList
.tail();
842 /** Finds text in this line.
843 @param needle the text to find
844 @param xpos the position where to start the search
845 @return the cursoor coord where it was found or -1
848 wxLayoutLine::FindText(const wxString
&needle
, CoordType xpos
) const
851 wxString
const *text
;
853 for(wxLOiterator i
= m_ObjectList
.begin(); i
!= m_ObjectList
.end(); i
++)
855 if(cpos
>= xpos
) // search from here!
857 if((**i
).GetType() == WXLO_TYPE_TEXT
)
859 text
= & ((wxLayoutObjectText
*)(*i
))->GetText();
860 int relpos
= text
->Find(needle
);
861 if(relpos
>= cpos
-xpos
) // -1 if not found
866 cpos
+= (**i
).GetLength();
869 return -1; // not found
873 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
876 wxASSERT(obj
!= NULL
);
881 wxLOiterator i
= FindObject(xpos
, &offset
);
882 wxLayoutObjectList::iterator
nulled(NULL
);
885 if(xpos
== 0 ) // aha, empty line!
887 m_ObjectList
.push_back(obj
);
888 m_Length
+= obj
->GetLength();
895 CoordType len
= (**i
).GetLength();
896 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
897 { // insert before this object
898 m_ObjectList
.insert(i
,obj
);
899 m_Length
+= obj
->GetLength();
904 if( i
== m_ObjectList
.tail()) // last object?
905 m_ObjectList
.push_back(obj
);
907 { // insert after current object
909 m_ObjectList
.insert(i
,obj
);
911 m_Length
+= obj
->GetLength();
914 /* Otherwise we need to split the current object.
915 Fortunately this can only be a text object. */
916 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
917 wxString left
, right
;
918 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
919 left
= tobj
->GetText().substr(0,offset
);
920 right
= tobj
->GetText().substr(offset
,len
-offset
);
921 // current text object gets set to right half
922 tobj
->GetText() = right
; // set new text
923 // before it we insert the new object
924 m_ObjectList
.insert(i
,obj
);
925 m_Length
+= obj
->GetLength();
926 // and before that we insert the left half
927 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
932 wxLayoutLine::Insert(CoordType xpos
, const wxString
& text
)
939 wxLOiterator i
= FindObject(xpos
, &offset
);
940 wxLayoutObjectList::iterator
nulled(NULL
);
941 if(i
!= nulled
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
943 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
944 tobj
->GetText().insert(offset
, text
);
945 m_Length
+= text
.Length();
949 if ( !Insert(xpos
, new wxLayoutObjectText(text
)) )
957 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
959 CoordType offset
, len
;
964 wxLOiterator i
= FindObject(xpos
, &offset
);
965 wxLayoutObjectList::iterator
nulled(NULL
);
968 if(i
== nulled
) return npos
;
969 // now delete from that object:
970 if((**i
).GetType() != WXLO_TYPE_TEXT
)
972 if(offset
!= 0) // at end of line after a non-text object
975 len
= (**i
).GetLength();
978 m_ObjectList
.erase(i
);
982 // tidy up: remove empty text objects
983 if((**i
).GetLength() == 0)
985 m_ObjectList
.erase(i
);
989 CoordType max
= (**i
).GetLength() - offset
;
990 if(npos
< max
) max
= npos
;
993 if(xpos
== GetLength())
996 { // at the end of an object
997 // move to begin of next object:
999 continue; // start over
1004 if(offset
== 0 && max
== (**i
).GetLength())
1005 m_ObjectList
.erase(i
); // remove the whole object
1007 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
1015 wxLayoutLine::DeleteWord(CoordType xpos
)
1017 wxASSERT(xpos
>= 0);
1021 wxLOiterator i
= FindObject(xpos
, &offset
);
1022 wxLayoutObjectList::iterator
nulled(NULL
);
1025 if(i
== nulled
) return false;
1026 if((**i
).GetType() != WXLO_TYPE_TEXT
)
1028 // This should only happen when at end of line, behind a non-text
1030 if(offset
== (**i
).GetLength()) return false;
1031 m_Length
-= (**i
).GetLength(); // -1
1032 m_ObjectList
.erase(i
);
1033 return true; // we are done
1037 if(offset
== (**i
).GetLength()) // at end of object
1043 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
1045 wxString str
= tobj
->GetText();
1046 str
= str
.substr(offset
,str
.Length()-offset
);
1047 // Find out how many positions we need to delete:
1048 // 1. eat leading space
1049 while(isspace(str
.c_str()[count
])) count
++;
1050 // 2. eat the word itself:
1051 while(isalnum(str
.c_str()[count
])) count
++;
1053 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
1054 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
1062 wxFAIL_MSG(wxT("unreachable"));
1067 wxLayoutLine::DeleteLine(bool update
, wxLayoutList
*llist
)
1069 // maintain linked list integrity
1071 m_Next
->m_Previous
= m_Previous
;
1073 m_Previous
->m_Next
= m_Next
;
1075 // get the line numbers right again
1076 if ( update
&& m_Next
)
1081 // we can't use m_Next after "delete this", so we must save this pointer
1083 wxLayoutLine
*next
= m_Next
;
1086 llist
->DecNumLines();
1092 wxLayoutLine::Draw(wxDC
&dc
,
1093 wxLayoutList
*llist
,
1094 const wxPoint
& offset
) const
1096 wxLayoutObjectList::iterator i
, nulled(NULL
);
1097 wxPoint pos
= offset
;
1098 pos
= pos
+ GetPosition();
1100 pos
.y
+= m_BaseLine
;
1102 CoordType xpos
= 0; // cursorpos, length of line
1106 int highlight
= llist
->IsSelected(this, &from
, &to
);
1107 // WXLO_DEBUG(("highlight=%d", highlight ));
1108 if(highlight
== 1) // we need to draw the whole line inverted!
1109 llist
->StartHighlighting(dc
);
1111 llist
->EndHighlighting(dc
);
1113 for(i
= m_ObjectList
.begin(); i
!= nulled
; i
++)
1115 if(highlight
== -1) // partially highlight line
1117 // parts of the line need highlighting
1119 // Next line commented, code has no effect
1120 // xpos+(**i).GetLength();
1121 (**i
).Draw(dc
, pos
, llist
, from
-xpos
, to
-xpos
);
1124 (**i
).Draw(dc
, pos
, llist
);
1125 pos
.x
+= (**i
).GetWidth();
1126 xpos
+= (**i
).GetLength();
1131 This function does all the recalculation, that is, it should only be
1132 called from within wxLayoutList::Layout(), as it uses the current
1133 list's styleinfo and updates it.
1136 wxLayoutLine::Layout(wxDC
&dc
,
1137 wxLayoutList
*llist
,
1139 wxPoint
*cursorSize
,
1140 wxLayoutStyleInfo
*cursorStyle
,
1142 bool WXUNUSED(suppressSIupdate
))
1144 wxLayoutObjectList::iterator i
, nulled(NULL
);
1146 // when a line becomes dirty, we redraw it from the place where it was
1147 // changed till the end of line (because the following wxLayoutObjects are
1148 // moved when the preceding one changes) - calculate the update rectangle.
1149 CoordType updateTop
= m_Position
.y
,
1151 updateWidth
= m_Width
,
1152 updateHeight
= m_Height
;
1156 bottomHeight
= 0; // above and below baseline
1158 objTopHeight
, objBottomHeight
; // above and below baseline
1162 CoordType heightOld
= m_Height
;
1168 bool cursorFound
= false;
1170 RecalculatePosition(llist
);
1174 *cursorPos
= m_Position
;
1175 if(cursorSize
) *cursorSize
= wxPoint(0,0);
1178 m_StyleInfo
= llist
->GetStyleInfo(); // save current style
1179 for(i
= m_ObjectList
.begin(); i
!= nulled
; i
++)
1181 wxLayoutObject
*obj
= *i
;
1182 obj
->Layout(dc
, llist
);
1183 wxPoint sizeObj
= obj
->GetSize(&objTopHeight
, &objBottomHeight
);
1185 if(cursorPos
&& ! cursorFound
)
1187 // we need to check whether the text cursor is here
1188 len
= obj
->GetLength();
1189 if(count
<= cx
&& count
+len
> cx
)
1191 if(obj
->GetType() == WXLO_TYPE_TEXT
)
1193 len
= cx
- count
; // pos in object
1194 CoordType width
, height
, descent
;
1195 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
1196 &width
, &height
, &descent
);
1197 cursorPos
->x
+= width
;
1198 cursorPos
->y
= m_Position
.y
;
1200 if(len
< obj
->GetLength())
1201 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
1203 str
= _T(WXLO_CURSORCHAR
);
1204 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
1206 if(cursorStyle
) // set style info
1207 *cursorStyle
= llist
->GetStyleInfo();
1210 // Just in case some joker inserted an empty string object:
1212 width
= WXLO_MINIMUM_CURSOR_WIDTH
;
1215 cursorSize
->x
= width
;
1216 cursorSize
->y
= height
;
1219 cursorFound
= true; // no more checks
1223 // on some other object
1224 CoordType top
, bottom
; // unused
1226 *cursorSize
= obj
->GetSize(&top
,&bottom
);
1227 cursorPos
->y
= m_Position
.y
;
1228 cursorFound
= true; // no more checks
1234 cursorPos
->x
+= obj
->GetWidth();
1238 m_Width
+= sizeObj
.x
;
1239 if(sizeObj
.y
> m_Height
)
1241 m_Height
= sizeObj
.y
;
1244 if(objTopHeight
> topHeight
)
1245 topHeight
= objTopHeight
;
1246 if(objBottomHeight
> bottomHeight
)
1247 bottomHeight
= objBottomHeight
;
1252 if ( updateHeight
< m_Height
)
1253 updateHeight
= m_Height
;
1254 if ( updateWidth
< m_Width
)
1255 updateWidth
= m_Width
;
1257 // update all line if we don't know where to start from
1258 if ( updateLeft
== -1 )
1261 llist
->SetUpdateRect(updateLeft
, updateTop
);
1262 llist
->SetUpdateRect(updateLeft
+ updateWidth
+ MSW_CORRECTION
,
1263 updateTop
+ updateHeight
+ MSW_CORRECTION
);
1266 if(topHeight
+ bottomHeight
> m_Height
)
1268 m_Height
= topHeight
+bottomHeight
;
1271 m_BaseLine
= topHeight
;
1275 CoordType width
, height
, descent
;
1276 dc
.GetTextExtent(_T(WXLO_CURSORCHAR
), &width
, &height
, &descent
);
1278 m_BaseLine
= m_Height
- descent
;
1281 // tell next line about coordinate change
1282 if(m_Next
&& m_Height
!= heightOld
)
1284 m_Next
->MarkDirty();
1287 // We need to check whether we found a valid cursor size:
1288 if(cursorPos
&& cursorSize
)
1290 // this might be the case if the cursor is at the end of the
1291 // line or on a command object:
1292 if(cursorSize
->x
< WXLO_MINIMUM_CURSOR_WIDTH
)
1294 CoordType width
, height
, descent
;
1295 dc
.GetTextExtent(_T(WXLO_CURSORCHAR
), &width
, &height
, &descent
);
1296 cursorSize
->x
= width
;
1297 cursorSize
->y
= height
;
1299 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
1300 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
1307 wxLayoutLine::Break(CoordType xpos
, wxLayoutList
*llist
)
1309 wxASSERT(xpos
>= 0);
1314 wxLOiterator i
= FindObject(xpos
, &offset
);
1315 wxLayoutObjectList::iterator
nulled(NULL
);
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 wxLayoutObjectList::iterator
nulled(NULL
);
1360 if(GetLength() < wrapmargin
)
1361 return false; // nothing to do
1363 // find the object which covers the wrapmargin:
1365 wxLOiterator i
= FindObject(wrapmargin
, &offset
);
1366 wxCHECK_MSG( i
!= nulled
, false,
1367 wxT("Cannot find object covering wrapmargin."));
1369 // from this object on, the rest of the line must be copied to the
1371 wxLOiterator copyObject
= nulled
;
1372 // if we split a text-object, we must pre-pend some text to the
1373 // next line later on, remember it here:
1374 wxString prependText
= _T("");
1375 // we might need to adjust the cursor position later, so remember it
1376 size_t xpos
= llist
->GetCursorPos().x
;
1377 // by how much did we shorten the current line:
1379 // remember cursor location of object
1380 size_t objectCursorPos
= 0;
1382 size_t breakpos
= offset
;
1384 if( (**i
).GetType() != WXLO_TYPE_TEXT
)
1386 // break before a non-text object
1391 bool foundSpace
= false;
1394 // while(i != nulled && (**i).GetType() != WXLO_TYPE_TEXT)
1396 // try to find a suitable place to split the object:
1397 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
1398 if((**i
).GetType() == WXLO_TYPE_TEXT
1399 && tobj
->GetText().Length() >= breakpos
)
1403 foundSpace
= isspace(tobj
->GetText()[breakpos
]) != 0;
1407 while ( breakpos
-- > 0 );
1414 if(! foundSpace
) // breakpos == 0!
1416 if(i
== m_ObjectList
.begin())
1417 return false; // could not break line
1421 while(i
!= m_ObjectList
.begin()
1422 && (**i
).GetType() != WXLO_TYPE_TEXT
)
1426 breakpos
= (**i
).GetLength();
1429 }while(! foundSpace
);
1430 // before we actually break the object, we need to know at which
1431 // cursorposition it starts, so we can restore the cursor if needed:
1432 if( this == llist
->GetCursorLine() && xpos
>= breakpos
)
1434 for(wxLOiterator j
= m_ObjectList
.begin();
1435 j
!= nulled
&& j
!= i
; j
++)
1436 objectCursorPos
+= (**j
).GetLength();
1438 // now we know where to break it:
1439 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
1440 shorter
= tobj
->GetLength() - breakpos
;
1441 // remember text to copy from this object
1442 prependText
= tobj
->GetText().Mid(breakpos
+1);
1443 tobj
->SetText(tobj
->GetText().Left(breakpos
));
1444 // copy every following object:
1445 copyObject
= i
; copyObject
++;
1448 // make sure there is an empty m_Next line:
1449 (void) new wxLayoutLine(this, llist
);
1451 // We need to move this and all following objects to the next
1452 // line. Starting from the end of line, to keep the order right.
1453 if(copyObject
!= nulled
)
1456 for(j
= m_ObjectList
.tail(); j
!= copyObject
; j
--)
1457 m_Next
->Prepend(*j
);
1458 m_Next
->Prepend(*copyObject
);
1459 // and now remove them from this list:
1460 while( copyObject
!= m_ObjectList
.end() )
1462 shorter
+= (**copyObject
).GetLength();
1463 m_ObjectList
.remove(copyObject
); // remove without deleting it
1466 m_Length
-= shorter
;
1468 if(prependText
.Length() > 0)
1469 m_Next
->Insert(0, prependText
);
1471 // do we need to adjust the cursor position?
1472 if( this == llist
->GetCursorLine() && xpos
>= breakpos
)
1474 xpos
= objectCursorPos
+ (xpos
- objectCursorPos
- breakpos
-
1475 ((xpos
> breakpos
) ? 1 : 0 ));
1477 // this assert is useless when xpos has unsigned type
1478 wxASSERT(xpos
>= 0);
1480 llist
->MoveCursorTo( wxPoint( xpos
, m_Next
->GetLineNumber()) );
1482 return true; // we wrapped the line
1486 wxLayoutLine::ReNumber()
1488 CoordType lineNo
= m_Previous
? m_Previous
->m_LineNumber
+1 : 0;
1489 m_LineNumber
= lineNo
++;
1491 for(wxLayoutLine
*next
= GetNextLine();
1492 next
; next
= next
->GetNextLine())
1493 next
->m_LineNumber
= lineNo
++;
1497 wxLayoutLine::MergeNextLine(wxLayoutList
*llist
)
1499 wxCHECK_RET( GetNextLine(),
1500 wxT("wxLayout internal error: no next line to merge"));
1501 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
1504 MarkDirty(GetWidth());
1506 wxLayoutObject
*last
= NULL
;
1507 for(i
= list
.begin(); i
!= list
.end();)
1509 wxLayoutObject
*current
= *i
;
1511 // merge text objects together for efficiency
1512 if ( last
&& last
->GetType() == WXLO_TYPE_TEXT
&&
1513 current
->GetType() == WXLO_TYPE_TEXT
)
1515 wxLayoutObjectText
*textObj
= (wxLayoutObjectText
*)last
;
1516 wxString
text(textObj
->GetText());
1517 text
+= ((wxLayoutObjectText
*)current
)->GetText();
1518 textObj
->SetText(text
);
1520 list
.erase(i
); // remove and delete it
1524 // just append the object "as was"
1527 list
.remove(i
); // remove without deleting it
1530 wxASSERT(list
.empty());
1532 wxLayoutLine
*oldnext
= GetNextLine();
1533 wxLayoutLine
*nextLine
= oldnext
->GetNextLine();
1537 nextLine
->ReNumber();
1541 // this is now done in Delete(), but if this function is ever called
1542 // from elsewhere, we might have to move refresh code back here (in
1543 // order not to duplicate it)
1545 wxPoint
pos(oldnext
->GetPosition());
1546 llist
->SetUpdateRect(pos
);
1547 llist
->SetUpdateRect(pos
.x
+ oldnext
->GetWidth() + MSW_CORRECTION
,
1548 pos
.y
+ oldnext
->GetHeight() + MSW_CORRECTION
);
1552 llist
->DecNumLines();
1558 wxLayoutLine::GetWrapPosition(CoordType column
)
1561 wxLOiterator i
= FindObject(column
, &offset
);
1562 wxLayoutObjectList::iterator
nulled(NULL
);
1563 if(i
== nulled
) return -1; // cannot wrap
1565 // go backwards through the list and look for space in text objects
1568 if((**i
).GetType() == WXLO_TYPE_TEXT
)
1572 if(isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1579 }while(offset
!= -1);
1580 i
--; // move on to previous object
1584 column
-= (**i
).GetLength();
1588 offset
= (**i
).GetLength();
1589 }while(i
!= nulled
);
1590 /* If we reached the begin of the list and have more than one
1591 object, that one is longer than the margin, so break behind
1594 i
= m_ObjectList
.begin();
1595 while(i
!= nulled
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1597 pos
+= (**i
).GetLength();
1600 if(i
== nulled
) return -1; //why should this happen?
1602 // now we are behind the one long text object and need to find the
1603 // first space in it
1604 for(offset
= 0; offset
< (**i
).GetLength(); offset
++)
1605 if( isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1609 pos
+= (**i
).GetLength();
1614 #ifdef WXLAYOUT_DEBUG
1616 wxLayoutLine::Debug() const
1618 wxLayoutObjectList::iterator
nulled(NULL
);
1619 wxPoint pos
= GetPosition();
1620 WXLO_DEBUG((wxT("Line %ld, Pos (%ld,%ld), Height %ld, BL %ld, Font: %d"),
1621 (long int) GetLineNumber(),
1622 (long int) pos
.x
, (long int) pos
.y
,
1623 (long int) GetHeight(),
1624 (long int) m_BaseLine
,
1625 (int) m_StyleInfo
.family
));
1626 if(m_ObjectList
.begin() != nulled
)
1628 WXLO_DEBUG(((**m_ObjectList
.begin()).DebugDump().c_str()));
1635 wxLayoutLine::Copy(wxLayoutList
*llist
,
1639 wxLayoutObjectList::iterator
nulled(NULL
);
1640 CoordType firstOffset
, lastOffset
;
1642 if(to
== -1) to
= GetLength();
1643 if(from
== to
) return;
1645 wxLOiterator first
= FindObject(from
, &firstOffset
);
1646 wxLOiterator last
= FindObject(to
, &lastOffset
);
1648 // Common special case: only one object
1649 if( first
!= nulled
&& last
!= nulled
&& *first
== *last
)
1651 if( (**first
).GetType() == WXLO_TYPE_TEXT
)
1653 llist
->Insert(new wxLayoutObjectText(
1654 ((wxLayoutObjectText
1655 *)*first
)->GetText().substr(firstOffset
,
1656 lastOffset
-firstOffset
))
1660 else // what can we do?
1662 if(lastOffset
> firstOffset
) // i.e. +1 :-)
1663 llist
->Insert( (**first
).Copy() );
1668 // If we reach here, we can safely copy the whole first object from
1669 // the firstOffset position on:
1670 if((**first
).GetType() == WXLO_TYPE_TEXT
&& firstOffset
!= 0)
1672 llist
->Insert(new wxLayoutObjectText(
1673 ((wxLayoutObjectText
*)*first
)->GetText().substr(firstOffset
))
1676 else if(firstOffset
== 0)
1677 llist
->Insert( (**first
).Copy() );
1678 // else nothing to copy :-(
1680 // Now we copy all objects before the last one:
1681 wxLOiterator i
= first
; i
++;
1682 for( ; i
!= last
; i
++)
1683 llist
->Insert( (**i
).Copy() );
1685 // And now the last object:
1688 if( (**last
).GetType() == WXLO_TYPE_TEXT
)
1690 llist
->Insert(new wxLayoutObjectText(
1691 ((wxLayoutObjectText
*)*last
)->GetText().substr(0,lastOffset
))
1695 llist
->Insert( (**last
).Copy() );
1700 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1702 The wxLayoutList object
1704 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1706 wxLayoutList::wxLayoutList()
1708 #ifdef WXLAYOUT_USE_CARET
1710 #endif // WXLAYOUT_USE_CARET
1714 SetAutoFormatting(true);
1715 ForceTotalLayout(true); // for the first time, do all
1716 InvalidateUpdateRect();
1720 wxLayoutList::~wxLayoutList()
1722 SetAutoFormatting(false);
1725 m_FirstLine
->DeleteLine(false, this);
1727 wxASSERT_MSG( m_numLines
== 0, wxT("line count calculation broken"));
1731 wxLayoutList::Empty()
1734 m_FirstLine
= m_FirstLine
->DeleteLine(false, this);
1736 m_CursorPos
= wxPoint(0,0);
1737 m_CursorScreenPos
= wxPoint(0,0);
1738 m_CursorSize
= wxPoint(0,0);
1739 m_movedCursor
= true;
1740 m_FirstLine
= new wxLayoutLine(NULL
, this); // empty first line
1741 m_CursorLine
= m_FirstLine
;
1742 InvalidateUpdateRect();
1747 wxLayoutList::InternalClear()
1749 m_Selection
.m_selecting
= false;
1750 m_Selection
.m_valid
= false;
1752 m_DefaultStyleInfo
.family
= wxSWISS
;
1753 m_DefaultStyleInfo
.size
= WXLO_DEFAULTFONTSIZE
;
1754 m_DefaultStyleInfo
.style
= wxNORMAL
;
1755 m_DefaultStyleInfo
.weight
= wxNORMAL
;
1756 m_DefaultStyleInfo
.underline
= 0;
1757 m_DefaultStyleInfo
.m_fg_valid
= true;
1758 m_DefaultStyleInfo
.m_fg
= *wxBLACK
;
1759 m_DefaultStyleInfo
.m_bg_valid
= true;
1760 m_DefaultStyleInfo
.m_bg
= *wxWHITE
;
1762 m_CurrentStyleInfo
= m_DefaultStyleInfo
;
1763 m_CursorStyleInfo
= m_DefaultStyleInfo
;
1767 wxLayoutList::Read(wxString
&istr
)
1769 /* In order to handle input of formatted string "nicely", we need
1770 to restore our current font settings after the string. So first
1771 of all, we create a StyleInfo structure with our current
1773 wxLayoutStyleInfo current_si
= GetStyleInfo();
1775 while(istr
.Length())
1777 // check for a linebreak:
1779 tmp
= istr
.BeforeFirst('\n');
1780 long l
= WXLO_TYPE_INVALID
;
1784 if(type
== WXLO_TYPE_LINEBREAK
)
1787 istr
= istr
.AfterFirst('\n');
1791 wxLayoutObject
*obj
= wxLayoutObject::Read(istr
);
1796 /* Now we use the current_si to restore our last font settings: */
1797 Insert(new wxLayoutObjectCmd(current_si
));
1802 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1803 int underline
, wxColour
*fg
,
1806 if(family
!= -1) m_CurrentStyleInfo
.family
= family
;
1807 if(size
!= -1) m_CurrentStyleInfo
.size
= size
;
1808 if(style
!= -1) m_CurrentStyleInfo
.style
= style
;
1809 if(weight
!= -1) m_CurrentStyleInfo
.weight
= weight
;
1810 if(underline
!= -1) m_CurrentStyleInfo
.underline
= underline
!= 0;
1811 if(fg
) m_CurrentStyleInfo
.m_fg
= *fg
;
1812 if(bg
) m_CurrentStyleInfo
.m_bg
= *bg
;
1814 new wxLayoutObjectCmd(
1815 m_CurrentStyleInfo
.family
,
1816 m_CurrentStyleInfo
.size
,
1817 m_CurrentStyleInfo
.style
,
1818 m_CurrentStyleInfo
.weight
,
1819 m_CurrentStyleInfo
.underline
,
1824 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1825 int underline
, wxChar
const *fg
, wxChar
const *bg
)
1828 wxColour cfg
= wxTheColourDatabase
->Find((fg
)?fg
:wxT("BLACK"));
1829 wxColour cbg
= wxTheColourDatabase
->Find((bg
)?bg
:wxT("WHITE"));
1831 SetFont(family
,size
,style
,weight
,underline
,&cfg
,&cbg
);
1835 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
1836 int underline
, wxColour
*fg
, wxColour
*bg
)
1839 m_DefaultStyleInfo
= wxLayoutStyleInfo(family
, size
, style
, weight
,
1841 m_CurrentStyleInfo
= m_DefaultStyleInfo
;
1843 // Empty() should be called after we set m_DefaultStyleInfo because
1844 // otherwise the style info for the first line (created in Empty()) would be
1850 wxLayoutList::FindText(const wxString
&needle
, const wxPoint
&cpos
) const
1855 for(line
= m_FirstLine
;
1857 line
= line
->GetNextLine())
1859 if(line
->GetLineNumber() >= cpos
.y
)
1861 xpos
= line
->FindText(needle
,
1862 (line
->GetLineNumber() == cpos
.y
) ?
1865 return wxPoint(xpos
, line
->GetLineNumber());
1868 return wxPoint(-1,-1);
1873 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
1875 AddCursorPosToUpdateRect();
1877 wxPoint cursorPosOld
= m_CursorPos
;
1879 wxLayoutLine
*line
= m_FirstLine
;
1880 while(line
&& line
->GetLineNumber() != p
.y
)
1881 line
= line
->GetNextLine();
1882 if(line
&& line
->GetLineNumber() == p
.y
) // found it
1884 m_CursorPos
.y
= p
.y
;
1885 m_CursorLine
= line
;
1886 CoordType len
= line
->GetLength();
1889 m_CursorPos
.x
= p
.x
;
1893 m_CursorPos
.x
= len
;
1897 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1899 return m_CursorPos
== p
;
1903 wxLayoutList::MoveCursorVertically(int n
)
1905 AddCursorPosToUpdateRect();
1907 wxPoint cursorPosOld
= m_CursorPos
;
1910 if(n
< 0) // move up
1912 if(m_CursorLine
== m_FirstLine
) return false;
1913 while(n
< 0 && m_CursorLine
)
1915 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1921 m_CursorLine
= m_FirstLine
;
1927 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1928 m_CursorPos
.x
= m_CursorLine
->GetLength();
1934 wxLayoutLine
*last
= m_CursorLine
;
1935 if(! m_CursorLine
->GetNextLine()) return false;
1936 while(n
> 0 && m_CursorLine
)
1940 last
= m_CursorLine
;
1941 m_CursorLine
= m_CursorLine
->GetNextLine();
1945 m_CursorLine
= last
;
1951 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1952 m_CursorPos
.x
= m_CursorLine
->GetLength();
1957 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1963 wxLayoutList::MoveCursorHorizontally(int n
)
1965 AddCursorPosToUpdateRect();
1967 wxPoint cursorPosOld
= m_CursorPos
;
1972 if(m_CursorPos
.x
== 0) // at begin of line
1974 if(! MoveCursorVertically(-1))
1976 MoveCursorToEndOfLine();
1981 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1982 m_CursorPos
.x
-= move
; n
+= move
;
1987 int len
= m_CursorLine
->GetLength();
1988 if(m_CursorPos
.x
== len
) // at end of line
1990 if(! MoveCursorVertically(1))
1992 MoveCursorToBeginOfLine();
1997 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1998 m_CursorPos
.x
+= move
;
2002 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
2008 wxLayoutList::MoveCursorWord(int n
, bool untilNext
)
2010 wxLayoutObjectList::iterator
nulled(NULL
);
2011 wxCHECK_MSG( m_CursorLine
, false, wxT("no current line") );
2012 wxCHECK_MSG( n
== -1 || n
== +1, false, wxT("not implemented yet") );
2014 CoordType moveDistance
= 0;
2016 wxLayoutLine
*lineCur
= m_CursorLine
;
2017 for ( wxLOiterator i
= lineCur
->FindObject(m_CursorPos
.x
, &offset
);
2025 // moving forward, pass to the first object of the next line
2027 lineCur
= lineCur
->GetNextLine();
2029 i
= lineCur
->GetFirstObject();
2033 // moving backwards, pass to the last object of the prev line
2035 lineCur
= lineCur
->GetPreviousLine();
2037 i
= lineCur
->GetLastObject();
2042 // moved to the end/beginning of text
2049 wxLayoutObject
*obj
= *i
;
2053 // calculate offset: we are either at the very beginning or the very
2054 // end of the object, so it isn't very difficult (the only time when
2055 // offset is != -1 is for the very first iteration when its value is
2056 // returned by FindObject)
2060 offset
= obj
->GetLength();
2063 if( obj
->GetType() != WXLO_TYPE_TEXT
)
2065 // any visible non text objects count as one word
2066 if ( obj
->IsVisibleObject() )
2070 moveDistance
+= obj
->GetLength();
2075 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)obj
;
2077 bool canAdvance
= true;
2079 if ( offset
== tobj
->GetLength() )
2084 // can't move further in this text object
2087 // still should move over the object border
2091 else if ( offset
> 0 )
2093 // offset is off by 1, make it a valid index
2100 const wxString
& text
= tobj
->GetText();
2101 const wxChar
*start
= text
.c_str();
2102 const wxChar
*end
= start
+ text
.length();
2103 const wxChar
*p
= start
+ offset
;
2111 // to the beginning/end of the next/prev word
2112 while ( p
>= start
&& p
< end
&& isspace(*p
) )
2117 // go to the end/beginning of the word (in a broad sense...)
2118 while ( p
>= start
&& p
< end
&& !isspace(*p
) )
2127 // now advance to the beginning of the next word
2128 while ( isspace(*p
) && p
< end
)
2134 // in these 2 cases we took 1 char too much
2135 if ( (p
< start
) || isspace(*p
) )
2141 CoordType moveDelta
= p
- start
- offset
;
2142 if ( (n
< 0) && (offset
== tobj
->GetLength() - 1) )
2144 // because we subtracted 1 from offset in this case above, now
2145 // compensate for it
2149 if ( moveDelta
!= 0 )
2151 moveDistance
+= moveDelta
;
2158 // except for the first iteration, offset is calculated in the beginning
2163 MoveCursorHorizontally(moveDistance
);
2169 wxLayoutList::Insert(wxString
const &text
)
2171 wxASSERT(m_CursorLine
);
2172 wxASSERT_MSG( text
.Find(wxT('\n')) == wxNOT_FOUND
,
2173 wxT("use wxLayoutImportText!") );
2178 AddCursorPosToUpdateRect();
2180 wxASSERT(m_CursorLine
->GetLength() >= m_CursorPos
.x
);
2182 if ( !m_CursorLine
->Insert(m_CursorPos
.x
, text
) )
2184 m_CursorPos
.x
+= text
.Length();
2186 m_movedCursor
= true;
2189 m_CursorLine
->MarkDirty();
2195 wxLayoutList::Insert(wxLayoutObject
*obj
)
2197 wxASSERT(m_CursorLine
);
2200 m_CursorLine
= GetFirstLine();
2202 AddCursorPosToUpdateRect();
2204 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
2205 m_CursorPos
.x
+= obj
->GetLength();
2206 m_movedCursor
= true;
2209 m_CursorLine
->MarkDirty();
2215 wxLayoutList::Insert(wxLayoutList
*llist
)
2217 wxLayoutObjectList::iterator
nulled(NULL
);
2221 for(wxLayoutLine
*line
= llist
->GetFirstLine();
2223 line
= line
->GetNextLine()
2226 for(wxLOiterator i
= line
->GetFirstObject();
2236 wxLayoutList::LineBreak()
2238 wxASSERT(m_CursorLine
);
2240 AddCursorPosToUpdateRect();
2242 wxPoint
position(m_CursorLine
->GetPosition());
2245 width
= m_CursorLine
->GetWidth(),
2246 height
= m_CursorLine
->GetHeight();
2248 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
, this);
2249 if(m_CursorLine
->GetPreviousLine() == NULL
)
2250 m_FirstLine
= m_CursorLine
;
2254 // The following code will produce a height which is guaranteed to
2255 // be too high: old lineheight + the height of both new lines.
2256 // We can probably drop the old line height and start with height =
2258 wxLayoutLine
*prev
= m_CursorLine
->GetPreviousLine();
2260 height
+= prev
->GetHeight();
2261 height
+= m_CursorLine
->GetHeight();
2263 m_movedCursor
= true;
2265 SetUpdateRect(position
);
2266 SetUpdateRect(position
.x
+ width
+ MSW_CORRECTION
,
2267 position
.y
+ height
+ MSW_CORRECTION
);
2273 wxLayoutList::WrapLine(CoordType column
)
2275 return m_CursorLine
->Wrap(column
, this);
2279 wxLayoutList::WrapAll(CoordType column
)
2281 wxLayoutLine
*line
= m_FirstLine
;
2287 rc
&= line
->Wrap(column
, this);
2288 line
= line
->GetNextLine();
2294 wxLayoutList::Delete(CoordType npos
)
2296 wxCHECK_MSG(m_CursorLine
, false, wxT("can't delete in non existing line"));
2301 AddCursorPosToUpdateRect();
2303 // were other lines appended to this one (this is important to know because
2304 // this means that our width _increased_ as the result of deletion)
2305 bool wasMerged
= false;
2307 // the size of the region to update
2308 CoordType totalHeight
= m_CursorLine
->GetHeight(),
2309 totalWidth
= m_CursorLine
->GetWidth();
2314 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
2318 // More to delete, continue on next line.
2320 // First, check if line is empty:
2321 if(m_CursorLine
->GetLength() == 0)
2323 // in this case, updating could probably be optimised
2325 wxASSERT(DeleteLines(1) == 0);
2334 // Need to join next line
2335 if(! m_CursorLine
->GetNextLine())
2340 wxLayoutLine
*next
= m_CursorLine
->GetNextLine();
2343 totalHeight
+= next
->GetHeight();
2344 totalWidth
+= next
->GetWidth();
2346 m_CursorLine
->MergeNextLine(this);
2351 wxFAIL_MSG(wxT("can't delete all this"));
2361 // we need to update the whole tail of the line and the lines which
2365 wxPoint
position(m_CursorLine
->GetPosition());
2366 SetUpdateRect(position
);
2367 SetUpdateRect(position
.x
+ totalWidth
+ MSW_CORRECTION
,
2368 position
.y
+ totalHeight
+ MSW_CORRECTION
);
2375 wxLayoutList::DeleteLines(int n
)
2377 wxASSERT(m_CursorLine
);
2380 AddCursorPosToUpdateRect();
2384 if(!m_CursorLine
->GetNextLine())
2385 { // we cannot delete this line, but we can clear it
2386 MoveCursorToBeginOfLine();
2387 DeleteToEndOfLine();
2389 m_CursorLine
->MarkDirty();
2393 line
= m_CursorLine
;
2394 m_CursorLine
= m_CursorLine
->DeleteLine(true, this);
2396 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
2397 wxASSERT(m_FirstLine
);
2398 wxASSERT(m_CursorLine
);
2401 m_CursorLine
->MarkDirty();
2406 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
)
2410 wxLayoutLine
*line
= m_FirstLine
;
2412 // first, make sure everything is calculated - this might not be
2413 // needed, optimise it later
2414 ApplyStyle(m_DefaultStyleInfo
, dc
);
2417 line
->RecalculatePosition(this); // so we don't need to do it all the time
2418 // little condition to speed up redrawing:
2419 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
2420 line
= line
->GetNextLine();
2425 wxLayoutList::GetCursorScreenPos() const
2427 return m_CursorScreenPos
;
2431 Is called before each Draw(). Now, it will re-layout all lines which
2435 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
, bool forceAll
,
2436 wxPoint
*cpos
, wxPoint
*csize
)
2438 // first, make sure everything is calculated - this might not be
2439 // needed, optimise it later
2440 ApplyStyle(m_DefaultStyleInfo
, dc
);
2449 ForceTotalLayout(false);
2452 // If one line was dirty, we need to re-calculate all
2453 // following lines, too.
2454 bool wasDirty
= forceAll
;
2455 // we need to layout until we reach at least the cursor line,
2456 // otherwise we won't be able to scroll to it
2457 bool cursorReached
= false;
2458 wxLayoutLine
*line
= m_FirstLine
;
2462 ApplyStyle(line
->GetStyleInfo(), dc
);
2464 // if any previous line was dirty, we need to layout all
2467 // go on until we find the cursorline
2469 // layout dirty lines:
2471 // always layout the cursor line toupdate the cursor
2472 // position and size:
2473 || line
== m_CursorLine
2474 // or if it's the line we are asked to look for:
2475 || (cpos
&& line
->GetLineNumber() == cpos
->y
)
2476 // layout at least the desired region:
2478 || (line
->GetPosition().y
<= bottom
)
2484 // The following Layout() calls will update our
2485 // m_CurrentStyleInfo if needed.
2486 if(line
== m_CursorLine
)
2488 line
->Layout(dc
, this,
2489 (wxPoint
*)&m_CursorScreenPos
,
2490 (wxPoint
*)&m_CursorSize
,
2493 // we cannot layout the line twice, so copy the coords:
2494 if(cpos
&& line
->GetLineNumber() == cpos
->y
)
2496 *cpos
= m_CursorScreenPos
;
2498 *csize
= m_CursorSize
;
2501 cursorReached
= true;
2505 if(cpos
&& line
->GetLineNumber() == cpos
->y
)
2507 line
->Layout(dc
, this,
2509 csize
, NULL
, cpos
->x
);
2510 cursorReached
= true;
2513 line
->Layout(dc
, this);
2517 line
= line
->GetNextLine();
2520 #ifndef WXLAYOUT_USE_CARET
2521 // can only be 0 if we are on the first line and have no next line
2522 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
2523 m_CursorLine
->GetNextLine() == NULL
&&
2524 m_CursorLine
== m_FirstLine
));
2525 #endif // WXLAYOUT_USE_CARET
2527 AddCursorPosToUpdateRect();
2531 wxLayoutList::GetScreenPos(wxDC
&dc
, const wxPoint
&cpos
, wxPoint
*csize
)
2534 Layout(dc
, -1, false, &pos
, csize
);
2539 wxLayoutList::Draw(wxDC
&dc
,
2540 wxPoint
const &offset
,
2545 wxLayoutLine
*line
= m_FirstLine
;
2547 if ( m_Selection
.m_discarded
)
2549 // calculate them if we don't have them already
2550 if ( !m_Selection
.HasValidScreenCoords() )
2552 m_Selection
.m_ScreenA
= GetScreenPos(dc
, m_Selection
.m_CursorA
);
2553 m_Selection
.m_ScreenB
= GetScreenPos(dc
, m_Selection
.m_CursorB
);
2556 // invalidate the area which was previousle selected - and which is not
2557 // selected any more
2558 SetUpdateRect(m_Selection
.m_ScreenA
);
2559 SetUpdateRect(m_Selection
.m_ScreenB
);
2561 m_Selection
.m_discarded
= false;
2564 /* This call to Layout() will re-calculate and update all lines
2569 ApplyStyle(m_DefaultStyleInfo
, dc
);
2570 wxBrush
brush(m_CurrentStyleInfo
.m_bg
, wxSOLID
);
2572 dc
.SetBackgroundMode(wxTRANSPARENT
);
2576 // only draw if between top and bottom:
2578 line
->GetPosition().y
+ line
->GetHeight() > top
))
2580 ApplyStyle(line
->GetStyleInfo(), dc
);
2581 // little condition to speed up redrawing:
2583 && line
->GetPosition().y
2584 +(clipStrictly
? line
->GetHeight() : 0) >= bottom
)
2587 line
->Draw(dc
, this, offset
);
2590 line
= line
->GetNextLine();
2593 InvalidateUpdateRect();
2595 WXLO_DEBUG((wxT("Selection is %s : %d,%d/%d,%d"),
2596 m_Selection
.m_valid
? wxT("valid") : wxT("invalid"),
2597 m_Selection
.m_CursorA
.x
, m_Selection
.m_CursorA
.y
,
2598 m_Selection
.m_CursorB
.x
, m_Selection
.m_CursorB
.y
));
2602 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
,
2603 wxPoint
*cursorPos
, bool *found
)
2605 wxLayoutObjectList::iterator
nulled(NULL
);
2606 // First, find the right line:
2608 *line
= m_FirstLine
,
2609 *lastline
= m_FirstLine
;
2612 ApplyStyle(m_DefaultStyleInfo
, dc
);
2615 p
= line
->GetPosition();
2616 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
2619 line
= line
->GetNextLine();
2622 bool didFind
= line
!= NULL
;
2626 // use the last line:
2631 cursorPos
->y
= line
->GetLineNumber();
2633 bool foundinline
= true;
2636 // Now, find the object in the line:
2641 i
= line
->FindObjectScreen(dc
, this,
2648 i
= line
->FindObjectScreen(dc
, this,
2654 *found
= didFind
&& foundinline
;
2656 return (i
== nulled
) ? NULL
: *i
;
2661 wxLayoutList::GetSize() const
2664 *line
= m_FirstLine
,
2667 return wxPoint(0,0);
2669 wxPoint
maxPoint(0,0);
2674 if(line
->GetWidth() > maxPoint
.x
)
2675 maxPoint
.x
= line
->GetWidth();
2677 line
= line
->GetNextLine();
2680 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
2682 // if the line was just added, its height would be 0 and we can't call
2683 // Layout() from here because we don't have a dc and we might be not drawing
2684 // at all, besides... So take the cursor height by default (taking 0 is bad
2685 // because then the scrollbars won't be resized and the new line won't be
2687 if ( last
->IsDirty() )
2689 if ( last
->GetHeight() == 0 )
2690 maxPoint
.y
+= m_CursorSize
.y
;
2691 if ( last
->GetWidth() == 0 && maxPoint
.x
< m_CursorSize
.x
)
2692 maxPoint
.x
= m_CursorSize
.x
;
2700 wxLayoutList::DrawCursor(wxDC
&
2701 #ifdef WXLAYOUT_USE_CARET
2707 #ifdef WXLAYOUT_USE_CARET
2712 , wxPoint
const &translate
)
2714 if ( m_movedCursor
)
2715 m_movedCursor
= false;
2717 wxPoint
coords(m_CursorScreenPos
);
2718 coords
+= translate
;
2720 #ifdef WXLAYOUT_DEBUG
2721 WXLO_DEBUG((wxT("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld"),
2722 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
2723 (long)coords
.x
, (long)coords
.y
,
2724 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
2725 (long)m_CursorLine
->GetLineNumber(),
2726 (long)m_CursorLine
->GetLength()));
2728 wxLogStatus(wxT("Cursor is at (%d, %d)"), m_CursorPos
.x
, m_CursorPos
.y
);
2731 #ifdef WXLAYOUT_USE_CARET
2732 m_caret
->Move(coords
);
2733 #else // !WXLAYOUT_USE_CARET
2735 wxASSERT(m_CursorSize
.x
>= WXLO_MINIMUM_CURSOR_WIDTH
);
2736 dc
.SetBrush(*wxWHITE_BRUSH
);
2737 //FIXME: wxGTK XOR is borken at the moment!!!dc.SetLogicalFunction(wxXOR);
2738 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
2741 dc
.SetLogicalFunction(wxXOR
);
2742 dc
.DrawRectangle(coords
.x
, coords
.y
,
2743 m_CursorSize
.x
, m_CursorSize
.y
);
2744 SetUpdateRect(coords
.x
, coords
.y
);
2745 SetUpdateRect(coords
.x
+m_CursorSize
.x
,
2746 coords
.y
+m_CursorSize
.y
);
2750 dc
.SetLogicalFunction(wxCOPY
);
2751 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
2752 coords
.x
, coords
.y
);
2753 SetUpdateRect(coords
.x
, coords
.y
+m_CursorSize
.y
-1);
2754 SetUpdateRect(coords
.x
, coords
.y
);
2757 dc
.SetLogicalFunction(wxCOPY
);
2758 //dc.SetBrush(wxNullBrush);
2759 #endif // WXLAYOUT_USE_CARET/!WXLAYOUT_USE_CARET
2763 wxLayoutList::SetUpdateRect(CoordType x
, CoordType y
)
2765 if(m_UpdateRectValid
)
2767 GrowRect(m_UpdateRect
, x
, y
);
2773 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
2774 m_UpdateRect
.height
= 4;// wxGTK :-)
2775 m_UpdateRectValid
= true;
2780 wxLayoutList::StartSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2782 wxPoint
cpos(cposOrig
);
2786 WXLO_DEBUG((wxT("Starting selection at %d/%d"), cpos
.x
, cpos
.y
));
2788 m_Selection
.m_CursorA
= cpos
;
2789 m_Selection
.m_CursorB
= cpos
;
2790 m_Selection
.m_ScreenA
= spos
;
2791 m_Selection
.m_ScreenB
= spos
;
2792 m_Selection
.m_selecting
= true;
2793 m_Selection
.m_valid
= false;
2797 wxLayoutList::ContinueSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2799 wxPoint
cpos(cposOrig
);
2803 wxASSERT(m_Selection
.m_selecting
== true);
2804 wxASSERT(m_Selection
.m_valid
== false);
2805 WXLO_DEBUG((wxT("Continuing selection at %d/%d"), cpos
.x
, cpos
.y
));
2807 m_Selection
.m_ScreenB
= spos
;
2808 m_Selection
.m_CursorB
= cpos
;
2812 wxLayoutList::EndSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2814 wxPoint
cpos(cposOrig
);
2816 if(cpos
.x
== -1) cpos
= m_CursorPos
;
2818 ContinueSelection(cpos
, spos
);
2820 WXLO_DEBUG((wxT("Ending selection at %d/%d"), cpos
.x
, cpos
.y
));
2822 // we always want m_CursorA <= m_CursorB!
2823 if( m_Selection
.m_CursorA
> m_Selection
.m_CursorB
)
2825 // exchange the start/end points
2826 wxPoint help
= m_Selection
.m_CursorB
;
2827 m_Selection
.m_CursorB
= m_Selection
.m_CursorA
;
2828 m_Selection
.m_CursorA
= help
;
2830 help
= m_Selection
.m_ScreenB
;
2831 m_Selection
.m_ScreenB
= m_Selection
.m_ScreenA
;
2832 m_Selection
.m_ScreenA
= help
;
2835 m_Selection
.m_selecting
= false;
2836 m_Selection
.m_valid
= true;
2837 /// In case we just clicked somewhere, the selection will have zero
2838 /// size, so we discard it immediately.
2839 if(m_Selection
.m_CursorA
== m_Selection
.m_CursorB
)
2846 wxLayoutList::DiscardSelection()
2848 if ( !HasSelection() )
2851 m_Selection
.m_valid
=
2852 m_Selection
.m_selecting
= false;
2853 m_Selection
.m_discarded
= true;
2857 wxLayoutList::IsSelecting() const
2859 return m_Selection
.m_selecting
;
2863 wxLayoutList::IsSelected(const wxPoint
&cursor
) const
2865 if ( !HasSelection() )
2869 (m_Selection
.m_CursorA
<= cursor
2870 && cursor
<= m_Selection
.m_CursorB
)
2871 || (m_Selection
.m_CursorB
<= cursor
2872 && cursor
<= m_Selection
.m_CursorA
)
2877 /** Tests whether this layout line is selected and needs
2879 @param line to test for
2880 @return 0 = not selected, 1 = fully selected, -1 = partially
2884 wxLayoutList::IsSelected(const wxLayoutLine
*line
, CoordType
*from
,
2887 wxASSERT(line
); wxASSERT(to
); wxASSERT(from
);
2889 if(! m_Selection
.m_valid
&& ! m_Selection
.m_selecting
)
2892 CoordType y
= line
->GetLineNumber();
2893 if ( (m_Selection
.m_CursorA
.y
< y
&& m_Selection
.m_CursorB
.y
> y
)
2894 || (m_Selection
.m_CursorB
.y
< y
&& m_Selection
.m_CursorA
.y
> y
) )
2898 else if (m_Selection
.m_CursorA
.y
== y
)
2900 *from
= m_Selection
.m_CursorA
.x
;
2901 if(m_Selection
.m_CursorB
.y
== y
)
2903 *to
= m_Selection
.m_CursorB
.x
;
2907 if(m_Selection
.m_CursorB
> m_Selection
.m_CursorA
)
2908 *to
= line
->GetLength();
2915 CoordType help
= *to
;
2922 else if (m_Selection
.m_CursorB
.y
== y
)
2924 *to
= m_Selection
.m_CursorB
.x
;
2925 if (m_Selection
.m_CursorA
.y
== y
)
2927 *from
= m_Selection
.m_CursorA
.x
;
2931 if(m_Selection
.m_CursorB
> m_Selection
.m_CursorA
)
2934 *from
= line
->GetLength();
2939 CoordType help
= *to
;
2952 wxLayoutList::DeleteSelection()
2954 if (! m_Selection
.m_valid
)
2957 m_Selection
.m_valid
= false;
2959 // Only delete part of the current line?
2960 if (m_Selection
.m_CursorA
.y
== m_Selection
.m_CursorB
.y
)
2962 MoveCursorTo(m_Selection
.m_CursorA
);
2963 Delete(m_Selection
.m_CursorB
.x
- m_Selection
.m_CursorA
.x
);
2967 // We now know that the two lines are different:
2970 * firstLine
= GetLine(m_Selection
.m_CursorA
.y
),
2971 * lastLine
= GetLine(m_Selection
.m_CursorB
.y
);
2973 // be a bit paranoid:
2974 if(! firstLine
|| ! lastLine
)
2977 // First, delete what's left of this line:
2978 MoveCursorTo(m_Selection
.m_CursorA
);
2979 DeleteToEndOfLine();
2981 wxLayoutLine
*prevLine
= firstLine
->GetPreviousLine(),
2982 *nextLine
= firstLine
->GetNextLine();
2984 while(nextLine
&& nextLine
!= lastLine
)
2986 nextLine
= nextLine
->DeleteLine(false, this);
2989 // Now nextLine = lastLine;
2990 Delete(1); // This joins firstLine and nextLine
2991 Delete(m_Selection
.m_CursorB
.x
); // This deletes the first x positions
2993 // Recalculate the line positions and numbers but notice that firstLine
2994 // might not exist any more - it could be deleted by Delete(1) above
2995 wxLayoutLine
*firstLine2
= prevLine
? prevLine
->GetNextLine() : m_FirstLine
;
2996 firstLine2
->MarkDirty();
2999 /// Starts highlighting the selection
3001 wxLayoutList::StartHighlighting(wxDC
&dc
)
3004 dc
.SetTextForeground(m_CurrentStyleInfo
.m_bg
);
3005 dc
.SetTextBackground(m_CurrentStyleInfo
.m_fg
);
3006 dc
.SetBackgroundMode(wxSOLID
);
3010 /// Ends highlighting the selection
3012 wxLayoutList::EndHighlighting(wxDC
&dc
)
3015 dc
.SetTextForeground(m_CurrentStyleInfo
.m_fg
);
3016 dc
.SetTextBackground(m_CurrentStyleInfo
.m_bg
);
3017 dc
.SetBackgroundMode(wxTRANSPARENT
);
3023 wxLayoutList::GetLine(CoordType index
) const
3025 wxASSERT_MSG( (0 <= index
) && (index
< (CoordType
)m_numLines
),
3026 wxT("invalid index") );
3029 CoordType n
= index
;
3031 CoordType lineNo
= 0;
3034 for ( line
= m_FirstLine
; line
&& n
-- > 0; line
= line
->GetNextLine() )
3037 wxASSERT(line
->GetLineNumber() == lineNo
);
3044 // should be the right one
3045 wxASSERT( line
->GetLineNumber() == index
);
3053 wxLayoutList::Copy(const wxPoint
&from
,
3060 for(firstLine
= m_FirstLine
;
3061 firstLine
&& firstLine
->GetLineNumber() < from
.y
;
3062 firstLine
=firstLine
->GetNextLine())
3065 if(!firstLine
|| firstLine
->GetLineNumber() != from
.y
)
3068 for(lastLine
= m_FirstLine
;
3069 lastLine
&& lastLine
->GetLineNumber() < to
.y
;
3070 lastLine
=lastLine
->GetNextLine())
3073 if(!lastLine
|| lastLine
->GetLineNumber() != to
.y
)
3078 wxLayoutLine
*tmp
= firstLine
;
3079 firstLine
= lastLine
;
3083 wxLayoutList
*llist
= new wxLayoutList();
3085 if(firstLine
== lastLine
)
3087 firstLine
->Copy(llist
, from
.x
, to
.x
);
3091 // Extract objects from first line
3092 firstLine
->Copy(llist
, from
.x
);
3094 // Extract all lines between
3095 for ( wxLayoutLine
*line
= firstLine
->GetNextLine();
3097 line
= line
->GetNextLine() )
3103 // Extract objects from last line
3104 lastLine
->Copy(llist
, 0, to
.x
);
3111 wxLayoutList::GetSelection(wxLayoutDataObject
*wxlo
, bool invalidate
)
3113 if(! m_Selection
.m_valid
)
3115 if(m_Selection
.m_selecting
)
3121 if(invalidate
) m_Selection
.m_valid
= false;
3123 wxLayoutList
*llist
= Copy( m_Selection
.m_CursorA
,
3124 m_Selection
.m_CursorB
);
3126 if(llist
&& wxlo
) // export as data object, too
3130 wxLayoutExportObject
*exp
;
3131 wxLayoutExportStatus
status(llist
);
3132 while((exp
= wxLayoutExport( &status
, WXLO_EXPORT_AS_OBJECTS
)) != NULL
)
3134 if(exp
->type
== WXLO_EXPORT_EMPTYLINE
)
3135 string
<< (int) WXLO_TYPE_LINEBREAK
<< '\n';
3137 exp
->content
.object
->Write(string
);
3141 wxlo
->SetLayoutData(string
);
3149 #define COPY_SI(what) if(si.what != -1) { m_CurrentStyleInfo.what = si.what; fontChanged = true; }
3152 wxLayoutList::ApplyStyle(wxLayoutStyleInfo
const &si
, wxDC
&dc
)
3154 bool fontChanged
= false;
3161 dc
.SetFont( m_FontCache
.GetFont(m_CurrentStyleInfo
) );
3165 m_CurrentStyleInfo
.m_fg
= si
.m_fg
;
3166 m_CurrentStyleInfo
.m_fg_valid
= true;
3167 dc
.SetTextForeground(m_CurrentStyleInfo
.m_fg
);
3172 m_CurrentStyleInfo
.m_bg
= si
.m_bg
;
3173 m_CurrentStyleInfo
.m_bg_valid
= true;
3174 dc
.SetTextBackground(m_CurrentStyleInfo
.m_bg
);
3179 #ifdef WXLAYOUT_DEBUG
3182 wxLayoutList::Debug()
3184 WXLO_DEBUG((wxT("Cursor is in line %d, screen pos = (%d, %d)"),
3185 (int)m_CursorLine
->GetLineNumber(),
3186 m_CursorScreenPos
.x
, m_CursorScreenPos
.y
));
3189 for(line
= m_FirstLine
; line
; line
= line
->GetNextLine())
3198 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3202 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
3204 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
3205 wxString
const & title
)
3210 // remove any highlighting which could interfere with printing:
3211 m_llist
->StartSelection();
3212 m_llist
->EndSelection();
3213 // force a full layout of the list:
3214 m_llist
->ForceTotalLayout();
3215 // layout is called in ScaleDC() when we have a DC
3219 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
3221 // The following bit is taken from the printing sample, let's see
3222 // whether it works for us.
3224 /* You might use THIS code to set the printer DC to ROUGHLY reflect
3225 * the screen text size. This page also draws lines of actual length 5cm
3229 // Get the logical pixels per inch of screen and printer
3230 int ppiScreenX
, ppiScreenY
;
3231 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
3232 int ppiPrinterX
, ppiPrinterY
;
3233 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
3235 if(ppiScreenX
== 0) // not yet set, need to guess
3240 wxUnusedVar(ppiScreenY
);
3242 if(ppiPrinterX
== 0) // not yet set, need to guess
3247 wxUnusedVar(ppiPrinterY
);
3249 // This scales the DC so that the printout roughly represents the
3250 // the screen scaling. The text point size _should_ be the right size
3251 // but in fact is too small for some reason. This is a detail that will
3252 // need to be addressed at some point but can be fudged for the
3254 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
3256 // Now we have to check in case our real page size is reduced
3257 // (e.g. because we're drawing to a print preview memory DC)
3258 int pageWidth
, pageHeight
;
3260 dc
->GetSize(&w
, &h
);
3261 GetPageSizePixels(&pageWidth
, &pageHeight
);
3262 wxUnusedVar(pageHeight
);
3263 if(pageWidth
!= 0) // doesn't work always
3265 // If printer pageWidth == current DC width, then this doesn't
3266 // change. But w might be the preview bitmap width, so scale down.
3267 scale
= scale
* (float)(w
/(float)pageWidth
);
3270 dc
->SetUserScale(scale
, scale
);
3274 bool wxLayoutPrintout::OnPrintPage(int page
)
3283 top
= (page
- 1)*m_PrintoutHeight
;
3284 bottom
= top
+ m_PrintoutHeight
;
3286 WXLO_DEBUG((wxT("OnPrintPage(%d) printing from %d to %d"), page
, top
,
3289 // SetDeviceOrigin() doesn't work here, so we need to manually
3290 // translate all coordinates.
3291 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
3292 m_llist
->Draw(*dc
, translate
, top
, bottom
, true /* clip strictly */);
3301 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
3303 /* We allocate a temporary wxDC for printing, so that we can
3304 determine the correct paper size and scaling. We don't actually
3305 print anything on it. */
3306 #if defined(__WXMSW__) || defined(__WXMAC__)
3307 wxPrinterDC
*psdc
= new wxPrinterDC(wxEmptyString
,wxEmptyString
,_T(WXLLIST_TEMPFILE
),false);
3310 data
.SetFilename(WXLLIST_TEMPFILE
);
3311 wxPostScriptDC
*psdc
= new wxPostScriptDC(data
);
3314 psdc
->StartDoc(m_title
);
3315 // before we draw anything, me must make sure the list is properly
3317 m_llist
->Layout(*psdc
);
3319 float scale
= ScaleDC(psdc
);
3321 psdc
->GetSize(&m_PageWidth
, &m_PageHeight
);
3323 // This sets a left/top origin of 15% and 5%:
3324 m_Offset
= wxPoint((15*m_PageWidth
)/100, (5*m_PageHeight
)/100);
3326 // This is the length of the printable area.
3327 m_PrintoutHeight
= m_PageHeight
- 2*m_Offset
.y
;
3328 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
3331 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
3334 *maxPage
= m_NumOfPages
;
3337 *selPageTo
= m_NumOfPages
;
3340 wxRemoveFile(_T(WXLLIST_TEMPFILE
));
3343 bool wxLayoutPrintout::HasPage(int pageNum
)
3345 return pageNum
<= m_NumOfPages
;
3349 Stupid wxWidgets doesn't draw proper ellipses, so we comment this
3350 out. It's a waste of paper anyway.
3354 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
3355 wxPoint topleft
, wxPoint bottomright
,
3358 // make backups of all essential parameters
3359 const wxBrush
& brush
= dc
.GetBrush();
3360 const wxPen
& pen
= dc
.GetPen();
3361 const wxFont
& font
= dc
.GetFont();
3363 dc
.SetBrush(*wxWHITE_BRUSH
);
3364 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
3365 dc
.DrawRoundedRectangle(topleft
.x
,
3366 topleft
.y
,bottomright
.x
-topleft
.x
,
3367 bottomright
.y
-topleft
.y
);
3368 dc
.SetBrush(*wxBLACK_BRUSH
);
3369 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
3370 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
3374 page
= "9999/9999 "; // many pages...
3376 dc
.GetTextExtent(page
,&w
,&h
);
3377 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
3378 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
3379 dc
.GetTextExtent("XXXX", &w
,&h
);
3380 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);
3391 wxFontCache::GetFont(int family
, int size
, int style
, int weight
,
3394 for(wxFCEList::iterator i
= m_FontList
.begin();
3395 i
!= m_FontList
.end(); i
++)
3396 if( (**i
).Matches(family
, size
, style
, weight
, underline
) )
3397 return (**i
).GetFont();
3399 wxFontCacheEntry
*fce
= new wxFontCacheEntry(family
, size
, style
,
3401 m_FontList
.push_back(fce
);
3402 return fce
->GetFont();