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"
20 #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"
39 # include <wx/print.h>
41 # include <wx/filefn.h>
46 /// This should never really get created
47 #define WXLLIST_TEMPFILE "__wxllist.tmp"
51 # define TypewxString(t) g_aTypewxStrings[t]
52 # define WXLO_DEBUG(x) wxLogDebug x
54 static const char *g_aTypewxStrings
[] =
56 "invalid", "text", "cmd", "icon"
59 wxLayoutObject::Debug(void)
61 WXLO_DEBUG(("%s",g_aTypewxStrings
[GetType()]));
64 # define TypewxString(t) ""
65 # define WXLO_DEBUG(x)
69 /// Cursors smaller than this disappear in XOR drawing mode
70 #define WXLO_MINIMUM_CURSOR_WIDTH 4
72 /// Use this character to estimate a cursor size when none is available.
73 #define WXLO_CURSORCHAR "E"
74 /** @name Helper functions */
76 /// allows me to compare to wxPoints
77 bool operator ==(wxPoint
const &p1
, wxPoint
const &p2
)
79 return p1
.x
== p2
.x
&& p1
.y
== p2
.y
;
82 /// allows me to compare to wxPoints
83 bool operator !=(wxPoint
const &p1
, wxPoint
const &p2
)
85 return p1
.x
!= p2
.x
|| p1
.y
!= p2
.y
;
88 /// allows me to compare to wxPoints
89 bool operator <=(wxPoint
const &p1
, wxPoint
const &p2
)
91 return p1
.y
< p2
.y
|| (p1
.y
== p2
.y
&& p1
.x
<= p2
.x
);
94 /// grows a wxRect so that it includes the given point
97 void GrowRect(wxRect
&r
, CoordType x
, CoordType y
)
101 else if(r
.x
+ r
.width
< x
)
106 else if(r
.y
+ r
.height
< y
)
112 /// returns true if the point is in the rectangle
114 bool Contains(const wxRect
&r
, const wxPoint
&p
)
116 return r
.x
<= p
.x
&& r
.y
<= p
.y
&& (r
.x
+r
.width
) >= p
.x
&& (r
.y
+ r
.height
) >= p
.y
;
124 void ReadString(wxString
&to
, wxString
&from
)
127 const char *cptr
= from
.c_str();
128 while(*cptr
&& *cptr
!= '\n')
134 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
138 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
142 wxLayoutObject::Read(wxString
&istr
)
145 ReadString(tmp
, istr
);
147 sscanf(tmp
.c_str(),"%d", &type
);
152 return wxLayoutObjectText::Read(istr
);
154 return wxLayoutObjectCmd::Read(istr
);
156 return wxLayoutObjectIcon::Read(istr
);
162 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
166 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
168 wxLayoutObjectText::wxLayoutObjectText(const wxString
&txt
)
178 wxLayoutObjectText::Copy(void)
180 wxLayoutObjectText
*obj
= new wxLayoutObjectText(m_Text
);
181 obj
->m_Width
= m_Width
;
182 obj
->m_Height
= m_Height
;
184 obj
->m_Bottom
= m_Bottom
;
185 obj
->SetUserData(m_UserData
);
191 wxLayoutObjectText::Write(wxString
&ostr
)
193 ostr
<< (int) WXLO_TYPE_TEXT
<< '\n'
198 wxLayoutObjectText::Read(wxString
&istr
)
201 ReadString(text
, istr
);
203 return new wxLayoutObjectText(text
);
207 wxLayoutObjectText::GetSize(CoordType
*top
, CoordType
*bottom
) const
210 *top
= m_Top
; *bottom
= m_Bottom
;
211 return wxPoint(m_Width
, m_Height
);
215 wxLayoutObjectText::Draw(wxDC
&dc
, wxPoint
const &coords
,
216 wxLayoutList
*wxllist
,
217 CoordType begin
, CoordType end
)
220 dc
.DrawText(m_Text
, coords
.x
, coords
.y
-m_Top
);
223 // highlight the bit between begin and len
227 ypos
= coords
.y
-m_Top
;
228 long width
, height
, descent
;
230 if(begin
< 0) begin
= 0;
231 if(end
> m_Text
.Length()) end
= m_Text
.Length();
234 str
= m_Text
.Mid(0, begin
);
235 dc
.DrawText(str
, xpos
, ypos
);
236 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
238 wxllist
->StartHighlighting(dc
);
239 str
= m_Text
.Mid(begin
, end
-begin
);
240 dc
.DrawText(str
, xpos
, ypos
);
241 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
243 wxllist
->EndHighlighting(dc
);
244 str
= m_Text
.Mid(end
, m_Text
.Length()-end
);
245 dc
.DrawText(str
, xpos
, ypos
);
250 wxLayoutObjectText::GetOffsetScreen(wxDC
&dc
, CoordType xpos
) const
254 maxlen
= m_Text
.Length();
257 height
, descent
= 0l;
259 if(xpos
== 0) return 0; // easy
261 while(width
< xpos
&& offs
< maxlen
)
263 dc
.GetTextExtent(m_Text
.substr(0,offs
),
264 &width
, &height
, &descent
);
267 /* We have to substract 1 to compensate for the offs++, and another
268 one because we don't want to position the cursor behind the
269 object what we clicked on, but before - otherwise it looks
271 return (xpos
> 2) ? offs
-2 : 0;
275 wxLayoutObjectText::Layout(wxDC
&dc
, class wxLayoutList
* )
279 dc
.GetTextExtent(m_Text
,&m_Width
, &m_Height
, &descent
);
281 m_Top
= m_Height
- m_Bottom
;
285 #ifdef WXLAYOUT_DEBUG
287 wxLayoutObjectText::Debug(void)
289 wxLayoutObject::Debug();
290 WXLO_DEBUG((" `%s`", m_Text
.c_str()));
294 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
298 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
300 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
const &icon
)
302 m_Icon
= new wxBitmap(icon
);
307 wxLayoutObjectIcon::Write(wxString
&ostr
)
309 /* Exports icon through a temporary file. */
311 wxString file
= wxGetTempFileName("wxloexport");
313 ostr
<< WXLO_TYPE_ICON
<< '\n'
315 m_Icon
->SaveFile(file
, WXLO_BITMAP_FORMAT
);
319 wxLayoutObjectIcon::Read(wxString
&istr
)
322 ReadString(file
, istr
);
324 if(! wxFileExists(file
))
326 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon
;
328 if(!obj
->m_Icon
->LoadFile(file
, WXLO_BITMAP_FORMAT
))
338 wxLayoutObjectIcon::Copy(void)
340 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon(new
342 obj
->SetUserData(m_UserData
);
346 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
*icon
)
352 wxLayoutObjectIcon::Draw(wxDC
&dc
, wxPoint
const &coords
,
353 wxLayoutList
*wxllist
,
354 CoordType begin
, CoordType
/* len */)
356 dc
.DrawBitmap(*m_Icon
, coords
.x
, coords
.y
-m_Icon
->GetHeight(),
357 (m_Icon
->GetMask() == NULL
) ? FALSE
: TRUE
);
361 wxLayoutObjectIcon::Layout(wxDC
& /* dc */, class wxLayoutList
* )
366 wxLayoutObjectIcon::GetSize(CoordType
*top
, CoordType
*bottom
) const
368 *top
= m_Icon
->GetHeight();
370 return wxPoint(m_Icon
->GetWidth(), m_Icon
->GetHeight());
375 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
379 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
382 wxLayoutStyleInfo::wxLayoutStyleInfo(int ifamily
,
390 family
= ifamily
; size
= isize
;
391 style
= istyle
; weight
= iweight
;
409 #define COPY_SI_(what) if(right.what != -1) what = right.what;
412 wxLayoutStyleInfo::operator=(const wxLayoutStyleInfo
&right
)
419 if(right
.m_fg_valid
) m_fg
= right
.m_fg
;
420 if(right
.m_bg_valid
) m_bg
= right
.m_bg
;
424 wxLayoutObjectCmd::wxLayoutObjectCmd(int family
, int size
, int style
, int
425 weight
, int underline
,
426 wxColour
*fg
, wxColour
*bg
)
429 m_StyleInfo
= new wxLayoutStyleInfo(family
, size
,style
,weight
,underline
,fg
,bg
);
433 wxLayoutObjectCmd::Copy(void)
435 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd(
440 m_StyleInfo
->underline
,
441 m_StyleInfo
->m_fg_valid
?
442 &m_StyleInfo
->m_fg
: NULL
,
443 m_StyleInfo
->m_bg_valid
?
444 &m_StyleInfo
->m_bg
: NULL
);
445 obj
->SetUserData(m_UserData
);
450 wxLayoutObjectCmd::Write(wxString
&ostr
)
452 ostr
<< WXLO_TYPE_CMD
<< '\n'
453 << m_StyleInfo
->size
<< '\n'
454 << m_StyleInfo
->family
<< '\n'
455 << m_StyleInfo
->style
<< '\n'
456 << m_StyleInfo
->weight
<< '\n'
457 << m_StyleInfo
->underline
<< '\n'
458 << m_StyleInfo
->m_fg_valid
<< '\n'
459 << m_StyleInfo
->m_bg_valid
<< '\n';
460 if(m_StyleInfo
->m_fg_valid
)
462 ostr
<< m_StyleInfo
->m_fg
.Red() << '\n'
463 << m_StyleInfo
->m_fg
.Green() << '\n'
464 << m_StyleInfo
->m_fg
.Blue() << '\n';
466 if(m_StyleInfo
->m_bg_valid
)
468 ostr
<< m_StyleInfo
->m_bg
.Red() << '\n'
469 << m_StyleInfo
->m_bg
.Green() << '\n'
470 << m_StyleInfo
->m_bg
.Blue() << '\n';
475 wxLayoutObjectCmd::Read(wxString
&istr
)
477 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd
;
480 ReadString(tmp
, istr
);
481 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->size
);
482 ReadString(tmp
, istr
);
483 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->family
);
484 ReadString(tmp
, istr
);
485 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->style
);
486 ReadString(tmp
, istr
);
487 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->weight
);
488 ReadString(tmp
, istr
);
489 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->underline
);
490 ReadString(tmp
, istr
);
491 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->m_fg_valid
);
492 ReadString(tmp
, istr
);
493 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->m_bg_valid
);
494 if(obj
->m_StyleInfo
->m_fg_valid
)
496 int red
, green
, blue
;
497 ReadString(tmp
, istr
);
498 sscanf(tmp
.c_str(),"%d", &red
);
499 ReadString(tmp
, istr
);
500 sscanf(tmp
.c_str(),"%d", &green
);
501 ReadString(tmp
, istr
);
502 sscanf(tmp
.c_str(),"%d", &blue
);
503 obj
->m_StyleInfo
->m_fg
= wxColour(red
, green
, blue
);
505 if(obj
->m_StyleInfo
->m_bg_valid
)
507 int red
, green
, blue
;
508 ReadString(tmp
, istr
);
509 sscanf(tmp
.c_str(),"%d", &red
);
510 ReadString(tmp
, istr
);
511 sscanf(tmp
.c_str(),"%d", &green
);
512 ReadString(tmp
, istr
);
513 sscanf(tmp
.c_str(),"%d", &blue
);
514 obj
->m_StyleInfo
->m_bg
= wxColour(red
, green
, blue
);
520 wxLayoutObjectCmd::~wxLayoutObjectCmd()
526 wxLayoutObjectCmd::GetStyle(void) const
532 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint
const & /* coords */,
533 wxLayoutList
*wxllist
,
534 CoordType begin
, CoordType
/* len */)
536 wxASSERT(m_StyleInfo
);
537 wxllist
->ApplyStyle(m_StyleInfo
, dc
);
541 wxLayoutObjectCmd::Layout(wxDC
&dc
, class wxLayoutList
* llist
)
543 // this get called, so that recalculation uses right font sizes
544 Draw(dc
, wxPoint(0,0), llist
);
548 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
550 The wxLayoutLine object
552 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
554 wxLayoutLine::wxLayoutLine(wxLayoutLine
*prev
, wxLayoutList
*llist
)
557 m_Width
= m_Height
= 0;
562 RecalculatePosition(llist
);
565 m_LineNumber
= m_Previous
->GetLineNumber()+1;
566 m_Next
= m_Previous
->GetNextLine();
567 m_Previous
->m_Next
= this;
571 m_Next
->m_Previous
= this;
572 m_Next
->MoveLines(+1);
573 m_Next
->RecalculatePositions(1,llist
);
577 wxLayoutLine::~wxLayoutLine()
579 // kbList cleans itself
583 wxLayoutLine::RecalculatePosition(wxLayoutList
*llist
)
585 wxASSERT(m_Previous
|| GetLineNumber() == 0);
589 m_Position
= m_Previous
->GetPosition();
590 m_Position
.y
+= m_Previous
->GetHeight();
593 m_Position
= wxPoint(0,0);
594 llist
->SetUpdateRect(m_Position
);
599 wxLayoutLine::RecalculatePositions(int recurse
, wxLayoutList
*llist
)
601 wxASSERT(recurse
>= 0);
602 wxPoint pos
= m_Position
;
603 CoordType height
= m_Height
;
605 // WXLO_TRACE("RecalculatePositions()");
606 RecalculatePosition(llist
);
610 m_Next
->RecalculatePositions(--recurse
, llist
);
611 else if(pos
!= m_Position
|| m_Height
!= height
)
612 m_Next
->RecalculatePositions(0, llist
);
616 wxLayoutObjectList::iterator
617 wxLayoutLine::FindObject(CoordType xpos
, CoordType
*offset
) const
621 wxLayoutObjectList::iterator
624 CoordType x
= 0, len
;
626 /* We search through the objects. As we don't like returning the
627 object that the cursor is behind, we just remember such an
628 object in "found" so we can return it if there is really no
629 further object following it. */
630 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
632 len
= (**i
).GetLength();
633 if( x
<= xpos
&& xpos
<= x
+ len
)
636 if(xpos
== x
+ len
) // is there another object behind?
638 else // we are really inside this object
641 x
+= (**i
).GetLength();
643 return found
; // ==NULL if really none found
646 wxLayoutObjectList::iterator
647 wxLayoutLine::FindObjectScreen(wxDC
&dc
,
648 CoordType xpos
, CoordType
*cxpos
,
653 wxLayoutObjectList::iterator i
;
654 CoordType x
= 0, cx
= 0, width
;
656 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
658 width
= (**i
).GetWidth();
659 if( x
<= xpos
&& xpos
<= x
+ width
)
661 *cxpos
= cx
+ (**i
).GetOffsetScreen(dc
, xpos
-x
);
662 if(found
) *found
= true;
665 x
+= (**i
).GetWidth();
666 cx
+= (**i
).GetLength();
668 // behind last object:
670 if(found
) *found
= false;
671 return m_ObjectList
.tail();
674 /** Finds text in this line.
675 @param needle the text to find
676 @param xpos the position where to start the search
677 @return the cursoor coord where it was found or -1
680 wxLayoutLine::FindText(const wxString
&needle
, CoordType xpos
) const
685 wxString
const *text
;
687 for(wxLOiterator i
= m_ObjectList
.begin(); i
!= m_ObjectList
.end(); i
++)
689 if(cpos
>= xpos
) // search from here!
691 if((**i
).GetType() == WXLO_TYPE_TEXT
)
693 text
= & ((wxLayoutObjectText
*)(*i
))->GetText();
694 relpos
= text
->Find(needle
);
695 if(relpos
>= cpos
-xpos
) // -1 if not found
700 cpos
+= (**i
).GetLength();
703 return -1; // not found
707 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
710 wxASSERT(obj
!= NULL
);
711 //FIXME: this could be optimised, for now be prudent:
714 wxLOiterator i
= FindObject(xpos
, &offset
);
717 if(xpos
== 0 ) // aha, empty line!
719 m_ObjectList
.push_back(obj
);
720 m_Length
+= obj
->GetLength();
727 CoordType len
= (**i
).GetLength();
728 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
729 { // insert before this object
730 m_ObjectList
.insert(i
,obj
);
731 m_Length
+= obj
->GetLength();
736 if( i
== m_ObjectList
.tail()) // last object?
737 m_ObjectList
.push_back(obj
);
739 { // insert after current object
741 m_ObjectList
.insert(i
,obj
);
743 m_Length
+= obj
->GetLength();
746 /* Otherwise we need to split the current object.
747 Fortunately this can only be a text object. */
748 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
749 wxString left
, right
;
750 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
751 left
= tobj
->GetText().substr(0,offset
);
752 right
= tobj
->GetText().substr(offset
,len
-offset
);
753 // current text object gets set to right half
754 tobj
->GetText() = right
; // set new text
755 // before it we insert the new object
756 m_ObjectList
.insert(i
,obj
);
757 m_Length
+= obj
->GetLength();
758 // and before that we insert the left half
759 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
764 wxLayoutLine::Insert(CoordType xpos
, wxString text
)
767 //FIXME: this could be optimised, for now be prudent:
770 wxLOiterator i
= FindObject(xpos
, &offset
);
771 if(i
!= NULLIT
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
773 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
774 tobj
->GetText().insert(offset
, text
);
775 m_Length
+= text
.Length();
780 return Insert(xpos
, new wxLayoutObjectText(text
));
784 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
786 CoordType offset
, len
;
790 //FIXME: this could be optimised, for now be prudent:
792 wxLOiterator i
= FindObject(xpos
, &offset
);
795 if(i
== NULLIT
) return npos
;
796 // now delete from that object:
797 if((**i
).GetType() != WXLO_TYPE_TEXT
)
799 if(offset
!= 0) // at end of line after a non-text object
802 len
= (**i
).GetLength();
805 m_ObjectList
.erase(i
);
809 // tidy up: remove empty text objects
810 if((**i
).GetLength() == 0)
812 m_ObjectList
.erase(i
);
816 CoordType max
= (**i
).GetLength() - offset
;
817 if(npos
< max
) max
= npos
;
820 if(xpos
== GetLength())
823 { // at the end of an object
824 // move to begin of next object:
826 continue; // start over
831 if(offset
== 0 && max
== (**i
).GetLength())
832 m_ObjectList
.erase(i
); // remove the whole object
834 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
841 wxLayoutLine::DeleteWord(CoordType xpos
)
845 //FIXME: this could be optimised, for now be prudent:
848 wxLOiterator i
= FindObject(xpos
, &offset
);
852 if(i
== NULLIT
) return false;
853 if((**i
).GetType() != WXLO_TYPE_TEXT
)
855 // This should only happen when at end of line, behind a non-text
857 if(offset
== (**i
).GetLength()) return false;
858 m_Length
-= (**i
).GetLength(); // -1
859 m_ObjectList
.erase(i
);
860 return true; // we are done
864 if(offset
== (**i
).GetLength()) // at end of object
869 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
871 wxString str
= tobj
->GetText();
872 str
= str
.substr(offset
,str
.Length()-offset
);
873 // Find out how many positions we need to delete:
874 // 1. eat leading space
875 while(isspace(str
.c_str()[count
])) count
++;
876 // 2. eat the word itself:
877 while(isalnum(str
.c_str()[count
])) count
++;
879 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
880 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
885 wxASSERT(0); // we should never arrive here
889 wxLayoutLine::DeleteLine(bool update
, wxLayoutList
*llist
)
891 if(m_Next
) m_Next
->m_Previous
= m_Previous
;
892 if(m_Previous
) m_Previous
->m_Next
= m_Next
;
895 m_Next
->MoveLines(-1);
896 m_Next
->RecalculatePositions(1, llist
);
898 wxLayoutLine
*next
= m_Next
;
904 wxLayoutLine::Draw(wxDC
&dc
,
906 const wxPoint
& offset
) const
908 wxLayoutObjectList::iterator i
;
909 wxPoint pos
= offset
;
910 pos
= pos
+ GetPosition();
914 CoordType xpos
= 0; // cursorpos, lenght of line
916 CoordType from
, to
, tempto
;
917 //FIXME This doesn't work yet, needs updating afterr default
918 //settings for list or a wxLayoutObjectCmd have changed:
919 //llist->ApplyStyle(&((wxLayoutLine *)this)->m_StyleInfo, dc);
920 int highlight
= llist
->IsSelected(this, &from
, &to
);
921 // WXLO_DEBUG(("highlight=%d", highlight ));
922 if(highlight
== 1) // we need to draw the whole line inverted!
923 llist
->StartHighlighting(dc
);
925 llist
->EndHighlighting(dc
);
927 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
929 if(highlight
== -1) // partially highlight line
931 // parts of the line need highlighting
932 tempto
= xpos
+(**i
).GetLength();
934 if(tempto
>= from
&& xpos
<= to
)
937 if(tempto
> (**i
).GetLength())
938 tempto
= (**i
).GetLength();
939 CoordType tmp
= from
-xpos
;
942 (**i
).Draw(dc
, pos
, llist
, from
-xpos
, to
-xpos
);
947 llist
->EndHighlighting(dc
); // FIXME! inefficient
948 (**i
).Draw(dc
, pos
, llist
);
953 (**i
).Draw(dc
, pos
, llist
);
954 pos
.x
+= (**i
).GetWidth();
955 xpos
+= (**i
).GetLength();
960 wxLayoutLine::Layout(wxDC
&dc
,
966 wxLayoutObjectList::iterator i
;
969 oldHeight
= m_Height
;
971 topHeight
, bottomHeight
; // above and below baseline
974 objTopHeight
, objBottomHeight
;
977 m_Height
= 0; m_BaseLine
= 0;
979 topHeight
= 0; bottomHeight
= 0;
981 bool cursorFound
= false;
987 *cursorPos
= m_Position
;
988 if(cursorSize
) *cursorSize
= wxPoint(0,0);
991 //FIXME This doesn't work yet, needs updating afterr default
992 //settings for list or a wxLayoutObjectCmd have changed:
993 //llist->ApplyStyle(&m_StyleInfo, dc);
994 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
996 (**i
).Layout(dc
, llist
);
997 size
= (**i
).GetSize(&objTopHeight
, &objBottomHeight
);
999 if(cursorPos
&& ! cursorFound
)
1000 { // we need to check whether the text cursor is here
1001 len
= (**i
).GetLength();
1002 if(count
<= cx
&& count
+len
> cx
)
1004 if((**i
).GetType() == WXLO_TYPE_TEXT
)
1006 len
= cx
- count
; // pos in object
1007 CoordType width
, height
, descent
;
1008 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
1009 &width
, &height
, &descent
);
1010 cursorPos
->x
+= width
;
1011 cursorPos
->y
= m_Position
.y
;
1013 if(len
< (**i
).GetLength())
1014 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
1016 str
= WXLO_CURSORCHAR
;
1017 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
1018 wxASSERT(cursorSize
);
1019 // Just in case some joker inserted an empty string object:
1020 if(width
== 0) width
= WXLO_MINIMUM_CURSOR_WIDTH
;
1021 if(height
== 0) height
= objHeight
;
1022 cursorSize
->x
= width
;
1023 cursorSize
->y
= height
;
1024 cursorFound
= true; // no more checks
1027 { // on some other object
1028 CoordType top
, bottom
; // unused
1029 *cursorSize
= (**i
).GetSize(&top
,&bottom
);
1030 cursorPos
->y
= m_Position
.y
;
1031 cursorFound
= true; // no more checks
1037 cursorPos
->x
+= (**i
).GetWidth();
1042 if(objHeight
> m_Height
) m_Height
= objHeight
;
1043 if(objTopHeight
> topHeight
) topHeight
= objTopHeight
;
1044 if(objBottomHeight
> bottomHeight
) bottomHeight
= objBottomHeight
;
1046 if(topHeight
+ bottomHeight
> m_Height
) m_Height
=
1047 topHeight
+bottomHeight
;
1048 m_BaseLine
= topHeight
;
1052 CoordType width
, height
, descent
;
1053 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1055 m_BaseLine
= m_Height
- descent
;
1059 // tell next line about coordinate change
1060 if(m_Next
&& objHeight
!= oldHeight
)
1061 m_Next
->RecalculatePositions(0, llist
);
1063 // We need to check whether we found a valid cursor size:
1066 // this might be the case if the cursor is at the end of the
1067 // line or on a command object:
1068 if(cursorSize
->y
< WXLO_MINIMUM_CURSOR_WIDTH
)
1070 CoordType width
, height
, descent
;
1071 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1072 cursorSize
->x
= width
;
1073 cursorSize
->y
= height
;
1075 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
1076 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
1078 RecalculatePositions(1, llist
);
1083 wxLayoutLine::Break(CoordType xpos
, wxLayoutList
*llist
)
1085 wxASSERT(xpos
>= 0);
1086 //FIXME: this could be optimised, for now be prudent:
1089 /* If we are at the begin of a line, we want to move all other
1090 lines down and stay with the cursor where we are. However, if we
1091 are in an empty line, we want to move down with it. */
1092 if(xpos
== 0 && GetLength() > 0)
1093 { // insert an empty line before this one
1094 wxLayoutLine
*prev
= new wxLayoutLine(m_Previous
, llist
);
1095 if(m_Previous
== NULL
)
1096 { // We were in first line, need to link in new empty line
1098 prev
->m_Next
= this;
1100 m_Previous
->m_Height
= 0; // this is a wild guess
1103 m_Next
->RecalculatePositions(1, llist
);
1108 wxLOiterator i
= FindObject(xpos
, &offset
);
1110 // must be at the end of the line then
1111 return new wxLayoutLine(this, llist
);
1114 wxLayoutLine
*newLine
= new wxLayoutLine(this, llist
);
1115 // split object at i:
1116 if((**i
).GetType() == WXLO_TYPE_TEXT
&& offset
!= 0)
1118 wxString left
, right
;
1119 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
1120 left
= tobj
->GetText().substr(0,offset
);
1121 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
1122 // current text object gets set to left half
1123 tobj
->GetText() = left
; // set new text
1124 newLine
->Append(new wxLayoutObjectText(right
));
1125 m_Length
-= right
.Length();
1126 i
++; // don't move this object to the new list
1130 i
++; // move objects from here to new list
1132 while(i
!= m_ObjectList
.end())
1134 newLine
->Append(*i
);
1135 m_Length
-= (**i
).GetLength();
1136 m_ObjectList
.remove(i
); // remove without deleting it
1139 m_Next
->RecalculatePositions(2, llist
);
1145 wxLayoutLine::MergeNextLine(wxLayoutList
*llist
)
1147 wxASSERT(GetNextLine());
1148 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
1150 //FIXME: this could be optimised, for now be prudent:
1153 for(i
= list
.begin(); i
!= list
.end();)
1156 list
.remove(i
); // remove without deleting it
1158 wxASSERT(list
.empty());
1159 wxLayoutLine
*oldnext
= GetNextLine();
1160 SetNext(GetNextLine()->GetNextLine());
1162 RecalculatePositions(1, llist
);
1166 wxLayoutLine::GetWrapPosition(CoordType column
)
1169 wxLOiterator i
= FindObject(column
, &offset
);
1170 if(i
== NULLIT
) return -1; // cannot wrap
1172 // go backwards through the list and look for space in text objects
1175 if((**i
).GetType() == WXLO_TYPE_TEXT
)
1179 if( isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1186 }while(offset
!= -1);
1187 i
--; // move on to previous object
1191 column
-= (**i
).GetLength();
1195 offset
= (**i
).GetLength();
1196 }while(i
!= NULLIT
);
1197 /* If we reached the begin of the list and have more than one
1198 object, that one is longer than the margin, so break behind
1201 i
= m_ObjectList
.begin();
1202 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1204 pos
+= (**i
).GetLength();
1207 if(i
== NULLIT
) return -1; //why should this happen?
1208 pos
+= (**i
).GetLength();
1210 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1212 pos
+= (**i
).GetLength();
1215 if(i
== NULLIT
) return -1; //this is possible, if there is only one text object
1216 // now we are at the second text object:
1217 pos
-= (**i
).GetLength();
1218 return pos
; // in front of it
1222 #ifdef WXLAYOUT_DEBUG
1224 wxLayoutLine::Debug(void)
1227 wxPoint pos
= GetPosition();
1228 WXLO_DEBUG(("Line %ld, Pos (%ld,%ld), Height %ld",
1229 (long int) GetLineNumber(),
1230 (long int) pos
.x
, (long int) pos
.y
,
1231 (long int) GetHeight()));
1232 if(m_ObjectList
.begin() != NULLIT
)
1233 (**m_ObjectList
.begin()).Debug();
1239 wxLayoutLine::Copy(wxLayoutList
*llist
,
1243 CoordType firstOffset
, lastOffset
;
1245 if(to
== -1) to
= GetLength();
1247 wxLOiterator first
= FindObject(from
, &firstOffset
);
1248 wxLOiterator last
= FindObject(to
, &lastOffset
);
1250 // Common special case: only one object
1251 if( first
!= NULLIT
&& last
!= NULLIT
&& *first
== *last
)
1253 if( (**first
).GetType() == WXLO_TYPE_TEXT
)
1255 llist
->Insert(new wxLayoutObjectText(
1256 ((wxLayoutObjectText
1257 *)*first
)->GetText().substr(firstOffset
,
1258 lastOffset
-firstOffset
))
1262 else // what can we do?
1264 if(lastOffset
> firstOffset
) // i.e. +1 :-)
1265 llist
->Insert( (**first
).Copy() );
1270 // If we reach here, we can safely copy the whole first object from
1271 // the firstOffset position on:
1272 if((**first
).GetType() == WXLO_TYPE_TEXT
&& firstOffset
!= 0)
1274 llist
->Insert(new wxLayoutObjectText(
1275 ((wxLayoutObjectText
*)*first
)->GetText().substr(firstOffset
))
1278 else if(firstOffset
== 0)
1279 llist
->Insert( (**first
).Copy() );
1280 // else nothing to copy :-(
1282 // Now we copy all objects before the last one:
1283 wxLOiterator i
= first
; i
++;
1284 for( ; i
!= last
; i
++)
1285 llist
->Insert( (**i
).Copy() );
1287 // And now the last object:
1290 if( (**last
).GetType() == WXLO_TYPE_TEXT
)
1292 llist
->Insert(new wxLayoutObjectText(
1293 ((wxLayoutObjectText
*)*last
)->GetText().substr(0,lastOffset
))
1297 llist
->Insert( (**last
).Copy() );
1302 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1304 The wxLayoutList object
1306 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1308 wxLayoutList::wxLayoutList()
1311 InvalidateUpdateRect();
1315 wxLayoutList::~wxLayoutList()
1318 m_FirstLine
->DeleteLine(false, this);
1322 wxLayoutList::Empty(void)
1325 m_FirstLine
= m_FirstLine
->DeleteLine(false, this);
1327 m_CursorPos
= wxPoint(0,0);
1328 m_CursorScreenPos
= wxPoint(0,0);
1329 m_CursorSize
= wxPoint(0,0);
1330 m_FirstLine
= new wxLayoutLine(NULL
, this); // empty first line
1331 m_CursorLine
= m_FirstLine
;
1332 InvalidateUpdateRect();
1337 wxLayoutList::InternalClear(void)
1340 m_Selection
.m_selecting
= false;
1341 m_Selection
.m_valid
= false;
1343 m_DefaultSetting
.family
= wxSWISS
;
1344 m_DefaultSetting
.size
= WXLO_DEFAULTFONTSIZE
;
1345 m_DefaultSetting
.style
= wxNORMAL
;
1346 m_DefaultSetting
.weight
= wxNORMAL
;
1347 m_DefaultSetting
.underline
= 0;
1348 m_DefaultSetting
.m_fg_valid
= TRUE
;
1349 m_DefaultSetting
.m_fg
= *wxBLACK
;
1350 m_DefaultSetting
.m_bg_valid
= TRUE
;
1351 m_DefaultSetting
.m_bg
= *wxWHITE
;
1353 m_CurrentSetting
= m_DefaultSetting
;
1357 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1358 int underline
, wxColour
*fg
,
1361 if(family
!= -1) m_CurrentSetting
.family
= family
;
1362 if(size
!= -1) m_CurrentSetting
.size
= size
;
1363 if(style
!= -1) m_CurrentSetting
.style
= style
;
1364 if(weight
!= -1) m_CurrentSetting
.weight
= weight
;
1365 if(underline
!= -1) m_CurrentSetting
.underline
= underline
!= 0;
1366 if(fg
) m_CurrentSetting
.m_fg
= *fg
;
1367 if(bg
) m_CurrentSetting
.m_bg
= *bg
;
1369 new wxLayoutObjectCmd(
1370 m_CurrentSetting
.family
,
1371 m_CurrentSetting
.size
,
1372 m_CurrentSetting
.style
,
1373 m_CurrentSetting
.weight
,
1374 m_CurrentSetting
.underline
,
1379 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1380 int underline
, char const *fg
, char const *bg
)
1388 cfg
= wxTheColourDatabase
->FindColour(fg
);
1390 cbg
= wxTheColourDatabase
->FindColour(bg
);
1392 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
1396 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
1397 int underline
, wxColour
*fg
, wxColour
*bg
)
1400 m_DefaultSetting
= wxLayoutStyleInfo(family
, size
, style
, weight
,
1402 m_CurrentSetting
= m_DefaultSetting
;
1406 wxLayoutList::FindText(const wxString
&needle
, const wxPoint
&cpos
) const
1411 for(line
= m_FirstLine
;
1413 line
= line
->GetNextLine())
1415 if(line
->GetLineNumber() >= cpos
.y
)
1417 xpos
= line
->FindText(needle
,
1418 (line
->GetLineNumber() == cpos
.y
) ?
1421 return wxPoint(xpos
, line
->GetLineNumber());
1424 return wxPoint(-1,-1);
1429 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
1431 SetUpdateRect(m_CursorScreenPos
);
1432 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1433 wxLayoutLine
*line
= m_FirstLine
;
1434 while(line
&& line
->GetLineNumber() != p
.y
)
1435 line
= line
->GetNextLine();
1436 if(line
&& line
->GetLineNumber() == p
.y
) // found it
1438 m_CursorPos
.y
= p
.y
;
1439 m_CursorLine
= line
;
1440 CoordType len
= line
->GetLength();
1443 m_CursorPos
.x
= p
.x
;
1448 m_CursorPos
.x
= len
;
1456 wxLayoutList::MoveCursorVertically(int n
)
1458 SetUpdateRect(m_CursorScreenPos
);
1459 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1461 if(n
< 0) // move up
1463 if(m_CursorLine
== m_FirstLine
) return false;
1464 while(n
< 0 && m_CursorLine
)
1466 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1472 m_CursorLine
= m_FirstLine
;
1478 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1479 m_CursorPos
.x
= m_CursorLine
->GetLength();
1485 wxLayoutLine
*last
= m_CursorLine
;
1486 if(! m_CursorLine
->GetNextLine()) return false;
1487 while(n
> 0 && m_CursorLine
)
1491 m_CursorLine
= m_CursorLine
->GetNextLine();
1495 m_CursorLine
= last
;
1501 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1502 m_CursorPos
.x
= m_CursorLine
->GetLength();
1510 wxLayoutList::MoveCursorHorizontally(int n
)
1512 SetUpdateRect(m_CursorScreenPos
);
1513 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1517 if(m_CursorPos
.x
== 0) // at begin of line
1519 if(! MoveCursorVertically(-1))
1521 MoveCursorToEndOfLine();
1526 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1527 m_CursorPos
.x
-= move
; n
+= move
;
1532 int len
= m_CursorLine
->GetLength();
1533 if(m_CursorPos
.x
== len
) // at end of line
1535 if(! MoveCursorVertically(1))
1537 MoveCursorToBeginOfLine();
1542 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1543 m_CursorPos
.x
+= move
;
1550 wxLayoutList::Insert(wxString
const &text
)
1552 wxASSERT(m_CursorLine
);
1553 SetUpdateRect(m_CursorScreenPos
);
1554 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1555 m_CursorLine
->Insert(m_CursorPos
.x
, text
);
1556 m_CursorPos
.x
+= text
.Length();
1557 m_CursorLine
->RecalculatePositions(true, this); //FIXME needed?
1562 wxLayoutList::Insert(wxLayoutObject
*obj
)
1564 wxASSERT(m_CursorLine
);
1565 SetUpdateRect(m_CursorScreenPos
);
1566 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1567 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
1568 m_CursorPos
.x
+= obj
->GetLength();
1569 m_CursorLine
->RecalculatePositions(true, this); //FIXME needed?
1574 wxLayoutList::Insert(wxLayoutList
*llist
)
1579 for(wxLayoutLine
*line
= llist
->GetFirstLine();
1581 line
= line
->GetNextLine()
1584 for(wxLOiterator i
= line
->GetFirstObject();
1597 wxLayoutList::LineBreak(void)
1599 wxASSERT(m_CursorLine
);
1600 bool setFirst
= (m_CursorLine
== m_FirstLine
&& m_CursorPos
.x
== 0);
1601 SetUpdateRect(m_CursorScreenPos
);
1602 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1603 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
, this);
1604 if(setFirst
) // we were at beginning of first line
1605 m_FirstLine
= m_CursorLine
->GetPreviousLine();
1606 if(m_CursorPos
.x
!= 0)
1609 // doesn't help m_CursorLine.MarkDirty();
1610 m_CursorLine
->RecalculatePositions(true, this); //FIXME needed?
1615 wxLayoutList::WrapLine(CoordType column
)
1617 if(m_CursorPos
.x
<= column
|| column
< 1)
1618 return false; // do nothing yet
1621 CoordType xpos
= m_CursorLine
->GetWrapPosition(column
);
1623 return false; // cannot break line
1625 CoordType newpos
= m_CursorPos
.x
- xpos
- 1;
1626 m_CursorPos
.x
= xpos
;
1627 SetUpdateRect(m_CursorScreenPos
);
1628 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1630 Delete(1); // delete the space
1631 m_CursorPos
.x
= newpos
;
1632 m_CursorLine
->RecalculatePositions(true, this); //FIXME needed?
1638 wxLayoutList::Delete(CoordType npos
)
1640 wxASSERT(m_CursorLine
);
1641 SetUpdateRect(m_CursorScreenPos
);
1642 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1646 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
1649 // More to delete, continue on next line.
1650 // First, check if line is empty:
1651 if(m_CursorLine
->GetLength() == 0)
1652 { // in this case, updating could probably be optimised
1654 wxASSERT(DeleteLines(1) == 0);
1663 // Need to join next line
1664 if(! m_CursorLine
->GetNextLine())
1668 m_CursorLine
->MergeNextLine(this);
1674 m_CursorLine
->RecalculatePositions(true, this); //FIXME needed?
1679 wxLayoutList::DeleteLines(int n
)
1681 wxASSERT(m_CursorLine
);
1683 SetUpdateRect(m_CursorScreenPos
);
1684 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1687 if(!m_CursorLine
->GetNextLine())
1688 { // we cannot delete this line, but we can clear it
1689 MoveCursorToBeginOfLine();
1690 DeleteToEndOfLine();
1691 m_CursorLine
->RecalculatePositions(2, this);
1695 line
= m_CursorLine
;
1696 m_CursorLine
= m_CursorLine
->DeleteLine(true, this);
1698 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
1699 wxASSERT(m_FirstLine
);
1700 wxASSERT(m_CursorLine
);
1702 m_CursorLine
->RecalculatePositions(2, this);
1707 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
)
1709 wxLayoutLine
*line
= m_FirstLine
;
1711 // first, make sure everything is calculated - this might not be
1712 // needed, optimise it later
1713 ApplyStyle(&m_DefaultSetting
, dc
);
1716 line
->RecalculatePosition(this); // so we don't need to do it all the time
1717 // little condition to speed up redrawing:
1718 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1719 line
= line
->GetNextLine();
1724 wxLayoutList::UpdateCursorScreenPos(wxDC
&dc
)
1726 wxASSERT(m_CursorLine
);
1727 m_CursorLine
->Layout(dc
, this, (wxPoint
*)&m_CursorScreenPos
, (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
1731 wxLayoutList::GetCursorScreenPos(wxDC
&dc
)
1733 UpdateCursorScreenPos(dc
);
1734 return m_CursorScreenPos
;
1738 Is called before each Draw(). Now, it will re-layout all lines which
1742 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
, bool forceAll
)
1744 wxLayoutLine
*line
= m_FirstLine
;
1746 // first, make sure everything is calculated - this might not be
1747 // needed, optimise it later
1748 ApplyStyle(&m_DefaultSetting
, dc
);
1751 if(forceAll
|| line
->IsDirty())
1753 line
->GetStyleInfo() = m_CurrentSetting
;
1754 if(line
== m_CursorLine
)
1755 line
->Layout(dc
, this, (wxPoint
*)&m_CursorScreenPos
,
1756 (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
1758 line
->Layout(dc
, this);
1759 // little condition to speed up redrawing:
1760 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1762 line
->RecalculatePosition(this);
1763 line
= line
->GetNextLine();
1766 ///FIXME: disabled for now
1768 // can only be 0 if we are on the first line and have no next line
1769 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
1770 m_CursorLine
->GetNextLine() == NULL
&&
1771 m_CursorLine
== m_FirstLine
));
1773 SetUpdateRect(m_CursorScreenPos
);
1774 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1778 wxLayoutList::Draw(wxDC
&dc
,
1779 wxPoint
const &offset
,
1783 wxLayoutLine
*line
= m_FirstLine
;
1786 ApplyStyle(&m_DefaultSetting
, dc
);
1787 wxBrush
brush(m_CurrentSetting
.m_bg
, wxSOLID
);
1792 // only draw if between top and bottom:
1793 if((top
== -1 || line
->GetPosition().y
+ line
->GetHeight() >= top
))
1794 line
->Draw(dc
, this, offset
);
1796 line
->Layout(dc
, this);
1797 // little condition to speed up redrawing:
1798 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1799 line
= line
->GetNextLine();
1801 InvalidateUpdateRect();
1803 WXLO_DEBUG(("Selection is %s : l%d,%ld/%ld,%ld",
1804 m_Selection
.m_valid
? "valid" : "invalid",
1805 m_Selection
.m_CursorA
.x
, m_Selection
.m_CursorA
.y
,
1806 m_Selection
.m_CursorB
.x
, m_Selection
.m_CursorB
.y
));
1810 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
,
1814 // First, find the right line:
1815 wxLayoutLine
*line
= m_FirstLine
;
1818 // we need to run a layout here to get font sizes right :-(
1819 ApplyStyle(&m_DefaultSetting
, dc
);
1822 p
= line
->GetPosition();
1823 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
1825 line
->Layout(dc
, this);
1826 line
= line
->GetNextLine();
1830 if(found
) *found
= false;
1831 return NULL
; // not found
1833 if(cursorPos
) cursorPos
->y
= line
->GetLineNumber();
1834 // Now, find the object in the line:
1835 wxLOiterator i
= line
->FindObjectScreen(dc
, pos
.x
,
1836 cursorPos
? & cursorPos
->x
: NULL
,
1838 return (i
== NULLIT
) ? NULL
: *i
;
1843 wxLayoutList::GetSize(void) const
1846 *line
= m_FirstLine
,
1849 return wxPoint(0,0);
1851 wxPoint
maxPoint(0,0);
1856 if(line
->GetWidth() > maxPoint
.x
)
1857 maxPoint
.x
= line
->GetWidth();
1859 line
= line
->GetNextLine();
1862 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
1868 wxLayoutList::DrawCursor(wxDC
&dc
, bool active
, wxPoint
const &translate
)
1871 coords
= m_CursorScreenPos
;
1872 coords
.x
+= translate
.x
;
1873 coords
.y
+= translate
.y
;
1875 #ifdef WXLAYOUT_DEBUG
1876 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
1877 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
1878 (long)coords
.x
, (long)coords
.y
,
1879 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
1880 (long)m_CursorLine
->GetLineNumber(),
1881 (long)m_CursorLine
->GetLength()));
1884 dc
.SetBrush(*wxBLACK_BRUSH
);
1885 dc
.SetLogicalFunction(wxXOR
);
1886 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
1889 dc
.DrawRectangle(coords
.x
, coords
.y
,
1890 m_CursorSize
.x
, m_CursorSize
.y
);
1891 SetUpdateRect(coords
.x
, coords
.y
);
1892 SetUpdateRect(coords
.x
+m_CursorSize
.x
, coords
.y
+m_CursorSize
.y
);
1896 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
1897 coords
.x
, coords
.y
);
1898 SetUpdateRect(coords
.x
, coords
.y
+m_CursorSize
.y
-1);
1899 SetUpdateRect(coords
.x
, coords
.y
);
1901 dc
.SetLogicalFunction(wxCOPY
);
1902 //dc.SetBrush(wxNullBrush);
1906 wxLayoutList::SetUpdateRect(CoordType x
, CoordType y
)
1908 if(m_UpdateRectValid
)
1909 GrowRect(m_UpdateRect
, x
, y
);
1914 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
1915 m_UpdateRect
.height
= 4;// wxGTK :-)
1916 m_UpdateRectValid
= true;
1921 wxLayoutList::StartSelection(wxPoint cpos
)
1925 WXLO_DEBUG(("Starting selection at %ld/%ld", cpos
.x
, cpos
.y
));
1926 m_Selection
.m_CursorA
= cpos
;
1927 m_Selection
.m_CursorB
= cpos
;
1928 m_Selection
.m_selecting
= true;
1929 m_Selection
.m_valid
= false;
1933 wxLayoutList::ContinueSelection(wxPoint cpos
)
1937 wxASSERT(m_Selection
.m_selecting
== true);
1938 wxASSERT(m_Selection
.m_valid
== false);
1939 WXLO_DEBUG(("Continuing selection at %ld/%ld", cpos
.x
, cpos
.y
));
1940 if(m_Selection
.m_CursorB
<= cpos
)
1941 m_Selection
.m_CursorB
= cpos
;
1943 m_Selection
.m_CursorA
= cpos
;
1944 // We always want m_CursorA <= m_CursorB!
1945 if(! (m_Selection
.m_CursorA
<= m_Selection
.m_CursorB
))
1947 wxPoint help
= m_Selection
.m_CursorB
;
1948 m_Selection
.m_CursorB
= m_Selection
.m_CursorA
;
1949 m_Selection
.m_CursorA
= help
;
1954 wxLayoutList::EndSelection(wxPoint cpos
)
1958 ContinueSelection(cpos
);
1959 WXLO_DEBUG(("Ending selection at %ld/%ld", cpos
.x
, cpos
.y
));
1960 m_Selection
.m_selecting
= false;
1961 m_Selection
.m_valid
= true;
1966 wxLayoutList::IsSelecting(void)
1968 return m_Selection
.m_selecting
;
1972 wxLayoutList::IsSelected(const wxPoint
&cursor
)
1974 if(! m_Selection
.m_valid
&& ! m_Selection
.m_selecting
)
1976 return m_Selection
.m_CursorA
<= cursor
1977 && cursor
<= m_Selection
.m_CursorB
;
1981 /** Tests whether this layout line is selected and needs
1983 @param line to test for
1984 @return 0 = not selected, 1 = fully selected, -1 = partially
1988 wxLayoutList::IsSelected(const wxLayoutLine
*line
, CoordType
*from
,
1991 wxASSERT(line
); wxASSERT(to
); wxASSERT(from
);
1993 if(! m_Selection
.m_valid
&& ! m_Selection
.m_selecting
)
1996 CoordType y
= line
->GetLineNumber();
1997 if(m_Selection
.m_CursorA
.y
< y
&& m_Selection
.m_CursorB
.y
> y
)
1999 else if(m_Selection
.m_CursorA
.y
== y
)
2001 *from
= m_Selection
.m_CursorA
.x
;
2002 if(m_Selection
.m_CursorB
.y
== y
)
2003 *to
= m_Selection
.m_CursorB
.x
;
2005 *to
= line
->GetLength();
2008 else if(m_Selection
.m_CursorB
.y
== y
)
2010 *to
= m_Selection
.m_CursorB
.x
;
2011 if(m_Selection
.m_CursorA
.y
== y
)
2012 *from
= m_Selection
.m_CursorA
.x
;
2022 wxLayoutList::DeleteSelection(void)
2024 if(! m_Selection
.m_valid
)
2027 m_Selection
.m_valid
= false;
2029 // Only delete part of the current line?
2030 if(m_Selection
.m_CursorA
.y
== m_Selection
.m_CursorB
.y
)
2032 MoveCursorTo(m_Selection
.m_CursorA
);
2033 Delete(m_Selection
.m_CursorB
.x
- m_Selection
.m_CursorA
.x
);
2042 for(firstLine
= m_FirstLine
;
2043 firstLine
&& firstLine
->GetLineNumber() < m_Selection
.m_CursorA
.y
;
2044 firstLine
=firstLine
->GetNextLine())
2046 if(!firstLine
|| firstLine
->GetLineNumber() != m_Selection
.m_CursorA
.y
)
2050 for(lastLine
= m_FirstLine
;
2051 lastLine
&& lastLine
->GetLineNumber() < m_Selection
.m_CursorB
.y
;
2052 lastLine
=lastLine
->GetNextLine())
2054 if(!lastLine
|| lastLine
->GetLineNumber() != m_Selection
.m_CursorB
.y
)
2058 // We now know that the two lines are different:
2060 // First, delete what's left of this line:
2061 MoveCursorTo(m_Selection
.m_CursorA
);
2062 DeleteToEndOfLine();
2064 wxLayoutLine
*nextLine
= firstLine
->GetNextLine();
2065 while(nextLine
&& nextLine
!= lastLine
)
2066 nextLine
= nextLine
->DeleteLine(false, this);
2068 // Now nextLine = lastLine;
2069 Delete(1); // This joins firstLine and nextLine
2070 Delete(m_Selection
.m_CursorB
.x
); // This deletes the first x
2074 firstLine
->RecalculatePositions(1, this);
2077 /// Starts highlighting the selection
2079 wxLayoutList::StartHighlighting(wxDC
&dc
)
2082 dc
.SetTextForeground(m_CurrentSetting
.m_bg
);
2083 dc
.SetTextBackground(m_CurrentSetting
.m_fg
);
2087 /// Ends highlighting the selection
2089 wxLayoutList::EndHighlighting(wxDC
&dc
)
2092 dc
.SetTextForeground(m_CurrentSetting
.m_fg
);
2093 dc
.SetTextBackground(m_CurrentSetting
.m_bg
);
2099 wxLayoutList::Copy(const wxPoint
&from
,
2106 for(firstLine
= m_FirstLine
;
2107 firstLine
&& firstLine
->GetLineNumber() < from
.y
;
2108 firstLine
=firstLine
->GetNextLine())
2110 if(!firstLine
|| firstLine
->GetLineNumber() != from
.y
)
2113 for(lastLine
= m_FirstLine
;
2114 lastLine
&& lastLine
->GetLineNumber() < to
.y
;
2115 lastLine
=lastLine
->GetNextLine())
2117 if(!lastLine
|| lastLine
->GetLineNumber() != to
.y
)
2122 wxLayoutLine
*tmp
= firstLine
;
2123 firstLine
= lastLine
;
2127 wxLayoutList
*llist
= new wxLayoutList();
2129 if(firstLine
== lastLine
)
2131 firstLine
->Copy(llist
, from
.x
, to
.x
);
2135 // Extract objects from first line
2136 firstLine
->Copy(llist
, from
.x
);
2138 // Extract all lines between
2139 for(wxLayoutLine
*line
= firstLine
->GetNextLine();
2141 line
= line
->GetNextLine())
2146 // Extract objects from last line
2147 lastLine
->Copy(llist
, 0, to
.x
);
2153 wxLayoutList::GetSelection(wxLayoutDataObject
*wxlo
, bool invalidate
)
2155 if(! m_Selection
.m_valid
)
2157 if(m_Selection
.m_selecting
)
2163 if(invalidate
) m_Selection
.m_valid
= false;
2165 wxLayoutList
*llist
= Copy( m_Selection
.m_CursorA
,
2166 m_Selection
.m_CursorB
);
2168 if(wxlo
) // export as data object, too
2172 wxLayoutExportObject
*export
;
2173 wxLayoutExportStatus
status(llist
);
2174 while((export
= wxLayoutExport( &status
, WXLO_EXPORT_AS_OBJECTS
)) != NULL
)
2176 if(export
->type
== WXLO_EXPORT_EMPTYLINE
)
2177 ; //FIXME missing support for linebreaks in string format
2179 export
->content
.object
->Write(string
);
2182 wxlo
->SetData(string
.c_str(), string
.Length()+1);
2189 #define COPY_SI(what) if(si->what != -1) { m_CurrentSetting.what = si->what; fontChanged = TRUE; }
2192 wxLayoutList::ApplyStyle(wxLayoutStyleInfo
*si
, wxDC
&dc
)
2194 bool fontChanged
= FALSE
;
2201 dc
.SetFont( m_FontCache
.GetFont(m_CurrentSetting
) );
2205 m_CurrentSetting
.m_fg
= si
->m_fg
;
2206 dc
.SetTextForeground(m_CurrentSetting
.m_fg
);
2210 m_CurrentSetting
.m_bg
= si
->m_bg
;
2211 dc
.SetTextBackground(m_CurrentSetting
.m_bg
);
2216 #ifdef WXLAYOUT_DEBUG
2219 wxLayoutList::Debug(void)
2224 for(line
= m_FirstLine
;
2226 line
= line
->GetNextLine())
2233 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2237 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2239 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
2240 wxString
const & title
)
2247 wxLayoutPrintout::~wxLayoutPrintout()
2252 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
2254 // The following bit is taken from the printing sample, let's see
2255 // whether it works for us.
2257 /* You might use THIS code to set the printer DC to ROUGHLY reflect
2258 * the screen text size. This page also draws lines of actual length 5cm
2261 // Get the logical pixels per inch of screen and printer
2262 int ppiScreenX
, ppiScreenY
;
2263 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
2264 int ppiPrinterX
, ppiPrinterY
;
2265 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
2267 if(ppiScreenX
== 0) // not yet set, need to guess
2272 if(ppiPrinterX
== 0) // not yet set, need to guess
2278 // This scales the DC so that the printout roughly represents the
2279 // the screen scaling. The text point size _should_ be the right size
2280 // but in fact is too small for some reason. This is a detail that will
2281 // need to be addressed at some point but can be fudged for the
2283 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
2285 // Now we have to check in case our real page size is reduced
2286 // (e.g. because we're drawing to a print preview memory DC)
2287 int pageWidth
, pageHeight
;
2289 dc
->GetSize(&w
, &h
);
2290 GetPageSizePixels(&pageWidth
, &pageHeight
);
2291 if(pageWidth
!= 0) // doesn't work always
2293 // If printer pageWidth == current DC width, then this doesn't
2294 // change. But w might be the preview bitmap width, so scale down.
2295 scale
= scale
* (float)(w
/(float)pageWidth
);
2297 dc
->SetUserScale(scale
, scale
);
2301 bool wxLayoutPrintout::OnPrintPage(int page
)
2310 top
= (page
- 1)*m_PrintoutHeight
;
2311 bottom
= top
+ m_PrintoutHeight
;
2312 // SetDeviceOrigin() doesn't work here, so we need to manually
2313 // translate all coordinates.
2314 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
2315 m_llist
->Draw(*dc
, translate
, top
, bottom
);
2322 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
2324 /* We allocate a temporary wxDC for printing, so that we can
2325 determine the correct paper size and scaling. We don't actually
2326 print anything on it. */
2328 wxPrinterDC
psdc("","",WXLLIST_TEMPFILE
,false);
2330 wxPostScriptDC
psdc(WXLLIST_TEMPFILE
,false);
2333 float scale
= ScaleDC(&psdc
);
2335 psdc
.GetSize(&m_PageWidth
, &m_PageHeight
);
2336 // This sets a left/top origin of 15% and 20%:
2337 m_Offset
= wxPoint((15*m_PageWidth
)/100, m_PageHeight
/20);
2339 // This is the length of the printable area.
2340 m_PrintoutHeight
= m_PageHeight
- (int) (m_PageHeight
* 0.15);
2341 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
2345 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
2348 *maxPage
= m_NumOfPages
;
2351 *selPageTo
= m_NumOfPages
;
2352 wxRemoveFile(WXLLIST_TEMPFILE
);
2355 bool wxLayoutPrintout::HasPage(int pageNum
)
2357 return pageNum
<= m_NumOfPages
;
2361 Stupid wxWindows doesn't draw proper ellipses, so we comment this
2362 out. It's a waste of paper anyway.
2366 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
2367 wxPoint topleft
, wxPoint bottomright
,
2370 // make backups of all essential parameters
2371 const wxBrush
& brush
= dc
.GetBrush();
2372 const wxPen
& pen
= dc
.GetPen();
2373 const wxFont
& font
= dc
.GetFont();
2375 dc
.SetBrush(*wxWHITE_BRUSH
);
2376 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
2377 dc
.DrawRoundedRectangle(topleft
.x
,
2378 topleft
.y
,bottomright
.x
-topleft
.x
,
2379 bottomright
.y
-topleft
.y
);
2380 dc
.SetBrush(*wxBLACK_BRUSH
);
2381 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
2382 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
2386 page
= "9999/9999 "; // many pages...
2388 dc
.GetTextExtent(page
,&w
,&h
);
2389 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
2390 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
2391 dc
.GetTextExtent("XXXX", &w
,&h
);
2392 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);
2403 wxFontCache::GetFont(int family
, int size
, int style
, int weight
,
2406 for(wxFCEList::iterator i
= m_FontList
.begin();
2407 i
!= m_FontList
.end(); i
++)
2408 if( (**i
).Matches(family
, size
, style
, weight
, underline
) )
2409 return (**i
).GetFont();
2411 wxFontCacheEntry
*fce
= new wxFontCacheEntry(family
, size
, style
,
2413 m_FontList
.push_back(fce
);
2414 return fce
->GetFont();