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
> (signed)m_Text
.Length() )
232 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 //FIXME: is this really needed? We run Layout() anyway.
602 // Recursing here, drives computation time up exponentially, as
603 // each line will cause all following lines to be recalculated.
604 // Yes, or linenumbers go wrong.
606 wxASSERT(recurse
>= 0);
607 wxPoint pos
= m_Position
;
608 CoordType height
= m_Height
;
610 // WXLO_TRACE("RecalculatePositions()");
611 RecalculatePosition(llist
);
615 m_Next
->RecalculatePositions(--recurse
, llist
);
616 else if(pos
!= m_Position
|| m_Height
!= height
)
617 m_Next
->RecalculatePositions(0, llist
);
621 wxLayoutObjectList::iterator
622 wxLayoutLine::FindObject(CoordType xpos
, CoordType
*offset
) const
626 wxLayoutObjectList::iterator
629 CoordType x
= 0, len
;
631 /* We search through the objects. As we don't like returning the
632 object that the cursor is behind, we just remember such an
633 object in "found" so we can return it if there is really no
634 further object following it. */
635 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
637 len
= (**i
).GetLength();
638 if( x
<= xpos
&& xpos
<= x
+ len
)
641 if(xpos
== x
+ len
) // is there another object behind?
643 else // we are really inside this object
646 x
+= (**i
).GetLength();
648 return found
; // ==NULL if really none found
651 wxLayoutObjectList::iterator
652 wxLayoutLine::FindObjectScreen(wxDC
&dc
,
653 CoordType xpos
, CoordType
*cxpos
,
658 wxLayoutObjectList::iterator i
;
659 CoordType x
= 0, cx
= 0, width
;
661 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
663 width
= (**i
).GetWidth();
664 if( x
<= xpos
&& xpos
<= x
+ width
)
666 *cxpos
= cx
+ (**i
).GetOffsetScreen(dc
, xpos
-x
);
667 if(found
) *found
= true;
670 x
+= (**i
).GetWidth();
671 cx
+= (**i
).GetLength();
673 // behind last object:
675 if(found
) *found
= false;
676 return m_ObjectList
.tail();
679 /** Finds text in this line.
680 @param needle the text to find
681 @param xpos the position where to start the search
682 @return the cursoor coord where it was found or -1
685 wxLayoutLine::FindText(const wxString
&needle
, CoordType xpos
) const
690 wxString
const *text
;
692 for(wxLOiterator i
= m_ObjectList
.begin(); i
!= m_ObjectList
.end(); i
++)
694 if(cpos
>= xpos
) // search from here!
696 if((**i
).GetType() == WXLO_TYPE_TEXT
)
698 text
= & ((wxLayoutObjectText
*)(*i
))->GetText();
699 relpos
= text
->Find(needle
);
700 if(relpos
>= cpos
-xpos
) // -1 if not found
705 cpos
+= (**i
).GetLength();
708 return -1; // not found
712 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
715 wxASSERT(obj
!= NULL
);
716 //FIXME: this could be optimised, for now be prudent:
719 wxLOiterator i
= FindObject(xpos
, &offset
);
722 if(xpos
== 0 ) // aha, empty line!
724 m_ObjectList
.push_back(obj
);
725 m_Length
+= obj
->GetLength();
732 CoordType len
= (**i
).GetLength();
733 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
734 { // insert before this object
735 m_ObjectList
.insert(i
,obj
);
736 m_Length
+= obj
->GetLength();
741 if( i
== m_ObjectList
.tail()) // last object?
742 m_ObjectList
.push_back(obj
);
744 { // insert after current object
746 m_ObjectList
.insert(i
,obj
);
748 m_Length
+= obj
->GetLength();
751 /* Otherwise we need to split the current object.
752 Fortunately this can only be a text object. */
753 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
754 wxString left
, right
;
755 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
756 left
= tobj
->GetText().substr(0,offset
);
757 right
= tobj
->GetText().substr(offset
,len
-offset
);
758 // current text object gets set to right half
759 tobj
->GetText() = right
; // set new text
760 // before it we insert the new object
761 m_ObjectList
.insert(i
,obj
);
762 m_Length
+= obj
->GetLength();
763 // and before that we insert the left half
764 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
769 wxLayoutLine::Insert(CoordType xpos
, wxString text
)
772 //FIXME: this could be optimised, for now be prudent:
775 wxLOiterator i
= FindObject(xpos
, &offset
);
776 if(i
!= NULLIT
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
778 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
779 tobj
->GetText().insert(offset
, text
);
780 m_Length
+= text
.Length();
785 return Insert(xpos
, new wxLayoutObjectText(text
));
789 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
791 CoordType offset
, len
;
795 //FIXME: this could be optimised, for now be prudent:
797 wxLOiterator i
= FindObject(xpos
, &offset
);
800 if(i
== NULLIT
) return npos
;
801 // now delete from that object:
802 if((**i
).GetType() != WXLO_TYPE_TEXT
)
804 if(offset
!= 0) // at end of line after a non-text object
807 len
= (**i
).GetLength();
810 m_ObjectList
.erase(i
);
814 // tidy up: remove empty text objects
815 if((**i
).GetLength() == 0)
817 m_ObjectList
.erase(i
);
821 CoordType max
= (**i
).GetLength() - offset
;
822 if(npos
< max
) max
= npos
;
825 if(xpos
== GetLength())
828 { // at the end of an object
829 // move to begin of next object:
831 continue; // start over
836 if(offset
== 0 && max
== (**i
).GetLength())
837 m_ObjectList
.erase(i
); // remove the whole object
839 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
846 wxLayoutLine::DeleteWord(CoordType xpos
)
850 //FIXME: this could be optimised, for now be prudent:
853 wxLOiterator i
= FindObject(xpos
, &offset
);
857 if(i
== NULLIT
) return false;
858 if((**i
).GetType() != WXLO_TYPE_TEXT
)
860 // This should only happen when at end of line, behind a non-text
862 if(offset
== (**i
).GetLength()) return false;
863 m_Length
-= (**i
).GetLength(); // -1
864 m_ObjectList
.erase(i
);
865 return true; // we are done
869 if(offset
== (**i
).GetLength()) // at end of object
874 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
876 wxString str
= tobj
->GetText();
877 str
= str
.substr(offset
,str
.Length()-offset
);
878 // Find out how many positions we need to delete:
879 // 1. eat leading space
880 while(isspace(str
.c_str()[count
])) count
++;
881 // 2. eat the word itself:
882 while(isalnum(str
.c_str()[count
])) count
++;
884 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
885 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
890 wxASSERT(0); // we should never arrive here
894 wxLayoutLine::DeleteLine(bool update
, wxLayoutList
*llist
)
896 if(m_Next
) m_Next
->m_Previous
= m_Previous
;
897 if(m_Previous
) m_Previous
->m_Next
= m_Next
;
900 m_Next
->MoveLines(-1);
901 m_Next
->RecalculatePositions(1, llist
);
903 wxLayoutLine
*next
= m_Next
;
909 wxLayoutLine::Draw(wxDC
&dc
,
911 const wxPoint
& offset
) const
913 wxLayoutObjectList::iterator i
;
914 wxPoint pos
= offset
;
915 pos
= pos
+ GetPosition();
919 CoordType xpos
= 0; // cursorpos, lenght of line
921 CoordType from
, to
, tempto
;
922 //FIXME This doesn't work yet, needs updating afterr default
923 //settings for list or a wxLayoutObjectCmd have changed:
924 //llist->ApplyStyle(&((wxLayoutLine *)this)->m_StyleInfo, dc);
925 int highlight
= llist
->IsSelected(this, &from
, &to
);
926 // WXLO_DEBUG(("highlight=%d", highlight ));
927 if(highlight
== 1) // we need to draw the whole line inverted!
928 llist
->StartHighlighting(dc
);
930 llist
->EndHighlighting(dc
);
932 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
934 if(highlight
== -1) // partially highlight line
936 // parts of the line need highlighting
937 tempto
= xpos
+(**i
).GetLength();
938 (**i
).Draw(dc
, pos
, llist
, from
-xpos
, to
-xpos
);
941 (**i
).Draw(dc
, pos
, llist
);
942 pos
.x
+= (**i
).GetWidth();
943 xpos
+= (**i
).GetLength();
948 wxLayoutLine::Layout(wxDC
&dc
,
954 wxLayoutObjectList::iterator i
;
957 oldHeight
= m_Height
;
959 topHeight
, bottomHeight
; // above and below baseline
962 objTopHeight
, objBottomHeight
;
965 m_Height
= 0; m_BaseLine
= 0;
967 topHeight
= 0; bottomHeight
= 0;
969 bool cursorFound
= false;
975 *cursorPos
= m_Position
;
976 if(cursorSize
) *cursorSize
= wxPoint(0,0);
979 //FIXME This doesn't work yet, needs updating afterr default
980 //settings for list or a wxLayoutObjectCmd have changed:
981 //llist->ApplyStyle(&m_StyleInfo, dc);
982 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
984 (**i
).Layout(dc
, llist
);
985 size
= (**i
).GetSize(&objTopHeight
, &objBottomHeight
);
987 if(cursorPos
&& ! cursorFound
)
988 { // we need to check whether the text cursor is here
989 len
= (**i
).GetLength();
990 if(count
<= cx
&& count
+len
> cx
)
992 if((**i
).GetType() == WXLO_TYPE_TEXT
)
994 len
= cx
- count
; // pos in object
995 CoordType width
, height
, descent
;
996 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
997 &width
, &height
, &descent
);
998 cursorPos
->x
+= width
;
999 cursorPos
->y
= m_Position
.y
;
1001 if(len
< (**i
).GetLength())
1002 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
1004 str
= WXLO_CURSORCHAR
;
1005 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
1006 wxASSERT(cursorSize
);
1007 // Just in case some joker inserted an empty string object:
1008 if(width
== 0) width
= WXLO_MINIMUM_CURSOR_WIDTH
;
1009 if(height
== 0) height
= objHeight
;
1010 cursorSize
->x
= width
;
1011 cursorSize
->y
= height
;
1012 cursorFound
= true; // no more checks
1015 { // on some other object
1016 CoordType top
, bottom
; // unused
1017 *cursorSize
= (**i
).GetSize(&top
,&bottom
);
1018 cursorPos
->y
= m_Position
.y
;
1019 cursorFound
= true; // no more checks
1025 cursorPos
->x
+= (**i
).GetWidth();
1030 if(objHeight
> m_Height
) m_Height
= objHeight
;
1031 if(objTopHeight
> topHeight
) topHeight
= objTopHeight
;
1032 if(objBottomHeight
> bottomHeight
) bottomHeight
= objBottomHeight
;
1034 if(topHeight
+ bottomHeight
> m_Height
) m_Height
=
1035 topHeight
+bottomHeight
;
1036 m_BaseLine
= topHeight
;
1040 CoordType width
, height
, descent
;
1041 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1043 m_BaseLine
= m_Height
- descent
;
1047 // tell next line about coordinate change
1048 if(m_Next
&& objHeight
!= oldHeight
)
1049 m_Next
->RecalculatePositions(0, llist
);
1051 // We need to check whether we found a valid cursor size:
1054 // this might be the case if the cursor is at the end of the
1055 // line or on a command object:
1056 if(cursorSize
->y
< WXLO_MINIMUM_CURSOR_WIDTH
)
1058 CoordType width
, height
, descent
;
1059 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1060 cursorSize
->x
= width
;
1061 cursorSize
->y
= height
;
1063 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
1064 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
1066 RecalculatePositions(1, llist
);
1071 wxLayoutLine::Break(CoordType xpos
, wxLayoutList
*llist
)
1073 wxASSERT(xpos
>= 0);
1074 //FIXME: this could be optimised, for now be prudent:
1077 /* If we are at the begin of a line, we want to move all other
1078 lines down and stay with the cursor where we are. However, if we
1079 are in an empty line, we want to move down with it. */
1080 if(xpos
== 0 && GetLength() > 0)
1081 { // insert an empty line before this one
1082 wxLayoutLine
*prev
= new wxLayoutLine(m_Previous
, llist
);
1083 if(m_Previous
== NULL
)
1084 { // We were in first line, need to link in new empty line
1086 prev
->m_Next
= this;
1088 m_Previous
->m_Height
= 0; // this is a wild guess
1091 m_Next
->RecalculatePositions(1, llist
);
1096 wxLOiterator i
= FindObject(xpos
, &offset
);
1098 // must be at the end of the line then
1099 return new wxLayoutLine(this, llist
);
1102 wxLayoutLine
*newLine
= new wxLayoutLine(this, llist
);
1103 // split object at i:
1104 if((**i
).GetType() == WXLO_TYPE_TEXT
&& offset
!= 0)
1106 wxString left
, right
;
1107 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
1108 left
= tobj
->GetText().substr(0,offset
);
1109 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
1110 // current text object gets set to left half
1111 tobj
->GetText() = left
; // set new text
1112 newLine
->Append(new wxLayoutObjectText(right
));
1113 m_Length
-= right
.Length();
1114 i
++; // don't move this object to the new list
1118 i
++; // move objects from here to new list
1120 while(i
!= m_ObjectList
.end())
1122 newLine
->Append(*i
);
1123 m_Length
-= (**i
).GetLength();
1124 m_ObjectList
.remove(i
); // remove without deleting it
1127 m_Next
->RecalculatePositions(2, llist
);
1133 wxLayoutLine::MergeNextLine(wxLayoutList
*llist
)
1135 wxCHECK_RET(GetNextLine(),"wxLayout internal error: no next line to merge");
1136 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
1138 //FIXME: this could be optimised, for now be prudent:
1141 for(i
= list
.begin(); i
!= list
.end();)
1144 list
.remove(i
); // remove without deleting it
1146 wxASSERT(list
.empty());
1147 wxLayoutLine
*oldnext
= GetNextLine();
1148 SetNext(GetNextLine()->GetNextLine());
1150 GetNextLine()->MoveLines(-1);
1151 RecalculatePositions(1, llist
);
1155 wxLayoutLine::GetWrapPosition(CoordType column
)
1158 wxLOiterator i
= FindObject(column
, &offset
);
1159 if(i
== NULLIT
) return -1; // cannot wrap
1161 // go backwards through the list and look for space in text objects
1164 if((**i
).GetType() == WXLO_TYPE_TEXT
)
1168 if( isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1175 }while(offset
!= -1);
1176 i
--; // move on to previous object
1180 column
-= (**i
).GetLength();
1184 offset
= (**i
).GetLength();
1185 }while(i
!= NULLIT
);
1186 /* If we reached the begin of the list and have more than one
1187 object, that one is longer than the margin, so break behind
1190 i
= m_ObjectList
.begin();
1191 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1193 pos
+= (**i
).GetLength();
1196 if(i
== NULLIT
) return -1; //why should this happen?
1197 pos
+= (**i
).GetLength();
1199 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1201 pos
+= (**i
).GetLength();
1204 if(i
== NULLIT
) return -1; //this is possible, if there is only one text object
1205 // now we are at the second text object:
1206 pos
-= (**i
).GetLength();
1207 return pos
; // in front of it
1211 #ifdef WXLAYOUT_DEBUG
1213 wxLayoutLine::Debug(void)
1216 wxPoint pos
= GetPosition();
1217 WXLO_DEBUG(("Line %ld, Pos (%ld,%ld), Height %ld",
1218 (long int) GetLineNumber(),
1219 (long int) pos
.x
, (long int) pos
.y
,
1220 (long int) GetHeight()));
1221 if(m_ObjectList
.begin() != NULLIT
)
1222 (**m_ObjectList
.begin()).Debug();
1228 wxLayoutLine::Copy(wxLayoutList
*llist
,
1232 CoordType firstOffset
, lastOffset
;
1234 if(to
== -1) to
= GetLength();
1235 if(from
== to
) return;
1237 wxLOiterator first
= FindObject(from
, &firstOffset
);
1238 wxLOiterator last
= FindObject(to
, &lastOffset
);
1240 // Common special case: only one object
1241 if( first
!= NULLIT
&& last
!= NULLIT
&& *first
== *last
)
1243 if( (**first
).GetType() == WXLO_TYPE_TEXT
)
1245 llist
->Insert(new wxLayoutObjectText(
1246 ((wxLayoutObjectText
1247 *)*first
)->GetText().substr(firstOffset
,
1248 lastOffset
-firstOffset
))
1252 else // what can we do?
1254 if(lastOffset
> firstOffset
) // i.e. +1 :-)
1255 llist
->Insert( (**first
).Copy() );
1260 // If we reach here, we can safely copy the whole first object from
1261 // the firstOffset position on:
1262 if((**first
).GetType() == WXLO_TYPE_TEXT
&& firstOffset
!= 0)
1264 llist
->Insert(new wxLayoutObjectText(
1265 ((wxLayoutObjectText
*)*first
)->GetText().substr(firstOffset
))
1268 else if(firstOffset
== 0)
1269 llist
->Insert( (**first
).Copy() );
1270 // else nothing to copy :-(
1272 // Now we copy all objects before the last one:
1273 wxLOiterator i
= first
; i
++;
1274 for( ; i
!= last
; i
++)
1275 llist
->Insert( (**i
).Copy() );
1277 // And now the last object:
1280 if( (**last
).GetType() == WXLO_TYPE_TEXT
)
1282 llist
->Insert(new wxLayoutObjectText(
1283 ((wxLayoutObjectText
*)*last
)->GetText().substr(0,lastOffset
))
1287 llist
->Insert( (**last
).Copy() );
1292 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1294 The wxLayoutList object
1296 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1298 wxLayoutList::wxLayoutList()
1301 InvalidateUpdateRect();
1305 wxLayoutList::~wxLayoutList()
1308 m_FirstLine
->DeleteLine(false, this);
1312 wxLayoutList::Empty(void)
1315 m_FirstLine
= m_FirstLine
->DeleteLine(false, this);
1317 m_CursorPos
= wxPoint(0,0);
1318 m_CursorScreenPos
= wxPoint(0,0);
1319 m_CursorSize
= wxPoint(0,0);
1320 m_FirstLine
= new wxLayoutLine(NULL
, this); // empty first line
1321 m_CursorLine
= m_FirstLine
;
1322 InvalidateUpdateRect();
1327 wxLayoutList::InternalClear(void)
1330 m_Selection
.m_selecting
= false;
1331 m_Selection
.m_valid
= false;
1333 m_DefaultSetting
.family
= wxSWISS
;
1334 m_DefaultSetting
.size
= WXLO_DEFAULTFONTSIZE
;
1335 m_DefaultSetting
.style
= wxNORMAL
;
1336 m_DefaultSetting
.weight
= wxNORMAL
;
1337 m_DefaultSetting
.underline
= 0;
1338 m_DefaultSetting
.m_fg_valid
= TRUE
;
1339 m_DefaultSetting
.m_fg
= *wxBLACK
;
1340 m_DefaultSetting
.m_bg_valid
= TRUE
;
1341 m_DefaultSetting
.m_bg
= *wxWHITE
;
1343 m_CurrentSetting
= m_DefaultSetting
;
1347 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1348 int underline
, wxColour
*fg
,
1351 if(family
!= -1) m_CurrentSetting
.family
= family
;
1352 if(size
!= -1) m_CurrentSetting
.size
= size
;
1353 if(style
!= -1) m_CurrentSetting
.style
= style
;
1354 if(weight
!= -1) m_CurrentSetting
.weight
= weight
;
1355 if(underline
!= -1) m_CurrentSetting
.underline
= underline
!= 0;
1356 if(fg
) m_CurrentSetting
.m_fg
= *fg
;
1357 if(bg
) m_CurrentSetting
.m_bg
= *bg
;
1359 new wxLayoutObjectCmd(
1360 m_CurrentSetting
.family
,
1361 m_CurrentSetting
.size
,
1362 m_CurrentSetting
.style
,
1363 m_CurrentSetting
.weight
,
1364 m_CurrentSetting
.underline
,
1369 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1370 int underline
, char const *fg
, char const *bg
)
1378 cfg
= wxTheColourDatabase
->FindColour(fg
);
1380 cbg
= wxTheColourDatabase
->FindColour(bg
);
1382 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
1386 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
1387 int underline
, wxColour
*fg
, wxColour
*bg
)
1390 m_DefaultSetting
= wxLayoutStyleInfo(family
, size
, style
, weight
,
1392 m_CurrentSetting
= m_DefaultSetting
;
1396 wxLayoutList::FindText(const wxString
&needle
, const wxPoint
&cpos
) const
1401 for(line
= m_FirstLine
;
1403 line
= line
->GetNextLine())
1405 if(line
->GetLineNumber() >= cpos
.y
)
1407 xpos
= line
->FindText(needle
,
1408 (line
->GetLineNumber() == cpos
.y
) ?
1411 return wxPoint(xpos
, line
->GetLineNumber());
1414 return wxPoint(-1,-1);
1419 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
1421 SetUpdateRect(m_CursorScreenPos
);
1422 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1423 wxLayoutLine
*line
= m_FirstLine
;
1424 while(line
&& line
->GetLineNumber() != p
.y
)
1425 line
= line
->GetNextLine();
1426 if(line
&& line
->GetLineNumber() == p
.y
) // found it
1428 m_CursorPos
.y
= p
.y
;
1429 m_CursorLine
= line
;
1430 CoordType len
= line
->GetLength();
1433 m_CursorPos
.x
= p
.x
;
1438 m_CursorPos
.x
= len
;
1446 wxLayoutList::MoveCursorVertically(int n
)
1448 SetUpdateRect(m_CursorScreenPos
);
1449 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1451 if(n
< 0) // move up
1453 if(m_CursorLine
== m_FirstLine
) return false;
1454 while(n
< 0 && m_CursorLine
)
1456 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1462 m_CursorLine
= m_FirstLine
;
1468 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1469 m_CursorPos
.x
= m_CursorLine
->GetLength();
1475 wxLayoutLine
*last
= m_CursorLine
;
1476 if(! m_CursorLine
->GetNextLine()) return false;
1477 while(n
> 0 && m_CursorLine
)
1481 m_CursorLine
= m_CursorLine
->GetNextLine();
1485 m_CursorLine
= last
;
1491 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1492 m_CursorPos
.x
= m_CursorLine
->GetLength();
1500 wxLayoutList::MoveCursorHorizontally(int n
)
1502 SetUpdateRect(m_CursorScreenPos
);
1503 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1507 if(m_CursorPos
.x
== 0) // at begin of line
1509 if(! MoveCursorVertically(-1))
1511 MoveCursorToEndOfLine();
1516 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1517 m_CursorPos
.x
-= move
; n
+= move
;
1522 int len
= m_CursorLine
->GetLength();
1523 if(m_CursorPos
.x
== len
) // at end of line
1525 if(! MoveCursorVertically(1))
1527 MoveCursorToBeginOfLine();
1532 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1533 m_CursorPos
.x
+= move
;
1540 wxLayoutList::Insert(wxString
const &text
)
1542 wxASSERT(m_CursorLine
);
1543 SetUpdateRect(m_CursorScreenPos
);
1544 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1545 m_CursorLine
->Insert(m_CursorPos
.x
, text
);
1546 m_CursorPos
.x
+= text
.Length();
1547 m_CursorLine
->RecalculatePositions(true, this); //FIXME needed?
1552 wxLayoutList::Insert(wxLayoutObject
*obj
)
1554 wxASSERT(m_CursorLine
);
1555 if(! m_CursorLine
) m_CursorLine
= GetFirstLine();
1556 SetUpdateRect(m_CursorScreenPos
);
1557 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1558 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
1559 m_CursorPos
.x
+= obj
->GetLength();
1560 m_CursorLine
->RecalculatePositions(true, this); //FIXME needed?
1565 wxLayoutList::Insert(wxLayoutList
*llist
)
1570 for(wxLayoutLine
*line
= llist
->GetFirstLine();
1572 line
= line
->GetNextLine()
1575 for(wxLOiterator i
= line
->GetFirstObject();
1588 wxLayoutList::LineBreak(void)
1590 wxASSERT(m_CursorLine
);
1591 bool setFirst
= (m_CursorLine
== m_FirstLine
&& m_CursorPos
.x
== 0);
1592 SetUpdateRect(m_CursorScreenPos
);
1593 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1594 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
, this);
1595 if(setFirst
) // we were at beginning of first line
1596 m_FirstLine
= m_CursorLine
->GetPreviousLine();
1597 if(m_CursorPos
.x
!= 0)
1600 // doesn't help m_CursorLine.MarkDirty();
1601 m_CursorLine
->RecalculatePositions(true, this); //FIXME needed?
1606 wxLayoutList::WrapLine(CoordType column
)
1608 if(m_CursorPos
.x
<= column
|| column
< 1)
1609 return false; // do nothing yet
1612 CoordType xpos
= m_CursorLine
->GetWrapPosition(column
);
1614 return false; // cannot break line
1616 CoordType newpos
= m_CursorPos
.x
- xpos
- 1;
1617 m_CursorPos
.x
= xpos
;
1618 SetUpdateRect(m_CursorScreenPos
);
1619 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1621 Delete(1); // delete the space
1622 m_CursorPos
.x
= newpos
;
1623 m_CursorLine
->RecalculatePositions(true, this); //FIXME needed?
1629 wxLayoutList::Delete(CoordType npos
)
1631 wxASSERT(m_CursorLine
);
1632 SetUpdateRect(m_CursorScreenPos
);
1633 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1637 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
1640 // More to delete, continue on next line.
1641 // First, check if line is empty:
1642 if(m_CursorLine
->GetLength() == 0)
1643 { // in this case, updating could probably be optimised
1645 wxASSERT(DeleteLines(1) == 0);
1654 // Need to join next line
1655 if(! m_CursorLine
->GetNextLine())
1659 m_CursorLine
->MergeNextLine(this);
1665 m_CursorLine
->RecalculatePositions(true, this); //FIXME needed?
1670 wxLayoutList::DeleteLines(int n
)
1672 wxASSERT(m_CursorLine
);
1674 SetUpdateRect(m_CursorScreenPos
);
1675 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1678 if(!m_CursorLine
->GetNextLine())
1679 { // we cannot delete this line, but we can clear it
1680 MoveCursorToBeginOfLine();
1681 DeleteToEndOfLine();
1682 m_CursorLine
->RecalculatePositions(2, this);
1686 line
= m_CursorLine
;
1687 m_CursorLine
= m_CursorLine
->DeleteLine(true, this);
1689 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
1690 wxASSERT(m_FirstLine
);
1691 wxASSERT(m_CursorLine
);
1693 m_CursorLine
->RecalculatePositions(2, this);
1698 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
)
1700 wxLayoutLine
*line
= m_FirstLine
;
1702 // first, make sure everything is calculated - this might not be
1703 // needed, optimise it later
1704 ApplyStyle(&m_DefaultSetting
, dc
);
1707 line
->RecalculatePosition(this); // so we don't need to do it all the time
1708 // little condition to speed up redrawing:
1709 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1710 line
= line
->GetNextLine();
1715 wxLayoutList::UpdateCursorScreenPos(wxDC
&dc
)
1717 wxASSERT(m_CursorLine
);
1718 m_CursorLine
->Layout(dc
, this, (wxPoint
*)&m_CursorScreenPos
, (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
1722 wxLayoutList::GetCursorScreenPos(wxDC
&dc
)
1724 UpdateCursorScreenPos(dc
);
1725 return m_CursorScreenPos
;
1729 Is called before each Draw(). Now, it will re-layout all lines which
1733 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
, bool forceAll
)
1735 wxLayoutLine
*line
= m_FirstLine
;
1737 // first, make sure everything is calculated - this might not be
1738 // needed, optimise it later
1739 ApplyStyle(&m_DefaultSetting
, dc
);
1742 if(forceAll
|| line
->IsDirty())
1744 line
->GetStyleInfo() = m_CurrentSetting
;
1745 if(line
== m_CursorLine
)
1746 line
->Layout(dc
, this, (wxPoint
*)&m_CursorScreenPos
,
1747 (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
1749 line
->Layout(dc
, this);
1750 // little condition to speed up redrawing:
1751 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1753 line
->RecalculatePositions(1,this);
1754 line
= line
->GetNextLine();
1757 ///FIXME: disabled for now
1759 // can only be 0 if we are on the first line and have no next line
1760 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
1761 m_CursorLine
->GetNextLine() == NULL
&&
1762 m_CursorLine
== m_FirstLine
));
1764 SetUpdateRect(m_CursorScreenPos
);
1765 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1769 wxLayoutList::Draw(wxDC
&dc
,
1770 wxPoint
const &offset
,
1774 wxLayoutLine
*line
= m_FirstLine
;
1777 ApplyStyle(&m_DefaultSetting
, dc
);
1778 wxBrush
brush(m_CurrentSetting
.m_bg
, wxSOLID
);
1780 dc
.SetBackgroundMode(wxTRANSPARENT
);
1784 // only draw if between top and bottom:
1785 if((top
== -1 || line
->GetPosition().y
+ line
->GetHeight() >= top
))
1786 line
->Draw(dc
, this, offset
);
1788 line
->Layout(dc
, this);
1789 // little condition to speed up redrawing:
1790 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1791 line
= line
->GetNextLine();
1793 InvalidateUpdateRect();
1795 WXLO_DEBUG(("Selection is %s : l%d,%ld/%ld,%ld",
1796 m_Selection
.m_valid
? "valid" : "invalid",
1797 m_Selection
.m_CursorA
.x
, m_Selection
.m_CursorA
.y
,
1798 m_Selection
.m_CursorB
.x
, m_Selection
.m_CursorB
.y
));
1802 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
,
1806 // First, find the right line:
1807 wxLayoutLine
*line
= m_FirstLine
;
1810 // we need to run a layout here to get font sizes right :-(
1811 ApplyStyle(&m_DefaultSetting
, dc
);
1814 p
= line
->GetPosition();
1815 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
1817 line
->Layout(dc
, this);
1818 line
= line
->GetNextLine();
1822 if(found
) *found
= false;
1823 return NULL
; // not found
1825 if(cursorPos
) cursorPos
->y
= line
->GetLineNumber();
1826 // Now, find the object in the line:
1827 wxLOiterator i
= line
->FindObjectScreen(dc
, pos
.x
,
1828 cursorPos
? & cursorPos
->x
: NULL
,
1830 return (i
== NULLIT
) ? NULL
: *i
;
1835 wxLayoutList::GetSize(void) const
1838 *line
= m_FirstLine
,
1841 return wxPoint(0,0);
1843 wxPoint
maxPoint(0,0);
1848 if(line
->GetWidth() > maxPoint
.x
)
1849 maxPoint
.x
= line
->GetWidth();
1851 line
= line
->GetNextLine();
1854 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
1860 wxLayoutList::DrawCursor(wxDC
&dc
, bool active
, wxPoint
const &translate
)
1863 coords
= m_CursorScreenPos
;
1864 coords
.x
+= translate
.x
;
1865 coords
.y
+= translate
.y
;
1867 #ifdef WXLAYOUT_DEBUG
1868 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
1869 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
1870 (long)coords
.x
, (long)coords
.y
,
1871 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
1872 (long)m_CursorLine
->GetLineNumber(),
1873 (long)m_CursorLine
->GetLength()));
1876 dc
.SetBrush(*wxBLACK_BRUSH
);
1877 dc
.SetLogicalFunction(wxXOR
);
1878 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
1881 dc
.DrawRectangle(coords
.x
, coords
.y
,
1882 m_CursorSize
.x
, m_CursorSize
.y
);
1883 SetUpdateRect(coords
.x
, coords
.y
);
1884 SetUpdateRect(coords
.x
+m_CursorSize
.x
, coords
.y
+m_CursorSize
.y
);
1888 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
1889 coords
.x
, coords
.y
);
1890 SetUpdateRect(coords
.x
, coords
.y
+m_CursorSize
.y
-1);
1891 SetUpdateRect(coords
.x
, coords
.y
);
1893 dc
.SetLogicalFunction(wxCOPY
);
1894 //dc.SetBrush(wxNullBrush);
1898 wxLayoutList::SetUpdateRect(CoordType x
, CoordType y
)
1900 if(m_UpdateRectValid
)
1901 GrowRect(m_UpdateRect
, x
, y
);
1906 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
1907 m_UpdateRect
.height
= 4;// wxGTK :-)
1908 m_UpdateRectValid
= true;
1913 wxLayoutList::StartSelection(wxPoint cpos
)
1917 WXLO_DEBUG(("Starting selection at %ld/%ld", cpos
.x
, cpos
.y
));
1918 m_Selection
.m_CursorA
= cpos
;
1919 m_Selection
.m_CursorB
= cpos
;
1920 m_Selection
.m_selecting
= true;
1921 m_Selection
.m_valid
= false;
1925 wxLayoutList::ContinueSelection(wxPoint cpos
)
1929 wxASSERT(m_Selection
.m_selecting
== true);
1930 wxASSERT(m_Selection
.m_valid
== false);
1931 WXLO_DEBUG(("Continuing selection at %ld/%ld", cpos
.x
, cpos
.y
));
1932 if(m_Selection
.m_CursorB
<= cpos
)
1933 m_Selection
.m_CursorB
= cpos
;
1935 m_Selection
.m_CursorA
= cpos
;
1936 // We always want m_CursorA <= m_CursorB!
1937 if(! (m_Selection
.m_CursorA
<= m_Selection
.m_CursorB
))
1939 wxPoint help
= m_Selection
.m_CursorB
;
1940 m_Selection
.m_CursorB
= m_Selection
.m_CursorA
;
1941 m_Selection
.m_CursorA
= help
;
1946 wxLayoutList::EndSelection(wxPoint cpos
)
1950 ContinueSelection(cpos
);
1951 WXLO_DEBUG(("Ending selection at %ld/%ld", cpos
.x
, cpos
.y
));
1952 m_Selection
.m_selecting
= false;
1953 m_Selection
.m_valid
= true;
1958 wxLayoutList::IsSelecting(void)
1960 return m_Selection
.m_selecting
;
1964 wxLayoutList::IsSelected(const wxPoint
&cursor
)
1966 if(! m_Selection
.m_valid
&& ! m_Selection
.m_selecting
)
1968 return m_Selection
.m_CursorA
<= cursor
1969 && cursor
<= m_Selection
.m_CursorB
;
1973 /** Tests whether this layout line is selected and needs
1975 @param line to test for
1976 @return 0 = not selected, 1 = fully selected, -1 = partially
1980 wxLayoutList::IsSelected(const wxLayoutLine
*line
, CoordType
*from
,
1983 wxASSERT(line
); wxASSERT(to
); wxASSERT(from
);
1985 if(! m_Selection
.m_valid
&& ! m_Selection
.m_selecting
)
1988 CoordType y
= line
->GetLineNumber();
1989 if(m_Selection
.m_CursorA
.y
< y
&& m_Selection
.m_CursorB
.y
> y
)
1991 else if(m_Selection
.m_CursorA
.y
== y
)
1993 *from
= m_Selection
.m_CursorA
.x
;
1994 if(m_Selection
.m_CursorB
.y
== y
)
1995 *to
= m_Selection
.m_CursorB
.x
;
1997 *to
= line
->GetLength();
2000 else if(m_Selection
.m_CursorB
.y
== y
)
2002 *to
= m_Selection
.m_CursorB
.x
;
2003 if(m_Selection
.m_CursorA
.y
== y
)
2004 *from
= m_Selection
.m_CursorA
.x
;
2014 wxLayoutList::DeleteSelection(void)
2016 if(! m_Selection
.m_valid
)
2019 m_Selection
.m_valid
= false;
2021 // Only delete part of the current line?
2022 if(m_Selection
.m_CursorA
.y
== m_Selection
.m_CursorB
.y
)
2024 MoveCursorTo(m_Selection
.m_CursorA
);
2025 Delete(m_Selection
.m_CursorB
.x
- m_Selection
.m_CursorA
.x
);
2034 for(firstLine
= m_FirstLine
;
2035 firstLine
&& firstLine
->GetLineNumber() < m_Selection
.m_CursorA
.y
;
2036 firstLine
=firstLine
->GetNextLine())
2038 if(!firstLine
|| firstLine
->GetLineNumber() != m_Selection
.m_CursorA
.y
)
2042 for(lastLine
= m_FirstLine
;
2043 lastLine
&& lastLine
->GetLineNumber() < m_Selection
.m_CursorB
.y
;
2044 lastLine
=lastLine
->GetNextLine())
2046 if(!lastLine
|| lastLine
->GetLineNumber() != m_Selection
.m_CursorB
.y
)
2050 // We now know that the two lines are different:
2052 // First, delete what's left of this line:
2053 MoveCursorTo(m_Selection
.m_CursorA
);
2054 DeleteToEndOfLine();
2056 wxLayoutLine
*nextLine
= firstLine
->GetNextLine();
2057 while(nextLine
&& nextLine
!= lastLine
)
2058 nextLine
= nextLine
->DeleteLine(false, this);
2060 // Now nextLine = lastLine;
2061 Delete(1); // This joins firstLine and nextLine
2062 Delete(m_Selection
.m_CursorB
.x
); // This deletes the first x
2066 firstLine
->RecalculatePositions(1, this);
2069 /// Starts highlighting the selection
2071 wxLayoutList::StartHighlighting(wxDC
&dc
)
2074 dc
.SetTextForeground(m_CurrentSetting
.m_bg
);
2075 dc
.SetTextBackground(m_CurrentSetting
.m_fg
);
2076 dc
.SetBackgroundMode(wxSOLID
);
2080 /// Ends highlighting the selection
2082 wxLayoutList::EndHighlighting(wxDC
&dc
)
2085 dc
.SetTextForeground(m_CurrentSetting
.m_fg
);
2086 dc
.SetTextBackground(m_CurrentSetting
.m_bg
);
2087 dc
.SetBackgroundMode(wxTRANSPARENT
);
2093 wxLayoutList::Copy(const wxPoint
&from
,
2100 for(firstLine
= m_FirstLine
;
2101 firstLine
&& firstLine
->GetLineNumber() < from
.y
;
2102 firstLine
=firstLine
->GetNextLine())
2104 if(!firstLine
|| firstLine
->GetLineNumber() != from
.y
)
2107 for(lastLine
= m_FirstLine
;
2108 lastLine
&& lastLine
->GetLineNumber() < to
.y
;
2109 lastLine
=lastLine
->GetNextLine())
2111 if(!lastLine
|| lastLine
->GetLineNumber() != to
.y
)
2116 wxLayoutLine
*tmp
= firstLine
;
2117 firstLine
= lastLine
;
2121 wxLayoutList
*llist
= new wxLayoutList();
2123 if(firstLine
== lastLine
)
2125 firstLine
->Copy(llist
, from
.x
, to
.x
);
2129 // Extract objects from first line
2130 firstLine
->Copy(llist
, from
.x
);
2132 // Extract all lines between
2133 for(wxLayoutLine
*line
= firstLine
->GetNextLine();
2135 line
= line
->GetNextLine())
2140 // Extract objects from last line
2141 lastLine
->Copy(llist
, 0, to
.x
);
2147 wxLayoutList::GetSelection(wxLayoutDataObject
*wxlo
, bool invalidate
)
2149 if(! m_Selection
.m_valid
)
2151 if(m_Selection
.m_selecting
)
2157 if(invalidate
) m_Selection
.m_valid
= false;
2159 wxLayoutList
*llist
= Copy( m_Selection
.m_CursorA
,
2160 m_Selection
.m_CursorB
);
2162 if(llist
&& wxlo
) // export as data object, too
2166 wxLayoutExportObject
*export
;
2167 wxLayoutExportStatus
status(llist
);
2168 while((export
= wxLayoutExport( &status
, WXLO_EXPORT_AS_OBJECTS
)) != NULL
)
2170 if(export
->type
== WXLO_EXPORT_EMPTYLINE
)
2171 ; //FIXME missing support for linebreaks in string format
2173 export
->content
.object
->Write(string
);
2176 wxlo
->SetData(string
.c_str(), string
.Length()+1);
2183 #define COPY_SI(what) if(si->what != -1) { m_CurrentSetting.what = si->what; fontChanged = TRUE; }
2186 wxLayoutList::ApplyStyle(wxLayoutStyleInfo
*si
, wxDC
&dc
)
2188 bool fontChanged
= FALSE
;
2195 dc
.SetFont( m_FontCache
.GetFont(m_CurrentSetting
) );
2199 m_CurrentSetting
.m_fg
= si
->m_fg
;
2200 dc
.SetTextForeground(m_CurrentSetting
.m_fg
);
2204 m_CurrentSetting
.m_bg
= si
->m_bg
;
2205 dc
.SetTextBackground(m_CurrentSetting
.m_bg
);
2210 #ifdef WXLAYOUT_DEBUG
2213 wxLayoutList::Debug(void)
2218 for(line
= m_FirstLine
;
2220 line
= line
->GetNextLine())
2227 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2231 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2233 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
2234 wxString
const & title
)
2239 // remove any highlighting which could interfere with printing:
2240 m_llist
->StartSelection();
2241 m_llist
->EndSelection();
2244 wxLayoutPrintout::~wxLayoutPrintout()
2249 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
2251 // The following bit is taken from the printing sample, let's see
2252 // whether it works for us.
2254 /* You might use THIS code to set the printer DC to ROUGHLY reflect
2255 * the screen text size. This page also draws lines of actual length 5cm
2258 // Get the logical pixels per inch of screen and printer
2259 int ppiScreenX
, ppiScreenY
;
2260 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
2261 int ppiPrinterX
, ppiPrinterY
;
2262 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
2264 if(ppiScreenX
== 0) // not yet set, need to guess
2269 if(ppiPrinterX
== 0) // not yet set, need to guess
2275 // This scales the DC so that the printout roughly represents the
2276 // the screen scaling. The text point size _should_ be the right size
2277 // but in fact is too small for some reason. This is a detail that will
2278 // need to be addressed at some point but can be fudged for the
2280 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
2282 // Now we have to check in case our real page size is reduced
2283 // (e.g. because we're drawing to a print preview memory DC)
2284 int pageWidth
, pageHeight
;
2286 dc
->GetSize(&w
, &h
);
2287 GetPageSizePixels(&pageWidth
, &pageHeight
);
2288 if(pageWidth
!= 0) // doesn't work always
2290 // If printer pageWidth == current DC width, then this doesn't
2291 // change. But w might be the preview bitmap width, so scale down.
2292 scale
= scale
* (float)(w
/(float)pageWidth
);
2294 dc
->SetUserScale(scale
, scale
);
2298 bool wxLayoutPrintout::OnPrintPage(int page
)
2307 top
= (page
- 1)*m_PrintoutHeight
;
2308 bottom
= top
+ m_PrintoutHeight
;
2309 // SetDeviceOrigin() doesn't work here, so we need to manually
2310 // translate all coordinates.
2311 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
2312 m_llist
->Draw(*dc
, translate
, top
, bottom
);
2319 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
2321 /* We allocate a temporary wxDC for printing, so that we can
2322 determine the correct paper size and scaling. We don't actually
2323 print anything on it. */
2325 wxPrinterDC
psdc("","",WXLLIST_TEMPFILE
,false);
2327 wxPostScriptDC
psdc(WXLLIST_TEMPFILE
,false);
2330 float scale
= ScaleDC(&psdc
);
2332 psdc
.GetSize(&m_PageWidth
, &m_PageHeight
);
2333 // This sets a left/top origin of 15% and 20%:
2334 m_Offset
= wxPoint((15*m_PageWidth
)/100, m_PageHeight
/20);
2336 // This is the length of the printable area.
2337 m_PrintoutHeight
= m_PageHeight
- (int) (m_PageHeight
* 0.15);
2338 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
2342 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
2345 *maxPage
= m_NumOfPages
;
2348 *selPageTo
= m_NumOfPages
;
2349 wxRemoveFile(WXLLIST_TEMPFILE
);
2352 bool wxLayoutPrintout::HasPage(int pageNum
)
2354 return pageNum
<= m_NumOfPages
;
2358 Stupid wxWindows doesn't draw proper ellipses, so we comment this
2359 out. It's a waste of paper anyway.
2363 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
2364 wxPoint topleft
, wxPoint bottomright
,
2367 // make backups of all essential parameters
2368 const wxBrush
& brush
= dc
.GetBrush();
2369 const wxPen
& pen
= dc
.GetPen();
2370 const wxFont
& font
= dc
.GetFont();
2372 dc
.SetBrush(*wxWHITE_BRUSH
);
2373 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
2374 dc
.DrawRoundedRectangle(topleft
.x
,
2375 topleft
.y
,bottomright
.x
-topleft
.x
,
2376 bottomright
.y
-topleft
.y
);
2377 dc
.SetBrush(*wxBLACK_BRUSH
);
2378 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
2379 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
2383 page
= "9999/9999 "; // many pages...
2385 dc
.GetTextExtent(page
,&w
,&h
);
2386 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
2387 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
2388 dc
.GetTextExtent("XXXX", &w
,&h
);
2389 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);
2400 wxFontCache::GetFont(int family
, int size
, int style
, int weight
,
2403 for(wxFCEList::iterator i
= m_FontList
.begin();
2404 i
!= m_FontList
.end(); i
++)
2405 if( (**i
).Matches(family
, size
, style
, weight
, underline
) )
2406 return (**i
).GetFont();
2408 wxFontCacheEntry
*fce
= new wxFontCacheEntry(family
, size
, style
,
2410 m_FontList
.push_back(fce
);
2411 return fce
->GetFont();