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
)
455 m_Icon
= new wxBitmap
;
460 wxLayoutObjectIcon::Draw(wxDC
&dc
, wxPoint
const &coords
,
461 wxLayoutList
*wxllist
,
462 CoordType begin
, CoordType
/* len */)
464 dc
.DrawBitmap(*m_Icon
, coords
.x
, coords
.y
-m_Icon
->GetHeight(),
465 (m_Icon
->GetMask() == NULL
) ? FALSE
: TRUE
);
469 wxLayoutObjectIcon::Layout(wxDC
& /* dc */, class wxLayoutList
* )
474 wxLayoutObjectIcon::GetSize(CoordType
*top
, CoordType
*bottom
) const
476 *top
= m_Icon
->GetHeight();
478 return wxPoint(m_Icon
->GetWidth(), m_Icon
->GetHeight());
483 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
487 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
490 wxLayoutStyleInfo::wxLayoutStyleInfo(int ifamily
,
502 underline
= iul
!= 0;
504 m_fg_valid
= fg
!= 0;
505 m_bg_valid
= bg
!= 0;
506 m_fg
= m_fg_valid
? *fg
: *wxBLACK
;
507 m_bg
= m_bg_valid
? *bg
: *wxWHITE
;
510 #define COPY_SI_(what) if(right.what != -1) what = right.what;
513 wxLayoutStyleInfo::operator=(const wxLayoutStyleInfo
&right
)
520 if(right
.m_fg_valid
) m_fg
= right
.m_fg
;
521 if(right
.m_bg_valid
) m_bg
= right
.m_bg
;
525 wxLayoutObjectCmd::wxLayoutObjectCmd(int family
, int size
, int style
, int
526 weight
, int underline
,
527 wxColour
*fg
, wxColour
*bg
)
530 m_StyleInfo
= new wxLayoutStyleInfo(family
, size
,style
,weight
,underline
,fg
,bg
);
533 wxLayoutObjectCmd::wxLayoutObjectCmd(const wxLayoutStyleInfo
&si
)
536 m_StyleInfo
= new wxLayoutStyleInfo
;
541 wxLayoutObjectCmd::Copy(void)
543 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd(
548 m_StyleInfo
->underline
,
549 m_StyleInfo
->m_fg_valid
?
550 &m_StyleInfo
->m_fg
: NULL
,
551 m_StyleInfo
->m_bg_valid
?
552 &m_StyleInfo
->m_bg
: NULL
);
553 obj
->SetUserData(m_UserData
);
558 wxLayoutObjectCmd::Write(wxString
&ostr
)
560 ostr
<< WXLO_TYPE_CMD
<< '\n'
561 << m_StyleInfo
->family
<< '\n'
562 << m_StyleInfo
->size
<< '\n'
563 << m_StyleInfo
->style
<< '\n'
564 << m_StyleInfo
->weight
<< '\n'
565 << m_StyleInfo
->underline
<< '\n'
566 << m_StyleInfo
->m_fg_valid
<< '\n'
567 << m_StyleInfo
->m_bg_valid
<< '\n';
568 if(m_StyleInfo
->m_fg_valid
)
570 ostr
<< m_StyleInfo
->m_fg
.Red() << '\n'
571 << m_StyleInfo
->m_fg
.Green() << '\n'
572 << m_StyleInfo
->m_fg
.Blue() << '\n';
574 if(m_StyleInfo
->m_bg_valid
)
576 ostr
<< m_StyleInfo
->m_bg
.Red() << '\n'
577 << m_StyleInfo
->m_bg
.Green() << '\n'
578 << m_StyleInfo
->m_bg
.Blue() << '\n';
583 wxLayoutObjectCmd::Read(wxString
&istr
)
585 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd
;
588 ReadString(tmp
, istr
);
589 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->family
);
590 ReadString(tmp
, istr
);
591 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->size
);
592 ReadString(tmp
, istr
);
593 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->style
);
594 ReadString(tmp
, istr
);
595 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->weight
);
596 ReadString(tmp
, istr
);
597 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->underline
);
598 ReadString(tmp
, istr
);
599 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->m_fg_valid
);
600 ReadString(tmp
, istr
);
601 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->m_bg_valid
);
602 if(obj
->m_StyleInfo
->m_fg_valid
)
604 int red
, green
, blue
;
605 ReadString(tmp
, istr
);
606 sscanf(tmp
.c_str(),"%d", &red
);
607 ReadString(tmp
, istr
);
608 sscanf(tmp
.c_str(),"%d", &green
);
609 ReadString(tmp
, istr
);
610 sscanf(tmp
.c_str(),"%d", &blue
);
611 obj
->m_StyleInfo
->m_fg
= wxColour(red
, green
, blue
);
613 if(obj
->m_StyleInfo
->m_bg_valid
)
615 int red
, green
, blue
;
616 ReadString(tmp
, istr
);
617 sscanf(tmp
.c_str(),"%d", &red
);
618 ReadString(tmp
, istr
);
619 sscanf(tmp
.c_str(),"%d", &green
);
620 ReadString(tmp
, istr
);
621 sscanf(tmp
.c_str(),"%d", &blue
);
622 obj
->m_StyleInfo
->m_bg
= wxColour(red
, green
, blue
);
628 wxLayoutObjectCmd::~wxLayoutObjectCmd()
634 wxLayoutObjectCmd::GetStyle(void) const
640 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint
const & /* coords */,
641 wxLayoutList
*wxllist
,
642 CoordType begin
, CoordType
/* len */)
644 wxASSERT(m_StyleInfo
);
645 wxllist
->ApplyStyle(*m_StyleInfo
, dc
);
649 wxLayoutObjectCmd::Layout(wxDC
&dc
, class wxLayoutList
* llist
)
651 // this get called, so that recalculation uses right font sizes
652 Draw(dc
, wxPoint(0,0), llist
);
656 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
658 The wxLayoutLine object
660 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
662 wxLayoutLine::wxLayoutLine(wxLayoutLine
*prev
, wxLayoutList
*llist
)
664 m_Width
= m_Height
= 0;
674 RecalculatePosition(llist
);
679 m_LineNumber
= m_Previous
->GetLineNumber() + 1;
680 m_Next
= m_Previous
->GetNextLine();
681 m_Previous
->m_Next
= this;
686 m_Next
->m_Previous
= this;
690 m_StyleInfo
= llist
->GetDefaultStyleInfo();
692 llist
->IncNumLines();
695 wxLayoutLine::~wxLayoutLine()
697 // kbList cleans itself
701 wxLayoutLine::RecalculatePosition(wxLayoutList
*llist
)
703 wxASSERT(m_Previous
|| GetLineNumber() == 0);
705 wxPoint
posOld(m_Position
);
709 m_Position
= m_Previous
->GetPosition();
710 m_Position
.y
+= m_Previous
->GetHeight();
713 m_Position
= wxPoint(0,0);
715 if ( m_Position
!= posOld
)
717 // the whole line moved and must be repainted
718 llist
->SetUpdateRect(m_Position
);
719 llist
->SetUpdateRect(m_Position
.x
+ GetWidth() + MSW_CORRECTION
,
720 m_Position
.y
+ GetHeight() + MSW_CORRECTION
);
721 llist
->SetUpdateRect(posOld
);
722 llist
->SetUpdateRect(posOld
.x
+ GetWidth() + MSW_CORRECTION
,
723 posOld
.y
+ GetHeight() + MSW_CORRECTION
);
730 wxLayoutObjectList::iterator
731 wxLayoutLine::FindObject(CoordType xpos
, CoordType
*offset
) const
735 wxLayoutObjectList::iterator
738 CoordType x
= 0, len
;
740 /* We search through the objects. As we don't like returning the
741 object that the cursor is behind, we just remember such an
742 object in "found" so we can return it if there is really no
743 further object following it. */
744 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
746 len
= (**i
).GetLength();
747 if( x
<= xpos
&& xpos
<= x
+ len
)
750 if(xpos
== x
+ len
) // is there another object behind?
752 else // we are really inside this object
755 x
+= (**i
).GetLength();
757 return found
; // ==NULL if really none found
760 wxLayoutObjectList::iterator
761 wxLayoutLine::FindObjectScreen(wxDC
&dc
, wxLayoutList
*llist
,
762 CoordType xpos
, CoordType
*cxpos
,
767 llist
->ApplyStyle(GetStyleInfo(), dc
);
769 wxLayoutObjectList::iterator i
;
770 CoordType x
= 0, cx
= 0, width
;
772 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
774 wxLayoutObject
*obj
= *i
;
775 if ( obj
->GetType() == WXLO_TYPE_CMD
)
777 // this will set the correct font for the objects which follow
778 obj
->Layout(dc
, llist
);
781 width
= obj
->GetWidth();
782 if( x
<= xpos
&& xpos
<= x
+ width
)
784 *cxpos
= cx
+ obj
->GetOffsetScreen(dc
, xpos
-x
);
791 x
+= obj
->GetWidth();
792 cx
+= obj
->GetLength();
795 // behind last object:
800 return m_ObjectList
.tail();
803 /** Finds text in this line.
804 @param needle the text to find
805 @param xpos the position where to start the search
806 @return the cursoor coord where it was found or -1
809 wxLayoutLine::FindText(const wxString
&needle
, CoordType xpos
) const
814 wxString
const *text
;
816 for(wxLOiterator i
= m_ObjectList
.begin(); i
!= m_ObjectList
.end(); i
++)
818 if(cpos
>= xpos
) // search from here!
820 if((**i
).GetType() == WXLO_TYPE_TEXT
)
822 text
= & ((wxLayoutObjectText
*)(*i
))->GetText();
823 relpos
= text
->Find(needle
);
824 if(relpos
>= cpos
-xpos
) // -1 if not found
829 cpos
+= (**i
).GetLength();
832 return -1; // not found
836 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
839 wxASSERT(obj
!= NULL
);
844 wxLOiterator i
= FindObject(xpos
, &offset
);
847 if(xpos
== 0 ) // aha, empty line!
849 m_ObjectList
.push_back(obj
);
850 m_Length
+= obj
->GetLength();
857 CoordType len
= (**i
).GetLength();
858 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
859 { // insert before this object
860 m_ObjectList
.insert(i
,obj
);
861 m_Length
+= obj
->GetLength();
866 if( i
== m_ObjectList
.tail()) // last object?
867 m_ObjectList
.push_back(obj
);
869 { // insert after current object
871 m_ObjectList
.insert(i
,obj
);
873 m_Length
+= obj
->GetLength();
876 /* Otherwise we need to split the current object.
877 Fortunately this can only be a text object. */
878 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
879 wxString left
, right
;
880 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
881 left
= tobj
->GetText().substr(0,offset
);
882 right
= tobj
->GetText().substr(offset
,len
-offset
);
883 // current text object gets set to right half
884 tobj
->GetText() = right
; // set new text
885 // before it we insert the new object
886 m_ObjectList
.insert(i
,obj
);
887 m_Length
+= obj
->GetLength();
888 // and before that we insert the left half
889 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
894 wxLayoutLine::Insert(CoordType xpos
, const wxString
& text
)
901 wxLOiterator i
= FindObject(xpos
, &offset
);
902 if(i
!= NULLIT
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
904 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
905 tobj
->GetText().insert(offset
, text
);
906 m_Length
+= text
.Length();
910 if ( !Insert(xpos
, new wxLayoutObjectText(text
)) )
918 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
920 CoordType offset
, len
;
925 wxLOiterator i
= FindObject(xpos
, &offset
);
928 if(i
== NULLIT
) return npos
;
929 // now delete from that object:
930 if((**i
).GetType() != WXLO_TYPE_TEXT
)
932 if(offset
!= 0) // at end of line after a non-text object
935 len
= (**i
).GetLength();
938 m_ObjectList
.erase(i
);
942 // tidy up: remove empty text objects
943 if((**i
).GetLength() == 0)
945 m_ObjectList
.erase(i
);
949 CoordType max
= (**i
).GetLength() - offset
;
950 if(npos
< max
) max
= npos
;
953 if(xpos
== GetLength())
956 { // at the end of an object
957 // move to begin of next object:
959 continue; // start over
964 if(offset
== 0 && max
== (**i
).GetLength())
965 m_ObjectList
.erase(i
); // remove the whole object
967 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
975 wxLayoutLine::DeleteWord(CoordType xpos
)
981 wxLOiterator i
= FindObject(xpos
, &offset
);
985 if(i
== NULLIT
) return false;
986 if((**i
).GetType() != WXLO_TYPE_TEXT
)
988 // This should only happen when at end of line, behind a non-text
990 if(offset
== (**i
).GetLength()) return false;
991 m_Length
-= (**i
).GetLength(); // -1
992 m_ObjectList
.erase(i
);
993 return true; // we are done
997 if(offset
== (**i
).GetLength()) // at end of object
1002 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
1004 wxString str
= tobj
->GetText();
1005 str
= str
.substr(offset
,str
.Length()-offset
);
1006 // Find out how many positions we need to delete:
1007 // 1. eat leading space
1008 while(isspace(str
.c_str()[count
])) count
++;
1009 // 2. eat the word itself:
1010 while(isalnum(str
.c_str()[count
])) count
++;
1012 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
1013 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
1019 wxFAIL_MSG("unreachable");
1023 wxLayoutLine::DeleteLine(bool update
, wxLayoutList
*llist
)
1025 // maintain linked list integrity
1027 m_Next
->m_Previous
= m_Previous
;
1029 m_Previous
->m_Next
= m_Next
;
1031 // get the line numbers right again
1032 if ( update
&& m_Next
)
1037 // we can't use m_Next after "delete this", so we must save this pointer
1039 wxLayoutLine
*next
= m_Next
;
1042 llist
->DecNumLines();
1048 wxLayoutLine::Draw(wxDC
&dc
,
1049 wxLayoutList
*llist
,
1050 const wxPoint
& offset
) const
1052 wxLayoutObjectList::iterator i
;
1053 wxPoint pos
= offset
;
1054 pos
= pos
+ GetPosition();
1056 pos
.y
+= m_BaseLine
;
1058 CoordType xpos
= 0; // cursorpos, lenght of line
1060 CoordType from
, to
, tempto
;
1062 int highlight
= llist
->IsSelected(this, &from
, &to
);
1063 // WXLO_DEBUG(("highlight=%d", highlight ));
1064 if(highlight
== 1) // we need to draw the whole line inverted!
1065 llist
->StartHighlighting(dc
);
1067 llist
->EndHighlighting(dc
);
1069 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
1071 if(highlight
== -1) // partially highlight line
1073 // parts of the line need highlighting
1074 tempto
= xpos
+(**i
).GetLength();
1075 (**i
).Draw(dc
, pos
, llist
, from
-xpos
, to
-xpos
);
1078 (**i
).Draw(dc
, pos
, llist
);
1079 pos
.x
+= (**i
).GetWidth();
1080 xpos
+= (**i
).GetLength();
1085 This function does all the recalculation, that is, it should only be
1086 called from within wxLayoutList::Layout(), as it uses the current
1087 list's styleinfo and updates it.
1090 wxLayoutLine::Layout(wxDC
&dc
,
1091 wxLayoutList
*llist
,
1093 wxPoint
*cursorSize
,
1094 wxLayoutStyleInfo
*cursorStyle
,
1096 bool suppressSIupdate
)
1098 wxLayoutObjectList::iterator i
;
1100 // when a line becomes dirty, we redraw it from the place where it was
1101 // changed till the end of line (because the following wxLayoutObjects are
1102 // moved when the preceding one changes) - calculate the update rectangle.
1103 CoordType updateTop
= m_Position
.y
,
1105 updateWidth
= m_Width
,
1106 updateHeight
= m_Height
;
1110 bottomHeight
= 0; // above and below baseline
1112 objTopHeight
, objBottomHeight
; // above and below baseline
1116 CoordType heightOld
= m_Height
;
1122 bool cursorFound
= false;
1124 RecalculatePosition(llist
);
1128 *cursorPos
= m_Position
;
1129 if(cursorSize
) *cursorSize
= wxPoint(0,0);
1132 m_StyleInfo
= llist
->GetStyleInfo(); // save current style
1133 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
1135 wxLayoutObject
*obj
= *i
;
1136 obj
->Layout(dc
, llist
);
1137 wxPoint sizeObj
= obj
->GetSize(&objTopHeight
, &objBottomHeight
);
1139 if(cursorPos
&& ! cursorFound
)
1141 // we need to check whether the text cursor is here
1142 len
= obj
->GetLength();
1143 if(count
<= cx
&& count
+len
> cx
)
1145 if(obj
->GetType() == WXLO_TYPE_TEXT
)
1147 len
= cx
- count
; // pos in object
1148 CoordType width
, height
, descent
;
1149 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
1150 &width
, &height
, &descent
);
1151 cursorPos
->x
+= width
;
1152 cursorPos
->y
= m_Position
.y
;
1154 if(len
< obj
->GetLength())
1155 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
1157 str
= WXLO_CURSORCHAR
;
1158 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
1160 if(cursorStyle
) // set style info
1161 *cursorStyle
= llist
->GetStyleInfo();
1164 // Just in case some joker inserted an empty string object:
1166 width
= WXLO_MINIMUM_CURSOR_WIDTH
;
1169 cursorSize
->x
= width
;
1170 cursorSize
->y
= height
;
1173 cursorFound
= true; // no more checks
1177 // on some other object
1178 CoordType top
, bottom
; // unused
1180 *cursorSize
= obj
->GetSize(&top
,&bottom
);
1181 cursorPos
->y
= m_Position
.y
;
1182 cursorFound
= true; // no more checks
1188 cursorPos
->x
+= obj
->GetWidth();
1192 m_Width
+= sizeObj
.x
;
1193 if(sizeObj
.y
> m_Height
)
1195 m_Height
= sizeObj
.y
;
1198 if(objTopHeight
> topHeight
)
1199 topHeight
= objTopHeight
;
1200 if(objBottomHeight
> bottomHeight
)
1201 bottomHeight
= objBottomHeight
;
1206 if ( updateHeight
< m_Height
)
1207 updateHeight
= m_Height
;
1208 if ( updateWidth
< m_Width
)
1209 updateWidth
= m_Width
;
1211 // update all line if we don't know where to start from
1212 if ( updateLeft
== -1 )
1215 llist
->SetUpdateRect(updateLeft
, updateTop
);
1216 llist
->SetUpdateRect(updateLeft
+ updateWidth
+ MSW_CORRECTION
,
1217 updateTop
+ updateHeight
+ MSW_CORRECTION
);
1220 if(topHeight
+ bottomHeight
> m_Height
)
1222 m_Height
= topHeight
+bottomHeight
;
1225 m_BaseLine
= topHeight
;
1229 CoordType width
, height
, descent
;
1230 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1232 m_BaseLine
= m_Height
- descent
;
1235 // tell next line about coordinate change
1236 if(m_Next
&& m_Height
!= heightOld
)
1238 m_Next
->MarkDirty();
1241 // We need to check whether we found a valid cursor size:
1242 if(cursorPos
&& cursorSize
)
1244 // this might be the case if the cursor is at the end of the
1245 // line or on a command object:
1246 if(cursorSize
->x
< WXLO_MINIMUM_CURSOR_WIDTH
)
1248 CoordType width
, height
, descent
;
1249 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1250 cursorSize
->x
= width
;
1251 cursorSize
->y
= height
;
1253 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
1254 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
1261 wxLayoutLine::Break(CoordType xpos
, wxLayoutList
*llist
)
1263 wxASSERT(xpos
>= 0);
1268 wxLOiterator i
= FindObject(xpos
, &offset
);
1270 // must be at the end of the line then
1271 return new wxLayoutLine(this, llist
);
1274 wxLayoutLine
*newLine
= new wxLayoutLine(this, llist
);
1275 // split object at i:
1276 if((**i
).GetType() == WXLO_TYPE_TEXT
&& offset
!= 0)
1278 wxString left
, right
;
1279 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
1280 left
= tobj
->GetText().substr(0,offset
);
1281 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
1282 // current text object gets set to left half
1283 tobj
->GetText() = left
; // set new text
1284 newLine
->Append(new wxLayoutObjectText(right
));
1285 m_Length
-= right
.Length();
1286 i
++; // don't move this object to the new list
1291 i
++; // move objects from here to new list
1294 while(i
!= m_ObjectList
.end())
1296 wxLayoutObject
*obj
= *i
;
1297 newLine
->Append(obj
);
1298 m_Length
-= obj
->GetLength();
1300 m_ObjectList
.remove(i
); // remove without deleting it
1303 m_Next
->MarkDirty();
1308 wxLayoutLine::ReNumber(void)
1310 CoordType lineNo
= m_Previous
? m_Previous
->m_LineNumber
+1 : 0;
1311 m_LineNumber
= lineNo
++;
1313 for(wxLayoutLine
*next
= GetNextLine();
1314 next
; next
= next
->GetNextLine())
1315 next
->m_LineNumber
= lineNo
++;
1319 wxLayoutLine::MergeNextLine(wxLayoutList
*llist
)
1321 wxCHECK_RET(GetNextLine(),"wxLayout internal error: no next line to merge");
1322 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
1325 MarkDirty(GetWidth());
1327 wxLayoutObject
*last
= NULL
;
1328 for(i
= list
.begin(); i
!= list
.end();)
1330 wxLayoutObject
*current
= *i
;
1332 // merge text objects together for efficiency
1333 if ( last
&& last
->GetType() == WXLO_TYPE_TEXT
&&
1334 current
->GetType() == WXLO_TYPE_TEXT
)
1336 wxLayoutObjectText
*textObj
= (wxLayoutObjectText
*)last
;
1337 wxString
text(textObj
->GetText());
1338 text
+= ((wxLayoutObjectText
*)current
)->GetText();
1339 textObj
->SetText(text
);
1341 list
.erase(i
); // remove and delete it
1345 // just append the object "as was"
1348 list
.remove(i
); // remove without deleting it
1351 wxASSERT(list
.empty());
1353 wxLayoutLine
*oldnext
= GetNextLine();
1354 wxLayoutLine
*nextLine
= oldnext
->GetNextLine();
1358 nextLine
->ReNumber();
1362 // this is now done in Delete(), but if this function is ever called
1363 // from elsewhere, we might have to move refresh code back here (in
1364 // order not to duplicate it)
1366 wxPoint
pos(oldnext
->GetPosition());
1367 llist
->SetUpdateRect(pos
);
1368 llist
->SetUpdateRect(pos
.x
+ oldnext
->GetWidth() + MSW_CORRECTION
,
1369 pos
.y
+ oldnext
->GetHeight() + MSW_CORRECTION
);
1373 llist
->DecNumLines();
1379 wxLayoutLine::GetWrapPosition(CoordType column
)
1382 wxLOiterator i
= FindObject(column
, &offset
);
1383 if(i
== NULLIT
) return -1; // cannot wrap
1385 // go backwards through the list and look for space in text objects
1388 if((**i
).GetType() == WXLO_TYPE_TEXT
)
1392 if( isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1399 }while(offset
!= -1);
1400 i
--; // move on to previous object
1404 column
-= (**i
).GetLength();
1408 offset
= (**i
).GetLength();
1409 }while(i
!= NULLIT
);
1410 /* If we reached the begin of the list and have more than one
1411 object, that one is longer than the margin, so break behind
1414 i
= m_ObjectList
.begin();
1415 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1417 pos
+= (**i
).GetLength();
1420 if(i
== NULLIT
) return -1; //why should this happen?
1421 pos
+= (**i
).GetLength();
1423 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1425 pos
+= (**i
).GetLength();
1428 if(i
== NULLIT
) return -1; //this is possible, if there is only one text object
1429 // now we are at the second text object:
1430 pos
-= (**i
).GetLength();
1431 return pos
; // in front of it
1435 #ifdef WXLAYOUT_DEBUG
1437 wxLayoutLine::Debug(void)
1440 wxPoint pos
= GetPosition();
1441 WXLO_DEBUG(("Line %ld, Pos (%ld,%ld), Height %ld, BL %ld, Font: %d",
1442 (long int) GetLineNumber(),
1443 (long int) pos
.x
, (long int) pos
.y
,
1444 (long int) GetHeight(),
1445 (long int) m_BaseLine
,
1446 (int) m_StyleInfo
.family
));
1447 if(m_ObjectList
.begin() != NULLIT
)
1448 (**m_ObjectList
.begin()).Debug();
1454 wxLayoutLine::Copy(wxLayoutList
*llist
,
1458 CoordType firstOffset
, lastOffset
;
1460 if(to
== -1) to
= GetLength();
1461 if(from
== to
) return;
1463 wxLOiterator first
= FindObject(from
, &firstOffset
);
1464 wxLOiterator last
= FindObject(to
, &lastOffset
);
1466 // Common special case: only one object
1467 if( first
!= NULLIT
&& last
!= NULLIT
&& *first
== *last
)
1469 if( (**first
).GetType() == WXLO_TYPE_TEXT
)
1471 llist
->Insert(new wxLayoutObjectText(
1472 ((wxLayoutObjectText
1473 *)*first
)->GetText().substr(firstOffset
,
1474 lastOffset
-firstOffset
))
1478 else // what can we do?
1480 if(lastOffset
> firstOffset
) // i.e. +1 :-)
1481 llist
->Insert( (**first
).Copy() );
1486 // If we reach here, we can safely copy the whole first object from
1487 // the firstOffset position on:
1488 if((**first
).GetType() == WXLO_TYPE_TEXT
&& firstOffset
!= 0)
1490 llist
->Insert(new wxLayoutObjectText(
1491 ((wxLayoutObjectText
*)*first
)->GetText().substr(firstOffset
))
1494 else if(firstOffset
== 0)
1495 llist
->Insert( (**first
).Copy() );
1496 // else nothing to copy :-(
1498 // Now we copy all objects before the last one:
1499 wxLOiterator i
= first
; i
++;
1500 for( ; i
!= last
; i
++)
1501 llist
->Insert( (**i
).Copy() );
1503 // And now the last object:
1506 if( (**last
).GetType() == WXLO_TYPE_TEXT
)
1508 llist
->Insert(new wxLayoutObjectText(
1509 ((wxLayoutObjectText
*)*last
)->GetText().substr(0,lastOffset
))
1513 llist
->Insert( (**last
).Copy() );
1518 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1520 The wxLayoutList object
1522 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1524 wxLayoutList::wxLayoutList()
1526 #ifdef WXLAYOUT_USE_CARET
1528 #endif // WXLAYOUT_USE_CARET
1532 SetAutoFormatting(TRUE
);
1533 ForceTotalLayout(TRUE
); // for the first time, do all
1534 InvalidateUpdateRect();
1538 wxLayoutList::~wxLayoutList()
1540 SetAutoFormatting(FALSE
);
1543 m_FirstLine
->DeleteLine(false, this);
1545 wxASSERT_MSG( m_numLines
== 0, "line count calculation broken" );
1549 wxLayoutList::Empty(void)
1552 m_FirstLine
= m_FirstLine
->DeleteLine(false, this);
1554 m_CursorPos
= wxPoint(0,0);
1555 m_CursorScreenPos
= wxPoint(0,0);
1556 m_CursorSize
= wxPoint(0,0);
1557 m_movedCursor
= true;
1558 m_FirstLine
= new wxLayoutLine(NULL
, this); // empty first line
1559 m_CursorLine
= m_FirstLine
;
1560 InvalidateUpdateRect();
1565 wxLayoutList::InternalClear(void)
1567 m_Selection
.m_selecting
= false;
1568 m_Selection
.m_valid
= false;
1570 m_DefaultStyleInfo
.family
= wxSWISS
;
1571 m_DefaultStyleInfo
.size
= WXLO_DEFAULTFONTSIZE
;
1572 m_DefaultStyleInfo
.style
= wxNORMAL
;
1573 m_DefaultStyleInfo
.weight
= wxNORMAL
;
1574 m_DefaultStyleInfo
.underline
= 0;
1575 m_DefaultStyleInfo
.m_fg_valid
= TRUE
;
1576 m_DefaultStyleInfo
.m_fg
= *wxBLACK
;
1577 m_DefaultStyleInfo
.m_bg_valid
= TRUE
;
1578 m_DefaultStyleInfo
.m_bg
= *wxWHITE
;
1580 m_CurrentStyleInfo
= m_DefaultStyleInfo
;
1581 m_CursorStyleInfo
= m_DefaultStyleInfo
;
1585 wxLayoutList::Read(wxString
&istr
)
1587 /* In order to handle input of formatted string "nicely", we need
1588 to restore our current font settings after the string. So first
1589 of all, we create a StyleInfo structure with our current
1591 wxLayoutStyleInfo current_si
= GetStyleInfo();
1593 while(istr
.Length())
1595 wxLayoutObject
*obj
= wxLayoutObject::Read(istr
);
1599 /* Now we use the current_si to restore our last font settings: */
1600 Insert(new wxLayoutObjectCmd(current_si
));
1605 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1606 int underline
, wxColour
*fg
,
1609 if(family
!= -1) m_CurrentStyleInfo
.family
= family
;
1610 if(size
!= -1) m_CurrentStyleInfo
.size
= size
;
1611 if(style
!= -1) m_CurrentStyleInfo
.style
= style
;
1612 if(weight
!= -1) m_CurrentStyleInfo
.weight
= weight
;
1613 if(underline
!= -1) m_CurrentStyleInfo
.underline
= underline
!= 0;
1614 if(fg
) m_CurrentStyleInfo
.m_fg
= *fg
;
1615 if(bg
) m_CurrentStyleInfo
.m_bg
= *bg
;
1617 new wxLayoutObjectCmd(
1618 m_CurrentStyleInfo
.family
,
1619 m_CurrentStyleInfo
.size
,
1620 m_CurrentStyleInfo
.style
,
1621 m_CurrentStyleInfo
.weight
,
1622 m_CurrentStyleInfo
.underline
,
1627 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1628 int underline
, char const *fg
, char const *bg
)
1636 cfg
= wxTheColourDatabase
->FindColour(fg
);
1638 cbg
= wxTheColourDatabase
->FindColour(bg
);
1640 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
1644 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
1645 int underline
, wxColour
*fg
, wxColour
*bg
)
1648 m_DefaultStyleInfo
= wxLayoutStyleInfo(family
, size
, style
, weight
,
1650 m_CurrentStyleInfo
= m_DefaultStyleInfo
;
1652 // Empty() should be called after we set m_DefaultStyleInfo because
1653 // otherwise the style info for the first line (created in Empty()) would be
1659 wxLayoutList::FindText(const wxString
&needle
, const wxPoint
&cpos
) const
1664 for(line
= m_FirstLine
;
1666 line
= line
->GetNextLine())
1668 if(line
->GetLineNumber() >= cpos
.y
)
1670 xpos
= line
->FindText(needle
,
1671 (line
->GetLineNumber() == cpos
.y
) ?
1674 return wxPoint(xpos
, line
->GetLineNumber());
1677 return wxPoint(-1,-1);
1682 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
1684 AddCursorPosToUpdateRect();
1686 wxPoint cursorPosOld
= m_CursorPos
;
1688 wxLayoutLine
*line
= m_FirstLine
;
1689 while(line
&& line
->GetLineNumber() != p
.y
)
1690 line
= line
->GetNextLine();
1691 if(line
&& line
->GetLineNumber() == p
.y
) // found it
1693 m_CursorPos
.y
= p
.y
;
1694 m_CursorLine
= line
;
1695 CoordType len
= line
->GetLength();
1698 m_CursorPos
.x
= p
.x
;
1702 m_CursorPos
.x
= len
;
1706 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1708 return m_CursorPos
== p
;
1712 wxLayoutList::MoveCursorVertically(int n
)
1714 AddCursorPosToUpdateRect();
1716 wxPoint cursorPosOld
= m_CursorPos
;
1719 if(n
< 0) // move up
1721 if(m_CursorLine
== m_FirstLine
) return false;
1722 while(n
< 0 && m_CursorLine
)
1724 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1730 m_CursorLine
= m_FirstLine
;
1736 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1737 m_CursorPos
.x
= m_CursorLine
->GetLength();
1743 wxLayoutLine
*last
= m_CursorLine
;
1744 if(! m_CursorLine
->GetNextLine()) return false;
1745 while(n
> 0 && m_CursorLine
)
1749 m_CursorLine
= m_CursorLine
->GetNextLine();
1753 m_CursorLine
= last
;
1759 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1760 m_CursorPos
.x
= m_CursorLine
->GetLength();
1765 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1771 wxLayoutList::MoveCursorHorizontally(int n
)
1773 AddCursorPosToUpdateRect();
1775 wxPoint cursorPosOld
= m_CursorPos
;
1780 if(m_CursorPos
.x
== 0) // at begin of line
1782 if(! MoveCursorVertically(-1))
1784 MoveCursorToEndOfLine();
1789 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1790 m_CursorPos
.x
-= move
; n
+= move
;
1795 int len
= m_CursorLine
->GetLength();
1796 if(m_CursorPos
.x
== len
) // at end of line
1798 if(! MoveCursorVertically(1))
1800 MoveCursorToBeginOfLine();
1805 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1806 m_CursorPos
.x
+= move
;
1810 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1816 wxLayoutList::MoveCursorWord(int n
, bool untilNext
)
1818 wxCHECK_MSG( m_CursorLine
, false, "no current line" );
1819 wxCHECK_MSG( n
== -1 || n
== +1, false, "not implemented yet" );
1821 CoordType moveDistance
= 0;
1823 wxLayoutLine
*lineCur
= m_CursorLine
;
1824 for ( wxLOiterator i
= lineCur
->FindObject(m_CursorPos
.x
, &offset
);
1832 // moving forward, pass to the first object of the next line
1834 lineCur
= lineCur
->GetNextLine();
1836 i
= lineCur
->GetFirstObject();
1840 // moving backwards, pass to the last object of the prev line
1842 lineCur
= lineCur
->GetPreviousLine();
1844 i
= lineCur
->GetLastObject();
1849 // moved to the end/beginning of text
1856 wxLayoutObject
*obj
= *i
;
1860 // calculate offset: we are either at the very beginning or the very
1861 // end of the object, so it isn't very difficult (the only time when
1862 // offset is != -1 is for the very first iteration when its value is
1863 // returned by FindObject)
1867 offset
= obj
->GetLength();
1870 if( obj
->GetType() != WXLO_TYPE_TEXT
)
1872 // any visible non text objects count as one word
1873 if ( obj
->IsVisibleObject() )
1877 moveDistance
+= obj
->GetLength();
1882 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)obj
;
1884 bool canAdvance
= true;
1886 if ( offset
== tobj
->GetLength() )
1891 // can't move further in this text object
1894 // still should move over the object border
1898 else if ( offset
> 0 )
1900 // offset is off by 1, make it a valid index
1907 const wxString
& text
= tobj
->GetText();
1908 const char *start
= text
.c_str();
1909 const char *end
= start
+ text
.length();
1910 const char *p
= start
+ offset
;
1918 // to the beginning/end of the next/prev word
1919 while ( p
>= start
&& p
< end
&& isspace(*p
) )
1924 // go to the end/beginning of the word (in a broad sense...)
1925 while ( p
>= start
&& p
< end
&& !isspace(*p
) )
1934 // now advance to the beginning of the next word
1935 while ( isspace(*p
) && p
< end
)
1941 // in these 2 cases we took 1 char too much
1942 if ( (p
< start
) || isspace(*p
) )
1948 CoordType moveDelta
= p
- start
- offset
;
1949 if ( (n
< 0) && (offset
== tobj
->GetLength() - 1) )
1951 // because we substracted 1 from offset in this case above, now
1952 // compensate for it
1956 if ( moveDelta
!= 0 )
1958 moveDistance
+= moveDelta
;
1965 // except for the first iteration, offset is calculated in the beginning
1970 MoveCursorHorizontally(moveDistance
);
1976 wxLayoutList::Insert(wxString
const &text
)
1978 wxASSERT(m_CursorLine
);
1979 wxASSERT_MSG( text
.Find('\n') == wxNOT_FOUND
, "use wxLayoutImportText!" );
1984 AddCursorPosToUpdateRect();
1986 if ( !m_CursorLine
->Insert(m_CursorPos
.x
, text
) )
1989 m_CursorPos
.x
+= text
.Length();
1991 m_movedCursor
= true;
1994 m_CursorLine
->MarkDirty();
2000 wxLayoutList::Insert(wxLayoutObject
*obj
)
2002 wxASSERT(m_CursorLine
);
2005 m_CursorLine
= GetFirstLine();
2007 AddCursorPosToUpdateRect();
2009 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
2010 m_CursorPos
.x
+= obj
->GetLength();
2011 m_movedCursor
= true;
2014 m_CursorLine
->MarkDirty();
2020 wxLayoutList::Insert(wxLayoutList
*llist
)
2025 for(wxLayoutLine
*line
= llist
->GetFirstLine();
2027 line
= line
->GetNextLine()
2030 for(wxLOiterator i
= line
->GetFirstObject();
2040 wxLayoutList::LineBreak(void)
2042 wxASSERT(m_CursorLine
);
2044 AddCursorPosToUpdateRect();
2046 wxPoint
position(m_CursorLine
->GetPosition());
2049 width
= m_CursorLine
->GetWidth(),
2050 height
= m_CursorLine
->GetHeight();
2052 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
, this);
2053 if(m_CursorLine
->GetPreviousLine() == NULL
)
2054 m_FirstLine
= m_CursorLine
;
2058 // The following code will produce a height which is guaranteed to
2059 // be too high: old lineheight + the height of both new lines.
2060 // We can probably drop the old line height and start with height =
2062 wxLayoutLine
*prev
= m_CursorLine
->GetPreviousLine();
2064 height
+= prev
->GetHeight();
2065 height
+= m_CursorLine
->GetHeight();
2067 m_movedCursor
= true;
2069 SetUpdateRect(position
);
2070 SetUpdateRect(position
.x
+ width
+ MSW_CORRECTION
,
2071 position
.y
+ height
+ MSW_CORRECTION
);
2077 wxLayoutList::WrapLine(CoordType column
)
2079 if(m_CursorPos
.x
<= column
|| column
< 1)
2080 return false; // do nothing yet
2083 CoordType xpos
= m_CursorLine
->GetWrapPosition(column
);
2085 return false; // cannot break line
2087 CoordType newpos
= m_CursorPos
.x
- xpos
- 1;
2088 m_CursorPos
.x
= xpos
;
2090 AddCursorPosToUpdateRect();
2093 Delete(1); // delete the space
2094 m_CursorPos
.x
= newpos
;
2096 m_CursorLine
->MarkDirty();
2098 m_movedCursor
= true;
2105 wxLayoutList::Delete(CoordType npos
)
2107 wxCHECK_MSG(m_CursorLine
, false, "can't delete in non existing line");
2112 AddCursorPosToUpdateRect();
2114 // were other lines appended to this one (this is important to know because
2115 // this means that our width _increased_ as the result of deletion)
2116 bool wasMerged
= false;
2118 // the size of the region to update
2119 CoordType totalHeight
= m_CursorLine
->GetHeight(),
2120 totalWidth
= m_CursorLine
->GetWidth();
2125 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
2129 // More to delete, continue on next line.
2131 // First, check if line is empty:
2132 if(m_CursorLine
->GetLength() == 0)
2134 // in this case, updating could probably be optimised
2136 wxASSERT(DeleteLines(1) == 0);
2145 // Need to join next line
2146 if(! m_CursorLine
->GetNextLine())
2151 wxLayoutLine
*next
= m_CursorLine
->GetNextLine();
2154 totalHeight
+= next
->GetHeight();
2155 totalWidth
+= next
->GetWidth();
2157 m_CursorLine
->MergeNextLine(this);
2162 wxFAIL_MSG("can't delete all this");
2172 // we need to update the whole tail of the line and the lines which
2176 wxPoint
position(m_CursorLine
->GetPosition());
2177 SetUpdateRect(position
);
2178 SetUpdateRect(position
.x
+ totalWidth
+ MSW_CORRECTION
,
2179 position
.y
+ totalHeight
+ MSW_CORRECTION
);
2186 wxLayoutList::DeleteLines(int n
)
2188 wxASSERT(m_CursorLine
);
2191 AddCursorPosToUpdateRect();
2195 if(!m_CursorLine
->GetNextLine())
2196 { // we cannot delete this line, but we can clear it
2197 MoveCursorToBeginOfLine();
2198 DeleteToEndOfLine();
2200 m_CursorLine
->MarkDirty();
2204 line
= m_CursorLine
;
2205 m_CursorLine
= m_CursorLine
->DeleteLine(true, this);
2207 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
2208 wxASSERT(m_FirstLine
);
2209 wxASSERT(m_CursorLine
);
2212 m_CursorLine
->MarkDirty();
2217 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
)
2221 wxLayoutLine
*line
= m_FirstLine
;
2223 // first, make sure everything is calculated - this might not be
2224 // needed, optimise it later
2225 ApplyStyle(m_DefaultStyleInfo
, dc
);
2228 line
->RecalculatePosition(this); // so we don't need to do it all the time
2229 // little condition to speed up redrawing:
2230 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
2231 line
= line
->GetNextLine();
2236 wxLayoutList::GetCursorScreenPos(void) const
2238 return m_CursorScreenPos
;
2242 Is called before each Draw(). Now, it will re-layout all lines which
2246 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
, bool forceAll
,
2247 wxPoint
*cpos
, wxPoint
*csize
)
2249 // first, make sure everything is calculated - this might not be
2250 // needed, optimise it later
2251 ApplyStyle(m_DefaultStyleInfo
, dc
);
2259 ForceTotalLayout(FALSE
);
2262 // If one line was dirty, we need to re-calculate all
2263 // following lines, too.
2264 bool wasDirty
= forceAll
;
2265 wxLayoutLine
*line
= m_FirstLine
;
2269 ApplyStyle(line
->GetStyleInfo(), dc
);
2271 // if any previous line was dirty, we need to layout all
2274 // layout dirty lines:
2276 // always layout the cursor line toupdate the cursor
2277 // position and size:
2278 || line
== m_CursorLine
2279 // or if it's the line we are asked to look for:
2280 || (cpos
&& line
->GetLineNumber() == cpos
->y
)
2286 // The following Layout() calls will update our
2287 // m_CurrentStyleInfo if needed.
2288 if(line
== m_CursorLine
)
2290 line
->Layout(dc
, this,
2291 (wxPoint
*)&m_CursorScreenPos
,
2292 (wxPoint
*)&m_CursorSize
,
2295 // we cannot layout the line twice, so copy the coords:
2296 if(cpos
&& line
->GetLineNumber() == cpos
->y
)
2298 *cpos
= m_CursorScreenPos
;
2300 *csize
= m_CursorSize
;
2304 if(cpos
&& line
->GetLineNumber() == cpos
->y
)
2305 line
->Layout(dc
, this,
2307 csize
, NULL
, cpos
->x
);
2309 line
->Layout(dc
, this);
2310 // little condition to speed up redrawing:
2311 if(bottom
!= -1 && line
->GetPosition().y
> bottom
)
2314 line
= line
->GetNextLine();
2317 #ifndef WXLAYOUT_USE_CARET
2318 // can only be 0 if we are on the first line and have no next line
2319 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
2320 m_CursorLine
->GetNextLine() == NULL
&&
2321 m_CursorLine
== m_FirstLine
));
2322 #endif // WXLAYOUT_USE_CARET
2323 AddCursorPosToUpdateRect();
2327 wxLayoutList::GetScreenPos(wxDC
&dc
, const wxPoint
&cpos
, wxPoint
*csize
)
2330 Layout(dc
, -1, false, &pos
, csize
);
2335 wxLayoutList::Draw(wxDC
&dc
,
2336 wxPoint
const &offset
,
2341 wxLayoutLine
*line
= m_FirstLine
;
2343 if ( m_Selection
.m_discarded
)
2345 // calculate them if we don't have them already
2346 if ( !m_Selection
.HasValidScreenCoords() )
2348 m_Selection
.m_ScreenA
= GetScreenPos(dc
, m_Selection
.m_CursorA
);
2349 m_Selection
.m_ScreenB
= GetScreenPos(dc
, m_Selection
.m_CursorB
);
2352 // invalidate the area which was previousle selected - and which is not
2353 // selected any more
2354 SetUpdateRect(m_Selection
.m_ScreenA
);
2355 SetUpdateRect(m_Selection
.m_ScreenB
);
2357 m_Selection
.m_discarded
= false;
2360 /* This call to Layout() will re-calculate and update all lines
2365 ApplyStyle(m_DefaultStyleInfo
, dc
);
2366 wxBrush
brush(m_CurrentStyleInfo
.m_bg
, wxSOLID
);
2368 dc
.SetBackgroundMode(wxTRANSPARENT
);
2372 // only draw if between top and bottom:
2374 line
->GetPosition().y
+ line
->GetHeight() > top
))
2376 ApplyStyle(line
->GetStyleInfo(), dc
);
2377 // little condition to speed up redrawing:
2379 && line
->GetPosition().y
2380 +(clipStrictly
? line
->GetHeight() : 0) >= bottom
)
2382 line
->Draw(dc
, this, offset
);
2384 line
= line
->GetNextLine();
2386 InvalidateUpdateRect();
2388 WXLO_DEBUG(("Selection is %s : l%d,%ld/%ld,%ld",
2389 m_Selection
.m_valid
? "valid" : "invalid",
2390 m_Selection
.m_CursorA
.x
, m_Selection
.m_CursorA
.y
,
2391 m_Selection
.m_CursorB
.x
, m_Selection
.m_CursorB
.y
));
2395 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
,
2399 // First, find the right line:
2401 *line
= m_FirstLine
,
2402 *lastline
= m_FirstLine
;
2405 ApplyStyle(m_DefaultStyleInfo
, dc
);
2408 p
= line
->GetPosition();
2409 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
2412 line
= line
->GetNextLine();
2415 bool didFind
= line
!= NULL
;
2419 // use the last line:
2424 cursorPos
->y
= line
->GetLineNumber();
2426 bool foundinline
= true;
2429 // Now, find the object in the line:
2434 i
= line
->FindObjectScreen(dc
, this,
2441 i
= line
->FindObjectScreen(dc
, this,
2446 *found
= didFind
&& foundinline
;
2448 return (i
== NULLIT
) ? NULL
: *i
;
2453 wxLayoutList::GetSize(void) const
2456 *line
= m_FirstLine
,
2459 return wxPoint(0,0);
2461 wxPoint
maxPoint(0,0);
2466 if(line
->GetWidth() > maxPoint
.x
)
2467 maxPoint
.x
= line
->GetWidth();
2469 line
= line
->GetNextLine();
2472 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
2474 // if the line was just added, its height would be 0 and we can't call
2475 // Layout() from here because we don't have a dc and we might be not drawing
2476 // at all, besides... So take the cursor height by default (taking 0 is bad
2477 // because then the scrollbars won't be resized and the new line won't be
2479 if ( last
->IsDirty() )
2481 if ( last
->GetHeight() == 0 )
2482 maxPoint
.y
+= m_CursorSize
.y
;
2483 if ( last
->GetWidth() == 0 && maxPoint
.x
< m_CursorSize
.x
)
2484 maxPoint
.x
= m_CursorSize
.x
;
2492 wxLayoutList::DrawCursor(wxDC
&dc
, bool active
, wxPoint
const &translate
)
2494 if ( m_movedCursor
)
2495 m_movedCursor
= false;
2497 wxPoint
coords(m_CursorScreenPos
);
2498 coords
+= translate
;
2500 #ifdef WXLAYOUT_DEBUG
2501 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
2502 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
2503 (long)coords
.x
, (long)coords
.y
,
2504 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
2505 (long)m_CursorLine
->GetLineNumber(),
2506 (long)m_CursorLine
->GetLength()));
2508 wxLogStatus("Cursor is at (%d, %d)", m_CursorPos
.x
, m_CursorPos
.y
);
2511 #ifdef WXLAYOUT_USE_CARET
2512 m_caret
->Move(coords
);
2513 #else // !WXLAYOUT_USE_CARET
2515 wxASSERT(m_CursorSize
.x
>= WXLO_MINIMUM_CURSOR_WIDTH
);
2516 dc
.SetBrush(*wxWHITE_BRUSH
);
2517 //FIXME: wxGTK XOR is borken at the moment!!!dc.SetLogicalFunction(wxXOR);
2518 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
2521 dc
.SetLogicalFunction(wxXOR
);
2522 dc
.DrawRectangle(coords
.x
, coords
.y
,
2523 m_CursorSize
.x
, m_CursorSize
.y
);
2524 SetUpdateRect(coords
.x
, coords
.y
);
2525 SetUpdateRect(coords
.x
+m_CursorSize
.x
, coords
.y
+m_CursorSize
.y
);
2529 dc
.SetLogicalFunction(wxCOPY
);
2530 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
2531 coords
.x
, coords
.y
);
2532 SetUpdateRect(coords
.x
, coords
.y
+m_CursorSize
.y
-1);
2533 SetUpdateRect(coords
.x
, coords
.y
);
2535 dc
.SetLogicalFunction(wxCOPY
);
2536 //dc.SetBrush(wxNullBrush);
2537 #endif // WXLAYOUT_USE_CARET/!WXLAYOUT_USE_CARET
2541 wxLayoutList::SetUpdateRect(CoordType x
, CoordType y
)
2543 if(m_UpdateRectValid
)
2544 GrowRect(m_UpdateRect
, x
, y
);
2549 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
2550 m_UpdateRect
.height
= 4;// wxGTK :-)
2551 m_UpdateRectValid
= true;
2556 wxLayoutList::StartSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2558 wxPoint
cpos(cposOrig
);
2561 WXLO_DEBUG(("Starting selection at %ld/%ld", cpos
.x
, cpos
.y
));
2562 m_Selection
.m_CursorA
= cpos
;
2563 m_Selection
.m_CursorB
= cpos
;
2564 m_Selection
.m_ScreenA
= spos
;
2565 m_Selection
.m_ScreenB
= spos
;
2566 m_Selection
.m_selecting
= true;
2567 m_Selection
.m_valid
= false;
2571 wxLayoutList::ContinueSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2573 wxPoint
cpos(cposOrig
);
2577 wxASSERT(m_Selection
.m_selecting
== true);
2578 wxASSERT(m_Selection
.m_valid
== false);
2579 WXLO_DEBUG(("Continuing selection at %ld/%ld", cpos
.x
, cpos
.y
));
2581 m_Selection
.m_ScreenB
= spos
;
2582 m_Selection
.m_CursorB
= cpos
;
2586 wxLayoutList::EndSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2588 wxPoint
cpos(cposOrig
);
2589 if(cpos
.x
== -1) cpos
= m_CursorPos
;
2590 ContinueSelection(cpos
, spos
);
2591 WXLO_DEBUG(("Ending selection at %ld/%ld", cpos
.x
, cpos
.y
));
2592 // we always want m_CursorA <= m_CursorB!
2593 if( m_Selection
.m_CursorA
> m_Selection
.m_CursorB
)
2595 // exchange the start/end points
2596 wxPoint help
= m_Selection
.m_CursorB
;
2597 m_Selection
.m_CursorB
= m_Selection
.m_CursorA
;
2598 m_Selection
.m_CursorA
= help
;
2600 help
= m_Selection
.m_ScreenB
;
2601 m_Selection
.m_ScreenB
= m_Selection
.m_ScreenA
;
2602 m_Selection
.m_ScreenA
= help
;
2604 m_Selection
.m_selecting
= false;
2605 m_Selection
.m_valid
= true;
2606 /// In case we just clicked somewhere, the selection will have zero
2607 /// size, so we discard it immediately.
2608 if(m_Selection
.m_CursorA
== m_Selection
.m_CursorB
)
2613 wxLayoutList::DiscardSelection()
2615 if ( !HasSelection() )
2618 m_Selection
.m_valid
=
2619 m_Selection
.m_selecting
= false;
2620 m_Selection
.m_discarded
= true;
2624 wxLayoutList::IsSelecting(void) const
2626 return m_Selection
.m_selecting
;
2630 wxLayoutList::IsSelected(const wxPoint
&cursor
) const
2632 if ( !HasSelection() )
2636 (m_Selection
.m_CursorA
<= cursor
2637 && cursor
<= m_Selection
.m_CursorB
)
2638 || (m_Selection
.m_CursorB
<= cursor
2639 && cursor
<= m_Selection
.m_CursorA
)
2644 /** Tests whether this layout line is selected and needs
2646 @param line to test for
2647 @return 0 = not selected, 1 = fully selected, -1 = partially
2651 wxLayoutList::IsSelected(const wxLayoutLine
*line
, CoordType
*from
,
2654 wxASSERT(line
); wxASSERT(to
); wxASSERT(from
);
2656 if(! m_Selection
.m_valid
&& ! m_Selection
.m_selecting
)
2659 CoordType y
= line
->GetLineNumber();
2661 (m_Selection
.m_CursorA
.y
< y
&& m_Selection
.m_CursorB
.y
> y
)
2662 || (m_Selection
.m_CursorB
.y
< y
&& m_Selection
.m_CursorA
.y
> y
)
2665 else if(m_Selection
.m_CursorA
.y
== y
)
2667 *from
= m_Selection
.m_CursorA
.x
;
2668 if(m_Selection
.m_CursorB
.y
== y
)
2669 *to
= m_Selection
.m_CursorB
.x
;
2672 if(m_Selection
.m_CursorB
> m_Selection
.m_CursorA
)
2673 *to
= line
->GetLength();
2679 CoordType help
= *to
;
2685 else if(m_Selection
.m_CursorB
.y
== y
)
2687 *to
= m_Selection
.m_CursorB
.x
;
2688 if(m_Selection
.m_CursorA
.y
== y
)
2689 *from
= m_Selection
.m_CursorA
.x
;
2692 if(m_Selection
.m_CursorB
> m_Selection
.m_CursorA
)
2695 *from
= line
->GetLength();
2699 CoordType help
= *to
;
2710 wxLayoutList::DeleteSelection(void)
2712 if(! m_Selection
.m_valid
)
2715 m_Selection
.m_valid
= false;
2717 // Only delete part of the current line?
2718 if(m_Selection
.m_CursorA
.y
== m_Selection
.m_CursorB
.y
)
2720 MoveCursorTo(m_Selection
.m_CursorA
);
2721 Delete(m_Selection
.m_CursorB
.x
- m_Selection
.m_CursorA
.x
);
2725 // We now know that the two lines are different:
2728 * firstLine
= GetLine(m_Selection
.m_CursorA
.y
),
2729 * lastLine
= GetLine(m_Selection
.m_CursorB
.y
);
2730 // be a bit paranoid:
2731 if(! firstLine
|| ! lastLine
)
2734 // First, delete what's left of this line:
2735 MoveCursorTo(m_Selection
.m_CursorA
);
2736 DeleteToEndOfLine();
2738 wxLayoutLine
*prevLine
= firstLine
->GetPreviousLine(),
2739 *nextLine
= firstLine
->GetNextLine();
2740 while(nextLine
&& nextLine
!= lastLine
)
2741 nextLine
= nextLine
->DeleteLine(false, this);
2743 // Now nextLine = lastLine;
2744 Delete(1); // This joins firstLine and nextLine
2745 Delete(m_Selection
.m_CursorB
.x
); // This deletes the first x positions
2747 // Recalculate the line positions and numbers but notice that firstLine
2748 // might not exist any more - it could be deleted by Delete(1) above
2749 wxLayoutLine
*firstLine2
= prevLine
? prevLine
->GetNextLine() : m_FirstLine
;
2750 firstLine2
->MarkDirty();
2753 /// Starts highlighting the selection
2755 wxLayoutList::StartHighlighting(wxDC
&dc
)
2758 dc
.SetTextForeground(m_CurrentStyleInfo
.m_bg
);
2759 dc
.SetTextBackground(m_CurrentStyleInfo
.m_fg
);
2760 dc
.SetBackgroundMode(wxSOLID
);
2764 /// Ends highlighting the selection
2766 wxLayoutList::EndHighlighting(wxDC
&dc
)
2769 dc
.SetTextForeground(m_CurrentStyleInfo
.m_fg
);
2770 dc
.SetTextBackground(m_CurrentStyleInfo
.m_bg
);
2771 dc
.SetBackgroundMode(wxTRANSPARENT
);
2777 wxLayoutList::GetLine(CoordType index
) const
2779 wxASSERT_MSG( (0 <= index
) && (index
< (CoordType
)m_numLines
),
2783 CoordType n
= index
;
2785 CoordType lineNo
= 0;
2788 for ( line
= m_FirstLine
; line
&& n
-- > 0; line
=
2789 line
->GetNextLine() )
2792 wxASSERT(line
->GetLineNumber() == lineNo
);
2799 // should be the right one
2800 wxASSERT( line
->GetLineNumber() == index
);
2808 wxLayoutList::Copy(const wxPoint
&from
,
2815 for(firstLine
= m_FirstLine
;
2816 firstLine
&& firstLine
->GetLineNumber() < from
.y
;
2817 firstLine
=firstLine
->GetNextLine())
2819 if(!firstLine
|| firstLine
->GetLineNumber() != from
.y
)
2822 for(lastLine
= m_FirstLine
;
2823 lastLine
&& lastLine
->GetLineNumber() < to
.y
;
2824 lastLine
=lastLine
->GetNextLine())
2826 if(!lastLine
|| lastLine
->GetLineNumber() != to
.y
)
2831 wxLayoutLine
*tmp
= firstLine
;
2832 firstLine
= lastLine
;
2836 wxLayoutList
*llist
= new wxLayoutList();
2838 if(firstLine
== lastLine
)
2840 firstLine
->Copy(llist
, from
.x
, to
.x
);
2844 // Extract objects from first line
2845 firstLine
->Copy(llist
, from
.x
);
2847 // Extract all lines between
2848 for(wxLayoutLine
*line
= firstLine
->GetNextLine();
2850 line
= line
->GetNextLine())
2855 // Extract objects from last line
2856 lastLine
->Copy(llist
, 0, to
.x
);
2862 wxLayoutList::GetSelection(wxLayoutDataObject
*wxlo
, bool invalidate
)
2864 if(! m_Selection
.m_valid
)
2866 if(m_Selection
.m_selecting
)
2872 if(invalidate
) m_Selection
.m_valid
= false;
2874 wxLayoutList
*llist
= Copy( m_Selection
.m_CursorA
,
2875 m_Selection
.m_CursorB
);
2877 if(llist
&& wxlo
) // export as data object, too
2881 wxLayoutExportObject
*exp
;
2882 wxLayoutExportStatus
status(llist
);
2883 while((exp
= wxLayoutExport( &status
, WXLO_EXPORT_AS_OBJECTS
)) != NULL
)
2885 if(exp
->type
== WXLO_EXPORT_EMPTYLINE
)
2886 ; //FIXME missing support for linebreaks in string format
2888 exp
->content
.object
->Write(string
);
2891 wxlo
->SetLayoutData(string
);
2898 #define COPY_SI(what) if(si.what != -1) { m_CurrentStyleInfo.what = si.what; fontChanged = TRUE; }
2901 wxLayoutList::ApplyStyle(wxLayoutStyleInfo
const &si
, wxDC
&dc
)
2903 bool fontChanged
= FALSE
;
2910 dc
.SetFont( m_FontCache
.GetFont(m_CurrentStyleInfo
) );
2914 m_CurrentStyleInfo
.m_fg
= si
.m_fg
;
2915 m_CurrentStyleInfo
.m_fg_valid
= true;
2916 dc
.SetTextForeground(m_CurrentStyleInfo
.m_fg
);
2920 m_CurrentStyleInfo
.m_bg
= si
.m_bg
;
2921 m_CurrentStyleInfo
.m_bg_valid
= true;
2922 dc
.SetTextBackground(m_CurrentStyleInfo
.m_bg
);
2927 #ifdef WXLAYOUT_DEBUG
2930 wxLayoutList::Debug(void)
2932 WXLO_DEBUG(("Cursor is in line %d, screen pos = (%d, %d)",
2933 m_CursorLine
->GetLineNumber(),
2934 m_CursorScreenPos
.x
, m_CursorScreenPos
.y
));
2937 for(line
= m_FirstLine
; line
; line
= line
->GetNextLine())
2946 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2950 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2952 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
2953 wxString
const & title
)
2958 // remove any highlighting which could interfere with printing:
2959 m_llist
->StartSelection();
2960 m_llist
->EndSelection();
2961 // force a full layout of the list:
2962 m_llist
->ForceTotalLayout();
2963 // layout is called in ScaleDC() when we have a DC
2966 wxLayoutPrintout::~wxLayoutPrintout()
2971 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
2973 // The following bit is taken from the printing sample, let's see
2974 // whether it works for us.
2976 /* You might use THIS code to set the printer DC to ROUGHLY reflect
2977 * the screen text size. This page also draws lines of actual length 5cm
2980 // Get the logical pixels per inch of screen and printer
2981 int ppiScreenX
, ppiScreenY
;
2982 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
2983 int ppiPrinterX
, ppiPrinterY
;
2984 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
2986 if(ppiScreenX
== 0) // not yet set, need to guess
2991 if(ppiPrinterX
== 0) // not yet set, need to guess
2997 // This scales the DC so that the printout roughly represents the
2998 // the screen scaling. The text point size _should_ be the right size
2999 // but in fact is too small for some reason. This is a detail that will
3000 // need to be addressed at some point but can be fudged for the
3002 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
3004 // Now we have to check in case our real page size is reduced
3005 // (e.g. because we're drawing to a print preview memory DC)
3006 int pageWidth
, pageHeight
;
3008 dc
->GetSize(&w
, &h
);
3009 GetPageSizePixels(&pageWidth
, &pageHeight
);
3010 if(pageWidth
!= 0) // doesn't work always
3012 // If printer pageWidth == current DC width, then this doesn't
3013 // change. But w might be the preview bitmap width, so scale down.
3014 scale
= scale
* (float)(w
/(float)pageWidth
);
3016 dc
->SetUserScale(scale
, scale
);
3020 bool wxLayoutPrintout::OnPrintPage(int page
)
3029 top
= (page
- 1)*m_PrintoutHeight
;
3030 bottom
= top
+ m_PrintoutHeight
;
3032 WXLO_DEBUG(("OnPrintPage(%d) printing from %d to %d", page
, top
,
3034 // SetDeviceOrigin() doesn't work here, so we need to manually
3035 // translate all coordinates.
3036 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
3037 m_llist
->Draw(*dc
, translate
, top
, bottom
, TRUE
/* clip strictly
3045 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
3047 /* We allocate a temporary wxDC for printing, so that we can
3048 determine the correct paper size and scaling. We don't actually
3049 print anything on it. */
3051 wxPrinterDC
*psdc
= new wxPrinterDC("","",WXLLIST_TEMPFILE
,false);
3053 wxPostScriptDC
*psdc
= new wxPostScriptDC(WXLLIST_TEMPFILE
,false);
3056 psdc
->StartDoc(m_title
);
3057 // before we draw anything, me must make sure the list is properly
3059 m_llist
->Layout(*psdc
);
3061 float scale
= ScaleDC(psdc
);
3063 psdc
->GetSize(&m_PageWidth
, &m_PageHeight
);
3065 // This sets a left/top origin of 15% and 5%:
3066 m_Offset
= wxPoint((15*m_PageWidth
)/100, (5*m_PageHeight
)/100);
3068 // This is the length of the printable area.
3069 m_PrintoutHeight
= m_PageHeight
- 2*m_Offset
.y
;
3070 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
3073 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
3076 *maxPage
= m_NumOfPages
;
3079 *selPageTo
= m_NumOfPages
;
3082 wxRemoveFile(WXLLIST_TEMPFILE
);
3085 bool wxLayoutPrintout::HasPage(int pageNum
)
3087 return pageNum
<= m_NumOfPages
;
3091 Stupid wxWindows doesn't draw proper ellipses, so we comment this
3092 out. It's a waste of paper anyway.
3096 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
3097 wxPoint topleft
, wxPoint bottomright
,
3100 // make backups of all essential parameters
3101 const wxBrush
& brush
= dc
.GetBrush();
3102 const wxPen
& pen
= dc
.GetPen();
3103 const wxFont
& font
= dc
.GetFont();
3105 dc
.SetBrush(*wxWHITE_BRUSH
);
3106 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
3107 dc
.DrawRoundedRectangle(topleft
.x
,
3108 topleft
.y
,bottomright
.x
-topleft
.x
,
3109 bottomright
.y
-topleft
.y
);
3110 dc
.SetBrush(*wxBLACK_BRUSH
);
3111 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
3112 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
3116 page
= "9999/9999 "; // many pages...
3118 dc
.GetTextExtent(page
,&w
,&h
);
3119 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
3120 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
3121 dc
.GetTextExtent("XXXX", &w
,&h
);
3122 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);
3133 wxFontCache::GetFont(int family
, int size
, int style
, int weight
,
3136 for(wxFCEList::iterator i
= m_FontList
.begin();
3137 i
!= m_FontList
.end(); i
++)
3138 if( (**i
).Matches(family
, size
, style
, weight
, underline
) )
3139 return (**i
).GetFont();
3141 wxFontCacheEntry
*fce
= new wxFontCacheEntry(family
, size
, style
,
3143 m_FontList
.push_back(fce
);
3144 return fce
->GetFont();