1 /*-*- c++ -*-********************************************************
2 * wxllist: wxLayoutList, a layout engine for text and graphics *
4 * (C) 1998-2000 by Karsten Ballüder (Ballueder@gmx.net) *
7 *******************************************************************/
13 Layout() recalculates the objects, sizes, etc.
14 Draw() just draws them with the current settings, without
15 re-layout()ing them again
17 Each line has its own wxLayoutStyleInfo structure which gets updated
18 from within Layout(). Thanks to this, we don't need to re-layout all
19 lines if we want to draw one, but can just use its styleinfo to set
25 # pragma implementation "wxllist.h"
28 #include "wx/wxprec.h"
38 # include "gui/wxllist.h"
39 # include "gui/wxlparser.h"
40 # define SHOW_SELECTIONS 1
43 # include "wxlparser.h"
44 # define SHOW_SELECTIONS 1
56 # include <wx/print.h>
58 # include <wx/filefn.h>
61 #ifdef WXLAYOUT_USE_CARET
62 # include <wx/caret.h>
63 #endif // WXLAYOUT_USE_CARET
68 /// This should never really get created
69 #define WXLLIST_TEMPFILE "__wxllist.tmp"
73 # define TypeString(t) g_aTypeStrings[t]
74 # define WXLO_DEBUG(x) wxLogDebug x
76 static const char *g_aTypeStrings
[] =
78 "invalid", "text", "cmd", "icon"
81 wxLayoutObject::DebugDump(void) const
84 str
.Printf("%s",g_aTypeStrings
[GetType()]);
88 # define TypeString(t) ""
89 # define WXLO_DEBUG(x)
93 // FIXME under MSW, this constant is needed to make the thing properly redraw
94 // itself - I don't know where the size calculation error is and I can't
95 // waste time looking for it right now. Search for occurences of
96 // MSW_CORRECTION to find all the places where I did it.
98 static const int MSW_CORRECTION
= 10;
100 static const int MSW_CORRECTION
= 0;
103 /// Cursors smaller than this disappear in XOR drawing mode
104 #define WXLO_MINIMUM_CURSOR_WIDTH 4
106 /// Use this character to estimate a cursor size when none is available.
107 #define WXLO_CURSORCHAR "E"
108 /** @name Helper functions */
110 /// allows me to compare to wxPoints
111 bool operator <=(wxPoint
const &p1
, wxPoint
const &p2
)
113 return p1
.y
< p2
.y
|| (p1
.y
== p2
.y
&& p1
.x
<= p2
.x
);
117 The following STAY HERE until we have a working wxGTK again!!!
119 #ifndef wxWANTS_CHARS
120 /// allows me to compare to wxPoints
121 bool operator ==(wxPoint
const &p1
, wxPoint
const &p2
)
123 return p1
.x
== p2
.x
&& p1
.y
== p2
.y
;
126 /// allows me to compare to wxPoints
127 bool operator !=(wxPoint
const &p1
, wxPoint
const &p2
)
129 return p1
.x
!= p2
.x
|| p1
.y
!= p2
.y
;
132 wxPoint
& operator += (wxPoint
&p1
, wxPoint
const &p2
)
140 /// allows me to compare to wxPoints
141 bool operator>(wxPoint
const &p1
, wxPoint
const &p2
)
146 /// grows a wxRect so that it includes the given point
149 void GrowRect(wxRect
&r
, CoordType x
, CoordType y
)
153 else if(r
.x
+ r
.width
< x
)
158 else if(r
.y
+ r
.height
< y
)
164 /// returns true if the point is in the rectangle
166 bool Contains(const wxRect
&r
, const wxPoint
&p
)
168 return r
.x
<= p
.x
&& r
.y
<= p
.y
&& (r
.x
+r
.width
) >= p
.x
&& (r
.y
+ r
.height
) >= p
.y
;
177 void ReadString(wxString
&to
, wxString
&from
)
180 const char *cptr
= from
.c_str();
181 while(*cptr
&& *cptr
!= '\n')
187 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
191 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
195 wxLayoutObject::Read(wxString
&istr
)
198 ReadString(tmp
, istr
);
199 int type
= WXLO_TYPE_INVALID
;
200 sscanf(tmp
.c_str(),"%d", &type
);
205 return wxLayoutObjectText::Read(istr
);
207 return wxLayoutObjectCmd::Read(istr
);
209 return wxLayoutObjectIcon::Read(istr
);
215 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
219 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
221 wxLayoutObjectText::wxLayoutObjectText(const wxString
&txt
)
231 wxLayoutObjectText::Copy(void)
233 wxLayoutObjectText
*obj
= new wxLayoutObjectText(m_Text
);
234 obj
->m_Width
= m_Width
;
235 obj
->m_Height
= m_Height
;
237 obj
->m_Bottom
= m_Bottom
;
238 obj
->SetUserData(m_UserData
);
244 wxLayoutObjectText::Write(wxString
&ostr
)
246 ostr
<< (int) WXLO_TYPE_TEXT
<< '\n'
251 wxLayoutObjectText::Read(wxString
&istr
)
254 ReadString(text
, istr
);
256 return new wxLayoutObjectText(text
);
260 wxLayoutObjectText::GetSize(CoordType
*top
, CoordType
*bottom
) const
263 *top
= m_Top
; *bottom
= m_Bottom
;
264 return wxPoint(m_Width
, m_Height
);
268 wxLayoutObjectText::Draw(wxDC
&dc
, wxPoint
const &coords
,
269 wxLayoutList
*wxllist
,
270 CoordType begin
, CoordType end
)
274 // draw the whole object normally
275 dc
.DrawText(m_Text
, coords
.x
, coords
.y
-m_Top
);
279 // highlight the bit between begin and len
282 ypos
= coords
.y
-m_Top
;
283 long width
, height
, descent
;
285 if(begin
< 0) begin
= 0;
286 if( end
> (signed)m_Text
.Length() )
287 end
= m_Text
.Length();
289 wxString str
= m_Text
.Mid(0, begin
);
290 dc
.DrawText(str
, xpos
, ypos
);
291 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
293 wxllist
->StartHighlighting(dc
);
294 str
= m_Text
.Mid(begin
, end
-begin
);
295 dc
.DrawText(str
, xpos
, ypos
);
296 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
298 wxllist
->EndHighlighting(dc
);
299 str
= m_Text
.Mid(end
, m_Text
.Length()-end
);
300 dc
.DrawText(str
, xpos
, ypos
);
305 wxLayoutObjectText::GetOffsetScreen(wxDC
&dc
, CoordType xpos
) const
309 maxlen
= m_Text
.Length();
312 height
, descent
= 0l;
314 if(xpos
== 0) return 0; // easy
316 while(width
< xpos
&& offs
< maxlen
)
318 dc
.GetTextExtent(m_Text
.substr(0,offs
),
319 &width
, &height
, &descent
);
322 /* We have to substract 1 to compensate for the offs++, and another
323 one because we don't want to position the cursor behind the
324 object what we clicked on, but before - otherwise it looks
326 return (xpos
> 2) ? offs
-2 : 0;
330 wxLayoutObjectText::Layout(wxDC
&dc
, class wxLayoutList
*llist
)
334 // now this is done in wxLayoutLine::Layout(), but this code might be
335 // reenabled later - in principle, it's more efficient
337 CoordType widthOld
= m_Width
,
338 heightOld
= m_Height
;
342 CoordType a
,b
,c
,d
,e
,f
;
343 dc
.GetTextExtent("test ", &a
, &b
, &c
);
344 dc
.GetTextExtent("test", &d
, &e
, &f
);
348 dc
.GetTextExtent(" ", &d
, &e
, &f
);
351 dc
.GetTextExtent(m_Text
, &m_Width
, &m_Height
, &descent
);
354 if ( widthOld
!= m_Width
|| heightOld
!= m_Height
)
356 // as the text length changed, it must be refreshed
357 wxLayoutLine
*line
= GetLine();
359 wxCHECK_RET( line
, "wxLayoutObjectText can't refresh itself" );
361 // as our size changed, we need to repaint the part which was appended
362 wxPoint
position(line
->GetPosition());
364 // this is not the most efficient way (we repaint the whole line), but
365 // it's not too slow and is *simple*
366 if ( widthOld
< m_Width
)
368 if ( heightOld
< m_Height
)
369 heightOld
= m_Height
;
371 llist
->SetUpdateRect(position
.x
+ widthOld
+ MSW_CORRECTION
,
372 position
.y
+ heightOld
+ MSW_CORRECTION
);
377 m_Top
= m_Height
- m_Bottom
;
381 #ifdef WXLAYOUT_DEBUG
383 wxLayoutObjectText::DebugDump(void) const
386 str
= wxLayoutObject::DebugDump();
388 str2
.Printf(" `%s`", m_Text
.c_str());
393 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
397 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
399 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
const &icon
)
403 wxFAIL_MSG("invalid icon");
411 // FIXME ugly, ugly, ugly - but the only way to avoid slicing
412 m_Icon
= icon
.GetHBITMAP() ? new wxBitmap(icon
)
413 : new wxBitmap(wxBitmap((const wxBitmap
&)icon
));
415 m_Icon
= new wxBitmap(icon
);
421 wxLayoutObjectIcon::Write(wxString
&ostr
)
423 /* Exports icon through a temporary file. */
425 wxString file
= wxGetTempFileName("wxloexport");
427 ostr
<< (int) WXLO_TYPE_ICON
<< '\n'
429 m_Icon
->SaveFile(file
, WXLO_BITMAP_FORMAT
);
433 wxLayoutObjectIcon::Read(wxString
&istr
)
436 ReadString(file
, istr
);
438 if(! wxFileExists(file
))
440 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon
;
442 if(!obj
->m_Icon
->LoadFile(file
, WXLO_BITMAP_FORMAT
))
452 wxLayoutObjectIcon::Copy(void)
454 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon(new
456 obj
->SetUserData(m_UserData
);
460 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
*icon
)
464 m_Icon
= new wxBitmap
;
468 wxLayoutObjectIcon::Draw(wxDC
&dc
, wxPoint
const &coords
,
469 wxLayoutList
*wxllist
,
470 CoordType begin
, CoordType
/* len */)
472 dc
.DrawBitmap(*m_Icon
, coords
.x
, coords
.y
-m_Icon
->GetHeight(),
473 (m_Icon
->GetMask() == NULL
) ? FALSE
: TRUE
);
477 wxLayoutObjectIcon::Layout(wxDC
& /* dc */, class wxLayoutList
* )
482 wxLayoutObjectIcon::GetSize(CoordType
*top
, CoordType
*bottom
) const
484 *top
= m_Icon
->GetHeight();
486 return wxPoint(m_Icon
->GetWidth(), m_Icon
->GetHeight());
491 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
495 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
498 wxLayoutStyleInfo::wxLayoutStyleInfo(int ifamily
,
510 underline
= iul
!= 0;
512 m_fg_valid
= fg
!= 0;
513 m_bg_valid
= bg
!= 0;
514 m_fg
= m_fg_valid
? *fg
: *wxBLACK
;
515 m_bg
= m_bg_valid
? *bg
: *wxWHITE
;
518 #define COPY_SI_(what) if(right.what != -1) what = right.what;
521 wxLayoutStyleInfo::operator=(const wxLayoutStyleInfo
&right
)
528 if(right
.m_fg_valid
) m_fg
= right
.m_fg
;
529 if(right
.m_bg_valid
) m_bg
= right
.m_bg
;
533 wxLayoutObjectCmd::wxLayoutObjectCmd(int family
, int size
, int style
, int
534 weight
, int underline
,
535 wxColour
*fg
, wxColour
*bg
)
538 m_StyleInfo
= new wxLayoutStyleInfo(family
, size
,style
,weight
,underline
,fg
,bg
);
541 wxLayoutObjectCmd::wxLayoutObjectCmd(const wxLayoutStyleInfo
&si
)
544 m_StyleInfo
= new wxLayoutStyleInfo
;
549 wxLayoutObjectCmd::Copy(void)
551 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd(
556 m_StyleInfo
->underline
,
557 m_StyleInfo
->m_fg_valid
?
558 &m_StyleInfo
->m_fg
: NULL
,
559 m_StyleInfo
->m_bg_valid
?
560 &m_StyleInfo
->m_bg
: NULL
);
561 obj
->SetUserData(m_UserData
);
566 wxLayoutObjectCmd::Write(wxString
&ostr
)
568 ostr
<< (int) WXLO_TYPE_CMD
<< '\n'
569 << (int) m_StyleInfo
->family
<< '\n'
570 << (int) m_StyleInfo
->size
<< '\n'
571 << (int) m_StyleInfo
->style
<< '\n'
572 << (int) m_StyleInfo
->weight
<< '\n'
573 << (int) m_StyleInfo
->underline
<< '\n'
574 << (int) m_StyleInfo
->m_fg_valid
<< '\n'
575 << (int) m_StyleInfo
->m_bg_valid
<< '\n';
576 if(m_StyleInfo
->m_fg_valid
)
578 ostr
<< (int) m_StyleInfo
->m_fg
.Red() << '\n'
579 << (int) m_StyleInfo
->m_fg
.Green() << '\n'
580 << (int) m_StyleInfo
->m_fg
.Blue() << '\n';
582 if(m_StyleInfo
->m_bg_valid
)
584 ostr
<< (int) m_StyleInfo
->m_bg
.Red() << '\n'
585 << (int) m_StyleInfo
->m_bg
.Green() << '\n'
586 << (int) m_StyleInfo
->m_bg
.Blue() << '\n';
591 wxLayoutObjectCmd::Read(wxString
&istr
)
593 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd
;
596 ReadString(tmp
, istr
);
597 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->family
);
598 ReadString(tmp
, istr
);
599 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->size
);
600 ReadString(tmp
, istr
);
601 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->style
);
602 ReadString(tmp
, istr
);
603 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->weight
);
604 ReadString(tmp
, istr
);
605 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->underline
);
606 ReadString(tmp
, istr
);
607 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->m_fg_valid
);
608 ReadString(tmp
, istr
);
609 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->m_bg_valid
);
610 if(obj
->m_StyleInfo
->m_fg_valid
)
612 int red
, green
, blue
;
613 ReadString(tmp
, istr
);
614 sscanf(tmp
.c_str(),"%d", &red
);
615 ReadString(tmp
, istr
);
616 sscanf(tmp
.c_str(),"%d", &green
);
617 ReadString(tmp
, istr
);
618 sscanf(tmp
.c_str(),"%d", &blue
);
619 obj
->m_StyleInfo
->m_fg
= wxColour(red
, green
, blue
);
621 if(obj
->m_StyleInfo
->m_bg_valid
)
623 int red
, green
, blue
;
624 ReadString(tmp
, istr
);
625 sscanf(tmp
.c_str(),"%d", &red
);
626 ReadString(tmp
, istr
);
627 sscanf(tmp
.c_str(),"%d", &green
);
628 ReadString(tmp
, istr
);
629 sscanf(tmp
.c_str(),"%d", &blue
);
630 obj
->m_StyleInfo
->m_bg
= wxColour(red
, green
, blue
);
636 wxLayoutObjectCmd::~wxLayoutObjectCmd()
642 wxLayoutObjectCmd::GetStyle(void) const
648 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint
const & /* coords */,
649 wxLayoutList
*wxllist
,
650 CoordType begin
, CoordType
/* len */)
652 wxASSERT(m_StyleInfo
);
653 wxllist
->ApplyStyle(*m_StyleInfo
, dc
);
657 wxLayoutObjectCmd::Layout(wxDC
&dc
, class wxLayoutList
* llist
)
659 // this get called, so that recalculation uses right font sizes
660 Draw(dc
, wxPoint(0,0), llist
);
664 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
666 The wxLayoutLine object
668 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
670 wxLayoutLine::wxLayoutLine(wxLayoutLine
*prev
, wxLayoutList
*llist
)
672 m_Width
= m_Height
= 0;
681 RecalculatePosition(llist
);
686 m_LineNumber
= m_Previous
->GetLineNumber() + 1;
687 m_Next
= m_Previous
->GetNextLine();
688 m_Previous
->m_Next
= this;
693 m_Next
->m_Previous
= this;
697 m_StyleInfo
= llist
->GetDefaultStyleInfo();
699 llist
->IncNumLines();
702 wxLayoutLine::~wxLayoutLine()
704 // kbList cleans itself
708 wxLayoutLine::RecalculatePosition(wxLayoutList
*llist
)
710 wxASSERT(m_Previous
|| GetLineNumber() == 0);
712 wxPoint
posOld(m_Position
);
716 m_Position
= m_Previous
->GetPosition();
717 m_Position
.y
+= m_Previous
->GetHeight();
720 m_Position
= wxPoint(0,0);
722 if ( m_Position
!= posOld
)
724 // the whole line moved and must be repainted
725 llist
->SetUpdateRect(m_Position
);
726 llist
->SetUpdateRect(m_Position
.x
+ GetWidth() + MSW_CORRECTION
,
727 m_Position
.y
+ GetHeight() + MSW_CORRECTION
);
728 llist
->SetUpdateRect(posOld
);
729 llist
->SetUpdateRect(posOld
.x
+ GetWidth() + MSW_CORRECTION
,
730 posOld
.y
+ GetHeight() + MSW_CORRECTION
);
737 wxLayoutObjectList::iterator
738 wxLayoutLine::FindObject(CoordType xpos
, CoordType
*offset
) const
742 wxLayoutObjectList::iterator
745 CoordType x
= 0, len
;
747 /* We search through the objects. As we don't like returning the
748 object that the cursor is behind, we just remember such an
749 object in "found" so we can return it if there is really no
750 further object following it. */
751 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
753 len
= (**i
).GetLength();
754 if( x
<= xpos
&& xpos
<= x
+ len
)
757 if(xpos
== x
+ len
) // is there another object behind?
759 else // we are really inside this object
762 x
+= (**i
).GetLength();
764 return found
; // ==NULL if really none found
767 wxLayoutObjectList::iterator
768 wxLayoutLine::FindObjectScreen(wxDC
&dc
, wxLayoutList
*llist
,
769 CoordType xpos
, CoordType
*cxpos
,
774 llist
->ApplyStyle(GetStyleInfo(), dc
);
776 wxLayoutObjectList::iterator i
;
777 CoordType x
= 0, cx
= 0, width
;
779 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
781 wxLayoutObject
*obj
= *i
;
782 if ( obj
->GetType() == WXLO_TYPE_CMD
)
784 // this will set the correct font for the objects which follow
785 obj
->Layout(dc
, llist
);
788 width
= obj
->GetWidth();
789 if( x
<= xpos
&& xpos
<= x
+ width
)
791 *cxpos
= cx
+ obj
->GetOffsetScreen(dc
, xpos
-x
);
798 x
+= obj
->GetWidth();
799 cx
+= obj
->GetLength();
802 // behind last object:
807 return m_ObjectList
.tail();
810 /** Finds text in this line.
811 @param needle the text to find
812 @param xpos the position where to start the search
813 @return the cursoor coord where it was found or -1
816 wxLayoutLine::FindText(const wxString
&needle
, CoordType xpos
) const
821 wxString
const *text
;
823 for(wxLOiterator i
= m_ObjectList
.begin(); i
!= m_ObjectList
.end(); i
++)
825 if(cpos
>= xpos
) // search from here!
827 if((**i
).GetType() == WXLO_TYPE_TEXT
)
829 text
= & ((wxLayoutObjectText
*)(*i
))->GetText();
830 relpos
= text
->Find(needle
);
831 if(relpos
>= cpos
-xpos
) // -1 if not found
836 cpos
+= (**i
).GetLength();
839 return -1; // not found
843 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
846 wxASSERT(obj
!= NULL
);
851 wxLOiterator i
= FindObject(xpos
, &offset
);
854 if(xpos
== 0 ) // aha, empty line!
856 m_ObjectList
.push_back(obj
);
857 m_Length
+= obj
->GetLength();
864 CoordType len
= (**i
).GetLength();
865 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
866 { // insert before this object
867 m_ObjectList
.insert(i
,obj
);
868 m_Length
+= obj
->GetLength();
873 if( i
== m_ObjectList
.tail()) // last object?
874 m_ObjectList
.push_back(obj
);
876 { // insert after current object
878 m_ObjectList
.insert(i
,obj
);
880 m_Length
+= obj
->GetLength();
883 /* Otherwise we need to split the current object.
884 Fortunately this can only be a text object. */
885 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
886 wxString left
, right
;
887 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
888 left
= tobj
->GetText().substr(0,offset
);
889 right
= tobj
->GetText().substr(offset
,len
-offset
);
890 // current text object gets set to right half
891 tobj
->GetText() = right
; // set new text
892 // before it we insert the new object
893 m_ObjectList
.insert(i
,obj
);
894 m_Length
+= obj
->GetLength();
895 // and before that we insert the left half
896 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
901 wxLayoutLine::Insert(CoordType xpos
, const wxString
& text
)
908 wxLOiterator i
= FindObject(xpos
, &offset
);
909 if(i
!= NULLIT
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
911 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
912 tobj
->GetText().insert(offset
, text
);
913 m_Length
+= text
.Length();
917 if ( !Insert(xpos
, new wxLayoutObjectText(text
)) )
925 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
927 CoordType offset
, len
;
932 wxLOiterator i
= FindObject(xpos
, &offset
);
935 if(i
== NULLIT
) return npos
;
936 // now delete from that object:
937 if((**i
).GetType() != WXLO_TYPE_TEXT
)
939 if(offset
!= 0) // at end of line after a non-text object
942 len
= (**i
).GetLength();
945 m_ObjectList
.erase(i
);
949 // tidy up: remove empty text objects
950 if((**i
).GetLength() == 0)
952 m_ObjectList
.erase(i
);
956 CoordType max
= (**i
).GetLength() - offset
;
957 if(npos
< max
) max
= npos
;
960 if(xpos
== GetLength())
963 { // at the end of an object
964 // move to begin of next object:
966 continue; // start over
971 if(offset
== 0 && max
== (**i
).GetLength())
972 m_ObjectList
.erase(i
); // remove the whole object
974 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
982 wxLayoutLine::DeleteWord(CoordType xpos
)
988 wxLOiterator i
= FindObject(xpos
, &offset
);
992 if(i
== NULLIT
) return false;
993 if((**i
).GetType() != WXLO_TYPE_TEXT
)
995 // This should only happen when at end of line, behind a non-text
997 if(offset
== (**i
).GetLength()) return false;
998 m_Length
-= (**i
).GetLength(); // -1
999 m_ObjectList
.erase(i
);
1000 return true; // we are done
1004 if(offset
== (**i
).GetLength()) // at end of object
1009 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
1011 wxString str
= tobj
->GetText();
1012 str
= str
.substr(offset
,str
.Length()-offset
);
1013 // Find out how many positions we need to delete:
1014 // 1. eat leading space
1015 while(isspace(str
.c_str()[count
])) count
++;
1016 // 2. eat the word itself:
1017 while(isalnum(str
.c_str()[count
])) count
++;
1019 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
1020 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
1026 wxFAIL_MSG("unreachable");
1030 wxLayoutLine::DeleteLine(bool update
, wxLayoutList
*llist
)
1032 // maintain linked list integrity
1034 m_Next
->m_Previous
= m_Previous
;
1036 m_Previous
->m_Next
= m_Next
;
1038 // get the line numbers right again
1039 if ( update
&& m_Next
)
1044 // we can't use m_Next after "delete this", so we must save this pointer
1046 wxLayoutLine
*next
= m_Next
;
1049 llist
->DecNumLines();
1055 wxLayoutLine::Draw(wxDC
&dc
,
1056 wxLayoutList
*llist
,
1057 const wxPoint
& offset
) const
1059 wxLayoutObjectList::iterator i
;
1060 wxPoint pos
= offset
;
1061 pos
= pos
+ GetPosition();
1063 pos
.y
+= m_BaseLine
;
1065 CoordType xpos
= 0; // cursorpos, lenght of line
1067 CoordType from
, to
, tempto
;
1069 int highlight
= llist
->IsSelected(this, &from
, &to
);
1070 // WXLO_DEBUG(("highlight=%d", highlight ));
1071 if(highlight
== 1) // we need to draw the whole line inverted!
1072 llist
->StartHighlighting(dc
);
1074 llist
->EndHighlighting(dc
);
1076 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
1078 if(highlight
== -1) // partially highlight line
1080 // parts of the line need highlighting
1081 tempto
= xpos
+(**i
).GetLength();
1082 (**i
).Draw(dc
, pos
, llist
, from
-xpos
, to
-xpos
);
1085 (**i
).Draw(dc
, pos
, llist
);
1086 pos
.x
+= (**i
).GetWidth();
1087 xpos
+= (**i
).GetLength();
1092 This function does all the recalculation, that is, it should only be
1093 called from within wxLayoutList::Layout(), as it uses the current
1094 list's styleinfo and updates it.
1097 wxLayoutLine::Layout(wxDC
&dc
,
1098 wxLayoutList
*llist
,
1100 wxPoint
*cursorSize
,
1101 wxLayoutStyleInfo
*cursorStyle
,
1103 bool suppressSIupdate
)
1105 wxLayoutObjectList::iterator i
;
1107 // when a line becomes dirty, we redraw it from the place where it was
1108 // changed till the end of line (because the following wxLayoutObjects are
1109 // moved when the preceding one changes) - calculate the update rectangle.
1110 CoordType updateTop
= m_Position
.y
,
1112 updateWidth
= m_Width
,
1113 updateHeight
= m_Height
;
1117 bottomHeight
= 0; // above and below baseline
1119 objTopHeight
, objBottomHeight
; // above and below baseline
1123 CoordType heightOld
= m_Height
;
1129 bool cursorFound
= false;
1131 RecalculatePosition(llist
);
1135 *cursorPos
= m_Position
;
1136 if(cursorSize
) *cursorSize
= wxPoint(0,0);
1139 m_StyleInfo
= llist
->GetStyleInfo(); // save current style
1140 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
1142 wxLayoutObject
*obj
= *i
;
1143 obj
->Layout(dc
, llist
);
1144 wxPoint sizeObj
= obj
->GetSize(&objTopHeight
, &objBottomHeight
);
1146 if(cursorPos
&& ! cursorFound
)
1148 // we need to check whether the text cursor is here
1149 len
= obj
->GetLength();
1150 if(count
<= cx
&& count
+len
> cx
)
1152 if(obj
->GetType() == WXLO_TYPE_TEXT
)
1154 len
= cx
- count
; // pos in object
1155 CoordType width
, height
, descent
;
1156 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
1157 &width
, &height
, &descent
);
1158 cursorPos
->x
+= width
;
1159 cursorPos
->y
= m_Position
.y
;
1161 if(len
< obj
->GetLength())
1162 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
1164 str
= WXLO_CURSORCHAR
;
1165 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
1167 if(cursorStyle
) // set style info
1168 *cursorStyle
= llist
->GetStyleInfo();
1171 // Just in case some joker inserted an empty string object:
1173 width
= WXLO_MINIMUM_CURSOR_WIDTH
;
1176 cursorSize
->x
= width
;
1177 cursorSize
->y
= height
;
1180 cursorFound
= true; // no more checks
1184 // on some other object
1185 CoordType top
, bottom
; // unused
1187 *cursorSize
= obj
->GetSize(&top
,&bottom
);
1188 cursorPos
->y
= m_Position
.y
;
1189 cursorFound
= true; // no more checks
1195 cursorPos
->x
+= obj
->GetWidth();
1199 m_Width
+= sizeObj
.x
;
1200 if(sizeObj
.y
> m_Height
)
1202 m_Height
= sizeObj
.y
;
1205 if(objTopHeight
> topHeight
)
1206 topHeight
= objTopHeight
;
1207 if(objBottomHeight
> bottomHeight
)
1208 bottomHeight
= objBottomHeight
;
1213 if ( updateHeight
< m_Height
)
1214 updateHeight
= m_Height
;
1215 if ( updateWidth
< m_Width
)
1216 updateWidth
= m_Width
;
1218 // update all line if we don't know where to start from
1219 if ( updateLeft
== -1 )
1222 llist
->SetUpdateRect(updateLeft
, updateTop
);
1223 llist
->SetUpdateRect(updateLeft
+ updateWidth
+ MSW_CORRECTION
,
1224 updateTop
+ updateHeight
+ MSW_CORRECTION
);
1227 if(topHeight
+ bottomHeight
> m_Height
)
1229 m_Height
= topHeight
+bottomHeight
;
1232 m_BaseLine
= topHeight
;
1236 CoordType width
, height
, descent
;
1237 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1239 m_BaseLine
= m_Height
- descent
;
1242 // tell next line about coordinate change
1243 if(m_Next
&& m_Height
!= heightOld
)
1245 m_Next
->MarkDirty();
1248 // We need to check whether we found a valid cursor size:
1249 if(cursorPos
&& cursorSize
)
1251 // this might be the case if the cursor is at the end of the
1252 // line or on a command object:
1253 if(cursorSize
->x
< WXLO_MINIMUM_CURSOR_WIDTH
)
1255 CoordType width
, height
, descent
;
1256 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1257 cursorSize
->x
= width
;
1258 cursorSize
->y
= height
;
1260 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
1261 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
1268 wxLayoutLine::Break(CoordType xpos
, wxLayoutList
*llist
)
1270 wxASSERT(xpos
>= 0);
1275 wxLOiterator i
= FindObject(xpos
, &offset
);
1277 // must be at the end of the line then
1278 return new wxLayoutLine(this, llist
);
1281 wxLayoutLine
*newLine
= new wxLayoutLine(this, llist
);
1282 // split object at i:
1283 if((**i
).GetType() == WXLO_TYPE_TEXT
1285 && offset
!= (**i
).GetLength() )
1287 wxString left
, right
;
1288 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
1289 left
= tobj
->GetText().substr(0,offset
);
1290 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
1291 // current text object gets set to left half
1292 tobj
->GetText() = left
; // set new text
1293 newLine
->Append(new wxLayoutObjectText(right
));
1294 m_Length
-= right
.Length();
1295 i
++; // don't move this object to the new list
1300 i
++; // move objects from here to new list
1303 while(i
!= m_ObjectList
.end())
1305 wxLayoutObject
*obj
= *i
;
1306 newLine
->Append(obj
);
1307 m_Length
-= obj
->GetLength();
1309 m_ObjectList
.remove(i
); // remove without deleting it
1312 m_Next
->MarkDirty();
1317 wxLayoutLine::Wrap(CoordType wrapmargin
, wxLayoutList
*llist
)
1319 if(GetLength() < wrapmargin
)
1320 return FALSE
; // nothing to do
1322 // find the object which covers the wrapmargin:
1324 wxLOiterator i
= FindObject(wrapmargin
, &offset
);
1325 wxCHECK_MSG( i
!= NULLIT
, FALSE
, "Cannot find object covering wrapmargin.");
1327 // from this object on, the rest of the line must be copied to the
1329 wxLOiterator copyObject
= NULLIT
;
1330 // if we split a text-object, we must pre-pend some text to the
1331 // next line later on, remember it here:
1332 wxString prependText
= "";
1333 // we might need to adjust the cursor position later, so remember it
1334 size_t xpos
= llist
->GetCursorPos().x
;
1335 // by how much did we shorten the current line:
1337 // remember cursor location of object
1338 size_t objectCursorPos
= 0;
1340 size_t breakpos
= offset
;
1342 if( (**i
).GetType() != WXLO_TYPE_TEXT
)
1344 // break before a non-text object
1349 bool foundSpace
= FALSE
;
1352 // while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT)
1354 // try to find a suitable place to split the object:
1355 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
1356 if((**i
).GetType() == WXLO_TYPE_TEXT
1357 && tobj
->GetText().Length() >= breakpos
)
1361 foundSpace
= isspace(tobj
->GetText()[breakpos
]) != 0;
1365 while ( breakpos
-- > 0 );
1372 if(! foundSpace
) // breakpos == 0!
1374 if(i
== m_ObjectList
.begin())
1375 return FALSE
; // could not break line
1379 while(i
!= m_ObjectList
.begin()
1380 && (**i
).GetType() != WXLO_TYPE_TEXT
)
1384 breakpos
= (**i
).GetLength();
1387 }while(! foundSpace
);
1388 // before we actually break the object, we need to know at which
1389 // cursorposition it starts, so we can restore the cursor if needed:
1390 if( this == llist
->GetCursorLine() && xpos
>= breakpos
)
1392 for(wxLOiterator j
= m_ObjectList
.begin();
1393 j
!= NULLIT
&& j
!= i
; j
++)
1394 objectCursorPos
+= (**j
).GetLength();
1396 // now we know where to break it:
1397 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
1398 shorter
= tobj
->GetLength() - breakpos
;
1399 // remember text to copy from this object
1400 prependText
= tobj
->GetText().Mid(breakpos
+1);
1401 tobj
->SetText(tobj
->GetText().Left(breakpos
));
1402 // copy every following object:
1403 copyObject
= i
; copyObject
++;
1406 // make sure there is an empty m_Next line:
1407 (void) new wxLayoutLine(this, llist
);
1409 // We need to move this and all following objects to the next
1410 // line. Starting from the end of line, to keep the order right.
1411 if(copyObject
!= NULLIT
)
1414 for(j
= m_ObjectList
.tail(); j
!= copyObject
; j
--)
1415 m_Next
->Prepend(*j
);
1416 m_Next
->Prepend(*copyObject
);
1417 // and now remove them from this list:
1418 while( copyObject
!= m_ObjectList
.end() )
1420 shorter
+= (**copyObject
).GetLength();
1421 m_ObjectList
.remove(copyObject
); // remove without deleting it
1424 m_Length
-= shorter
;
1426 if(prependText
.Length() > 0)
1427 m_Next
->Insert(0, prependText
);
1429 // do we need to adjust the cursor position?
1430 if( this == llist
->GetCursorLine() && xpos
>= breakpos
)
1432 xpos
= objectCursorPos
+ (xpos
- objectCursorPos
- breakpos
-
1433 ((xpos
> breakpos
) ? 1 : 0 ));
1434 wxASSERT(xpos
>= 0);
1435 llist
->MoveCursorTo( wxPoint( xpos
, m_Next
->GetLineNumber()) );
1437 return TRUE
; // we wrapped the line
1441 wxLayoutLine::ReNumber(void)
1443 CoordType lineNo
= m_Previous
? m_Previous
->m_LineNumber
+1 : 0;
1444 m_LineNumber
= lineNo
++;
1446 for(wxLayoutLine
*next
= GetNextLine();
1447 next
; next
= next
->GetNextLine())
1448 next
->m_LineNumber
= lineNo
++;
1452 wxLayoutLine::MergeNextLine(wxLayoutList
*llist
)
1454 wxCHECK_RET(GetNextLine(),"wxLayout internal error: no next line to merge");
1455 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
1458 MarkDirty(GetWidth());
1460 wxLayoutObject
*last
= NULL
;
1461 for(i
= list
.begin(); i
!= list
.end();)
1463 wxLayoutObject
*current
= *i
;
1465 // merge text objects together for efficiency
1466 if ( last
&& last
->GetType() == WXLO_TYPE_TEXT
&&
1467 current
->GetType() == WXLO_TYPE_TEXT
)
1469 wxLayoutObjectText
*textObj
= (wxLayoutObjectText
*)last
;
1470 wxString
text(textObj
->GetText());
1471 text
+= ((wxLayoutObjectText
*)current
)->GetText();
1472 textObj
->SetText(text
);
1474 list
.erase(i
); // remove and delete it
1478 // just append the object "as was"
1481 list
.remove(i
); // remove without deleting it
1484 wxASSERT(list
.empty());
1486 wxLayoutLine
*oldnext
= GetNextLine();
1487 wxLayoutLine
*nextLine
= oldnext
->GetNextLine();
1491 nextLine
->ReNumber();
1495 // this is now done in Delete(), but if this function is ever called
1496 // from elsewhere, we might have to move refresh code back here (in
1497 // order not to duplicate it)
1499 wxPoint
pos(oldnext
->GetPosition());
1500 llist
->SetUpdateRect(pos
);
1501 llist
->SetUpdateRect(pos
.x
+ oldnext
->GetWidth() + MSW_CORRECTION
,
1502 pos
.y
+ oldnext
->GetHeight() + MSW_CORRECTION
);
1506 llist
->DecNumLines();
1512 wxLayoutLine::GetWrapPosition(CoordType column
)
1515 wxLOiterator i
= FindObject(column
, &offset
);
1516 if(i
== NULLIT
) return -1; // cannot wrap
1518 // go backwards through the list and look for space in text objects
1521 if((**i
).GetType() == WXLO_TYPE_TEXT
)
1525 if(isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1532 }while(offset
!= -1);
1533 i
--; // move on to previous object
1537 column
-= (**i
).GetLength();
1541 offset
= (**i
).GetLength();
1542 }while(i
!= NULLIT
);
1543 /* If we reached the begin of the list and have more than one
1544 object, that one is longer than the margin, so break behind
1547 i
= m_ObjectList
.begin();
1548 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1550 pos
+= (**i
).GetLength();
1553 if(i
== NULLIT
) return -1; //why should this happen?
1555 // now we are behind the one long text object and need to find the
1556 // first space in it
1557 for(offset
= 0; offset
< (**i
).GetLength(); offset
++)
1558 if( isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1562 pos
+= (**i
).GetLength();
1567 #ifdef WXLAYOUT_DEBUG
1569 wxLayoutLine::Debug(void) const
1571 wxPoint pos
= GetPosition();
1572 WXLO_DEBUG(("Line %ld, Pos (%ld,%ld), Height %ld, BL %ld, Font: %d",
1573 (long int) GetLineNumber(),
1574 (long int) pos
.x
, (long int) pos
.y
,
1575 (long int) GetHeight(),
1576 (long int) m_BaseLine
,
1577 (int) m_StyleInfo
.family
));
1578 if(m_ObjectList
.begin() != NULLIT
)
1580 WXLO_DEBUG(((**m_ObjectList
.begin()).DebugDump().c_str()));
1587 wxLayoutLine::Copy(wxLayoutList
*llist
,
1591 CoordType firstOffset
, lastOffset
;
1593 if(to
== -1) to
= GetLength();
1594 if(from
== to
) return;
1596 wxLOiterator first
= FindObject(from
, &firstOffset
);
1597 wxLOiterator last
= FindObject(to
, &lastOffset
);
1599 // Common special case: only one object
1600 if( first
!= NULLIT
&& last
!= NULLIT
&& *first
== *last
)
1602 if( (**first
).GetType() == WXLO_TYPE_TEXT
)
1604 llist
->Insert(new wxLayoutObjectText(
1605 ((wxLayoutObjectText
1606 *)*first
)->GetText().substr(firstOffset
,
1607 lastOffset
-firstOffset
))
1611 else // what can we do?
1613 if(lastOffset
> firstOffset
) // i.e. +1 :-)
1614 llist
->Insert( (**first
).Copy() );
1619 // If we reach here, we can safely copy the whole first object from
1620 // the firstOffset position on:
1621 if((**first
).GetType() == WXLO_TYPE_TEXT
&& firstOffset
!= 0)
1623 llist
->Insert(new wxLayoutObjectText(
1624 ((wxLayoutObjectText
*)*first
)->GetText().substr(firstOffset
))
1627 else if(firstOffset
== 0)
1628 llist
->Insert( (**first
).Copy() );
1629 // else nothing to copy :-(
1631 // Now we copy all objects before the last one:
1632 wxLOiterator i
= first
; i
++;
1633 for( ; i
!= last
; i
++)
1634 llist
->Insert( (**i
).Copy() );
1636 // And now the last object:
1639 if( (**last
).GetType() == WXLO_TYPE_TEXT
)
1641 llist
->Insert(new wxLayoutObjectText(
1642 ((wxLayoutObjectText
*)*last
)->GetText().substr(0,lastOffset
))
1646 llist
->Insert( (**last
).Copy() );
1651 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1653 The wxLayoutList object
1655 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1657 wxLayoutList::wxLayoutList()
1659 #ifdef WXLAYOUT_USE_CARET
1661 #endif // WXLAYOUT_USE_CARET
1665 SetAutoFormatting(TRUE
);
1666 ForceTotalLayout(TRUE
); // for the first time, do all
1667 InvalidateUpdateRect();
1671 wxLayoutList::~wxLayoutList()
1673 SetAutoFormatting(FALSE
);
1676 m_FirstLine
->DeleteLine(false, this);
1678 wxASSERT_MSG( m_numLines
== 0, "line count calculation broken" );
1682 wxLayoutList::Empty(void)
1685 m_FirstLine
= m_FirstLine
->DeleteLine(false, this);
1687 m_CursorPos
= wxPoint(0,0);
1688 m_CursorScreenPos
= wxPoint(0,0);
1689 m_CursorSize
= wxPoint(0,0);
1690 m_movedCursor
= true;
1691 m_FirstLine
= new wxLayoutLine(NULL
, this); // empty first line
1692 m_CursorLine
= m_FirstLine
;
1693 InvalidateUpdateRect();
1698 wxLayoutList::InternalClear(void)
1700 m_Selection
.m_selecting
= false;
1701 m_Selection
.m_valid
= false;
1703 m_DefaultStyleInfo
.family
= wxSWISS
;
1704 m_DefaultStyleInfo
.size
= WXLO_DEFAULTFONTSIZE
;
1705 m_DefaultStyleInfo
.style
= wxNORMAL
;
1706 m_DefaultStyleInfo
.weight
= wxNORMAL
;
1707 m_DefaultStyleInfo
.underline
= 0;
1708 m_DefaultStyleInfo
.m_fg_valid
= TRUE
;
1709 m_DefaultStyleInfo
.m_fg
= *wxBLACK
;
1710 m_DefaultStyleInfo
.m_bg_valid
= TRUE
;
1711 m_DefaultStyleInfo
.m_bg
= *wxWHITE
;
1713 m_CurrentStyleInfo
= m_DefaultStyleInfo
;
1714 m_CursorStyleInfo
= m_DefaultStyleInfo
;
1718 wxLayoutList::Read(wxString
&istr
)
1720 /* In order to handle input of formatted string "nicely", we need
1721 to restore our current font settings after the string. So first
1722 of all, we create a StyleInfo structure with our current
1724 wxLayoutStyleInfo current_si
= GetStyleInfo();
1726 while(istr
.Length())
1728 // check for a linebreak:
1730 tmp
= istr
.BeforeFirst('\n');
1731 int type
= WXLO_TYPE_INVALID
;
1732 sscanf(tmp
.c_str(),"%d", &type
);
1733 if(type
== WXLO_TYPE_LINEBREAK
)
1736 istr
= istr
.AfterFirst('\n');
1740 wxLayoutObject
*obj
= wxLayoutObject::Read(istr
);
1745 /* Now we use the current_si to restore our last font settings: */
1746 Insert(new wxLayoutObjectCmd(current_si
));
1751 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1752 int underline
, wxColour
*fg
,
1755 if(family
!= -1) m_CurrentStyleInfo
.family
= family
;
1756 if(size
!= -1) m_CurrentStyleInfo
.size
= size
;
1757 if(style
!= -1) m_CurrentStyleInfo
.style
= style
;
1758 if(weight
!= -1) m_CurrentStyleInfo
.weight
= weight
;
1759 if(underline
!= -1) m_CurrentStyleInfo
.underline
= underline
!= 0;
1760 if(fg
) m_CurrentStyleInfo
.m_fg
= *fg
;
1761 if(bg
) m_CurrentStyleInfo
.m_bg
= *bg
;
1763 new wxLayoutObjectCmd(
1764 m_CurrentStyleInfo
.family
,
1765 m_CurrentStyleInfo
.size
,
1766 m_CurrentStyleInfo
.style
,
1767 m_CurrentStyleInfo
.weight
,
1768 m_CurrentStyleInfo
.underline
,
1773 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1774 int underline
, char const *fg
, char const *bg
)
1782 cfg
= wxTheColourDatabase
->FindColour(fg
);
1784 cbg
= wxTheColourDatabase
->FindColour(bg
);
1786 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
1790 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
1791 int underline
, wxColour
*fg
, wxColour
*bg
)
1794 m_DefaultStyleInfo
= wxLayoutStyleInfo(family
, size
, style
, weight
,
1796 m_CurrentStyleInfo
= m_DefaultStyleInfo
;
1798 // Empty() should be called after we set m_DefaultStyleInfo because
1799 // otherwise the style info for the first line (created in Empty()) would be
1805 wxLayoutList::FindText(const wxString
&needle
, const wxPoint
&cpos
) const
1810 for(line
= m_FirstLine
;
1812 line
= line
->GetNextLine())
1814 if(line
->GetLineNumber() >= cpos
.y
)
1816 xpos
= line
->FindText(needle
,
1817 (line
->GetLineNumber() == cpos
.y
) ?
1820 return wxPoint(xpos
, line
->GetLineNumber());
1823 return wxPoint(-1,-1);
1828 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
1830 AddCursorPosToUpdateRect();
1832 wxPoint cursorPosOld
= m_CursorPos
;
1834 wxLayoutLine
*line
= m_FirstLine
;
1835 while(line
&& line
->GetLineNumber() != p
.y
)
1836 line
= line
->GetNextLine();
1837 if(line
&& line
->GetLineNumber() == p
.y
) // found it
1839 m_CursorPos
.y
= p
.y
;
1840 m_CursorLine
= line
;
1841 CoordType len
= line
->GetLength();
1844 m_CursorPos
.x
= p
.x
;
1848 m_CursorPos
.x
= len
;
1852 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1854 return m_CursorPos
== p
;
1858 wxLayoutList::MoveCursorVertically(int n
)
1860 AddCursorPosToUpdateRect();
1862 wxPoint cursorPosOld
= m_CursorPos
;
1865 if(n
< 0) // move up
1867 if(m_CursorLine
== m_FirstLine
) return false;
1868 while(n
< 0 && m_CursorLine
)
1870 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1876 m_CursorLine
= m_FirstLine
;
1882 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1883 m_CursorPos
.x
= m_CursorLine
->GetLength();
1889 wxLayoutLine
*last
= m_CursorLine
;
1890 if(! m_CursorLine
->GetNextLine()) return false;
1891 while(n
> 0 && m_CursorLine
)
1895 last
= m_CursorLine
;
1896 m_CursorLine
= m_CursorLine
->GetNextLine();
1900 m_CursorLine
= last
;
1906 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1907 m_CursorPos
.x
= m_CursorLine
->GetLength();
1912 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1918 wxLayoutList::MoveCursorHorizontally(int n
)
1920 AddCursorPosToUpdateRect();
1922 wxPoint cursorPosOld
= m_CursorPos
;
1927 if(m_CursorPos
.x
== 0) // at begin of line
1929 if(! MoveCursorVertically(-1))
1931 MoveCursorToEndOfLine();
1936 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1937 m_CursorPos
.x
-= move
; n
+= move
;
1942 int len
= m_CursorLine
->GetLength();
1943 if(m_CursorPos
.x
== len
) // at end of line
1945 if(! MoveCursorVertically(1))
1947 MoveCursorToBeginOfLine();
1952 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1953 m_CursorPos
.x
+= move
;
1957 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1963 wxLayoutList::MoveCursorWord(int n
, bool untilNext
)
1965 wxCHECK_MSG( m_CursorLine
, false, "no current line" );
1966 wxCHECK_MSG( n
== -1 || n
== +1, false, "not implemented yet" );
1968 CoordType moveDistance
= 0;
1970 wxLayoutLine
*lineCur
= m_CursorLine
;
1971 for ( wxLOiterator i
= lineCur
->FindObject(m_CursorPos
.x
, &offset
);
1979 // moving forward, pass to the first object of the next line
1981 lineCur
= lineCur
->GetNextLine();
1983 i
= lineCur
->GetFirstObject();
1987 // moving backwards, pass to the last object of the prev line
1989 lineCur
= lineCur
->GetPreviousLine();
1991 i
= lineCur
->GetLastObject();
1996 // moved to the end/beginning of text
2003 wxLayoutObject
*obj
= *i
;
2007 // calculate offset: we are either at the very beginning or the very
2008 // end of the object, so it isn't very difficult (the only time when
2009 // offset is != -1 is for the very first iteration when its value is
2010 // returned by FindObject)
2014 offset
= obj
->GetLength();
2017 if( obj
->GetType() != WXLO_TYPE_TEXT
)
2019 // any visible non text objects count as one word
2020 if ( obj
->IsVisibleObject() )
2024 moveDistance
+= obj
->GetLength();
2029 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)obj
;
2031 bool canAdvance
= true;
2033 if ( offset
== tobj
->GetLength() )
2038 // can't move further in this text object
2041 // still should move over the object border
2045 else if ( offset
> 0 )
2047 // offset is off by 1, make it a valid index
2054 const wxString
& text
= tobj
->GetText();
2055 const char *start
= text
.c_str();
2056 const char *end
= start
+ text
.length();
2057 const char *p
= start
+ offset
;
2065 // to the beginning/end of the next/prev word
2066 while ( p
>= start
&& p
< end
&& isspace(*p
) )
2071 // go to the end/beginning of the word (in a broad sense...)
2072 while ( p
>= start
&& p
< end
&& !isspace(*p
) )
2081 // now advance to the beginning of the next word
2082 while ( isspace(*p
) && p
< end
)
2088 // in these 2 cases we took 1 char too much
2089 if ( (p
< start
) || isspace(*p
) )
2095 CoordType moveDelta
= p
- start
- offset
;
2096 if ( (n
< 0) && (offset
== tobj
->GetLength() - 1) )
2098 // because we substracted 1 from offset in this case above, now
2099 // compensate for it
2103 if ( moveDelta
!= 0 )
2105 moveDistance
+= moveDelta
;
2112 // except for the first iteration, offset is calculated in the beginning
2117 MoveCursorHorizontally(moveDistance
);
2123 wxLayoutList::Insert(wxString
const &text
)
2125 wxASSERT(m_CursorLine
);
2126 wxASSERT_MSG( text
.Find('\n') == wxNOT_FOUND
, "use wxLayoutImportText!" );
2131 AddCursorPosToUpdateRect();
2133 wxASSERT(m_CursorLine
->GetLength() >= m_CursorPos
.x
);
2135 if ( !m_CursorLine
->Insert(m_CursorPos
.x
, text
) )
2137 m_CursorPos
.x
+= text
.Length();
2139 m_movedCursor
= true;
2142 m_CursorLine
->MarkDirty();
2148 wxLayoutList::Insert(wxLayoutObject
*obj
)
2150 wxASSERT(m_CursorLine
);
2153 m_CursorLine
= GetFirstLine();
2155 AddCursorPosToUpdateRect();
2157 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
2158 m_CursorPos
.x
+= obj
->GetLength();
2159 m_movedCursor
= true;
2162 m_CursorLine
->MarkDirty();
2168 wxLayoutList::Insert(wxLayoutList
*llist
)
2173 for(wxLayoutLine
*line
= llist
->GetFirstLine();
2175 line
= line
->GetNextLine()
2178 for(wxLOiterator i
= line
->GetFirstObject();
2188 wxLayoutList::LineBreak(void)
2190 wxASSERT(m_CursorLine
);
2192 AddCursorPosToUpdateRect();
2194 wxPoint
position(m_CursorLine
->GetPosition());
2197 width
= m_CursorLine
->GetWidth(),
2198 height
= m_CursorLine
->GetHeight();
2200 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
, this);
2201 if(m_CursorLine
->GetPreviousLine() == NULL
)
2202 m_FirstLine
= m_CursorLine
;
2206 // The following code will produce a height which is guaranteed to
2207 // be too high: old lineheight + the height of both new lines.
2208 // We can probably drop the old line height and start with height =
2210 wxLayoutLine
*prev
= m_CursorLine
->GetPreviousLine();
2212 height
+= prev
->GetHeight();
2213 height
+= m_CursorLine
->GetHeight();
2215 m_movedCursor
= true;
2217 SetUpdateRect(position
);
2218 SetUpdateRect(position
.x
+ width
+ MSW_CORRECTION
,
2219 position
.y
+ height
+ MSW_CORRECTION
);
2225 wxLayoutList::WrapLine(CoordType column
)
2227 return m_CursorLine
->Wrap(column
, this);
2231 wxLayoutList::WrapAll(CoordType column
)
2233 wxLayoutLine
*line
= m_FirstLine
;
2239 rc
&= line
->Wrap(column
, this);
2240 line
= line
->GetNextLine();
2246 wxLayoutList::Delete(CoordType npos
)
2248 wxCHECK_MSG(m_CursorLine
, false, "can't delete in non existing line");
2253 AddCursorPosToUpdateRect();
2255 // were other lines appended to this one (this is important to know because
2256 // this means that our width _increased_ as the result of deletion)
2257 bool wasMerged
= false;
2259 // the size of the region to update
2260 CoordType totalHeight
= m_CursorLine
->GetHeight(),
2261 totalWidth
= m_CursorLine
->GetWidth();
2266 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
2270 // More to delete, continue on next line.
2272 // First, check if line is empty:
2273 if(m_CursorLine
->GetLength() == 0)
2275 // in this case, updating could probably be optimised
2277 wxASSERT(DeleteLines(1) == 0);
2286 // Need to join next line
2287 if(! m_CursorLine
->GetNextLine())
2292 wxLayoutLine
*next
= m_CursorLine
->GetNextLine();
2295 totalHeight
+= next
->GetHeight();
2296 totalWidth
+= next
->GetWidth();
2298 m_CursorLine
->MergeNextLine(this);
2303 wxFAIL_MSG("can't delete all this");
2313 // we need to update the whole tail of the line and the lines which
2317 wxPoint
position(m_CursorLine
->GetPosition());
2318 SetUpdateRect(position
);
2319 SetUpdateRect(position
.x
+ totalWidth
+ MSW_CORRECTION
,
2320 position
.y
+ totalHeight
+ MSW_CORRECTION
);
2327 wxLayoutList::DeleteLines(int n
)
2329 wxASSERT(m_CursorLine
);
2332 AddCursorPosToUpdateRect();
2336 if(!m_CursorLine
->GetNextLine())
2337 { // we cannot delete this line, but we can clear it
2338 MoveCursorToBeginOfLine();
2339 DeleteToEndOfLine();
2341 m_CursorLine
->MarkDirty();
2345 line
= m_CursorLine
;
2346 m_CursorLine
= m_CursorLine
->DeleteLine(true, this);
2348 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
2349 wxASSERT(m_FirstLine
);
2350 wxASSERT(m_CursorLine
);
2353 m_CursorLine
->MarkDirty();
2358 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
)
2362 wxLayoutLine
*line
= m_FirstLine
;
2364 // first, make sure everything is calculated - this might not be
2365 // needed, optimise it later
2366 ApplyStyle(m_DefaultStyleInfo
, dc
);
2369 line
->RecalculatePosition(this); // so we don't need to do it all the time
2370 // little condition to speed up redrawing:
2371 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
2372 line
= line
->GetNextLine();
2377 wxLayoutList::GetCursorScreenPos(void) const
2379 return m_CursorScreenPos
;
2383 Is called before each Draw(). Now, it will re-layout all lines which
2387 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
, bool forceAll
,
2388 wxPoint
*cpos
, wxPoint
*csize
)
2390 // first, make sure everything is calculated - this might not be
2391 // needed, optimise it later
2392 ApplyStyle(m_DefaultStyleInfo
, dc
);
2400 ForceTotalLayout(FALSE
);
2403 // If one line was dirty, we need to re-calculate all
2404 // following lines, too.
2405 bool wasDirty
= forceAll
;
2406 // we need to layout until we reach at least the cursor line,
2407 // otherwise we won't be able to scroll to it
2408 bool cursorReached
= false;
2409 wxLayoutLine
*line
= m_FirstLine
;
2413 ApplyStyle(line
->GetStyleInfo(), dc
);
2415 // if any previous line was dirty, we need to layout all
2418 // go on until we find the cursorline
2420 // layout dirty lines:
2422 // always layout the cursor line toupdate the cursor
2423 // position and size:
2424 || line
== m_CursorLine
2425 // or if it's the line we are asked to look for:
2426 || (cpos
&& line
->GetLineNumber() == cpos
->y
)
2427 // layout at least the desired region:
2429 || (line
->GetPosition().y
<= bottom
)
2435 // The following Layout() calls will update our
2436 // m_CurrentStyleInfo if needed.
2437 if(line
== m_CursorLine
)
2439 line
->Layout(dc
, this,
2440 (wxPoint
*)&m_CursorScreenPos
,
2441 (wxPoint
*)&m_CursorSize
,
2444 // we cannot layout the line twice, so copy the coords:
2445 if(cpos
&& line
->GetLineNumber() == cpos
->y
)
2447 *cpos
= m_CursorScreenPos
;
2449 *csize
= m_CursorSize
;
2451 cursorReached
= TRUE
;
2455 if(cpos
&& line
->GetLineNumber() == cpos
->y
)
2457 line
->Layout(dc
, this,
2459 csize
, NULL
, cpos
->x
);
2460 cursorReached
= TRUE
;
2463 line
->Layout(dc
, this);
2466 line
= line
->GetNextLine();
2469 #ifndef WXLAYOUT_USE_CARET
2470 // can only be 0 if we are on the first line and have no next line
2471 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
2472 m_CursorLine
->GetNextLine() == NULL
&&
2473 m_CursorLine
== m_FirstLine
));
2474 #endif // WXLAYOUT_USE_CARET
2475 AddCursorPosToUpdateRect();
2479 wxLayoutList::GetScreenPos(wxDC
&dc
, const wxPoint
&cpos
, wxPoint
*csize
)
2482 Layout(dc
, -1, false, &pos
, csize
);
2487 wxLayoutList::Draw(wxDC
&dc
,
2488 wxPoint
const &offset
,
2493 wxLayoutLine
*line
= m_FirstLine
;
2495 if ( m_Selection
.m_discarded
)
2497 // calculate them if we don't have them already
2498 if ( !m_Selection
.HasValidScreenCoords() )
2500 m_Selection
.m_ScreenA
= GetScreenPos(dc
, m_Selection
.m_CursorA
);
2501 m_Selection
.m_ScreenB
= GetScreenPos(dc
, m_Selection
.m_CursorB
);
2504 // invalidate the area which was previousle selected - and which is not
2505 // selected any more
2506 SetUpdateRect(m_Selection
.m_ScreenA
);
2507 SetUpdateRect(m_Selection
.m_ScreenB
);
2509 m_Selection
.m_discarded
= false;
2512 /* This call to Layout() will re-calculate and update all lines
2517 ApplyStyle(m_DefaultStyleInfo
, dc
);
2518 wxBrush
brush(m_CurrentStyleInfo
.m_bg
, wxSOLID
);
2520 dc
.SetBackgroundMode(wxTRANSPARENT
);
2524 // only draw if between top and bottom:
2526 line
->GetPosition().y
+ line
->GetHeight() > top
))
2528 ApplyStyle(line
->GetStyleInfo(), dc
);
2529 // little condition to speed up redrawing:
2531 && line
->GetPosition().y
2532 +(clipStrictly
? line
->GetHeight() : 0) >= bottom
)
2534 line
->Draw(dc
, this, offset
);
2536 line
= line
->GetNextLine();
2538 InvalidateUpdateRect();
2540 WXLO_DEBUG(("Selection is %s : %ld,%ld/%ld,%ld",
2541 m_Selection
.m_valid
? "valid" : "invalid",
2542 m_Selection
.m_CursorA
.x
, m_Selection
.m_CursorA
.y
,
2543 m_Selection
.m_CursorB
.x
, m_Selection
.m_CursorB
.y
));
2547 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
,
2551 // First, find the right line:
2553 *line
= m_FirstLine
,
2554 *lastline
= m_FirstLine
;
2557 ApplyStyle(m_DefaultStyleInfo
, dc
);
2560 p
= line
->GetPosition();
2561 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
2564 line
= line
->GetNextLine();
2567 bool didFind
= line
!= NULL
;
2571 // use the last line:
2576 cursorPos
->y
= line
->GetLineNumber();
2578 bool foundinline
= true;
2581 // Now, find the object in the line:
2586 i
= line
->FindObjectScreen(dc
, this,
2593 i
= line
->FindObjectScreen(dc
, this,
2598 *found
= didFind
&& foundinline
;
2600 return (i
== NULLIT
) ? NULL
: *i
;
2605 wxLayoutList::GetSize(void) const
2608 *line
= m_FirstLine
,
2611 return wxPoint(0,0);
2613 wxPoint
maxPoint(0,0);
2618 if(line
->GetWidth() > maxPoint
.x
)
2619 maxPoint
.x
= line
->GetWidth();
2621 line
= line
->GetNextLine();
2624 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
2626 // if the line was just added, its height would be 0 and we can't call
2627 // Layout() from here because we don't have a dc and we might be not drawing
2628 // at all, besides... So take the cursor height by default (taking 0 is bad
2629 // because then the scrollbars won't be resized and the new line won't be
2631 if ( last
->IsDirty() )
2633 if ( last
->GetHeight() == 0 )
2634 maxPoint
.y
+= m_CursorSize
.y
;
2635 if ( last
->GetWidth() == 0 && maxPoint
.x
< m_CursorSize
.x
)
2636 maxPoint
.x
= m_CursorSize
.x
;
2644 wxLayoutList::DrawCursor(wxDC
&dc
, bool active
, wxPoint
const &translate
)
2646 if ( m_movedCursor
)
2647 m_movedCursor
= false;
2649 wxPoint
coords(m_CursorScreenPos
);
2650 coords
+= translate
;
2652 #ifdef WXLAYOUT_DEBUG
2653 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
2654 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
2655 (long)coords
.x
, (long)coords
.y
,
2656 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
2657 (long)m_CursorLine
->GetLineNumber(),
2658 (long)m_CursorLine
->GetLength()));
2660 wxLogStatus("Cursor is at (%d, %d)", m_CursorPos
.x
, m_CursorPos
.y
);
2663 #ifdef WXLAYOUT_USE_CARET
2664 m_caret
->Move(coords
);
2665 #else // !WXLAYOUT_USE_CARET
2667 wxASSERT(m_CursorSize
.x
>= WXLO_MINIMUM_CURSOR_WIDTH
);
2668 dc
.SetBrush(*wxWHITE_BRUSH
);
2669 //FIXME: wxGTK XOR is borken at the moment!!!dc.SetLogicalFunction(wxXOR);
2670 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
2673 dc
.SetLogicalFunction(wxXOR
);
2674 dc
.DrawRectangle(coords
.x
, coords
.y
,
2675 m_CursorSize
.x
, m_CursorSize
.y
);
2676 SetUpdateRect(coords
.x
, coords
.y
);
2677 SetUpdateRect(coords
.x
+m_CursorSize
.x
,
2678 coords
.y
+m_CursorSize
.y
);
2682 dc
.SetLogicalFunction(wxCOPY
);
2683 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
2684 coords
.x
, coords
.y
);
2685 SetUpdateRect(coords
.x
, coords
.y
+m_CursorSize
.y
-1);
2686 SetUpdateRect(coords
.x
, coords
.y
);
2688 dc
.SetLogicalFunction(wxCOPY
);
2689 //dc.SetBrush(wxNullBrush);
2690 #endif // WXLAYOUT_USE_CARET/!WXLAYOUT_USE_CARET
2694 wxLayoutList::SetUpdateRect(CoordType x
, CoordType y
)
2696 if(m_UpdateRectValid
)
2697 GrowRect(m_UpdateRect
, x
, y
);
2702 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
2703 m_UpdateRect
.height
= 4;// wxGTK :-)
2704 m_UpdateRectValid
= true;
2709 wxLayoutList::StartSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2711 wxPoint
cpos(cposOrig
);
2714 WXLO_DEBUG(("Starting selection at %ld/%ld", cpos
.x
, cpos
.y
));
2715 m_Selection
.m_CursorA
= cpos
;
2716 m_Selection
.m_CursorB
= cpos
;
2717 m_Selection
.m_ScreenA
= spos
;
2718 m_Selection
.m_ScreenB
= spos
;
2719 m_Selection
.m_selecting
= true;
2720 m_Selection
.m_valid
= false;
2724 wxLayoutList::ContinueSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2726 wxPoint
cpos(cposOrig
);
2730 wxASSERT(m_Selection
.m_selecting
== true);
2731 wxASSERT(m_Selection
.m_valid
== false);
2732 WXLO_DEBUG(("Continuing selection at %ld/%ld", cpos
.x
, cpos
.y
));
2734 m_Selection
.m_ScreenB
= spos
;
2735 m_Selection
.m_CursorB
= cpos
;
2739 wxLayoutList::EndSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2741 wxPoint
cpos(cposOrig
);
2742 if(cpos
.x
== -1) cpos
= m_CursorPos
;
2743 ContinueSelection(cpos
, spos
);
2744 WXLO_DEBUG(("Ending selection at %ld/%ld", cpos
.x
, cpos
.y
));
2745 // we always want m_CursorA <= m_CursorB!
2746 if( m_Selection
.m_CursorA
> m_Selection
.m_CursorB
)
2748 // exchange the start/end points
2749 wxPoint help
= m_Selection
.m_CursorB
;
2750 m_Selection
.m_CursorB
= m_Selection
.m_CursorA
;
2751 m_Selection
.m_CursorA
= help
;
2753 help
= m_Selection
.m_ScreenB
;
2754 m_Selection
.m_ScreenB
= m_Selection
.m_ScreenA
;
2755 m_Selection
.m_ScreenA
= help
;
2757 m_Selection
.m_selecting
= false;
2758 m_Selection
.m_valid
= true;
2759 /// In case we just clicked somewhere, the selection will have zero
2760 /// size, so we discard it immediately.
2761 if(m_Selection
.m_CursorA
== m_Selection
.m_CursorB
)
2766 wxLayoutList::DiscardSelection()
2768 if ( !HasSelection() )
2771 m_Selection
.m_valid
=
2772 m_Selection
.m_selecting
= false;
2773 m_Selection
.m_discarded
= true;
2777 wxLayoutList::IsSelecting(void) const
2779 return m_Selection
.m_selecting
;
2783 wxLayoutList::IsSelected(const wxPoint
&cursor
) const
2785 if ( !HasSelection() )
2789 (m_Selection
.m_CursorA
<= cursor
2790 && cursor
<= m_Selection
.m_CursorB
)
2791 || (m_Selection
.m_CursorB
<= cursor
2792 && cursor
<= m_Selection
.m_CursorA
)
2797 /** Tests whether this layout line is selected and needs
2799 @param line to test for
2800 @return 0 = not selected, 1 = fully selected, -1 = partially
2804 wxLayoutList::IsSelected(const wxLayoutLine
*line
, CoordType
*from
,
2807 wxASSERT(line
); wxASSERT(to
); wxASSERT(from
);
2809 if(! m_Selection
.m_valid
&& ! m_Selection
.m_selecting
)
2812 CoordType y
= line
->GetLineNumber();
2814 (m_Selection
.m_CursorA
.y
< y
&& m_Selection
.m_CursorB
.y
> y
)
2815 || (m_Selection
.m_CursorB
.y
< y
&& m_Selection
.m_CursorA
.y
> y
)
2818 else if(m_Selection
.m_CursorA
.y
== y
)
2820 *from
= m_Selection
.m_CursorA
.x
;
2821 if(m_Selection
.m_CursorB
.y
== y
)
2822 *to
= m_Selection
.m_CursorB
.x
;
2825 if(m_Selection
.m_CursorB
> m_Selection
.m_CursorA
)
2826 *to
= line
->GetLength();
2832 CoordType help
= *to
;
2838 else if(m_Selection
.m_CursorB
.y
== y
)
2840 *to
= m_Selection
.m_CursorB
.x
;
2841 if(m_Selection
.m_CursorA
.y
== y
)
2842 *from
= m_Selection
.m_CursorA
.x
;
2845 if(m_Selection
.m_CursorB
> m_Selection
.m_CursorA
)
2848 *from
= line
->GetLength();
2852 CoordType help
= *to
;
2863 wxLayoutList::DeleteSelection(void)
2865 if(! m_Selection
.m_valid
)
2868 m_Selection
.m_valid
= false;
2870 // Only delete part of the current line?
2871 if(m_Selection
.m_CursorA
.y
== m_Selection
.m_CursorB
.y
)
2873 MoveCursorTo(m_Selection
.m_CursorA
);
2874 Delete(m_Selection
.m_CursorB
.x
- m_Selection
.m_CursorA
.x
);
2878 // We now know that the two lines are different:
2881 * firstLine
= GetLine(m_Selection
.m_CursorA
.y
),
2882 * lastLine
= GetLine(m_Selection
.m_CursorB
.y
);
2883 // be a bit paranoid:
2884 if(! firstLine
|| ! lastLine
)
2887 // First, delete what's left of this line:
2888 MoveCursorTo(m_Selection
.m_CursorA
);
2889 DeleteToEndOfLine();
2891 wxLayoutLine
*prevLine
= firstLine
->GetPreviousLine(),
2892 *nextLine
= firstLine
->GetNextLine();
2893 while(nextLine
&& nextLine
!= lastLine
)
2894 nextLine
= nextLine
->DeleteLine(false, this);
2896 // Now nextLine = lastLine;
2897 Delete(1); // This joins firstLine and nextLine
2898 Delete(m_Selection
.m_CursorB
.x
); // This deletes the first x positions
2900 // Recalculate the line positions and numbers but notice that firstLine
2901 // might not exist any more - it could be deleted by Delete(1) above
2902 wxLayoutLine
*firstLine2
= prevLine
? prevLine
->GetNextLine() : m_FirstLine
;
2903 firstLine2
->MarkDirty();
2906 /// Starts highlighting the selection
2908 wxLayoutList::StartHighlighting(wxDC
&dc
)
2911 dc
.SetTextForeground(m_CurrentStyleInfo
.m_bg
);
2912 dc
.SetTextBackground(m_CurrentStyleInfo
.m_fg
);
2913 dc
.SetBackgroundMode(wxSOLID
);
2917 /// Ends highlighting the selection
2919 wxLayoutList::EndHighlighting(wxDC
&dc
)
2922 dc
.SetTextForeground(m_CurrentStyleInfo
.m_fg
);
2923 dc
.SetTextBackground(m_CurrentStyleInfo
.m_bg
);
2924 dc
.SetBackgroundMode(wxTRANSPARENT
);
2930 wxLayoutList::GetLine(CoordType index
) const
2932 wxASSERT_MSG( (0 <= index
) && (index
< (CoordType
)m_numLines
),
2936 CoordType n
= index
;
2938 CoordType lineNo
= 0;
2941 for ( line
= m_FirstLine
; line
&& n
-- > 0; line
=
2942 line
->GetNextLine() )
2945 wxASSERT(line
->GetLineNumber() == lineNo
);
2952 // should be the right one
2953 wxASSERT( line
->GetLineNumber() == index
);
2961 wxLayoutList::Copy(const wxPoint
&from
,
2968 for(firstLine
= m_FirstLine
;
2969 firstLine
&& firstLine
->GetLineNumber() < from
.y
;
2970 firstLine
=firstLine
->GetNextLine())
2972 if(!firstLine
|| firstLine
->GetLineNumber() != from
.y
)
2975 for(lastLine
= m_FirstLine
;
2976 lastLine
&& lastLine
->GetLineNumber() < to
.y
;
2977 lastLine
=lastLine
->GetNextLine())
2979 if(!lastLine
|| lastLine
->GetLineNumber() != to
.y
)
2984 wxLayoutLine
*tmp
= firstLine
;
2985 firstLine
= lastLine
;
2989 wxLayoutList
*llist
= new wxLayoutList();
2991 if(firstLine
== lastLine
)
2993 firstLine
->Copy(llist
, from
.x
, to
.x
);
2997 // Extract objects from first line
2998 firstLine
->Copy(llist
, from
.x
);
3000 // Extract all lines between
3001 for(wxLayoutLine
*line
= firstLine
->GetNextLine();
3003 line
= line
->GetNextLine())
3008 // Extract objects from last line
3009 lastLine
->Copy(llist
, 0, to
.x
);
3015 wxLayoutList::GetSelection(wxLayoutDataObject
*wxlo
, bool invalidate
)
3017 if(! m_Selection
.m_valid
)
3019 if(m_Selection
.m_selecting
)
3025 if(invalidate
) m_Selection
.m_valid
= false;
3027 wxLayoutList
*llist
= Copy( m_Selection
.m_CursorA
,
3028 m_Selection
.m_CursorB
);
3030 if(llist
&& wxlo
) // export as data object, too
3034 wxLayoutExportObject
*exp
;
3035 wxLayoutExportStatus
status(llist
);
3036 while((exp
= wxLayoutExport( &status
, WXLO_EXPORT_AS_OBJECTS
)) != NULL
)
3038 if(exp
->type
== WXLO_EXPORT_EMPTYLINE
)
3039 string
<< (int) WXLO_TYPE_LINEBREAK
<< '\n';
3041 exp
->content
.object
->Write(string
);
3044 wxlo
->SetLayoutData(string
);
3051 #define COPY_SI(what) if(si.what != -1) { m_CurrentStyleInfo.what = si.what; fontChanged = TRUE; }
3054 wxLayoutList::ApplyStyle(wxLayoutStyleInfo
const &si
, wxDC
&dc
)
3056 bool fontChanged
= FALSE
;
3063 dc
.SetFont( m_FontCache
.GetFont(m_CurrentStyleInfo
) );
3067 m_CurrentStyleInfo
.m_fg
= si
.m_fg
;
3068 m_CurrentStyleInfo
.m_fg_valid
= true;
3069 dc
.SetTextForeground(m_CurrentStyleInfo
.m_fg
);
3073 m_CurrentStyleInfo
.m_bg
= si
.m_bg
;
3074 m_CurrentStyleInfo
.m_bg_valid
= true;
3075 dc
.SetTextBackground(m_CurrentStyleInfo
.m_bg
);
3080 #ifdef WXLAYOUT_DEBUG
3083 wxLayoutList::Debug(void)
3085 WXLO_DEBUG(("Cursor is in line %d, screen pos = (%d, %d)",
3086 m_CursorLine
->GetLineNumber(),
3087 m_CursorScreenPos
.x
, m_CursorScreenPos
.y
));
3090 for(line
= m_FirstLine
; line
; line
= line
->GetNextLine())
3099 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3103 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
3105 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
3106 wxString
const & title
)
3111 // remove any highlighting which could interfere with printing:
3112 m_llist
->StartSelection();
3113 m_llist
->EndSelection();
3114 // force a full layout of the list:
3115 m_llist
->ForceTotalLayout();
3116 // layout is called in ScaleDC() when we have a DC
3119 wxLayoutPrintout::~wxLayoutPrintout()
3124 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
3126 // The following bit is taken from the printing sample, let's see
3127 // whether it works for us.
3129 /* You might use THIS code to set the printer DC to ROUGHLY reflect
3130 * the screen text size. This page also draws lines of actual length 5cm
3133 // Get the logical pixels per inch of screen and printer
3134 int ppiScreenX
, ppiScreenY
;
3135 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
3136 int ppiPrinterX
, ppiPrinterY
;
3137 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
3139 if(ppiScreenX
== 0) // not yet set, need to guess
3144 if(ppiPrinterX
== 0) // not yet set, need to guess
3150 // This scales the DC so that the printout roughly represents the
3151 // the screen scaling. The text point size _should_ be the right size
3152 // but in fact is too small for some reason. This is a detail that will
3153 // need to be addressed at some point but can be fudged for the
3155 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
3157 // Now we have to check in case our real page size is reduced
3158 // (e.g. because we're drawing to a print preview memory DC)
3159 int pageWidth
, pageHeight
;
3161 dc
->GetSize(&w
, &h
);
3162 GetPageSizePixels(&pageWidth
, &pageHeight
);
3163 if(pageWidth
!= 0) // doesn't work always
3165 // If printer pageWidth == current DC width, then this doesn't
3166 // change. But w might be the preview bitmap width, so scale down.
3167 scale
= scale
* (float)(w
/(float)pageWidth
);
3169 dc
->SetUserScale(scale
, scale
);
3173 bool wxLayoutPrintout::OnPrintPage(int page
)
3182 top
= (page
- 1)*m_PrintoutHeight
;
3183 bottom
= top
+ m_PrintoutHeight
;
3185 WXLO_DEBUG(("OnPrintPage(%d) printing from %d to %d", page
, top
,
3187 // SetDeviceOrigin() doesn't work here, so we need to manually
3188 // translate all coordinates.
3189 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
3190 m_llist
->Draw(*dc
, translate
, top
, bottom
, TRUE
/* clip strictly
3198 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
3200 /* We allocate a temporary wxDC for printing, so that we can
3201 determine the correct paper size and scaling. We don't actually
3202 print anything on it. */
3203 #if defined(__WXMSW__)
3204 wxPrinterDC
*psdc
= new wxPrinterDC("","",WXLLIST_TEMPFILE
,false);
3206 wxPostScriptDC
*psdc
= new wxPostScriptDC(WXLLIST_TEMPFILE
,false);
3209 psdc
->StartDoc(m_title
);
3210 // before we draw anything, me must make sure the list is properly
3212 m_llist
->Layout(*psdc
);
3214 float scale
= ScaleDC(psdc
);
3216 psdc
->GetSize(&m_PageWidth
, &m_PageHeight
);
3218 // This sets a left/top origin of 15% and 5%:
3219 m_Offset
= wxPoint((15*m_PageWidth
)/100, (5*m_PageHeight
)/100);
3221 // This is the length of the printable area.
3222 m_PrintoutHeight
= m_PageHeight
- 2*m_Offset
.y
;
3223 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
3226 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
3229 *maxPage
= m_NumOfPages
;
3232 *selPageTo
= m_NumOfPages
;
3235 wxRemoveFile(WXLLIST_TEMPFILE
);
3238 bool wxLayoutPrintout::HasPage(int pageNum
)
3240 return pageNum
<= m_NumOfPages
;
3244 Stupid wxWindows doesn't draw proper ellipses, so we comment this
3245 out. It's a waste of paper anyway.
3249 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
3250 wxPoint topleft
, wxPoint bottomright
,
3253 // make backups of all essential parameters
3254 const wxBrush
& brush
= dc
.GetBrush();
3255 const wxPen
& pen
= dc
.GetPen();
3256 const wxFont
& font
= dc
.GetFont();
3258 dc
.SetBrush(*wxWHITE_BRUSH
);
3259 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
3260 dc
.DrawRoundedRectangle(topleft
.x
,
3261 topleft
.y
,bottomright
.x
-topleft
.x
,
3262 bottomright
.y
-topleft
.y
);
3263 dc
.SetBrush(*wxBLACK_BRUSH
);
3264 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
3265 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
3269 page
= "9999/9999 "; // many pages...
3271 dc
.GetTextExtent(page
,&w
,&h
);
3272 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
3273 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
3274 dc
.GetTextExtent("XXXX", &w
,&h
);
3275 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);
3286 wxFontCache::GetFont(int family
, int size
, int style
, int weight
,
3289 for(wxFCEList::iterator i
= m_FontList
.begin();
3290 i
!= m_FontList
.end(); i
++)
3291 if( (**i
).Matches(family
, size
, style
, weight
, underline
) )
3292 return (**i
).GetFont();
3294 wxFontCacheEntry
*fce
= new wxFontCacheEntry(family
, size
, style
,
3296 m_FontList
.push_back(fce
);
3297 return fce
->GetFont();