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
= 10;
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 CoordType widthOld
= m_Width
,
282 heightOld
= m_Height
;
283 dc
.GetTextExtent(m_Text
, &m_Width
, &m_Height
, &descent
);
285 if ( widthOld
!= m_Width
|| heightOld
!= m_Height
)
287 // as the text length changed, it must be refreshed
288 wxLayoutLine
*line
= GetLine();
290 wxCHECK_RET( line
, "wxLayoutObjectText can't refresh itself" );
292 // as our size changed, we need to repaint the part which was appended
293 wxPoint
position(line
->GetPosition());
295 // this is not the most efficient way (we repaint the whole line), but
296 // it's not too slow and is *simple*
297 if ( widthOld
< m_Width
)
299 if ( heightOld
< m_Height
)
300 heightOld
= m_Height
;
302 llist
->SetUpdateRect(position
.x
+ widthOld
+ MSW_CORRECTION
,
303 position
.y
+ heightOld
+ MSW_CORRECTION
);
307 m_Top
= m_Height
- m_Bottom
;
311 #ifdef WXLAYOUT_DEBUG
313 wxLayoutObjectText::Debug(void)
315 wxLayoutObject::Debug();
316 WXLO_DEBUG((" `%s`", m_Text
.c_str()));
320 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
324 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
326 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
const &icon
)
328 m_Icon
= new wxBitmap(icon
);
333 wxLayoutObjectIcon::Write(wxString
&ostr
)
335 /* Exports icon through a temporary file. */
337 wxString file
= wxGetTempFileName("wxloexport");
339 ostr
<< WXLO_TYPE_ICON
<< '\n'
341 m_Icon
->SaveFile(file
, WXLO_BITMAP_FORMAT
);
345 wxLayoutObjectIcon::Read(wxString
&istr
)
348 ReadString(file
, istr
);
350 if(! wxFileExists(file
))
352 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon
;
354 if(!obj
->m_Icon
->LoadFile(file
, WXLO_BITMAP_FORMAT
))
364 wxLayoutObjectIcon::Copy(void)
366 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon(new
368 obj
->SetUserData(m_UserData
);
372 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
*icon
)
378 wxLayoutObjectIcon::Draw(wxDC
&dc
, wxPoint
const &coords
,
379 wxLayoutList
*wxllist
,
380 CoordType begin
, CoordType
/* len */)
382 dc
.DrawBitmap(*m_Icon
, coords
.x
, coords
.y
-m_Icon
->GetHeight(),
383 (m_Icon
->GetMask() == NULL
) ? FALSE
: TRUE
);
387 wxLayoutObjectIcon::Layout(wxDC
& /* dc */, class wxLayoutList
* )
392 wxLayoutObjectIcon::GetSize(CoordType
*top
, CoordType
*bottom
) const
394 *top
= m_Icon
->GetHeight();
396 return wxPoint(m_Icon
->GetWidth(), m_Icon
->GetHeight());
401 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
405 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
408 wxLayoutStyleInfo::wxLayoutStyleInfo(int ifamily
,
416 family
= ifamily
; size
= isize
;
417 style
= istyle
; weight
= iweight
;
418 underline
= iul
!= 0;
435 #define COPY_SI_(what) if(right.what != -1) what = right.what;
438 wxLayoutStyleInfo::operator=(const wxLayoutStyleInfo
&right
)
445 if(right
.m_fg_valid
) m_fg
= right
.m_fg
;
446 if(right
.m_bg_valid
) m_bg
= right
.m_bg
;
450 wxLayoutObjectCmd::wxLayoutObjectCmd(int family
, int size
, int style
, int
451 weight
, int underline
,
452 wxColour
*fg
, wxColour
*bg
)
455 m_StyleInfo
= new wxLayoutStyleInfo(family
, size
,style
,weight
,underline
,fg
,bg
);
459 wxLayoutObjectCmd::Copy(void)
461 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd(
466 m_StyleInfo
->underline
,
467 m_StyleInfo
->m_fg_valid
?
468 &m_StyleInfo
->m_fg
: NULL
,
469 m_StyleInfo
->m_bg_valid
?
470 &m_StyleInfo
->m_bg
: NULL
);
471 obj
->SetUserData(m_UserData
);
476 wxLayoutObjectCmd::Write(wxString
&ostr
)
478 ostr
<< WXLO_TYPE_CMD
<< '\n'
479 << m_StyleInfo
->size
<< '\n'
480 << m_StyleInfo
->family
<< '\n'
481 << m_StyleInfo
->style
<< '\n'
482 << m_StyleInfo
->weight
<< '\n'
483 << m_StyleInfo
->underline
<< '\n'
484 << m_StyleInfo
->m_fg_valid
<< '\n'
485 << m_StyleInfo
->m_bg_valid
<< '\n';
486 if(m_StyleInfo
->m_fg_valid
)
488 ostr
<< m_StyleInfo
->m_fg
.Red() << '\n'
489 << m_StyleInfo
->m_fg
.Green() << '\n'
490 << m_StyleInfo
->m_fg
.Blue() << '\n';
492 if(m_StyleInfo
->m_bg_valid
)
494 ostr
<< m_StyleInfo
->m_bg
.Red() << '\n'
495 << m_StyleInfo
->m_bg
.Green() << '\n'
496 << m_StyleInfo
->m_bg
.Blue() << '\n';
501 wxLayoutObjectCmd::Read(wxString
&istr
)
503 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd
;
506 ReadString(tmp
, istr
);
507 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->size
);
508 ReadString(tmp
, istr
);
509 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->family
);
510 ReadString(tmp
, istr
);
511 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->style
);
512 ReadString(tmp
, istr
);
513 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->weight
);
514 ReadString(tmp
, istr
);
515 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->underline
);
516 ReadString(tmp
, istr
);
517 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->m_fg_valid
);
518 ReadString(tmp
, istr
);
519 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->m_bg_valid
);
520 if(obj
->m_StyleInfo
->m_fg_valid
)
522 int red
, green
, blue
;
523 ReadString(tmp
, istr
);
524 sscanf(tmp
.c_str(),"%d", &red
);
525 ReadString(tmp
, istr
);
526 sscanf(tmp
.c_str(),"%d", &green
);
527 ReadString(tmp
, istr
);
528 sscanf(tmp
.c_str(),"%d", &blue
);
529 obj
->m_StyleInfo
->m_fg
= wxColour(red
, green
, blue
);
531 if(obj
->m_StyleInfo
->m_bg_valid
)
533 int red
, green
, blue
;
534 ReadString(tmp
, istr
);
535 sscanf(tmp
.c_str(),"%d", &red
);
536 ReadString(tmp
, istr
);
537 sscanf(tmp
.c_str(),"%d", &green
);
538 ReadString(tmp
, istr
);
539 sscanf(tmp
.c_str(),"%d", &blue
);
540 obj
->m_StyleInfo
->m_bg
= wxColour(red
, green
, blue
);
546 wxLayoutObjectCmd::~wxLayoutObjectCmd()
552 wxLayoutObjectCmd::GetStyle(void) const
558 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint
const & /* coords */,
559 wxLayoutList
*wxllist
,
560 CoordType begin
, CoordType
/* len */)
562 wxASSERT(m_StyleInfo
);
563 wxllist
->ApplyStyle(m_StyleInfo
, dc
);
567 wxLayoutObjectCmd::Layout(wxDC
&dc
, class wxLayoutList
* llist
)
569 // this get called, so that recalculation uses right font sizes
570 Draw(dc
, wxPoint(0,0), llist
);
574 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
576 The wxLayoutLine object
578 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
580 wxLayoutLine::wxLayoutLine(wxLayoutLine
*prev
, wxLayoutList
*llist
)
583 m_Width
= m_Height
= 0;
588 RecalculatePosition(llist
);
591 m_LineNumber
= m_Previous
->GetLineNumber()+1;
592 m_Next
= m_Previous
->GetNextLine();
593 m_Previous
->m_Next
= this;
597 m_Next
->m_Previous
= this;
598 m_Next
->MoveLines(+1);
599 m_Next
->RecalculatePositions(1,llist
);
603 wxLayoutLine::~wxLayoutLine()
605 // kbList cleans itself
609 wxLayoutLine::RecalculatePosition(wxLayoutList
*llist
)
611 wxASSERT(m_Previous
|| GetLineNumber() == 0);
615 m_Position
= m_Previous
->GetPosition();
616 m_Position
.y
+= m_Previous
->GetHeight();
619 m_Position
= wxPoint(0,0);
620 llist
->SetUpdateRect(m_Position
);
625 wxLayoutLine::RecalculatePositions(int recurse
, wxLayoutList
*llist
)
627 //FIXME: is this really needed? We run Layout() anyway.
628 // Recursing here, drives computation time up exponentially, as
629 // each line will cause all following lines to be recalculated.
630 // Yes, or linenumbers go wrong.
632 wxASSERT(recurse
>= 0);
633 wxPoint pos
= m_Position
;
634 CoordType height
= m_Height
;
636 // WXLO_TRACE("RecalculatePositions()");
637 RecalculatePosition(llist
);
641 m_Next
->RecalculatePositions(--recurse
, llist
);
642 else if(pos
!= m_Position
|| m_Height
!= height
)
643 m_Next
->RecalculatePositions(0, llist
);
647 wxLayoutObjectList::iterator
648 wxLayoutLine::FindObject(CoordType xpos
, CoordType
*offset
) const
652 wxLayoutObjectList::iterator
655 CoordType x
= 0, len
;
657 /* We search through the objects. As we don't like returning the
658 object that the cursor is behind, we just remember such an
659 object in "found" so we can return it if there is really no
660 further object following it. */
661 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
663 len
= (**i
).GetLength();
664 if( x
<= xpos
&& xpos
<= x
+ len
)
667 if(xpos
== x
+ len
) // is there another object behind?
669 else // we are really inside this object
672 x
+= (**i
).GetLength();
674 return found
; // ==NULL if really none found
677 wxLayoutObjectList::iterator
678 wxLayoutLine::FindObjectScreen(wxDC
&dc
,
679 CoordType xpos
, CoordType
*cxpos
,
684 wxLayoutObjectList::iterator i
;
685 CoordType x
= 0, cx
= 0, width
;
687 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
689 width
= (**i
).GetWidth();
690 if( x
<= xpos
&& xpos
<= x
+ width
)
692 *cxpos
= cx
+ (**i
).GetOffsetScreen(dc
, xpos
-x
);
693 if(found
) *found
= true;
696 x
+= (**i
).GetWidth();
697 cx
+= (**i
).GetLength();
699 // behind last object:
701 if(found
) *found
= false;
702 return m_ObjectList
.tail();
705 /** Finds text in this line.
706 @param needle the text to find
707 @param xpos the position where to start the search
708 @return the cursoor coord where it was found or -1
711 wxLayoutLine::FindText(const wxString
&needle
, CoordType xpos
) const
716 wxString
const *text
;
718 for(wxLOiterator i
= m_ObjectList
.begin(); i
!= m_ObjectList
.end(); i
++)
720 if(cpos
>= xpos
) // search from here!
722 if((**i
).GetType() == WXLO_TYPE_TEXT
)
724 text
= & ((wxLayoutObjectText
*)(*i
))->GetText();
725 relpos
= text
->Find(needle
);
726 if(relpos
>= cpos
-xpos
) // -1 if not found
731 cpos
+= (**i
).GetLength();
734 return -1; // not found
738 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
741 wxASSERT(obj
!= NULL
);
743 // in any case, the object is going to belong to this line
744 obj
->AttachToLine(this);
746 //FIXME: this could be optimised, for now be prudent:
749 wxLOiterator i
= FindObject(xpos
, &offset
);
752 if(xpos
== 0 ) // aha, empty line!
754 m_ObjectList
.push_back(obj
);
755 m_Length
+= obj
->GetLength();
762 CoordType len
= (**i
).GetLength();
763 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
764 { // insert before this object
765 m_ObjectList
.insert(i
,obj
);
766 m_Length
+= obj
->GetLength();
771 if( i
== m_ObjectList
.tail()) // last object?
772 m_ObjectList
.push_back(obj
);
774 { // insert after current object
776 m_ObjectList
.insert(i
,obj
);
778 m_Length
+= obj
->GetLength();
781 /* Otherwise we need to split the current object.
782 Fortunately this can only be a text object. */
783 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
784 wxString left
, right
;
785 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
786 left
= tobj
->GetText().substr(0,offset
);
787 right
= tobj
->GetText().substr(offset
,len
-offset
);
788 // current text object gets set to right half
789 tobj
->GetText() = right
; // set new text
790 // before it we insert the new object
791 m_ObjectList
.insert(i
,obj
);
792 m_Length
+= obj
->GetLength();
793 // and before that we insert the left half
794 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
799 wxLayoutLine::Insert(CoordType xpos
, wxString text
)
802 //FIXME: this could be optimised, for now be prudent:
805 wxLOiterator i
= FindObject(xpos
, &offset
);
806 if(i
!= NULLIT
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
808 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
809 tobj
->GetText().insert(offset
, text
);
810 m_Length
+= text
.Length();
815 return Insert(xpos
, new wxLayoutObjectText(text
));
819 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
821 CoordType offset
, len
;
825 //FIXME: this could be optimised, for now be prudent:
827 wxLOiterator i
= FindObject(xpos
, &offset
);
830 if(i
== NULLIT
) return npos
;
831 // now delete from that object:
832 if((**i
).GetType() != WXLO_TYPE_TEXT
)
834 if(offset
!= 0) // at end of line after a non-text object
837 len
= (**i
).GetLength();
840 m_ObjectList
.erase(i
);
844 // tidy up: remove empty text objects
845 if((**i
).GetLength() == 0)
847 m_ObjectList
.erase(i
);
851 CoordType max
= (**i
).GetLength() - offset
;
852 if(npos
< max
) max
= npos
;
855 if(xpos
== GetLength())
858 { // at the end of an object
859 // move to begin of next object:
861 continue; // start over
866 if(offset
== 0 && max
== (**i
).GetLength())
867 m_ObjectList
.erase(i
); // remove the whole object
869 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
877 wxLayoutLine::DeleteWord(CoordType xpos
)
881 //FIXME: this could be optimised, for now be prudent:
884 wxLOiterator i
= FindObject(xpos
, &offset
);
888 if(i
== NULLIT
) return false;
889 if((**i
).GetType() != WXLO_TYPE_TEXT
)
891 // This should only happen when at end of line, behind a non-text
893 if(offset
== (**i
).GetLength()) return false;
894 m_Length
-= (**i
).GetLength(); // -1
895 m_ObjectList
.erase(i
);
896 return true; // we are done
900 if(offset
== (**i
).GetLength()) // at end of object
905 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
907 wxString str
= tobj
->GetText();
908 str
= str
.substr(offset
,str
.Length()-offset
);
909 // Find out how many positions we need to delete:
910 // 1. eat leading space
911 while(isspace(str
.c_str()[count
])) count
++;
912 // 2. eat the word itself:
913 while(isalnum(str
.c_str()[count
])) count
++;
915 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
916 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
921 wxASSERT(0); // we should never arrive here
925 wxLayoutLine::DeleteLine(bool update
, wxLayoutList
*llist
)
927 if(m_Next
) m_Next
->m_Previous
= m_Previous
;
928 if(m_Previous
) m_Previous
->m_Next
= m_Next
;
931 m_Next
->MoveLines(-1);
932 m_Next
->RecalculatePositions(1, llist
);
934 wxLayoutLine
*next
= m_Next
;
940 wxLayoutLine::Draw(wxDC
&dc
,
942 const wxPoint
& offset
) const
944 wxLayoutObjectList::iterator i
;
945 wxPoint pos
= offset
;
946 pos
= pos
+ GetPosition();
950 CoordType xpos
= 0; // cursorpos, lenght of line
952 CoordType from
, to
, tempto
;
953 //FIXME This doesn't work yet, needs updating afterr default
954 //settings for list or a wxLayoutObjectCmd have changed:
955 //llist->ApplyStyle(&((wxLayoutLine *)this)->m_StyleInfo, dc);
956 int highlight
= llist
->IsSelected(this, &from
, &to
);
957 // WXLO_DEBUG(("highlight=%d", highlight ));
958 if(highlight
== 1) // we need to draw the whole line inverted!
959 llist
->StartHighlighting(dc
);
961 llist
->EndHighlighting(dc
);
963 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
965 if(highlight
== -1) // partially highlight line
967 // parts of the line need highlighting
968 tempto
= xpos
+(**i
).GetLength();
969 (**i
).Draw(dc
, pos
, llist
, from
-xpos
, to
-xpos
);
972 (**i
).Draw(dc
, pos
, llist
);
973 pos
.x
+= (**i
).GetWidth();
974 xpos
+= (**i
).GetLength();
979 wxLayoutLine::Layout(wxDC
&dc
,
985 wxLayoutObjectList::iterator i
;
989 oldHeight
= m_Height
;
991 topHeight
, bottomHeight
; // above and below baseline
994 objTopHeight
, objBottomHeight
;
997 m_Height
= 0; m_BaseLine
= 0;
999 topHeight
= 0; bottomHeight
= 0;
1001 bool cursorFound
= false;
1007 *cursorPos
= m_Position
;
1008 if(cursorSize
) *cursorSize
= wxPoint(0,0);
1011 //FIXME This doesn't work yet, needs updating afterr default
1012 //settings for list or a wxLayoutObjectCmd have changed:
1013 //llist->ApplyStyle(&m_StyleInfo, dc);
1014 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
1016 (**i
).Layout(dc
, llist
);
1017 size
= (**i
).GetSize(&objTopHeight
, &objBottomHeight
);
1019 if(cursorPos
&& ! cursorFound
)
1020 { // we need to check whether the text cursor is here
1021 len
= (**i
).GetLength();
1022 if(count
<= cx
&& count
+len
> cx
)
1024 if((**i
).GetType() == WXLO_TYPE_TEXT
)
1026 len
= cx
- count
; // pos in object
1027 CoordType width
, height
, descent
;
1028 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
1029 &width
, &height
, &descent
);
1030 cursorPos
->x
+= width
;
1031 cursorPos
->y
= m_Position
.y
;
1033 if(len
< (**i
).GetLength())
1034 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
1036 str
= WXLO_CURSORCHAR
;
1037 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
1038 wxASSERT(cursorSize
);
1039 // Just in case some joker inserted an empty string object:
1040 if(width
== 0) width
= WXLO_MINIMUM_CURSOR_WIDTH
;
1041 if(height
== 0) height
= objHeight
;
1042 cursorSize
->x
= width
;
1043 cursorSize
->y
= height
;
1044 cursorFound
= true; // no more checks
1047 { // on some other object
1048 CoordType top
, bottom
; // unused
1049 *cursorSize
= (**i
).GetSize(&top
,&bottom
);
1050 cursorPos
->y
= m_Position
.y
;
1051 cursorFound
= true; // no more checks
1057 cursorPos
->x
+= (**i
).GetWidth();
1062 if(objHeight
> m_Height
) m_Height
= objHeight
;
1063 if(objTopHeight
> topHeight
) topHeight
= objTopHeight
;
1064 if(objBottomHeight
> bottomHeight
) bottomHeight
= objBottomHeight
;
1067 // special case of a line which becomes empty (after deletion, for example):
1068 // we should invalidate the screen space it occupied (usually this happens
1069 // from wxLayoutObject::Layout called in the loop above)
1070 if ( m_ObjectList
.empty() )
1072 wxPoint
position(GetPosition());
1073 llist
->SetUpdateRect(position
.x
+ oldWidth
+ MSW_CORRECTION
,
1074 position
.y
+ oldHeight
+ MSW_CORRECTION
);
1077 if(topHeight
+ bottomHeight
> m_Height
)
1078 m_Height
= topHeight
+bottomHeight
;
1079 m_BaseLine
= topHeight
;
1083 CoordType width
, height
, descent
;
1084 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1086 m_BaseLine
= m_Height
- descent
;
1090 // tell next line about coordinate change
1091 if(m_Next
&& objHeight
!= oldHeight
)
1092 m_Next
->RecalculatePositions(0, llist
);
1094 // We need to check whether we found a valid cursor size:
1097 // this might be the case if the cursor is at the end of the
1098 // line or on a command object:
1099 if(cursorSize
->y
< WXLO_MINIMUM_CURSOR_WIDTH
)
1101 CoordType width
, height
, descent
;
1102 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1103 cursorSize
->x
= width
;
1104 cursorSize
->y
= height
;
1106 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
1107 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
1109 RecalculatePositions(1, llist
);
1114 wxLayoutLine::Break(CoordType xpos
, wxLayoutList
*llist
)
1116 wxASSERT(xpos
>= 0);
1117 //FIXME: this could be optimised, for now be prudent:
1120 /* If we are at the begin of a line, we want to move all other
1121 lines down and stay with the cursor where we are. However, if we
1122 are in an empty line, we want to move down with it. */
1123 if(xpos
== 0 && GetLength() > 0)
1124 { // insert an empty line before this one
1125 wxLayoutLine
*prev
= new wxLayoutLine(m_Previous
, llist
);
1126 if(m_Previous
== NULL
)
1127 { // We were in first line, need to link in new empty line
1129 prev
->m_Next
= this;
1131 m_Previous
->m_Height
= 0; // this is a wild guess
1134 m_Next
->RecalculatePositions(1, llist
);
1139 wxLOiterator i
= FindObject(xpos
, &offset
);
1141 // must be at the end of the line then
1142 return new wxLayoutLine(this, llist
);
1145 wxLayoutLine
*newLine
= new wxLayoutLine(this, llist
);
1146 // split object at i:
1147 if((**i
).GetType() == WXLO_TYPE_TEXT
&& offset
!= 0)
1149 wxString left
, right
;
1150 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
1151 left
= tobj
->GetText().substr(0,offset
);
1152 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
1153 // current text object gets set to left half
1154 tobj
->GetText() = left
; // set new text
1155 newLine
->Append(new wxLayoutObjectText(right
));
1156 m_Length
-= right
.Length();
1157 i
++; // don't move this object to the new list
1161 i
++; // move objects from here to new list
1163 while(i
!= m_ObjectList
.end())
1165 newLine
->Append(*i
);
1166 m_Length
-= (**i
).GetLength();
1167 m_ObjectList
.remove(i
); // remove without deleting it
1170 m_Next
->RecalculatePositions(2, llist
);
1176 wxLayoutLine::MergeNextLine(wxLayoutList
*llist
)
1178 wxCHECK_RET(GetNextLine(),"wxLayout internal error: no next line to merge");
1179 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
1181 //FIXME: this could be optimised, for now be prudent:
1184 wxLayoutObject
*last
= NULL
;
1185 for(i
= list
.begin(); i
!= list
.end();)
1187 wxLayoutObject
*current
= *i
;
1189 // merge text objects together for efficiency
1190 if ( last
&& last
->GetType() == WXLO_TYPE_TEXT
&&
1191 current
->GetType() == WXLO_TYPE_TEXT
)
1193 wxLayoutObjectText
*textObj
= (wxLayoutObjectText
*)last
;
1194 wxString
text(textObj
->GetText());
1195 text
+= ((wxLayoutObjectText
*)current
)->GetText();
1196 textObj
->SetText(text
);
1198 list
.erase(i
); // remove and delete it
1202 // just append the object "as was"
1203 current
->UnattachFromLine();
1206 list
.remove(i
); // remove without deleting it
1209 wxASSERT(list
.empty());
1211 wxLayoutLine
*oldnext
= GetNextLine();
1212 wxLayoutLine
*nextLine
= oldnext
->GetNextLine();
1217 nextLine
->MoveLines(-1);
1220 // no RecalculatePositions needed - called from Delete() anyhow
1224 wxLayoutLine::GetWrapPosition(CoordType column
)
1227 wxLOiterator i
= FindObject(column
, &offset
);
1228 if(i
== NULLIT
) return -1; // cannot wrap
1230 // go backwards through the list and look for space in text objects
1233 if((**i
).GetType() == WXLO_TYPE_TEXT
)
1237 if( isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1244 }while(offset
!= -1);
1245 i
--; // move on to previous object
1249 column
-= (**i
).GetLength();
1253 offset
= (**i
).GetLength();
1254 }while(i
!= NULLIT
);
1255 /* If we reached the begin of the list and have more than one
1256 object, that one is longer than the margin, so break behind
1259 i
= m_ObjectList
.begin();
1260 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1262 pos
+= (**i
).GetLength();
1265 if(i
== NULLIT
) return -1; //why should this happen?
1266 pos
+= (**i
).GetLength();
1268 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1270 pos
+= (**i
).GetLength();
1273 if(i
== NULLIT
) return -1; //this is possible, if there is only one text object
1274 // now we are at the second text object:
1275 pos
-= (**i
).GetLength();
1276 return pos
; // in front of it
1280 #ifdef WXLAYOUT_DEBUG
1282 wxLayoutLine::Debug(void)
1285 wxPoint pos
= GetPosition();
1286 WXLO_DEBUG(("Line %ld, Pos (%ld,%ld), Height %ld",
1287 (long int) GetLineNumber(),
1288 (long int) pos
.x
, (long int) pos
.y
,
1289 (long int) GetHeight()));
1290 if(m_ObjectList
.begin() != NULLIT
)
1291 (**m_ObjectList
.begin()).Debug();
1297 wxLayoutLine::Copy(wxLayoutList
*llist
,
1301 CoordType firstOffset
, lastOffset
;
1303 if(to
== -1) to
= GetLength();
1304 if(from
== to
) return;
1306 wxLOiterator first
= FindObject(from
, &firstOffset
);
1307 wxLOiterator last
= FindObject(to
, &lastOffset
);
1309 // Common special case: only one object
1310 if( first
!= NULLIT
&& last
!= NULLIT
&& *first
== *last
)
1312 if( (**first
).GetType() == WXLO_TYPE_TEXT
)
1314 llist
->Insert(new wxLayoutObjectText(
1315 ((wxLayoutObjectText
1316 *)*first
)->GetText().substr(firstOffset
,
1317 lastOffset
-firstOffset
))
1321 else // what can we do?
1323 if(lastOffset
> firstOffset
) // i.e. +1 :-)
1324 llist
->Insert( (**first
).Copy() );
1329 // If we reach here, we can safely copy the whole first object from
1330 // the firstOffset position on:
1331 if((**first
).GetType() == WXLO_TYPE_TEXT
&& firstOffset
!= 0)
1333 llist
->Insert(new wxLayoutObjectText(
1334 ((wxLayoutObjectText
*)*first
)->GetText().substr(firstOffset
))
1337 else if(firstOffset
== 0)
1338 llist
->Insert( (**first
).Copy() );
1339 // else nothing to copy :-(
1341 // Now we copy all objects before the last one:
1342 wxLOiterator i
= first
; i
++;
1343 for( ; i
!= last
; i
++)
1344 llist
->Insert( (**i
).Copy() );
1346 // And now the last object:
1349 if( (**last
).GetType() == WXLO_TYPE_TEXT
)
1351 llist
->Insert(new wxLayoutObjectText(
1352 ((wxLayoutObjectText
*)*last
)->GetText().substr(0,lastOffset
))
1356 llist
->Insert( (**last
).Copy() );
1361 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1363 The wxLayoutList object
1365 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1367 wxLayoutList::wxLayoutList()
1369 #ifdef WXLAYOUT_USE_CARET
1371 #endif // WXLAYOUT_USE_CARET
1374 InvalidateUpdateRect();
1378 wxLayoutList::~wxLayoutList()
1381 m_FirstLine
->DeleteLine(false, this);
1385 wxLayoutList::Empty(void)
1388 m_FirstLine
= m_FirstLine
->DeleteLine(false, this);
1390 m_CursorPos
= wxPoint(0,0);
1391 m_CursorScreenPos
= wxPoint(0,0);
1392 m_CursorSize
= wxPoint(0,0);
1393 m_FirstLine
= new wxLayoutLine(NULL
, this); // empty first line
1394 m_CursorLine
= m_FirstLine
;
1395 InvalidateUpdateRect();
1400 wxLayoutList::InternalClear(void)
1403 m_Selection
.m_selecting
= false;
1404 m_Selection
.m_valid
= false;
1406 m_DefaultSetting
.family
= wxSWISS
;
1407 m_DefaultSetting
.size
= WXLO_DEFAULTFONTSIZE
;
1408 m_DefaultSetting
.style
= wxNORMAL
;
1409 m_DefaultSetting
.weight
= wxNORMAL
;
1410 m_DefaultSetting
.underline
= 0;
1411 m_DefaultSetting
.m_fg_valid
= TRUE
;
1412 m_DefaultSetting
.m_fg
= *wxBLACK
;
1413 m_DefaultSetting
.m_bg_valid
= TRUE
;
1414 m_DefaultSetting
.m_bg
= *wxWHITE
;
1416 m_CurrentSetting
= m_DefaultSetting
;
1420 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1421 int underline
, wxColour
*fg
,
1424 if(family
!= -1) m_CurrentSetting
.family
= family
;
1425 if(size
!= -1) m_CurrentSetting
.size
= size
;
1426 if(style
!= -1) m_CurrentSetting
.style
= style
;
1427 if(weight
!= -1) m_CurrentSetting
.weight
= weight
;
1428 if(underline
!= -1) m_CurrentSetting
.underline
= underline
!= 0;
1429 if(fg
) m_CurrentSetting
.m_fg
= *fg
;
1430 if(bg
) m_CurrentSetting
.m_bg
= *bg
;
1432 new wxLayoutObjectCmd(
1433 m_CurrentSetting
.family
,
1434 m_CurrentSetting
.size
,
1435 m_CurrentSetting
.style
,
1436 m_CurrentSetting
.weight
,
1437 m_CurrentSetting
.underline
,
1442 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1443 int underline
, char const *fg
, char const *bg
)
1451 cfg
= wxTheColourDatabase
->FindColour(fg
);
1453 cbg
= wxTheColourDatabase
->FindColour(bg
);
1455 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
1459 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
1460 int underline
, wxColour
*fg
, wxColour
*bg
)
1463 m_DefaultSetting
= wxLayoutStyleInfo(family
, size
, style
, weight
,
1465 m_CurrentSetting
= m_DefaultSetting
;
1469 wxLayoutList::FindText(const wxString
&needle
, const wxPoint
&cpos
) const
1474 for(line
= m_FirstLine
;
1476 line
= line
->GetNextLine())
1478 if(line
->GetLineNumber() >= cpos
.y
)
1480 xpos
= line
->FindText(needle
,
1481 (line
->GetLineNumber() == cpos
.y
) ?
1484 return wxPoint(xpos
, line
->GetLineNumber());
1487 return wxPoint(-1,-1);
1492 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
1494 AddCursorPosToUpdateRect();
1496 wxLayoutLine
*line
= m_FirstLine
;
1497 while(line
&& line
->GetLineNumber() != p
.y
)
1498 line
= line
->GetNextLine();
1499 if(line
&& line
->GetLineNumber() == p
.y
) // found it
1501 m_CursorPos
.y
= p
.y
;
1502 m_CursorLine
= line
;
1503 CoordType len
= line
->GetLength();
1506 m_CursorPos
.x
= p
.x
;
1511 m_CursorPos
.x
= len
;
1519 wxLayoutList::MoveCursorVertically(int n
)
1521 AddCursorPosToUpdateRect();
1524 if(n
< 0) // move up
1526 if(m_CursorLine
== m_FirstLine
) return false;
1527 while(n
< 0 && m_CursorLine
)
1529 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1535 m_CursorLine
= m_FirstLine
;
1541 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1542 m_CursorPos
.x
= m_CursorLine
->GetLength();
1548 wxLayoutLine
*last
= m_CursorLine
;
1549 if(! m_CursorLine
->GetNextLine()) return false;
1550 while(n
> 0 && m_CursorLine
)
1554 m_CursorLine
= m_CursorLine
->GetNextLine();
1558 m_CursorLine
= last
;
1564 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1565 m_CursorPos
.x
= m_CursorLine
->GetLength();
1573 wxLayoutList::MoveCursorHorizontally(int n
)
1575 AddCursorPosToUpdateRect();
1580 if(m_CursorPos
.x
== 0) // at begin of line
1582 if(! MoveCursorVertically(-1))
1584 MoveCursorToEndOfLine();
1589 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1590 m_CursorPos
.x
-= move
; n
+= move
;
1595 int len
= m_CursorLine
->GetLength();
1596 if(m_CursorPos
.x
== len
) // at end of line
1598 if(! MoveCursorVertically(1))
1600 MoveCursorToBeginOfLine();
1605 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1606 m_CursorPos
.x
+= move
;
1613 wxLayoutList::Insert(wxString
const &text
)
1615 wxASSERT(m_CursorLine
);
1617 AddCursorPosToUpdateRect();
1619 m_CursorLine
->Insert(m_CursorPos
.x
, text
);
1620 m_CursorPos
.x
+= text
.Length();
1621 m_CursorLine
->RecalculatePositions(true, this); //FIXME needed?
1626 wxLayoutList::Insert(wxLayoutObject
*obj
)
1628 wxASSERT(m_CursorLine
);
1631 m_CursorLine
= GetFirstLine();
1633 AddCursorPosToUpdateRect();
1635 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
1636 m_CursorPos
.x
+= obj
->GetLength();
1637 m_CursorLine
->RecalculatePositions(true, this); //FIXME needed?
1642 wxLayoutList::Insert(wxLayoutList
*llist
)
1647 for(wxLayoutLine
*line
= llist
->GetFirstLine();
1649 line
= line
->GetNextLine()
1652 for(wxLOiterator i
= line
->GetFirstObject();
1662 wxLayoutList::LineBreak(void)
1664 wxASSERT(m_CursorLine
);
1665 bool setFirst
= (m_CursorLine
== m_FirstLine
&& m_CursorPos
.x
== 0);
1667 AddCursorPosToUpdateRect();
1669 wxPoint
position(m_CursorLine
->GetPosition());
1671 wxCoord width
= m_CursorLine
->GetWidth(),
1672 height
= m_CursorLine
->GetHeight();
1674 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
, this);
1675 if(setFirst
) // we were at beginning of first line
1676 m_FirstLine
= m_CursorLine
->GetPreviousLine();
1677 if(m_CursorPos
.x
!= 0)
1680 // doesn't help m_CursorLine.MarkDirty();
1682 wxLayoutLine
*prev
= m_CursorLine
->GetPreviousLine();
1683 wxCHECK_MSG(prev
, false, "just broke the line, where is the previous one?");
1685 height
+= prev
->GetHeight();
1687 SetUpdateRect(position
);
1688 SetUpdateRect(position
.x
+ width
+ MSW_CORRECTION
,
1689 position
.y
+ height
+ MSW_CORRECTION
);
1695 wxLayoutList::WrapLine(CoordType column
)
1697 if(m_CursorPos
.x
<= column
|| column
< 1)
1698 return false; // do nothing yet
1701 CoordType xpos
= m_CursorLine
->GetWrapPosition(column
);
1703 return false; // cannot break line
1705 CoordType newpos
= m_CursorPos
.x
- xpos
- 1;
1706 m_CursorPos
.x
= xpos
;
1708 AddCursorPosToUpdateRect();
1711 Delete(1); // delete the space
1712 m_CursorPos
.x
= newpos
;
1713 m_CursorLine
->RecalculatePositions(true, this); //FIXME needed?
1719 wxLayoutList::Delete(CoordType npos
)
1721 wxCHECK_MSG(m_CursorLine
, false, "can't delete in non existing line");
1722 wxASSERT_MSG(npos
> 0, "nothing to delete?");
1724 AddCursorPosToUpdateRect();
1726 // were other lines appended to this one (this is important to know because
1727 // this means that our width _increased_ as the result of deletion)
1728 bool wasMerged
= false;
1730 // the size of the region to update
1731 CoordType totalHeight
= m_CursorLine
->GetHeight(),
1732 totalWidth
= m_CursorLine
->GetWidth();
1737 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
1741 // More to delete, continue on next line.
1743 // First, check if line is empty:
1744 if(m_CursorLine
->GetLength() == 0)
1746 // in this case, updating could probably be optimised
1748 wxASSERT(DeleteLines(1) == 0);
1757 // Need to join next line
1758 if(! m_CursorLine
->GetNextLine())
1763 wxLayoutLine
*next
= m_CursorLine
->GetNextLine();
1765 totalHeight
+= next
->GetHeight();
1766 m_CursorLine
->MergeNextLine(this);
1774 // we need to update the whole tail of the line and the lines which
1778 wxPoint
position(m_CursorLine
->GetPosition());
1779 SetUpdateRect(position
.x
+ totalWidth
+ MSW_CORRECTION
,
1780 position
.y
+ totalHeight
+ MSW_CORRECTION
);
1787 wxLayoutList::DeleteLines(int n
)
1789 wxASSERT(m_CursorLine
);
1792 AddCursorPosToUpdateRect();
1796 if(!m_CursorLine
->GetNextLine())
1797 { // we cannot delete this line, but we can clear it
1798 MoveCursorToBeginOfLine();
1799 DeleteToEndOfLine();
1800 m_CursorLine
->RecalculatePositions(2, this);
1804 line
= m_CursorLine
;
1805 m_CursorLine
= m_CursorLine
->DeleteLine(true, this);
1807 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
1808 wxASSERT(m_FirstLine
);
1809 wxASSERT(m_CursorLine
);
1811 m_CursorLine
->RecalculatePositions(2, this);
1816 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
)
1818 wxLayoutLine
*line
= m_FirstLine
;
1820 // first, make sure everything is calculated - this might not be
1821 // needed, optimise it later
1822 ApplyStyle(&m_DefaultSetting
, dc
);
1825 line
->RecalculatePosition(this); // so we don't need to do it all the time
1826 // little condition to speed up redrawing:
1827 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1828 line
= line
->GetNextLine();
1833 wxLayoutList::UpdateCursorScreenPos(wxDC
&dc
)
1835 wxASSERT(m_CursorLine
);
1836 m_CursorLine
->Layout(dc
, this, (wxPoint
*)&m_CursorScreenPos
, (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
1840 wxLayoutList::GetCursorScreenPos(wxDC
&dc
)
1842 UpdateCursorScreenPos(dc
);
1843 return m_CursorScreenPos
;
1847 Is called before each Draw(). Now, it will re-layout all lines which
1851 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
, bool forceAll
)
1853 wxLayoutLine
*line
= m_FirstLine
;
1855 // first, make sure everything is calculated - this might not be
1856 // needed, optimise it later
1857 ApplyStyle(&m_DefaultSetting
, dc
);
1860 if(forceAll
|| line
->IsDirty())
1862 line
->GetStyleInfo() = m_CurrentSetting
;
1863 if(line
== m_CursorLine
)
1864 line
->Layout(dc
, this, (wxPoint
*)&m_CursorScreenPos
,
1865 (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
1867 line
->Layout(dc
, this);
1868 // little condition to speed up redrawing:
1869 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1871 line
->RecalculatePositions(1,this);
1872 line
= line
->GetNextLine();
1875 ///FIXME: disabled for now
1877 // can only be 0 if we are on the first line and have no next line
1878 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
1879 m_CursorLine
->GetNextLine() == NULL
&&
1880 m_CursorLine
== m_FirstLine
));
1882 AddCursorPosToUpdateRect();
1886 wxLayoutList::Draw(wxDC
&dc
,
1887 wxPoint
const &offset
,
1891 wxLayoutLine
*line
= m_FirstLine
;
1894 ApplyStyle(&m_DefaultSetting
, dc
);
1895 wxBrush
brush(m_CurrentSetting
.m_bg
, wxSOLID
);
1897 dc
.SetBackgroundMode(wxTRANSPARENT
);
1901 // only draw if between top and bottom:
1902 if((top
== -1 || line
->GetPosition().y
+ line
->GetHeight() >= top
))
1903 line
->Draw(dc
, this, offset
);
1905 line
->Layout(dc
, this);
1906 // little condition to speed up redrawing:
1907 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1908 line
= line
->GetNextLine();
1910 InvalidateUpdateRect();
1912 WXLO_DEBUG(("Selection is %s : l%d,%ld/%ld,%ld",
1913 m_Selection
.m_valid
? "valid" : "invalid",
1914 m_Selection
.m_CursorA
.x
, m_Selection
.m_CursorA
.y
,
1915 m_Selection
.m_CursorB
.x
, m_Selection
.m_CursorB
.y
));
1919 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
,
1923 // First, find the right line:
1924 wxLayoutLine
*line
= m_FirstLine
;
1927 // we need to run a layout here to get font sizes right :-(
1928 ApplyStyle(&m_DefaultSetting
, dc
);
1931 p
= line
->GetPosition();
1932 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
1934 line
->Layout(dc
, this);
1935 line
= line
->GetNextLine();
1939 if(found
) *found
= false;
1940 return NULL
; // not found
1942 if(cursorPos
) cursorPos
->y
= line
->GetLineNumber();
1943 // Now, find the object in the line:
1944 wxLOiterator i
= line
->FindObjectScreen(dc
, pos
.x
,
1945 cursorPos
? & cursorPos
->x
: NULL
,
1947 return (i
== NULLIT
) ? NULL
: *i
;
1952 wxLayoutList::GetSize(void) const
1955 *line
= m_FirstLine
,
1958 return wxPoint(0,0);
1960 wxPoint
maxPoint(0,0);
1965 if(line
->GetWidth() > maxPoint
.x
)
1966 maxPoint
.x
= line
->GetWidth();
1968 line
= line
->GetNextLine();
1971 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
1977 wxLayoutList::DrawCursor(wxDC
&dc
, bool active
, wxPoint
const &translate
)
1979 wxPoint
coords(m_CursorScreenPos
);
1980 coords
+= translate
;
1982 #ifdef WXLAYOUT_DEBUG
1983 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
1984 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
1985 (long)coords
.x
, (long)coords
.y
,
1986 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
1987 (long)m_CursorLine
->GetLineNumber(),
1988 (long)m_CursorLine
->GetLength()));
1990 wxLogStatus("Cursor is at (%d, %d)", m_CursorPos
.x
, m_CursorPos
.y
);
1993 #ifdef WXLAYOUT_USE_CARET
1994 m_caret
->Move(coords
);
1995 #else // !WXLAYOUT_USE_CARET
1996 dc
.SetBrush(*wxBLACK_BRUSH
);
1997 dc
.SetLogicalFunction(wxXOR
);
1998 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
2001 dc
.DrawRectangle(coords
.x
, coords
.y
,
2002 m_CursorSize
.x
, m_CursorSize
.y
);
2003 SetUpdateRect(coords
.x
, coords
.y
);
2004 SetUpdateRect(coords
.x
+m_CursorSize
.x
, coords
.y
+m_CursorSize
.y
);
2008 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
2009 coords
.x
, coords
.y
);
2010 SetUpdateRect(coords
.x
, coords
.y
+m_CursorSize
.y
-1);
2011 SetUpdateRect(coords
.x
, coords
.y
);
2013 dc
.SetLogicalFunction(wxCOPY
);
2014 //dc.SetBrush(wxNullBrush);
2015 #endif // WXLAYOUT_USE_CARET/!WXLAYOUT_USE_CARET
2019 wxLayoutList::SetUpdateRect(CoordType x
, CoordType y
)
2021 if(m_UpdateRectValid
)
2022 GrowRect(m_UpdateRect
, x
, y
);
2027 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
2028 m_UpdateRect
.height
= 4;// wxGTK :-)
2029 m_UpdateRectValid
= true;
2034 wxLayoutList::StartSelection(wxPoint cpos
)
2038 WXLO_DEBUG(("Starting selection at %ld/%ld", cpos
.x
, cpos
.y
));
2039 m_Selection
.m_CursorA
= cpos
;
2040 m_Selection
.m_CursorB
= cpos
;
2041 m_Selection
.m_selecting
= true;
2042 m_Selection
.m_valid
= false;
2046 wxLayoutList::ContinueSelection(wxPoint cpos
)
2050 wxASSERT(m_Selection
.m_selecting
== true);
2051 wxASSERT(m_Selection
.m_valid
== false);
2052 WXLO_DEBUG(("Continuing selection at %ld/%ld", cpos
.x
, cpos
.y
));
2053 if(m_Selection
.m_CursorB
<= cpos
)
2054 m_Selection
.m_CursorB
= cpos
;
2056 m_Selection
.m_CursorA
= cpos
;
2057 // We always want m_CursorA <= m_CursorB!
2058 if(! (m_Selection
.m_CursorA
<= m_Selection
.m_CursorB
))
2060 wxPoint help
= m_Selection
.m_CursorB
;
2061 m_Selection
.m_CursorB
= m_Selection
.m_CursorA
;
2062 m_Selection
.m_CursorA
= help
;
2067 wxLayoutList::EndSelection(wxPoint cpos
)
2071 ContinueSelection(cpos
);
2072 WXLO_DEBUG(("Ending selection at %ld/%ld", cpos
.x
, cpos
.y
));
2073 m_Selection
.m_selecting
= false;
2074 m_Selection
.m_valid
= true;
2079 wxLayoutList::IsSelecting(void)
2081 return m_Selection
.m_selecting
;
2085 wxLayoutList::IsSelected(const wxPoint
&cursor
)
2087 if(! m_Selection
.m_valid
&& ! m_Selection
.m_selecting
)
2089 return m_Selection
.m_CursorA
<= cursor
2090 && cursor
<= m_Selection
.m_CursorB
;
2094 /** Tests whether this layout line is selected and needs
2096 @param line to test for
2097 @return 0 = not selected, 1 = fully selected, -1 = partially
2101 wxLayoutList::IsSelected(const wxLayoutLine
*line
, CoordType
*from
,
2104 wxASSERT(line
); wxASSERT(to
); wxASSERT(from
);
2106 if(! m_Selection
.m_valid
&& ! m_Selection
.m_selecting
)
2109 CoordType y
= line
->GetLineNumber();
2110 if(m_Selection
.m_CursorA
.y
< y
&& m_Selection
.m_CursorB
.y
> y
)
2112 else if(m_Selection
.m_CursorA
.y
== y
)
2114 *from
= m_Selection
.m_CursorA
.x
;
2115 if(m_Selection
.m_CursorB
.y
== y
)
2116 *to
= m_Selection
.m_CursorB
.x
;
2118 *to
= line
->GetLength();
2121 else if(m_Selection
.m_CursorB
.y
== y
)
2123 *to
= m_Selection
.m_CursorB
.x
;
2124 if(m_Selection
.m_CursorA
.y
== y
)
2125 *from
= m_Selection
.m_CursorA
.x
;
2135 wxLayoutList::DeleteSelection(void)
2137 if(! m_Selection
.m_valid
)
2140 m_Selection
.m_valid
= false;
2142 // Only delete part of the current line?
2143 if(m_Selection
.m_CursorA
.y
== m_Selection
.m_CursorB
.y
)
2145 MoveCursorTo(m_Selection
.m_CursorA
);
2146 Delete(m_Selection
.m_CursorB
.x
- m_Selection
.m_CursorA
.x
);
2155 for(firstLine
= m_FirstLine
;
2156 firstLine
&& firstLine
->GetLineNumber() < m_Selection
.m_CursorA
.y
;
2157 firstLine
=firstLine
->GetNextLine())
2159 if(!firstLine
|| firstLine
->GetLineNumber() != m_Selection
.m_CursorA
.y
)
2163 for(lastLine
= m_FirstLine
;
2164 lastLine
&& lastLine
->GetLineNumber() < m_Selection
.m_CursorB
.y
;
2165 lastLine
=lastLine
->GetNextLine())
2167 if(!lastLine
|| lastLine
->GetLineNumber() != m_Selection
.m_CursorB
.y
)
2171 // We now know that the two lines are different:
2173 // First, delete what's left of this line:
2174 MoveCursorTo(m_Selection
.m_CursorA
);
2175 DeleteToEndOfLine();
2177 wxLayoutLine
*nextLine
= firstLine
->GetNextLine();
2178 while(nextLine
&& nextLine
!= lastLine
)
2179 nextLine
= nextLine
->DeleteLine(false, this);
2181 // Now nextLine = lastLine;
2182 Delete(1); // This joins firstLine and nextLine
2183 Delete(m_Selection
.m_CursorB
.x
); // This deletes the first x
2187 firstLine
->RecalculatePositions(1, this);
2190 /// Starts highlighting the selection
2192 wxLayoutList::StartHighlighting(wxDC
&dc
)
2195 dc
.SetTextForeground(m_CurrentSetting
.m_bg
);
2196 dc
.SetTextBackground(m_CurrentSetting
.m_fg
);
2197 dc
.SetBackgroundMode(wxSOLID
);
2201 /// Ends highlighting the selection
2203 wxLayoutList::EndHighlighting(wxDC
&dc
)
2206 dc
.SetTextForeground(m_CurrentSetting
.m_fg
);
2207 dc
.SetTextBackground(m_CurrentSetting
.m_bg
);
2208 dc
.SetBackgroundMode(wxTRANSPARENT
);
2214 wxLayoutList::Copy(const wxPoint
&from
,
2221 for(firstLine
= m_FirstLine
;
2222 firstLine
&& firstLine
->GetLineNumber() < from
.y
;
2223 firstLine
=firstLine
->GetNextLine())
2225 if(!firstLine
|| firstLine
->GetLineNumber() != from
.y
)
2228 for(lastLine
= m_FirstLine
;
2229 lastLine
&& lastLine
->GetLineNumber() < to
.y
;
2230 lastLine
=lastLine
->GetNextLine())
2232 if(!lastLine
|| lastLine
->GetLineNumber() != to
.y
)
2237 wxLayoutLine
*tmp
= firstLine
;
2238 firstLine
= lastLine
;
2242 wxLayoutList
*llist
= new wxLayoutList();
2244 if(firstLine
== lastLine
)
2246 firstLine
->Copy(llist
, from
.x
, to
.x
);
2250 // Extract objects from first line
2251 firstLine
->Copy(llist
, from
.x
);
2253 // Extract all lines between
2254 for(wxLayoutLine
*line
= firstLine
->GetNextLine();
2256 line
= line
->GetNextLine())
2261 // Extract objects from last line
2262 lastLine
->Copy(llist
, 0, to
.x
);
2268 wxLayoutList::GetSelection(wxLayoutDataObject
*wxlo
, bool invalidate
)
2270 if(! m_Selection
.m_valid
)
2272 if(m_Selection
.m_selecting
)
2278 if(invalidate
) m_Selection
.m_valid
= false;
2280 wxLayoutList
*llist
= Copy( m_Selection
.m_CursorA
,
2281 m_Selection
.m_CursorB
);
2283 if(llist
&& wxlo
) // export as data object, too
2287 wxLayoutExportObject
*export
;
2288 wxLayoutExportStatus
status(llist
);
2289 while((export
= wxLayoutExport( &status
, WXLO_EXPORT_AS_OBJECTS
)) != NULL
)
2291 if(export
->type
== WXLO_EXPORT_EMPTYLINE
)
2292 ; //FIXME missing support for linebreaks in string format
2294 export
->content
.object
->Write(string
);
2298 wxlo
->SetData(string
.c_str(), string
.Length()+1);
2305 #define COPY_SI(what) if(si->what != -1) { m_CurrentSetting.what = si->what; fontChanged = TRUE; }
2308 wxLayoutList::ApplyStyle(wxLayoutStyleInfo
*si
, wxDC
&dc
)
2310 bool fontChanged
= FALSE
;
2317 dc
.SetFont( m_FontCache
.GetFont(m_CurrentSetting
) );
2321 m_CurrentSetting
.m_fg
= si
->m_fg
;
2322 dc
.SetTextForeground(m_CurrentSetting
.m_fg
);
2326 m_CurrentSetting
.m_bg
= si
->m_bg
;
2327 dc
.SetTextBackground(m_CurrentSetting
.m_bg
);
2332 #ifdef WXLAYOUT_DEBUG
2335 wxLayoutList::Debug(void)
2340 for(line
= m_FirstLine
;
2342 line
= line
->GetNextLine())
2349 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2353 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2355 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
2356 wxString
const & title
)
2361 // remove any highlighting which could interfere with printing:
2362 m_llist
->StartSelection();
2363 m_llist
->EndSelection();
2366 wxLayoutPrintout::~wxLayoutPrintout()
2371 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
2373 // The following bit is taken from the printing sample, let's see
2374 // whether it works for us.
2376 /* You might use THIS code to set the printer DC to ROUGHLY reflect
2377 * the screen text size. This page also draws lines of actual length 5cm
2380 // Get the logical pixels per inch of screen and printer
2381 int ppiScreenX
, ppiScreenY
;
2382 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
2383 int ppiPrinterX
, ppiPrinterY
;
2384 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
2386 if(ppiScreenX
== 0) // not yet set, need to guess
2391 if(ppiPrinterX
== 0) // not yet set, need to guess
2397 // This scales the DC so that the printout roughly represents the
2398 // the screen scaling. The text point size _should_ be the right size
2399 // but in fact is too small for some reason. This is a detail that will
2400 // need to be addressed at some point but can be fudged for the
2402 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
2404 // Now we have to check in case our real page size is reduced
2405 // (e.g. because we're drawing to a print preview memory DC)
2406 int pageWidth
, pageHeight
;
2408 dc
->GetSize(&w
, &h
);
2409 GetPageSizePixels(&pageWidth
, &pageHeight
);
2410 if(pageWidth
!= 0) // doesn't work always
2412 // If printer pageWidth == current DC width, then this doesn't
2413 // change. But w might be the preview bitmap width, so scale down.
2414 scale
= scale
* (float)(w
/(float)pageWidth
);
2416 dc
->SetUserScale(scale
, scale
);
2420 bool wxLayoutPrintout::OnPrintPage(int page
)
2429 top
= (page
- 1)*m_PrintoutHeight
;
2430 bottom
= top
+ m_PrintoutHeight
;
2431 // SetDeviceOrigin() doesn't work here, so we need to manually
2432 // translate all coordinates.
2433 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
2434 m_llist
->Draw(*dc
, translate
, top
, bottom
);
2441 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
2443 /* We allocate a temporary wxDC for printing, so that we can
2444 determine the correct paper size and scaling. We don't actually
2445 print anything on it. */
2447 wxPrinterDC
psdc("","",WXLLIST_TEMPFILE
,false);
2449 wxPostScriptDC
psdc(WXLLIST_TEMPFILE
,false);
2452 float scale
= ScaleDC(&psdc
);
2454 psdc
.GetSize(&m_PageWidth
, &m_PageHeight
);
2455 // This sets a left/top origin of 15% and 20%:
2456 m_Offset
= wxPoint((15*m_PageWidth
)/100, m_PageHeight
/20);
2458 // This is the length of the printable area.
2459 m_PrintoutHeight
= m_PageHeight
- (int) (m_PageHeight
* 0.15);
2460 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
2464 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
2467 *maxPage
= m_NumOfPages
;
2470 *selPageTo
= m_NumOfPages
;
2471 wxRemoveFile(WXLLIST_TEMPFILE
);
2474 bool wxLayoutPrintout::HasPage(int pageNum
)
2476 return pageNum
<= m_NumOfPages
;
2480 Stupid wxWindows doesn't draw proper ellipses, so we comment this
2481 out. It's a waste of paper anyway.
2485 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
2486 wxPoint topleft
, wxPoint bottomright
,
2489 // make backups of all essential parameters
2490 const wxBrush
& brush
= dc
.GetBrush();
2491 const wxPen
& pen
= dc
.GetPen();
2492 const wxFont
& font
= dc
.GetFont();
2494 dc
.SetBrush(*wxWHITE_BRUSH
);
2495 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
2496 dc
.DrawRoundedRectangle(topleft
.x
,
2497 topleft
.y
,bottomright
.x
-topleft
.x
,
2498 bottomright
.y
-topleft
.y
);
2499 dc
.SetBrush(*wxBLACK_BRUSH
);
2500 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
2501 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
2505 page
= "9999/9999 "; // many pages...
2507 dc
.GetTextExtent(page
,&w
,&h
);
2508 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
2509 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
2510 dc
.GetTextExtent("XXXX", &w
,&h
);
2511 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);
2522 wxFontCache::GetFont(int family
, int size
, int style
, int weight
,
2525 for(wxFCEList::iterator i
= m_FontList
.begin();
2526 i
!= m_FontList
.end(); i
++)
2527 if( (**i
).Matches(family
, size
, style
, weight
, underline
) )
2528 return (**i
).GetFont();
2530 wxFontCacheEntry
*fce
= new wxFontCacheEntry(family
, size
, style
,
2532 m_FontList
.push_back(fce
);
2533 return fce
->GetFont();