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
)
454 wxLayoutObjectIcon::Draw(wxDC
&dc
, wxPoint
const &coords
,
455 wxLayoutList
*wxllist
,
456 CoordType begin
, CoordType
/* len */)
458 dc
.DrawBitmap(*m_Icon
, coords
.x
, coords
.y
-m_Icon
->GetHeight(),
459 (m_Icon
->GetMask() == NULL
) ? FALSE
: TRUE
);
463 wxLayoutObjectIcon::Layout(wxDC
& /* dc */, class wxLayoutList
* )
468 wxLayoutObjectIcon::GetSize(CoordType
*top
, CoordType
*bottom
) const
470 *top
= m_Icon
->GetHeight();
472 return wxPoint(m_Icon
->GetWidth(), m_Icon
->GetHeight());
477 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
481 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
484 wxLayoutStyleInfo::wxLayoutStyleInfo(int ifamily
,
496 underline
= iul
!= 0;
498 m_fg_valid
= fg
!= 0;
499 m_bg_valid
= bg
!= 0;
500 m_fg
= m_fg_valid
? *fg
: *wxBLACK
;
501 m_bg
= m_bg_valid
? *bg
: *wxWHITE
;
504 #define COPY_SI_(what) if(right.what != -1) what = right.what;
507 wxLayoutStyleInfo::operator=(const wxLayoutStyleInfo
&right
)
514 if(right
.m_fg_valid
) m_fg
= right
.m_fg
;
515 if(right
.m_bg_valid
) m_bg
= right
.m_bg
;
519 wxLayoutObjectCmd::wxLayoutObjectCmd(int family
, int size
, int style
, int
520 weight
, int underline
,
521 wxColour
*fg
, wxColour
*bg
)
524 m_StyleInfo
= new wxLayoutStyleInfo(family
, size
,style
,weight
,underline
,fg
,bg
);
528 wxLayoutObjectCmd::Copy(void)
530 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd(
535 m_StyleInfo
->underline
,
536 m_StyleInfo
->m_fg_valid
?
537 &m_StyleInfo
->m_fg
: NULL
,
538 m_StyleInfo
->m_bg_valid
?
539 &m_StyleInfo
->m_bg
: NULL
);
540 obj
->SetUserData(m_UserData
);
545 wxLayoutObjectCmd::Write(wxString
&ostr
)
547 ostr
<< WXLO_TYPE_CMD
<< '\n'
548 << m_StyleInfo
->size
<< '\n'
549 << m_StyleInfo
->family
<< '\n'
550 << m_StyleInfo
->style
<< '\n'
551 << m_StyleInfo
->weight
<< '\n'
552 << m_StyleInfo
->underline
<< '\n'
553 << m_StyleInfo
->m_fg_valid
<< '\n'
554 << m_StyleInfo
->m_bg_valid
<< '\n';
555 if(m_StyleInfo
->m_fg_valid
)
557 ostr
<< m_StyleInfo
->m_fg
.Red() << '\n'
558 << m_StyleInfo
->m_fg
.Green() << '\n'
559 << m_StyleInfo
->m_fg
.Blue() << '\n';
561 if(m_StyleInfo
->m_bg_valid
)
563 ostr
<< m_StyleInfo
->m_bg
.Red() << '\n'
564 << m_StyleInfo
->m_bg
.Green() << '\n'
565 << m_StyleInfo
->m_bg
.Blue() << '\n';
570 wxLayoutObjectCmd::Read(wxString
&istr
)
572 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd
;
575 ReadString(tmp
, istr
);
576 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->size
);
577 ReadString(tmp
, istr
);
578 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->family
);
579 ReadString(tmp
, istr
);
580 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->style
);
581 ReadString(tmp
, istr
);
582 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->weight
);
583 ReadString(tmp
, istr
);
584 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->underline
);
585 ReadString(tmp
, istr
);
586 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->m_fg_valid
);
587 ReadString(tmp
, istr
);
588 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->m_bg_valid
);
589 if(obj
->m_StyleInfo
->m_fg_valid
)
591 int red
, green
, blue
;
592 ReadString(tmp
, istr
);
593 sscanf(tmp
.c_str(),"%d", &red
);
594 ReadString(tmp
, istr
);
595 sscanf(tmp
.c_str(),"%d", &green
);
596 ReadString(tmp
, istr
);
597 sscanf(tmp
.c_str(),"%d", &blue
);
598 obj
->m_StyleInfo
->m_fg
= wxColour(red
, green
, blue
);
600 if(obj
->m_StyleInfo
->m_bg_valid
)
602 int red
, green
, blue
;
603 ReadString(tmp
, istr
);
604 sscanf(tmp
.c_str(),"%d", &red
);
605 ReadString(tmp
, istr
);
606 sscanf(tmp
.c_str(),"%d", &green
);
607 ReadString(tmp
, istr
);
608 sscanf(tmp
.c_str(),"%d", &blue
);
609 obj
->m_StyleInfo
->m_bg
= wxColour(red
, green
, blue
);
615 wxLayoutObjectCmd::~wxLayoutObjectCmd()
621 wxLayoutObjectCmd::GetStyle(void) const
627 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint
const & /* coords */,
628 wxLayoutList
*wxllist
,
629 CoordType begin
, CoordType
/* len */)
631 wxASSERT(m_StyleInfo
);
632 wxllist
->ApplyStyle(*m_StyleInfo
, dc
);
636 wxLayoutObjectCmd::Layout(wxDC
&dc
, class wxLayoutList
* llist
)
638 // this get called, so that recalculation uses right font sizes
639 Draw(dc
, wxPoint(0,0), llist
);
643 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
645 The wxLayoutLine object
647 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
649 wxLayoutLine::wxLayoutLine(wxLayoutLine
*prev
, wxLayoutList
*llist
)
651 m_Width
= m_Height
= 0;
661 RecalculatePosition(llist
);
666 m_LineNumber
= m_Previous
->GetLineNumber() + 1;
667 m_Next
= m_Previous
->GetNextLine();
668 m_Previous
->m_Next
= this;
673 m_Next
->m_Previous
= this;
677 m_StyleInfo
= llist
->GetDefaultStyleInfo();
679 llist
->IncNumLines();
682 wxLayoutLine::~wxLayoutLine()
684 // kbList cleans itself
688 wxLayoutLine::RecalculatePosition(wxLayoutList
*llist
)
690 wxASSERT(m_Previous
|| GetLineNumber() == 0);
692 wxPoint
posOld(m_Position
);
696 m_Position
= m_Previous
->GetPosition();
697 m_Position
.y
+= m_Previous
->GetHeight();
700 m_Position
= wxPoint(0,0);
702 if ( m_Position
!= posOld
)
704 // the whole line moved and must be repainted
705 llist
->SetUpdateRect(m_Position
);
706 llist
->SetUpdateRect(m_Position
.x
+ GetWidth() + MSW_CORRECTION
,
707 m_Position
.y
+ GetHeight() + MSW_CORRECTION
);
708 llist
->SetUpdateRect(posOld
);
709 llist
->SetUpdateRect(posOld
.x
+ GetWidth() + MSW_CORRECTION
,
710 posOld
.y
+ GetHeight() + MSW_CORRECTION
);
717 wxLayoutObjectList::iterator
718 wxLayoutLine::FindObject(CoordType xpos
, CoordType
*offset
) const
722 wxLayoutObjectList::iterator
725 CoordType x
= 0, len
;
727 /* We search through the objects. As we don't like returning the
728 object that the cursor is behind, we just remember such an
729 object in "found" so we can return it if there is really no
730 further object following it. */
731 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
733 len
= (**i
).GetLength();
734 if( x
<= xpos
&& xpos
<= x
+ len
)
737 if(xpos
== x
+ len
) // is there another object behind?
739 else // we are really inside this object
742 x
+= (**i
).GetLength();
744 return found
; // ==NULL if really none found
747 wxLayoutObjectList::iterator
748 wxLayoutLine::FindObjectScreen(wxDC
&dc
, wxLayoutList
*llist
,
749 CoordType xpos
, CoordType
*cxpos
,
754 llist
->ApplyStyle(GetStyleInfo(), dc
);
756 wxLayoutObjectList::iterator i
;
757 CoordType x
= 0, cx
= 0, width
;
759 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
761 wxLayoutObject
*obj
= *i
;
762 if ( obj
->GetType() == WXLO_TYPE_CMD
)
764 // this will set the correct font for the objects which follow
765 obj
->Layout(dc
, llist
);
768 width
= obj
->GetWidth();
769 if( x
<= xpos
&& xpos
<= x
+ width
)
771 *cxpos
= cx
+ obj
->GetOffsetScreen(dc
, xpos
-x
);
778 x
+= obj
->GetWidth();
779 cx
+= obj
->GetLength();
782 // behind last object:
787 return m_ObjectList
.tail();
790 /** Finds text in this line.
791 @param needle the text to find
792 @param xpos the position where to start the search
793 @return the cursoor coord where it was found or -1
796 wxLayoutLine::FindText(const wxString
&needle
, CoordType xpos
) const
801 wxString
const *text
;
803 for(wxLOiterator i
= m_ObjectList
.begin(); i
!= m_ObjectList
.end(); i
++)
805 if(cpos
>= xpos
) // search from here!
807 if((**i
).GetType() == WXLO_TYPE_TEXT
)
809 text
= & ((wxLayoutObjectText
*)(*i
))->GetText();
810 relpos
= text
->Find(needle
);
811 if(relpos
>= cpos
-xpos
) // -1 if not found
816 cpos
+= (**i
).GetLength();
819 return -1; // not found
823 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
826 wxASSERT(obj
!= NULL
);
831 wxLOiterator i
= FindObject(xpos
, &offset
);
834 if(xpos
== 0 ) // aha, empty line!
836 m_ObjectList
.push_back(obj
);
837 m_Length
+= obj
->GetLength();
844 CoordType len
= (**i
).GetLength();
845 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
846 { // insert before this object
847 m_ObjectList
.insert(i
,obj
);
848 m_Length
+= obj
->GetLength();
853 if( i
== m_ObjectList
.tail()) // last object?
854 m_ObjectList
.push_back(obj
);
856 { // insert after current object
858 m_ObjectList
.insert(i
,obj
);
860 m_Length
+= obj
->GetLength();
863 /* Otherwise we need to split the current object.
864 Fortunately this can only be a text object. */
865 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
866 wxString left
, right
;
867 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
868 left
= tobj
->GetText().substr(0,offset
);
869 right
= tobj
->GetText().substr(offset
,len
-offset
);
870 // current text object gets set to right half
871 tobj
->GetText() = right
; // set new text
872 // before it we insert the new object
873 m_ObjectList
.insert(i
,obj
);
874 m_Length
+= obj
->GetLength();
875 // and before that we insert the left half
876 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
881 wxLayoutLine::Insert(CoordType xpos
, const wxString
& text
)
888 wxLOiterator i
= FindObject(xpos
, &offset
);
889 if(i
!= NULLIT
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
891 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
892 tobj
->GetText().insert(offset
, text
);
893 m_Length
+= text
.Length();
897 if ( !Insert(xpos
, new wxLayoutObjectText(text
)) )
905 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
907 CoordType offset
, len
;
912 wxLOiterator i
= FindObject(xpos
, &offset
);
915 if(i
== NULLIT
) return npos
;
916 // now delete from that object:
917 if((**i
).GetType() != WXLO_TYPE_TEXT
)
919 if(offset
!= 0) // at end of line after a non-text object
922 len
= (**i
).GetLength();
925 m_ObjectList
.erase(i
);
929 // tidy up: remove empty text objects
930 if((**i
).GetLength() == 0)
932 m_ObjectList
.erase(i
);
936 CoordType max
= (**i
).GetLength() - offset
;
937 if(npos
< max
) max
= npos
;
940 if(xpos
== GetLength())
943 { // at the end of an object
944 // move to begin of next object:
946 continue; // start over
951 if(offset
== 0 && max
== (**i
).GetLength())
952 m_ObjectList
.erase(i
); // remove the whole object
954 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
962 wxLayoutLine::DeleteWord(CoordType xpos
)
968 wxLOiterator i
= FindObject(xpos
, &offset
);
972 if(i
== NULLIT
) return false;
973 if((**i
).GetType() != WXLO_TYPE_TEXT
)
975 // This should only happen when at end of line, behind a non-text
977 if(offset
== (**i
).GetLength()) return false;
978 m_Length
-= (**i
).GetLength(); // -1
979 m_ObjectList
.erase(i
);
980 return true; // we are done
984 if(offset
== (**i
).GetLength()) // at end of object
989 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
991 wxString str
= tobj
->GetText();
992 str
= str
.substr(offset
,str
.Length()-offset
);
993 // Find out how many positions we need to delete:
994 // 1. eat leading space
995 while(isspace(str
.c_str()[count
])) count
++;
996 // 2. eat the word itself:
997 while(isalnum(str
.c_str()[count
])) count
++;
999 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
1000 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
1006 wxFAIL_MSG("unreachable");
1010 wxLayoutLine::DeleteLine(bool update
, wxLayoutList
*llist
)
1012 // maintain linked list integrity
1014 m_Next
->m_Previous
= m_Previous
;
1016 m_Previous
->m_Next
= m_Next
;
1018 // get the line numbers right again
1019 if ( update
&& m_Next
)
1024 // we can't use m_Next after "delete this", so we must save this pointer
1026 wxLayoutLine
*next
= m_Next
;
1029 llist
->DecNumLines();
1035 wxLayoutLine::Draw(wxDC
&dc
,
1036 wxLayoutList
*llist
,
1037 const wxPoint
& offset
) const
1039 wxLayoutObjectList::iterator i
;
1040 wxPoint pos
= offset
;
1041 pos
= pos
+ GetPosition();
1043 pos
.y
+= m_BaseLine
;
1045 CoordType xpos
= 0; // cursorpos, lenght of line
1047 CoordType from
, to
, tempto
;
1049 int highlight
= llist
->IsSelected(this, &from
, &to
);
1050 // WXLO_DEBUG(("highlight=%d", highlight ));
1051 if(highlight
== 1) // we need to draw the whole line inverted!
1052 llist
->StartHighlighting(dc
);
1054 llist
->EndHighlighting(dc
);
1056 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
1058 if(highlight
== -1) // partially highlight line
1060 // parts of the line need highlighting
1061 tempto
= xpos
+(**i
).GetLength();
1062 (**i
).Draw(dc
, pos
, llist
, from
-xpos
, to
-xpos
);
1065 (**i
).Draw(dc
, pos
, llist
);
1066 pos
.x
+= (**i
).GetWidth();
1067 xpos
+= (**i
).GetLength();
1072 This function does all the recalculation, that is, it should only be
1073 called from within wxLayoutList::Layout(), as it uses the current
1074 list's styleinfo and updates it.
1077 wxLayoutLine::Layout(wxDC
&dc
,
1078 wxLayoutList
*llist
,
1080 wxPoint
*cursorSize
,
1081 wxLayoutStyleInfo
*cursorStyle
,
1083 bool suppressSIupdate
)
1085 wxLayoutObjectList::iterator i
;
1087 // when a line becomes dirty, we redraw it from the place where it was
1088 // changed till the end of line (because the following wxLayoutObjects are
1089 // moved when the preceding one changes) - calculate the update rectangle.
1090 CoordType updateTop
= m_Position
.y
,
1092 updateWidth
= m_Width
,
1093 updateHeight
= m_Height
;
1097 bottomHeight
= 0; // above and below baseline
1099 objTopHeight
, objBottomHeight
; // above and below baseline
1103 CoordType heightOld
= m_Height
;
1109 bool cursorFound
= false;
1111 RecalculatePosition(llist
);
1115 *cursorPos
= m_Position
;
1116 if(cursorSize
) *cursorSize
= wxPoint(0,0);
1119 m_StyleInfo
= llist
->GetStyleInfo(); // save current style
1120 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
1122 wxLayoutObject
*obj
= *i
;
1123 obj
->Layout(dc
, llist
);
1124 wxPoint sizeObj
= obj
->GetSize(&objTopHeight
, &objBottomHeight
);
1126 if(cursorPos
&& ! cursorFound
)
1128 // we need to check whether the text cursor is here
1129 len
= obj
->GetLength();
1130 if(count
<= cx
&& count
+len
> cx
)
1132 if(obj
->GetType() == WXLO_TYPE_TEXT
)
1134 len
= cx
- count
; // pos in object
1135 CoordType width
, height
, descent
;
1136 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
1137 &width
, &height
, &descent
);
1138 cursorPos
->x
+= width
;
1139 cursorPos
->y
= m_Position
.y
;
1141 if(len
< obj
->GetLength())
1142 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
1144 str
= WXLO_CURSORCHAR
;
1145 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
1147 if(cursorStyle
) // set style info
1148 *cursorStyle
= llist
->GetStyleInfo();
1151 // Just in case some joker inserted an empty string object:
1153 width
= WXLO_MINIMUM_CURSOR_WIDTH
;
1156 cursorSize
->x
= width
;
1157 cursorSize
->y
= height
;
1160 cursorFound
= true; // no more checks
1164 // on some other object
1165 CoordType top
, bottom
; // unused
1167 *cursorSize
= obj
->GetSize(&top
,&bottom
);
1168 cursorPos
->y
= m_Position
.y
;
1169 cursorFound
= true; // no more checks
1175 cursorPos
->x
+= obj
->GetWidth();
1179 m_Width
+= sizeObj
.x
;
1180 if(sizeObj
.y
> m_Height
)
1182 m_Height
= sizeObj
.y
;
1185 if(objTopHeight
> topHeight
)
1186 topHeight
= objTopHeight
;
1187 if(objBottomHeight
> bottomHeight
)
1188 bottomHeight
= objBottomHeight
;
1193 if ( updateHeight
< m_Height
)
1194 updateHeight
= m_Height
;
1195 if ( updateWidth
< m_Width
)
1196 updateWidth
= m_Width
;
1198 // update all line if we don't know where to start from
1199 if ( updateLeft
== -1 )
1202 llist
->SetUpdateRect(updateLeft
, updateTop
);
1203 llist
->SetUpdateRect(updateLeft
+ updateWidth
+ MSW_CORRECTION
,
1204 updateTop
+ updateHeight
+ MSW_CORRECTION
);
1207 if(topHeight
+ bottomHeight
> m_Height
)
1209 m_Height
= topHeight
+bottomHeight
;
1212 m_BaseLine
= topHeight
;
1216 CoordType width
, height
, descent
;
1217 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1219 m_BaseLine
= m_Height
- descent
;
1222 // tell next line about coordinate change
1223 if(m_Next
&& m_Height
!= heightOld
)
1225 m_Next
->MarkDirty();
1228 // We need to check whether we found a valid cursor size:
1229 if(cursorPos
&& cursorSize
)
1231 // this might be the case if the cursor is at the end of the
1232 // line or on a command object:
1233 if(cursorSize
->x
< WXLO_MINIMUM_CURSOR_WIDTH
)
1235 CoordType width
, height
, descent
;
1236 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1237 cursorSize
->x
= width
;
1238 cursorSize
->y
= height
;
1240 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
1241 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
1248 wxLayoutLine::Break(CoordType xpos
, wxLayoutList
*llist
)
1250 wxASSERT(xpos
>= 0);
1255 wxLOiterator i
= FindObject(xpos
, &offset
);
1257 // must be at the end of the line then
1258 return new wxLayoutLine(this, llist
);
1261 wxLayoutLine
*newLine
= new wxLayoutLine(this, llist
);
1262 // split object at i:
1263 if((**i
).GetType() == WXLO_TYPE_TEXT
&& offset
!= 0)
1265 wxString left
, right
;
1266 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
1267 left
= tobj
->GetText().substr(0,offset
);
1268 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
1269 // current text object gets set to left half
1270 tobj
->GetText() = left
; // set new text
1271 newLine
->Append(new wxLayoutObjectText(right
));
1272 m_Length
-= right
.Length();
1273 i
++; // don't move this object to the new list
1278 i
++; // move objects from here to new list
1281 while(i
!= m_ObjectList
.end())
1283 wxLayoutObject
*obj
= *i
;
1284 newLine
->Append(obj
);
1285 m_Length
-= obj
->GetLength();
1287 m_ObjectList
.remove(i
); // remove without deleting it
1290 m_Next
->MarkDirty();
1295 wxLayoutLine::ReNumber(void)
1297 CoordType lineNo
= m_Previous
? m_Previous
->m_LineNumber
+1 : 0;
1298 m_LineNumber
= lineNo
++;
1300 for(wxLayoutLine
*next
= GetNextLine();
1301 next
; next
= next
->GetNextLine())
1302 next
->m_LineNumber
= lineNo
++;
1306 wxLayoutLine::MergeNextLine(wxLayoutList
*llist
)
1308 wxCHECK_RET(GetNextLine(),"wxLayout internal error: no next line to merge");
1309 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
1312 MarkDirty(GetWidth());
1314 wxLayoutObject
*last
= NULL
;
1315 for(i
= list
.begin(); i
!= list
.end();)
1317 wxLayoutObject
*current
= *i
;
1319 // merge text objects together for efficiency
1320 if ( last
&& last
->GetType() == WXLO_TYPE_TEXT
&&
1321 current
->GetType() == WXLO_TYPE_TEXT
)
1323 wxLayoutObjectText
*textObj
= (wxLayoutObjectText
*)last
;
1324 wxString
text(textObj
->GetText());
1325 text
+= ((wxLayoutObjectText
*)current
)->GetText();
1326 textObj
->SetText(text
);
1328 list
.erase(i
); // remove and delete it
1332 // just append the object "as was"
1335 list
.remove(i
); // remove without deleting it
1338 wxASSERT(list
.empty());
1340 wxLayoutLine
*oldnext
= GetNextLine();
1341 wxLayoutLine
*nextLine
= oldnext
->GetNextLine();
1345 nextLine
->ReNumber();
1349 // this is now done in Delete(), but if this function is ever called
1350 // from elsewhere, we might have to move refresh code back here (in
1351 // order not to duplicate it)
1353 wxPoint
pos(oldnext
->GetPosition());
1354 llist
->SetUpdateRect(pos
);
1355 llist
->SetUpdateRect(pos
.x
+ oldnext
->GetWidth() + MSW_CORRECTION
,
1356 pos
.y
+ oldnext
->GetHeight() + MSW_CORRECTION
);
1360 llist
->DecNumLines();
1366 wxLayoutLine::GetWrapPosition(CoordType column
)
1369 wxLOiterator i
= FindObject(column
, &offset
);
1370 if(i
== NULLIT
) return -1; // cannot wrap
1372 // go backwards through the list and look for space in text objects
1375 if((**i
).GetType() == WXLO_TYPE_TEXT
)
1379 if( isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1386 }while(offset
!= -1);
1387 i
--; // move on to previous object
1391 column
-= (**i
).GetLength();
1395 offset
= (**i
).GetLength();
1396 }while(i
!= NULLIT
);
1397 /* If we reached the begin of the list and have more than one
1398 object, that one is longer than the margin, so break behind
1401 i
= m_ObjectList
.begin();
1402 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1404 pos
+= (**i
).GetLength();
1407 if(i
== NULLIT
) return -1; //why should this happen?
1408 pos
+= (**i
).GetLength();
1410 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1412 pos
+= (**i
).GetLength();
1415 if(i
== NULLIT
) return -1; //this is possible, if there is only one text object
1416 // now we are at the second text object:
1417 pos
-= (**i
).GetLength();
1418 return pos
; // in front of it
1422 #ifdef WXLAYOUT_DEBUG
1424 wxLayoutLine::Debug(void)
1427 wxPoint pos
= GetPosition();
1428 WXLO_DEBUG(("Line %ld, Pos (%ld,%ld), Height %ld, BL %ld, Font: %d",
1429 (long int) GetLineNumber(),
1430 (long int) pos
.x
, (long int) pos
.y
,
1431 (long int) GetHeight(),
1432 (long int) m_BaseLine
,
1433 (int) m_StyleInfo
.family
));
1434 if(m_ObjectList
.begin() != NULLIT
)
1435 (**m_ObjectList
.begin()).Debug();
1441 wxLayoutLine::Copy(wxLayoutList
*llist
,
1445 CoordType firstOffset
, lastOffset
;
1447 if(to
== -1) to
= GetLength();
1448 if(from
== to
) return;
1450 wxLOiterator first
= FindObject(from
, &firstOffset
);
1451 wxLOiterator last
= FindObject(to
, &lastOffset
);
1453 // Common special case: only one object
1454 if( first
!= NULLIT
&& last
!= NULLIT
&& *first
== *last
)
1456 if( (**first
).GetType() == WXLO_TYPE_TEXT
)
1458 llist
->Insert(new wxLayoutObjectText(
1459 ((wxLayoutObjectText
1460 *)*first
)->GetText().substr(firstOffset
,
1461 lastOffset
-firstOffset
))
1465 else // what can we do?
1467 if(lastOffset
> firstOffset
) // i.e. +1 :-)
1468 llist
->Insert( (**first
).Copy() );
1473 // If we reach here, we can safely copy the whole first object from
1474 // the firstOffset position on:
1475 if((**first
).GetType() == WXLO_TYPE_TEXT
&& firstOffset
!= 0)
1477 llist
->Insert(new wxLayoutObjectText(
1478 ((wxLayoutObjectText
*)*first
)->GetText().substr(firstOffset
))
1481 else if(firstOffset
== 0)
1482 llist
->Insert( (**first
).Copy() );
1483 // else nothing to copy :-(
1485 // Now we copy all objects before the last one:
1486 wxLOiterator i
= first
; i
++;
1487 for( ; i
!= last
; i
++)
1488 llist
->Insert( (**i
).Copy() );
1490 // And now the last object:
1493 if( (**last
).GetType() == WXLO_TYPE_TEXT
)
1495 llist
->Insert(new wxLayoutObjectText(
1496 ((wxLayoutObjectText
*)*last
)->GetText().substr(0,lastOffset
))
1500 llist
->Insert( (**last
).Copy() );
1505 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1507 The wxLayoutList object
1509 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1511 wxLayoutList::wxLayoutList()
1513 #ifdef WXLAYOUT_USE_CARET
1515 #endif // WXLAYOUT_USE_CARET
1519 SetAutoFormatting(TRUE
);
1520 ForceTotalLayout(TRUE
); // for the first time, do all
1521 InvalidateUpdateRect();
1525 wxLayoutList::~wxLayoutList()
1527 SetAutoFormatting(FALSE
);
1530 m_FirstLine
->DeleteLine(false, this);
1532 wxASSERT_MSG( m_numLines
== 0, "line count calculation broken" );
1536 wxLayoutList::Empty(void)
1539 m_FirstLine
= m_FirstLine
->DeleteLine(false, this);
1541 m_CursorPos
= wxPoint(0,0);
1542 m_CursorScreenPos
= wxPoint(0,0);
1543 m_CursorSize
= wxPoint(0,0);
1544 m_movedCursor
= true;
1545 m_FirstLine
= new wxLayoutLine(NULL
, this); // empty first line
1546 m_CursorLine
= m_FirstLine
;
1547 InvalidateUpdateRect();
1552 wxLayoutList::InternalClear(void)
1554 m_Selection
.m_selecting
= false;
1555 m_Selection
.m_valid
= false;
1557 m_DefaultStyleInfo
.family
= wxSWISS
;
1558 m_DefaultStyleInfo
.size
= WXLO_DEFAULTFONTSIZE
;
1559 m_DefaultStyleInfo
.style
= wxNORMAL
;
1560 m_DefaultStyleInfo
.weight
= wxNORMAL
;
1561 m_DefaultStyleInfo
.underline
= 0;
1562 m_DefaultStyleInfo
.m_fg_valid
= TRUE
;
1563 m_DefaultStyleInfo
.m_fg
= *wxBLACK
;
1564 m_DefaultStyleInfo
.m_bg_valid
= TRUE
;
1565 m_DefaultStyleInfo
.m_bg
= *wxWHITE
;
1567 m_CurrentStyleInfo
= m_DefaultStyleInfo
;
1568 m_CursorStyleInfo
= m_DefaultStyleInfo
;
1572 wxLayoutList::Read(wxString
&istr
)
1574 while(istr
.Length())
1576 wxLayoutObject
*obj
= wxLayoutObject::Read(istr
);
1584 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1585 int underline
, wxColour
*fg
,
1588 if(family
!= -1) m_CurrentStyleInfo
.family
= family
;
1589 if(size
!= -1) m_CurrentStyleInfo
.size
= size
;
1590 if(style
!= -1) m_CurrentStyleInfo
.style
= style
;
1591 if(weight
!= -1) m_CurrentStyleInfo
.weight
= weight
;
1592 if(underline
!= -1) m_CurrentStyleInfo
.underline
= underline
!= 0;
1593 if(fg
) m_CurrentStyleInfo
.m_fg
= *fg
;
1594 if(bg
) m_CurrentStyleInfo
.m_bg
= *bg
;
1596 new wxLayoutObjectCmd(
1597 m_CurrentStyleInfo
.family
,
1598 m_CurrentStyleInfo
.size
,
1599 m_CurrentStyleInfo
.style
,
1600 m_CurrentStyleInfo
.weight
,
1601 m_CurrentStyleInfo
.underline
,
1606 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1607 int underline
, char const *fg
, char const *bg
)
1615 cfg
= wxTheColourDatabase
->FindColour(fg
);
1617 cbg
= wxTheColourDatabase
->FindColour(bg
);
1619 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
1623 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
1624 int underline
, wxColour
*fg
, wxColour
*bg
)
1627 m_DefaultStyleInfo
= wxLayoutStyleInfo(family
, size
, style
, weight
,
1629 m_CurrentStyleInfo
= m_DefaultStyleInfo
;
1631 // Empty() should be called after we set m_DefaultStyleInfo because
1632 // otherwise the style info for the first line (created in Empty()) would be
1638 wxLayoutList::FindText(const wxString
&needle
, const wxPoint
&cpos
) const
1643 for(line
= m_FirstLine
;
1645 line
= line
->GetNextLine())
1647 if(line
->GetLineNumber() >= cpos
.y
)
1649 xpos
= line
->FindText(needle
,
1650 (line
->GetLineNumber() == cpos
.y
) ?
1653 return wxPoint(xpos
, line
->GetLineNumber());
1656 return wxPoint(-1,-1);
1661 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
1663 AddCursorPosToUpdateRect();
1665 wxPoint cursorPosOld
= m_CursorPos
;
1667 wxLayoutLine
*line
= m_FirstLine
;
1668 while(line
&& line
->GetLineNumber() != p
.y
)
1669 line
= line
->GetNextLine();
1670 if(line
&& line
->GetLineNumber() == p
.y
) // found it
1672 m_CursorPos
.y
= p
.y
;
1673 m_CursorLine
= line
;
1674 CoordType len
= line
->GetLength();
1677 m_CursorPos
.x
= p
.x
;
1681 m_CursorPos
.x
= len
;
1685 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1687 return m_CursorPos
== p
;
1691 wxLayoutList::MoveCursorVertically(int n
)
1693 AddCursorPosToUpdateRect();
1695 wxPoint cursorPosOld
= m_CursorPos
;
1698 if(n
< 0) // move up
1700 if(m_CursorLine
== m_FirstLine
) return false;
1701 while(n
< 0 && m_CursorLine
)
1703 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1709 m_CursorLine
= m_FirstLine
;
1715 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1716 m_CursorPos
.x
= m_CursorLine
->GetLength();
1722 wxLayoutLine
*last
= m_CursorLine
;
1723 if(! m_CursorLine
->GetNextLine()) return false;
1724 while(n
> 0 && m_CursorLine
)
1728 m_CursorLine
= m_CursorLine
->GetNextLine();
1732 m_CursorLine
= last
;
1738 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1739 m_CursorPos
.x
= m_CursorLine
->GetLength();
1744 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1750 wxLayoutList::MoveCursorHorizontally(int n
)
1752 AddCursorPosToUpdateRect();
1754 wxPoint cursorPosOld
= m_CursorPos
;
1759 if(m_CursorPos
.x
== 0) // at begin of line
1761 if(! MoveCursorVertically(-1))
1763 MoveCursorToEndOfLine();
1768 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1769 m_CursorPos
.x
-= move
; n
+= move
;
1774 int len
= m_CursorLine
->GetLength();
1775 if(m_CursorPos
.x
== len
) // at end of line
1777 if(! MoveCursorVertically(1))
1779 MoveCursorToBeginOfLine();
1784 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1785 m_CursorPos
.x
+= move
;
1789 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1795 wxLayoutList::MoveCursorWord(int n
, bool untilNext
)
1797 wxCHECK_MSG( m_CursorLine
, false, "no current line" );
1798 wxCHECK_MSG( n
== -1 || n
== +1, false, "not implemented yet" );
1800 CoordType moveDistance
= 0;
1802 wxLayoutLine
*lineCur
= m_CursorLine
;
1803 for ( wxLOiterator i
= lineCur
->FindObject(m_CursorPos
.x
, &offset
);
1811 // moving forward, pass to the first object of the next line
1813 lineCur
= lineCur
->GetNextLine();
1815 i
= lineCur
->GetFirstObject();
1819 // moving backwards, pass to the last object of the prev line
1821 lineCur
= lineCur
->GetPreviousLine();
1823 i
= lineCur
->GetLastObject();
1828 // moved to the end/beginning of text
1835 wxLayoutObject
*obj
= *i
;
1839 // calculate offset: we are either at the very beginning or the very
1840 // end of the object, so it isn't very difficult (the only time when
1841 // offset is != -1 is for the very first iteration when its value is
1842 // returned by FindObject)
1846 offset
= obj
->GetLength();
1849 if( obj
->GetType() != WXLO_TYPE_TEXT
)
1851 // any visible non text objects count as one word
1852 if ( obj
->IsVisibleObject() )
1856 moveDistance
+= obj
->GetLength();
1861 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)obj
;
1863 bool canAdvance
= true;
1865 if ( offset
== tobj
->GetLength() )
1870 // can't move further in this text object
1873 // still should move over the object border
1877 else if ( offset
> 0 )
1879 // offset is off by 1, make it a valid index
1886 const wxString
& text
= tobj
->GetText();
1887 const char *start
= text
.c_str();
1888 const char *end
= start
+ text
.length();
1889 const char *p
= start
+ offset
;
1897 // to the beginning/end of the next/prev word
1898 while ( p
>= start
&& p
< end
&& isspace(*p
) )
1903 // go to the end/beginning of the word (in a broad sense...)
1904 while ( p
>= start
&& p
< end
&& !isspace(*p
) )
1913 // now advance to the beginning of the next word
1914 while ( isspace(*p
) && p
< end
)
1920 // in these 2 cases we took 1 char too much
1921 if ( (p
< start
) || isspace(*p
) )
1927 CoordType moveDelta
= p
- start
- offset
;
1928 if ( (n
< 0) && (offset
== tobj
->GetLength() - 1) )
1930 // because we substracted 1 from offset in this case above, now
1931 // compensate for it
1935 if ( moveDelta
!= 0 )
1937 moveDistance
+= moveDelta
;
1944 // except for the first iteration, offset is calculated in the beginning
1949 MoveCursorHorizontally(moveDistance
);
1955 wxLayoutList::Insert(wxString
const &text
)
1957 wxASSERT(m_CursorLine
);
1958 wxASSERT_MSG( text
.Find('\n') == wxNOT_FOUND
, "use wxLayoutImportText!" );
1963 AddCursorPosToUpdateRect();
1965 if ( !m_CursorLine
->Insert(m_CursorPos
.x
, text
) )
1968 m_CursorPos
.x
+= text
.Length();
1970 m_movedCursor
= true;
1973 m_CursorLine
->MarkDirty();
1979 wxLayoutList::Insert(wxLayoutObject
*obj
)
1981 wxASSERT(m_CursorLine
);
1984 m_CursorLine
= GetFirstLine();
1986 AddCursorPosToUpdateRect();
1988 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
1989 m_CursorPos
.x
+= obj
->GetLength();
1990 m_movedCursor
= true;
1993 m_CursorLine
->MarkDirty();
1999 wxLayoutList::Insert(wxLayoutList
*llist
)
2004 for(wxLayoutLine
*line
= llist
->GetFirstLine();
2006 line
= line
->GetNextLine()
2009 for(wxLOiterator i
= line
->GetFirstObject();
2019 wxLayoutList::LineBreak(void)
2021 wxASSERT(m_CursorLine
);
2023 AddCursorPosToUpdateRect();
2025 wxPoint
position(m_CursorLine
->GetPosition());
2028 width
= m_CursorLine
->GetWidth(),
2029 height
= m_CursorLine
->GetHeight();
2031 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
, this);
2032 if(m_CursorLine
->GetPreviousLine() == NULL
)
2033 m_FirstLine
= m_CursorLine
;
2037 // The following code will produce a height which is guaranteed to
2038 // be too high: old lineheight + the height of both new lines.
2039 // We can probably drop the old line height and start with height =
2041 wxLayoutLine
*prev
= m_CursorLine
->GetPreviousLine();
2043 height
+= prev
->GetHeight();
2044 height
+= m_CursorLine
->GetHeight();
2046 m_movedCursor
= true;
2048 SetUpdateRect(position
);
2049 SetUpdateRect(position
.x
+ width
+ MSW_CORRECTION
,
2050 position
.y
+ height
+ MSW_CORRECTION
);
2056 wxLayoutList::WrapLine(CoordType column
)
2058 if(m_CursorPos
.x
<= column
|| column
< 1)
2059 return false; // do nothing yet
2062 CoordType xpos
= m_CursorLine
->GetWrapPosition(column
);
2064 return false; // cannot break line
2066 CoordType newpos
= m_CursorPos
.x
- xpos
- 1;
2067 m_CursorPos
.x
= xpos
;
2069 AddCursorPosToUpdateRect();
2072 Delete(1); // delete the space
2073 m_CursorPos
.x
= newpos
;
2075 m_CursorLine
->MarkDirty();
2077 m_movedCursor
= true;
2084 wxLayoutList::Delete(CoordType npos
)
2086 wxCHECK_MSG(m_CursorLine
, false, "can't delete in non existing line");
2091 AddCursorPosToUpdateRect();
2093 // were other lines appended to this one (this is important to know because
2094 // this means that our width _increased_ as the result of deletion)
2095 bool wasMerged
= false;
2097 // the size of the region to update
2098 CoordType totalHeight
= m_CursorLine
->GetHeight(),
2099 totalWidth
= m_CursorLine
->GetWidth();
2104 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
2108 // More to delete, continue on next line.
2110 // First, check if line is empty:
2111 if(m_CursorLine
->GetLength() == 0)
2113 // in this case, updating could probably be optimised
2115 wxASSERT(DeleteLines(1) == 0);
2124 // Need to join next line
2125 if(! m_CursorLine
->GetNextLine())
2130 wxLayoutLine
*next
= m_CursorLine
->GetNextLine();
2133 totalHeight
+= next
->GetHeight();
2134 totalWidth
+= next
->GetWidth();
2136 m_CursorLine
->MergeNextLine(this);
2141 wxFAIL_MSG("can't delete all this");
2151 // we need to update the whole tail of the line and the lines which
2155 wxPoint
position(m_CursorLine
->GetPosition());
2156 SetUpdateRect(position
);
2157 SetUpdateRect(position
.x
+ totalWidth
+ MSW_CORRECTION
,
2158 position
.y
+ totalHeight
+ MSW_CORRECTION
);
2165 wxLayoutList::DeleteLines(int n
)
2167 wxASSERT(m_CursorLine
);
2170 AddCursorPosToUpdateRect();
2174 if(!m_CursorLine
->GetNextLine())
2175 { // we cannot delete this line, but we can clear it
2176 MoveCursorToBeginOfLine();
2177 DeleteToEndOfLine();
2179 m_CursorLine
->MarkDirty();
2183 line
= m_CursorLine
;
2184 m_CursorLine
= m_CursorLine
->DeleteLine(true, this);
2186 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
2187 wxASSERT(m_FirstLine
);
2188 wxASSERT(m_CursorLine
);
2191 m_CursorLine
->MarkDirty();
2196 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
)
2200 wxLayoutLine
*line
= m_FirstLine
;
2202 // first, make sure everything is calculated - this might not be
2203 // needed, optimise it later
2204 ApplyStyle(m_DefaultStyleInfo
, dc
);
2207 line
->RecalculatePosition(this); // so we don't need to do it all the time
2208 // little condition to speed up redrawing:
2209 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
2210 line
= line
->GetNextLine();
2215 wxLayoutList::GetCursorScreenPos(void) const
2217 return m_CursorScreenPos
;
2221 Is called before each Draw(). Now, it will re-layout all lines which
2225 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
, bool forceAll
,
2226 wxPoint
*cpos
, wxPoint
*csize
)
2228 // first, make sure everything is calculated - this might not be
2229 // needed, optimise it later
2230 ApplyStyle(m_DefaultStyleInfo
, dc
);
2238 ForceTotalLayout(FALSE
);
2241 // If one line was dirty, we need to re-calculate all
2242 // following lines, too.
2243 bool wasDirty
= forceAll
;
2244 wxLayoutLine
*line
= m_FirstLine
;
2248 ApplyStyle(line
->GetStyleInfo(), dc
);
2250 // if any previous line was dirty, we need to layout all
2253 // layout dirty lines:
2255 // always layout the cursor line toupdate the cursor
2256 // position and size:
2257 || line
== m_CursorLine
2258 // or if it's the line we are asked to look for:
2259 || (cpos
&& line
->GetLineNumber() == cpos
->y
)
2265 // The following Layout() calls will update our
2266 // m_CurrentStyleInfo if needed.
2267 if(line
== m_CursorLine
)
2269 line
->Layout(dc
, this,
2270 (wxPoint
*)&m_CursorScreenPos
,
2271 (wxPoint
*)&m_CursorSize
,
2274 // we cannot layout the line twice, so copy the coords:
2275 if(cpos
&& line
->GetLineNumber() == cpos
->y
)
2277 *cpos
= m_CursorScreenPos
;
2279 *csize
= m_CursorSize
;
2283 if(cpos
&& line
->GetLineNumber() == cpos
->y
)
2284 line
->Layout(dc
, this,
2286 csize
, NULL
, cpos
->x
);
2288 line
->Layout(dc
, this);
2289 // little condition to speed up redrawing:
2290 if(bottom
!= -1 && line
->GetPosition().y
> bottom
)
2293 line
= line
->GetNextLine();
2296 #ifndef WXLAYOUT_USE_CARET
2297 // can only be 0 if we are on the first line and have no next line
2298 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
2299 m_CursorLine
->GetNextLine() == NULL
&&
2300 m_CursorLine
== m_FirstLine
));
2301 #endif // WXLAYOUT_USE_CARET
2302 AddCursorPosToUpdateRect();
2306 wxLayoutList::GetScreenPos(wxDC
&dc
, const wxPoint
&cpos
, wxPoint
*csize
)
2309 Layout(dc
, -1, false, &pos
, csize
);
2314 wxLayoutList::Draw(wxDC
&dc
,
2315 wxPoint
const &offset
,
2320 wxLayoutLine
*line
= m_FirstLine
;
2322 if ( m_Selection
.m_discarded
)
2324 // calculate them if we don't have them already
2325 if ( !m_Selection
.HasValidScreenCoords() )
2327 m_Selection
.m_ScreenA
= GetScreenPos(dc
, m_Selection
.m_CursorA
);
2328 m_Selection
.m_ScreenB
= GetScreenPos(dc
, m_Selection
.m_CursorB
);
2331 // invalidate the area which was previousle selected - and which is not
2332 // selected any more
2333 SetUpdateRect(m_Selection
.m_ScreenA
);
2334 SetUpdateRect(m_Selection
.m_ScreenB
);
2336 m_Selection
.m_discarded
= false;
2339 /* This call to Layout() will re-calculate and update all lines
2344 ApplyStyle(m_DefaultStyleInfo
, dc
);
2345 wxBrush
brush(m_CurrentStyleInfo
.m_bg
, wxSOLID
);
2347 dc
.SetBackgroundMode(wxTRANSPARENT
);
2351 // only draw if between top and bottom:
2353 line
->GetPosition().y
+ line
->GetHeight() > top
))
2355 ApplyStyle(line
->GetStyleInfo(), dc
);
2356 // little condition to speed up redrawing:
2358 && line
->GetPosition().y
2359 +(clipStrictly
? line
->GetHeight() : 0) >= bottom
)
2361 line
->Draw(dc
, this, offset
);
2363 line
= line
->GetNextLine();
2365 InvalidateUpdateRect();
2367 WXLO_DEBUG(("Selection is %s : l%d,%ld/%ld,%ld",
2368 m_Selection
.m_valid
? "valid" : "invalid",
2369 m_Selection
.m_CursorA
.x
, m_Selection
.m_CursorA
.y
,
2370 m_Selection
.m_CursorB
.x
, m_Selection
.m_CursorB
.y
));
2374 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
,
2378 // First, find the right line:
2380 *line
= m_FirstLine
,
2381 *lastline
= m_FirstLine
;
2384 ApplyStyle(m_DefaultStyleInfo
, dc
);
2387 p
= line
->GetPosition();
2388 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
2391 line
= line
->GetNextLine();
2394 bool didFind
= line
!= NULL
;
2398 // use the last line:
2403 cursorPos
->y
= line
->GetLineNumber();
2405 bool foundinline
= true;
2408 // Now, find the object in the line:
2413 i
= line
->FindObjectScreen(dc
, this,
2420 i
= line
->FindObjectScreen(dc
, this,
2425 *found
= didFind
&& foundinline
;
2427 return (i
== NULLIT
) ? NULL
: *i
;
2432 wxLayoutList::GetSize(void) const
2435 *line
= m_FirstLine
,
2438 return wxPoint(0,0);
2440 wxPoint
maxPoint(0,0);
2445 if(line
->GetWidth() > maxPoint
.x
)
2446 maxPoint
.x
= line
->GetWidth();
2448 line
= line
->GetNextLine();
2451 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
2453 // if the line was just added, its height would be 0 and we can't call
2454 // Layout() from here because we don't have a dc and we might be not drawing
2455 // at all, besides... So take the cursor height by default (taking 0 is bad
2456 // because then the scrollbars won't be resized and the new line won't be
2458 if ( last
->IsDirty() )
2460 if ( last
->GetHeight() == 0 )
2461 maxPoint
.y
+= m_CursorSize
.y
;
2462 if ( last
->GetWidth() == 0 && maxPoint
.x
< m_CursorSize
.x
)
2463 maxPoint
.x
= m_CursorSize
.x
;
2471 wxLayoutList::DrawCursor(wxDC
&dc
, bool active
, wxPoint
const &translate
)
2473 if ( m_movedCursor
)
2474 m_movedCursor
= false;
2476 wxPoint
coords(m_CursorScreenPos
);
2477 coords
+= translate
;
2479 #ifdef WXLAYOUT_DEBUG
2480 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
2481 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
2482 (long)coords
.x
, (long)coords
.y
,
2483 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
2484 (long)m_CursorLine
->GetLineNumber(),
2485 (long)m_CursorLine
->GetLength()));
2487 wxLogStatus("Cursor is at (%d, %d)", m_CursorPos
.x
, m_CursorPos
.y
);
2490 #ifdef WXLAYOUT_USE_CARET
2491 m_caret
->Move(coords
);
2492 #else // !WXLAYOUT_USE_CARET
2494 wxASSERT(m_CursorSize
.x
>= WXLO_MINIMUM_CURSOR_WIDTH
);
2495 dc
.SetBrush(*wxWHITE_BRUSH
);
2496 //FIXME: wxGTK XOR is borken at the moment!!!dc.SetLogicalFunction(wxXOR);
2497 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
2500 dc
.SetLogicalFunction(wxXOR
);
2501 dc
.DrawRectangle(coords
.x
, coords
.y
,
2502 m_CursorSize
.x
, m_CursorSize
.y
);
2503 SetUpdateRect(coords
.x
, coords
.y
);
2504 SetUpdateRect(coords
.x
+m_CursorSize
.x
, coords
.y
+m_CursorSize
.y
);
2508 dc
.SetLogicalFunction(wxCOPY
);
2509 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
2510 coords
.x
, coords
.y
);
2511 SetUpdateRect(coords
.x
, coords
.y
+m_CursorSize
.y
-1);
2512 SetUpdateRect(coords
.x
, coords
.y
);
2514 dc
.SetLogicalFunction(wxCOPY
);
2515 //dc.SetBrush(wxNullBrush);
2516 #endif // WXLAYOUT_USE_CARET/!WXLAYOUT_USE_CARET
2520 wxLayoutList::SetUpdateRect(CoordType x
, CoordType y
)
2522 if(m_UpdateRectValid
)
2523 GrowRect(m_UpdateRect
, x
, y
);
2528 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
2529 m_UpdateRect
.height
= 4;// wxGTK :-)
2530 m_UpdateRectValid
= true;
2535 wxLayoutList::StartSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2537 wxPoint
cpos(cposOrig
);
2540 WXLO_DEBUG(("Starting selection at %ld/%ld", cpos
.x
, cpos
.y
));
2541 m_Selection
.m_CursorA
= cpos
;
2542 m_Selection
.m_CursorB
= cpos
;
2543 m_Selection
.m_ScreenA
= spos
;
2544 m_Selection
.m_ScreenB
= spos
;
2545 m_Selection
.m_selecting
= true;
2546 m_Selection
.m_valid
= false;
2550 wxLayoutList::ContinueSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2552 wxPoint
cpos(cposOrig
);
2556 wxASSERT(m_Selection
.m_selecting
== true);
2557 wxASSERT(m_Selection
.m_valid
== false);
2558 WXLO_DEBUG(("Continuing selection at %ld/%ld", cpos
.x
, cpos
.y
));
2560 m_Selection
.m_ScreenB
= spos
;
2561 m_Selection
.m_CursorB
= cpos
;
2565 wxLayoutList::EndSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2567 wxPoint
cpos(cposOrig
);
2568 if(cpos
.x
== -1) cpos
= m_CursorPos
;
2569 ContinueSelection(cpos
, spos
);
2570 WXLO_DEBUG(("Ending selection at %ld/%ld", cpos
.x
, cpos
.y
));
2571 // we always want m_CursorA <= m_CursorB!
2572 if( m_Selection
.m_CursorA
> m_Selection
.m_CursorB
)
2574 // exchange the start/end points
2575 wxPoint help
= m_Selection
.m_CursorB
;
2576 m_Selection
.m_CursorB
= m_Selection
.m_CursorA
;
2577 m_Selection
.m_CursorA
= help
;
2579 help
= m_Selection
.m_ScreenB
;
2580 m_Selection
.m_ScreenB
= m_Selection
.m_ScreenA
;
2581 m_Selection
.m_ScreenA
= help
;
2583 m_Selection
.m_selecting
= false;
2584 m_Selection
.m_valid
= true;
2585 /// In case we just clicked somewhere, the selection will have zero
2586 /// size, so we discard it immediately.
2587 if(m_Selection
.m_CursorA
== m_Selection
.m_CursorB
)
2592 wxLayoutList::DiscardSelection()
2594 if ( !HasSelection() )
2597 m_Selection
.m_valid
=
2598 m_Selection
.m_selecting
= false;
2599 m_Selection
.m_discarded
= true;
2603 wxLayoutList::IsSelecting(void) const
2605 return m_Selection
.m_selecting
;
2609 wxLayoutList::IsSelected(const wxPoint
&cursor
) const
2611 if ( !HasSelection() )
2615 (m_Selection
.m_CursorA
<= cursor
2616 && cursor
<= m_Selection
.m_CursorB
)
2617 || (m_Selection
.m_CursorB
<= cursor
2618 && cursor
<= m_Selection
.m_CursorA
)
2623 /** Tests whether this layout line is selected and needs
2625 @param line to test for
2626 @return 0 = not selected, 1 = fully selected, -1 = partially
2630 wxLayoutList::IsSelected(const wxLayoutLine
*line
, CoordType
*from
,
2633 wxASSERT(line
); wxASSERT(to
); wxASSERT(from
);
2635 if(! m_Selection
.m_valid
&& ! m_Selection
.m_selecting
)
2638 CoordType y
= line
->GetLineNumber();
2640 (m_Selection
.m_CursorA
.y
< y
&& m_Selection
.m_CursorB
.y
> y
)
2641 || (m_Selection
.m_CursorB
.y
< y
&& m_Selection
.m_CursorA
.y
> y
)
2644 else if(m_Selection
.m_CursorA
.y
== y
)
2646 *from
= m_Selection
.m_CursorA
.x
;
2647 if(m_Selection
.m_CursorB
.y
== y
)
2648 *to
= m_Selection
.m_CursorB
.x
;
2651 if(m_Selection
.m_CursorB
> m_Selection
.m_CursorA
)
2652 *to
= line
->GetLength();
2658 CoordType help
= *to
;
2664 else if(m_Selection
.m_CursorB
.y
== y
)
2666 *to
= m_Selection
.m_CursorB
.x
;
2667 if(m_Selection
.m_CursorA
.y
== y
)
2668 *from
= m_Selection
.m_CursorA
.x
;
2671 if(m_Selection
.m_CursorB
> m_Selection
.m_CursorA
)
2674 *from
= line
->GetLength();
2678 CoordType help
= *to
;
2689 wxLayoutList::DeleteSelection(void)
2691 if(! m_Selection
.m_valid
)
2694 m_Selection
.m_valid
= false;
2696 // Only delete part of the current line?
2697 if(m_Selection
.m_CursorA
.y
== m_Selection
.m_CursorB
.y
)
2699 MoveCursorTo(m_Selection
.m_CursorA
);
2700 Delete(m_Selection
.m_CursorB
.x
- m_Selection
.m_CursorA
.x
);
2704 // We now know that the two lines are different:
2707 * firstLine
= GetLine(m_Selection
.m_CursorA
.y
),
2708 * lastLine
= GetLine(m_Selection
.m_CursorB
.y
);
2709 // be a bit paranoid:
2710 if(! firstLine
|| ! lastLine
)
2713 // First, delete what's left of this line:
2714 MoveCursorTo(m_Selection
.m_CursorA
);
2715 DeleteToEndOfLine();
2717 wxLayoutLine
*prevLine
= firstLine
->GetPreviousLine(),
2718 *nextLine
= firstLine
->GetNextLine();
2719 while(nextLine
&& nextLine
!= lastLine
)
2720 nextLine
= nextLine
->DeleteLine(false, this);
2722 // Now nextLine = lastLine;
2723 Delete(1); // This joins firstLine and nextLine
2724 Delete(m_Selection
.m_CursorB
.x
); // This deletes the first x positions
2726 // Recalculate the line positions and numbers but notice that firstLine
2727 // might not exist any more - it could be deleted by Delete(1) above
2728 wxLayoutLine
*firstLine2
= prevLine
? prevLine
->GetNextLine() : m_FirstLine
;
2729 firstLine2
->MarkDirty();
2732 /// Starts highlighting the selection
2734 wxLayoutList::StartHighlighting(wxDC
&dc
)
2737 dc
.SetTextForeground(m_CurrentStyleInfo
.m_bg
);
2738 dc
.SetTextBackground(m_CurrentStyleInfo
.m_fg
);
2739 dc
.SetBackgroundMode(wxSOLID
);
2743 /// Ends highlighting the selection
2745 wxLayoutList::EndHighlighting(wxDC
&dc
)
2748 dc
.SetTextForeground(m_CurrentStyleInfo
.m_fg
);
2749 dc
.SetTextBackground(m_CurrentStyleInfo
.m_bg
);
2750 dc
.SetBackgroundMode(wxTRANSPARENT
);
2756 wxLayoutList::GetLine(CoordType index
) const
2758 wxASSERT_MSG( (0 <= index
) && (index
< (CoordType
)m_numLines
),
2762 CoordType n
= index
;
2764 CoordType lineNo
= 0;
2767 for ( line
= m_FirstLine
; line
&& n
-- > 0; line
=
2768 line
->GetNextLine() )
2771 wxASSERT(line
->GetLineNumber() == lineNo
);
2778 // should be the right one
2779 wxASSERT( line
->GetLineNumber() == index
);
2787 wxLayoutList::Copy(const wxPoint
&from
,
2794 for(firstLine
= m_FirstLine
;
2795 firstLine
&& firstLine
->GetLineNumber() < from
.y
;
2796 firstLine
=firstLine
->GetNextLine())
2798 if(!firstLine
|| firstLine
->GetLineNumber() != from
.y
)
2801 for(lastLine
= m_FirstLine
;
2802 lastLine
&& lastLine
->GetLineNumber() < to
.y
;
2803 lastLine
=lastLine
->GetNextLine())
2805 if(!lastLine
|| lastLine
->GetLineNumber() != to
.y
)
2810 wxLayoutLine
*tmp
= firstLine
;
2811 firstLine
= lastLine
;
2815 wxLayoutList
*llist
= new wxLayoutList();
2817 if(firstLine
== lastLine
)
2819 firstLine
->Copy(llist
, from
.x
, to
.x
);
2823 // Extract objects from first line
2824 firstLine
->Copy(llist
, from
.x
);
2826 // Extract all lines between
2827 for(wxLayoutLine
*line
= firstLine
->GetNextLine();
2829 line
= line
->GetNextLine())
2834 // Extract objects from last line
2835 lastLine
->Copy(llist
, 0, to
.x
);
2841 wxLayoutList::GetSelection(wxLayoutDataObject
*wxlo
, bool invalidate
)
2843 if(! m_Selection
.m_valid
)
2845 if(m_Selection
.m_selecting
)
2851 if(invalidate
) m_Selection
.m_valid
= false;
2853 wxLayoutList
*llist
= Copy( m_Selection
.m_CursorA
,
2854 m_Selection
.m_CursorB
);
2856 if(llist
&& wxlo
) // export as data object, too
2860 wxLayoutExportObject
*exp
;
2861 wxLayoutExportStatus
status(llist
);
2862 while((exp
= wxLayoutExport( &status
, WXLO_EXPORT_AS_OBJECTS
)) != NULL
)
2864 if(exp
->type
== WXLO_EXPORT_EMPTYLINE
)
2865 ; //FIXME missing support for linebreaks in string format
2867 exp
->content
.object
->Write(string
);
2870 wxlo
->SetLayoutData(string
);
2877 #define COPY_SI(what) if(si.what != -1) { m_CurrentStyleInfo.what = si.what; fontChanged = TRUE; }
2880 wxLayoutList::ApplyStyle(wxLayoutStyleInfo
const &si
, wxDC
&dc
)
2882 bool fontChanged
= FALSE
;
2889 dc
.SetFont( m_FontCache
.GetFont(m_CurrentStyleInfo
) );
2893 m_CurrentStyleInfo
.m_fg
= si
.m_fg
;
2894 dc
.SetTextForeground(m_CurrentStyleInfo
.m_fg
);
2898 m_CurrentStyleInfo
.m_bg
= si
.m_bg
;
2899 dc
.SetTextBackground(m_CurrentStyleInfo
.m_bg
);
2904 #ifdef WXLAYOUT_DEBUG
2907 wxLayoutList::Debug(void)
2909 WXLO_DEBUG(("Cursor is in line %d, screen pos = (%d, %d)",
2910 m_CursorLine
->GetLineNumber(),
2911 m_CursorScreenPos
.x
, m_CursorScreenPos
.y
));
2914 for(line
= m_FirstLine
; line
; line
= line
->GetNextLine())
2923 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2927 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2929 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
2930 wxString
const & title
)
2935 // remove any highlighting which could interfere with printing:
2936 m_llist
->StartSelection();
2937 m_llist
->EndSelection();
2938 // force a full layout of the list:
2939 m_llist
->ForceTotalLayout();
2940 // layout is called in ScaleDC() when we have a DC
2943 wxLayoutPrintout::~wxLayoutPrintout()
2948 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
2950 // The following bit is taken from the printing sample, let's see
2951 // whether it works for us.
2953 /* You might use THIS code to set the printer DC to ROUGHLY reflect
2954 * the screen text size. This page also draws lines of actual length 5cm
2957 // Get the logical pixels per inch of screen and printer
2958 int ppiScreenX
, ppiScreenY
;
2959 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
2960 int ppiPrinterX
, ppiPrinterY
;
2961 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
2963 if(ppiScreenX
== 0) // not yet set, need to guess
2968 if(ppiPrinterX
== 0) // not yet set, need to guess
2974 // This scales the DC so that the printout roughly represents the
2975 // the screen scaling. The text point size _should_ be the right size
2976 // but in fact is too small for some reason. This is a detail that will
2977 // need to be addressed at some point but can be fudged for the
2979 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
2981 // Now we have to check in case our real page size is reduced
2982 // (e.g. because we're drawing to a print preview memory DC)
2983 int pageWidth
, pageHeight
;
2985 dc
->GetSize(&w
, &h
);
2986 GetPageSizePixels(&pageWidth
, &pageHeight
);
2987 if(pageWidth
!= 0) // doesn't work always
2989 // If printer pageWidth == current DC width, then this doesn't
2990 // change. But w might be the preview bitmap width, so scale down.
2991 scale
= scale
* (float)(w
/(float)pageWidth
);
2993 dc
->SetUserScale(scale
, scale
);
2997 bool wxLayoutPrintout::OnPrintPage(int page
)
3006 top
= (page
- 1)*m_PrintoutHeight
;
3007 bottom
= top
+ m_PrintoutHeight
;
3009 WXLO_DEBUG(("OnPrintPage(%d) printing from %d to %d", page
, top
,
3011 // SetDeviceOrigin() doesn't work here, so we need to manually
3012 // translate all coordinates.
3013 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
3014 m_llist
->Draw(*dc
, translate
, top
, bottom
, TRUE
/* clip strictly
3022 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
3024 /* We allocate a temporary wxDC for printing, so that we can
3025 determine the correct paper size and scaling. We don't actually
3026 print anything on it. */
3028 wxPrinterDC
*psdc
= new wxPrinterDC("","",WXLLIST_TEMPFILE
,false);
3030 wxPostScriptDC
*psdc
= new wxPostScriptDC(WXLLIST_TEMPFILE
,false);
3033 psdc
->StartDoc(m_title
);
3034 // before we draw anything, me must make sure the list is properly
3036 m_llist
->Layout(*psdc
);
3038 float scale
= ScaleDC(psdc
);
3040 psdc
->GetSize(&m_PageWidth
, &m_PageHeight
);
3042 // This sets a left/top origin of 15% and 5%:
3043 m_Offset
= wxPoint((15*m_PageWidth
)/100, (5*m_PageHeight
)/100);
3045 // This is the length of the printable area.
3046 m_PrintoutHeight
= m_PageHeight
- 2*m_Offset
.y
;
3047 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
3050 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
3053 *maxPage
= m_NumOfPages
;
3056 *selPageTo
= m_NumOfPages
;
3059 wxRemoveFile(WXLLIST_TEMPFILE
);
3062 bool wxLayoutPrintout::HasPage(int pageNum
)
3064 return pageNum
<= m_NumOfPages
;
3068 Stupid wxWindows doesn't draw proper ellipses, so we comment this
3069 out. It's a waste of paper anyway.
3073 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
3074 wxPoint topleft
, wxPoint bottomright
,
3077 // make backups of all essential parameters
3078 const wxBrush
& brush
= dc
.GetBrush();
3079 const wxPen
& pen
= dc
.GetPen();
3080 const wxFont
& font
= dc
.GetFont();
3082 dc
.SetBrush(*wxWHITE_BRUSH
);
3083 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
3084 dc
.DrawRoundedRectangle(topleft
.x
,
3085 topleft
.y
,bottomright
.x
-topleft
.x
,
3086 bottomright
.y
-topleft
.y
);
3087 dc
.SetBrush(*wxBLACK_BRUSH
);
3088 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
3089 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
3093 page
= "9999/9999 "; // many pages...
3095 dc
.GetTextExtent(page
,&w
,&h
);
3096 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
3097 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
3098 dc
.GetTextExtent("XXXX", &w
,&h
);
3099 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);
3110 wxFontCache::GetFont(int family
, int size
, int style
, int weight
,
3113 for(wxFCEList::iterator i
= m_FontList
.begin();
3114 i
!= m_FontList
.end(); i
++)
3115 if( (**i
).Matches(family
, size
, style
, weight
, underline
) )
3116 return (**i
).GetFont();
3118 wxFontCacheEntry
*fce
= new wxFontCacheEntry(family
, size
, style
,
3120 m_FontList
.push_back(fce
);
3121 return fce
->GetFont();