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
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
64 /// This should never really get created
65 #define WXLLIST_TEMPFILE "__wxllist.tmp"
69 # define TypeString(t) g_aTypeStrings[t]
70 # define WXLO_DEBUG(x) wxLogDebug x
72 static const char *g_aTypeStrings
[] =
74 "invalid", "text", "cmd", "icon"
77 wxLayoutObject::DebugDump(void) const
80 str
.Printf("%s",g_aTypeStrings
[GetType()]);
84 # define TypeString(t) ""
85 # define WXLO_DEBUG(x)
89 // FIXME under MSW, this constant is needed to make the thing properly redraw
90 // itself - I don't know where the size calculation error is and I can't
91 // waste time looking for it right now. Search for occurences of
92 // MSW_CORRECTION to find all the places where I did it.
94 static const int MSW_CORRECTION
= 10;
96 static const int MSW_CORRECTION
= 0;
99 /// Cursors smaller than this disappear in XOR drawing mode
100 #define WXLO_MINIMUM_CURSOR_WIDTH 4
102 /// Use this character to estimate a cursor size when none is available.
103 #define WXLO_CURSORCHAR "E"
104 /** @name Helper functions */
106 /// allows me to compare to wxPoints
107 bool operator <=(wxPoint
const &p1
, wxPoint
const &p2
)
109 return p1
.y
< p2
.y
|| (p1
.y
== p2
.y
&& p1
.x
<= p2
.x
);
113 The following STAY HERE until we have a working wxGTK again!!!
115 #ifndef wxWANTS_CHARS
116 /// allows me to compare to wxPoints
117 bool operator ==(wxPoint
const &p1
, wxPoint
const &p2
)
119 return p1
.x
== p2
.x
&& p1
.y
== p2
.y
;
122 /// allows me to compare to wxPoints
123 bool operator !=(wxPoint
const &p1
, wxPoint
const &p2
)
125 return p1
.x
!= p2
.x
|| p1
.y
!= p2
.y
;
128 wxPoint
& operator += (wxPoint
&p1
, wxPoint
const &p2
)
136 /// allows me to compare to wxPoints
137 bool operator>(wxPoint
const &p1
, wxPoint
const &p2
)
142 /// grows a wxRect so that it includes the given point
145 void GrowRect(wxRect
&r
, CoordType x
, CoordType y
)
149 else if(r
.x
+ r
.width
< x
)
154 else if(r
.y
+ r
.height
< y
)
160 /// returns true if the point is in the rectangle
162 bool Contains(const wxRect
&r
, const wxPoint
&p
)
164 return r
.x
<= p
.x
&& r
.y
<= p
.y
&& (r
.x
+r
.width
) >= p
.x
&& (r
.y
+ r
.height
) >= p
.y
;
173 void ReadString(wxString
&to
, wxString
&from
)
176 const char *cptr
= from
.c_str();
177 while(*cptr
&& *cptr
!= '\n')
183 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
187 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
191 wxLayoutObject::Read(wxString
&istr
)
194 ReadString(tmp
, istr
);
195 int type
= WXLO_TYPE_INVALID
;
196 sscanf(tmp
.c_str(),"%d", &type
);
201 return wxLayoutObjectText::Read(istr
);
203 return wxLayoutObjectCmd::Read(istr
);
205 return wxLayoutObjectIcon::Read(istr
);
211 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
215 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
217 wxLayoutObjectText::wxLayoutObjectText(const wxString
&txt
)
227 wxLayoutObjectText::Copy(void)
229 wxLayoutObjectText
*obj
= new wxLayoutObjectText(m_Text
);
230 obj
->m_Width
= m_Width
;
231 obj
->m_Height
= m_Height
;
233 obj
->m_Bottom
= m_Bottom
;
234 obj
->SetUserData(m_UserData
);
240 wxLayoutObjectText::Write(wxString
&ostr
)
242 ostr
<< (int) WXLO_TYPE_TEXT
<< '\n'
247 wxLayoutObjectText::Read(wxString
&istr
)
250 ReadString(text
, istr
);
252 return new wxLayoutObjectText(text
);
256 wxLayoutObjectText::GetSize(CoordType
*top
, CoordType
*bottom
) const
259 *top
= m_Top
; *bottom
= m_Bottom
;
260 return wxPoint(m_Width
, m_Height
);
264 wxLayoutObjectText::Draw(wxDC
&dc
, wxPoint
const &coords
,
265 wxLayoutList
*wxllist
,
266 CoordType begin
, CoordType end
)
270 // draw the whole object normally
271 dc
.DrawText(m_Text
, coords
.x
, coords
.y
-m_Top
);
275 // highlight the bit between begin and len
278 ypos
= coords
.y
-m_Top
;
279 long width
, height
, descent
;
281 if(begin
< 0) begin
= 0;
282 if( end
> (signed)m_Text
.Length() )
283 end
= m_Text
.Length();
285 wxString str
= m_Text
.Mid(0, begin
);
286 dc
.DrawText(str
, xpos
, ypos
);
287 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
289 wxllist
->StartHighlighting(dc
);
290 str
= m_Text
.Mid(begin
, end
-begin
);
291 dc
.DrawText(str
, xpos
, ypos
);
292 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
294 wxllist
->EndHighlighting(dc
);
295 str
= m_Text
.Mid(end
, m_Text
.Length()-end
);
296 dc
.DrawText(str
, xpos
, ypos
);
301 wxLayoutObjectText::GetOffsetScreen(wxDC
&dc
, CoordType xpos
) const
305 maxlen
= m_Text
.Length();
308 height
, descent
= 0l;
310 if(xpos
== 0) return 0; // easy
312 while(width
< xpos
&& offs
< maxlen
)
314 dc
.GetTextExtent(m_Text
.substr(0,offs
),
315 &width
, &height
, &descent
);
318 /* We have to substract 1 to compensate for the offs++, and another
319 one because we don't want to position the cursor behind the
320 object what we clicked on, but before - otherwise it looks
322 return (xpos
> 2) ? offs
-2 : 0;
326 wxLayoutObjectText::Layout(wxDC
&dc
, class wxLayoutList
*llist
)
330 // now this is done in wxLayoutLine::Layout(), but this code might be
331 // reenabled later - in principle, it's more efficient
333 CoordType widthOld
= m_Width
,
334 heightOld
= m_Height
;
338 CoordType a
,b
,c
,d
,e
,f
;
339 dc
.GetTextExtent("test ", &a
, &b
, &c
);
340 dc
.GetTextExtent("test", &d
, &e
, &f
);
344 dc
.GetTextExtent(" ", &d
, &e
, &f
);
347 dc
.GetTextExtent(m_Text
, &m_Width
, &m_Height
, &descent
);
350 if ( widthOld
!= m_Width
|| heightOld
!= m_Height
)
352 // as the text length changed, it must be refreshed
353 wxLayoutLine
*line
= GetLine();
355 wxCHECK_RET( line
, "wxLayoutObjectText can't refresh itself" );
357 // as our size changed, we need to repaint the part which was appended
358 wxPoint
position(line
->GetPosition());
360 // this is not the most efficient way (we repaint the whole line), but
361 // it's not too slow and is *simple*
362 if ( widthOld
< m_Width
)
364 if ( heightOld
< m_Height
)
365 heightOld
= m_Height
;
367 llist
->SetUpdateRect(position
.x
+ widthOld
+ MSW_CORRECTION
,
368 position
.y
+ heightOld
+ MSW_CORRECTION
);
373 m_Top
= m_Height
- m_Bottom
;
377 #ifdef WXLAYOUT_DEBUG
379 wxLayoutObjectText::DebugDump(void) const
382 str
= wxLayoutObject::DebugDump();
384 str2
.Printf(" `%s`", m_Text
.c_str());
389 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
393 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
395 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
const &icon
)
399 wxFAIL_MSG("invalid icon");
407 // FIXME ugly, ugly, ugly - but the only way to avoid slicing
408 m_Icon
= icon
.GetHBITMAP() ? new wxBitmap(icon
)
409 : new wxBitmap(wxBitmap((const wxBitmap
&)icon
));
411 m_Icon
= new wxBitmap(icon
);
417 wxLayoutObjectIcon::Write(wxString
&ostr
)
419 /* Exports icon through a temporary file. */
421 wxString file
= wxGetTempFileName("wxloexport");
423 ostr
<< (int) WXLO_TYPE_ICON
<< '\n'
425 m_Icon
->SaveFile(file
, WXLO_BITMAP_FORMAT
);
429 wxLayoutObjectIcon::Read(wxString
&istr
)
432 ReadString(file
, istr
);
434 if(! wxFileExists(file
))
436 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon
;
438 if(!obj
->m_Icon
->LoadFile(file
, WXLO_BITMAP_FORMAT
))
448 wxLayoutObjectIcon::Copy(void)
450 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon(new
452 obj
->SetUserData(m_UserData
);
456 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
*icon
)
460 m_Icon
= new wxBitmap
;
464 wxLayoutObjectIcon::Draw(wxDC
&dc
, wxPoint
const &coords
,
465 wxLayoutList
*wxllist
,
466 CoordType begin
, CoordType
/* len */)
468 dc
.DrawBitmap(*m_Icon
, coords
.x
, coords
.y
-m_Icon
->GetHeight(),
469 (m_Icon
->GetMask() == NULL
) ? FALSE
: TRUE
);
473 wxLayoutObjectIcon::Layout(wxDC
& /* dc */, class wxLayoutList
* )
478 wxLayoutObjectIcon::GetSize(CoordType
*top
, CoordType
*bottom
) const
480 *top
= m_Icon
->GetHeight();
482 return wxPoint(m_Icon
->GetWidth(), m_Icon
->GetHeight());
487 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
491 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
494 wxLayoutStyleInfo::wxLayoutStyleInfo(int ifamily
,
506 underline
= iul
!= 0;
508 m_fg_valid
= fg
!= 0;
509 m_bg_valid
= bg
!= 0;
510 m_fg
= m_fg_valid
? *fg
: *wxBLACK
;
511 m_bg
= m_bg_valid
? *bg
: *wxWHITE
;
514 #define COPY_SI_(what) if(right.what != -1) what = right.what;
517 wxLayoutStyleInfo::operator=(const wxLayoutStyleInfo
&right
)
524 if(right
.m_fg_valid
) m_fg
= right
.m_fg
;
525 if(right
.m_bg_valid
) m_bg
= right
.m_bg
;
529 wxLayoutObjectCmd::wxLayoutObjectCmd(int family
, int size
, int style
, int
530 weight
, int underline
,
531 wxColour
*fg
, wxColour
*bg
)
534 m_StyleInfo
= new wxLayoutStyleInfo(family
, size
,style
,weight
,underline
,fg
,bg
);
537 wxLayoutObjectCmd::wxLayoutObjectCmd(const wxLayoutStyleInfo
&si
)
540 m_StyleInfo
= new wxLayoutStyleInfo
;
545 wxLayoutObjectCmd::Copy(void)
547 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd(
552 m_StyleInfo
->underline
,
553 m_StyleInfo
->m_fg_valid
?
554 &m_StyleInfo
->m_fg
: NULL
,
555 m_StyleInfo
->m_bg_valid
?
556 &m_StyleInfo
->m_bg
: NULL
);
557 obj
->SetUserData(m_UserData
);
562 wxLayoutObjectCmd::Write(wxString
&ostr
)
564 ostr
<< (int) WXLO_TYPE_CMD
<< '\n'
565 << (int) m_StyleInfo
->family
<< '\n'
566 << (int) m_StyleInfo
->size
<< '\n'
567 << (int) m_StyleInfo
->style
<< '\n'
568 << (int) m_StyleInfo
->weight
<< '\n'
569 << (int) m_StyleInfo
->underline
<< '\n'
570 << (int) m_StyleInfo
->m_fg_valid
<< '\n'
571 << (int) m_StyleInfo
->m_bg_valid
<< '\n';
572 if(m_StyleInfo
->m_fg_valid
)
574 ostr
<< (int) m_StyleInfo
->m_fg
.Red() << '\n'
575 << (int) m_StyleInfo
->m_fg
.Green() << '\n'
576 << (int) m_StyleInfo
->m_fg
.Blue() << '\n';
578 if(m_StyleInfo
->m_bg_valid
)
580 ostr
<< (int) m_StyleInfo
->m_bg
.Red() << '\n'
581 << (int) m_StyleInfo
->m_bg
.Green() << '\n'
582 << (int) m_StyleInfo
->m_bg
.Blue() << '\n';
587 wxLayoutObjectCmd::Read(wxString
&istr
)
589 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd
;
592 ReadString(tmp
, istr
);
593 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->family
);
594 ReadString(tmp
, istr
);
595 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->size
);
596 ReadString(tmp
, istr
);
597 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->style
);
598 ReadString(tmp
, istr
);
599 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->weight
);
600 ReadString(tmp
, istr
);
601 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->underline
);
602 ReadString(tmp
, istr
);
603 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->m_fg_valid
);
604 ReadString(tmp
, istr
);
605 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->m_bg_valid
);
606 if(obj
->m_StyleInfo
->m_fg_valid
)
608 int red
, green
, blue
;
609 ReadString(tmp
, istr
);
610 sscanf(tmp
.c_str(),"%d", &red
);
611 ReadString(tmp
, istr
);
612 sscanf(tmp
.c_str(),"%d", &green
);
613 ReadString(tmp
, istr
);
614 sscanf(tmp
.c_str(),"%d", &blue
);
615 obj
->m_StyleInfo
->m_fg
= wxColour(red
, green
, blue
);
617 if(obj
->m_StyleInfo
->m_bg_valid
)
619 int red
, green
, blue
;
620 ReadString(tmp
, istr
);
621 sscanf(tmp
.c_str(),"%d", &red
);
622 ReadString(tmp
, istr
);
623 sscanf(tmp
.c_str(),"%d", &green
);
624 ReadString(tmp
, istr
);
625 sscanf(tmp
.c_str(),"%d", &blue
);
626 obj
->m_StyleInfo
->m_bg
= wxColour(red
, green
, blue
);
632 wxLayoutObjectCmd::~wxLayoutObjectCmd()
638 wxLayoutObjectCmd::GetStyle(void) const
644 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint
const & /* coords */,
645 wxLayoutList
*wxllist
,
646 CoordType begin
, CoordType
/* len */)
648 wxASSERT(m_StyleInfo
);
649 wxllist
->ApplyStyle(*m_StyleInfo
, dc
);
653 wxLayoutObjectCmd::Layout(wxDC
&dc
, class wxLayoutList
* llist
)
655 // this get called, so that recalculation uses right font sizes
656 Draw(dc
, wxPoint(0,0), llist
);
660 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
662 The wxLayoutLine object
664 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
666 wxLayoutLine::wxLayoutLine(wxLayoutLine
*prev
, wxLayoutList
*llist
)
668 m_Width
= m_Height
= 0;
677 RecalculatePosition(llist
);
682 m_LineNumber
= m_Previous
->GetLineNumber() + 1;
683 m_Next
= m_Previous
->GetNextLine();
684 m_Previous
->m_Next
= this;
689 m_Next
->m_Previous
= this;
693 m_StyleInfo
= llist
->GetDefaultStyleInfo();
695 llist
->IncNumLines();
698 wxLayoutLine::~wxLayoutLine()
700 // kbList cleans itself
704 wxLayoutLine::RecalculatePosition(wxLayoutList
*llist
)
706 wxASSERT(m_Previous
|| GetLineNumber() == 0);
708 wxPoint
posOld(m_Position
);
712 m_Position
= m_Previous
->GetPosition();
713 m_Position
.y
+= m_Previous
->GetHeight();
716 m_Position
= wxPoint(0,0);
718 if ( m_Position
!= posOld
)
720 // the whole line moved and must be repainted
721 llist
->SetUpdateRect(m_Position
);
722 llist
->SetUpdateRect(m_Position
.x
+ GetWidth() + MSW_CORRECTION
,
723 m_Position
.y
+ GetHeight() + MSW_CORRECTION
);
724 llist
->SetUpdateRect(posOld
);
725 llist
->SetUpdateRect(posOld
.x
+ GetWidth() + MSW_CORRECTION
,
726 posOld
.y
+ GetHeight() + MSW_CORRECTION
);
733 wxLayoutObjectList::iterator
734 wxLayoutLine::FindObject(CoordType xpos
, CoordType
*offset
) const
738 wxLayoutObjectList::iterator
741 CoordType x
= 0, len
;
743 /* We search through the objects. As we don't like returning the
744 object that the cursor is behind, we just remember such an
745 object in "found" so we can return it if there is really no
746 further object following it. */
747 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
749 len
= (**i
).GetLength();
750 if( x
<= xpos
&& xpos
<= x
+ len
)
753 if(xpos
== x
+ len
) // is there another object behind?
755 else // we are really inside this object
758 x
+= (**i
).GetLength();
760 return found
; // ==NULL if really none found
763 wxLayoutObjectList::iterator
764 wxLayoutLine::FindObjectScreen(wxDC
&dc
, wxLayoutList
*llist
,
765 CoordType xpos
, CoordType
*cxpos
,
770 llist
->ApplyStyle(GetStyleInfo(), dc
);
772 wxLayoutObjectList::iterator i
;
773 CoordType x
= 0, cx
= 0, width
;
775 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
777 wxLayoutObject
*obj
= *i
;
778 if ( obj
->GetType() == WXLO_TYPE_CMD
)
780 // this will set the correct font for the objects which follow
781 obj
->Layout(dc
, llist
);
784 width
= obj
->GetWidth();
785 if( x
<= xpos
&& xpos
<= x
+ width
)
787 *cxpos
= cx
+ obj
->GetOffsetScreen(dc
, xpos
-x
);
794 x
+= obj
->GetWidth();
795 cx
+= obj
->GetLength();
798 // behind last object:
803 return m_ObjectList
.tail();
806 /** Finds text in this line.
807 @param needle the text to find
808 @param xpos the position where to start the search
809 @return the cursoor coord where it was found or -1
812 wxLayoutLine::FindText(const wxString
&needle
, CoordType xpos
) const
817 wxString
const *text
;
819 for(wxLOiterator i
= m_ObjectList
.begin(); i
!= m_ObjectList
.end(); i
++)
821 if(cpos
>= xpos
) // search from here!
823 if((**i
).GetType() == WXLO_TYPE_TEXT
)
825 text
= & ((wxLayoutObjectText
*)(*i
))->GetText();
826 relpos
= text
->Find(needle
);
827 if(relpos
>= cpos
-xpos
) // -1 if not found
832 cpos
+= (**i
).GetLength();
835 return -1; // not found
839 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
842 wxASSERT(obj
!= NULL
);
847 wxLOiterator i
= FindObject(xpos
, &offset
);
850 if(xpos
== 0 ) // aha, empty line!
852 m_ObjectList
.push_back(obj
);
853 m_Length
+= obj
->GetLength();
860 CoordType len
= (**i
).GetLength();
861 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
862 { // insert before this object
863 m_ObjectList
.insert(i
,obj
);
864 m_Length
+= obj
->GetLength();
869 if( i
== m_ObjectList
.tail()) // last object?
870 m_ObjectList
.push_back(obj
);
872 { // insert after current object
874 m_ObjectList
.insert(i
,obj
);
876 m_Length
+= obj
->GetLength();
879 /* Otherwise we need to split the current object.
880 Fortunately this can only be a text object. */
881 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
882 wxString left
, right
;
883 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
884 left
= tobj
->GetText().substr(0,offset
);
885 right
= tobj
->GetText().substr(offset
,len
-offset
);
886 // current text object gets set to right half
887 tobj
->GetText() = right
; // set new text
888 // before it we insert the new object
889 m_ObjectList
.insert(i
,obj
);
890 m_Length
+= obj
->GetLength();
891 // and before that we insert the left half
892 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
897 wxLayoutLine::Insert(CoordType xpos
, const wxString
& text
)
904 wxLOiterator i
= FindObject(xpos
, &offset
);
905 if(i
!= NULLIT
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
907 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
908 tobj
->GetText().insert(offset
, text
);
909 m_Length
+= text
.Length();
913 if ( !Insert(xpos
, new wxLayoutObjectText(text
)) )
921 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
923 CoordType offset
, len
;
928 wxLOiterator i
= FindObject(xpos
, &offset
);
931 if(i
== NULLIT
) return npos
;
932 // now delete from that object:
933 if((**i
).GetType() != WXLO_TYPE_TEXT
)
935 if(offset
!= 0) // at end of line after a non-text object
938 len
= (**i
).GetLength();
941 m_ObjectList
.erase(i
);
945 // tidy up: remove empty text objects
946 if((**i
).GetLength() == 0)
948 m_ObjectList
.erase(i
);
952 CoordType max
= (**i
).GetLength() - offset
;
953 if(npos
< max
) max
= npos
;
956 if(xpos
== GetLength())
959 { // at the end of an object
960 // move to begin of next object:
962 continue; // start over
967 if(offset
== 0 && max
== (**i
).GetLength())
968 m_ObjectList
.erase(i
); // remove the whole object
970 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
978 wxLayoutLine::DeleteWord(CoordType xpos
)
984 wxLOiterator i
= FindObject(xpos
, &offset
);
988 if(i
== NULLIT
) return false;
989 if((**i
).GetType() != WXLO_TYPE_TEXT
)
991 // This should only happen when at end of line, behind a non-text
993 if(offset
== (**i
).GetLength()) return false;
994 m_Length
-= (**i
).GetLength(); // -1
995 m_ObjectList
.erase(i
);
996 return true; // we are done
1000 if(offset
== (**i
).GetLength()) // at end of object
1005 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
1007 wxString str
= tobj
->GetText();
1008 str
= str
.substr(offset
,str
.Length()-offset
);
1009 // Find out how many positions we need to delete:
1010 // 1. eat leading space
1011 while(isspace(str
.c_str()[count
])) count
++;
1012 // 2. eat the word itself:
1013 while(isalnum(str
.c_str()[count
])) count
++;
1015 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
1016 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
1022 wxFAIL_MSG("unreachable");
1026 wxLayoutLine::DeleteLine(bool update
, wxLayoutList
*llist
)
1028 // maintain linked list integrity
1030 m_Next
->m_Previous
= m_Previous
;
1032 m_Previous
->m_Next
= m_Next
;
1034 // get the line numbers right again
1035 if ( update
&& m_Next
)
1040 // we can't use m_Next after "delete this", so we must save this pointer
1042 wxLayoutLine
*next
= m_Next
;
1045 llist
->DecNumLines();
1051 wxLayoutLine::Draw(wxDC
&dc
,
1052 wxLayoutList
*llist
,
1053 const wxPoint
& offset
) const
1055 wxLayoutObjectList::iterator i
;
1056 wxPoint pos
= offset
;
1057 pos
= pos
+ GetPosition();
1059 pos
.y
+= m_BaseLine
;
1061 CoordType xpos
= 0; // cursorpos, lenght of line
1063 CoordType from
, to
, tempto
;
1065 int highlight
= llist
->IsSelected(this, &from
, &to
);
1066 // WXLO_DEBUG(("highlight=%d", highlight ));
1067 if(highlight
== 1) // we need to draw the whole line inverted!
1068 llist
->StartHighlighting(dc
);
1070 llist
->EndHighlighting(dc
);
1072 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
1074 if(highlight
== -1) // partially highlight line
1076 // parts of the line need highlighting
1077 tempto
= xpos
+(**i
).GetLength();
1078 (**i
).Draw(dc
, pos
, llist
, from
-xpos
, to
-xpos
);
1081 (**i
).Draw(dc
, pos
, llist
);
1082 pos
.x
+= (**i
).GetWidth();
1083 xpos
+= (**i
).GetLength();
1088 This function does all the recalculation, that is, it should only be
1089 called from within wxLayoutList::Layout(), as it uses the current
1090 list's styleinfo and updates it.
1093 wxLayoutLine::Layout(wxDC
&dc
,
1094 wxLayoutList
*llist
,
1096 wxPoint
*cursorSize
,
1097 wxLayoutStyleInfo
*cursorStyle
,
1099 bool suppressSIupdate
)
1101 wxLayoutObjectList::iterator i
;
1103 // when a line becomes dirty, we redraw it from the place where it was
1104 // changed till the end of line (because the following wxLayoutObjects are
1105 // moved when the preceding one changes) - calculate the update rectangle.
1106 CoordType updateTop
= m_Position
.y
,
1108 updateWidth
= m_Width
,
1109 updateHeight
= m_Height
;
1113 bottomHeight
= 0; // above and below baseline
1115 objTopHeight
, objBottomHeight
; // above and below baseline
1119 CoordType heightOld
= m_Height
;
1125 bool cursorFound
= false;
1127 RecalculatePosition(llist
);
1131 *cursorPos
= m_Position
;
1132 if(cursorSize
) *cursorSize
= wxPoint(0,0);
1135 m_StyleInfo
= llist
->GetStyleInfo(); // save current style
1136 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
1138 wxLayoutObject
*obj
= *i
;
1139 obj
->Layout(dc
, llist
);
1140 wxPoint sizeObj
= obj
->GetSize(&objTopHeight
, &objBottomHeight
);
1142 if(cursorPos
&& ! cursorFound
)
1144 // we need to check whether the text cursor is here
1145 len
= obj
->GetLength();
1146 if(count
<= cx
&& count
+len
> cx
)
1148 if(obj
->GetType() == WXLO_TYPE_TEXT
)
1150 len
= cx
- count
; // pos in object
1151 CoordType width
, height
, descent
;
1152 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
1153 &width
, &height
, &descent
);
1154 cursorPos
->x
+= width
;
1155 cursorPos
->y
= m_Position
.y
;
1157 if(len
< obj
->GetLength())
1158 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
1160 str
= WXLO_CURSORCHAR
;
1161 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
1163 if(cursorStyle
) // set style info
1164 *cursorStyle
= llist
->GetStyleInfo();
1167 // Just in case some joker inserted an empty string object:
1169 width
= WXLO_MINIMUM_CURSOR_WIDTH
;
1172 cursorSize
->x
= width
;
1173 cursorSize
->y
= height
;
1176 cursorFound
= true; // no more checks
1180 // on some other object
1181 CoordType top
, bottom
; // unused
1183 *cursorSize
= obj
->GetSize(&top
,&bottom
);
1184 cursorPos
->y
= m_Position
.y
;
1185 cursorFound
= true; // no more checks
1191 cursorPos
->x
+= obj
->GetWidth();
1195 m_Width
+= sizeObj
.x
;
1196 if(sizeObj
.y
> m_Height
)
1198 m_Height
= sizeObj
.y
;
1201 if(objTopHeight
> topHeight
)
1202 topHeight
= objTopHeight
;
1203 if(objBottomHeight
> bottomHeight
)
1204 bottomHeight
= objBottomHeight
;
1209 if ( updateHeight
< m_Height
)
1210 updateHeight
= m_Height
;
1211 if ( updateWidth
< m_Width
)
1212 updateWidth
= m_Width
;
1214 // update all line if we don't know where to start from
1215 if ( updateLeft
== -1 )
1218 llist
->SetUpdateRect(updateLeft
, updateTop
);
1219 llist
->SetUpdateRect(updateLeft
+ updateWidth
+ MSW_CORRECTION
,
1220 updateTop
+ updateHeight
+ MSW_CORRECTION
);
1223 if(topHeight
+ bottomHeight
> m_Height
)
1225 m_Height
= topHeight
+bottomHeight
;
1228 m_BaseLine
= topHeight
;
1232 CoordType width
, height
, descent
;
1233 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1235 m_BaseLine
= m_Height
- descent
;
1238 // tell next line about coordinate change
1239 if(m_Next
&& m_Height
!= heightOld
)
1241 m_Next
->MarkDirty();
1244 // We need to check whether we found a valid cursor size:
1245 if(cursorPos
&& cursorSize
)
1247 // this might be the case if the cursor is at the end of the
1248 // line or on a command object:
1249 if(cursorSize
->x
< WXLO_MINIMUM_CURSOR_WIDTH
)
1251 CoordType width
, height
, descent
;
1252 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1253 cursorSize
->x
= width
;
1254 cursorSize
->y
= height
;
1256 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
1257 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
1264 wxLayoutLine::Break(CoordType xpos
, wxLayoutList
*llist
)
1266 wxASSERT(xpos
>= 0);
1271 wxLOiterator i
= FindObject(xpos
, &offset
);
1273 // must be at the end of the line then
1274 return new wxLayoutLine(this, llist
);
1277 wxLayoutLine
*newLine
= new wxLayoutLine(this, llist
);
1278 // split object at i:
1279 if((**i
).GetType() == WXLO_TYPE_TEXT
1281 && offset
!= (**i
).GetLength() )
1283 wxString left
, right
;
1284 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
1285 left
= tobj
->GetText().substr(0,offset
);
1286 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
1287 // current text object gets set to left half
1288 tobj
->GetText() = left
; // set new text
1289 newLine
->Append(new wxLayoutObjectText(right
));
1290 m_Length
-= right
.Length();
1291 i
++; // don't move this object to the new list
1296 i
++; // move objects from here to new list
1299 while(i
!= m_ObjectList
.end())
1301 wxLayoutObject
*obj
= *i
;
1302 newLine
->Append(obj
);
1303 m_Length
-= obj
->GetLength();
1305 m_ObjectList
.remove(i
); // remove without deleting it
1308 m_Next
->MarkDirty();
1313 wxLayoutLine::Wrap(CoordType wrapmargin
, wxLayoutList
*llist
)
1315 if(GetLength() < wrapmargin
)
1316 return FALSE
; // nothing to do
1318 // find the object which covers the wrapmargin:
1320 wxLOiterator i
= FindObject(wrapmargin
, &offset
);
1321 wxCHECK_MSG( i
!= NULLIT
, FALSE
, "Cannot find object covering wrapmargin.");
1323 // from this object on, the rest of the line must be copied to the
1325 wxLOiterator copyObject
= NULLIT
;
1326 // if we split a text-object, we must pre-pend some text to the
1327 // next line later on, remember it here:
1328 wxString prependText
= "";
1329 // we might need to adjust the cursor position later, so remember it
1330 size_t xpos
= llist
->GetCursorPos().x
;
1331 // by how much did we shorten the current line:
1333 // remember cursor location of object
1334 size_t objectCursorPos
= 0;
1336 size_t breakpos
= offset
;
1338 if( (**i
).GetType() != WXLO_TYPE_TEXT
)
1340 // break before a non-text object
1345 bool foundSpace
= FALSE
;
1348 // while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT)
1350 // try to find a suitable place to split the object:
1351 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
1352 if((**i
).GetType() == WXLO_TYPE_TEXT
1353 && tobj
->GetText().Length() >= breakpos
)
1357 foundSpace
= isspace(tobj
->GetText()[breakpos
]) != 0;
1361 while ( breakpos
-- > 0 );
1368 if(! foundSpace
) // breakpos == 0!
1370 if(i
== m_ObjectList
.begin())
1371 return FALSE
; // could not break line
1375 while(i
!= m_ObjectList
.begin()
1376 && (**i
).GetType() != WXLO_TYPE_TEXT
)
1380 breakpos
= (**i
).GetLength();
1383 }while(! foundSpace
);
1384 // before we actually break the object, we need to know at which
1385 // cursorposition it starts, so we can restore the cursor if needed:
1386 if( this == llist
->GetCursorLine() && xpos
>= breakpos
)
1388 for(wxLOiterator j
= m_ObjectList
.begin();
1389 j
!= NULLIT
&& j
!= i
; j
++)
1390 objectCursorPos
+= (**j
).GetLength();
1392 // now we know where to break it:
1393 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
1394 shorter
= tobj
->GetLength() - breakpos
;
1395 // remember text to copy from this object
1396 prependText
= tobj
->GetText().Mid(breakpos
+1);
1397 tobj
->SetText(tobj
->GetText().Left(breakpos
));
1398 // copy every following object:
1399 copyObject
= i
; copyObject
++;
1402 // make sure there is an empty m_Next line:
1403 (void) new wxLayoutLine(this, llist
);
1405 // We need to move this and all following objects to the next
1406 // line. Starting from the end of line, to keep the order right.
1407 if(copyObject
!= NULLIT
)
1410 for(j
= m_ObjectList
.tail(); j
!= copyObject
; j
--)
1411 m_Next
->Prepend(*j
);
1412 m_Next
->Prepend(*copyObject
);
1413 // and now remove them from this list:
1414 while( copyObject
!= m_ObjectList
.end() )
1416 shorter
+= (**copyObject
).GetLength();
1417 m_ObjectList
.remove(copyObject
); // remove without deleting it
1420 m_Length
-= shorter
;
1422 if(prependText
.Length() > 0)
1423 m_Next
->Insert(0, prependText
);
1425 // do we need to adjust the cursor position?
1426 if( this == llist
->GetCursorLine() && xpos
>= breakpos
)
1428 xpos
= objectCursorPos
+ (xpos
- objectCursorPos
- breakpos
-
1429 ((xpos
> breakpos
) ? 1 : 0 ));
1430 wxASSERT(xpos
>= 0);
1431 llist
->MoveCursorTo( wxPoint( xpos
, m_Next
->GetLineNumber()) );
1433 return TRUE
; // we wrapped the line
1437 wxLayoutLine::ReNumber(void)
1439 CoordType lineNo
= m_Previous
? m_Previous
->m_LineNumber
+1 : 0;
1440 m_LineNumber
= lineNo
++;
1442 for(wxLayoutLine
*next
= GetNextLine();
1443 next
; next
= next
->GetNextLine())
1444 next
->m_LineNumber
= lineNo
++;
1448 wxLayoutLine::MergeNextLine(wxLayoutList
*llist
)
1450 wxCHECK_RET(GetNextLine(),"wxLayout internal error: no next line to merge");
1451 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
1454 MarkDirty(GetWidth());
1456 wxLayoutObject
*last
= NULL
;
1457 for(i
= list
.begin(); i
!= list
.end();)
1459 wxLayoutObject
*current
= *i
;
1461 // merge text objects together for efficiency
1462 if ( last
&& last
->GetType() == WXLO_TYPE_TEXT
&&
1463 current
->GetType() == WXLO_TYPE_TEXT
)
1465 wxLayoutObjectText
*textObj
= (wxLayoutObjectText
*)last
;
1466 wxString
text(textObj
->GetText());
1467 text
+= ((wxLayoutObjectText
*)current
)->GetText();
1468 textObj
->SetText(text
);
1470 list
.erase(i
); // remove and delete it
1474 // just append the object "as was"
1477 list
.remove(i
); // remove without deleting it
1480 wxASSERT(list
.empty());
1482 wxLayoutLine
*oldnext
= GetNextLine();
1483 wxLayoutLine
*nextLine
= oldnext
->GetNextLine();
1487 nextLine
->ReNumber();
1491 // this is now done in Delete(), but if this function is ever called
1492 // from elsewhere, we might have to move refresh code back here (in
1493 // order not to duplicate it)
1495 wxPoint
pos(oldnext
->GetPosition());
1496 llist
->SetUpdateRect(pos
);
1497 llist
->SetUpdateRect(pos
.x
+ oldnext
->GetWidth() + MSW_CORRECTION
,
1498 pos
.y
+ oldnext
->GetHeight() + MSW_CORRECTION
);
1502 llist
->DecNumLines();
1508 wxLayoutLine::GetWrapPosition(CoordType column
)
1511 wxLOiterator i
= FindObject(column
, &offset
);
1512 if(i
== NULLIT
) return -1; // cannot wrap
1514 // go backwards through the list and look for space in text objects
1517 if((**i
).GetType() == WXLO_TYPE_TEXT
)
1521 if(isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1528 }while(offset
!= -1);
1529 i
--; // move on to previous object
1533 column
-= (**i
).GetLength();
1537 offset
= (**i
).GetLength();
1538 }while(i
!= NULLIT
);
1539 /* If we reached the begin of the list and have more than one
1540 object, that one is longer than the margin, so break behind
1543 i
= m_ObjectList
.begin();
1544 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1546 pos
+= (**i
).GetLength();
1549 if(i
== NULLIT
) return -1; //why should this happen?
1551 // now we are behind the one long text object and need to find the
1552 // first space in it
1553 for(offset
= 0; offset
< (**i
).GetLength(); offset
++)
1554 if( isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1558 pos
+= (**i
).GetLength();
1563 #ifdef WXLAYOUT_DEBUG
1565 wxLayoutLine::Debug(void) const
1567 wxPoint pos
= GetPosition();
1568 WXLO_DEBUG(("Line %ld, Pos (%ld,%ld), Height %ld, BL %ld, Font: %d",
1569 (long int) GetLineNumber(),
1570 (long int) pos
.x
, (long int) pos
.y
,
1571 (long int) GetHeight(),
1572 (long int) m_BaseLine
,
1573 (int) m_StyleInfo
.family
));
1574 if(m_ObjectList
.begin() != NULLIT
)
1576 WXLO_DEBUG(((**m_ObjectList
.begin()).DebugDump().c_str()));
1583 wxLayoutLine::Copy(wxLayoutList
*llist
,
1587 CoordType firstOffset
, lastOffset
;
1589 if(to
== -1) to
= GetLength();
1590 if(from
== to
) return;
1592 wxLOiterator first
= FindObject(from
, &firstOffset
);
1593 wxLOiterator last
= FindObject(to
, &lastOffset
);
1595 // Common special case: only one object
1596 if( first
!= NULLIT
&& last
!= NULLIT
&& *first
== *last
)
1598 if( (**first
).GetType() == WXLO_TYPE_TEXT
)
1600 llist
->Insert(new wxLayoutObjectText(
1601 ((wxLayoutObjectText
1602 *)*first
)->GetText().substr(firstOffset
,
1603 lastOffset
-firstOffset
))
1607 else // what can we do?
1609 if(lastOffset
> firstOffset
) // i.e. +1 :-)
1610 llist
->Insert( (**first
).Copy() );
1615 // If we reach here, we can safely copy the whole first object from
1616 // the firstOffset position on:
1617 if((**first
).GetType() == WXLO_TYPE_TEXT
&& firstOffset
!= 0)
1619 llist
->Insert(new wxLayoutObjectText(
1620 ((wxLayoutObjectText
*)*first
)->GetText().substr(firstOffset
))
1623 else if(firstOffset
== 0)
1624 llist
->Insert( (**first
).Copy() );
1625 // else nothing to copy :-(
1627 // Now we copy all objects before the last one:
1628 wxLOiterator i
= first
; i
++;
1629 for( ; i
!= last
; i
++)
1630 llist
->Insert( (**i
).Copy() );
1632 // And now the last object:
1635 if( (**last
).GetType() == WXLO_TYPE_TEXT
)
1637 llist
->Insert(new wxLayoutObjectText(
1638 ((wxLayoutObjectText
*)*last
)->GetText().substr(0,lastOffset
))
1642 llist
->Insert( (**last
).Copy() );
1647 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1649 The wxLayoutList object
1651 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1653 wxLayoutList::wxLayoutList()
1655 #ifdef WXLAYOUT_USE_CARET
1657 #endif // WXLAYOUT_USE_CARET
1661 SetAutoFormatting(TRUE
);
1662 ForceTotalLayout(TRUE
); // for the first time, do all
1663 InvalidateUpdateRect();
1667 wxLayoutList::~wxLayoutList()
1669 SetAutoFormatting(FALSE
);
1672 m_FirstLine
->DeleteLine(false, this);
1674 wxASSERT_MSG( m_numLines
== 0, "line count calculation broken" );
1678 wxLayoutList::Empty(void)
1681 m_FirstLine
= m_FirstLine
->DeleteLine(false, this);
1683 m_CursorPos
= wxPoint(0,0);
1684 m_CursorScreenPos
= wxPoint(0,0);
1685 m_CursorSize
= wxPoint(0,0);
1686 m_movedCursor
= true;
1687 m_FirstLine
= new wxLayoutLine(NULL
, this); // empty first line
1688 m_CursorLine
= m_FirstLine
;
1689 InvalidateUpdateRect();
1694 wxLayoutList::InternalClear(void)
1696 m_Selection
.m_selecting
= false;
1697 m_Selection
.m_valid
= false;
1699 m_DefaultStyleInfo
.family
= wxSWISS
;
1700 m_DefaultStyleInfo
.size
= WXLO_DEFAULTFONTSIZE
;
1701 m_DefaultStyleInfo
.style
= wxNORMAL
;
1702 m_DefaultStyleInfo
.weight
= wxNORMAL
;
1703 m_DefaultStyleInfo
.underline
= 0;
1704 m_DefaultStyleInfo
.m_fg_valid
= TRUE
;
1705 m_DefaultStyleInfo
.m_fg
= *wxBLACK
;
1706 m_DefaultStyleInfo
.m_bg_valid
= TRUE
;
1707 m_DefaultStyleInfo
.m_bg
= *wxWHITE
;
1709 m_CurrentStyleInfo
= m_DefaultStyleInfo
;
1710 m_CursorStyleInfo
= m_DefaultStyleInfo
;
1714 wxLayoutList::Read(wxString
&istr
)
1716 /* In order to handle input of formatted string "nicely", we need
1717 to restore our current font settings after the string. So first
1718 of all, we create a StyleInfo structure with our current
1720 wxLayoutStyleInfo current_si
= GetStyleInfo();
1722 while(istr
.Length())
1724 // check for a linebreak:
1726 tmp
= istr
.BeforeFirst('\n');
1727 int type
= WXLO_TYPE_INVALID
;
1728 sscanf(tmp
.c_str(),"%d", &type
);
1729 if(type
== WXLO_TYPE_LINEBREAK
)
1732 istr
= istr
.AfterFirst('\n');
1736 wxLayoutObject
*obj
= wxLayoutObject::Read(istr
);
1741 /* Now we use the current_si to restore our last font settings: */
1742 Insert(new wxLayoutObjectCmd(current_si
));
1747 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1748 int underline
, wxColour
*fg
,
1751 if(family
!= -1) m_CurrentStyleInfo
.family
= family
;
1752 if(size
!= -1) m_CurrentStyleInfo
.size
= size
;
1753 if(style
!= -1) m_CurrentStyleInfo
.style
= style
;
1754 if(weight
!= -1) m_CurrentStyleInfo
.weight
= weight
;
1755 if(underline
!= -1) m_CurrentStyleInfo
.underline
= underline
!= 0;
1756 if(fg
) m_CurrentStyleInfo
.m_fg
= *fg
;
1757 if(bg
) m_CurrentStyleInfo
.m_bg
= *bg
;
1759 new wxLayoutObjectCmd(
1760 m_CurrentStyleInfo
.family
,
1761 m_CurrentStyleInfo
.size
,
1762 m_CurrentStyleInfo
.style
,
1763 m_CurrentStyleInfo
.weight
,
1764 m_CurrentStyleInfo
.underline
,
1769 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1770 int underline
, char const *fg
, char const *bg
)
1778 cfg
= wxTheColourDatabase
->FindColour(fg
);
1780 cbg
= wxTheColourDatabase
->FindColour(bg
);
1782 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
1786 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
1787 int underline
, wxColour
*fg
, wxColour
*bg
)
1790 m_DefaultStyleInfo
= wxLayoutStyleInfo(family
, size
, style
, weight
,
1792 m_CurrentStyleInfo
= m_DefaultStyleInfo
;
1794 // Empty() should be called after we set m_DefaultStyleInfo because
1795 // otherwise the style info for the first line (created in Empty()) would be
1801 wxLayoutList::FindText(const wxString
&needle
, const wxPoint
&cpos
) const
1806 for(line
= m_FirstLine
;
1808 line
= line
->GetNextLine())
1810 if(line
->GetLineNumber() >= cpos
.y
)
1812 xpos
= line
->FindText(needle
,
1813 (line
->GetLineNumber() == cpos
.y
) ?
1816 return wxPoint(xpos
, line
->GetLineNumber());
1819 return wxPoint(-1,-1);
1824 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
1826 AddCursorPosToUpdateRect();
1828 wxPoint cursorPosOld
= m_CursorPos
;
1830 wxLayoutLine
*line
= m_FirstLine
;
1831 while(line
&& line
->GetLineNumber() != p
.y
)
1832 line
= line
->GetNextLine();
1833 if(line
&& line
->GetLineNumber() == p
.y
) // found it
1835 m_CursorPos
.y
= p
.y
;
1836 m_CursorLine
= line
;
1837 CoordType len
= line
->GetLength();
1840 m_CursorPos
.x
= p
.x
;
1844 m_CursorPos
.x
= len
;
1848 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1850 return m_CursorPos
== p
;
1854 wxLayoutList::MoveCursorVertically(int n
)
1856 AddCursorPosToUpdateRect();
1858 wxPoint cursorPosOld
= m_CursorPos
;
1861 if(n
< 0) // move up
1863 if(m_CursorLine
== m_FirstLine
) return false;
1864 while(n
< 0 && m_CursorLine
)
1866 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1872 m_CursorLine
= m_FirstLine
;
1878 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1879 m_CursorPos
.x
= m_CursorLine
->GetLength();
1885 wxLayoutLine
*last
= m_CursorLine
;
1886 if(! m_CursorLine
->GetNextLine()) return false;
1887 while(n
> 0 && m_CursorLine
)
1891 last
= m_CursorLine
;
1892 m_CursorLine
= m_CursorLine
->GetNextLine();
1896 m_CursorLine
= last
;
1902 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1903 m_CursorPos
.x
= m_CursorLine
->GetLength();
1908 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1914 wxLayoutList::MoveCursorHorizontally(int n
)
1916 AddCursorPosToUpdateRect();
1918 wxPoint cursorPosOld
= m_CursorPos
;
1923 if(m_CursorPos
.x
== 0) // at begin of line
1925 if(! MoveCursorVertically(-1))
1927 MoveCursorToEndOfLine();
1932 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1933 m_CursorPos
.x
-= move
; n
+= move
;
1938 int len
= m_CursorLine
->GetLength();
1939 if(m_CursorPos
.x
== len
) // at end of line
1941 if(! MoveCursorVertically(1))
1943 MoveCursorToBeginOfLine();
1948 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1949 m_CursorPos
.x
+= move
;
1953 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1959 wxLayoutList::MoveCursorWord(int n
, bool untilNext
)
1961 wxCHECK_MSG( m_CursorLine
, false, "no current line" );
1962 wxCHECK_MSG( n
== -1 || n
== +1, false, "not implemented yet" );
1964 CoordType moveDistance
= 0;
1966 wxLayoutLine
*lineCur
= m_CursorLine
;
1967 for ( wxLOiterator i
= lineCur
->FindObject(m_CursorPos
.x
, &offset
);
1975 // moving forward, pass to the first object of the next line
1977 lineCur
= lineCur
->GetNextLine();
1979 i
= lineCur
->GetFirstObject();
1983 // moving backwards, pass to the last object of the prev line
1985 lineCur
= lineCur
->GetPreviousLine();
1987 i
= lineCur
->GetLastObject();
1992 // moved to the end/beginning of text
1999 wxLayoutObject
*obj
= *i
;
2003 // calculate offset: we are either at the very beginning or the very
2004 // end of the object, so it isn't very difficult (the only time when
2005 // offset is != -1 is for the very first iteration when its value is
2006 // returned by FindObject)
2010 offset
= obj
->GetLength();
2013 if( obj
->GetType() != WXLO_TYPE_TEXT
)
2015 // any visible non text objects count as one word
2016 if ( obj
->IsVisibleObject() )
2020 moveDistance
+= obj
->GetLength();
2025 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)obj
;
2027 bool canAdvance
= true;
2029 if ( offset
== tobj
->GetLength() )
2034 // can't move further in this text object
2037 // still should move over the object border
2041 else if ( offset
> 0 )
2043 // offset is off by 1, make it a valid index
2050 const wxString
& text
= tobj
->GetText();
2051 const char *start
= text
.c_str();
2052 const char *end
= start
+ text
.length();
2053 const char *p
= start
+ offset
;
2061 // to the beginning/end of the next/prev word
2062 while ( p
>= start
&& p
< end
&& isspace(*p
) )
2067 // go to the end/beginning of the word (in a broad sense...)
2068 while ( p
>= start
&& p
< end
&& !isspace(*p
) )
2077 // now advance to the beginning of the next word
2078 while ( isspace(*p
) && p
< end
)
2084 // in these 2 cases we took 1 char too much
2085 if ( (p
< start
) || isspace(*p
) )
2091 CoordType moveDelta
= p
- start
- offset
;
2092 if ( (n
< 0) && (offset
== tobj
->GetLength() - 1) )
2094 // because we substracted 1 from offset in this case above, now
2095 // compensate for it
2099 if ( moveDelta
!= 0 )
2101 moveDistance
+= moveDelta
;
2108 // except for the first iteration, offset is calculated in the beginning
2113 MoveCursorHorizontally(moveDistance
);
2119 wxLayoutList::Insert(wxString
const &text
)
2121 wxASSERT(m_CursorLine
);
2122 wxASSERT_MSG( text
.Find('\n') == wxNOT_FOUND
, "use wxLayoutImportText!" );
2127 AddCursorPosToUpdateRect();
2129 wxASSERT(m_CursorLine
->GetLength() >= m_CursorPos
.x
);
2131 if ( !m_CursorLine
->Insert(m_CursorPos
.x
, text
) )
2133 m_CursorPos
.x
+= text
.Length();
2135 m_movedCursor
= true;
2138 m_CursorLine
->MarkDirty();
2144 wxLayoutList::Insert(wxLayoutObject
*obj
)
2146 wxASSERT(m_CursorLine
);
2149 m_CursorLine
= GetFirstLine();
2151 AddCursorPosToUpdateRect();
2153 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
2154 m_CursorPos
.x
+= obj
->GetLength();
2155 m_movedCursor
= true;
2158 m_CursorLine
->MarkDirty();
2164 wxLayoutList::Insert(wxLayoutList
*llist
)
2169 for(wxLayoutLine
*line
= llist
->GetFirstLine();
2171 line
= line
->GetNextLine()
2174 for(wxLOiterator i
= line
->GetFirstObject();
2184 wxLayoutList::LineBreak(void)
2186 wxASSERT(m_CursorLine
);
2188 AddCursorPosToUpdateRect();
2190 wxPoint
position(m_CursorLine
->GetPosition());
2193 width
= m_CursorLine
->GetWidth(),
2194 height
= m_CursorLine
->GetHeight();
2196 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
, this);
2197 if(m_CursorLine
->GetPreviousLine() == NULL
)
2198 m_FirstLine
= m_CursorLine
;
2202 // The following code will produce a height which is guaranteed to
2203 // be too high: old lineheight + the height of both new lines.
2204 // We can probably drop the old line height and start with height =
2206 wxLayoutLine
*prev
= m_CursorLine
->GetPreviousLine();
2208 height
+= prev
->GetHeight();
2209 height
+= m_CursorLine
->GetHeight();
2211 m_movedCursor
= true;
2213 SetUpdateRect(position
);
2214 SetUpdateRect(position
.x
+ width
+ MSW_CORRECTION
,
2215 position
.y
+ height
+ MSW_CORRECTION
);
2221 wxLayoutList::WrapLine(CoordType column
)
2223 return m_CursorLine
->Wrap(column
, this);
2227 wxLayoutList::WrapAll(CoordType column
)
2229 wxLayoutLine
*line
= m_FirstLine
;
2235 rc
&= line
->Wrap(column
, this);
2236 line
= line
->GetNextLine();
2242 wxLayoutList::Delete(CoordType npos
)
2244 wxCHECK_MSG(m_CursorLine
, false, "can't delete in non existing line");
2249 AddCursorPosToUpdateRect();
2251 // were other lines appended to this one (this is important to know because
2252 // this means that our width _increased_ as the result of deletion)
2253 bool wasMerged
= false;
2255 // the size of the region to update
2256 CoordType totalHeight
= m_CursorLine
->GetHeight(),
2257 totalWidth
= m_CursorLine
->GetWidth();
2262 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
2266 // More to delete, continue on next line.
2268 // First, check if line is empty:
2269 if(m_CursorLine
->GetLength() == 0)
2271 // in this case, updating could probably be optimised
2273 wxASSERT(DeleteLines(1) == 0);
2282 // Need to join next line
2283 if(! m_CursorLine
->GetNextLine())
2288 wxLayoutLine
*next
= m_CursorLine
->GetNextLine();
2291 totalHeight
+= next
->GetHeight();
2292 totalWidth
+= next
->GetWidth();
2294 m_CursorLine
->MergeNextLine(this);
2299 wxFAIL_MSG("can't delete all this");
2309 // we need to update the whole tail of the line and the lines which
2313 wxPoint
position(m_CursorLine
->GetPosition());
2314 SetUpdateRect(position
);
2315 SetUpdateRect(position
.x
+ totalWidth
+ MSW_CORRECTION
,
2316 position
.y
+ totalHeight
+ MSW_CORRECTION
);
2323 wxLayoutList::DeleteLines(int n
)
2325 wxASSERT(m_CursorLine
);
2328 AddCursorPosToUpdateRect();
2332 if(!m_CursorLine
->GetNextLine())
2333 { // we cannot delete this line, but we can clear it
2334 MoveCursorToBeginOfLine();
2335 DeleteToEndOfLine();
2337 m_CursorLine
->MarkDirty();
2341 line
= m_CursorLine
;
2342 m_CursorLine
= m_CursorLine
->DeleteLine(true, this);
2344 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
2345 wxASSERT(m_FirstLine
);
2346 wxASSERT(m_CursorLine
);
2349 m_CursorLine
->MarkDirty();
2354 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
)
2358 wxLayoutLine
*line
= m_FirstLine
;
2360 // first, make sure everything is calculated - this might not be
2361 // needed, optimise it later
2362 ApplyStyle(m_DefaultStyleInfo
, dc
);
2365 line
->RecalculatePosition(this); // so we don't need to do it all the time
2366 // little condition to speed up redrawing:
2367 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
2368 line
= line
->GetNextLine();
2373 wxLayoutList::GetCursorScreenPos(void) const
2375 return m_CursorScreenPos
;
2379 Is called before each Draw(). Now, it will re-layout all lines which
2383 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
, bool forceAll
,
2384 wxPoint
*cpos
, wxPoint
*csize
)
2386 // first, make sure everything is calculated - this might not be
2387 // needed, optimise it later
2388 ApplyStyle(m_DefaultStyleInfo
, dc
);
2396 ForceTotalLayout(FALSE
);
2399 // If one line was dirty, we need to re-calculate all
2400 // following lines, too.
2401 bool wasDirty
= forceAll
;
2402 // we need to layout until we reach at least the cursor line,
2403 // otherwise we won't be able to scroll to it
2404 bool cursorReached
= false;
2405 wxLayoutLine
*line
= m_FirstLine
;
2409 ApplyStyle(line
->GetStyleInfo(), dc
);
2411 // if any previous line was dirty, we need to layout all
2414 // go on until we find the cursorline
2416 // layout dirty lines:
2418 // always layout the cursor line toupdate the cursor
2419 // position and size:
2420 || line
== m_CursorLine
2421 // or if it's the line we are asked to look for:
2422 || (cpos
&& line
->GetLineNumber() == cpos
->y
)
2423 // layout at least the desired region:
2425 || (line
->GetPosition().y
<= bottom
)
2431 // The following Layout() calls will update our
2432 // m_CurrentStyleInfo if needed.
2433 if(line
== m_CursorLine
)
2435 line
->Layout(dc
, this,
2436 (wxPoint
*)&m_CursorScreenPos
,
2437 (wxPoint
*)&m_CursorSize
,
2440 // we cannot layout the line twice, so copy the coords:
2441 if(cpos
&& line
->GetLineNumber() == cpos
->y
)
2443 *cpos
= m_CursorScreenPos
;
2445 *csize
= m_CursorSize
;
2447 cursorReached
= TRUE
;
2451 if(cpos
&& line
->GetLineNumber() == cpos
->y
)
2453 line
->Layout(dc
, this,
2455 csize
, NULL
, cpos
->x
);
2456 cursorReached
= TRUE
;
2459 line
->Layout(dc
, this);
2462 line
= line
->GetNextLine();
2465 #ifndef WXLAYOUT_USE_CARET
2466 // can only be 0 if we are on the first line and have no next line
2467 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
2468 m_CursorLine
->GetNextLine() == NULL
&&
2469 m_CursorLine
== m_FirstLine
));
2470 #endif // WXLAYOUT_USE_CARET
2471 AddCursorPosToUpdateRect();
2475 wxLayoutList::GetScreenPos(wxDC
&dc
, const wxPoint
&cpos
, wxPoint
*csize
)
2478 Layout(dc
, -1, false, &pos
, csize
);
2483 wxLayoutList::Draw(wxDC
&dc
,
2484 wxPoint
const &offset
,
2489 wxLayoutLine
*line
= m_FirstLine
;
2491 if ( m_Selection
.m_discarded
)
2493 // calculate them if we don't have them already
2494 if ( !m_Selection
.HasValidScreenCoords() )
2496 m_Selection
.m_ScreenA
= GetScreenPos(dc
, m_Selection
.m_CursorA
);
2497 m_Selection
.m_ScreenB
= GetScreenPos(dc
, m_Selection
.m_CursorB
);
2500 // invalidate the area which was previousle selected - and which is not
2501 // selected any more
2502 SetUpdateRect(m_Selection
.m_ScreenA
);
2503 SetUpdateRect(m_Selection
.m_ScreenB
);
2505 m_Selection
.m_discarded
= false;
2508 /* This call to Layout() will re-calculate and update all lines
2513 ApplyStyle(m_DefaultStyleInfo
, dc
);
2514 wxBrush
brush(m_CurrentStyleInfo
.m_bg
, wxSOLID
);
2516 dc
.SetBackgroundMode(wxTRANSPARENT
);
2520 // only draw if between top and bottom:
2522 line
->GetPosition().y
+ line
->GetHeight() > top
))
2524 ApplyStyle(line
->GetStyleInfo(), dc
);
2525 // little condition to speed up redrawing:
2527 && line
->GetPosition().y
2528 +(clipStrictly
? line
->GetHeight() : 0) >= bottom
)
2530 line
->Draw(dc
, this, offset
);
2532 line
= line
->GetNextLine();
2534 InvalidateUpdateRect();
2536 WXLO_DEBUG(("Selection is %s : %ld,%ld/%ld,%ld",
2537 m_Selection
.m_valid
? "valid" : "invalid",
2538 m_Selection
.m_CursorA
.x
, m_Selection
.m_CursorA
.y
,
2539 m_Selection
.m_CursorB
.x
, m_Selection
.m_CursorB
.y
));
2543 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
,
2547 // First, find the right line:
2549 *line
= m_FirstLine
,
2550 *lastline
= m_FirstLine
;
2553 ApplyStyle(m_DefaultStyleInfo
, dc
);
2556 p
= line
->GetPosition();
2557 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
2560 line
= line
->GetNextLine();
2563 bool didFind
= line
!= NULL
;
2567 // use the last line:
2572 cursorPos
->y
= line
->GetLineNumber();
2574 bool foundinline
= true;
2577 // Now, find the object in the line:
2582 i
= line
->FindObjectScreen(dc
, this,
2589 i
= line
->FindObjectScreen(dc
, this,
2594 *found
= didFind
&& foundinline
;
2596 return (i
== NULLIT
) ? NULL
: *i
;
2601 wxLayoutList::GetSize(void) const
2604 *line
= m_FirstLine
,
2607 return wxPoint(0,0);
2609 wxPoint
maxPoint(0,0);
2614 if(line
->GetWidth() > maxPoint
.x
)
2615 maxPoint
.x
= line
->GetWidth();
2617 line
= line
->GetNextLine();
2620 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
2622 // if the line was just added, its height would be 0 and we can't call
2623 // Layout() from here because we don't have a dc and we might be not drawing
2624 // at all, besides... So take the cursor height by default (taking 0 is bad
2625 // because then the scrollbars won't be resized and the new line won't be
2627 if ( last
->IsDirty() )
2629 if ( last
->GetHeight() == 0 )
2630 maxPoint
.y
+= m_CursorSize
.y
;
2631 if ( last
->GetWidth() == 0 && maxPoint
.x
< m_CursorSize
.x
)
2632 maxPoint
.x
= m_CursorSize
.x
;
2640 wxLayoutList::DrawCursor(wxDC
&dc
, bool active
, wxPoint
const &translate
)
2642 if ( m_movedCursor
)
2643 m_movedCursor
= false;
2645 wxPoint
coords(m_CursorScreenPos
);
2646 coords
+= translate
;
2648 #ifdef WXLAYOUT_DEBUG
2649 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
2650 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
2651 (long)coords
.x
, (long)coords
.y
,
2652 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
2653 (long)m_CursorLine
->GetLineNumber(),
2654 (long)m_CursorLine
->GetLength()));
2656 wxLogStatus("Cursor is at (%d, %d)", m_CursorPos
.x
, m_CursorPos
.y
);
2659 #ifdef WXLAYOUT_USE_CARET
2660 m_caret
->Move(coords
);
2661 #else // !WXLAYOUT_USE_CARET
2663 wxASSERT(m_CursorSize
.x
>= WXLO_MINIMUM_CURSOR_WIDTH
);
2664 dc
.SetBrush(*wxWHITE_BRUSH
);
2665 //FIXME: wxGTK XOR is borken at the moment!!!dc.SetLogicalFunction(wxXOR);
2666 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
2669 dc
.SetLogicalFunction(wxXOR
);
2670 dc
.DrawRectangle(coords
.x
, coords
.y
,
2671 m_CursorSize
.x
, m_CursorSize
.y
);
2672 SetUpdateRect(coords
.x
, coords
.y
);
2673 SetUpdateRect(coords
.x
+m_CursorSize
.x
,
2674 coords
.y
+m_CursorSize
.y
);
2678 dc
.SetLogicalFunction(wxCOPY
);
2679 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
2680 coords
.x
, coords
.y
);
2681 SetUpdateRect(coords
.x
, coords
.y
+m_CursorSize
.y
-1);
2682 SetUpdateRect(coords
.x
, coords
.y
);
2684 dc
.SetLogicalFunction(wxCOPY
);
2685 //dc.SetBrush(wxNullBrush);
2686 #endif // WXLAYOUT_USE_CARET/!WXLAYOUT_USE_CARET
2690 wxLayoutList::SetUpdateRect(CoordType x
, CoordType y
)
2692 if(m_UpdateRectValid
)
2693 GrowRect(m_UpdateRect
, x
, y
);
2698 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
2699 m_UpdateRect
.height
= 4;// wxGTK :-)
2700 m_UpdateRectValid
= true;
2705 wxLayoutList::StartSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2707 wxPoint
cpos(cposOrig
);
2710 WXLO_DEBUG(("Starting selection at %ld/%ld", cpos
.x
, cpos
.y
));
2711 m_Selection
.m_CursorA
= cpos
;
2712 m_Selection
.m_CursorB
= cpos
;
2713 m_Selection
.m_ScreenA
= spos
;
2714 m_Selection
.m_ScreenB
= spos
;
2715 m_Selection
.m_selecting
= true;
2716 m_Selection
.m_valid
= false;
2720 wxLayoutList::ContinueSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2722 wxPoint
cpos(cposOrig
);
2726 wxASSERT(m_Selection
.m_selecting
== true);
2727 wxASSERT(m_Selection
.m_valid
== false);
2728 WXLO_DEBUG(("Continuing selection at %ld/%ld", cpos
.x
, cpos
.y
));
2730 m_Selection
.m_ScreenB
= spos
;
2731 m_Selection
.m_CursorB
= cpos
;
2735 wxLayoutList::EndSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2737 wxPoint
cpos(cposOrig
);
2738 if(cpos
.x
== -1) cpos
= m_CursorPos
;
2739 ContinueSelection(cpos
, spos
);
2740 WXLO_DEBUG(("Ending selection at %ld/%ld", cpos
.x
, cpos
.y
));
2741 // we always want m_CursorA <= m_CursorB!
2742 if( m_Selection
.m_CursorA
> m_Selection
.m_CursorB
)
2744 // exchange the start/end points
2745 wxPoint help
= m_Selection
.m_CursorB
;
2746 m_Selection
.m_CursorB
= m_Selection
.m_CursorA
;
2747 m_Selection
.m_CursorA
= help
;
2749 help
= m_Selection
.m_ScreenB
;
2750 m_Selection
.m_ScreenB
= m_Selection
.m_ScreenA
;
2751 m_Selection
.m_ScreenA
= help
;
2753 m_Selection
.m_selecting
= false;
2754 m_Selection
.m_valid
= true;
2755 /// In case we just clicked somewhere, the selection will have zero
2756 /// size, so we discard it immediately.
2757 if(m_Selection
.m_CursorA
== m_Selection
.m_CursorB
)
2762 wxLayoutList::DiscardSelection()
2764 if ( !HasSelection() )
2767 m_Selection
.m_valid
=
2768 m_Selection
.m_selecting
= false;
2769 m_Selection
.m_discarded
= true;
2773 wxLayoutList::IsSelecting(void) const
2775 return m_Selection
.m_selecting
;
2779 wxLayoutList::IsSelected(const wxPoint
&cursor
) const
2781 if ( !HasSelection() )
2785 (m_Selection
.m_CursorA
<= cursor
2786 && cursor
<= m_Selection
.m_CursorB
)
2787 || (m_Selection
.m_CursorB
<= cursor
2788 && cursor
<= m_Selection
.m_CursorA
)
2793 /** Tests whether this layout line is selected and needs
2795 @param line to test for
2796 @return 0 = not selected, 1 = fully selected, -1 = partially
2800 wxLayoutList::IsSelected(const wxLayoutLine
*line
, CoordType
*from
,
2803 wxASSERT(line
); wxASSERT(to
); wxASSERT(from
);
2805 if(! m_Selection
.m_valid
&& ! m_Selection
.m_selecting
)
2808 CoordType y
= line
->GetLineNumber();
2810 (m_Selection
.m_CursorA
.y
< y
&& m_Selection
.m_CursorB
.y
> y
)
2811 || (m_Selection
.m_CursorB
.y
< y
&& m_Selection
.m_CursorA
.y
> y
)
2814 else if(m_Selection
.m_CursorA
.y
== y
)
2816 *from
= m_Selection
.m_CursorA
.x
;
2817 if(m_Selection
.m_CursorB
.y
== y
)
2818 *to
= m_Selection
.m_CursorB
.x
;
2821 if(m_Selection
.m_CursorB
> m_Selection
.m_CursorA
)
2822 *to
= line
->GetLength();
2828 CoordType help
= *to
;
2834 else if(m_Selection
.m_CursorB
.y
== y
)
2836 *to
= m_Selection
.m_CursorB
.x
;
2837 if(m_Selection
.m_CursorA
.y
== y
)
2838 *from
= m_Selection
.m_CursorA
.x
;
2841 if(m_Selection
.m_CursorB
> m_Selection
.m_CursorA
)
2844 *from
= line
->GetLength();
2848 CoordType help
= *to
;
2859 wxLayoutList::DeleteSelection(void)
2861 if(! m_Selection
.m_valid
)
2864 m_Selection
.m_valid
= false;
2866 // Only delete part of the current line?
2867 if(m_Selection
.m_CursorA
.y
== m_Selection
.m_CursorB
.y
)
2869 MoveCursorTo(m_Selection
.m_CursorA
);
2870 Delete(m_Selection
.m_CursorB
.x
- m_Selection
.m_CursorA
.x
);
2874 // We now know that the two lines are different:
2877 * firstLine
= GetLine(m_Selection
.m_CursorA
.y
),
2878 * lastLine
= GetLine(m_Selection
.m_CursorB
.y
);
2879 // be a bit paranoid:
2880 if(! firstLine
|| ! lastLine
)
2883 // First, delete what's left of this line:
2884 MoveCursorTo(m_Selection
.m_CursorA
);
2885 DeleteToEndOfLine();
2887 wxLayoutLine
*prevLine
= firstLine
->GetPreviousLine(),
2888 *nextLine
= firstLine
->GetNextLine();
2889 while(nextLine
&& nextLine
!= lastLine
)
2890 nextLine
= nextLine
->DeleteLine(false, this);
2892 // Now nextLine = lastLine;
2893 Delete(1); // This joins firstLine and nextLine
2894 Delete(m_Selection
.m_CursorB
.x
); // This deletes the first x positions
2896 // Recalculate the line positions and numbers but notice that firstLine
2897 // might not exist any more - it could be deleted by Delete(1) above
2898 wxLayoutLine
*firstLine2
= prevLine
? prevLine
->GetNextLine() : m_FirstLine
;
2899 firstLine2
->MarkDirty();
2902 /// Starts highlighting the selection
2904 wxLayoutList::StartHighlighting(wxDC
&dc
)
2907 dc
.SetTextForeground(m_CurrentStyleInfo
.m_bg
);
2908 dc
.SetTextBackground(m_CurrentStyleInfo
.m_fg
);
2909 dc
.SetBackgroundMode(wxSOLID
);
2913 /// Ends highlighting the selection
2915 wxLayoutList::EndHighlighting(wxDC
&dc
)
2918 dc
.SetTextForeground(m_CurrentStyleInfo
.m_fg
);
2919 dc
.SetTextBackground(m_CurrentStyleInfo
.m_bg
);
2920 dc
.SetBackgroundMode(wxTRANSPARENT
);
2926 wxLayoutList::GetLine(CoordType index
) const
2928 wxASSERT_MSG( (0 <= index
) && (index
< (CoordType
)m_numLines
),
2932 CoordType n
= index
;
2934 CoordType lineNo
= 0;
2937 for ( line
= m_FirstLine
; line
&& n
-- > 0; line
=
2938 line
->GetNextLine() )
2941 wxASSERT(line
->GetLineNumber() == lineNo
);
2948 // should be the right one
2949 wxASSERT( line
->GetLineNumber() == index
);
2957 wxLayoutList::Copy(const wxPoint
&from
,
2964 for(firstLine
= m_FirstLine
;
2965 firstLine
&& firstLine
->GetLineNumber() < from
.y
;
2966 firstLine
=firstLine
->GetNextLine())
2968 if(!firstLine
|| firstLine
->GetLineNumber() != from
.y
)
2971 for(lastLine
= m_FirstLine
;
2972 lastLine
&& lastLine
->GetLineNumber() < to
.y
;
2973 lastLine
=lastLine
->GetNextLine())
2975 if(!lastLine
|| lastLine
->GetLineNumber() != to
.y
)
2980 wxLayoutLine
*tmp
= firstLine
;
2981 firstLine
= lastLine
;
2985 wxLayoutList
*llist
= new wxLayoutList();
2987 if(firstLine
== lastLine
)
2989 firstLine
->Copy(llist
, from
.x
, to
.x
);
2993 // Extract objects from first line
2994 firstLine
->Copy(llist
, from
.x
);
2996 // Extract all lines between
2997 for(wxLayoutLine
*line
= firstLine
->GetNextLine();
2999 line
= line
->GetNextLine())
3004 // Extract objects from last line
3005 lastLine
->Copy(llist
, 0, to
.x
);
3011 wxLayoutList::GetSelection(wxLayoutDataObject
*wxlo
, bool invalidate
)
3013 if(! m_Selection
.m_valid
)
3015 if(m_Selection
.m_selecting
)
3021 if(invalidate
) m_Selection
.m_valid
= false;
3023 wxLayoutList
*llist
= Copy( m_Selection
.m_CursorA
,
3024 m_Selection
.m_CursorB
);
3026 if(llist
&& wxlo
) // export as data object, too
3030 wxLayoutExportObject
*exp
;
3031 wxLayoutExportStatus
status(llist
);
3032 while((exp
= wxLayoutExport( &status
, WXLO_EXPORT_AS_OBJECTS
)) != NULL
)
3034 if(exp
->type
== WXLO_EXPORT_EMPTYLINE
)
3035 string
<< (int) WXLO_TYPE_LINEBREAK
<< '\n';
3037 exp
->content
.object
->Write(string
);
3040 wxlo
->SetLayoutData(string
);
3047 #define COPY_SI(what) if(si.what != -1) { m_CurrentStyleInfo.what = si.what; fontChanged = TRUE; }
3050 wxLayoutList::ApplyStyle(wxLayoutStyleInfo
const &si
, wxDC
&dc
)
3052 bool fontChanged
= FALSE
;
3059 dc
.SetFont( m_FontCache
.GetFont(m_CurrentStyleInfo
) );
3063 m_CurrentStyleInfo
.m_fg
= si
.m_fg
;
3064 m_CurrentStyleInfo
.m_fg_valid
= true;
3065 dc
.SetTextForeground(m_CurrentStyleInfo
.m_fg
);
3069 m_CurrentStyleInfo
.m_bg
= si
.m_bg
;
3070 m_CurrentStyleInfo
.m_bg_valid
= true;
3071 dc
.SetTextBackground(m_CurrentStyleInfo
.m_bg
);
3076 #ifdef WXLAYOUT_DEBUG
3079 wxLayoutList::Debug(void)
3081 WXLO_DEBUG(("Cursor is in line %d, screen pos = (%d, %d)",
3082 m_CursorLine
->GetLineNumber(),
3083 m_CursorScreenPos
.x
, m_CursorScreenPos
.y
));
3086 for(line
= m_FirstLine
; line
; line
= line
->GetNextLine())
3095 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3099 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
3101 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
3102 wxString
const & title
)
3107 // remove any highlighting which could interfere with printing:
3108 m_llist
->StartSelection();
3109 m_llist
->EndSelection();
3110 // force a full layout of the list:
3111 m_llist
->ForceTotalLayout();
3112 // layout is called in ScaleDC() when we have a DC
3115 wxLayoutPrintout::~wxLayoutPrintout()
3120 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
3122 // The following bit is taken from the printing sample, let's see
3123 // whether it works for us.
3125 /* You might use THIS code to set the printer DC to ROUGHLY reflect
3126 * the screen text size. This page also draws lines of actual length 5cm
3129 // Get the logical pixels per inch of screen and printer
3130 int ppiScreenX
, ppiScreenY
;
3131 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
3132 int ppiPrinterX
, ppiPrinterY
;
3133 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
3135 if(ppiScreenX
== 0) // not yet set, need to guess
3140 if(ppiPrinterX
== 0) // not yet set, need to guess
3146 // This scales the DC so that the printout roughly represents the
3147 // the screen scaling. The text point size _should_ be the right size
3148 // but in fact is too small for some reason. This is a detail that will
3149 // need to be addressed at some point but can be fudged for the
3151 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
3153 // Now we have to check in case our real page size is reduced
3154 // (e.g. because we're drawing to a print preview memory DC)
3155 int pageWidth
, pageHeight
;
3157 dc
->GetSize(&w
, &h
);
3158 GetPageSizePixels(&pageWidth
, &pageHeight
);
3159 if(pageWidth
!= 0) // doesn't work always
3161 // If printer pageWidth == current DC width, then this doesn't
3162 // change. But w might be the preview bitmap width, so scale down.
3163 scale
= scale
* (float)(w
/(float)pageWidth
);
3165 dc
->SetUserScale(scale
, scale
);
3169 bool wxLayoutPrintout::OnPrintPage(int page
)
3178 top
= (page
- 1)*m_PrintoutHeight
;
3179 bottom
= top
+ m_PrintoutHeight
;
3181 WXLO_DEBUG(("OnPrintPage(%d) printing from %d to %d", page
, top
,
3183 // SetDeviceOrigin() doesn't work here, so we need to manually
3184 // translate all coordinates.
3185 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
3186 m_llist
->Draw(*dc
, translate
, top
, bottom
, TRUE
/* clip strictly
3194 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
3196 /* We allocate a temporary wxDC for printing, so that we can
3197 determine the correct paper size and scaling. We don't actually
3198 print anything on it. */
3199 #if defined(__WXMSW__)
3200 wxPrinterDC
*psdc
= new wxPrinterDC("","",WXLLIST_TEMPFILE
,false);
3202 wxPostScriptDC
*psdc
= new wxPostScriptDC(WXLLIST_TEMPFILE
,false);
3205 psdc
->StartDoc(m_title
);
3206 // before we draw anything, me must make sure the list is properly
3208 m_llist
->Layout(*psdc
);
3210 float scale
= ScaleDC(psdc
);
3212 psdc
->GetSize(&m_PageWidth
, &m_PageHeight
);
3214 // This sets a left/top origin of 15% and 5%:
3215 m_Offset
= wxPoint((15*m_PageWidth
)/100, (5*m_PageHeight
)/100);
3217 // This is the length of the printable area.
3218 m_PrintoutHeight
= m_PageHeight
- 2*m_Offset
.y
;
3219 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
3222 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
3225 *maxPage
= m_NumOfPages
;
3228 *selPageTo
= m_NumOfPages
;
3231 wxRemoveFile(WXLLIST_TEMPFILE
);
3234 bool wxLayoutPrintout::HasPage(int pageNum
)
3236 return pageNum
<= m_NumOfPages
;
3240 Stupid wxWindows doesn't draw proper ellipses, so we comment this
3241 out. It's a waste of paper anyway.
3245 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
3246 wxPoint topleft
, wxPoint bottomright
,
3249 // make backups of all essential parameters
3250 const wxBrush
& brush
= dc
.GetBrush();
3251 const wxPen
& pen
= dc
.GetPen();
3252 const wxFont
& font
= dc
.GetFont();
3254 dc
.SetBrush(*wxWHITE_BRUSH
);
3255 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
3256 dc
.DrawRoundedRectangle(topleft
.x
,
3257 topleft
.y
,bottomright
.x
-topleft
.x
,
3258 bottomright
.y
-topleft
.y
);
3259 dc
.SetBrush(*wxBLACK_BRUSH
);
3260 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
3261 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
3265 page
= "9999/9999 "; // many pages...
3267 dc
.GetTextExtent(page
,&w
,&h
);
3268 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
3269 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
3270 dc
.GetTextExtent("XXXX", &w
,&h
);
3271 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);
3282 wxFontCache::GetFont(int family
, int size
, int style
, int weight
,
3285 for(wxFCEList::iterator i
= m_FontList
.begin();
3286 i
!= m_FontList
.end(); i
++)
3287 if( (**i
).Matches(family
, size
, style
, weight
, underline
) )
3288 return (**i
).GetFont();
3290 wxFontCacheEntry
*fce
= new wxFontCacheEntry(family
, size
, style
,
3292 m_FontList
.push_back(fce
);
3293 return fce
->GetFont();