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
);
940 wxASSERT(0); // we should never arrive here
944 wxLayoutLine::DeleteLine(bool update
, wxLayoutList
*llist
)
946 if(m_Next
) m_Next
->m_Previous
= m_Previous
;
947 if(m_Previous
) m_Previous
->m_Next
= m_Next
;
950 m_Next
->MoveLines(-1);
951 m_Next
->RecalculatePositions(1, llist
);
953 wxLayoutLine
*next
= m_Next
;
959 wxLayoutLine::Draw(wxDC
&dc
,
961 const wxPoint
& offset
) const
963 wxLayoutObjectList::iterator i
;
964 wxPoint pos
= offset
;
965 pos
= pos
+ GetPosition();
969 CoordType xpos
= 0; // cursorpos, lenght of line
971 CoordType from
, to
, tempto
;
972 //FIXME This doesn't work yet, needs updating afterr default
973 //settings for list or a wxLayoutObjectCmd have changed:
974 //llist->ApplyStyle(&((wxLayoutLine *)this)->m_StyleInfo, dc);
975 int highlight
= llist
->IsSelected(this, &from
, &to
);
976 // WXLO_DEBUG(("highlight=%d", highlight ));
977 if(highlight
== 1) // we need to draw the whole line inverted!
978 llist
->StartHighlighting(dc
);
980 llist
->EndHighlighting(dc
);
982 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
984 if(highlight
== -1) // partially highlight line
986 // parts of the line need highlighting
987 tempto
= xpos
+(**i
).GetLength();
988 (**i
).Draw(dc
, pos
, llist
, from
-xpos
, to
-xpos
);
991 (**i
).Draw(dc
, pos
, llist
);
992 pos
.x
+= (**i
).GetWidth();
993 xpos
+= (**i
).GetLength();
998 wxLayoutLine::Layout(wxDC
&dc
,
1001 wxPoint
*cursorSize
,
1004 wxLayoutObjectList::iterator i
;
1006 // when a line becomes dirty, we redraw it from the place where it was
1007 // changed till the end of line (because the following wxLayoutObjects are
1008 // moved when the preceding one changes) - calculate the update rectangle.
1009 CoordType updateTop
= m_Position
.y
,
1011 updateWidth
= m_Width
,
1012 updateHeight
= m_Height
;
1016 bottomHeight
= 0; // above and below baseline
1018 objTopHeight
, objBottomHeight
; // above and below baseline
1022 CoordType heightOld
= m_Height
;
1028 bool cursorFound
= false;
1032 *cursorPos
= m_Position
;
1033 if(cursorSize
) *cursorSize
= wxPoint(0,0);
1036 //FIXME This doesn't work yet, needs updating afterr default
1037 //settings for list or a wxLayoutObjectCmd have changed:
1038 //llist->ApplyStyle(&m_StyleInfo, dc);
1039 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
1041 wxLayoutObject
*obj
= *i
;
1042 obj
->Layout(dc
, llist
);
1043 wxPoint sizeObj
= obj
->GetSize(&objTopHeight
, &objBottomHeight
);
1045 if(cursorPos
&& ! cursorFound
)
1047 // we need to check whether the text cursor is here
1048 len
= obj
->GetLength();
1049 if(count
<= cx
&& count
+len
> cx
)
1051 if(obj
->GetType() == WXLO_TYPE_TEXT
)
1053 len
= cx
- count
; // pos in object
1054 CoordType width
, height
, descent
;
1055 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
1056 &width
, &height
, &descent
);
1057 cursorPos
->x
+= width
;
1058 cursorPos
->y
= m_Position
.y
;
1060 if(len
< obj
->GetLength())
1061 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
1063 str
= WXLO_CURSORCHAR
;
1064 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
1065 wxASSERT(cursorSize
);
1066 // Just in case some joker inserted an empty string object:
1067 if(width
== 0) width
= WXLO_MINIMUM_CURSOR_WIDTH
;
1068 if(height
== 0) height
= sizeObj
.y
;
1069 cursorSize
->x
= width
;
1070 cursorSize
->y
= height
;
1071 cursorFound
= true; // no more checks
1075 // on some other object
1076 CoordType top
, bottom
; // unused
1077 *cursorSize
= obj
->GetSize(&top
,&bottom
);
1078 cursorPos
->y
= m_Position
.y
;
1079 cursorFound
= true; // no more checks
1085 cursorPos
->x
+= obj
->GetWidth();
1089 m_Width
+= sizeObj
.x
;
1090 if(sizeObj
.y
> m_Height
)
1092 m_Height
= sizeObj
.y
;
1095 if(objTopHeight
> topHeight
)
1096 topHeight
= objTopHeight
;
1097 if(objBottomHeight
> bottomHeight
)
1098 bottomHeight
= objBottomHeight
;
1103 if ( updateHeight
< m_Height
)
1104 updateHeight
= m_Height
;
1105 if ( updateWidth
< m_Width
)
1106 updateWidth
= m_Width
;
1108 // update all line if we don't know where to start from
1109 if ( updateLeft
== -1 )
1112 llist
->SetUpdateRect(updateLeft
, updateTop
);
1113 llist
->SetUpdateRect(updateLeft
+ updateWidth
+ MSW_CORRECTION
,
1114 updateTop
+ updateHeight
+ MSW_CORRECTION
);
1117 if(topHeight
+ bottomHeight
> m_Height
)
1119 m_Height
= topHeight
+bottomHeight
;
1122 m_BaseLine
= topHeight
;
1126 CoordType width
, height
, descent
;
1127 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1129 m_BaseLine
= m_Height
- descent
;
1132 // tell next line about coordinate change
1133 if(m_Next
&& m_Height
!= heightOld
)
1135 // FIXME isn't this done in RecalculatePositions() below anyhow?
1136 m_Next
->RecalculatePositions(0, llist
);
1139 // We need to check whether we found a valid cursor size:
1142 // this might be the case if the cursor is at the end of the
1143 // line or on a command object:
1144 if(cursorSize
->y
< WXLO_MINIMUM_CURSOR_WIDTH
)
1146 CoordType width
, height
, descent
;
1147 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1148 cursorSize
->x
= width
;
1149 cursorSize
->y
= height
;
1151 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
1152 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
1155 RecalculatePositions(1, llist
);
1162 wxLayoutLine::Break(CoordType xpos
, wxLayoutList
*llist
)
1164 wxASSERT(xpos
>= 0);
1168 /* If we are at the begin of a line, we want to move all other
1169 lines down and stay with the cursor where we are. However, if we
1170 are in an empty line, we want to move down with it. */
1171 if(xpos
== 0 && GetLength() > 0)
1172 { // insert an empty line before this one
1173 wxLayoutLine
*prev
= new wxLayoutLine(m_Previous
, llist
);
1174 if(m_Previous
== NULL
)
1175 { // We were in first line, need to link in new empty line
1177 prev
->m_Next
= this;
1179 m_Previous
->m_Height
= 0; // this is a wild guess
1182 m_Next
->RecalculatePositions(1, llist
);
1187 wxLOiterator i
= FindObject(xpos
, &offset
);
1189 // must be at the end of the line then
1190 return new wxLayoutLine(this, llist
);
1193 wxLayoutLine
*newLine
= new wxLayoutLine(this, llist
);
1194 // split object at i:
1195 if((**i
).GetType() == WXLO_TYPE_TEXT
&& offset
!= 0)
1197 wxString left
, right
;
1198 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
1199 left
= tobj
->GetText().substr(0,offset
);
1200 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
1201 // current text object gets set to left half
1202 tobj
->GetText() = left
; // set new text
1203 newLine
->Append(new wxLayoutObjectText(right
));
1204 m_Length
-= right
.Length();
1205 i
++; // don't move this object to the new list
1210 i
++; // move objects from here to new list
1213 while(i
!= m_ObjectList
.end())
1215 wxLayoutObject
*obj
= *i
;
1216 newLine
->Append(obj
);
1217 m_Length
-= obj
->GetLength();
1219 m_ObjectList
.remove(i
); // remove without deleting it
1222 m_Next
->RecalculatePositions(2, llist
);
1228 wxLayoutLine::MergeNextLine(wxLayoutList
*llist
)
1230 wxCHECK_RET(GetNextLine(),"wxLayout internal error: no next line to merge");
1231 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
1234 MarkDirty(GetWidth());
1236 wxLayoutObject
*last
= NULL
;
1237 for(i
= list
.begin(); i
!= list
.end();)
1239 wxLayoutObject
*current
= *i
;
1241 // merge text objects together for efficiency
1242 if ( last
&& last
->GetType() == WXLO_TYPE_TEXT
&&
1243 current
->GetType() == WXLO_TYPE_TEXT
)
1245 wxLayoutObjectText
*textObj
= (wxLayoutObjectText
*)last
;
1246 wxString
text(textObj
->GetText());
1247 text
+= ((wxLayoutObjectText
*)current
)->GetText();
1248 textObj
->SetText(text
);
1250 list
.erase(i
); // remove and delete it
1254 // just append the object "as was"
1257 list
.remove(i
); // remove without deleting it
1260 wxASSERT(list
.empty());
1262 wxLayoutLine
*oldnext
= GetNextLine();
1263 wxLayoutLine
*nextLine
= oldnext
->GetNextLine();
1267 nextLine
->MoveLines(-1);
1271 // this is now done in Delete(), but if this function is ever called
1272 // from elsewhere, we might have to move refresh code back here (in
1273 // order not to duplicate it)
1275 wxPoint
pos(oldnext
->GetPosition());
1276 llist
->SetUpdateRect(pos
);
1277 llist
->SetUpdateRect(pos
.x
+ oldnext
->GetWidth() + MSW_CORRECTION
,
1278 pos
.y
+ oldnext
->GetHeight() + MSW_CORRECTION
);
1286 wxLayoutLine::GetWrapPosition(CoordType column
)
1289 wxLOiterator i
= FindObject(column
, &offset
);
1290 if(i
== NULLIT
) return -1; // cannot wrap
1292 // go backwards through the list and look for space in text objects
1295 if((**i
).GetType() == WXLO_TYPE_TEXT
)
1299 if( isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1306 }while(offset
!= -1);
1307 i
--; // move on to previous object
1311 column
-= (**i
).GetLength();
1315 offset
= (**i
).GetLength();
1316 }while(i
!= NULLIT
);
1317 /* If we reached the begin of the list and have more than one
1318 object, that one is longer than the margin, so break behind
1321 i
= m_ObjectList
.begin();
1322 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1324 pos
+= (**i
).GetLength();
1327 if(i
== NULLIT
) return -1; //why should this happen?
1328 pos
+= (**i
).GetLength();
1330 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1332 pos
+= (**i
).GetLength();
1335 if(i
== NULLIT
) return -1; //this is possible, if there is only one text object
1336 // now we are at the second text object:
1337 pos
-= (**i
).GetLength();
1338 return pos
; // in front of it
1342 #ifdef WXLAYOUT_DEBUG
1344 wxLayoutLine::Debug(void)
1347 wxPoint pos
= GetPosition();
1348 WXLO_DEBUG(("Line %ld, Pos (%ld,%ld), Height %ld",
1349 (long int) GetLineNumber(),
1350 (long int) pos
.x
, (long int) pos
.y
,
1351 (long int) GetHeight()));
1352 if(m_ObjectList
.begin() != NULLIT
)
1353 (**m_ObjectList
.begin()).Debug();
1359 wxLayoutLine::Copy(wxLayoutList
*llist
,
1363 CoordType firstOffset
, lastOffset
;
1365 if(to
== -1) to
= GetLength();
1366 if(from
== to
) return;
1368 wxLOiterator first
= FindObject(from
, &firstOffset
);
1369 wxLOiterator last
= FindObject(to
, &lastOffset
);
1371 // Common special case: only one object
1372 if( first
!= NULLIT
&& last
!= NULLIT
&& *first
== *last
)
1374 if( (**first
).GetType() == WXLO_TYPE_TEXT
)
1376 llist
->Insert(new wxLayoutObjectText(
1377 ((wxLayoutObjectText
1378 *)*first
)->GetText().substr(firstOffset
,
1379 lastOffset
-firstOffset
))
1383 else // what can we do?
1385 if(lastOffset
> firstOffset
) // i.e. +1 :-)
1386 llist
->Insert( (**first
).Copy() );
1391 // If we reach here, we can safely copy the whole first object from
1392 // the firstOffset position on:
1393 if((**first
).GetType() == WXLO_TYPE_TEXT
&& firstOffset
!= 0)
1395 llist
->Insert(new wxLayoutObjectText(
1396 ((wxLayoutObjectText
*)*first
)->GetText().substr(firstOffset
))
1399 else if(firstOffset
== 0)
1400 llist
->Insert( (**first
).Copy() );
1401 // else nothing to copy :-(
1403 // Now we copy all objects before the last one:
1404 wxLOiterator i
= first
; i
++;
1405 for( ; i
!= last
; i
++)
1406 llist
->Insert( (**i
).Copy() );
1408 // And now the last object:
1411 if( (**last
).GetType() == WXLO_TYPE_TEXT
)
1413 llist
->Insert(new wxLayoutObjectText(
1414 ((wxLayoutObjectText
*)*last
)->GetText().substr(0,lastOffset
))
1418 llist
->Insert( (**last
).Copy() );
1423 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1425 The wxLayoutList object
1427 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1429 wxLayoutList::wxLayoutList()
1431 #ifdef WXLAYOUT_USE_CARET
1433 #endif // WXLAYOUT_USE_CARET
1436 InvalidateUpdateRect();
1440 wxLayoutList::~wxLayoutList()
1443 m_FirstLine
->DeleteLine(false, this);
1447 wxLayoutList::Empty(void)
1450 m_FirstLine
= m_FirstLine
->DeleteLine(false, this);
1452 m_CursorPos
= wxPoint(0,0);
1453 m_CursorScreenPos
= wxPoint(0,0);
1454 m_CursorSize
= wxPoint(0,0);
1455 m_FirstLine
= new wxLayoutLine(NULL
, this); // empty first line
1456 m_CursorLine
= m_FirstLine
;
1457 InvalidateUpdateRect();
1462 wxLayoutList::InternalClear(void)
1465 m_Selection
.m_selecting
= false;
1466 m_Selection
.m_valid
= false;
1468 m_DefaultSetting
.family
= wxSWISS
;
1469 m_DefaultSetting
.size
= WXLO_DEFAULTFONTSIZE
;
1470 m_DefaultSetting
.style
= wxNORMAL
;
1471 m_DefaultSetting
.weight
= wxNORMAL
;
1472 m_DefaultSetting
.underline
= 0;
1473 m_DefaultSetting
.m_fg_valid
= TRUE
;
1474 m_DefaultSetting
.m_fg
= *wxBLACK
;
1475 m_DefaultSetting
.m_bg_valid
= TRUE
;
1476 m_DefaultSetting
.m_bg
= *wxWHITE
;
1478 m_CurrentSetting
= m_DefaultSetting
;
1482 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1483 int underline
, wxColour
*fg
,
1486 if(family
!= -1) m_CurrentSetting
.family
= family
;
1487 if(size
!= -1) m_CurrentSetting
.size
= size
;
1488 if(style
!= -1) m_CurrentSetting
.style
= style
;
1489 if(weight
!= -1) m_CurrentSetting
.weight
= weight
;
1490 if(underline
!= -1) m_CurrentSetting
.underline
= underline
!= 0;
1491 if(fg
) m_CurrentSetting
.m_fg
= *fg
;
1492 if(bg
) m_CurrentSetting
.m_bg
= *bg
;
1494 new wxLayoutObjectCmd(
1495 m_CurrentSetting
.family
,
1496 m_CurrentSetting
.size
,
1497 m_CurrentSetting
.style
,
1498 m_CurrentSetting
.weight
,
1499 m_CurrentSetting
.underline
,
1504 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1505 int underline
, char const *fg
, char const *bg
)
1513 cfg
= wxTheColourDatabase
->FindColour(fg
);
1515 cbg
= wxTheColourDatabase
->FindColour(bg
);
1517 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
1521 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
1522 int underline
, wxColour
*fg
, wxColour
*bg
)
1525 m_DefaultSetting
= wxLayoutStyleInfo(family
, size
, style
, weight
,
1527 m_CurrentSetting
= m_DefaultSetting
;
1531 wxLayoutList::FindText(const wxString
&needle
, const wxPoint
&cpos
) const
1536 for(line
= m_FirstLine
;
1538 line
= line
->GetNextLine())
1540 if(line
->GetLineNumber() >= cpos
.y
)
1542 xpos
= line
->FindText(needle
,
1543 (line
->GetLineNumber() == cpos
.y
) ?
1546 return wxPoint(xpos
, line
->GetLineNumber());
1549 return wxPoint(-1,-1);
1554 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
1556 AddCursorPosToUpdateRect();
1558 wxLayoutLine
*line
= m_FirstLine
;
1559 while(line
&& line
->GetLineNumber() != p
.y
)
1560 line
= line
->GetNextLine();
1561 if(line
&& line
->GetLineNumber() == p
.y
) // found it
1563 m_CursorPos
.y
= p
.y
;
1564 m_CursorLine
= line
;
1565 CoordType len
= line
->GetLength();
1568 m_CursorPos
.x
= p
.x
;
1573 m_CursorPos
.x
= len
;
1581 wxLayoutList::MoveCursorVertically(int n
)
1583 AddCursorPosToUpdateRect();
1586 if(n
< 0) // move up
1588 if(m_CursorLine
== m_FirstLine
) return false;
1589 while(n
< 0 && m_CursorLine
)
1591 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1597 m_CursorLine
= m_FirstLine
;
1603 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1604 m_CursorPos
.x
= m_CursorLine
->GetLength();
1610 wxLayoutLine
*last
= m_CursorLine
;
1611 if(! m_CursorLine
->GetNextLine()) return false;
1612 while(n
> 0 && m_CursorLine
)
1616 m_CursorLine
= m_CursorLine
->GetNextLine();
1620 m_CursorLine
= last
;
1626 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1627 m_CursorPos
.x
= m_CursorLine
->GetLength();
1635 wxLayoutList::MoveCursorHorizontally(int n
)
1637 AddCursorPosToUpdateRect();
1642 if(m_CursorPos
.x
== 0) // at begin of line
1644 if(! MoveCursorVertically(-1))
1646 MoveCursorToEndOfLine();
1651 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1652 m_CursorPos
.x
-= move
; n
+= move
;
1657 int len
= m_CursorLine
->GetLength();
1658 if(m_CursorPos
.x
== len
) // at end of line
1660 if(! MoveCursorVertically(1))
1662 MoveCursorToBeginOfLine();
1667 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1668 m_CursorPos
.x
+= move
;
1675 wxLayoutList::Insert(wxString
const &text
)
1677 wxASSERT(m_CursorLine
);
1678 wxASSERT_MSG( text
.Find('\n') == wxNOT_FOUND
, "use wxLayoutImportText!" );
1683 AddCursorPosToUpdateRect();
1685 if ( !m_CursorLine
->Insert(m_CursorPos
.x
, text
) )
1688 m_CursorPos
.x
+= text
.Length();
1690 m_CursorLine
->RecalculatePositions(0, this);
1696 wxLayoutList::Insert(wxLayoutObject
*obj
)
1698 wxASSERT(m_CursorLine
);
1701 m_CursorLine
= GetFirstLine();
1703 AddCursorPosToUpdateRect();
1705 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
1706 m_CursorPos
.x
+= obj
->GetLength();
1708 m_CursorLine
->RecalculatePositions(0, this);
1714 wxLayoutList::Insert(wxLayoutList
*llist
)
1719 for(wxLayoutLine
*line
= llist
->GetFirstLine();
1721 line
= line
->GetNextLine()
1724 for(wxLOiterator i
= line
->GetFirstObject();
1734 wxLayoutList::LineBreak(void)
1736 wxASSERT(m_CursorLine
);
1737 bool setFirst
= (m_CursorLine
== m_FirstLine
&& m_CursorPos
.x
== 0);
1739 AddCursorPosToUpdateRect();
1741 wxPoint
position(m_CursorLine
->GetPosition());
1743 wxCoord width
= m_CursorLine
->GetWidth(),
1744 height
= m_CursorLine
->GetHeight();
1746 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
, this);
1747 if(setFirst
) // we were at beginning of first line
1748 m_FirstLine
= m_CursorLine
->GetPreviousLine();
1749 if(m_CursorPos
.x
!= 0)
1752 // doesn't help m_CursorLine.MarkDirty();
1754 wxLayoutLine
*prev
= m_CursorLine
->GetPreviousLine();
1755 wxCHECK_MSG(prev
, false, "just broke the line, where is the previous one?");
1757 height
+= prev
->GetHeight();
1759 SetUpdateRect(position
);
1760 SetUpdateRect(position
.x
+ width
+ MSW_CORRECTION
,
1761 position
.y
+ height
+ MSW_CORRECTION
);
1767 wxLayoutList::WrapLine(CoordType column
)
1769 if(m_CursorPos
.x
<= column
|| column
< 1)
1770 return false; // do nothing yet
1773 CoordType xpos
= m_CursorLine
->GetWrapPosition(column
);
1775 return false; // cannot break line
1777 CoordType newpos
= m_CursorPos
.x
- xpos
- 1;
1778 m_CursorPos
.x
= xpos
;
1780 AddCursorPosToUpdateRect();
1783 Delete(1); // delete the space
1784 m_CursorPos
.x
= newpos
;
1786 m_CursorLine
->RecalculatePositions(1, this);
1793 wxLayoutList::Delete(CoordType npos
)
1795 wxCHECK_MSG(m_CursorLine
, false, "can't delete in non existing line");
1796 wxASSERT_MSG(npos
> 0, "nothing to delete?");
1798 AddCursorPosToUpdateRect();
1800 // were other lines appended to this one (this is important to know because
1801 // this means that our width _increased_ as the result of deletion)
1802 bool wasMerged
= false;
1804 // the size of the region to update
1805 CoordType totalHeight
= m_CursorLine
->GetHeight(),
1806 totalWidth
= m_CursorLine
->GetWidth();
1811 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
1815 // More to delete, continue on next line.
1817 // First, check if line is empty:
1818 if(m_CursorLine
->GetLength() == 0)
1820 // in this case, updating could probably be optimised
1822 wxASSERT(DeleteLines(1) == 0);
1831 // Need to join next line
1832 if(! m_CursorLine
->GetNextLine())
1837 wxLayoutLine
*next
= m_CursorLine
->GetNextLine();
1840 totalHeight
+= next
->GetHeight();
1841 totalWidth
+= next
->GetWidth();
1843 m_CursorLine
->MergeNextLine(this);
1848 wxFAIL_MSG("can't delete all this");
1858 // we need to update the whole tail of the line and the lines which
1862 wxPoint
position(m_CursorLine
->GetPosition());
1863 SetUpdateRect(position
);
1864 SetUpdateRect(position
.x
+ totalWidth
+ MSW_CORRECTION
,
1865 position
.y
+ totalHeight
+ MSW_CORRECTION
);
1872 wxLayoutList::DeleteLines(int n
)
1874 wxASSERT(m_CursorLine
);
1877 AddCursorPosToUpdateRect();
1881 if(!m_CursorLine
->GetNextLine())
1882 { // we cannot delete this line, but we can clear it
1883 MoveCursorToBeginOfLine();
1884 DeleteToEndOfLine();
1885 m_CursorLine
->RecalculatePositions(2, this);
1889 line
= m_CursorLine
;
1890 m_CursorLine
= m_CursorLine
->DeleteLine(true, this);
1892 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
1893 wxASSERT(m_FirstLine
);
1894 wxASSERT(m_CursorLine
);
1896 m_CursorLine
->RecalculatePositions(2, this);
1901 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
)
1903 wxLayoutLine
*line
= m_FirstLine
;
1905 // first, make sure everything is calculated - this might not be
1906 // needed, optimise it later
1907 ApplyStyle(&m_DefaultSetting
, dc
);
1910 line
->RecalculatePosition(this); // so we don't need to do it all the time
1911 // little condition to speed up redrawing:
1912 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1913 line
= line
->GetNextLine();
1918 wxLayoutList::UpdateCursorScreenPos(wxDC
&dc
)
1920 wxASSERT(m_CursorLine
);
1921 m_CursorLine
->Layout(dc
, this, (wxPoint
*)&m_CursorScreenPos
, (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
1925 wxLayoutList::GetCursorScreenPos(wxDC
&dc
)
1927 UpdateCursorScreenPos(dc
);
1928 return m_CursorScreenPos
;
1932 Is called before each Draw(). Now, it will re-layout all lines which
1936 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
, bool forceAll
)
1938 // first, make sure everything is calculated - this might not be
1939 // needed, optimise it later
1940 ApplyStyle(&m_DefaultSetting
, dc
);
1942 // FIXME this is completely wrong - we should start by first *visible* line
1943 // (and stop on the last one) instead of looping over all lines!!
1944 wxLayoutLine
*line
= m_FirstLine
;
1947 if(forceAll
|| line
->IsDirty())
1949 line
->GetStyleInfo() = m_CurrentSetting
;
1950 if(line
== m_CursorLine
)
1951 line
->Layout(dc
, this, (wxPoint
*)&m_CursorScreenPos
,
1952 (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
1954 line
->Layout(dc
, this);
1956 // little condition to speed up redrawing:
1957 if(bottom
!= -1 && line
->GetPosition().y
> bottom
)
1961 line
->RecalculatePositions(1, this);
1962 line
= line
->GetNextLine();
1965 ///FIXME: disabled for now
1967 // can only be 0 if we are on the first line and have no next line
1968 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
1969 m_CursorLine
->GetNextLine() == NULL
&&
1970 m_CursorLine
== m_FirstLine
));
1972 AddCursorPosToUpdateRect();
1976 wxLayoutList::Draw(wxDC
&dc
,
1977 wxPoint
const &offset
,
1981 wxLayoutLine
*line
= m_FirstLine
;
1984 ApplyStyle(&m_DefaultSetting
, dc
);
1985 wxBrush
brush(m_CurrentSetting
.m_bg
, wxSOLID
);
1987 dc
.SetBackgroundMode(wxTRANSPARENT
);
1991 // only draw if between top and bottom:
1992 if((top
== -1 || line
->GetPosition().y
+ line
->GetHeight() >= top
))
1993 line
->Draw(dc
, this, offset
);
1995 line
->Layout(dc
, this);
1996 // little condition to speed up redrawing:
1997 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1998 line
= line
->GetNextLine();
2000 InvalidateUpdateRect();
2002 WXLO_DEBUG(("Selection is %s : l%d,%ld/%ld,%ld",
2003 m_Selection
.m_valid
? "valid" : "invalid",
2004 m_Selection
.m_CursorA
.x
, m_Selection
.m_CursorA
.y
,
2005 m_Selection
.m_CursorB
.x
, m_Selection
.m_CursorB
.y
));
2009 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
,
2013 // First, find the right line:
2014 wxLayoutLine
*line
= m_FirstLine
;
2017 ApplyStyle(&m_DefaultSetting
, dc
);
2020 p
= line
->GetPosition();
2021 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
2024 // we need to run a layout here to get font sizes right :-(
2026 // VZ: we can't call Layout() from here because it marks the line as
2027 // clean and it is not refreshed when it's called from wxLayoutList::
2028 // Layout() - if we really need to do this, we should introduce an
2029 // extra argument to Layout() to prevent the line from MarkClean()ing
2031 line
->Layout(dc
, this);
2033 line
= line
->GetNextLine();
2037 if(found
) *found
= false;
2038 return NULL
; // not found
2040 if(cursorPos
) cursorPos
->y
= line
->GetLineNumber();
2041 // Now, find the object in the line:
2042 wxLOiterator i
= line
->FindObjectScreen(dc
, pos
.x
,
2043 cursorPos
? & cursorPos
->x
: NULL
,
2045 return (i
== NULLIT
) ? NULL
: *i
;
2050 wxLayoutList::GetSize(void) const
2053 *line
= m_FirstLine
,
2056 return wxPoint(0,0);
2058 wxPoint
maxPoint(0,0);
2063 if(line
->GetWidth() > maxPoint
.x
)
2064 maxPoint
.x
= line
->GetWidth();
2066 line
= line
->GetNextLine();
2069 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
2075 wxLayoutList::DrawCursor(wxDC
&dc
, bool active
, wxPoint
const &translate
)
2077 wxPoint
coords(m_CursorScreenPos
);
2078 coords
+= translate
;
2080 #ifdef WXLAYOUT_DEBUG
2081 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
2082 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
2083 (long)coords
.x
, (long)coords
.y
,
2084 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
2085 (long)m_CursorLine
->GetLineNumber(),
2086 (long)m_CursorLine
->GetLength()));
2088 wxLogStatus("Cursor is at (%d, %d)", m_CursorPos
.x
, m_CursorPos
.y
);
2091 #ifdef WXLAYOUT_USE_CARET
2092 m_caret
->Move(coords
);
2093 #else // !WXLAYOUT_USE_CARET
2094 dc
.SetBrush(*wxBLACK_BRUSH
);
2095 dc
.SetLogicalFunction(wxXOR
);
2096 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
2099 dc
.DrawRectangle(coords
.x
, coords
.y
,
2100 m_CursorSize
.x
, m_CursorSize
.y
);
2101 SetUpdateRect(coords
.x
, coords
.y
);
2102 SetUpdateRect(coords
.x
+m_CursorSize
.x
, coords
.y
+m_CursorSize
.y
);
2106 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
2107 coords
.x
, coords
.y
);
2108 SetUpdateRect(coords
.x
, coords
.y
+m_CursorSize
.y
-1);
2109 SetUpdateRect(coords
.x
, coords
.y
);
2111 dc
.SetLogicalFunction(wxCOPY
);
2112 //dc.SetBrush(wxNullBrush);
2113 #endif // WXLAYOUT_USE_CARET/!WXLAYOUT_USE_CARET
2117 wxLayoutList::SetUpdateRect(CoordType x
, CoordType y
)
2119 if(m_UpdateRectValid
)
2120 GrowRect(m_UpdateRect
, x
, y
);
2125 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
2126 m_UpdateRect
.height
= 4;// wxGTK :-)
2127 m_UpdateRectValid
= true;
2132 wxLayoutList::StartSelection(wxPoint cpos
)
2136 WXLO_DEBUG(("Starting selection at %ld/%ld", cpos
.x
, cpos
.y
));
2137 m_Selection
.m_CursorA
= cpos
;
2138 m_Selection
.m_CursorB
= cpos
;
2139 m_Selection
.m_selecting
= true;
2140 m_Selection
.m_valid
= false;
2144 wxLayoutList::ContinueSelection(wxPoint cpos
)
2148 wxASSERT(m_Selection
.m_selecting
== true);
2149 wxASSERT(m_Selection
.m_valid
== false);
2150 WXLO_DEBUG(("Continuing selection at %ld/%ld", cpos
.x
, cpos
.y
));
2151 if(m_Selection
.m_CursorB
<= cpos
)
2152 m_Selection
.m_CursorB
= cpos
;
2154 m_Selection
.m_CursorA
= cpos
;
2155 // We always want m_CursorA <= m_CursorB!
2156 if(! (m_Selection
.m_CursorA
<= m_Selection
.m_CursorB
))
2158 wxPoint help
= m_Selection
.m_CursorB
;
2159 m_Selection
.m_CursorB
= m_Selection
.m_CursorA
;
2160 m_Selection
.m_CursorA
= help
;
2165 wxLayoutList::EndSelection(wxPoint cpos
)
2169 ContinueSelection(cpos
);
2170 WXLO_DEBUG(("Ending selection at %ld/%ld", cpos
.x
, cpos
.y
));
2171 m_Selection
.m_selecting
= false;
2172 m_Selection
.m_valid
= true;
2177 wxLayoutList::IsSelecting(void)
2179 return m_Selection
.m_selecting
;
2183 wxLayoutList::IsSelected(const wxPoint
&cursor
)
2185 if(! m_Selection
.m_valid
&& ! m_Selection
.m_selecting
)
2187 return m_Selection
.m_CursorA
<= cursor
2188 && cursor
<= m_Selection
.m_CursorB
;
2192 /** Tests whether this layout line is selected and needs
2194 @param line to test for
2195 @return 0 = not selected, 1 = fully selected, -1 = partially
2199 wxLayoutList::IsSelected(const wxLayoutLine
*line
, CoordType
*from
,
2202 wxASSERT(line
); wxASSERT(to
); wxASSERT(from
);
2204 if(! m_Selection
.m_valid
&& ! m_Selection
.m_selecting
)
2207 CoordType y
= line
->GetLineNumber();
2208 if(m_Selection
.m_CursorA
.y
< y
&& m_Selection
.m_CursorB
.y
> y
)
2210 else if(m_Selection
.m_CursorA
.y
== y
)
2212 *from
= m_Selection
.m_CursorA
.x
;
2213 if(m_Selection
.m_CursorB
.y
== y
)
2214 *to
= m_Selection
.m_CursorB
.x
;
2216 *to
= line
->GetLength();
2219 else if(m_Selection
.m_CursorB
.y
== y
)
2221 *to
= m_Selection
.m_CursorB
.x
;
2222 if(m_Selection
.m_CursorA
.y
== y
)
2223 *from
= m_Selection
.m_CursorA
.x
;
2233 wxLayoutList::DeleteSelection(void)
2235 if(! m_Selection
.m_valid
)
2238 m_Selection
.m_valid
= false;
2240 // Only delete part of the current line?
2241 if(m_Selection
.m_CursorA
.y
== m_Selection
.m_CursorB
.y
)
2243 MoveCursorTo(m_Selection
.m_CursorA
);
2244 Delete(m_Selection
.m_CursorB
.x
- m_Selection
.m_CursorA
.x
);
2253 for(firstLine
= m_FirstLine
;
2254 firstLine
&& firstLine
->GetLineNumber() < m_Selection
.m_CursorA
.y
;
2255 firstLine
=firstLine
->GetNextLine())
2257 if(!firstLine
|| firstLine
->GetLineNumber() != m_Selection
.m_CursorA
.y
)
2261 for(lastLine
= m_FirstLine
;
2262 lastLine
&& lastLine
->GetLineNumber() < m_Selection
.m_CursorB
.y
;
2263 lastLine
=lastLine
->GetNextLine())
2265 if(!lastLine
|| lastLine
->GetLineNumber() != m_Selection
.m_CursorB
.y
)
2269 // We now know that the two lines are different:
2271 // First, delete what's left of this line:
2272 MoveCursorTo(m_Selection
.m_CursorA
);
2273 DeleteToEndOfLine();
2275 wxLayoutLine
*nextLine
= firstLine
->GetNextLine();
2276 while(nextLine
&& nextLine
!= lastLine
)
2277 nextLine
= nextLine
->DeleteLine(false, this);
2279 // Now nextLine = lastLine;
2280 Delete(1); // This joins firstLine and nextLine
2281 Delete(m_Selection
.m_CursorB
.x
); // This deletes the first x
2285 firstLine
->RecalculatePositions(1, this);
2288 /// Starts highlighting the selection
2290 wxLayoutList::StartHighlighting(wxDC
&dc
)
2293 dc
.SetTextForeground(m_CurrentSetting
.m_bg
);
2294 dc
.SetTextBackground(m_CurrentSetting
.m_fg
);
2295 dc
.SetBackgroundMode(wxSOLID
);
2299 /// Ends highlighting the selection
2301 wxLayoutList::EndHighlighting(wxDC
&dc
)
2304 dc
.SetTextForeground(m_CurrentSetting
.m_fg
);
2305 dc
.SetTextBackground(m_CurrentSetting
.m_bg
);
2306 dc
.SetBackgroundMode(wxTRANSPARENT
);
2312 wxLayoutList::Copy(const wxPoint
&from
,
2319 for(firstLine
= m_FirstLine
;
2320 firstLine
&& firstLine
->GetLineNumber() < from
.y
;
2321 firstLine
=firstLine
->GetNextLine())
2323 if(!firstLine
|| firstLine
->GetLineNumber() != from
.y
)
2326 for(lastLine
= m_FirstLine
;
2327 lastLine
&& lastLine
->GetLineNumber() < to
.y
;
2328 lastLine
=lastLine
->GetNextLine())
2330 if(!lastLine
|| lastLine
->GetLineNumber() != to
.y
)
2335 wxLayoutLine
*tmp
= firstLine
;
2336 firstLine
= lastLine
;
2340 wxLayoutList
*llist
= new wxLayoutList();
2342 if(firstLine
== lastLine
)
2344 firstLine
->Copy(llist
, from
.x
, to
.x
);
2348 // Extract objects from first line
2349 firstLine
->Copy(llist
, from
.x
);
2351 // Extract all lines between
2352 for(wxLayoutLine
*line
= firstLine
->GetNextLine();
2354 line
= line
->GetNextLine())
2359 // Extract objects from last line
2360 lastLine
->Copy(llist
, 0, to
.x
);
2366 wxLayoutList::GetSelection(wxLayoutDataObject
*wxlo
, bool invalidate
)
2368 if(! m_Selection
.m_valid
)
2370 if(m_Selection
.m_selecting
)
2376 if(invalidate
) m_Selection
.m_valid
= false;
2378 wxLayoutList
*llist
= Copy( m_Selection
.m_CursorA
,
2379 m_Selection
.m_CursorB
);
2381 if(llist
&& wxlo
) // export as data object, too
2385 wxLayoutExportObject
*export
;
2386 wxLayoutExportStatus
status(llist
);
2387 while((export
= wxLayoutExport( &status
, WXLO_EXPORT_AS_OBJECTS
)) != NULL
)
2389 if(export
->type
== WXLO_EXPORT_EMPTYLINE
)
2390 ; //FIXME missing support for linebreaks in string format
2392 export
->content
.object
->Write(string
);
2396 wxlo
->SetData(string
.c_str(), string
.Length()+1);
2403 #define COPY_SI(what) if(si->what != -1) { m_CurrentSetting.what = si->what; fontChanged = TRUE; }
2406 wxLayoutList::ApplyStyle(wxLayoutStyleInfo
*si
, wxDC
&dc
)
2408 bool fontChanged
= FALSE
;
2415 dc
.SetFont( m_FontCache
.GetFont(m_CurrentSetting
) );
2419 m_CurrentSetting
.m_fg
= si
->m_fg
;
2420 dc
.SetTextForeground(m_CurrentSetting
.m_fg
);
2424 m_CurrentSetting
.m_bg
= si
->m_bg
;
2425 dc
.SetTextBackground(m_CurrentSetting
.m_bg
);
2430 #ifdef WXLAYOUT_DEBUG
2433 wxLayoutList::Debug(void)
2438 for(line
= m_FirstLine
;
2440 line
= line
->GetNextLine())
2447 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2451 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2453 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
2454 wxString
const & title
)
2459 // remove any highlighting which could interfere with printing:
2460 m_llist
->StartSelection();
2461 m_llist
->EndSelection();
2464 wxLayoutPrintout::~wxLayoutPrintout()
2469 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
2471 // The following bit is taken from the printing sample, let's see
2472 // whether it works for us.
2474 /* You might use THIS code to set the printer DC to ROUGHLY reflect
2475 * the screen text size. This page also draws lines of actual length 5cm
2478 // Get the logical pixels per inch of screen and printer
2479 int ppiScreenX
, ppiScreenY
;
2480 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
2481 int ppiPrinterX
, ppiPrinterY
;
2482 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
2484 if(ppiScreenX
== 0) // not yet set, need to guess
2489 if(ppiPrinterX
== 0) // not yet set, need to guess
2495 // This scales the DC so that the printout roughly represents the
2496 // the screen scaling. The text point size _should_ be the right size
2497 // but in fact is too small for some reason. This is a detail that will
2498 // need to be addressed at some point but can be fudged for the
2500 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
2502 // Now we have to check in case our real page size is reduced
2503 // (e.g. because we're drawing to a print preview memory DC)
2504 int pageWidth
, pageHeight
;
2506 dc
->GetSize(&w
, &h
);
2507 GetPageSizePixels(&pageWidth
, &pageHeight
);
2508 if(pageWidth
!= 0) // doesn't work always
2510 // If printer pageWidth == current DC width, then this doesn't
2511 // change. But w might be the preview bitmap width, so scale down.
2512 scale
= scale
* (float)(w
/(float)pageWidth
);
2514 dc
->SetUserScale(scale
, scale
);
2518 bool wxLayoutPrintout::OnPrintPage(int page
)
2527 top
= (page
- 1)*m_PrintoutHeight
;
2528 bottom
= top
+ m_PrintoutHeight
;
2529 // SetDeviceOrigin() doesn't work here, so we need to manually
2530 // translate all coordinates.
2531 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
2532 m_llist
->Draw(*dc
, translate
, top
, bottom
);
2539 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
2541 /* We allocate a temporary wxDC for printing, so that we can
2542 determine the correct paper size and scaling. We don't actually
2543 print anything on it. */
2545 wxPrinterDC
psdc("","",WXLLIST_TEMPFILE
,false);
2547 wxPostScriptDC
psdc(WXLLIST_TEMPFILE
,false);
2550 float scale
= ScaleDC(&psdc
);
2552 psdc
.GetSize(&m_PageWidth
, &m_PageHeight
);
2553 // This sets a left/top origin of 15% and 20%:
2554 m_Offset
= wxPoint((15*m_PageWidth
)/100, m_PageHeight
/20);
2556 // This is the length of the printable area.
2557 m_PrintoutHeight
= m_PageHeight
- (int) (m_PageHeight
* 0.15);
2558 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
2562 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
2565 *maxPage
= m_NumOfPages
;
2568 *selPageTo
= m_NumOfPages
;
2569 wxRemoveFile(WXLLIST_TEMPFILE
);
2572 bool wxLayoutPrintout::HasPage(int pageNum
)
2574 return pageNum
<= m_NumOfPages
;
2578 Stupid wxWindows doesn't draw proper ellipses, so we comment this
2579 out. It's a waste of paper anyway.
2583 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
2584 wxPoint topleft
, wxPoint bottomright
,
2587 // make backups of all essential parameters
2588 const wxBrush
& brush
= dc
.GetBrush();
2589 const wxPen
& pen
= dc
.GetPen();
2590 const wxFont
& font
= dc
.GetFont();
2592 dc
.SetBrush(*wxWHITE_BRUSH
);
2593 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
2594 dc
.DrawRoundedRectangle(topleft
.x
,
2595 topleft
.y
,bottomright
.x
-topleft
.x
,
2596 bottomright
.y
-topleft
.y
);
2597 dc
.SetBrush(*wxBLACK_BRUSH
);
2598 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
2599 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
2603 page
= "9999/9999 "; // many pages...
2605 dc
.GetTextExtent(page
,&w
,&h
);
2606 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
2607 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
2608 dc
.GetTextExtent("XXXX", &w
,&h
);
2609 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);
2620 wxFontCache::GetFont(int family
, int size
, int style
, int weight
,
2623 for(wxFCEList::iterator i
= m_FontList
.begin();
2624 i
!= m_FontList
.end(); i
++)
2625 if( (**i
).Matches(family
, size
, style
, weight
, underline
) )
2626 return (**i
).GetFont();
2628 wxFontCacheEntry
*fce
= new wxFontCacheEntry(family
, size
, style
,
2630 m_FontList
.push_back(fce
);
2631 return fce
->GetFont();