1 /*-*- c++ -*-********************************************************
2 * wxllist: wxLayoutList, a layout engine for text and graphics *
4 * (C) 1998-1999 by Karsten Ballüder (Ballueder@usa.net) *
7 *******************************************************************/
13 Layout() recalculates the objects, sizes, etc.
14 Draw() just draws them with the current settings, without
15 re-layout()ing them again
17 Each line has its own wxLayoutStyleInfo structure which gets updated
18 from within Layout(). Thanks to this, we don't need to re-layout all
19 lines if we want to draw one, but can just use its styleinfo to set
25 # pragma implementation "wxllist.h"
28 #include <wx/wxprec.h>
37 # include "gui/wxllist.h"
38 # include "gui/wxlparser.h"
39 # define SHOW_SELECTIONS 1
42 # include "wxlparser.h"
43 # define SHOW_SELECTIONS 1
47 # include <iostream.h>
51 # include <wx/print.h>
53 # include <wx/filefn.h>
56 #ifdef WXLAYOUT_USE_CARET
57 # include <wx/caret.h>
58 #endif // WXLAYOUT_USE_CARET
62 /// This should never really get created
63 #define WXLLIST_TEMPFILE "__wxllist.tmp"
67 # define TypeString(t) g_aTypeStrings[t]
68 # define WXLO_DEBUG(x) wxLogDebug x
70 static const char *g_aTypeStrings
[] =
72 "invalid", "text", "cmd", "icon"
75 wxLayoutObject::Debug(void)
77 WXLO_DEBUG(("%s",g_aTypeStrings
[GetType()]));
80 # define TypeString(t) ""
81 # define WXLO_DEBUG(x)
84 // FIXME under MSW, this constant is needed to make the thing properly redraw
85 // itself - I don't know where the size calculation error is and I can't
86 // waste time looking for it right now. Search for occurences of
87 // MSW_CORRECTION to find all the places where I did it.
89 static const int MSW_CORRECTION
= 10;
91 static const int MSW_CORRECTION
= 0;
94 /// Cursors smaller than this disappear in XOR drawing mode
95 #define WXLO_MINIMUM_CURSOR_WIDTH 4
97 /// Use this character to estimate a cursor size when none is available.
98 #define WXLO_CURSORCHAR "E"
99 /** @name Helper functions */
101 /// allows me to compare to wxPoints
102 bool operator <=(wxPoint
const &p1
, wxPoint
const &p2
)
104 return p1
.y
< p2
.y
|| (p1
.y
== p2
.y
&& p1
.x
<= p2
.x
);
108 The following STAY HERE until we have a working wxGTK again!!!
110 #ifndef wxWANTS_CHARS
111 /// allows me to compare to wxPoints
112 bool operator ==(wxPoint
const &p1
, wxPoint
const &p2
)
114 return p1
.x
== p2
.x
&& p1
.y
== p2
.y
;
117 /// allows me to compare to wxPoints
118 bool operator !=(wxPoint
const &p1
, wxPoint
const &p2
)
120 return p1
.x
!= p2
.x
|| p1
.y
!= p2
.y
;
123 wxPoint
& operator += (wxPoint
&p1
, wxPoint
const &p2
)
131 /// allows me to compare to wxPoints
132 bool operator>(wxPoint
const &p1
, wxPoint
const &p2
)
137 /// grows a wxRect so that it includes the given point
140 void GrowRect(wxRect
&r
, CoordType x
, CoordType y
)
144 else if(r
.x
+ r
.width
< x
)
149 else if(r
.y
+ r
.height
< y
)
155 /// returns true if the point is in the rectangle
157 bool Contains(const wxRect
&r
, const wxPoint
&p
)
159 return r
.x
<= p
.x
&& r
.y
<= p
.y
&& (r
.x
+r
.width
) >= p
.x
&& (r
.y
+ r
.height
) >= p
.y
;
167 void ReadString(wxString
&to
, wxString
&from
)
170 const char *cptr
= from
.c_str();
171 while(*cptr
&& *cptr
!= '\n')
177 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
181 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
185 wxLayoutObject::Read(wxString
&istr
)
188 ReadString(tmp
, istr
);
190 sscanf(tmp
.c_str(),"%d", &type
);
195 return wxLayoutObjectText::Read(istr
);
197 return wxLayoutObjectCmd::Read(istr
);
199 return wxLayoutObjectIcon::Read(istr
);
205 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
209 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
211 wxLayoutObjectText::wxLayoutObjectText(const wxString
&txt
)
221 wxLayoutObjectText::Copy(void)
223 wxLayoutObjectText
*obj
= new wxLayoutObjectText(m_Text
);
224 obj
->m_Width
= m_Width
;
225 obj
->m_Height
= m_Height
;
227 obj
->m_Bottom
= m_Bottom
;
228 obj
->SetUserData(m_UserData
);
234 wxLayoutObjectText::Write(wxString
&ostr
)
236 ostr
<< (int) WXLO_TYPE_TEXT
<< '\n'
241 wxLayoutObjectText::Read(wxString
&istr
)
244 ReadString(text
, istr
);
246 return new wxLayoutObjectText(text
);
250 wxLayoutObjectText::GetSize(CoordType
*top
, CoordType
*bottom
) const
253 *top
= m_Top
; *bottom
= m_Bottom
;
254 return wxPoint(m_Width
, m_Height
);
258 wxLayoutObjectText::Draw(wxDC
&dc
, wxPoint
const &coords
,
259 wxLayoutList
*wxllist
,
260 CoordType begin
, CoordType end
)
264 // draw the whole object normally
265 dc
.DrawText(m_Text
, coords
.x
, coords
.y
-m_Top
);
269 // highlight the bit between begin and len
272 ypos
= coords
.y
-m_Top
;
273 long width
, height
, descent
;
275 if(begin
< 0) begin
= 0;
276 if( end
> (signed)m_Text
.Length() )
277 end
= m_Text
.Length();
279 wxString str
= m_Text
.Mid(0, begin
);
280 dc
.DrawText(str
, xpos
, ypos
);
281 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
283 wxllist
->StartHighlighting(dc
);
284 str
= m_Text
.Mid(begin
, end
-begin
);
285 dc
.DrawText(str
, xpos
, ypos
);
286 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
288 wxllist
->EndHighlighting(dc
);
289 str
= m_Text
.Mid(end
, m_Text
.Length()-end
);
290 dc
.DrawText(str
, xpos
, ypos
);
295 wxLayoutObjectText::GetOffsetScreen(wxDC
&dc
, CoordType xpos
) const
299 maxlen
= m_Text
.Length();
302 height
, descent
= 0l;
304 if(xpos
== 0) return 0; // easy
306 while(width
< xpos
&& offs
< maxlen
)
308 dc
.GetTextExtent(m_Text
.substr(0,offs
),
309 &width
, &height
, &descent
);
312 /* We have to substract 1 to compensate for the offs++, and another
313 one because we don't want to position the cursor behind the
314 object what we clicked on, but before - otherwise it looks
316 return (xpos
> 2) ? offs
-2 : 0;
320 wxLayoutObjectText::Layout(wxDC
&dc
, class wxLayoutList
*llist
)
324 // now this is done in wxLayoutLine::Layout(), but this code might be
325 // reenabled later - in principle, it's more efficient
327 CoordType widthOld
= m_Width
,
328 heightOld
= m_Height
;
331 dc
.GetTextExtent(m_Text
, &m_Width
, &m_Height
, &descent
);
334 if ( widthOld
!= m_Width
|| heightOld
!= m_Height
)
336 // as the text length changed, it must be refreshed
337 wxLayoutLine
*line
= GetLine();
339 wxCHECK_RET( line
, "wxLayoutObjectText can't refresh itself" );
341 // as our size changed, we need to repaint the part which was appended
342 wxPoint
position(line
->GetPosition());
344 // this is not the most efficient way (we repaint the whole line), but
345 // it's not too slow and is *simple*
346 if ( widthOld
< m_Width
)
348 if ( heightOld
< m_Height
)
349 heightOld
= m_Height
;
351 llist
->SetUpdateRect(position
.x
+ widthOld
+ MSW_CORRECTION
,
352 position
.y
+ heightOld
+ MSW_CORRECTION
);
357 m_Top
= m_Height
- m_Bottom
;
361 #ifdef WXLAYOUT_DEBUG
363 wxLayoutObjectText::Debug(void)
365 wxLayoutObject::Debug();
366 WXLO_DEBUG((" `%s`", m_Text
.c_str()));
370 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
374 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
376 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
const &icon
)
378 m_Icon
= new wxBitmap(icon
);
383 wxLayoutObjectIcon::Write(wxString
&ostr
)
385 /* Exports icon through a temporary file. */
387 wxString file
= wxGetTempFileName("wxloexport");
389 ostr
<< WXLO_TYPE_ICON
<< '\n'
391 m_Icon
->SaveFile(file
, WXLO_BITMAP_FORMAT
);
395 wxLayoutObjectIcon::Read(wxString
&istr
)
398 ReadString(file
, istr
);
400 if(! wxFileExists(file
))
402 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon
;
404 if(!obj
->m_Icon
->LoadFile(file
, WXLO_BITMAP_FORMAT
))
414 wxLayoutObjectIcon::Copy(void)
416 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon(new
418 obj
->SetUserData(m_UserData
);
422 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
*icon
)
428 wxLayoutObjectIcon::Draw(wxDC
&dc
, wxPoint
const &coords
,
429 wxLayoutList
*wxllist
,
430 CoordType begin
, CoordType
/* len */)
432 dc
.DrawBitmap(*m_Icon
, coords
.x
, coords
.y
-m_Icon
->GetHeight(),
433 (m_Icon
->GetMask() == NULL
) ? FALSE
: TRUE
);
437 wxLayoutObjectIcon::Layout(wxDC
& /* dc */, class wxLayoutList
* )
442 wxLayoutObjectIcon::GetSize(CoordType
*top
, CoordType
*bottom
) const
444 *top
= m_Icon
->GetHeight();
446 return wxPoint(m_Icon
->GetWidth(), m_Icon
->GetHeight());
451 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
455 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
458 wxLayoutStyleInfo::wxLayoutStyleInfo(int ifamily
,
470 underline
= iul
!= 0;
472 m_fg_valid
= fg
!= 0;
473 m_bg_valid
= bg
!= 0;
474 m_fg
= m_fg_valid
? *fg
: *wxBLACK
;
475 m_bg
= m_fg_valid
? *bg
: *wxWHITE
;
478 #define COPY_SI_(what) if(right.what != -1) what = right.what;
481 wxLayoutStyleInfo::operator=(const wxLayoutStyleInfo
&right
)
488 if(right
.m_fg_valid
) m_fg
= right
.m_fg
;
489 if(right
.m_bg_valid
) m_bg
= right
.m_bg
;
493 wxLayoutObjectCmd::wxLayoutObjectCmd(int family
, int size
, int style
, int
494 weight
, int underline
,
495 wxColour
*fg
, wxColour
*bg
)
498 m_StyleInfo
= new wxLayoutStyleInfo(family
, size
,style
,weight
,underline
,fg
,bg
);
502 wxLayoutObjectCmd::Copy(void)
504 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd(
509 m_StyleInfo
->underline
,
510 m_StyleInfo
->m_fg_valid
?
511 &m_StyleInfo
->m_fg
: NULL
,
512 m_StyleInfo
->m_bg_valid
?
513 &m_StyleInfo
->m_bg
: NULL
);
514 obj
->SetUserData(m_UserData
);
519 wxLayoutObjectCmd::Write(wxString
&ostr
)
521 ostr
<< WXLO_TYPE_CMD
<< '\n'
522 << m_StyleInfo
->size
<< '\n'
523 << m_StyleInfo
->family
<< '\n'
524 << m_StyleInfo
->style
<< '\n'
525 << m_StyleInfo
->weight
<< '\n'
526 << m_StyleInfo
->underline
<< '\n'
527 << m_StyleInfo
->m_fg_valid
<< '\n'
528 << m_StyleInfo
->m_bg_valid
<< '\n';
529 if(m_StyleInfo
->m_fg_valid
)
531 ostr
<< m_StyleInfo
->m_fg
.Red() << '\n'
532 << m_StyleInfo
->m_fg
.Green() << '\n'
533 << m_StyleInfo
->m_fg
.Blue() << '\n';
535 if(m_StyleInfo
->m_bg_valid
)
537 ostr
<< m_StyleInfo
->m_bg
.Red() << '\n'
538 << m_StyleInfo
->m_bg
.Green() << '\n'
539 << m_StyleInfo
->m_bg
.Blue() << '\n';
544 wxLayoutObjectCmd::Read(wxString
&istr
)
546 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd
;
549 ReadString(tmp
, istr
);
550 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->size
);
551 ReadString(tmp
, istr
);
552 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->family
);
553 ReadString(tmp
, istr
);
554 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->style
);
555 ReadString(tmp
, istr
);
556 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->weight
);
557 ReadString(tmp
, istr
);
558 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->underline
);
559 ReadString(tmp
, istr
);
560 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->m_fg_valid
);
561 ReadString(tmp
, istr
);
562 sscanf(tmp
.c_str(),"%d", &obj
->m_StyleInfo
->m_bg_valid
);
563 if(obj
->m_StyleInfo
->m_fg_valid
)
565 int red
, green
, blue
;
566 ReadString(tmp
, istr
);
567 sscanf(tmp
.c_str(),"%d", &red
);
568 ReadString(tmp
, istr
);
569 sscanf(tmp
.c_str(),"%d", &green
);
570 ReadString(tmp
, istr
);
571 sscanf(tmp
.c_str(),"%d", &blue
);
572 obj
->m_StyleInfo
->m_fg
= wxColour(red
, green
, blue
);
574 if(obj
->m_StyleInfo
->m_bg_valid
)
576 int red
, green
, blue
;
577 ReadString(tmp
, istr
);
578 sscanf(tmp
.c_str(),"%d", &red
);
579 ReadString(tmp
, istr
);
580 sscanf(tmp
.c_str(),"%d", &green
);
581 ReadString(tmp
, istr
);
582 sscanf(tmp
.c_str(),"%d", &blue
);
583 obj
->m_StyleInfo
->m_bg
= wxColour(red
, green
, blue
);
589 wxLayoutObjectCmd::~wxLayoutObjectCmd()
595 wxLayoutObjectCmd::GetStyle(void) const
601 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint
const & /* coords */,
602 wxLayoutList
*wxllist
,
603 CoordType begin
, CoordType
/* len */)
605 wxASSERT(m_StyleInfo
);
606 wxllist
->ApplyStyle(*m_StyleInfo
, dc
);
610 wxLayoutObjectCmd::Layout(wxDC
&dc
, class wxLayoutList
* llist
)
612 // this get called, so that recalculation uses right font sizes
613 Draw(dc
, wxPoint(0,0), llist
);
617 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
619 The wxLayoutLine object
621 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
623 wxLayoutLine::wxLayoutLine(wxLayoutLine
*prev
, wxLayoutList
*llist
)
626 m_Width
= m_Height
= 0;
632 RecalculatePosition(llist
);
635 m_LineNumber
= m_Previous
->GetLineNumber()+1;
636 m_Next
= m_Previous
->GetNextLine();
637 m_Previous
->m_Next
= this;
641 m_Next
->m_Previous
= this;
642 m_Next
->MoveLines(+1);
643 m_Next
->RecalculatePositions(1,llist
);
646 m_StyleInfo
= llist
->GetDefaultStyleInfo();
649 wxLayoutLine::~wxLayoutLine()
651 // kbList cleans itself
655 wxLayoutLine::RecalculatePosition(wxLayoutList
*llist
)
657 wxASSERT(m_Previous
|| GetLineNumber() == 0);
659 wxPoint
posOld(m_Position
);
663 m_Position
= m_Previous
->GetPosition();
664 m_Position
.y
+= m_Previous
->GetHeight();
667 m_Position
= wxPoint(0,0);
669 if ( m_Position
!= posOld
)
671 // the whole line moved and must be repainted
672 llist
->SetUpdateRect(m_Position
);
673 llist
->SetUpdateRect(m_Position
.x
+ GetWidth() + MSW_CORRECTION
,
674 m_Position
.y
+ GetHeight() + MSW_CORRECTION
);
675 llist
->SetUpdateRect(posOld
);
676 llist
->SetUpdateRect(posOld
.x
+ GetWidth() + MSW_CORRECTION
,
677 posOld
.y
+ GetHeight() + MSW_CORRECTION
);
684 wxLayoutLine::RecalculatePositions(int recurse
, wxLayoutList
*llist
)
686 //FIXME: is this really needed? We run Layout() anyway.
687 // Recursing here, drives computation time up exponentially, as
688 // each line will cause all following lines to be recalculated.
689 // Yes, or linenumbers go wrong.
691 wxASSERT(recurse
>= 0);
692 wxPoint pos
= m_Position
;
693 CoordType height
= m_Height
;
695 // WXLO_TRACE("RecalculatePositions()");
696 RecalculatePosition(llist
);
700 m_Next
->RecalculatePositions(--recurse
, llist
);
701 else if(pos
!= m_Position
|| m_Height
!= height
)
702 m_Next
->RecalculatePositions(0, llist
);
706 wxLayoutObjectList::iterator
707 wxLayoutLine::FindObject(CoordType xpos
, CoordType
*offset
) const
711 wxLayoutObjectList::iterator
714 CoordType x
= 0, len
;
716 /* We search through the objects. As we don't like returning the
717 object that the cursor is behind, we just remember such an
718 object in "found" so we can return it if there is really no
719 further object following it. */
720 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
722 len
= (**i
).GetLength();
723 if( x
<= xpos
&& xpos
<= x
+ len
)
726 if(xpos
== x
+ len
) // is there another object behind?
728 else // we are really inside this object
731 x
+= (**i
).GetLength();
733 return found
; // ==NULL if really none found
736 wxLayoutObjectList::iterator
737 wxLayoutLine::FindObjectScreen(wxDC
&dc
,
738 CoordType xpos
, CoordType
*cxpos
,
743 wxLayoutObjectList::iterator i
;
744 CoordType x
= 0, cx
= 0, width
;
746 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
748 width
= (**i
).GetWidth();
749 if( x
<= xpos
&& xpos
<= x
+ width
)
751 *cxpos
= cx
+ (**i
).GetOffsetScreen(dc
, xpos
-x
);
752 if(found
) *found
= true;
755 x
+= (**i
).GetWidth();
756 cx
+= (**i
).GetLength();
758 // behind last object:
760 if(found
) *found
= false;
761 return m_ObjectList
.tail();
764 /** Finds text in this line.
765 @param needle the text to find
766 @param xpos the position where to start the search
767 @return the cursoor coord where it was found or -1
770 wxLayoutLine::FindText(const wxString
&needle
, CoordType xpos
) const
775 wxString
const *text
;
777 for(wxLOiterator i
= m_ObjectList
.begin(); i
!= m_ObjectList
.end(); i
++)
779 if(cpos
>= xpos
) // search from here!
781 if((**i
).GetType() == WXLO_TYPE_TEXT
)
783 text
= & ((wxLayoutObjectText
*)(*i
))->GetText();
784 relpos
= text
->Find(needle
);
785 if(relpos
>= cpos
-xpos
) // -1 if not found
790 cpos
+= (**i
).GetLength();
793 return -1; // not found
797 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
800 wxASSERT(obj
!= NULL
);
804 // If we insert a command object, we need to recalculate all lines
805 // to update their styleinfo structure.
806 if(obj
->GetType() == WXLO_TYPE_CMD
)
810 wxLOiterator i
= FindObject(xpos
, &offset
);
813 if(xpos
== 0 ) // aha, empty line!
815 m_ObjectList
.push_back(obj
);
816 m_Length
+= obj
->GetLength();
823 CoordType len
= (**i
).GetLength();
824 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
825 { // insert before this object
826 m_ObjectList
.insert(i
,obj
);
827 m_Length
+= obj
->GetLength();
832 if( i
== m_ObjectList
.tail()) // last object?
833 m_ObjectList
.push_back(obj
);
835 { // insert after current object
837 m_ObjectList
.insert(i
,obj
);
839 m_Length
+= obj
->GetLength();
842 /* Otherwise we need to split the current object.
843 Fortunately this can only be a text object. */
844 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
845 wxString left
, right
;
846 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
847 left
= tobj
->GetText().substr(0,offset
);
848 right
= tobj
->GetText().substr(offset
,len
-offset
);
849 // current text object gets set to right half
850 tobj
->GetText() = right
; // set new text
851 // before it we insert the new object
852 m_ObjectList
.insert(i
,obj
);
853 m_Length
+= obj
->GetLength();
854 // and before that we insert the left half
855 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
860 wxLayoutLine::Insert(CoordType xpos
, const wxString
& text
)
867 wxLOiterator i
= FindObject(xpos
, &offset
);
868 if(i
!= NULLIT
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
870 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
871 tobj
->GetText().insert(offset
, text
);
872 m_Length
+= text
.Length();
876 if ( !Insert(xpos
, new wxLayoutObjectText(text
)) )
884 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
886 CoordType offset
, len
;
891 wxLOiterator i
= FindObject(xpos
, &offset
);
894 if(i
== NULLIT
) return npos
;
895 // now delete from that object:
896 if((**i
).GetType() != WXLO_TYPE_TEXT
)
898 if(offset
!= 0) // at end of line after a non-text object
901 len
= (**i
).GetLength();
904 // If we delete a command object, we need to recalculate all lines
905 // to update their styleinfo structure.
906 if((**i
).GetType() == WXLO_TYPE_CMD
)
908 m_ObjectList
.erase(i
);
912 // tidy up: remove empty text objects
913 if((**i
).GetLength() == 0)
915 m_ObjectList
.erase(i
);
919 CoordType max
= (**i
).GetLength() - offset
;
920 if(npos
< max
) max
= npos
;
923 if(xpos
== GetLength())
926 { // at the end of an object
927 // move to begin of next object:
929 continue; // start over
934 if(offset
== 0 && max
== (**i
).GetLength())
935 m_ObjectList
.erase(i
); // remove the whole object
937 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
945 wxLayoutLine::MarkNextDirty(int recurse
)
947 wxLayoutLine
*line
= GetNextLine();
948 while(line
&& (recurse
== -1 || recurse
>= 0))
951 line
= line
->GetNextLine();
952 if(recurse
> 0) recurse
--;
957 wxLayoutLine::DeleteWord(CoordType xpos
)
963 wxLOiterator i
= FindObject(xpos
, &offset
);
967 if(i
== NULLIT
) return false;
968 if((**i
).GetType() != WXLO_TYPE_TEXT
)
970 // This should only happen when at end of line, behind a non-text
972 if(offset
== (**i
).GetLength()) return false;
973 m_Length
-= (**i
).GetLength(); // -1
974 m_ObjectList
.erase(i
);
975 return true; // we are done
979 if(offset
== (**i
).GetLength()) // at end of object
984 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
986 wxString str
= tobj
->GetText();
987 str
= str
.substr(offset
,str
.Length()-offset
);
988 // Find out how many positions we need to delete:
989 // 1. eat leading space
990 while(isspace(str
.c_str()[count
])) count
++;
991 // 2. eat the word itself:
992 while(isalnum(str
.c_str()[count
])) count
++;
994 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
995 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
1001 wxFAIL_MSG("unreachable");
1005 wxLayoutLine::DeleteLine(bool update
, wxLayoutList
*llist
)
1007 if(m_Next
) m_Next
->m_Previous
= m_Previous
;
1008 if(m_Previous
) m_Previous
->m_Next
= m_Next
;
1011 m_Next
->MoveLines(-1);
1012 m_Next
->RecalculatePositions(1, llist
);
1013 /* We assume that if we have more than one object in the list,
1014 this means that we have a command object, so we need to
1015 update the following lines. */
1016 if(m_ObjectList
.size() > 1 ||
1017 ( m_ObjectList
.begin() != NULLIT
&&
1018 (**m_ObjectList
.begin()).GetType() == WXLO_TYPE_CMD
)
1022 wxLayoutLine
*next
= m_Next
;
1028 wxLayoutLine::Draw(wxDC
&dc
,
1029 wxLayoutList
*llist
,
1030 const wxPoint
& offset
) const
1032 wxLayoutObjectList::iterator i
;
1033 wxPoint pos
= offset
;
1034 pos
= pos
+ GetPosition();
1036 pos
.y
+= m_BaseLine
;
1038 CoordType xpos
= 0; // cursorpos, lenght of line
1040 CoordType from
, to
, tempto
;
1042 int highlight
= llist
->IsSelected(this, &from
, &to
);
1043 // WXLO_DEBUG(("highlight=%d", highlight ));
1044 if(highlight
== 1) // we need to draw the whole line inverted!
1045 llist
->StartHighlighting(dc
);
1047 llist
->EndHighlighting(dc
);
1049 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
1051 if(highlight
== -1) // partially highlight line
1053 // parts of the line need highlighting
1054 tempto
= xpos
+(**i
).GetLength();
1055 (**i
).Draw(dc
, pos
, llist
, from
-xpos
, to
-xpos
);
1058 (**i
).Draw(dc
, pos
, llist
);
1059 pos
.x
+= (**i
).GetWidth();
1060 xpos
+= (**i
).GetLength();
1065 This function does all the recalculation, that is, it should only be
1066 called from within wxLayoutList::Layout(), as it uses the current
1067 list's styleinfo and updates it.
1070 wxLayoutLine::Layout(wxDC
&dc
,
1071 wxLayoutList
*llist
,
1073 wxPoint
*cursorSize
,
1075 bool suppressSIupdate
)
1077 wxLayoutObjectList::iterator i
;
1079 // when a line becomes dirty, we redraw it from the place where it was
1080 // changed till the end of line (because the following wxLayoutObjects are
1081 // moved when the preceding one changes) - calculate the update rectangle.
1082 CoordType updateTop
= m_Position
.y
,
1084 updateWidth
= m_Width
,
1085 updateHeight
= m_Height
;
1089 bottomHeight
= 0; // above and below baseline
1091 objTopHeight
, objBottomHeight
; // above and below baseline
1095 CoordType heightOld
= m_Height
;
1101 bool cursorFound
= false;
1105 *cursorPos
= m_Position
;
1106 if(cursorSize
) *cursorSize
= wxPoint(0,0);
1109 m_StyleInfo
= llist
->GetStyleInfo(); // save current style
1110 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
1112 wxLayoutObject
*obj
= *i
;
1113 obj
->Layout(dc
, llist
);
1114 wxPoint sizeObj
= obj
->GetSize(&objTopHeight
, &objBottomHeight
);
1116 if(cursorPos
&& ! cursorFound
)
1118 // we need to check whether the text cursor is here
1119 len
= obj
->GetLength();
1120 if(count
<= cx
&& count
+len
> cx
)
1122 if(obj
->GetType() == WXLO_TYPE_TEXT
)
1124 len
= cx
- count
; // pos in object
1125 CoordType width
, height
, descent
;
1126 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
1127 &width
, &height
, &descent
);
1128 cursorPos
->x
+= width
;
1129 cursorPos
->y
= m_Position
.y
;
1131 if(len
< obj
->GetLength())
1132 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
1134 str
= WXLO_CURSORCHAR
;
1135 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
1139 // Just in case some joker inserted an empty string object:
1141 width
= WXLO_MINIMUM_CURSOR_WIDTH
;
1144 cursorSize
->x
= width
;
1145 cursorSize
->y
= height
;
1148 cursorFound
= true; // no more checks
1152 // on some other object
1153 CoordType top
, bottom
; // unused
1154 *cursorSize
= obj
->GetSize(&top
,&bottom
);
1155 cursorPos
->y
= m_Position
.y
;
1156 cursorFound
= true; // no more checks
1162 cursorPos
->x
+= obj
->GetWidth();
1166 m_Width
+= sizeObj
.x
;
1167 if(sizeObj
.y
> m_Height
)
1169 m_Height
= sizeObj
.y
;
1172 if(objTopHeight
> topHeight
)
1173 topHeight
= objTopHeight
;
1174 if(objBottomHeight
> bottomHeight
)
1175 bottomHeight
= objBottomHeight
;
1180 if ( updateHeight
< m_Height
)
1181 updateHeight
= m_Height
;
1182 if ( updateWidth
< m_Width
)
1183 updateWidth
= m_Width
;
1185 // update all line if we don't know where to start from
1186 if ( updateLeft
== -1 )
1189 llist
->SetUpdateRect(updateLeft
, updateTop
);
1190 llist
->SetUpdateRect(updateLeft
+ updateWidth
+ MSW_CORRECTION
,
1191 updateTop
+ updateHeight
+ MSW_CORRECTION
);
1194 if(topHeight
+ bottomHeight
> m_Height
)
1196 m_Height
= topHeight
+bottomHeight
;
1199 m_BaseLine
= topHeight
;
1203 CoordType width
, height
, descent
;
1204 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1206 m_BaseLine
= m_Height
- descent
;
1209 // tell next line about coordinate change
1210 if(m_Next
&& m_Height
!= heightOld
)
1212 // FIXME isn't this done in RecalculatePositions() below anyhow?
1213 m_Next
->RecalculatePositions(0, llist
);
1216 // We need to check whether we found a valid cursor size:
1217 if(cursorPos
&& cursorSize
)
1219 // this might be the case if the cursor is at the end of the
1220 // line or on a command object:
1221 if(cursorSize
->y
< WXLO_MINIMUM_CURSOR_WIDTH
)
1223 CoordType width
, height
, descent
;
1224 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
1225 cursorSize
->x
= width
;
1226 cursorSize
->y
= height
;
1228 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
1229 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
1231 RecalculatePositions(1, llist
);
1237 wxLayoutLine::Break(CoordType xpos
, wxLayoutList
*llist
)
1239 wxASSERT(xpos
>= 0);
1243 /* If we are at the begin of a line, we want to move all other
1244 lines down and stay with the cursor where we are. However, if we
1245 are in an empty line, we want to move down with it. */
1246 if(xpos
== 0 && GetLength() > 0)
1247 { // insert an empty line before this one
1248 wxLayoutLine
*prev
= new wxLayoutLine(m_Previous
, llist
);
1249 if(m_Previous
== NULL
)
1250 { // We were in first line, need to link in new empty line
1252 prev
->m_Next
= this;
1254 m_Previous
->m_Height
= 0; // this is a wild guess
1257 m_Next
->RecalculatePositions(1, llist
);
1262 wxLOiterator i
= FindObject(xpos
, &offset
);
1264 // must be at the end of the line then
1265 return new wxLayoutLine(this, llist
);
1268 wxLayoutLine
*newLine
= new wxLayoutLine(this, llist
);
1269 // split object at i:
1270 if((**i
).GetType() == WXLO_TYPE_TEXT
&& offset
!= 0)
1272 wxString left
, right
;
1273 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
1274 left
= tobj
->GetText().substr(0,offset
);
1275 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
1276 // current text object gets set to left half
1277 tobj
->GetText() = left
; // set new text
1278 newLine
->Append(new wxLayoutObjectText(right
));
1279 m_Length
-= right
.Length();
1280 i
++; // don't move this object to the new list
1285 i
++; // move objects from here to new list
1288 while(i
!= m_ObjectList
.end())
1290 wxLayoutObject
*obj
= *i
;
1291 newLine
->Append(obj
);
1292 m_Length
-= obj
->GetLength();
1294 m_ObjectList
.remove(i
); // remove without deleting it
1297 m_Next
->RecalculatePositions(2, llist
);
1303 wxLayoutLine::MergeNextLine(wxLayoutList
*llist
)
1305 wxCHECK_RET(GetNextLine(),"wxLayout internal error: no next line to merge");
1306 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
1309 MarkDirty(GetWidth());
1311 wxLayoutObject
*last
= NULL
;
1312 for(i
= list
.begin(); i
!= list
.end();)
1314 wxLayoutObject
*current
= *i
;
1316 // merge text objects together for efficiency
1317 if ( last
&& last
->GetType() == WXLO_TYPE_TEXT
&&
1318 current
->GetType() == WXLO_TYPE_TEXT
)
1320 wxLayoutObjectText
*textObj
= (wxLayoutObjectText
*)last
;
1321 wxString
text(textObj
->GetText());
1322 text
+= ((wxLayoutObjectText
*)current
)->GetText();
1323 textObj
->SetText(text
);
1325 list
.erase(i
); // remove and delete it
1329 // just append the object "as was"
1332 list
.remove(i
); // remove without deleting it
1335 wxASSERT(list
.empty());
1337 wxLayoutLine
*oldnext
= GetNextLine();
1338 wxLayoutLine
*nextLine
= oldnext
->GetNextLine();
1342 nextLine
->MoveLines(-1);
1346 // this is now done in Delete(), but if this function is ever called
1347 // from elsewhere, we might have to move refresh code back here (in
1348 // order not to duplicate it)
1350 wxPoint
pos(oldnext
->GetPosition());
1351 llist
->SetUpdateRect(pos
);
1352 llist
->SetUpdateRect(pos
.x
+ oldnext
->GetWidth() + MSW_CORRECTION
,
1353 pos
.y
+ oldnext
->GetHeight() + MSW_CORRECTION
);
1361 wxLayoutLine::GetWrapPosition(CoordType column
)
1364 wxLOiterator i
= FindObject(column
, &offset
);
1365 if(i
== NULLIT
) return -1; // cannot wrap
1367 // go backwards through the list and look for space in text objects
1370 if((**i
).GetType() == WXLO_TYPE_TEXT
)
1374 if( isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
1381 }while(offset
!= -1);
1382 i
--; // move on to previous object
1386 column
-= (**i
).GetLength();
1390 offset
= (**i
).GetLength();
1391 }while(i
!= NULLIT
);
1392 /* If we reached the begin of the list and have more than one
1393 object, that one is longer than the margin, so break behind
1396 i
= m_ObjectList
.begin();
1397 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1399 pos
+= (**i
).GetLength();
1402 if(i
== NULLIT
) return -1; //why should this happen?
1403 pos
+= (**i
).GetLength();
1405 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
1407 pos
+= (**i
).GetLength();
1410 if(i
== NULLIT
) return -1; //this is possible, if there is only one text object
1411 // now we are at the second text object:
1412 pos
-= (**i
).GetLength();
1413 return pos
; // in front of it
1417 #ifdef WXLAYOUT_DEBUG
1419 wxLayoutLine::Debug(void)
1422 wxPoint pos
= GetPosition();
1423 WXLO_DEBUG(("Line %ld, Pos (%ld,%ld), Height %ld, BL %ld, Font: %d",
1424 (long int) GetLineNumber(),
1425 (long int) pos
.x
, (long int) pos
.y
,
1426 (long int) GetHeight(),
1427 (long int) m_BaseLine
,
1428 (int) m_StyleInfo
.family
));
1429 if(m_ObjectList
.begin() != NULLIT
)
1430 (**m_ObjectList
.begin()).Debug();
1436 wxLayoutLine::Copy(wxLayoutList
*llist
,
1440 CoordType firstOffset
, lastOffset
;
1442 if(to
== -1) to
= GetLength();
1443 if(from
== to
) return;
1445 wxLOiterator first
= FindObject(from
, &firstOffset
);
1446 wxLOiterator last
= FindObject(to
, &lastOffset
);
1448 // Common special case: only one object
1449 if( first
!= NULLIT
&& last
!= NULLIT
&& *first
== *last
)
1451 if( (**first
).GetType() == WXLO_TYPE_TEXT
)
1453 llist
->Insert(new wxLayoutObjectText(
1454 ((wxLayoutObjectText
1455 *)*first
)->GetText().substr(firstOffset
,
1456 lastOffset
-firstOffset
))
1460 else // what can we do?
1462 if(lastOffset
> firstOffset
) // i.e. +1 :-)
1463 llist
->Insert( (**first
).Copy() );
1468 // If we reach here, we can safely copy the whole first object from
1469 // the firstOffset position on:
1470 if((**first
).GetType() == WXLO_TYPE_TEXT
&& firstOffset
!= 0)
1472 llist
->Insert(new wxLayoutObjectText(
1473 ((wxLayoutObjectText
*)*first
)->GetText().substr(firstOffset
))
1476 else if(firstOffset
== 0)
1477 llist
->Insert( (**first
).Copy() );
1478 // else nothing to copy :-(
1480 // Now we copy all objects before the last one:
1481 wxLOiterator i
= first
; i
++;
1482 for( ; i
!= last
; i
++)
1483 llist
->Insert( (**i
).Copy() );
1485 // And now the last object:
1488 if( (**last
).GetType() == WXLO_TYPE_TEXT
)
1490 llist
->Insert(new wxLayoutObjectText(
1491 ((wxLayoutObjectText
*)*last
)->GetText().substr(0,lastOffset
))
1495 llist
->Insert( (**last
).Copy() );
1500 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1502 The wxLayoutList object
1504 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1506 wxLayoutList::wxLayoutList()
1508 #ifdef WXLAYOUT_USE_CARET
1510 #endif // WXLAYOUT_USE_CARET
1513 InvalidateUpdateRect();
1517 wxLayoutList::~wxLayoutList()
1520 m_FirstLine
->DeleteLine(false, this);
1524 wxLayoutList::Empty(void)
1527 m_FirstLine
= m_FirstLine
->DeleteLine(false, this);
1529 m_CursorPos
= wxPoint(0,0);
1530 m_CursorScreenPos
= wxPoint(0,0);
1531 m_CursorSize
= wxPoint(0,0);
1532 m_movedCursor
= true;
1533 m_FirstLine
= new wxLayoutLine(NULL
, this); // empty first line
1534 m_CursorLine
= m_FirstLine
;
1535 InvalidateUpdateRect();
1540 wxLayoutList::InternalClear(void)
1543 m_Selection
.m_selecting
= false;
1544 m_Selection
.m_valid
= false;
1546 m_DefaultStyleInfo
.family
= wxSWISS
;
1547 m_DefaultStyleInfo
.size
= WXLO_DEFAULTFONTSIZE
;
1548 m_DefaultStyleInfo
.style
= wxNORMAL
;
1549 m_DefaultStyleInfo
.weight
= wxNORMAL
;
1550 m_DefaultStyleInfo
.underline
= 0;
1551 m_DefaultStyleInfo
.m_fg_valid
= TRUE
;
1552 m_DefaultStyleInfo
.m_fg
= *wxBLACK
;
1553 m_DefaultStyleInfo
.m_bg_valid
= TRUE
;
1554 m_DefaultStyleInfo
.m_bg
= *wxWHITE
;
1556 m_CurrentStyleInfo
= m_DefaultStyleInfo
;
1560 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1561 int underline
, wxColour
*fg
,
1564 if(family
!= -1) m_CurrentStyleInfo
.family
= family
;
1565 if(size
!= -1) m_CurrentStyleInfo
.size
= size
;
1566 if(style
!= -1) m_CurrentStyleInfo
.style
= style
;
1567 if(weight
!= -1) m_CurrentStyleInfo
.weight
= weight
;
1568 if(underline
!= -1) m_CurrentStyleInfo
.underline
= underline
!= 0;
1569 if(fg
) m_CurrentStyleInfo
.m_fg
= *fg
;
1570 if(bg
) m_CurrentStyleInfo
.m_bg
= *bg
;
1572 new wxLayoutObjectCmd(
1573 m_CurrentStyleInfo
.family
,
1574 m_CurrentStyleInfo
.size
,
1575 m_CurrentStyleInfo
.style
,
1576 m_CurrentStyleInfo
.weight
,
1577 m_CurrentStyleInfo
.underline
,
1582 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
1583 int underline
, char const *fg
, char const *bg
)
1591 cfg
= wxTheColourDatabase
->FindColour(fg
);
1593 cbg
= wxTheColourDatabase
->FindColour(bg
);
1595 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
1599 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
1600 int underline
, wxColour
*fg
, wxColour
*bg
)
1603 m_DefaultStyleInfo
= wxLayoutStyleInfo(family
, size
, style
, weight
,
1605 m_CurrentStyleInfo
= m_DefaultStyleInfo
;
1609 wxLayoutList::FindText(const wxString
&needle
, const wxPoint
&cpos
) const
1614 for(line
= m_FirstLine
;
1616 line
= line
->GetNextLine())
1618 if(line
->GetLineNumber() >= cpos
.y
)
1620 xpos
= line
->FindText(needle
,
1621 (line
->GetLineNumber() == cpos
.y
) ?
1624 return wxPoint(xpos
, line
->GetLineNumber());
1627 return wxPoint(-1,-1);
1632 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
1634 AddCursorPosToUpdateRect();
1636 wxPoint cursorPosOld
= m_CursorPos
;
1638 wxLayoutLine
*line
= m_FirstLine
;
1639 while(line
&& line
->GetLineNumber() != p
.y
)
1640 line
= line
->GetNextLine();
1641 if(line
&& line
->GetLineNumber() == p
.y
) // found it
1643 m_CursorPos
.y
= p
.y
;
1644 m_CursorLine
= line
;
1645 CoordType len
= line
->GetLength();
1648 m_CursorPos
.x
= p
.x
;
1652 m_CursorPos
.x
= len
;
1656 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1658 return m_CursorPos
== p
;
1662 wxLayoutList::MoveCursorVertically(int n
)
1664 AddCursorPosToUpdateRect();
1666 wxPoint cursorPosOld
= m_CursorPos
;
1669 if(n
< 0) // move up
1671 if(m_CursorLine
== m_FirstLine
) return false;
1672 while(n
< 0 && m_CursorLine
)
1674 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1680 m_CursorLine
= m_FirstLine
;
1686 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1687 m_CursorPos
.x
= m_CursorLine
->GetLength();
1693 wxLayoutLine
*last
= m_CursorLine
;
1694 if(! m_CursorLine
->GetNextLine()) return false;
1695 while(n
> 0 && m_CursorLine
)
1699 m_CursorLine
= m_CursorLine
->GetNextLine();
1703 m_CursorLine
= last
;
1709 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1710 m_CursorPos
.x
= m_CursorLine
->GetLength();
1715 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1721 wxLayoutList::MoveCursorHorizontally(int n
)
1723 AddCursorPosToUpdateRect();
1725 wxPoint cursorPosOld
= m_CursorPos
;
1730 if(m_CursorPos
.x
== 0) // at begin of line
1732 if(! MoveCursorVertically(-1))
1734 MoveCursorToEndOfLine();
1739 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1740 m_CursorPos
.x
-= move
; n
+= move
;
1745 int len
= m_CursorLine
->GetLength();
1746 if(m_CursorPos
.x
== len
) // at end of line
1748 if(! MoveCursorVertically(1))
1750 MoveCursorToBeginOfLine();
1755 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1756 m_CursorPos
.x
+= move
;
1760 m_movedCursor
= m_CursorPos
!= cursorPosOld
;
1766 wxLayoutList::MoveCursorWord(int n
, bool untilNext
)
1768 wxCHECK_MSG( m_CursorLine
, false, "no current line" );
1769 wxCHECK_MSG( n
== -1 || n
== +1, false, "not implemented yet" );
1771 CoordType moveDistance
= 0;
1773 for ( wxLOiterator i
= m_CursorLine
->FindObject(m_CursorPos
.x
, &offset
);
1780 wxLayoutObject
*obj
= *i
;
1781 if( obj
->GetType() != WXLO_TYPE_TEXT
)
1783 // any visible non text objects count as one word
1784 if ( obj
->IsVisibleObject() )
1788 moveDistance
+= obj
->GetLength();
1793 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)obj
;
1795 bool canAdvance
= true;
1797 if ( offset
== tobj
->GetLength() )
1802 // can't move further in this text object
1807 else if ( offset
> 0 )
1809 // offset is off by 1, make it a valid index
1816 const wxString
& text
= tobj
->GetText();
1817 const char *start
= text
.c_str();
1818 const char *end
= start
+ text
.length();
1819 const char *p
= start
+ offset
;
1827 // to the beginning/end of the next/prev word
1828 while ( p
>= start
&& p
< end
&& isspace(*p
) )
1833 // go to the end/beginning of the word (in a broad sense...)
1834 while ( p
>= start
&& p
< end
&& !isspace(*p
) )
1843 // now advance to the beginning of the next word
1844 while ( isspace(*p
) && p
< end
)
1856 moveDistance
= p
- start
- offset
;
1860 // except for the first iteration, offset is 0
1864 MoveCursorHorizontally(moveDistance
);
1870 wxLayoutList::Insert(wxString
const &text
)
1872 wxASSERT(m_CursorLine
);
1873 wxASSERT_MSG( text
.Find('\n') == wxNOT_FOUND
, "use wxLayoutImportText!" );
1878 AddCursorPosToUpdateRect();
1880 if ( !m_CursorLine
->Insert(m_CursorPos
.x
, text
) )
1883 m_CursorPos
.x
+= text
.Length();
1885 m_movedCursor
= true;
1887 m_CursorLine
->RecalculatePositions(0, this);
1893 wxLayoutList::Insert(wxLayoutObject
*obj
)
1895 wxASSERT(m_CursorLine
);
1898 m_CursorLine
= GetFirstLine();
1900 AddCursorPosToUpdateRect();
1902 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
1903 m_CursorPos
.x
+= obj
->GetLength();
1904 m_movedCursor
= true;
1906 m_CursorLine
->RecalculatePositions(0, this);
1912 wxLayoutList::Insert(wxLayoutList
*llist
)
1917 for(wxLayoutLine
*line
= llist
->GetFirstLine();
1919 line
= line
->GetNextLine()
1922 for(wxLOiterator i
= line
->GetFirstObject();
1932 wxLayoutList::LineBreak(void)
1934 wxASSERT(m_CursorLine
);
1935 bool setFirst
= (m_CursorLine
== m_FirstLine
&& m_CursorPos
.x
== 0);
1937 AddCursorPosToUpdateRect();
1939 wxPoint
position(m_CursorLine
->GetPosition());
1942 width
= m_CursorLine
->GetWidth(),
1943 height
= m_CursorLine
->GetHeight();
1945 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
, this);
1946 if(setFirst
) // we were at beginning of first line
1947 m_FirstLine
= m_CursorLine
->GetPreviousLine();
1948 if(m_CursorPos
.x
!= 0)
1952 wxLayoutLine
*prev
= m_CursorLine
->GetPreviousLine();
1953 wxCHECK_MSG(prev
, false, "just broke the line, where is the previous one?");
1955 height
+= prev
->GetHeight();
1957 m_movedCursor
= true;
1959 SetUpdateRect(position
);
1960 SetUpdateRect(position
.x
+ width
+ MSW_CORRECTION
,
1961 position
.y
+ height
+ MSW_CORRECTION
);
1967 wxLayoutList::WrapLine(CoordType column
)
1969 if(m_CursorPos
.x
<= column
|| column
< 1)
1970 return false; // do nothing yet
1973 CoordType xpos
= m_CursorLine
->GetWrapPosition(column
);
1975 return false; // cannot break line
1977 CoordType newpos
= m_CursorPos
.x
- xpos
- 1;
1978 m_CursorPos
.x
= xpos
;
1980 AddCursorPosToUpdateRect();
1983 Delete(1); // delete the space
1984 m_CursorPos
.x
= newpos
;
1986 m_CursorLine
->RecalculatePositions(1, this);
1988 m_movedCursor
= true;
1995 wxLayoutList::Delete(CoordType npos
)
1997 wxCHECK_MSG(m_CursorLine
, false, "can't delete in non existing line");
2002 AddCursorPosToUpdateRect();
2004 // were other lines appended to this one (this is important to know because
2005 // this means that our width _increased_ as the result of deletion)
2006 bool wasMerged
= false;
2008 // the size of the region to update
2009 CoordType totalHeight
= m_CursorLine
->GetHeight(),
2010 totalWidth
= m_CursorLine
->GetWidth();
2015 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
2019 // More to delete, continue on next line.
2021 // First, check if line is empty:
2022 if(m_CursorLine
->GetLength() == 0)
2024 // in this case, updating could probably be optimised
2026 wxASSERT(DeleteLines(1) == 0);
2035 // Need to join next line
2036 if(! m_CursorLine
->GetNextLine())
2041 wxLayoutLine
*next
= m_CursorLine
->GetNextLine();
2044 totalHeight
+= next
->GetHeight();
2045 totalWidth
+= next
->GetWidth();
2047 m_CursorLine
->MergeNextLine(this);
2052 wxFAIL_MSG("can't delete all this");
2062 // we need to update the whole tail of the line and the lines which
2066 wxPoint
position(m_CursorLine
->GetPosition());
2067 SetUpdateRect(position
);
2068 SetUpdateRect(position
.x
+ totalWidth
+ MSW_CORRECTION
,
2069 position
.y
+ totalHeight
+ MSW_CORRECTION
);
2076 wxLayoutList::DeleteLines(int n
)
2078 wxASSERT(m_CursorLine
);
2081 AddCursorPosToUpdateRect();
2085 if(!m_CursorLine
->GetNextLine())
2086 { // we cannot delete this line, but we can clear it
2087 MoveCursorToBeginOfLine();
2088 DeleteToEndOfLine();
2089 m_CursorLine
->RecalculatePositions(2, this);
2093 line
= m_CursorLine
;
2094 m_CursorLine
= m_CursorLine
->DeleteLine(true, this);
2096 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
2097 wxASSERT(m_FirstLine
);
2098 wxASSERT(m_CursorLine
);
2100 m_CursorLine
->RecalculatePositions(2, this);
2105 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
)
2107 wxLayoutLine
*line
= m_FirstLine
;
2109 // first, make sure everything is calculated - this might not be
2110 // needed, optimise it later
2111 ApplyStyle(m_DefaultStyleInfo
, dc
);
2114 line
->RecalculatePosition(this); // so we don't need to do it all the time
2115 // little condition to speed up redrawing:
2116 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
2117 line
= line
->GetNextLine();
2122 wxLayoutList::UpdateCursorScreenPos(wxDC
&dc
)
2124 wxCHECK_RET( m_CursorLine
, "no cursor line" );
2126 // we need to save the current style, in case the layout() of the line
2128 wxLayoutStyleInfo SiBackup
= m_CurrentStyleInfo
;
2129 m_CursorLine
->Layout(dc
, this,
2130 &m_CursorScreenPos
, &m_CursorSize
,
2132 true /* suppress update */);
2133 ApplyStyle(SiBackup
, dc
); // restore it
2137 wxLayoutList::GetCursorScreenPos(wxDC
&dc
)
2139 UpdateCursorScreenPos(dc
);
2141 return m_CursorScreenPos
;
2145 Is called before each Draw(). Now, it will re-layout all lines which
2149 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
, bool forceAll
,
2150 wxPoint
*cpos
, wxPoint
*csize
)
2152 // first, make sure everything is calculated - this might not be
2153 // needed, optimise it later
2154 ApplyStyle(m_DefaultStyleInfo
, dc
);
2156 // This one we always Layout() to get the current cursor
2157 // coordinates on the screen:
2158 m_CursorLine
->MarkDirty();
2159 bool wasDirty
= false;
2160 wxLayoutLine
*line
= m_FirstLine
;
2164 ApplyStyle(line
->GetStyleInfo(), dc
);
2165 if(forceAll
|| line
->IsDirty()
2166 || (cpos
&& line
->GetLineNumber() == cpos
->y
))
2168 // The following Layout() calls will update our
2169 // m_CurrentStyleInfo if needed.
2170 if(line
== m_CursorLine
)
2171 line
->Layout(dc
, this,
2172 (wxPoint
*)&m_CursorScreenPos
,
2173 (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
2174 if(cpos
&& line
->GetLineNumber() == cpos
->y
)
2175 line
->Layout(dc
, this,
2179 line
->Layout(dc
, this);
2180 // little condition to speed up redrawing:
2181 if(bottom
!= -1 && line
->GetPosition().y
> bottom
)
2185 line
->RecalculatePositions(1, this);
2186 line
= line
->GetNextLine();
2189 // can only be 0 if we are on the first line and have no next line
2190 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
2191 m_CursorLine
->GetNextLine() == NULL
&&
2192 m_CursorLine
== m_FirstLine
));
2193 AddCursorPosToUpdateRect();
2197 wxLayoutList::GetScreenPos(wxDC
&dc
, const wxPoint
&cpos
, wxPoint
*csize
)
2200 Layout(dc
, -1, false, &pos
, csize
);
2205 wxLayoutList::Draw(wxDC
&dc
,
2206 wxPoint
const &offset
,
2210 wxLayoutLine
*line
= m_FirstLine
;
2212 if ( m_Selection
.m_discarded
)
2214 // calculate them if we don't have them already
2215 if ( !m_Selection
.HasValidScreenCoords() )
2217 m_Selection
.m_ScreenA
= GetScreenPos(dc
, m_Selection
.m_CursorA
);
2218 m_Selection
.m_ScreenB
= GetScreenPos(dc
, m_Selection
.m_CursorB
);
2221 // invalidate the area which was previousle selected - and which is not
2222 // selected any more
2223 SetUpdateRect(m_Selection
.m_ScreenA
);
2224 SetUpdateRect(m_Selection
.m_ScreenB
);
2226 m_Selection
.m_discarded
= false;
2229 /* We need to re-layout all dirty lines to update styleinfos
2230 etc. However, somehow we don't find all dirty lines... */
2231 Layout(dc
); //,-1,true); //FIXME
2232 ApplyStyle(m_DefaultStyleInfo
, dc
);
2233 wxBrush
brush(m_CurrentStyleInfo
.m_bg
, wxSOLID
);
2235 dc
.SetBackgroundMode(wxTRANSPARENT
);
2237 bool style_set
= false;
2240 // only draw if between top and bottom:
2242 line
->GetPosition().y
+ line
->GetHeight() >= top
))
2246 ApplyStyle(line
->GetStyleInfo(), dc
);
2249 line
->Draw(dc
, this, offset
);
2253 line
->Layout(dc
, this);
2255 // little condition to speed up redrawing:
2256 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
2257 line
= line
->GetNextLine();
2259 InvalidateUpdateRect();
2261 WXLO_DEBUG(("Selection is %s : l%d,%ld/%ld,%ld",
2262 m_Selection
.m_valid
? "valid" : "invalid",
2263 m_Selection
.m_CursorA
.x
, m_Selection
.m_CursorA
.y
,
2264 m_Selection
.m_CursorB
.x
, m_Selection
.m_CursorB
.y
));
2268 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
,
2272 // First, find the right line:
2273 wxLayoutLine
*line
= m_FirstLine
;
2276 ApplyStyle(m_DefaultStyleInfo
, dc
);
2279 p
= line
->GetPosition();
2280 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
2283 // we need to run a layout here to get font sizes right :-(
2285 // VZ: we can't call Layout() from here because it marks the line as
2286 // clean and it is not refreshed when it's called from wxLayoutList::
2287 // Layout() - if we really need to do this, we should introduce an
2288 // extra argument to Layout() to prevent the line from MarkClean()ing
2290 line
->Layout(dc
, this);
2292 line
= line
->GetNextLine();
2296 if(found
) *found
= false;
2297 return NULL
; // not found
2299 if(cursorPos
) cursorPos
->y
= line
->GetLineNumber();
2300 // Now, find the object in the line:
2301 wxLOiterator i
= line
->FindObjectScreen(dc
, pos
.x
,
2302 cursorPos
? & cursorPos
->x
: NULL
,
2304 return (i
== NULLIT
) ? NULL
: *i
;
2309 wxLayoutList::GetSize(void) const
2312 *line
= m_FirstLine
,
2315 return wxPoint(0,0);
2317 wxPoint
maxPoint(0,0);
2322 if(line
->GetWidth() > maxPoint
.x
)
2323 maxPoint
.x
= line
->GetWidth();
2325 line
= line
->GetNextLine();
2328 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
2330 // if the line was just added, its height would be 0 and we can't call
2331 // Layout() from here because we don't have a dc and we might be not drawing
2332 // at all, besides... So take the cursor height by default (taking 0 is bad
2333 // because then the scrollbars won't be resized and the new line won't be
2335 if ( last
->IsDirty() )
2337 if ( last
->GetHeight() == 0 )
2338 maxPoint
.y
+= m_CursorSize
.y
;
2339 if ( last
->GetWidth() == 0 && maxPoint
.x
< m_CursorSize
.x
)
2340 maxPoint
.x
= m_CursorSize
.x
;
2348 wxLayoutList::DrawCursor(wxDC
&dc
, bool active
, wxPoint
const &translate
)
2350 if ( m_movedCursor
)
2352 UpdateCursorScreenPos(dc
);
2354 m_movedCursor
= false;
2357 wxPoint
coords(m_CursorScreenPos
);
2358 coords
+= translate
;
2360 #ifdef WXLAYOUT_DEBUG
2361 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
2362 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
2363 (long)coords
.x
, (long)coords
.y
,
2364 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
2365 (long)m_CursorLine
->GetLineNumber(),
2366 (long)m_CursorLine
->GetLength()));
2368 wxLogStatus("Cursor is at (%d, %d)", m_CursorPos
.x
, m_CursorPos
.y
);
2371 #ifdef WXLAYOUT_USE_CARET
2372 m_caret
->Move(coords
);
2373 #else // !WXLAYOUT_USE_CARET
2374 dc
.SetBrush(*wxBLACK_BRUSH
);
2375 dc
.SetLogicalFunction(wxXOR
);
2376 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
2379 dc
.DrawRectangle(coords
.x
, coords
.y
,
2380 m_CursorSize
.x
, m_CursorSize
.y
);
2381 SetUpdateRect(coords
.x
, coords
.y
);
2382 SetUpdateRect(coords
.x
+m_CursorSize
.x
, coords
.y
+m_CursorSize
.y
);
2386 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
2387 coords
.x
, coords
.y
);
2388 SetUpdateRect(coords
.x
, coords
.y
+m_CursorSize
.y
-1);
2389 SetUpdateRect(coords
.x
, coords
.y
);
2391 dc
.SetLogicalFunction(wxCOPY
);
2392 //dc.SetBrush(wxNullBrush);
2393 #endif // WXLAYOUT_USE_CARET/!WXLAYOUT_USE_CARET
2397 wxLayoutList::SetUpdateRect(CoordType x
, CoordType y
)
2399 if(m_UpdateRectValid
)
2400 GrowRect(m_UpdateRect
, x
, y
);
2405 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
2406 m_UpdateRect
.height
= 4;// wxGTK :-)
2407 m_UpdateRectValid
= true;
2412 wxLayoutList::StartSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2414 wxPoint
cpos(cposOrig
);
2417 WXLO_DEBUG(("Starting selection at %ld/%ld", cpos
.x
, cpos
.y
));
2418 m_Selection
.m_CursorA
= cpos
;
2419 m_Selection
.m_CursorB
= cpos
;
2420 m_Selection
.m_ScreenA
= spos
;
2421 m_Selection
.m_ScreenB
= spos
;
2422 m_Selection
.m_selecting
= true;
2423 m_Selection
.m_valid
= false;
2427 wxLayoutList::ContinueSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2429 wxPoint
cpos(cposOrig
);
2433 wxASSERT(m_Selection
.m_selecting
== true);
2434 wxASSERT(m_Selection
.m_valid
== false);
2435 WXLO_DEBUG(("Continuing selection at %ld/%ld", cpos
.x
, cpos
.y
));
2437 if ( m_Selection
.m_CursorB
<= cpos
)
2439 m_Selection
.m_ScreenB
= spos
;
2440 m_Selection
.m_CursorB
= cpos
;
2444 m_Selection
.m_ScreenA
= spos
;
2445 m_Selection
.m_CursorA
= cpos
;
2448 // we always want m_CursorA <= m_CursorB!
2449 if( m_Selection
.m_CursorA
> m_Selection
.m_CursorB
)
2451 // exchange the start/end points
2452 wxPoint help
= m_Selection
.m_CursorB
;
2453 m_Selection
.m_CursorB
= m_Selection
.m_CursorA
;
2454 m_Selection
.m_CursorA
= help
;
2456 help
= m_Selection
.m_ScreenB
;
2457 m_Selection
.m_ScreenB
= m_Selection
.m_ScreenA
;
2458 m_Selection
.m_ScreenA
= help
;
2463 wxLayoutList::EndSelection(const wxPoint
& cposOrig
, const wxPoint
& spos
)
2465 wxPoint
cpos(cposOrig
);
2468 ContinueSelection(cpos
);
2469 WXLO_DEBUG(("Ending selection at %ld/%ld", cpos
.x
, cpos
.y
));
2470 m_Selection
.m_selecting
= false;
2471 m_Selection
.m_valid
= true;
2475 wxLayoutList::DiscardSelection()
2477 if ( !HasSelection() )
2480 m_Selection
.m_valid
=
2481 m_Selection
.m_selecting
= false;
2482 m_Selection
.m_discarded
= true;
2486 wxLayoutList::IsSelecting(void) const
2488 return m_Selection
.m_selecting
;
2492 wxLayoutList::IsSelected(const wxPoint
&cursor
) const
2494 if ( !HasSelection() )
2497 return m_Selection
.m_CursorA
<= cursor
&& cursor
<= m_Selection
.m_CursorB
;
2501 /** Tests whether this layout line is selected and needs
2503 @param line to test for
2504 @return 0 = not selected, 1 = fully selected, -1 = partially
2508 wxLayoutList::IsSelected(const wxLayoutLine
*line
, CoordType
*from
,
2511 wxASSERT(line
); wxASSERT(to
); wxASSERT(from
);
2513 if(! m_Selection
.m_valid
&& ! m_Selection
.m_selecting
)
2516 CoordType y
= line
->GetLineNumber();
2517 if(m_Selection
.m_CursorA
.y
< y
&& m_Selection
.m_CursorB
.y
> y
)
2519 else if(m_Selection
.m_CursorA
.y
== y
)
2521 *from
= m_Selection
.m_CursorA
.x
;
2522 if(m_Selection
.m_CursorB
.y
== y
)
2523 *to
= m_Selection
.m_CursorB
.x
;
2525 *to
= line
->GetLength();
2528 else if(m_Selection
.m_CursorB
.y
== y
)
2530 *to
= m_Selection
.m_CursorB
.x
;
2531 if(m_Selection
.m_CursorA
.y
== y
)
2532 *from
= m_Selection
.m_CursorA
.x
;
2542 wxLayoutList::DeleteSelection(void)
2544 if(! m_Selection
.m_valid
)
2547 m_Selection
.m_valid
= false;
2549 // Only delete part of the current line?
2550 if(m_Selection
.m_CursorA
.y
== m_Selection
.m_CursorB
.y
)
2552 MoveCursorTo(m_Selection
.m_CursorA
);
2553 Delete(m_Selection
.m_CursorB
.x
- m_Selection
.m_CursorA
.x
);
2562 for(firstLine
= m_FirstLine
;
2563 firstLine
&& firstLine
->GetLineNumber() < m_Selection
.m_CursorA
.y
;
2564 firstLine
=firstLine
->GetNextLine())
2566 if(!firstLine
|| firstLine
->GetLineNumber() != m_Selection
.m_CursorA
.y
)
2570 for(lastLine
= m_FirstLine
;
2571 lastLine
&& lastLine
->GetLineNumber() < m_Selection
.m_CursorB
.y
;
2572 lastLine
=lastLine
->GetNextLine())
2574 if(!lastLine
|| lastLine
->GetLineNumber() != m_Selection
.m_CursorB
.y
)
2578 // We now know that the two lines are different:
2580 // First, delete what's left of this line:
2581 MoveCursorTo(m_Selection
.m_CursorA
);
2582 DeleteToEndOfLine();
2584 wxLayoutLine
*nextLine
= firstLine
->GetNextLine();
2585 while(nextLine
&& nextLine
!= lastLine
)
2586 nextLine
= nextLine
->DeleteLine(false, this);
2588 // Now nextLine = lastLine;
2589 Delete(1); // This joins firstLine and nextLine
2590 Delete(m_Selection
.m_CursorB
.x
); // This deletes the first x
2594 firstLine
->RecalculatePositions(1, this);
2597 /// Starts highlighting the selection
2599 wxLayoutList::StartHighlighting(wxDC
&dc
)
2602 dc
.SetTextForeground(m_CurrentStyleInfo
.m_bg
);
2603 dc
.SetTextBackground(m_CurrentStyleInfo
.m_fg
);
2604 dc
.SetBackgroundMode(wxSOLID
);
2608 /// Ends highlighting the selection
2610 wxLayoutList::EndHighlighting(wxDC
&dc
)
2613 dc
.SetTextForeground(m_CurrentStyleInfo
.m_fg
);
2614 dc
.SetTextBackground(m_CurrentStyleInfo
.m_bg
);
2615 dc
.SetBackgroundMode(wxTRANSPARENT
);
2621 wxLayoutList::Copy(const wxPoint
&from
,
2628 for(firstLine
= m_FirstLine
;
2629 firstLine
&& firstLine
->GetLineNumber() < from
.y
;
2630 firstLine
=firstLine
->GetNextLine())
2632 if(!firstLine
|| firstLine
->GetLineNumber() != from
.y
)
2635 for(lastLine
= m_FirstLine
;
2636 lastLine
&& lastLine
->GetLineNumber() < to
.y
;
2637 lastLine
=lastLine
->GetNextLine())
2639 if(!lastLine
|| lastLine
->GetLineNumber() != to
.y
)
2644 wxLayoutLine
*tmp
= firstLine
;
2645 firstLine
= lastLine
;
2649 wxLayoutList
*llist
= new wxLayoutList();
2651 if(firstLine
== lastLine
)
2653 firstLine
->Copy(llist
, from
.x
, to
.x
);
2657 // Extract objects from first line
2658 firstLine
->Copy(llist
, from
.x
);
2660 // Extract all lines between
2661 for(wxLayoutLine
*line
= firstLine
->GetNextLine();
2663 line
= line
->GetNextLine())
2668 // Extract objects from last line
2669 lastLine
->Copy(llist
, 0, to
.x
);
2675 wxLayoutList::GetSelection(wxLayoutDataObject
*wxlo
, bool invalidate
)
2677 if(! m_Selection
.m_valid
)
2679 if(m_Selection
.m_selecting
)
2685 if(invalidate
) m_Selection
.m_valid
= false;
2687 wxLayoutList
*llist
= Copy( m_Selection
.m_CursorA
,
2688 m_Selection
.m_CursorB
);
2690 if(llist
&& wxlo
) // export as data object, too
2694 wxLayoutExportObject
*export
;
2695 wxLayoutExportStatus
status(llist
);
2696 while((export
= wxLayoutExport( &status
, WXLO_EXPORT_AS_OBJECTS
)) != NULL
)
2698 if(export
->type
== WXLO_EXPORT_EMPTYLINE
)
2699 ; //FIXME missing support for linebreaks in string format
2701 export
->content
.object
->Write(string
);
2705 wxlo
->SetData(string
.c_str(), string
.Length()+1);
2712 #define COPY_SI(what) if(si.what != -1) { m_CurrentStyleInfo.what = si.what; fontChanged = TRUE; }
2715 wxLayoutList::ApplyStyle(wxLayoutStyleInfo
const &si
, wxDC
&dc
)
2717 bool fontChanged
= FALSE
;
2724 dc
.SetFont( m_FontCache
.GetFont(m_CurrentStyleInfo
) );
2728 m_CurrentStyleInfo
.m_fg
= si
.m_fg
;
2729 dc
.SetTextForeground(m_CurrentStyleInfo
.m_fg
);
2733 m_CurrentStyleInfo
.m_bg
= si
.m_bg
;
2734 dc
.SetTextBackground(m_CurrentStyleInfo
.m_bg
);
2739 #ifdef WXLAYOUT_DEBUG
2742 wxLayoutList::Debug(void)
2744 WXLO_DEBUG(("Cursor is in line %d, screen pos = (%d, %d)",
2745 m_CursorLine
->GetLineNumber(),
2746 m_CursorScreenPos
.x
, m_CursorScreenPos
.y
));
2749 for(line
= m_FirstLine
; line
; line
= line
->GetNextLine())
2758 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2762 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2764 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
2765 wxString
const & title
)
2770 // remove any highlighting which could interfere with printing:
2771 m_llist
->StartSelection();
2772 m_llist
->EndSelection();
2775 wxLayoutPrintout::~wxLayoutPrintout()
2780 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
2782 // The following bit is taken from the printing sample, let's see
2783 // whether it works for us.
2785 /* You might use THIS code to set the printer DC to ROUGHLY reflect
2786 * the screen text size. This page also draws lines of actual length 5cm
2789 // Get the logical pixels per inch of screen and printer
2790 int ppiScreenX
, ppiScreenY
;
2791 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
2792 int ppiPrinterX
, ppiPrinterY
;
2793 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
2795 if(ppiScreenX
== 0) // not yet set, need to guess
2800 if(ppiPrinterX
== 0) // not yet set, need to guess
2806 // This scales the DC so that the printout roughly represents the
2807 // the screen scaling. The text point size _should_ be the right size
2808 // but in fact is too small for some reason. This is a detail that will
2809 // need to be addressed at some point but can be fudged for the
2811 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
2813 // Now we have to check in case our real page size is reduced
2814 // (e.g. because we're drawing to a print preview memory DC)
2815 int pageWidth
, pageHeight
;
2817 dc
->GetSize(&w
, &h
);
2818 GetPageSizePixels(&pageWidth
, &pageHeight
);
2819 if(pageWidth
!= 0) // doesn't work always
2821 // If printer pageWidth == current DC width, then this doesn't
2822 // change. But w might be the preview bitmap width, so scale down.
2823 scale
= scale
* (float)(w
/(float)pageWidth
);
2825 dc
->SetUserScale(scale
, scale
);
2829 bool wxLayoutPrintout::OnPrintPage(int page
)
2838 top
= (page
- 1)*m_PrintoutHeight
;
2839 bottom
= top
+ m_PrintoutHeight
;
2840 // SetDeviceOrigin() doesn't work here, so we need to manually
2841 // translate all coordinates.
2842 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
2843 m_llist
->Draw(*dc
, translate
, top
, bottom
);
2850 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
2852 /* We allocate a temporary wxDC for printing, so that we can
2853 determine the correct paper size and scaling. We don't actually
2854 print anything on it. */
2856 wxPrinterDC
psdc("","",WXLLIST_TEMPFILE
,false);
2858 wxPostScriptDC
psdc(WXLLIST_TEMPFILE
,false);
2861 float scale
= ScaleDC(&psdc
);
2863 psdc
.GetSize(&m_PageWidth
, &m_PageHeight
);
2864 // This sets a left/top origin of 15% and 20%:
2865 m_Offset
= wxPoint((15*m_PageWidth
)/100, m_PageHeight
/20);
2867 // This is the length of the printable area.
2868 m_PrintoutHeight
= m_PageHeight
- (int) (m_PageHeight
* 0.15);
2869 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
2873 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
2876 *maxPage
= m_NumOfPages
;
2879 *selPageTo
= m_NumOfPages
;
2880 wxRemoveFile(WXLLIST_TEMPFILE
);
2883 bool wxLayoutPrintout::HasPage(int pageNum
)
2885 return pageNum
<= m_NumOfPages
;
2889 Stupid wxWindows doesn't draw proper ellipses, so we comment this
2890 out. It's a waste of paper anyway.
2894 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
2895 wxPoint topleft
, wxPoint bottomright
,
2898 // make backups of all essential parameters
2899 const wxBrush
& brush
= dc
.GetBrush();
2900 const wxPen
& pen
= dc
.GetPen();
2901 const wxFont
& font
= dc
.GetFont();
2903 dc
.SetBrush(*wxWHITE_BRUSH
);
2904 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
2905 dc
.DrawRoundedRectangle(topleft
.x
,
2906 topleft
.y
,bottomright
.x
-topleft
.x
,
2907 bottomright
.y
-topleft
.y
);
2908 dc
.SetBrush(*wxBLACK_BRUSH
);
2909 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
2910 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
2914 page
= "9999/9999 "; // many pages...
2916 dc
.GetTextExtent(page
,&w
,&h
);
2917 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
2918 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
2919 dc
.GetTextExtent("XXXX", &w
,&h
);
2920 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);
2931 wxFontCache::GetFont(int family
, int size
, int style
, int weight
,
2934 for(wxFCEList::iterator i
= m_FontList
.begin();
2935 i
!= m_FontList
.end(); i
++)
2936 if( (**i
).Matches(family
, size
, style
, weight
, underline
) )
2937 return (**i
).GetFont();
2939 wxFontCacheEntry
*fce
= new wxFontCacheEntry(family
, size
, style
,
2941 m_FontList
.push_back(fce
);
2942 return fce
->GetFont();