1 /*-*- c++ -*-********************************************************
2 * wxllist: wxLayoutList, a layout engine for text and graphics *
4 * (C) 1998-1999 by Karsten Ballüder (Ballueder@usa.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
48 # include <iostream.h>
52 # include <wx/print.h>
54 # include <wx/filefn.h>
57 #ifdef WXLAYOUT_USE_CARET
58 # include <wx/caret.h>
59 #endif // WXLAYOUT_USE_CARET
63 /// This should never really get created
64 #define WXLLIST_TEMPFILE "__wxllist.tmp"
68 # define TypeString(t) g_aTypeStrings[t]
69 # define WXLO_DEBUG(x) wxLogDebug x
71 static const char *g_aTypeStrings
[] =
73 "invalid", "text", "cmd", "icon"
76 wxLayoutObject::Debug(void)
78 WXLO_DEBUG(("%s",g_aTypeStrings
[GetType()]));
81 # define TypeString(t) ""
82 # define WXLO_DEBUG(x)
85 // FIXME under MSW, this constant is needed to make the thing properly redraw
86 // itself - I don't know where the size calculation error is and I can't
87 // waste time looking for it right now. Search for occurences of
88 // MSW_CORRECTION to find all the places where I did it.
90 static const int MSW_CORRECTION
= 10;
92 static const int MSW_CORRECTION
= 0;
95 /// Cursors smaller than this disappear in XOR drawing mode
96 #define WXLO_MINIMUM_CURSOR_WIDTH 4
98 /// Use this character to estimate a cursor size when none is available.
99 #define WXLO_CURSORCHAR "E"
100 /** @name Helper functions */
102 /// allows me to compare to wxPoints
103 bool operator <=(wxPoint
const &p1
, wxPoint
const &p2
)
105 return p1
.y
< p2
.y
|| (p1
.y
== p2
.y
&& p1
.x
<= p2
.x
);
109 The following STAY HERE until we have a working wxGTK again!!!
111 #ifndef wxWANTS_CHARS
112 /// allows me to compare to wxPoints
113 bool operator ==(wxPoint
const &p1
, wxPoint
const &p2
)
115 return p1
.x
== p2
.x
&& p1
.y
== p2
.y
;
118 /// allows me to compare to wxPoints
119 bool operator !=(wxPoint
const &p1
, wxPoint
const &p2
)
121 return p1
.x
!= p2
.x
|| p1
.y
!= p2
.y
;
124 wxPoint
& operator += (wxPoint
&p1
, wxPoint
const &p2
)
132 /// allows me to compare to wxPoints
133 bool operator>(wxPoint
const &p1
, wxPoint
const &p2
)
138 /// grows a wxRect so that it includes the given point
141 void GrowRect(wxRect
&r
, CoordType x
, CoordType y
)
145 else if(r
.x
+ r
.width
< x
)
150 else if(r
.y
+ r
.height
< y
)
156 /// returns true if the point is in the rectangle
158 bool Contains(const wxRect
&r
, const wxPoint
&p
)
160 return r
.x
<= p
.x
&& r
.y
<= p
.y
&& (r
.x
+r
.width
) >= p
.x
&& (r
.y
+ r
.height
) >= p
.y
;
168 void ReadString(wxString
&to
, wxString
&from
)
171 const char *cptr
= from
.c_str();
172 while(*cptr
&& *cptr
!= '\n')
178 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
182 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
186 wxLayoutObject::Read(wxString
&istr
)
189 ReadString(tmp
, istr
);
191 sscanf(tmp
.c_str(),"%d", &type
);
196 return wxLayoutObjectText::Read(istr
);
198 return wxLayoutObjectCmd::Read(istr
);
200 return wxLayoutObjectIcon::Read(istr
);
206 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
210 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
212 wxLayoutObjectText::wxLayoutObjectText(const wxString
&txt
)
222 wxLayoutObjectText::Copy(void)
224 wxLayoutObjectText
*obj
= new wxLayoutObjectText(m_Text
);
225 obj
->m_Width
= m_Width
;
226 obj
->m_Height
= m_Height
;
228 obj
->m_Bottom
= m_Bottom
;
229 obj
->SetUserData(m_UserData
);
235 wxLayoutObjectText::Write(wxString
&ostr
)
237 ostr
<< (int) WXLO_TYPE_TEXT
<< '\n'
242 wxLayoutObjectText::Read(wxString
&istr
)
245 ReadString(text
, istr
);
247 return new wxLayoutObjectText(text
);
251 wxLayoutObjectText::GetSize(CoordType
*top
, CoordType
*bottom
) const
254 *top
= m_Top
; *bottom
= m_Bottom
;
255 return wxPoint(m_Width
, m_Height
);
259 wxLayoutObjectText::Draw(wxDC
&dc
, wxPoint
const &coords
,
260 wxLayoutList
*wxllist
,
261 CoordType begin
, CoordType end
)
265 // draw the whole object normally
266 dc
.DrawText(m_Text
, coords
.x
, coords
.y
-m_Top
);
270 // highlight the bit between begin and len
273 ypos
= coords
.y
-m_Top
;
274 long width
, height
, descent
;
276 if(begin
< 0) begin
= 0;
277 if( end
> (signed)m_Text
.Length() )
278 end
= m_Text
.Length();
280 wxString str
= m_Text
.Mid(0, begin
);
281 dc
.DrawText(str
, xpos
, ypos
);
282 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
284 wxllist
->StartHighlighting(dc
);
285 str
= m_Text
.Mid(begin
, end
-begin
);
286 dc
.DrawText(str
, xpos
, ypos
);
287 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
289 wxllist
->EndHighlighting(dc
);
290 str
= m_Text
.Mid(end
, m_Text
.Length()-end
);
291 dc
.DrawText(str
, xpos
, ypos
);
296 wxLayoutObjectText::GetOffsetScreen(wxDC
&dc
, CoordType xpos
) const
300 maxlen
= m_Text
.Length();
303 height
, descent
= 0l;
305 if(xpos
== 0) return 0; // easy
307 while(width
< xpos
&& offs
< maxlen
)
309 dc
.GetTextExtent(m_Text
.substr(0,offs
),
310 &width
, &height
, &descent
);
313 /* We have to substract 1 to compensate for the offs++, and another
314 one because we don't want to position the cursor behind the
315 object what we clicked on, but before - otherwise it looks
317 return (xpos
> 2) ? offs
-2 : 0;
321 wxLayoutObjectText::Layout(wxDC
&dc
, class wxLayoutList
*llist
)
325 // now this is done in wxLayoutLine::Layout(), but this code might be
326 // reenabled later - in principle, it's more efficient
328 CoordType widthOld
= m_Width
,
329 heightOld
= m_Height
;
333 CoordType a
,b
,c
,d
,e
,f
;
334 dc
.GetTextExtent("test ", &a
, &b
, &c
);
335 dc
.GetTextExtent("test", &d
, &e
, &f
);
339 dc
.GetTextExtent(" ", &d
, &e
, &f
);
342 dc
.GetTextExtent(m_Text
, &m_Width
, &m_Height
, &descent
);
345 if ( widthOld
!= m_Width
|| heightOld
!= m_Height
)
347 // as the text length changed, it must be refreshed
348 wxLayoutLine
*line
= GetLine();
350 wxCHECK_RET( line
, "wxLayoutObjectText can't refresh itself" );
352 // as our size changed, we need to repaint the part which was appended
353 wxPoint
position(line
->GetPosition());
355 // this is not the most efficient way (we repaint the whole line), but
356 // it's not too slow and is *simple*
357 if ( widthOld
< m_Width
)
359 if ( heightOld
< m_Height
)
360 heightOld
= m_Height
;
362 llist
->SetUpdateRect(position
.x
+ widthOld
+ MSW_CORRECTION
,
363 position
.y
+ heightOld
+ MSW_CORRECTION
);
368 m_Top
= m_Height
- m_Bottom
;
372 #ifdef WXLAYOUT_DEBUG
374 wxLayoutObjectText::Debug(void)
376 wxLayoutObject::Debug();
377 WXLO_DEBUG((" `%s`", m_Text
.c_str()));
381 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
385 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
387 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
const &icon
)
391 wxFAIL_MSG("invalid icon");
399 // FIXME ugly, ugly, ugly - but the only way to avoid slicing
400 m_Icon
= icon
.GetHBITMAP() ? new wxBitmap(icon
)
401 : new wxBitmap(wxBitmap((const wxBitmap
&)icon
));
403 m_Icon
= new wxBitmap(icon
);
409 wxLayoutObjectIcon::Write(wxString
&ostr
)
411 /* Exports icon through a temporary file. */
413 wxString file
= wxGetTempFileName("wxloexport");
415 ostr
<< WXLO_TYPE_ICON
<< '\n'
417 m_Icon
->SaveFile(file
, WXLO_BITMAP_FORMAT
);
421 wxLayoutObjectIcon::Read(wxString
&istr
)
424 ReadString(file
, istr
);
426 if(! wxFileExists(file
))
428 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon
;
430 if(!obj
->m_Icon
->LoadFile(file
, WXLO_BITMAP_FORMAT
))
440 wxLayoutObjectIcon::Copy(void)
442 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon(new
444 obj
->SetUserData(m_UserData
);
448 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
*icon
)
456 wxLayoutObjectIcon::Draw(wxDC
&dc
, wxPoint
const &coords
,
457 wxLayoutList
*wxllist
,
458 CoordType begin
, CoordType
/* len */)
460 dc
.DrawBitmap(*m_Icon
, coords
.x
, coords
.y
-m_Icon
->GetHeight(),
461 (m_Icon
->GetMask() == NULL
) ? FALSE
: TRUE
);
465 wxLayoutObjectIcon::Layout(wxDC
& /* dc */, class wxLayoutList
* )
470 wxLayoutObjectIcon::GetSize(CoordType
*top
, CoordType
*bottom
) const
472 *top
= m_Icon
->GetHeight();
474 return wxPoint(m_Icon
->GetWidth(), m_Icon
->GetHeight());
479 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
483 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
486 wxLayoutStyleInfo::wxLayoutStyleInfo(int ifamily
,
498 underline
= iul
!= 0;
500 m_fg_valid
= fg
!= 0;
501 m_bg_valid
= bg
!= 0;
502 m_fg
= m_fg_valid
? *fg
: *wxBLACK
;
503 m_bg
= m_bg_valid
? *bg
: *wxWHITE
;
506 #define COPY_SI_(what) if(right.what != -1) what = right.what;
509 wxLayoutStyleInfo::operator=(const wxLayoutStyleInfo
&right
)
516 if(right
.m_fg_valid
) m_fg
= right
.m_fg
;
517 if(right
.m_bg_valid
) m_bg
= right
.m_bg
;
521 wxLayoutObjectCmd::wxLayoutObjectCmd(int family
, int size
, int style
, int
522 weight
, int underline
,
523 wxColour
*fg
, wxColour
*bg
)
526 m_StyleInfo
= new wxLayoutStyleInfo(family
, size
,style
,weight
,underline
,fg
,bg
);
529 wxLayoutObjectCmd::wxLayoutObjectCmd(const wxLayoutStyleInfo
&si
)
532 m_StyleInfo
= new wxLayoutStyleInfo
;
537 wxLayoutObjectCmd::Copy(void)
539 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd(
544 m_StyleInfo
->underline
,
545 m_StyleInfo
->m_fg_valid
?
546 &m_StyleInfo
->m_fg
: NULL
,
547 m_StyleInfo
->m_bg_valid
?
548 &m_StyleInfo
->m_bg
: NULL
);
549 obj
->SetUserData(m_UserData
);
554 wxLayoutObjectCmd::Write(wxString
&ostr
)
556 ostr
<< WXLO_TYPE_CMD
<< '\n'
557 << m_StyleInfo
->family
<< '\n'
558 << m_StyleInfo
->size
<< '\n'
559 << m_StyleInfo
->style
<< '\n'
560 << m_StyleInfo
->weight
<< '\n'
561 << m_StyleInfo
->underline
<< '\n'
562 << m_StyleInfo
->m_fg_valid
<< '\n'
563 << m_StyleInfo
->m_bg_valid
<< '\n';
564 if(m_StyleInfo
->m_fg_valid
)
566 ostr
<< m_StyleInfo
->m_fg
.Red() << '\n'
567 << m_StyleInfo
->m_fg
.Green() << '\n'
568 << m_StyleInfo
->m_fg
.Blue() << '\n';
570 if(m_StyleInfo
->m_bg_valid
)
572 ostr
<< m_StyleInfo
->m_bg
.Red() << '\n'
573 << m_StyleInfo
->m_bg
.Green() << '\n'
574 << m_StyleInfo
->m_bg
.Blue() << '\n';
579 wxLayoutObjectCmd::Read(wxString
&istr
)
581 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd
;
584 ReadString(tmp
, istr
);
585 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->family
);
586 ReadString(tmp
, istr
);
587 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->size
);
588 ReadString(tmp
, istr
);
589 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->style
);
590 ReadString(tmp
, istr
);
591 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->weight
);
592 ReadString(tmp
, istr
);
593 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->underline
);
594 ReadString(tmp
, istr
);
595 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->m_fg_valid
);
596 ReadString(tmp
, istr
);
597 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->m_bg_valid
);
598 if(obj
->m_StyleInfo
->m_fg_valid
)
600 int red
, green
, blue
;
601 ReadString(tmp
, istr
);
602 sscanf(tmp
.c_str(),"%d", &red
);
603 ReadString(tmp
, istr
);
604 sscanf(tmp
.c_str(),"%d", &green
);
605 ReadString(tmp
, istr
);
606 sscanf(tmp
.c_str(),"%d", &blue
);
607 obj
->m_StyleInfo
->m_fg
= wxColour(red
, green
, blue
);
609 if(obj
->m_StyleInfo
->m_bg_valid
)
611 int red
, green
, blue
;
612 ReadString(tmp
, istr
);
613 sscanf(tmp
.c_str(),"%d", &red
);
614 ReadString(tmp
, istr
);
615 sscanf(tmp
.c_str(),"%d", &green
);
616 ReadString(tmp
, istr
);
617 sscanf(tmp
.c_str(),"%d", &blue
);
618 obj
->m_StyleInfo
->m_bg
= wxColour(red
, green
, blue
);
624 wxLayoutObjectCmd::~wxLayoutObjectCmd()
630 wxLayoutObjectCmd::GetStyle(void) const
636 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint
const & /* coords */,
637 wxLayoutList
*wxllist
,
638 CoordType begin
, CoordType
/* len */)
640 wxASSERT(m_StyleInfo
);
641 wxllist
->ApplyStyle(*m_StyleInfo
, dc
);
645 wxLayoutObjectCmd::Layout(wxDC
&dc
, class wxLayoutList
* llist
)
647 // this get called, so that recalculation uses right font sizes
648 Draw(dc
, wxPoint(0,0), llist
);
652 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
654 The wxLayoutLine object
656 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
658 wxLayoutLine::wxLayoutLine(wxLayoutLine
*prev
, wxLayoutList
*llist
)
660 m_Width
= m_Height
= 0;
670 RecalculatePosition(llist
);
675 m_LineNumber
= m_Previous
->GetLineNumber() + 1;
676 m_Next
= m_Previous
->GetNextLine();
677 m_Previous
->m_Next
= this;
682 m_Next
->m_Previous
= this;
686 m_StyleInfo
= llist
->GetDefaultStyleInfo();
688 llist
->IncNumLines();
691 wxLayoutLine::~wxLayoutLine()
693 // kbList cleans itself
697 wxLayoutLine::RecalculatePosition(wxLayoutList
*llist
)
699 wxASSERT(m_Previous
|| GetLineNumber() == 0);
701 wxPoint
posOld(m_Position
);
705 m_Position
= m_Previous
->GetPosition();
706 m_Position
.y
+= m_Previous
->GetHeight();
709 m_Position
= wxPoint(0,0);
711 if ( m_Position
!= posOld
)
713 // the whole line moved and must be repainted
714 llist
->SetUpdateRect(m_Position
);
715 llist
->SetUpdateRect(m_Position
.x
+ GetWidth() + MSW_CORRECTION
,
716 m_Position
.y
+ GetHeight() + MSW_CORRECTION
);
717 llist
->SetUpdateRect(posOld
);
718 llist
->SetUpdateRect(posOld
.x
+ GetWidth() + MSW_CORRECTION
,
719 posOld
.y
+ GetHeight() + MSW_CORRECTION
);
726 wxLayoutObjectList::iterator
727 wxLayoutLine::FindObject(CoordType xpos
, CoordType
*offset
) const
731 wxLayoutObjectList::iterator
734 CoordType x
= 0, len
;
736 /* We search through the objects. As we don't like returning the
737 object that the cursor is behind, we just remember such an
738 object in "found" so we can return it if there is really no
739 further object following it. */
740 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
742 len
= (**i
).GetLength();
743 if( x
<= xpos
&& xpos
<= x
+ len
)
746 if(xpos
== x
+ len
) // is there another object behind?
748 else // we are really inside this object
751 x
+= (**i
).GetLength();
753 return found
; // ==NULL if really none found
756 wxLayoutObjectList::iterator
757 wxLayoutLine::FindObjectScreen(wxDC
&dc
, wxLayoutList
*llist
,
758 CoordType xpos
, CoordType
*cxpos
,
763 llist
->ApplyStyle(GetStyleInfo(), dc
);
765 wxLayoutObjectList::iterator i
;
766 CoordType x
= 0, cx
= 0, width
;
768 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
770 wxLayoutObject
*obj
= *i
;
771 if ( obj
->GetType() == WXLO_TYPE_CMD
)
773 // this will set the correct font for the objects which follow
774 obj
->Layout(dc
, llist
);
777 width
= obj
->GetWidth();
778 if( x
<= xpos
&& xpos
<= x
+ width
)
780 *cxpos
= cx
+ obj
->GetOffsetScreen(dc
, xpos
-x
);
787 x
+= obj
->GetWidth();
788 cx
+= obj
->GetLength();
791 // behind last object:
796 return m_ObjectList
.tail();
799 /** Finds text in this line.
800 @param needle the text to find
801 @param xpos the position where to start the search
802 @return the cursoor coord where it was found or -1
805 wxLayoutLine::FindText(const wxString
&needle
, CoordType xpos
) const
810 wxString
const *text
;
812 for(wxLOiterator i
= m_ObjectList
.begin(); i
!= m_ObjectList
.end(); i
++)
814 if(cpos
>= xpos
) // search from here!
816 if((**i
).GetType() == WXLO_TYPE_TEXT
)
818 text
= & ((wxLayoutObjectText
*)(*i
))->GetText();
819 relpos
= text
->Find(needle
);
820 if(relpos
>= cpos
-xpos
) // -1 if not found
825 cpos
+= (**i
).GetLength();
828 return -1; // not found
832 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
835 wxASSERT(obj
!= NULL
);
840 wxLOiterator i
= FindObject(xpos
, &offset
);
843 if(xpos
== 0 ) // aha, empty line!
845 m_ObjectList
.push_back(obj
);
846 m_Length
+= obj
->GetLength();
853 CoordType len
= (**i
).GetLength();
854 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
855 { // insert before this object
856 m_ObjectList
.insert(i
,obj
);
857 m_Length
+= obj
->GetLength();
862 if( i
== m_ObjectList
.tail()) // last object?
863 m_ObjectList
.push_back(obj
);
865 { // insert after current object
867 m_ObjectList
.insert(i
,obj
);
869 m_Length
+= obj
->GetLength();
872 /* Otherwise we need to split the current object.
873 Fortunately this can only be a text object. */
874 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
875 wxString left
, right
;
876 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
877 left
= tobj
->GetText().substr(0,offset
);
878 right
= tobj
->GetText().substr(offset
,len
-offset
);
879 // current text object gets set to right half
880 tobj
->GetText() = right
; // set new text
881 // before it we insert the new object
882 m_ObjectList
.insert(i
,obj
);
883 m_Length
+= obj
->GetLength();
884 // and before that we insert the left half
885 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
890 wxLayoutLine::Insert(CoordType xpos
, const wxString
& text
)
897 wxLOiterator i
= FindObject(xpos
, &offset
);
898 if(i
!= NULLIT
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
900 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
901 tobj
->GetText().insert(offset
, text
);
902 m_Length
+= text
.Length();
906 if ( !Insert(xpos
, new wxLayoutObjectText(text
)) )
914 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
916 CoordType offset
, len
;
921 wxLOiterator i
= FindObject(xpos
, &offset
);
924 if(i
== NULLIT
) return npos
;
925 // now delete from that object:
926 if((**i
).GetType() != WXLO_TYPE_TEXT
)
928 if(offset
!= 0) // at end of line after a non-text object
931 len
= (**i
).GetLength();
934 m_ObjectList
.erase(i
);
938 // tidy up: remove empty text objects
939 if((**i
).GetLength() == 0)
941 m_ObjectList
.erase(i
);
945 CoordType max
= (**i
).GetLength() - offset
;
946 if(npos
< max
) max
= npos
;
949 if(xpos
== GetLength())
952 { // at the end of an object
953 // move to begin of next object:
955 continue; // start over
960 if(offset
== 0 && max
== (**i
).GetLength())
961 m_ObjectList
.erase(i
); // remove the whole object
963 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
971 wxLayoutLine::DeleteWord(CoordType xpos
)
977 wxLOiterator i
= FindObject(xpos
, &offset
);
981 if(i
== NULLIT
) return false;
982 if((**i
).GetType() != WXLO_TYPE_TEXT
)
984 // This should only happen when at end of line, behind a non-text
986 if(offset
== (**i
).GetLength()) return false;
987 m_Length
-= (**i
).GetLength(); // -1
988 m_ObjectList
.erase(i
);
989 return true; // we are done
993 if(offset
== (**i
).GetLength()) // at end of object
998 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
1000 wxString str
= tobj
->GetText();
1001 str
= str
.substr(offset
,str
.Length()-offset
);
1002 // Find out how many positions we need to delete:
1003 // 1. eat leading space
1004 while(isspace(str
.c_str()[count
])) count
++;
1005 // 2. eat the word itself:
1006 while(isalnum(str
.c_str()[count
])) count
++;
1008 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
1009 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
1015 wxFAIL_MSG("unreachable");
1019 wxLayoutLine::DeleteLine(bool update
, wxLayoutList
*llist
)
1021 // maintain linked list integrity
1023 m_Next
->m_Previous
= m_Previous
;
1025 m_Previous
->m_Next
= m_Next
;
1027 // get the line numbers right again
1028 if ( update
&& m_Next
)
1033 // we can't use m_Next after "delete this", so we must save this pointer
1035 wxLayoutLine
*next
= m_Next
;
1038 llist
->DecNumLines();
1044 wxLayoutLine::Draw(wxDC
&dc
,
1045 wxLayoutList
*llist
,
1046 const wxPoint
& offset
) const
1048 wxLayoutObjectList::iterator i
;
1049 wxPoint pos
= offset
;
1050 pos
= pos
+ GetPosition();
1052 pos
.y
+= m_BaseLine
;
1054 CoordType xpos
= 0; // cursorpos, lenght of line
1056 CoordType from
, to
, tempto
;
1058 int highlight
= llist
->IsSelected(this, &from
, &to
);
1059 // WXLO_DEBUG(("highlight=%d", highlight ));
1060 if(highlight
== 1) // we need to draw the whole line inverted!
1061 llist
->StartHighlighting(dc
);
1063 llist
->EndHighlighting(dc
);
1065 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
1067 if(highlight
== -1) // partially highlight line
1069 // parts of the line need highlighting
1070 tempto
= xpos
+(**i
).GetLength();
1071 (**i
).Draw(dc
, pos
, llist
, from
-xpos
, to
-xpos
);
1074 (**i
).Draw(dc
, pos
, llist
);
1075 pos
.x
+= (**i
).GetWidth();
1076 xpos
+= (**i
).GetLength();
1081 This function does all the recalculation, that is, it should only be
1082 called from within wxLayoutList::Layout(), as it uses the current
1083 list's styleinfo and updates it.
1086 wxLayoutLine::Layout(wxDC
&dc
,
1087 wxLayoutList
*llist
,
1089 wxPoint
*cursorSize
,
1090 wxLayoutStyleInfo
*cursorStyle
,
1092 bool suppressSIupdate
)
1094 wxLayoutObjectList::iterator i
;
1096 // when a line becomes dirty, we redraw it from the place where it was
1097 // changed till the end of line (because the following wxLayoutObjects are
1098 // moved when the preceding one changes) - calculate the update rectangle.
1099 CoordType updateTop
= m_Position
.y
,
1101 updateWidth
= m_Width
,
1102 updateHeight
= m_Height
;
1106 bottomHeight
= 0; // above and below baseline
1108 objTopHeight
, objBottomHeight
; // above and below baseline
1112 CoordType heightOld
= m_Height
;
1118 bool cursorFound
= false;
1120 RecalculatePosition(llist
);
1124 *cursorPos
= m_Position
;
1125 if(cursorSize
) *cursorSize
= wxPoint(0,0);
1128 m_StyleInfo
= llist
->GetStyleInfo(); // save current style
1129 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
1131 wxLayoutObject
*obj
= *i
;
1132 obj
->Layout(dc
, llist
);
1133 wxPoint sizeObj
= obj
->GetSize(&objTopHeight
, &objBottomHeight
);
1135 if(cursorPos
&& ! cursorFound
)
1137 // we need to check whether the text cursor is here
1138 len
= obj
->GetLength();
1139 if(count
<= cx
&& count
+len
> cx
)
1141 if(obj
->GetType() == WXLO_TYPE_TEXT
)
1143 len
= cx
- count
; // pos in object
1144 CoordType width
, height
, descent
;
1145 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
1146 &width
, &height
, &descent
);
1147 cursorPos
->x
+= width
;
1148 cursorPos
->y
= m_Position
.y
;
1150 if(len
< obj
->GetLength())
1151 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
1153 str
= WXLO_CURSORCHAR
;
1154 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
1156 if(cursorStyle
) // set style info
1157 *cursorStyle
= llist
->GetStyleInfo();
1160 // Just in case some joker inserted an empty string object:
1162 width
= WXLO_MINIMUM_CURSOR_WIDTH
;
1165 cursorSize
->x
= width
;
1166 cursorSize
->y
= height
;
1169 cursorFound
= true; // no more checks
1173 // on some other object
1174 CoordType top
, bottom
; // unused
1176 *cursorSize
= obj
->GetSize(&top
,&bottom
);
1177 cursorPos
->y
= m_Position
.y
;
1178 cursorFound
= true; // no more checks
1184 cursorPos
->x
+= obj
->GetWidth();
1188 m_Width
+= sizeObj
.x
;
1189 if(sizeObj
.y
> m_Height
)
1191 m_Height
= sizeObj
.y
;
1194 if(objTopHeight
> topHeight
)
1195 topHeight
= objTopHeight
;
1196 if(objBottomHeight
> bottomHeight
)
1197 bottomHeight
= objBottomHeight
;
1202 if ( updateHeight
< m_Height
)
1203 updateHeight
= m_Height
;
1204 if ( updateWidth
< m_Width
)
1205 updateWidth
= m_Width
;
1207 // update all line if we don't know where to start from
1208 if ( updateLeft
== -1 )
1211 llist
->SetUpdateRect(updateLeft
, updateTop
);
1212 llist
->SetUpdateRect(updateLeft
+ updateWidth
+ MSW_CORRECTION
,
1213 updateTop
+ updateHeight
+ MSW_CORRECTION
);
1216 if(topHeight
+ bottomHeight
> m_Height
)
1218 m_Height
= topHeight
+bottomHeight
;
1221 m_BaseLine
= topHeight
;
1225 CoordType width
, height
, descent
;
1226 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1228 m_BaseLine
= m_Height
- descent
;
1231 // tell next line about coordinate change
1232 if(m_Next
&& m_Height
!= heightOld
)
1234 m_Next
->MarkDirty();
1237 // We need to check whether we found a valid cursor size:
1238 if(cursorPos
&& cursorSize
)
1240 // this might be the case if the cursor is at the end of the
1241 // line or on a command object:
1242 if(cursorSize
->x
< WXLO_MINIMUM_CURSOR_WIDTH
)
1244 CoordType width
, height
, descent
;
1245 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1246 cursorSize
->x
= width
;
1247 cursorSize
->y
= height
;
1249 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
1250 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
1257 wxLayoutLine::Break(CoordType xpos
, wxLayoutList
*llist
)
1259 wxASSERT(xpos
>= 0);
1264 wxLOiterator i
= FindObject(xpos
, &offset
);
1266 // must be at the end of the line then
1267 return new wxLayoutLine(this, llist
);
1270 wxLayoutLine
*newLine
= new wxLayoutLine(this, llist
);
1271 // split object at i:
1272 if((**i
).GetType() == WXLO_TYPE_TEXT
&& offset
!= 0)
1274 wxString left
, right
;
1275 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
1276 left
= tobj
->GetText().substr(0,offset
);
1277 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
1278 // current text object gets set to left half
1279 tobj
->GetText() = left
; // set new text
1280 newLine
->Append(new wxLayoutObjectText(right
));
1281 m_Length
-= right
.Length();
1282 i
++; // don't move this object to the new list
1287 i
++; // move objects from here to new list
1290 while(i
!= m_ObjectList
.end())
1292 wxLayoutObject
*obj
= *i
;
1293 newLine
->Append(obj
);
1294 m_Length
-= obj
->GetLength();
1296 m_ObjectList
.remove(i
); // remove without deleting it
1299 m_Next
->MarkDirty();
1304 wxLayoutLine::ReNumber(void)
1306 CoordType lineNo
= m_Previous
? m_Previous
->m_LineNumber
+1 : 0;
1307 m_LineNumber
= lineNo
++;
1309 for(wxLayoutLine
*next
= GetNextLine();
1310 next
; next
= next
->GetNextLine())
1311 next
->m_LineNumber
= lineNo
++;
1315 wxLayoutLine::MergeNextLine(wxLayoutList
*llist
)
1317 wxCHECK_RET(GetNextLine(),"wxLayout internal error: no next line to merge");
1318 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
1321 MarkDirty(GetWidth());
1323 wxLayoutObject
*last
= NULL
;
1324 for(i
= list
.begin(); i
!= list
.end();)
1326 wxLayoutObject
*current
= *i
;
1328 // merge text objects together for efficiency
1329 if ( last
&& last
->GetType() == WXLO_TYPE_TEXT
&&
1330 current
->GetType() == WXLO_TYPE_TEXT
)
1332 wxLayoutObjectText
*textObj
= (wxLayoutObjectText
*)last
;
1333 wxString
text(textObj
->GetText());
1334 text
+= ((wxLayoutObjectText
*)current
)->GetText();
1335 textObj
->SetText(text
);
1337 list
.erase(i
); // remove and delete it
1341 // just append the object "as was"
1344 list
.remove(i
); // remove without deleting it
1347 wxASSERT(list
.empty());
1349 wxLayoutLine
*oldnext
= GetNextLine();
1350 wxLayoutLine
*nextLine
= oldnext
->GetNextLine();
1354 nextLine
->ReNumber();
1358 // this is now done in Delete(), but if this function is ever called
1359 // from elsewhere, we might have to move refresh code back here (in
1360 // order not to duplicate it)
1362 wxPoint
pos(oldnext
->GetPosition());
1363 llist
->SetUpdateRect(pos
);
1364 llist
->SetUpdateRect(pos
.x
+ oldnext
->GetWidth() + MSW_CORRECTION
,
1365 pos
.y
+ oldnext
->GetHeight() + MSW_CORRECTION
);
1369 llist
->DecNumLines();
1375 wxLayoutLine::GetWrapPosition(CoordType column
)
1378 wxLOiterator i
= FindObject(column
, &offset
);
1379 if(i
== NULLIT
) return -1; // cannot wrap
1381 // go backwards through the list and look for space in text objects
1384 if((**i
).GetType() == WXLO_TYPE_TEXT
)
1388 if( isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1395 }while(offset
!= -1);
1396 i
--; // move on to previous object
1400 column
-= (**i
).GetLength();
1404 offset
= (**i
).GetLength();
1405 }while(i
!= NULLIT
);
1406 /* If we reached the begin of the list and have more than one
1407 object, that one is longer than the margin, so break behind
1410 i
= m_ObjectList
.begin();
1411 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1413 pos
+= (**i
).GetLength();
1416 if(i
== NULLIT
) return -1; //why should this happen?
1417 pos
+= (**i
).GetLength();
1419 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1421 pos
+= (**i
).GetLength();
1424 if(i
== NULLIT
) return -1; //this is possible, if there is only one text object
1425 // now we are at the second text object:
1426 pos
-= (**i
).GetLength();
1427 return pos
; // in front of it
1431 #ifdef WXLAYOUT_DEBUG
1433 wxLayoutLine::Debug(void)
1436 wxPoint pos
= GetPosition();
1437 WXLO_DEBUG(("Line %ld, Pos (%ld,%ld), Height %ld, BL %ld, Font: %d",
1438 (long int) GetLineNumber(),
1439 (long int) pos
.x
, (long int) pos
.y
,
1440 (long int) GetHeight(),
1441 (long int) m_BaseLine
,
1442 (int) m_StyleInfo
.family
));
1443 if(m_ObjectList
.begin() != NULLIT
)
1444 (**m_ObjectList
.begin()).Debug();
1450 wxLayoutLine::Copy(wxLayoutList
*llist
,
1454 CoordType firstOffset
, lastOffset
;
1456 if(to
== -1) to
= GetLength();
1457 if(from
== to
) return;
1459 wxLOiterator first
= FindObject(from
, &firstOffset
);
1460 wxLOiterator last
= FindObject(to
, &lastOffset
);
1462 // Common special case: only one object
1463 if( first
!= NULLIT
&& last
!= NULLIT
&& *first
== *last
)
1465 if( (**first
).GetType() == WXLO_TYPE_TEXT
)
1467 llist
->Insert(new wxLayoutObjectText(
1468 ((wxLayoutObjectText
1469 *)*first
)->GetText().substr(firstOffset
,
1470 lastOffset
-firstOffset
))
1474 else // what can we do?
1476 if(lastOffset
> firstOffset
) // i.e. +1 :-)
1477 llist
->Insert( (**first
).Copy() );
1482 // If we reach here, we can safely copy the whole first object from
1483 // the firstOffset position on:
1484 if((**first
).GetType() == WXLO_TYPE_TEXT
&& firstOffset
!= 0)
1486 llist
->Insert(new wxLayoutObjectText(
1487 ((wxLayoutObjectText
*)*first
)->GetText().substr(firstOffset
))
1490 else if(firstOffset
== 0)
1491 llist
->Insert( (**first
).Copy() );
1492 // else nothing to copy :-(
1494 // Now we copy all objects before the last one:
1495 wxLOiterator i
= first
; i
++;
1496 for( ; i
!= last
; i
++)
1497 llist
->Insert( (**i
).Copy() );
1499 // And now the last object:
1502 if( (**last
).GetType() == WXLO_TYPE_TEXT
)
1504 llist
->Insert(new wxLayoutObjectText(
1505 ((wxLayoutObjectText
*)*last
)->GetText().substr(0,lastOffset
))
1509 llist
->Insert( (**last
).Copy() );
1514 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1516 The wxLayoutList object
1518 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1520 wxLayoutList::wxLayoutList()
1522 #ifdef WXLAYOUT_USE_CARET
1524 #endif // WXLAYOUT_USE_CARET
1528 SetAutoFormatting(TRUE
);
1529 ForceTotalLayout(TRUE
); // for the first time, do all
1530 InvalidateUpdateRect();
1534 wxLayoutList::~wxLayoutList()
1536 SetAutoFormatting(FALSE
);
1539 m_FirstLine
->DeleteLine(false, this);
1541 wxASSERT_MSG( m_numLines
== 0, "line count calculation broken" );
1545 wxLayoutList::Empty(void)
1548 m_FirstLine
= m_FirstLine
->DeleteLine(false, this);
1550 m_CursorPos
= wxPoint(0,0);
1551 m_CursorScreenPos
= wxPoint(0,0);
1552 m_CursorSize
= wxPoint(0,0);
1553 m_movedCursor
= true;
1554 m_FirstLine
= new wxLayoutLine(NULL
, this); // empty first line
1555 m_CursorLine
= m_FirstLine
;
1556 InvalidateUpdateRect();
1561 wxLayoutList::InternalClear(void)
1563 m_Selection
.m_selecting
= false;
1564 m_Selection
.m_valid
= false;
1566 m_DefaultStyleInfo
.family
= wxSWISS
;
1567 m_DefaultStyleInfo
.size
= WXLO_DEFAULTFONTSIZE
;
1568 m_DefaultStyleInfo
.style
= wxNORMAL
;
1569 m_DefaultStyleInfo
.weight
= wxNORMAL
;
1570 m_DefaultStyleInfo
.underline
= 0;
1571 m_DefaultStyleInfo
.m_fg_valid
= TRUE
;
1572 m_DefaultStyleInfo
.m_fg
= *wxBLACK
;
1573 m_DefaultStyleInfo
.m_bg_valid
= TRUE
;
1574 m_DefaultStyleInfo
.m_bg
= *wxWHITE
;
1576 m_CurrentStyleInfo
= m_DefaultStyleInfo
;
1577 m_CursorStyleInfo
= m_DefaultStyleInfo
;
1581 wxLayoutList::Read(wxString
&istr
)
1583 /* In order to handle input of formatted string "nicely", we need
1584 to restore our current font settings after the string. So first
1585 of all, we create a StyleInfo structure with our current
1587 wxLayoutStyleInfo current_si
= GetStyleInfo();
1589 while(istr
.Length())
1591 wxLayoutObject
*obj
= wxLayoutObject::Read(istr
);
1595 /* Now we use the current_si to restore our last font settings: */
1596 Insert(new wxLayoutObjectCmd(current_si
));
1601 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1602 int underline
, wxColour
*fg
,
1605 if(family
!= -1) m_CurrentStyleInfo
.family
= family
;
1606 if(size
!= -1) m_CurrentStyleInfo
.size
= size
;
1607 if(style
!= -1) m_CurrentStyleInfo
.style
= style
;
1608 if(weight
!= -1) m_CurrentStyleInfo
.weight
= weight
;
1609 if(underline
!= -1) m_CurrentStyleInfo
.underline
= underline
!= 0;
1610 if(fg
) m_CurrentStyleInfo
.m_fg
= *fg
;
1611 if(bg
) m_CurrentStyleInfo
.m_bg
= *bg
;
1613 new wxLayoutObjectCmd(
1614 m_CurrentStyleInfo
.family
,
1615 m_CurrentStyleInfo
.size
,
1616 m_CurrentStyleInfo
.style
,
1617 m_CurrentStyleInfo
.weight
,
1618 m_CurrentStyleInfo
.underline
,
1623 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1624 int underline
, char const *fg
, char const *bg
)
1632 cfg
= wxTheColourDatabase
->FindColour(fg
);
1634 cbg
= wxTheColourDatabase
->FindColour(bg
);
1636 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
1640 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
1641 int underline
, wxColour
*fg
, wxColour
*bg
)
1644 m_DefaultStyleInfo
= wxLayoutStyleInfo(family
, size
, style
, weight
,
1646 m_CurrentStyleInfo
= m_DefaultStyleInfo
;
1648 // Empty() should be called after we set m_DefaultStyleInfo because
1649 // otherwise the style info for the first line (created in Empty()) would be
1655 wxLayoutList::FindText(const wxString
&needle
, const wxPoint
&cpos
) const
1660 for(line
= m_FirstLine
;
1662 line
= line
->GetNextLine())
1664 if(line
->GetLineNumber() >= cpos
.y
)
1666 xpos
= line
->FindText(needle
,
1667 (line
->GetLineNumber() == cpos
.y
) ?
1670 return wxPoint(xpos
, line
->GetLineNumber());
1673 return wxPoint(-1,-1);
1678 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
1680 AddCursorPosToUpdateRect();
1682 wxPoint cursorPosOld
= m_CursorPos
;
1684 wxLayoutLine
*line
= m_FirstLine
;
1685 while(line
&& line
->GetLineNumber() != p
.y
)
1686 line
= line
->GetNextLine();
1687 if(line
&& line
->GetLineNumber() == p
.y
) // found it
1689 m_CursorPos
.y
= p
.y
;
1690 m_CursorLine
= line
;
1691 CoordType len
= line
->GetLength();
1694 m_CursorPos
.x
= p
.x
;
1698 m_CursorPos
.x
= len
;
1702 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1704 return m_CursorPos
== p
;
1708 wxLayoutList::MoveCursorVertically(int n
)
1710 AddCursorPosToUpdateRect();
1712 wxPoint cursorPosOld
= m_CursorPos
;
1715 if(n
< 0) // move up
1717 if(m_CursorLine
== m_FirstLine
) return false;
1718 while(n
< 0 && m_CursorLine
)
1720 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1726 m_CursorLine
= m_FirstLine
;
1732 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1733 m_CursorPos
.x
= m_CursorLine
->GetLength();
1739 wxLayoutLine
*last
= m_CursorLine
;
1740 if(! m_CursorLine
->GetNextLine()) return false;
1741 while(n
> 0 && m_CursorLine
)
1745 m_CursorLine
= m_CursorLine
->GetNextLine();
1749 m_CursorLine
= last
;
1755 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1756 m_CursorPos
.x
= m_CursorLine
->GetLength();
1761 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1767 wxLayoutList::MoveCursorHorizontally(int n
)
1769 AddCursorPosToUpdateRect();
1771 wxPoint cursorPosOld
= m_CursorPos
;
1776 if(m_CursorPos
.x
== 0) // at begin of line
1778 if(! MoveCursorVertically(-1))
1780 MoveCursorToEndOfLine();
1785 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1786 m_CursorPos
.x
-= move
; n
+= move
;
1791 int len
= m_CursorLine
->GetLength();
1792 if(m_CursorPos
.x
== len
) // at end of line
1794 if(! MoveCursorVertically(1))
1796 MoveCursorToBeginOfLine();
1801 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1802 m_CursorPos
.x
+= move
;
1806 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1812 wxLayoutList::MoveCursorWord(int n
, bool untilNext
)
1814 wxCHECK_MSG( m_CursorLine
, false, "no current line" );
1815 wxCHECK_MSG( n
== -1 || n
== +1, false, "not implemented yet" );
1817 CoordType moveDistance
= 0;
1819 wxLayoutLine
*lineCur
= m_CursorLine
;
1820 for ( wxLOiterator i
= lineCur
->FindObject(m_CursorPos
.x
, &offset
);
1828 // moving forward, pass to the first object of the next line
1830 lineCur
= lineCur
->GetNextLine();
1832 i
= lineCur
->GetFirstObject();
1836 // moving backwards, pass to the last object of the prev line
1838 lineCur
= lineCur
->GetPreviousLine();
1840 i
= lineCur
->GetLastObject();
1845 // moved to the end/beginning of text
1852 wxLayoutObject
*obj
= *i
;
1856 // calculate offset: we are either at the very beginning or the very
1857 // end of the object, so it isn't very difficult (the only time when
1858 // offset is != -1 is for the very first iteration when its value is
1859 // returned by FindObject)
1863 offset
= obj
->GetLength();
1866 if( obj
->GetType() != WXLO_TYPE_TEXT
)
1868 // any visible non text objects count as one word
1869 if ( obj
->IsVisibleObject() )
1873 moveDistance
+= obj
->GetLength();
1878 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)obj
;
1880 bool canAdvance
= true;
1882 if ( offset
== tobj
->GetLength() )
1887 // can't move further in this text object
1890 // still should move over the object border
1894 else if ( offset
> 0 )
1896 // offset is off by 1, make it a valid index
1903 const wxString
& text
= tobj
->GetText();
1904 const char *start
= text
.c_str();
1905 const char *end
= start
+ text
.length();
1906 const char *p
= start
+ offset
;
1914 // to the beginning/end of the next/prev word
1915 while ( p
>= start
&& p
< end
&& isspace(*p
) )
1920 // go to the end/beginning of the word (in a broad sense...)
1921 while ( p
>= start
&& p
< end
&& !isspace(*p
) )
1930 // now advance to the beginning of the next word
1931 while ( isspace(*p
) && p
< end
)
1937 // in these 2 cases we took 1 char too much
1938 if ( (p
< start
) || isspace(*p
) )
1944 CoordType moveDelta
= p
- start
- offset
;
1945 if ( (n
< 0) && (offset
== tobj
->GetLength() - 1) )
1947 // because we substracted 1 from offset in this case above, now
1948 // compensate for it
1952 if ( moveDelta
!= 0 )
1954 moveDistance
+= moveDelta
;
1961 // except for the first iteration, offset is calculated in the beginning
1966 MoveCursorHorizontally(moveDistance
);
1972 wxLayoutList::Insert(wxString
const &text
)
1974 wxASSERT(m_CursorLine
);
1975 wxASSERT_MSG( text
.Find('\n') == wxNOT_FOUND
, "use wxLayoutImportText!" );
1980 AddCursorPosToUpdateRect();
1982 if ( !m_CursorLine
->Insert(m_CursorPos
.x
, text
) )
1985 m_CursorPos
.x
+= text
.Length();
1987 m_movedCursor
= true;
1990 m_CursorLine
->MarkDirty();
1996 wxLayoutList::Insert(wxLayoutObject
*obj
)
1998 wxASSERT(m_CursorLine
);
2001 m_CursorLine
= GetFirstLine();
2003 AddCursorPosToUpdateRect();
2005 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
2006 m_CursorPos
.x
+= obj
->GetLength();
2007 m_movedCursor
= true;
2010 m_CursorLine
->MarkDirty();
2016 wxLayoutList::Insert(wxLayoutList
*llist
)
2021 for(wxLayoutLine
*line
= llist
->GetFirstLine();
2023 line
= line
->GetNextLine()
2026 for(wxLOiterator i
= line
->GetFirstObject();
2036 wxLayoutList::LineBreak(void)
2038 wxASSERT(m_CursorLine
);
2040 AddCursorPosToUpdateRect();
2042 wxPoint
position(m_CursorLine
->GetPosition());
2045 width
= m_CursorLine
->GetWidth(),
2046 height
= m_CursorLine
->GetHeight();
2048 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
, this);
2049 if(m_CursorLine
->GetPreviousLine() == NULL
)
2050 m_FirstLine
= m_CursorLine
;
2054 // The following code will produce a height which is guaranteed to
2055 // be too high: old lineheight + the height of both new lines.
2056 // We can probably drop the old line height and start with height =
2058 wxLayoutLine
*prev
= m_CursorLine
->GetPreviousLine();
2060 height
+= prev
->GetHeight();
2061 height
+= m_CursorLine
->GetHeight();
2063 m_movedCursor
= true;
2065 SetUpdateRect(position
);
2066 SetUpdateRect(position
.x
+ width
+ MSW_CORRECTION
,
2067 position
.y
+ height
+ MSW_CORRECTION
);
2073 wxLayoutList::WrapLine(CoordType column
)
2075 if(m_CursorPos
.x
<= column
|| column
< 1)
2076 return false; // do nothing yet
2079 CoordType xpos
= m_CursorLine
->GetWrapPosition(column
);
2081 return false; // cannot break line
2083 CoordType newpos
= m_CursorPos
.x
- xpos
- 1;
2084 m_CursorPos
.x
= xpos
;
2086 AddCursorPosToUpdateRect();
2089 Delete(1); // delete the space
2090 m_CursorPos
.x
= newpos
;
2092 m_CursorLine
->MarkDirty();
2094 m_movedCursor
= true;
2101 wxLayoutList::Delete(CoordType npos
)
2103 wxCHECK_MSG(m_CursorLine
, false, "can't delete in non existing line");
2108 AddCursorPosToUpdateRect();
2110 // were other lines appended to this one (this is important to know because
2111 // this means that our width _increased_ as the result of deletion)
2112 bool wasMerged
= false;
2114 // the size of the region to update
2115 CoordType totalHeight
= m_CursorLine
->GetHeight(),
2116 totalWidth
= m_CursorLine
->GetWidth();
2121 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
2125 // More to delete, continue on next line.
2127 // First, check if line is empty:
2128 if(m_CursorLine
->GetLength() == 0)
2130 // in this case, updating could probably be optimised
2132 wxASSERT(DeleteLines(1) == 0);
2141 // Need to join next line
2142 if(! m_CursorLine
->GetNextLine())
2147 wxLayoutLine
*next
= m_CursorLine
->GetNextLine();
2150 totalHeight
+= next
->GetHeight();
2151 totalWidth
+= next
->GetWidth();
2153 m_CursorLine
->MergeNextLine(this);
2158 wxFAIL_MSG("can't delete all this");
2168 // we need to update the whole tail of the line and the lines which
2172 wxPoint
position(m_CursorLine
->GetPosition());
2173 SetUpdateRect(position
);
2174 SetUpdateRect(position
.x
+ totalWidth
+ MSW_CORRECTION
,
2175 position
.y
+ totalHeight
+ MSW_CORRECTION
);
2182 wxLayoutList::DeleteLines(int n
)
2184 wxASSERT(m_CursorLine
);
2187 AddCursorPosToUpdateRect();
2191 if(!m_CursorLine
->GetNextLine())
2192 { // we cannot delete this line, but we can clear it
2193 MoveCursorToBeginOfLine();
2194 DeleteToEndOfLine();
2196 m_CursorLine
->MarkDirty();
2200 line
= m_CursorLine
;
2201 m_CursorLine
= m_CursorLine
->DeleteLine(true, this);
2203 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
2204 wxASSERT(m_FirstLine
);
2205 wxASSERT(m_CursorLine
);
2208 m_CursorLine
->MarkDirty();
2213 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
)
2217 wxLayoutLine
*line
= m_FirstLine
;
2219 // first, make sure everything is calculated - this might not be
2220 // needed, optimise it later
2221 ApplyStyle(m_DefaultStyleInfo
, dc
);
2224 line
->RecalculatePosition(this); // so we don't need to do it all the time
2225 // little condition to speed up redrawing:
2226 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
2227 line
= line
->GetNextLine();
2232 wxLayoutList::GetCursorScreenPos(void) const
2234 return m_CursorScreenPos
;
2238 Is called before each Draw(). Now, it will re-layout all lines which
2242 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
, bool forceAll
,
2243 wxPoint
*cpos
, wxPoint
*csize
)
2245 // first, make sure everything is calculated - this might not be
2246 // needed, optimise it later
2247 ApplyStyle(m_DefaultStyleInfo
, dc
);
2255 ForceTotalLayout(FALSE
);
2258 // If one line was dirty, we need to re-calculate all
2259 // following lines, too.
2260 bool wasDirty
= forceAll
;
2261 wxLayoutLine
*line
= m_FirstLine
;
2265 ApplyStyle(line
->GetStyleInfo(), dc
);
2267 // if any previous line was dirty, we need to layout all
2270 // layout dirty lines:
2272 // always layout the cursor line toupdate the cursor
2273 // position and size:
2274 || line
== m_CursorLine
2275 // or if it's the line we are asked to look for:
2276 || (cpos
&& line
->GetLineNumber() == cpos
->y
)
2282 // The following Layout() calls will update our
2283 // m_CurrentStyleInfo if needed.
2284 if(line
== m_CursorLine
)
2286 line
->Layout(dc
, this,
2287 (wxPoint
*)&m_CursorScreenPos
,
2288 (wxPoint
*)&m_CursorSize
,
2291 // we cannot layout the line twice, so copy the coords:
2292 if(cpos
&& line
->GetLineNumber() == cpos
->y
)
2294 *cpos
= m_CursorScreenPos
;
2296 *csize
= m_CursorSize
;
2300 if(cpos
&& line
->GetLineNumber() == cpos
->y
)
2301 line
->Layout(dc
, this,
2303 csize
, NULL
, cpos
->x
);
2305 line
->Layout(dc
, this);
2306 // little condition to speed up redrawing:
2307 if(bottom
!= -1 && line
->GetPosition().y
> bottom
)
2310 line
= line
->GetNextLine();
2313 #ifndef WXLAYOUT_USE_CARET
2314 // can only be 0 if we are on the first line and have no next line
2315 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
2316 m_CursorLine
->GetNextLine() == NULL
&&
2317 m_CursorLine
== m_FirstLine
));
2318 #endif // WXLAYOUT_USE_CARET
2319 AddCursorPosToUpdateRect();
2323 wxLayoutList::GetScreenPos(wxDC
&dc
, const wxPoint
&cpos
, wxPoint
*csize
)
2326 Layout(dc
, -1, false, &pos
, csize
);
2331 wxLayoutList::Draw(wxDC
&dc
,
2332 wxPoint
const &offset
,
2337 wxLayoutLine
*line
= m_FirstLine
;
2339 if ( m_Selection
.m_discarded
)
2341 // calculate them if we don't have them already
2342 if ( !m_Selection
.HasValidScreenCoords() )
2344 m_Selection
.m_ScreenA
= GetScreenPos(dc
, m_Selection
.m_CursorA
);
2345 m_Selection
.m_ScreenB
= GetScreenPos(dc
, m_Selection
.m_CursorB
);
2348 // invalidate the area which was previousle selected - and which is not
2349 // selected any more
2350 SetUpdateRect(m_Selection
.m_ScreenA
);
2351 SetUpdateRect(m_Selection
.m_ScreenB
);
2353 m_Selection
.m_discarded
= false;
2356 /* This call to Layout() will re-calculate and update all lines
2361 ApplyStyle(m_DefaultStyleInfo
, dc
);
2362 wxBrush
brush(m_CurrentStyleInfo
.m_bg
, wxSOLID
);
2364 dc
.SetBackgroundMode(wxTRANSPARENT
);
2368 // only draw if between top and bottom:
2370 line
->GetPosition().y
+ line
->GetHeight() > top
))
2372 ApplyStyle(line
->GetStyleInfo(), dc
);
2373 // little condition to speed up redrawing:
2375 && line
->GetPosition().y
2376 +(clipStrictly
? line
->GetHeight() : 0) >= bottom
)
2378 line
->Draw(dc
, this, offset
);
2380 line
= line
->GetNextLine();
2382 InvalidateUpdateRect();
2384 WXLO_DEBUG(("Selection is %s : l%d,%ld/%ld,%ld",
2385 m_Selection
.m_valid
? "valid" : "invalid",
2386 m_Selection
.m_CursorA
.x
, m_Selection
.m_CursorA
.y
,
2387 m_Selection
.m_CursorB
.x
, m_Selection
.m_CursorB
.y
));
2391 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
,
2395 // First, find the right line:
2397 *line
= m_FirstLine
,
2398 *lastline
= m_FirstLine
;
2401 ApplyStyle(m_DefaultStyleInfo
, dc
);
2404 p
= line
->GetPosition();
2405 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
2408 line
= line
->GetNextLine();
2411 bool didFind
= line
!= NULL
;
2415 // use the last line:
2420 cursorPos
->y
= line
->GetLineNumber();
2422 bool foundinline
= true;
2425 // Now, find the object in the line:
2430 i
= line
->FindObjectScreen(dc
, this,
2437 i
= line
->FindObjectScreen(dc
, this,
2442 *found
= didFind
&& foundinline
;
2444 return (i
== NULLIT
) ? NULL
: *i
;
2449 wxLayoutList::GetSize(void) const
2452 *line
= m_FirstLine
,
2455 return wxPoint(0,0);
2457 wxPoint
maxPoint(0,0);
2462 if(line
->GetWidth() > maxPoint
.x
)
2463 maxPoint
.x
= line
->GetWidth();
2465 line
= line
->GetNextLine();
2468 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
2470 // if the line was just added, its height would be 0 and we can't call
2471 // Layout() from here because we don't have a dc and we might be not drawing
2472 // at all, besides... So take the cursor height by default (taking 0 is bad
2473 // because then the scrollbars won't be resized and the new line won't be
2475 if ( last
->IsDirty() )
2477 if ( last
->GetHeight() == 0 )
2478 maxPoint
.y
+= m_CursorSize
.y
;
2479 if ( last
->GetWidth() == 0 && maxPoint
.x
< m_CursorSize
.x
)
2480 maxPoint
.x
= m_CursorSize
.x
;
2488 wxLayoutList::DrawCursor(wxDC
&dc
, bool active
, wxPoint
const &translate
)
2490 if ( m_movedCursor
)
2491 m_movedCursor
= false;
2493 wxPoint
coords(m_CursorScreenPos
);
2494 coords
+= translate
;
2496 #ifdef WXLAYOUT_DEBUG
2497 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
2498 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
2499 (long)coords
.x
, (long)coords
.y
,
2500 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
2501 (long)m_CursorLine
->GetLineNumber(),
2502 (long)m_CursorLine
->GetLength()));
2504 wxLogStatus("Cursor is at (%d, %d)", m_CursorPos
.x
, m_CursorPos
.y
);
2507 #ifdef WXLAYOUT_USE_CARET
2508 m_caret
->Move(coords
);
2509 #else // !WXLAYOUT_USE_CARET
2511 wxASSERT(m_CursorSize
.x
>= WXLO_MINIMUM_CURSOR_WIDTH
);
2512 dc
.SetBrush(*wxWHITE_BRUSH
);
2513 //FIXME: wxGTK XOR is borken at the moment!!!dc.SetLogicalFunction(wxXOR);
2514 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
2517 dc
.SetLogicalFunction(wxXOR
);
2518 dc
.DrawRectangle(coords
.x
, coords
.y
,
2519 m_CursorSize
.x
, m_CursorSize
.y
);
2520 SetUpdateRect(coords
.x
, coords
.y
);
2521 SetUpdateRect(coords
.x
+m_CursorSize
.x
, coords
.y
+m_CursorSize
.y
);
2525 dc
.SetLogicalFunction(wxCOPY
);
2526 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
2527 coords
.x
, coords
.y
);
2528 SetUpdateRect(coords
.x
, coords
.y
+m_CursorSize
.y
-1);
2529 SetUpdateRect(coords
.x
, coords
.y
);
2531 dc
.SetLogicalFunction(wxCOPY
);
2532 //dc.SetBrush(wxNullBrush);
2533 #endif // WXLAYOUT_USE_CARET/!WXLAYOUT_USE_CARET
2537 wxLayoutList::SetUpdateRect(CoordType x
, CoordType y
)
2539 if(m_UpdateRectValid
)
2540 GrowRect(m_UpdateRect
, x
, y
);
2545 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
2546 m_UpdateRect
.height
= 4;// wxGTK :-)
2547 m_UpdateRectValid
= true;
2552 wxLayoutList::StartSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2554 wxPoint
cpos(cposOrig
);
2557 WXLO_DEBUG(("Starting selection at %ld/%ld", cpos
.x
, cpos
.y
));
2558 m_Selection
.m_CursorA
= cpos
;
2559 m_Selection
.m_CursorB
= cpos
;
2560 m_Selection
.m_ScreenA
= spos
;
2561 m_Selection
.m_ScreenB
= spos
;
2562 m_Selection
.m_selecting
= true;
2563 m_Selection
.m_valid
= false;
2567 wxLayoutList::ContinueSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2569 wxPoint
cpos(cposOrig
);
2573 wxASSERT(m_Selection
.m_selecting
== true);
2574 wxASSERT(m_Selection
.m_valid
== false);
2575 WXLO_DEBUG(("Continuing selection at %ld/%ld", cpos
.x
, cpos
.y
));
2577 m_Selection
.m_ScreenB
= spos
;
2578 m_Selection
.m_CursorB
= cpos
;
2582 wxLayoutList::EndSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2584 wxPoint
cpos(cposOrig
);
2585 if(cpos
.x
== -1) cpos
= m_CursorPos
;
2586 ContinueSelection(cpos
, spos
);
2587 WXLO_DEBUG(("Ending selection at %ld/%ld", cpos
.x
, cpos
.y
));
2588 // we always want m_CursorA <= m_CursorB!
2589 if( m_Selection
.m_CursorA
> m_Selection
.m_CursorB
)
2591 // exchange the start/end points
2592 wxPoint help
= m_Selection
.m_CursorB
;
2593 m_Selection
.m_CursorB
= m_Selection
.m_CursorA
;
2594 m_Selection
.m_CursorA
= help
;
2596 help
= m_Selection
.m_ScreenB
;
2597 m_Selection
.m_ScreenB
= m_Selection
.m_ScreenA
;
2598 m_Selection
.m_ScreenA
= help
;
2600 m_Selection
.m_selecting
= false;
2601 m_Selection
.m_valid
= true;
2602 /// In case we just clicked somewhere, the selection will have zero
2603 /// size, so we discard it immediately.
2604 if(m_Selection
.m_CursorA
== m_Selection
.m_CursorB
)
2609 wxLayoutList::DiscardSelection()
2611 if ( !HasSelection() )
2614 m_Selection
.m_valid
=
2615 m_Selection
.m_selecting
= false;
2616 m_Selection
.m_discarded
= true;
2620 wxLayoutList::IsSelecting(void) const
2622 return m_Selection
.m_selecting
;
2626 wxLayoutList::IsSelected(const wxPoint
&cursor
) const
2628 if ( !HasSelection() )
2632 (m_Selection
.m_CursorA
<= cursor
2633 && cursor
<= m_Selection
.m_CursorB
)
2634 || (m_Selection
.m_CursorB
<= cursor
2635 && cursor
<= m_Selection
.m_CursorA
)
2640 /** Tests whether this layout line is selected and needs
2642 @param line to test for
2643 @return 0 = not selected, 1 = fully selected, -1 = partially
2647 wxLayoutList::IsSelected(const wxLayoutLine
*line
, CoordType
*from
,
2650 wxASSERT(line
); wxASSERT(to
); wxASSERT(from
);
2652 if(! m_Selection
.m_valid
&& ! m_Selection
.m_selecting
)
2655 CoordType y
= line
->GetLineNumber();
2657 (m_Selection
.m_CursorA
.y
< y
&& m_Selection
.m_CursorB
.y
> y
)
2658 || (m_Selection
.m_CursorB
.y
< y
&& m_Selection
.m_CursorA
.y
> y
)
2661 else if(m_Selection
.m_CursorA
.y
== y
)
2663 *from
= m_Selection
.m_CursorA
.x
;
2664 if(m_Selection
.m_CursorB
.y
== y
)
2665 *to
= m_Selection
.m_CursorB
.x
;
2668 if(m_Selection
.m_CursorB
> m_Selection
.m_CursorA
)
2669 *to
= line
->GetLength();
2675 CoordType help
= *to
;
2681 else if(m_Selection
.m_CursorB
.y
== y
)
2683 *to
= m_Selection
.m_CursorB
.x
;
2684 if(m_Selection
.m_CursorA
.y
== y
)
2685 *from
= m_Selection
.m_CursorA
.x
;
2688 if(m_Selection
.m_CursorB
> m_Selection
.m_CursorA
)
2691 *from
= line
->GetLength();
2695 CoordType help
= *to
;
2706 wxLayoutList::DeleteSelection(void)
2708 if(! m_Selection
.m_valid
)
2711 m_Selection
.m_valid
= false;
2713 // Only delete part of the current line?
2714 if(m_Selection
.m_CursorA
.y
== m_Selection
.m_CursorB
.y
)
2716 MoveCursorTo(m_Selection
.m_CursorA
);
2717 Delete(m_Selection
.m_CursorB
.x
- m_Selection
.m_CursorA
.x
);
2721 // We now know that the two lines are different:
2724 * firstLine
= GetLine(m_Selection
.m_CursorA
.y
),
2725 * lastLine
= GetLine(m_Selection
.m_CursorB
.y
);
2726 // be a bit paranoid:
2727 if(! firstLine
|| ! lastLine
)
2730 // First, delete what's left of this line:
2731 MoveCursorTo(m_Selection
.m_CursorA
);
2732 DeleteToEndOfLine();
2734 wxLayoutLine
*prevLine
= firstLine
->GetPreviousLine(),
2735 *nextLine
= firstLine
->GetNextLine();
2736 while(nextLine
&& nextLine
!= lastLine
)
2737 nextLine
= nextLine
->DeleteLine(false, this);
2739 // Now nextLine = lastLine;
2740 Delete(1); // This joins firstLine and nextLine
2741 Delete(m_Selection
.m_CursorB
.x
); // This deletes the first x positions
2743 // Recalculate the line positions and numbers but notice that firstLine
2744 // might not exist any more - it could be deleted by Delete(1) above
2745 wxLayoutLine
*firstLine2
= prevLine
? prevLine
->GetNextLine() : m_FirstLine
;
2746 firstLine2
->MarkDirty();
2749 /// Starts highlighting the selection
2751 wxLayoutList::StartHighlighting(wxDC
&dc
)
2754 dc
.SetTextForeground(m_CurrentStyleInfo
.m_bg
);
2755 dc
.SetTextBackground(m_CurrentStyleInfo
.m_fg
);
2756 dc
.SetBackgroundMode(wxSOLID
);
2760 /// Ends highlighting the selection
2762 wxLayoutList::EndHighlighting(wxDC
&dc
)
2765 dc
.SetTextForeground(m_CurrentStyleInfo
.m_fg
);
2766 dc
.SetTextBackground(m_CurrentStyleInfo
.m_bg
);
2767 dc
.SetBackgroundMode(wxTRANSPARENT
);
2773 wxLayoutList::GetLine(CoordType index
) const
2775 wxASSERT_MSG( (0 <= index
) && (index
< (CoordType
)m_numLines
),
2779 CoordType n
= index
;
2781 CoordType lineNo
= 0;
2784 for ( line
= m_FirstLine
; line
&& n
-- > 0; line
=
2785 line
->GetNextLine() )
2788 wxASSERT(line
->GetLineNumber() == lineNo
);
2795 // should be the right one
2796 wxASSERT( line
->GetLineNumber() == index
);
2804 wxLayoutList::Copy(const wxPoint
&from
,
2811 for(firstLine
= m_FirstLine
;
2812 firstLine
&& firstLine
->GetLineNumber() < from
.y
;
2813 firstLine
=firstLine
->GetNextLine())
2815 if(!firstLine
|| firstLine
->GetLineNumber() != from
.y
)
2818 for(lastLine
= m_FirstLine
;
2819 lastLine
&& lastLine
->GetLineNumber() < to
.y
;
2820 lastLine
=lastLine
->GetNextLine())
2822 if(!lastLine
|| lastLine
->GetLineNumber() != to
.y
)
2827 wxLayoutLine
*tmp
= firstLine
;
2828 firstLine
= lastLine
;
2832 wxLayoutList
*llist
= new wxLayoutList();
2834 if(firstLine
== lastLine
)
2836 firstLine
->Copy(llist
, from
.x
, to
.x
);
2840 // Extract objects from first line
2841 firstLine
->Copy(llist
, from
.x
);
2843 // Extract all lines between
2844 for(wxLayoutLine
*line
= firstLine
->GetNextLine();
2846 line
= line
->GetNextLine())
2851 // Extract objects from last line
2852 lastLine
->Copy(llist
, 0, to
.x
);
2858 wxLayoutList::GetSelection(wxLayoutDataObject
*wxlo
, bool invalidate
)
2860 if(! m_Selection
.m_valid
)
2862 if(m_Selection
.m_selecting
)
2868 if(invalidate
) m_Selection
.m_valid
= false;
2870 wxLayoutList
*llist
= Copy( m_Selection
.m_CursorA
,
2871 m_Selection
.m_CursorB
);
2873 if(llist
&& wxlo
) // export as data object, too
2877 wxLayoutExportObject
*exp
;
2878 wxLayoutExportStatus
status(llist
);
2879 while((exp
= wxLayoutExport( &status
, WXLO_EXPORT_AS_OBJECTS
)) != NULL
)
2881 if(exp
->type
== WXLO_EXPORT_EMPTYLINE
)
2882 ; //FIXME missing support for linebreaks in string format
2884 exp
->content
.object
->Write(string
);
2887 wxlo
->SetLayoutData(string
);
2894 #define COPY_SI(what) if(si.what != -1) { m_CurrentStyleInfo.what = si.what; fontChanged = TRUE; }
2897 wxLayoutList::ApplyStyle(wxLayoutStyleInfo
const &si
, wxDC
&dc
)
2899 bool fontChanged
= FALSE
;
2906 dc
.SetFont( m_FontCache
.GetFont(m_CurrentStyleInfo
) );
2910 m_CurrentStyleInfo
.m_fg
= si
.m_fg
;
2911 m_CurrentStyleInfo
.m_fg_valid
= true;
2912 dc
.SetTextForeground(m_CurrentStyleInfo
.m_fg
);
2916 m_CurrentStyleInfo
.m_bg
= si
.m_bg
;
2917 m_CurrentStyleInfo
.m_bg_valid
= true;
2918 dc
.SetTextBackground(m_CurrentStyleInfo
.m_bg
);
2923 #ifdef WXLAYOUT_DEBUG
2926 wxLayoutList::Debug(void)
2928 WXLO_DEBUG(("Cursor is in line %d, screen pos = (%d, %d)",
2929 m_CursorLine
->GetLineNumber(),
2930 m_CursorScreenPos
.x
, m_CursorScreenPos
.y
));
2933 for(line
= m_FirstLine
; line
; line
= line
->GetNextLine())
2942 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2946 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2948 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
2949 wxString
const & title
)
2954 // remove any highlighting which could interfere with printing:
2955 m_llist
->StartSelection();
2956 m_llist
->EndSelection();
2957 // force a full layout of the list:
2958 m_llist
->ForceTotalLayout();
2959 // layout is called in ScaleDC() when we have a DC
2962 wxLayoutPrintout::~wxLayoutPrintout()
2967 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
2969 // The following bit is taken from the printing sample, let's see
2970 // whether it works for us.
2972 /* You might use THIS code to set the printer DC to ROUGHLY reflect
2973 * the screen text size. This page also draws lines of actual length 5cm
2976 // Get the logical pixels per inch of screen and printer
2977 int ppiScreenX
, ppiScreenY
;
2978 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
2979 int ppiPrinterX
, ppiPrinterY
;
2980 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
2982 if(ppiScreenX
== 0) // not yet set, need to guess
2987 if(ppiPrinterX
== 0) // not yet set, need to guess
2993 // This scales the DC so that the printout roughly represents the
2994 // the screen scaling. The text point size _should_ be the right size
2995 // but in fact is too small for some reason. This is a detail that will
2996 // need to be addressed at some point but can be fudged for the
2998 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
3000 // Now we have to check in case our real page size is reduced
3001 // (e.g. because we're drawing to a print preview memory DC)
3002 int pageWidth
, pageHeight
;
3004 dc
->GetSize(&w
, &h
);
3005 GetPageSizePixels(&pageWidth
, &pageHeight
);
3006 if(pageWidth
!= 0) // doesn't work always
3008 // If printer pageWidth == current DC width, then this doesn't
3009 // change. But w might be the preview bitmap width, so scale down.
3010 scale
= scale
* (float)(w
/(float)pageWidth
);
3012 dc
->SetUserScale(scale
, scale
);
3016 bool wxLayoutPrintout::OnPrintPage(int page
)
3025 top
= (page
- 1)*m_PrintoutHeight
;
3026 bottom
= top
+ m_PrintoutHeight
;
3028 WXLO_DEBUG(("OnPrintPage(%d) printing from %d to %d", page
, top
,
3030 // SetDeviceOrigin() doesn't work here, so we need to manually
3031 // translate all coordinates.
3032 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
3033 m_llist
->Draw(*dc
, translate
, top
, bottom
, TRUE
/* clip strictly
3041 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
3043 /* We allocate a temporary wxDC for printing, so that we can
3044 determine the correct paper size and scaling. We don't actually
3045 print anything on it. */
3047 wxPrinterDC
*psdc
= new wxPrinterDC("","",WXLLIST_TEMPFILE
,false);
3049 wxPostScriptDC
*psdc
= new wxPostScriptDC(WXLLIST_TEMPFILE
,false);
3052 psdc
->StartDoc(m_title
);
3053 // before we draw anything, me must make sure the list is properly
3055 m_llist
->Layout(*psdc
);
3057 float scale
= ScaleDC(psdc
);
3059 psdc
->GetSize(&m_PageWidth
, &m_PageHeight
);
3061 // This sets a left/top origin of 15% and 5%:
3062 m_Offset
= wxPoint((15*m_PageWidth
)/100, (5*m_PageHeight
)/100);
3064 // This is the length of the printable area.
3065 m_PrintoutHeight
= m_PageHeight
- 2*m_Offset
.y
;
3066 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
3069 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
3072 *maxPage
= m_NumOfPages
;
3075 *selPageTo
= m_NumOfPages
;
3078 wxRemoveFile(WXLLIST_TEMPFILE
);
3081 bool wxLayoutPrintout::HasPage(int pageNum
)
3083 return pageNum
<= m_NumOfPages
;
3087 Stupid wxWindows doesn't draw proper ellipses, so we comment this
3088 out. It's a waste of paper anyway.
3092 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
3093 wxPoint topleft
, wxPoint bottomright
,
3096 // make backups of all essential parameters
3097 const wxBrush
& brush
= dc
.GetBrush();
3098 const wxPen
& pen
= dc
.GetPen();
3099 const wxFont
& font
= dc
.GetFont();
3101 dc
.SetBrush(*wxWHITE_BRUSH
);
3102 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
3103 dc
.DrawRoundedRectangle(topleft
.x
,
3104 topleft
.y
,bottomright
.x
-topleft
.x
,
3105 bottomright
.y
-topleft
.y
);
3106 dc
.SetBrush(*wxBLACK_BRUSH
);
3107 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
3108 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
3112 page
= "9999/9999 "; // many pages...
3114 dc
.GetTextExtent(page
,&w
,&h
);
3115 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
3116 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
3117 dc
.GetTextExtent("XXXX", &w
,&h
);
3118 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);
3129 wxFontCache::GetFont(int family
, int size
, int style
, int weight
,
3132 for(wxFCEList::iterator i
= m_FontList
.begin();
3133 i
!= m_FontList
.end(); i
++)
3134 if( (**i
).Matches(family
, size
, style
, weight
, underline
) )
3135 return (**i
).GetFont();
3137 wxFontCacheEntry
*fce
= new wxFontCacheEntry(family
, size
, style
,
3139 m_FontList
.push_back(fce
);
3140 return fce
->GetFont();