1 /*-*- c++ -*-********************************************************
2 * wxllist: wxLayoutList, a layout engine for text and graphics *
4 * (C) 1998-1999 by Karsten Ballüder (Ballueder@usa.net) *
7 *******************************************************************/
14 #pragma implementation "wxllist.h"
17 #include <wx/wxprec.h>
26 # include "gui/wxllist.h"
27 # include "gui/wxlparser.h"
28 # define SHOW_SELECTIONS 1
31 # include "wxlparser.h"
32 # define SHOW_SELECTIONS 1
36 # include <iostream.h>
40 # include <wx/print.h>
42 # include <wx/filefn.h>
45 #ifdef WXLAYOUT_USE_CARET
46 # include <wx/caret.h>
47 #endif // WXLAYOUT_USE_CARET
51 /// This should never really get created
52 #define WXLLIST_TEMPFILE "__wxllist.tmp"
56 # define TypeString(t) g_aTypeStrings[t]
57 # define WXLO_DEBUG(x) wxLogDebug x
59 static const char *g_aTypeStrings
[] =
61 "invalid", "text", "cmd", "icon"
64 wxLayoutObject::Debug(void)
66 WXLO_DEBUG(("%s",g_aTypeStrings
[GetType()]));
69 # define TypeString(t) ""
70 # define WXLO_DEBUG(x)
73 // FIXME under MSW, this constant is needed to make the thing properly redraw
74 // itself - I don't know where the size calculation error is and I can't
75 // waste time looking for it right now. Search for occurences of
76 // MSW_CORRECTION to find all the places where I did it.
78 static const int MSW_CORRECTION
= 5;
80 static const int MSW_CORRECTION
= 0;
83 /// Cursors smaller than this disappear in XOR drawing mode
84 #define WXLO_MINIMUM_CURSOR_WIDTH 4
86 /// Use this character to estimate a cursor size when none is available.
87 #define WXLO_CURSORCHAR "E"
88 /** @name Helper functions */
90 /// allows me to compare to wxPoints
91 bool operator <=(wxPoint
const &p1
, wxPoint
const &p2
)
93 return p1
.y
< p2
.y
|| (p1
.y
== p2
.y
&& p1
.x
<= p2
.x
);
96 /// grows a wxRect so that it includes the given point
99 void GrowRect(wxRect
&r
, CoordType x
, CoordType y
)
103 else if(r
.x
+ r
.width
< x
)
108 else if(r
.y
+ r
.height
< y
)
114 /// returns true if the point is in the rectangle
116 bool Contains(const wxRect
&r
, const wxPoint
&p
)
118 return r
.x
<= p
.x
&& r
.y
<= p
.y
&& (r
.x
+r
.width
) >= p
.x
&& (r
.y
+ r
.height
) >= p
.y
;
126 void ReadString(wxString
&to
, wxString
&from
)
129 const char *cptr
= from
.c_str();
130 while(*cptr
&& *cptr
!= '\n')
136 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
140 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
144 wxLayoutObject::Read(wxString
&istr
)
147 ReadString(tmp
, istr
);
149 sscanf(tmp
.c_str(),"%d", &type
);
154 return wxLayoutObjectText::Read(istr
);
156 return wxLayoutObjectCmd::Read(istr
);
158 return wxLayoutObjectIcon::Read(istr
);
164 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
168 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
170 wxLayoutObjectText::wxLayoutObjectText(const wxString
&txt
)
180 wxLayoutObjectText::Copy(void)
182 wxLayoutObjectText
*obj
= new wxLayoutObjectText(m_Text
);
183 obj
->m_Width
= m_Width
;
184 obj
->m_Height
= m_Height
;
186 obj
->m_Bottom
= m_Bottom
;
187 obj
->SetUserData(m_UserData
);
193 wxLayoutObjectText::Write(wxString
&ostr
)
195 ostr
<< (int) WXLO_TYPE_TEXT
<< '\n'
200 wxLayoutObjectText::Read(wxString
&istr
)
203 ReadString(text
, istr
);
205 return new wxLayoutObjectText(text
);
209 wxLayoutObjectText::GetSize(CoordType
*top
, CoordType
*bottom
) const
212 *top
= m_Top
; *bottom
= m_Bottom
;
213 return wxPoint(m_Width
, m_Height
);
217 wxLayoutObjectText::Draw(wxDC
&dc
, wxPoint
const &coords
,
218 wxLayoutList
*wxllist
,
219 CoordType begin
, CoordType end
)
222 dc
.DrawText(m_Text
, coords
.x
, coords
.y
-m_Top
);
225 // highlight the bit between begin and len
229 ypos
= coords
.y
-m_Top
;
230 long width
, height
, descent
;
232 if(begin
< 0) begin
= 0;
233 if( end
> (signed)m_Text
.Length() )
234 end
= m_Text
.Length();
236 str
= m_Text
.Mid(0, begin
);
237 dc
.DrawText(str
, xpos
, ypos
);
238 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
240 wxllist
->StartHighlighting(dc
);
241 str
= m_Text
.Mid(begin
, end
-begin
);
242 dc
.DrawText(str
, xpos
, ypos
);
243 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
245 wxllist
->EndHighlighting(dc
);
246 str
= m_Text
.Mid(end
, m_Text
.Length()-end
);
247 dc
.DrawText(str
, xpos
, ypos
);
252 wxLayoutObjectText::GetOffsetScreen(wxDC
&dc
, CoordType xpos
) const
256 maxlen
= m_Text
.Length();
259 height
, descent
= 0l;
261 if(xpos
== 0) return 0; // easy
263 while(width
< xpos
&& offs
< maxlen
)
265 dc
.GetTextExtent(m_Text
.substr(0,offs
),
266 &width
, &height
, &descent
);
269 /* We have to substract 1 to compensate for the offs++, and another
270 one because we don't want to position the cursor behind the
271 object what we clicked on, but before - otherwise it looks
273 return (xpos
> 2) ? offs
-2 : 0;
277 wxLayoutObjectText::Layout(wxDC
&dc
, class wxLayoutList
*llist
)
281 // now this is done in wxLayoutLine::Layout(), but this code might be
282 // reenabled later - in principle, it's more efficient
284 CoordType widthOld
= m_Width
,
285 heightOld
= m_Height
;
288 dc
.GetTextExtent(m_Text
, &m_Width
, &m_Height
, &descent
);
291 if ( widthOld
!= m_Width
|| heightOld
!= m_Height
)
293 // as the text length changed, it must be refreshed
294 wxLayoutLine
*line
= GetLine();
296 wxCHECK_RET( line
, "wxLayoutObjectText can't refresh itself" );
298 // as our size changed, we need to repaint the part which was appended
299 wxPoint
position(line
->GetPosition());
301 // this is not the most efficient way (we repaint the whole line), but
302 // it's not too slow and is *simple*
303 if ( widthOld
< m_Width
)
305 if ( heightOld
< m_Height
)
306 heightOld
= m_Height
;
308 llist
->SetUpdateRect(position
.x
+ widthOld
+ MSW_CORRECTION
,
309 position
.y
+ heightOld
+ MSW_CORRECTION
);
314 m_Top
= m_Height
- m_Bottom
;
318 #ifdef WXLAYOUT_DEBUG
320 wxLayoutObjectText::Debug(void)
322 wxLayoutObject::Debug();
323 WXLO_DEBUG((" `%s`", m_Text
.c_str()));
327 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
331 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
333 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
const &icon
)
335 m_Icon
= new wxBitmap(icon
);
340 wxLayoutObjectIcon::Write(wxString
&ostr
)
342 /* Exports icon through a temporary file. */
344 wxString file
= wxGetTempFileName("wxloexport");
346 ostr
<< WXLO_TYPE_ICON
<< '\n'
348 m_Icon
->SaveFile(file
, WXLO_BITMAP_FORMAT
);
352 wxLayoutObjectIcon::Read(wxString
&istr
)
355 ReadString(file
, istr
);
357 if(! wxFileExists(file
))
359 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon
;
361 if(!obj
->m_Icon
->LoadFile(file
, WXLO_BITMAP_FORMAT
))
371 wxLayoutObjectIcon::Copy(void)
373 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon(new
375 obj
->SetUserData(m_UserData
);
379 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
*icon
)
385 wxLayoutObjectIcon::Draw(wxDC
&dc
, wxPoint
const &coords
,
386 wxLayoutList
*wxllist
,
387 CoordType begin
, CoordType
/* len */)
389 dc
.DrawBitmap(*m_Icon
, coords
.x
, coords
.y
-m_Icon
->GetHeight(),
390 (m_Icon
->GetMask() == NULL
) ? FALSE
: TRUE
);
394 wxLayoutObjectIcon::Layout(wxDC
& /* dc */, class wxLayoutList
* )
399 wxLayoutObjectIcon::GetSize(CoordType
*top
, CoordType
*bottom
) const
401 *top
= m_Icon
->GetHeight();
403 return wxPoint(m_Icon
->GetWidth(), m_Icon
->GetHeight());
408 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
412 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
415 wxLayoutStyleInfo::wxLayoutStyleInfo(int ifamily
,
423 family
= ifamily
; size
= isize
;
424 style
= istyle
; weight
= iweight
;
425 underline
= iul
!= 0;
442 #define COPY_SI_(what) if(right.what != -1) what = right.what;
445 wxLayoutStyleInfo::operator=(const wxLayoutStyleInfo
&right
)
452 if(right
.m_fg_valid
) m_fg
= right
.m_fg
;
453 if(right
.m_bg_valid
) m_bg
= right
.m_bg
;
457 wxLayoutObjectCmd::wxLayoutObjectCmd(int family
, int size
, int style
, int
458 weight
, int underline
,
459 wxColour
*fg
, wxColour
*bg
)
462 m_StyleInfo
= new wxLayoutStyleInfo(family
, size
,style
,weight
,underline
,fg
,bg
);
466 wxLayoutObjectCmd::Copy(void)
468 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd(
473 m_StyleInfo
->underline
,
474 m_StyleInfo
->m_fg_valid
?
475 &m_StyleInfo
->m_fg
: NULL
,
476 m_StyleInfo
->m_bg_valid
?
477 &m_StyleInfo
->m_bg
: NULL
);
478 obj
->SetUserData(m_UserData
);
483 wxLayoutObjectCmd::Write(wxString
&ostr
)
485 ostr
<< WXLO_TYPE_CMD
<< '\n'
486 << m_StyleInfo
->size
<< '\n'
487 << m_StyleInfo
->family
<< '\n'
488 << m_StyleInfo
->style
<< '\n'
489 << m_StyleInfo
->weight
<< '\n'
490 << m_StyleInfo
->underline
<< '\n'
491 << m_StyleInfo
->m_fg_valid
<< '\n'
492 << m_StyleInfo
->m_bg_valid
<< '\n';
493 if(m_StyleInfo
->m_fg_valid
)
495 ostr
<< m_StyleInfo
->m_fg
.Red() << '\n'
496 << m_StyleInfo
->m_fg
.Green() << '\n'
497 << m_StyleInfo
->m_fg
.Blue() << '\n';
499 if(m_StyleInfo
->m_bg_valid
)
501 ostr
<< m_StyleInfo
->m_bg
.Red() << '\n'
502 << m_StyleInfo
->m_bg
.Green() << '\n'
503 << m_StyleInfo
->m_bg
.Blue() << '\n';
508 wxLayoutObjectCmd::Read(wxString
&istr
)
510 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd
;
513 ReadString(tmp
, istr
);
514 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->size
);
515 ReadString(tmp
, istr
);
516 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->family
);
517 ReadString(tmp
, istr
);
518 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->style
);
519 ReadString(tmp
, istr
);
520 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->weight
);
521 ReadString(tmp
, istr
);
522 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->underline
);
523 ReadString(tmp
, istr
);
524 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->m_fg_valid
);
525 ReadString(tmp
, istr
);
526 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->m_bg_valid
);
527 if(obj
->m_StyleInfo
->m_fg_valid
)
529 int red
, green
, blue
;
530 ReadString(tmp
, istr
);
531 sscanf(tmp
.c_str(),"%d", &red
);
532 ReadString(tmp
, istr
);
533 sscanf(tmp
.c_str(),"%d", &green
);
534 ReadString(tmp
, istr
);
535 sscanf(tmp
.c_str(),"%d", &blue
);
536 obj
->m_StyleInfo
->m_fg
= wxColour(red
, green
, blue
);
538 if(obj
->m_StyleInfo
->m_bg_valid
)
540 int red
, green
, blue
;
541 ReadString(tmp
, istr
);
542 sscanf(tmp
.c_str(),"%d", &red
);
543 ReadString(tmp
, istr
);
544 sscanf(tmp
.c_str(),"%d", &green
);
545 ReadString(tmp
, istr
);
546 sscanf(tmp
.c_str(),"%d", &blue
);
547 obj
->m_StyleInfo
->m_bg
= wxColour(red
, green
, blue
);
553 wxLayoutObjectCmd::~wxLayoutObjectCmd()
559 wxLayoutObjectCmd::GetStyle(void) const
565 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint
const & /* coords */,
566 wxLayoutList
*wxllist
,
567 CoordType begin
, CoordType
/* len */)
569 wxASSERT(m_StyleInfo
);
570 wxllist
->ApplyStyle(m_StyleInfo
, dc
);
574 wxLayoutObjectCmd::Layout(wxDC
&dc
, class wxLayoutList
* llist
)
576 // this get called, so that recalculation uses right font sizes
577 Draw(dc
, wxPoint(0,0), llist
);
581 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
583 The wxLayoutLine object
585 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
587 wxLayoutLine::wxLayoutLine(wxLayoutLine
*prev
, wxLayoutList
*llist
)
590 m_Width
= m_Height
= 0;
595 RecalculatePosition(llist
);
598 m_LineNumber
= m_Previous
->GetLineNumber()+1;
599 m_Next
= m_Previous
->GetNextLine();
600 m_Previous
->m_Next
= this;
604 m_Next
->m_Previous
= this;
605 m_Next
->MoveLines(+1);
606 m_Next
->RecalculatePositions(1,llist
);
610 wxLayoutLine::~wxLayoutLine()
612 // kbList cleans itself
616 wxLayoutLine::RecalculatePosition(wxLayoutList
*llist
)
618 wxASSERT(m_Previous
|| GetLineNumber() == 0);
620 wxPoint
posOld(m_Position
);
624 m_Position
= m_Previous
->GetPosition();
625 m_Position
.y
+= m_Previous
->GetHeight();
628 m_Position
= wxPoint(0,0);
630 if ( m_Position
!= posOld
)
632 // the whole line moved and must be repainted
633 llist
->SetUpdateRect(m_Position
);
634 llist
->SetUpdateRect(m_Position
.x
+ GetWidth() + MSW_CORRECTION
,
635 m_Position
.y
+ GetHeight() + MSW_CORRECTION
);
636 llist
->SetUpdateRect(posOld
);
637 llist
->SetUpdateRect(posOld
.x
+ GetWidth() + MSW_CORRECTION
,
638 posOld
.y
+ GetHeight() + MSW_CORRECTION
);
645 wxLayoutLine::RecalculatePositions(int recurse
, wxLayoutList
*llist
)
647 //FIXME: is this really needed? We run Layout() anyway.
648 // Recursing here, drives computation time up exponentially, as
649 // each line will cause all following lines to be recalculated.
650 // Yes, or linenumbers go wrong.
652 wxASSERT(recurse
>= 0);
653 wxPoint pos
= m_Position
;
654 CoordType height
= m_Height
;
656 // WXLO_TRACE("RecalculatePositions()");
657 RecalculatePosition(llist
);
661 m_Next
->RecalculatePositions(--recurse
, llist
);
662 else if(pos
!= m_Position
|| m_Height
!= height
)
663 m_Next
->RecalculatePositions(0, llist
);
667 wxLayoutObjectList::iterator
668 wxLayoutLine::FindObject(CoordType xpos
, CoordType
*offset
) const
672 wxLayoutObjectList::iterator
675 CoordType x
= 0, len
;
677 /* We search through the objects. As we don't like returning the
678 object that the cursor is behind, we just remember such an
679 object in "found" so we can return it if there is really no
680 further object following it. */
681 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
683 len
= (**i
).GetLength();
684 if( x
<= xpos
&& xpos
<= x
+ len
)
687 if(xpos
== x
+ len
) // is there another object behind?
689 else // we are really inside this object
692 x
+= (**i
).GetLength();
694 return found
; // ==NULL if really none found
697 wxLayoutObjectList::iterator
698 wxLayoutLine::FindObjectScreen(wxDC
&dc
,
699 CoordType xpos
, CoordType
*cxpos
,
704 wxLayoutObjectList::iterator i
;
705 CoordType x
= 0, cx
= 0, width
;
707 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
709 width
= (**i
).GetWidth();
710 if( x
<= xpos
&& xpos
<= x
+ width
)
712 *cxpos
= cx
+ (**i
).GetOffsetScreen(dc
, xpos
-x
);
713 if(found
) *found
= true;
716 x
+= (**i
).GetWidth();
717 cx
+= (**i
).GetLength();
719 // behind last object:
721 if(found
) *found
= false;
722 return m_ObjectList
.tail();
725 /** Finds text in this line.
726 @param needle the text to find
727 @param xpos the position where to start the search
728 @return the cursoor coord where it was found or -1
731 wxLayoutLine::FindText(const wxString
&needle
, CoordType xpos
) const
736 wxString
const *text
;
738 for(wxLOiterator i
= m_ObjectList
.begin(); i
!= m_ObjectList
.end(); i
++)
740 if(cpos
>= xpos
) // search from here!
742 if((**i
).GetType() == WXLO_TYPE_TEXT
)
744 text
= & ((wxLayoutObjectText
*)(*i
))->GetText();
745 relpos
= text
->Find(needle
);
746 if(relpos
>= cpos
-xpos
) // -1 if not found
751 cpos
+= (**i
).GetLength();
754 return -1; // not found
758 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
761 wxASSERT(obj
!= NULL
);
766 wxLOiterator i
= FindObject(xpos
, &offset
);
769 if(xpos
== 0 ) // aha, empty line!
771 m_ObjectList
.push_back(obj
);
772 m_Length
+= obj
->GetLength();
779 CoordType len
= (**i
).GetLength();
780 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
781 { // insert before this object
782 m_ObjectList
.insert(i
,obj
);
783 m_Length
+= obj
->GetLength();
788 if( i
== m_ObjectList
.tail()) // last object?
789 m_ObjectList
.push_back(obj
);
791 { // insert after current object
793 m_ObjectList
.insert(i
,obj
);
795 m_Length
+= obj
->GetLength();
798 /* Otherwise we need to split the current object.
799 Fortunately this can only be a text object. */
800 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
801 wxString left
, right
;
802 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
803 left
= tobj
->GetText().substr(0,offset
);
804 right
= tobj
->GetText().substr(offset
,len
-offset
);
805 // current text object gets set to right half
806 tobj
->GetText() = right
; // set new text
807 // before it we insert the new object
808 m_ObjectList
.insert(i
,obj
);
809 m_Length
+= obj
->GetLength();
810 // and before that we insert the left half
811 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
816 wxLayoutLine::Insert(CoordType xpos
, const wxString
& text
)
823 wxLOiterator i
= FindObject(xpos
, &offset
);
824 if(i
!= NULLIT
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
826 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
827 tobj
->GetText().insert(offset
, text
);
828 m_Length
+= text
.Length();
832 if ( !Insert(xpos
, new wxLayoutObjectText(text
)) )
840 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
842 CoordType offset
, len
;
847 wxLOiterator i
= FindObject(xpos
, &offset
);
850 if(i
== NULLIT
) return npos
;
851 // now delete from that object:
852 if((**i
).GetType() != WXLO_TYPE_TEXT
)
854 if(offset
!= 0) // at end of line after a non-text object
857 len
= (**i
).GetLength();
860 m_ObjectList
.erase(i
);
864 // tidy up: remove empty text objects
865 if((**i
).GetLength() == 0)
867 m_ObjectList
.erase(i
);
871 CoordType max
= (**i
).GetLength() - offset
;
872 if(npos
< max
) max
= npos
;
875 if(xpos
== GetLength())
878 { // at the end of an object
879 // move to begin of next object:
881 continue; // start over
886 if(offset
== 0 && max
== (**i
).GetLength())
887 m_ObjectList
.erase(i
); // remove the whole object
889 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
897 wxLayoutLine::DeleteWord(CoordType xpos
)
903 wxLOiterator i
= FindObject(xpos
, &offset
);
907 if(i
== NULLIT
) return false;
908 if((**i
).GetType() != WXLO_TYPE_TEXT
)
910 // This should only happen when at end of line, behind a non-text
912 if(offset
== (**i
).GetLength()) return false;
913 m_Length
-= (**i
).GetLength(); // -1
914 m_ObjectList
.erase(i
);
915 return true; // we are done
919 if(offset
== (**i
).GetLength()) // at end of object
924 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
926 wxString str
= tobj
->GetText();
927 str
= str
.substr(offset
,str
.Length()-offset
);
928 // Find out how many positions we need to delete:
929 // 1. eat leading space
930 while(isspace(str
.c_str()[count
])) count
++;
931 // 2. eat the word itself:
932 while(isalnum(str
.c_str()[count
])) count
++;
934 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
935 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
941 wxFAIL_MSG("unreachable");
945 wxLayoutLine::DeleteLine(bool update
, wxLayoutList
*llist
)
947 if(m_Next
) m_Next
->m_Previous
= m_Previous
;
948 if(m_Previous
) m_Previous
->m_Next
= m_Next
;
951 m_Next
->MoveLines(-1);
952 m_Next
->RecalculatePositions(1, llist
);
954 wxLayoutLine
*next
= m_Next
;
960 wxLayoutLine::Draw(wxDC
&dc
,
962 const wxPoint
& offset
) const
964 wxLayoutObjectList::iterator i
;
965 wxPoint pos
= offset
;
966 pos
= pos
+ GetPosition();
970 CoordType xpos
= 0; // cursorpos, lenght of line
972 CoordType from
, to
, tempto
;
973 //FIXME This doesn't work yet, needs updating afterr default
974 //settings for list or a wxLayoutObjectCmd have changed:
975 //llist->ApplyStyle(&((wxLayoutLine *)this)->m_StyleInfo, dc);
976 int highlight
= llist
->IsSelected(this, &from
, &to
);
977 // WXLO_DEBUG(("highlight=%d", highlight ));
978 if(highlight
== 1) // we need to draw the whole line inverted!
979 llist
->StartHighlighting(dc
);
981 llist
->EndHighlighting(dc
);
983 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
985 if(highlight
== -1) // partially highlight line
987 // parts of the line need highlighting
988 tempto
= xpos
+(**i
).GetLength();
989 (**i
).Draw(dc
, pos
, llist
, from
-xpos
, to
-xpos
);
992 (**i
).Draw(dc
, pos
, llist
);
993 pos
.x
+= (**i
).GetWidth();
994 xpos
+= (**i
).GetLength();
999 wxLayoutLine::Layout(wxDC
&dc
,
1000 wxLayoutList
*llist
,
1002 wxPoint
*cursorSize
,
1005 wxLayoutObjectList::iterator i
;
1007 // when a line becomes dirty, we redraw it from the place where it was
1008 // changed till the end of line (because the following wxLayoutObjects are
1009 // moved when the preceding one changes) - calculate the update rectangle.
1010 CoordType updateTop
= m_Position
.y
,
1012 updateWidth
= m_Width
,
1013 updateHeight
= m_Height
;
1017 bottomHeight
= 0; // above and below baseline
1019 objTopHeight
, objBottomHeight
; // above and below baseline
1023 CoordType heightOld
= m_Height
;
1029 bool cursorFound
= false;
1033 *cursorPos
= m_Position
;
1034 if(cursorSize
) *cursorSize
= wxPoint(0,0);
1037 //FIXME This doesn't work yet, needs updating afterr default
1038 //settings for list or a wxLayoutObjectCmd have changed:
1039 //llist->ApplyStyle(&m_StyleInfo, dc);
1040 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
1042 wxLayoutObject
*obj
= *i
;
1043 obj
->Layout(dc
, llist
);
1044 wxPoint sizeObj
= obj
->GetSize(&objTopHeight
, &objBottomHeight
);
1046 if(cursorPos
&& ! cursorFound
)
1048 // we need to check whether the text cursor is here
1049 len
= obj
->GetLength();
1050 if(count
<= cx
&& count
+len
> cx
)
1052 if(obj
->GetType() == WXLO_TYPE_TEXT
)
1054 len
= cx
- count
; // pos in object
1055 CoordType width
, height
, descent
;
1056 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
1057 &width
, &height
, &descent
);
1058 cursorPos
->x
+= width
;
1059 cursorPos
->y
= m_Position
.y
;
1061 if(len
< obj
->GetLength())
1062 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
1064 str
= WXLO_CURSORCHAR
;
1065 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
1066 wxASSERT(cursorSize
);
1067 // Just in case some joker inserted an empty string object:
1068 if(width
== 0) width
= WXLO_MINIMUM_CURSOR_WIDTH
;
1069 if(height
== 0) height
= sizeObj
.y
;
1070 cursorSize
->x
= width
;
1071 cursorSize
->y
= height
;
1072 cursorFound
= true; // no more checks
1076 // on some other object
1077 CoordType top
, bottom
; // unused
1078 *cursorSize
= obj
->GetSize(&top
,&bottom
);
1079 cursorPos
->y
= m_Position
.y
;
1080 cursorFound
= true; // no more checks
1086 cursorPos
->x
+= obj
->GetWidth();
1090 m_Width
+= sizeObj
.x
;
1091 if(sizeObj
.y
> m_Height
)
1093 m_Height
= sizeObj
.y
;
1096 if(objTopHeight
> topHeight
)
1097 topHeight
= objTopHeight
;
1098 if(objBottomHeight
> bottomHeight
)
1099 bottomHeight
= objBottomHeight
;
1104 if ( updateHeight
< m_Height
)
1105 updateHeight
= m_Height
;
1106 if ( updateWidth
< m_Width
)
1107 updateWidth
= m_Width
;
1109 // update all line if we don't know where to start from
1110 if ( updateLeft
== -1 )
1113 llist
->SetUpdateRect(updateLeft
, updateTop
);
1114 llist
->SetUpdateRect(updateLeft
+ updateWidth
+ MSW_CORRECTION
,
1115 updateTop
+ updateHeight
+ MSW_CORRECTION
);
1118 if(topHeight
+ bottomHeight
> m_Height
)
1120 m_Height
= topHeight
+bottomHeight
;
1123 m_BaseLine
= topHeight
;
1127 CoordType width
, height
, descent
;
1128 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1130 m_BaseLine
= m_Height
- descent
;
1133 // tell next line about coordinate change
1134 if(m_Next
&& m_Height
!= heightOld
)
1136 // FIXME isn't this done in RecalculatePositions() below anyhow?
1137 m_Next
->RecalculatePositions(0, llist
);
1140 // We need to check whether we found a valid cursor size:
1143 // this might be the case if the cursor is at the end of the
1144 // line or on a command object:
1145 if(cursorSize
->y
< WXLO_MINIMUM_CURSOR_WIDTH
)
1147 CoordType width
, height
, descent
;
1148 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1149 cursorSize
->x
= width
;
1150 cursorSize
->y
= height
;
1152 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
1153 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
1156 RecalculatePositions(1, llist
);
1163 wxLayoutLine::Break(CoordType xpos
, wxLayoutList
*llist
)
1165 wxASSERT(xpos
>= 0);
1169 /* If we are at the begin of a line, we want to move all other
1170 lines down and stay with the cursor where we are. However, if we
1171 are in an empty line, we want to move down with it. */
1172 if(xpos
== 0 && GetLength() > 0)
1173 { // insert an empty line before this one
1174 wxLayoutLine
*prev
= new wxLayoutLine(m_Previous
, llist
);
1175 if(m_Previous
== NULL
)
1176 { // We were in first line, need to link in new empty line
1178 prev
->m_Next
= this;
1180 m_Previous
->m_Height
= 0; // this is a wild guess
1183 m_Next
->RecalculatePositions(1, llist
);
1188 wxLOiterator i
= FindObject(xpos
, &offset
);
1190 // must be at the end of the line then
1191 return new wxLayoutLine(this, llist
);
1194 wxLayoutLine
*newLine
= new wxLayoutLine(this, llist
);
1195 // split object at i:
1196 if((**i
).GetType() == WXLO_TYPE_TEXT
&& offset
!= 0)
1198 wxString left
, right
;
1199 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
1200 left
= tobj
->GetText().substr(0,offset
);
1201 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
1202 // current text object gets set to left half
1203 tobj
->GetText() = left
; // set new text
1204 newLine
->Append(new wxLayoutObjectText(right
));
1205 m_Length
-= right
.Length();
1206 i
++; // don't move this object to the new list
1211 i
++; // move objects from here to new list
1214 while(i
!= m_ObjectList
.end())
1216 wxLayoutObject
*obj
= *i
;
1217 newLine
->Append(obj
);
1218 m_Length
-= obj
->GetLength();
1220 m_ObjectList
.remove(i
); // remove without deleting it
1223 m_Next
->RecalculatePositions(2, llist
);
1229 wxLayoutLine::MergeNextLine(wxLayoutList
*llist
)
1231 wxCHECK_RET(GetNextLine(),"wxLayout internal error: no next line to merge");
1232 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
1235 MarkDirty(GetWidth());
1237 wxLayoutObject
*last
= NULL
;
1238 for(i
= list
.begin(); i
!= list
.end();)
1240 wxLayoutObject
*current
= *i
;
1242 // merge text objects together for efficiency
1243 if ( last
&& last
->GetType() == WXLO_TYPE_TEXT
&&
1244 current
->GetType() == WXLO_TYPE_TEXT
)
1246 wxLayoutObjectText
*textObj
= (wxLayoutObjectText
*)last
;
1247 wxString
text(textObj
->GetText());
1248 text
+= ((wxLayoutObjectText
*)current
)->GetText();
1249 textObj
->SetText(text
);
1251 list
.erase(i
); // remove and delete it
1255 // just append the object "as was"
1258 list
.remove(i
); // remove without deleting it
1261 wxASSERT(list
.empty());
1263 wxLayoutLine
*oldnext
= GetNextLine();
1264 wxLayoutLine
*nextLine
= oldnext
->GetNextLine();
1268 nextLine
->MoveLines(-1);
1272 // this is now done in Delete(), but if this function is ever called
1273 // from elsewhere, we might have to move refresh code back here (in
1274 // order not to duplicate it)
1276 wxPoint
pos(oldnext
->GetPosition());
1277 llist
->SetUpdateRect(pos
);
1278 llist
->SetUpdateRect(pos
.x
+ oldnext
->GetWidth() + MSW_CORRECTION
,
1279 pos
.y
+ oldnext
->GetHeight() + MSW_CORRECTION
);
1287 wxLayoutLine::GetWrapPosition(CoordType column
)
1290 wxLOiterator i
= FindObject(column
, &offset
);
1291 if(i
== NULLIT
) return -1; // cannot wrap
1293 // go backwards through the list and look for space in text objects
1296 if((**i
).GetType() == WXLO_TYPE_TEXT
)
1300 if( isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1307 }while(offset
!= -1);
1308 i
--; // move on to previous object
1312 column
-= (**i
).GetLength();
1316 offset
= (**i
).GetLength();
1317 }while(i
!= NULLIT
);
1318 /* If we reached the begin of the list and have more than one
1319 object, that one is longer than the margin, so break behind
1322 i
= m_ObjectList
.begin();
1323 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1325 pos
+= (**i
).GetLength();
1328 if(i
== NULLIT
) return -1; //why should this happen?
1329 pos
+= (**i
).GetLength();
1331 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1333 pos
+= (**i
).GetLength();
1336 if(i
== NULLIT
) return -1; //this is possible, if there is only one text object
1337 // now we are at the second text object:
1338 pos
-= (**i
).GetLength();
1339 return pos
; // in front of it
1343 #ifdef WXLAYOUT_DEBUG
1345 wxLayoutLine::Debug(void)
1348 wxPoint pos
= GetPosition();
1349 WXLO_DEBUG(("Line %ld, Pos (%ld,%ld), Height %ld",
1350 (long int) GetLineNumber(),
1351 (long int) pos
.x
, (long int) pos
.y
,
1352 (long int) GetHeight()));
1353 if(m_ObjectList
.begin() != NULLIT
)
1354 (**m_ObjectList
.begin()).Debug();
1360 wxLayoutLine::Copy(wxLayoutList
*llist
,
1364 CoordType firstOffset
, lastOffset
;
1366 if(to
== -1) to
= GetLength();
1367 if(from
== to
) return;
1369 wxLOiterator first
= FindObject(from
, &firstOffset
);
1370 wxLOiterator last
= FindObject(to
, &lastOffset
);
1372 // Common special case: only one object
1373 if( first
!= NULLIT
&& last
!= NULLIT
&& *first
== *last
)
1375 if( (**first
).GetType() == WXLO_TYPE_TEXT
)
1377 llist
->Insert(new wxLayoutObjectText(
1378 ((wxLayoutObjectText
1379 *)*first
)->GetText().substr(firstOffset
,
1380 lastOffset
-firstOffset
))
1384 else // what can we do?
1386 if(lastOffset
> firstOffset
) // i.e. +1 :-)
1387 llist
->Insert( (**first
).Copy() );
1392 // If we reach here, we can safely copy the whole first object from
1393 // the firstOffset position on:
1394 if((**first
).GetType() == WXLO_TYPE_TEXT
&& firstOffset
!= 0)
1396 llist
->Insert(new wxLayoutObjectText(
1397 ((wxLayoutObjectText
*)*first
)->GetText().substr(firstOffset
))
1400 else if(firstOffset
== 0)
1401 llist
->Insert( (**first
).Copy() );
1402 // else nothing to copy :-(
1404 // Now we copy all objects before the last one:
1405 wxLOiterator i
= first
; i
++;
1406 for( ; i
!= last
; i
++)
1407 llist
->Insert( (**i
).Copy() );
1409 // And now the last object:
1412 if( (**last
).GetType() == WXLO_TYPE_TEXT
)
1414 llist
->Insert(new wxLayoutObjectText(
1415 ((wxLayoutObjectText
*)*last
)->GetText().substr(0,lastOffset
))
1419 llist
->Insert( (**last
).Copy() );
1424 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1426 The wxLayoutList object
1428 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1430 wxLayoutList::wxLayoutList()
1432 #ifdef WXLAYOUT_USE_CARET
1434 #endif // WXLAYOUT_USE_CARET
1437 InvalidateUpdateRect();
1441 wxLayoutList::~wxLayoutList()
1444 m_FirstLine
->DeleteLine(false, this);
1448 wxLayoutList::Empty(void)
1451 m_FirstLine
= m_FirstLine
->DeleteLine(false, this);
1453 m_CursorPos
= wxPoint(0,0);
1454 m_CursorScreenPos
= wxPoint(0,0);
1455 m_CursorSize
= wxPoint(0,0);
1456 m_FirstLine
= new wxLayoutLine(NULL
, this); // empty first line
1457 m_CursorLine
= m_FirstLine
;
1458 InvalidateUpdateRect();
1463 wxLayoutList::InternalClear(void)
1466 m_Selection
.m_selecting
= false;
1467 m_Selection
.m_valid
= false;
1469 m_DefaultSetting
.family
= wxSWISS
;
1470 m_DefaultSetting
.size
= WXLO_DEFAULTFONTSIZE
;
1471 m_DefaultSetting
.style
= wxNORMAL
;
1472 m_DefaultSetting
.weight
= wxNORMAL
;
1473 m_DefaultSetting
.underline
= 0;
1474 m_DefaultSetting
.m_fg_valid
= TRUE
;
1475 m_DefaultSetting
.m_fg
= *wxBLACK
;
1476 m_DefaultSetting
.m_bg_valid
= TRUE
;
1477 m_DefaultSetting
.m_bg
= *wxWHITE
;
1479 m_CurrentSetting
= m_DefaultSetting
;
1483 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1484 int underline
, wxColour
*fg
,
1487 if(family
!= -1) m_CurrentSetting
.family
= family
;
1488 if(size
!= -1) m_CurrentSetting
.size
= size
;
1489 if(style
!= -1) m_CurrentSetting
.style
= style
;
1490 if(weight
!= -1) m_CurrentSetting
.weight
= weight
;
1491 if(underline
!= -1) m_CurrentSetting
.underline
= underline
!= 0;
1492 if(fg
) m_CurrentSetting
.m_fg
= *fg
;
1493 if(bg
) m_CurrentSetting
.m_bg
= *bg
;
1495 new wxLayoutObjectCmd(
1496 m_CurrentSetting
.family
,
1497 m_CurrentSetting
.size
,
1498 m_CurrentSetting
.style
,
1499 m_CurrentSetting
.weight
,
1500 m_CurrentSetting
.underline
,
1505 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1506 int underline
, char const *fg
, char const *bg
)
1514 cfg
= wxTheColourDatabase
->FindColour(fg
);
1516 cbg
= wxTheColourDatabase
->FindColour(bg
);
1518 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
1522 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
1523 int underline
, wxColour
*fg
, wxColour
*bg
)
1526 m_DefaultSetting
= wxLayoutStyleInfo(family
, size
, style
, weight
,
1528 m_CurrentSetting
= m_DefaultSetting
;
1532 wxLayoutList::FindText(const wxString
&needle
, const wxPoint
&cpos
) const
1537 for(line
= m_FirstLine
;
1539 line
= line
->GetNextLine())
1541 if(line
->GetLineNumber() >= cpos
.y
)
1543 xpos
= line
->FindText(needle
,
1544 (line
->GetLineNumber() == cpos
.y
) ?
1547 return wxPoint(xpos
, line
->GetLineNumber());
1550 return wxPoint(-1,-1);
1555 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
1557 AddCursorPosToUpdateRect();
1559 wxLayoutLine
*line
= m_FirstLine
;
1560 while(line
&& line
->GetLineNumber() != p
.y
)
1561 line
= line
->GetNextLine();
1562 if(line
&& line
->GetLineNumber() == p
.y
) // found it
1564 m_CursorPos
.y
= p
.y
;
1565 m_CursorLine
= line
;
1566 CoordType len
= line
->GetLength();
1569 m_CursorPos
.x
= p
.x
;
1574 m_CursorPos
.x
= len
;
1582 wxLayoutList::MoveCursorVertically(int n
)
1584 AddCursorPosToUpdateRect();
1587 if(n
< 0) // move up
1589 if(m_CursorLine
== m_FirstLine
) return false;
1590 while(n
< 0 && m_CursorLine
)
1592 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1598 m_CursorLine
= m_FirstLine
;
1604 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1605 m_CursorPos
.x
= m_CursorLine
->GetLength();
1611 wxLayoutLine
*last
= m_CursorLine
;
1612 if(! m_CursorLine
->GetNextLine()) return false;
1613 while(n
> 0 && m_CursorLine
)
1617 m_CursorLine
= m_CursorLine
->GetNextLine();
1621 m_CursorLine
= last
;
1627 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1628 m_CursorPos
.x
= m_CursorLine
->GetLength();
1636 wxLayoutList::MoveCursorHorizontally(int n
)
1638 AddCursorPosToUpdateRect();
1643 if(m_CursorPos
.x
== 0) // at begin of line
1645 if(! MoveCursorVertically(-1))
1647 MoveCursorToEndOfLine();
1652 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1653 m_CursorPos
.x
-= move
; n
+= move
;
1658 int len
= m_CursorLine
->GetLength();
1659 if(m_CursorPos
.x
== len
) // at end of line
1661 if(! MoveCursorVertically(1))
1663 MoveCursorToBeginOfLine();
1668 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1669 m_CursorPos
.x
+= move
;
1676 wxLayoutList::MoveCursorWord(int n
)
1678 wxCHECK_MSG( m_CursorLine
, false, "no current line" );
1679 wxCHECK_MSG( n
== -1 || n
== +1, false, "not implemented yet" );
1681 CoordType moveDistance
= 0;
1683 for ( wxLOiterator i
= m_CursorLine
->FindObject(m_CursorPos
.x
, &offset
);
1690 wxLayoutObject
*obj
= *i
;
1691 if( obj
->GetType() != WXLO_TYPE_TEXT
)
1693 // any non text objects count as one word
1696 moveDistance
+= obj
->GetLength();
1701 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)obj
;
1703 if ( offset
== tobj
->GetLength() )
1710 const char *start
= tobj
->GetText().c_str();
1711 const char *p
= start
+ offset
;
1713 // to the beginning/end of the next/prev word
1714 while ( isspace(*p
) )
1719 // go to the end/beginning of the word (in a broad sense...)
1720 while ( p
>= start
&& !isspace(*p
) )
1727 // now advance to the beginning of the next word
1728 while ( isspace(*p
) )
1734 moveDistance
= p
- start
- offset
;
1738 // except for the first iteration, offset is 0
1742 MoveCursorHorizontally(moveDistance
);
1748 wxLayoutList::Insert(wxString
const &text
)
1750 wxASSERT(m_CursorLine
);
1751 wxASSERT_MSG( text
.Find('\n') == wxNOT_FOUND
, "use wxLayoutImportText!" );
1756 AddCursorPosToUpdateRect();
1758 if ( !m_CursorLine
->Insert(m_CursorPos
.x
, text
) )
1761 m_CursorPos
.x
+= text
.Length();
1763 m_CursorLine
->RecalculatePositions(0, this);
1769 wxLayoutList::Insert(wxLayoutObject
*obj
)
1771 wxASSERT(m_CursorLine
);
1774 m_CursorLine
= GetFirstLine();
1776 AddCursorPosToUpdateRect();
1778 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
1779 m_CursorPos
.x
+= obj
->GetLength();
1781 m_CursorLine
->RecalculatePositions(0, this);
1787 wxLayoutList::Insert(wxLayoutList
*llist
)
1792 for(wxLayoutLine
*line
= llist
->GetFirstLine();
1794 line
= line
->GetNextLine()
1797 for(wxLOiterator i
= line
->GetFirstObject();
1807 wxLayoutList::LineBreak(void)
1809 wxASSERT(m_CursorLine
);
1810 bool setFirst
= (m_CursorLine
== m_FirstLine
&& m_CursorPos
.x
== 0);
1812 AddCursorPosToUpdateRect();
1814 wxPoint
position(m_CursorLine
->GetPosition());
1816 wxCoord width
= m_CursorLine
->GetWidth(),
1817 height
= m_CursorLine
->GetHeight();
1819 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
, this);
1820 if(setFirst
) // we were at beginning of first line
1821 m_FirstLine
= m_CursorLine
->GetPreviousLine();
1822 if(m_CursorPos
.x
!= 0)
1825 // doesn't help m_CursorLine.MarkDirty();
1827 wxLayoutLine
*prev
= m_CursorLine
->GetPreviousLine();
1828 wxCHECK_MSG(prev
, false, "just broke the line, where is the previous one?");
1830 height
+= prev
->GetHeight();
1832 SetUpdateRect(position
);
1833 SetUpdateRect(position
.x
+ width
+ MSW_CORRECTION
,
1834 position
.y
+ height
+ MSW_CORRECTION
);
1840 wxLayoutList::WrapLine(CoordType column
)
1842 if(m_CursorPos
.x
<= column
|| column
< 1)
1843 return false; // do nothing yet
1846 CoordType xpos
= m_CursorLine
->GetWrapPosition(column
);
1848 return false; // cannot break line
1850 CoordType newpos
= m_CursorPos
.x
- xpos
- 1;
1851 m_CursorPos
.x
= xpos
;
1853 AddCursorPosToUpdateRect();
1856 Delete(1); // delete the space
1857 m_CursorPos
.x
= newpos
;
1859 m_CursorLine
->RecalculatePositions(1, this);
1866 wxLayoutList::Delete(CoordType npos
)
1868 wxCHECK_MSG(m_CursorLine
, false, "can't delete in non existing line");
1869 wxASSERT_MSG(npos
> 0, "nothing to delete?");
1871 AddCursorPosToUpdateRect();
1873 // were other lines appended to this one (this is important to know because
1874 // this means that our width _increased_ as the result of deletion)
1875 bool wasMerged
= false;
1877 // the size of the region to update
1878 CoordType totalHeight
= m_CursorLine
->GetHeight(),
1879 totalWidth
= m_CursorLine
->GetWidth();
1884 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
1888 // More to delete, continue on next line.
1890 // First, check if line is empty:
1891 if(m_CursorLine
->GetLength() == 0)
1893 // in this case, updating could probably be optimised
1895 wxASSERT(DeleteLines(1) == 0);
1904 // Need to join next line
1905 if(! m_CursorLine
->GetNextLine())
1910 wxLayoutLine
*next
= m_CursorLine
->GetNextLine();
1913 totalHeight
+= next
->GetHeight();
1914 totalWidth
+= next
->GetWidth();
1916 m_CursorLine
->MergeNextLine(this);
1921 wxFAIL_MSG("can't delete all this");
1931 // we need to update the whole tail of the line and the lines which
1935 wxPoint
position(m_CursorLine
->GetPosition());
1936 SetUpdateRect(position
);
1937 SetUpdateRect(position
.x
+ totalWidth
+ MSW_CORRECTION
,
1938 position
.y
+ totalHeight
+ MSW_CORRECTION
);
1945 wxLayoutList::DeleteLines(int n
)
1947 wxASSERT(m_CursorLine
);
1950 AddCursorPosToUpdateRect();
1954 if(!m_CursorLine
->GetNextLine())
1955 { // we cannot delete this line, but we can clear it
1956 MoveCursorToBeginOfLine();
1957 DeleteToEndOfLine();
1958 m_CursorLine
->RecalculatePositions(2, this);
1962 line
= m_CursorLine
;
1963 m_CursorLine
= m_CursorLine
->DeleteLine(true, this);
1965 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
1966 wxASSERT(m_FirstLine
);
1967 wxASSERT(m_CursorLine
);
1969 m_CursorLine
->RecalculatePositions(2, this);
1974 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
)
1976 wxLayoutLine
*line
= m_FirstLine
;
1978 // first, make sure everything is calculated - this might not be
1979 // needed, optimise it later
1980 ApplyStyle(&m_DefaultSetting
, dc
);
1983 line
->RecalculatePosition(this); // so we don't need to do it all the time
1984 // little condition to speed up redrawing:
1985 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1986 line
= line
->GetNextLine();
1991 wxLayoutList::UpdateCursorScreenPos(wxDC
&dc
)
1993 wxASSERT(m_CursorLine
);
1994 m_CursorLine
->Layout(dc
, this, (wxPoint
*)&m_CursorScreenPos
, (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
1998 wxLayoutList::GetCursorScreenPos(wxDC
&dc
)
2000 UpdateCursorScreenPos(dc
);
2001 return m_CursorScreenPos
;
2005 Is called before each Draw(). Now, it will re-layout all lines which
2009 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
, bool forceAll
)
2011 // first, make sure everything is calculated - this might not be
2012 // needed, optimise it later
2013 ApplyStyle(&m_DefaultSetting
, dc
);
2015 // FIXME this is completely wrong - we should start by first *visible* line
2016 // (and stop on the last one) instead of looping over all lines!!
2017 wxLayoutLine
*line
= m_FirstLine
;
2020 if(forceAll
|| line
->IsDirty())
2022 line
->GetStyleInfo() = m_CurrentSetting
;
2023 if(line
== m_CursorLine
)
2024 line
->Layout(dc
, this, (wxPoint
*)&m_CursorScreenPos
,
2025 (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
2027 line
->Layout(dc
, this);
2029 // little condition to speed up redrawing:
2030 if(bottom
!= -1 && line
->GetPosition().y
> bottom
)
2034 line
->RecalculatePositions(1, this);
2035 line
= line
->GetNextLine();
2038 ///FIXME: disabled for now
2040 // can only be 0 if we are on the first line and have no next line
2041 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
2042 m_CursorLine
->GetNextLine() == NULL
&&
2043 m_CursorLine
== m_FirstLine
));
2045 AddCursorPosToUpdateRect();
2049 wxLayoutList::Draw(wxDC
&dc
,
2050 wxPoint
const &offset
,
2054 wxLayoutLine
*line
= m_FirstLine
;
2057 ApplyStyle(&m_DefaultSetting
, dc
);
2058 wxBrush
brush(m_CurrentSetting
.m_bg
, wxSOLID
);
2060 dc
.SetBackgroundMode(wxTRANSPARENT
);
2064 // only draw if between top and bottom:
2065 if((top
== -1 || line
->GetPosition().y
+ line
->GetHeight() >= top
))
2066 line
->Draw(dc
, this, offset
);
2068 line
->Layout(dc
, this);
2069 // little condition to speed up redrawing:
2070 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
2071 line
= line
->GetNextLine();
2073 InvalidateUpdateRect();
2075 WXLO_DEBUG(("Selection is %s : l%d,%ld/%ld,%ld",
2076 m_Selection
.m_valid
? "valid" : "invalid",
2077 m_Selection
.m_CursorA
.x
, m_Selection
.m_CursorA
.y
,
2078 m_Selection
.m_CursorB
.x
, m_Selection
.m_CursorB
.y
));
2082 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
,
2086 // First, find the right line:
2087 wxLayoutLine
*line
= m_FirstLine
;
2090 ApplyStyle(&m_DefaultSetting
, dc
);
2093 p
= line
->GetPosition();
2094 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
2097 // we need to run a layout here to get font sizes right :-(
2099 // VZ: we can't call Layout() from here because it marks the line as
2100 // clean and it is not refreshed when it's called from wxLayoutList::
2101 // Layout() - if we really need to do this, we should introduce an
2102 // extra argument to Layout() to prevent the line from MarkClean()ing
2104 line
->Layout(dc
, this);
2106 line
= line
->GetNextLine();
2110 if(found
) *found
= false;
2111 return NULL
; // not found
2113 if(cursorPos
) cursorPos
->y
= line
->GetLineNumber();
2114 // Now, find the object in the line:
2115 wxLOiterator i
= line
->FindObjectScreen(dc
, pos
.x
,
2116 cursorPos
? & cursorPos
->x
: NULL
,
2118 return (i
== NULLIT
) ? NULL
: *i
;
2123 wxLayoutList::GetSize(void) const
2126 *line
= m_FirstLine
,
2129 return wxPoint(0,0);
2131 wxPoint
maxPoint(0,0);
2136 if(line
->GetWidth() > maxPoint
.x
)
2137 maxPoint
.x
= line
->GetWidth();
2139 line
= line
->GetNextLine();
2142 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
2148 wxLayoutList::DrawCursor(wxDC
&dc
, bool active
, wxPoint
const &translate
)
2150 wxPoint
coords(m_CursorScreenPos
);
2151 coords
+= translate
;
2153 #ifdef WXLAYOUT_DEBUG
2154 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
2155 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
2156 (long)coords
.x
, (long)coords
.y
,
2157 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
2158 (long)m_CursorLine
->GetLineNumber(),
2159 (long)m_CursorLine
->GetLength()));
2161 wxLogStatus("Cursor is at (%d, %d)", m_CursorPos
.x
, m_CursorPos
.y
);
2164 #ifdef WXLAYOUT_USE_CARET
2165 m_caret
->Move(coords
);
2166 #else // !WXLAYOUT_USE_CARET
2167 dc
.SetBrush(*wxBLACK_BRUSH
);
2168 dc
.SetLogicalFunction(wxXOR
);
2169 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
2172 dc
.DrawRectangle(coords
.x
, coords
.y
,
2173 m_CursorSize
.x
, m_CursorSize
.y
);
2174 SetUpdateRect(coords
.x
, coords
.y
);
2175 SetUpdateRect(coords
.x
+m_CursorSize
.x
, coords
.y
+m_CursorSize
.y
);
2179 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
2180 coords
.x
, coords
.y
);
2181 SetUpdateRect(coords
.x
, coords
.y
+m_CursorSize
.y
-1);
2182 SetUpdateRect(coords
.x
, coords
.y
);
2184 dc
.SetLogicalFunction(wxCOPY
);
2185 //dc.SetBrush(wxNullBrush);
2186 #endif // WXLAYOUT_USE_CARET/!WXLAYOUT_USE_CARET
2190 wxLayoutList::SetUpdateRect(CoordType x
, CoordType y
)
2192 if(m_UpdateRectValid
)
2193 GrowRect(m_UpdateRect
, x
, y
);
2198 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
2199 m_UpdateRect
.height
= 4;// wxGTK :-)
2200 m_UpdateRectValid
= true;
2205 wxLayoutList::StartSelection(wxPoint cpos
)
2209 WXLO_DEBUG(("Starting selection at %ld/%ld", cpos
.x
, cpos
.y
));
2210 m_Selection
.m_CursorA
= cpos
;
2211 m_Selection
.m_CursorB
= cpos
;
2212 m_Selection
.m_selecting
= true;
2213 m_Selection
.m_valid
= false;
2217 wxLayoutList::ContinueSelection(wxPoint cpos
)
2221 wxASSERT(m_Selection
.m_selecting
== true);
2222 wxASSERT(m_Selection
.m_valid
== false);
2223 WXLO_DEBUG(("Continuing selection at %ld/%ld", cpos
.x
, cpos
.y
));
2224 if(m_Selection
.m_CursorB
<= cpos
)
2225 m_Selection
.m_CursorB
= cpos
;
2227 m_Selection
.m_CursorA
= cpos
;
2228 // We always want m_CursorA <= m_CursorB!
2229 if(! (m_Selection
.m_CursorA
<= m_Selection
.m_CursorB
))
2231 wxPoint help
= m_Selection
.m_CursorB
;
2232 m_Selection
.m_CursorB
= m_Selection
.m_CursorA
;
2233 m_Selection
.m_CursorA
= help
;
2238 wxLayoutList::EndSelection(wxPoint cpos
)
2242 ContinueSelection(cpos
);
2243 WXLO_DEBUG(("Ending selection at %ld/%ld", cpos
.x
, cpos
.y
));
2244 m_Selection
.m_selecting
= false;
2245 m_Selection
.m_valid
= true;
2250 wxLayoutList::IsSelecting(void)
2252 return m_Selection
.m_selecting
;
2256 wxLayoutList::IsSelected(const wxPoint
&cursor
)
2258 if(! m_Selection
.m_valid
&& ! m_Selection
.m_selecting
)
2260 return m_Selection
.m_CursorA
<= cursor
2261 && cursor
<= m_Selection
.m_CursorB
;
2265 /** Tests whether this layout line is selected and needs
2267 @param line to test for
2268 @return 0 = not selected, 1 = fully selected, -1 = partially
2272 wxLayoutList::IsSelected(const wxLayoutLine
*line
, CoordType
*from
,
2275 wxASSERT(line
); wxASSERT(to
); wxASSERT(from
);
2277 if(! m_Selection
.m_valid
&& ! m_Selection
.m_selecting
)
2280 CoordType y
= line
->GetLineNumber();
2281 if(m_Selection
.m_CursorA
.y
< y
&& m_Selection
.m_CursorB
.y
> y
)
2283 else if(m_Selection
.m_CursorA
.y
== y
)
2285 *from
= m_Selection
.m_CursorA
.x
;
2286 if(m_Selection
.m_CursorB
.y
== y
)
2287 *to
= m_Selection
.m_CursorB
.x
;
2289 *to
= line
->GetLength();
2292 else if(m_Selection
.m_CursorB
.y
== y
)
2294 *to
= m_Selection
.m_CursorB
.x
;
2295 if(m_Selection
.m_CursorA
.y
== y
)
2296 *from
= m_Selection
.m_CursorA
.x
;
2306 wxLayoutList::DeleteSelection(void)
2308 if(! m_Selection
.m_valid
)
2311 m_Selection
.m_valid
= false;
2313 // Only delete part of the current line?
2314 if(m_Selection
.m_CursorA
.y
== m_Selection
.m_CursorB
.y
)
2316 MoveCursorTo(m_Selection
.m_CursorA
);
2317 Delete(m_Selection
.m_CursorB
.x
- m_Selection
.m_CursorA
.x
);
2326 for(firstLine
= m_FirstLine
;
2327 firstLine
&& firstLine
->GetLineNumber() < m_Selection
.m_CursorA
.y
;
2328 firstLine
=firstLine
->GetNextLine())
2330 if(!firstLine
|| firstLine
->GetLineNumber() != m_Selection
.m_CursorA
.y
)
2334 for(lastLine
= m_FirstLine
;
2335 lastLine
&& lastLine
->GetLineNumber() < m_Selection
.m_CursorB
.y
;
2336 lastLine
=lastLine
->GetNextLine())
2338 if(!lastLine
|| lastLine
->GetLineNumber() != m_Selection
.m_CursorB
.y
)
2342 // We now know that the two lines are different:
2344 // First, delete what's left of this line:
2345 MoveCursorTo(m_Selection
.m_CursorA
);
2346 DeleteToEndOfLine();
2348 wxLayoutLine
*nextLine
= firstLine
->GetNextLine();
2349 while(nextLine
&& nextLine
!= lastLine
)
2350 nextLine
= nextLine
->DeleteLine(false, this);
2352 // Now nextLine = lastLine;
2353 Delete(1); // This joins firstLine and nextLine
2354 Delete(m_Selection
.m_CursorB
.x
); // This deletes the first x
2358 firstLine
->RecalculatePositions(1, this);
2361 /// Starts highlighting the selection
2363 wxLayoutList::StartHighlighting(wxDC
&dc
)
2366 dc
.SetTextForeground(m_CurrentSetting
.m_bg
);
2367 dc
.SetTextBackground(m_CurrentSetting
.m_fg
);
2368 dc
.SetBackgroundMode(wxSOLID
);
2372 /// Ends highlighting the selection
2374 wxLayoutList::EndHighlighting(wxDC
&dc
)
2377 dc
.SetTextForeground(m_CurrentSetting
.m_fg
);
2378 dc
.SetTextBackground(m_CurrentSetting
.m_bg
);
2379 dc
.SetBackgroundMode(wxTRANSPARENT
);
2385 wxLayoutList::Copy(const wxPoint
&from
,
2392 for(firstLine
= m_FirstLine
;
2393 firstLine
&& firstLine
->GetLineNumber() < from
.y
;
2394 firstLine
=firstLine
->GetNextLine())
2396 if(!firstLine
|| firstLine
->GetLineNumber() != from
.y
)
2399 for(lastLine
= m_FirstLine
;
2400 lastLine
&& lastLine
->GetLineNumber() < to
.y
;
2401 lastLine
=lastLine
->GetNextLine())
2403 if(!lastLine
|| lastLine
->GetLineNumber() != to
.y
)
2408 wxLayoutLine
*tmp
= firstLine
;
2409 firstLine
= lastLine
;
2413 wxLayoutList
*llist
= new wxLayoutList();
2415 if(firstLine
== lastLine
)
2417 firstLine
->Copy(llist
, from
.x
, to
.x
);
2421 // Extract objects from first line
2422 firstLine
->Copy(llist
, from
.x
);
2424 // Extract all lines between
2425 for(wxLayoutLine
*line
= firstLine
->GetNextLine();
2427 line
= line
->GetNextLine())
2432 // Extract objects from last line
2433 lastLine
->Copy(llist
, 0, to
.x
);
2439 wxLayoutList::GetSelection(wxLayoutDataObject
*wxlo
, bool invalidate
)
2441 if(! m_Selection
.m_valid
)
2443 if(m_Selection
.m_selecting
)
2449 if(invalidate
) m_Selection
.m_valid
= false;
2451 wxLayoutList
*llist
= Copy( m_Selection
.m_CursorA
,
2452 m_Selection
.m_CursorB
);
2454 if(llist
&& wxlo
) // export as data object, too
2458 wxLayoutExportObject
*export
;
2459 wxLayoutExportStatus
status(llist
);
2460 while((export
= wxLayoutExport( &status
, WXLO_EXPORT_AS_OBJECTS
)) != NULL
)
2462 if(export
->type
== WXLO_EXPORT_EMPTYLINE
)
2463 ; //FIXME missing support for linebreaks in string format
2465 export
->content
.object
->Write(string
);
2469 wxlo
->SetData(string
.c_str(), string
.Length()+1);
2476 #define COPY_SI(what) if(si->what != -1) { m_CurrentSetting.what = si->what; fontChanged = TRUE; }
2479 wxLayoutList::ApplyStyle(wxLayoutStyleInfo
*si
, wxDC
&dc
)
2481 bool fontChanged
= FALSE
;
2488 dc
.SetFont( m_FontCache
.GetFont(m_CurrentSetting
) );
2492 m_CurrentSetting
.m_fg
= si
->m_fg
;
2493 dc
.SetTextForeground(m_CurrentSetting
.m_fg
);
2497 m_CurrentSetting
.m_bg
= si
->m_bg
;
2498 dc
.SetTextBackground(m_CurrentSetting
.m_bg
);
2503 #ifdef WXLAYOUT_DEBUG
2506 wxLayoutList::Debug(void)
2511 for(line
= m_FirstLine
;
2513 line
= line
->GetNextLine())
2520 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2524 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2526 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
2527 wxString
const & title
)
2532 // remove any highlighting which could interfere with printing:
2533 m_llist
->StartSelection();
2534 m_llist
->EndSelection();
2537 wxLayoutPrintout::~wxLayoutPrintout()
2542 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
2544 // The following bit is taken from the printing sample, let's see
2545 // whether it works for us.
2547 /* You might use THIS code to set the printer DC to ROUGHLY reflect
2548 * the screen text size. This page also draws lines of actual length 5cm
2551 // Get the logical pixels per inch of screen and printer
2552 int ppiScreenX
, ppiScreenY
;
2553 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
2554 int ppiPrinterX
, ppiPrinterY
;
2555 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
2557 if(ppiScreenX
== 0) // not yet set, need to guess
2562 if(ppiPrinterX
== 0) // not yet set, need to guess
2568 // This scales the DC so that the printout roughly represents the
2569 // the screen scaling. The text point size _should_ be the right size
2570 // but in fact is too small for some reason. This is a detail that will
2571 // need to be addressed at some point but can be fudged for the
2573 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
2575 // Now we have to check in case our real page size is reduced
2576 // (e.g. because we're drawing to a print preview memory DC)
2577 int pageWidth
, pageHeight
;
2579 dc
->GetSize(&w
, &h
);
2580 GetPageSizePixels(&pageWidth
, &pageHeight
);
2581 if(pageWidth
!= 0) // doesn't work always
2583 // If printer pageWidth == current DC width, then this doesn't
2584 // change. But w might be the preview bitmap width, so scale down.
2585 scale
= scale
* (float)(w
/(float)pageWidth
);
2587 dc
->SetUserScale(scale
, scale
);
2591 bool wxLayoutPrintout::OnPrintPage(int page
)
2600 top
= (page
- 1)*m_PrintoutHeight
;
2601 bottom
= top
+ m_PrintoutHeight
;
2602 // SetDeviceOrigin() doesn't work here, so we need to manually
2603 // translate all coordinates.
2604 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
2605 m_llist
->Draw(*dc
, translate
, top
, bottom
);
2612 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
2614 /* We allocate a temporary wxDC for printing, so that we can
2615 determine the correct paper size and scaling. We don't actually
2616 print anything on it. */
2618 wxPrinterDC
psdc("","",WXLLIST_TEMPFILE
,false);
2620 wxPostScriptDC
psdc(WXLLIST_TEMPFILE
,false);
2623 float scale
= ScaleDC(&psdc
);
2625 psdc
.GetSize(&m_PageWidth
, &m_PageHeight
);
2626 // This sets a left/top origin of 15% and 20%:
2627 m_Offset
= wxPoint((15*m_PageWidth
)/100, m_PageHeight
/20);
2629 // This is the length of the printable area.
2630 m_PrintoutHeight
= m_PageHeight
- (int) (m_PageHeight
* 0.15);
2631 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
2635 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
2638 *maxPage
= m_NumOfPages
;
2641 *selPageTo
= m_NumOfPages
;
2642 wxRemoveFile(WXLLIST_TEMPFILE
);
2645 bool wxLayoutPrintout::HasPage(int pageNum
)
2647 return pageNum
<= m_NumOfPages
;
2651 Stupid wxWindows doesn't draw proper ellipses, so we comment this
2652 out. It's a waste of paper anyway.
2656 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
2657 wxPoint topleft
, wxPoint bottomright
,
2660 // make backups of all essential parameters
2661 const wxBrush
& brush
= dc
.GetBrush();
2662 const wxPen
& pen
= dc
.GetPen();
2663 const wxFont
& font
= dc
.GetFont();
2665 dc
.SetBrush(*wxWHITE_BRUSH
);
2666 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
2667 dc
.DrawRoundedRectangle(topleft
.x
,
2668 topleft
.y
,bottomright
.x
-topleft
.x
,
2669 bottomright
.y
-topleft
.y
);
2670 dc
.SetBrush(*wxBLACK_BRUSH
);
2671 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
2672 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
2676 page
= "9999/9999 "; // many pages...
2678 dc
.GetTextExtent(page
,&w
,&h
);
2679 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
2680 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
2681 dc
.GetTextExtent("XXXX", &w
,&h
);
2682 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);
2693 wxFontCache::GetFont(int family
, int size
, int style
, int weight
,
2696 for(wxFCEList::iterator i
= m_FontList
.begin();
2697 i
!= m_FontList
.end(); i
++)
2698 if( (**i
).Matches(family
, size
, style
, weight
, underline
) )
2699 return (**i
).GetFont();
2701 wxFontCacheEntry
*fce
= new wxFontCacheEntry(family
, size
, style
,
2703 m_FontList
.push_back(fce
);
2704 return fce
->GetFont();