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/wxMDialogs.h"
33 # include "iostream.h"
36 # include <wx/print.h>
42 /// This should never really get created
43 #define WXLLIST_TEMPFILE "__wxllist.tmp"
47 # define TypewxString(t) g_aTypewxStrings[t]
48 # define WXLO_DEBUG(x) wxLogDebug x
50 static const char *g_aTypewxStrings
[] =
52 "invalid", "text", "cmd", "icon"
55 wxLayoutObject::Debug(void)
57 WXLO_DEBUG(("%s",g_aTypewxStrings
[GetType()]));
60 # define TypewxString(t) ""
61 # define WXLO_DEBUG(x)
65 /// Cursors smaller than this disappear in XOR drawing mode
66 #define WXLO_MINIMUM_CURSOR_WIDTH 4
68 /// Use this character to estimate a cursor size when none is available.
69 #define WXLO_CURSORCHAR "E"
70 /** @name Helper functions */
72 /// allows me to compare to wxPoints
73 bool operator ==(wxPoint
const &p1
, wxPoint
const &p2
)
75 return p1
.x
== p2
.x
&& p1
.y
== p2
.y
;
78 /// allows me to compare to wxPoints
79 bool operator !=(wxPoint
const &p1
, wxPoint
const &p2
)
81 return p1
.x
!= p2
.x
|| p1
.y
!= p2
.y
;
84 /// allows me to compare to wxPoints
85 bool operator <=(wxPoint
const &p1
, wxPoint
const &p2
)
87 return p1
.y
< p2
.y
|| (p1
.y
== p2
.y
&& p1
.x
<= p2
.x
);
90 /// grows a wxRect so that it includes the given point
93 void GrowRect(wxRect
&r
, CoordType x
, CoordType y
)
97 else if(r
.x
+ r
.width
< x
)
102 else if(r
.y
+ r
.height
< y
)
106 /// returns true if the point is in the rectangle
108 bool Contains(const wxRect
&r
, const wxPoint
&p
)
110 return r
.x
<= p
.x
&& r
.y
<= p
.y
&& (r
.x
+r
.width
) >= p
.x
&& (r
.y
+ r
.height
) >= p
.y
;
114 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
118 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
120 wxLayoutObjectText::wxLayoutObjectText(const wxString
&txt
)
130 wxLayoutObjectText::Copy(void)
132 wxLayoutObjectText
*obj
= new wxLayoutObjectText(m_Text
);
133 obj
->m_Width
= m_Width
;
134 obj
->m_Height
= m_Height
;
136 obj
->m_Bottom
= m_Bottom
;
137 obj
->SetUserData(m_UserData
);
142 wxLayoutObjectText::GetSize(CoordType
*top
, CoordType
*bottom
) const
145 *top
= m_Top
; *bottom
= m_Bottom
;
146 return wxPoint(m_Width
, m_Height
);
150 wxLayoutObjectText::Draw(wxDC
&dc
, wxPoint
const &coords
)
152 dc
.DrawText(m_Text
, coords
.x
, coords
.y
-m_Top
);
156 wxLayoutObjectText::GetOffsetScreen(wxDC
&dc
, CoordType xpos
) const
160 maxlen
= m_Text
.Length();
163 height
, descent
= 0l;
165 if(xpos
== 0) return 0; // easy
167 while(width
< xpos
&& offs
< maxlen
)
169 dc
.GetTextExtent(m_Text
.substr(0,offs
),
170 &width
, &height
, &descent
);
173 /* We have to substract 1 to compensate for the offs++, and another
174 one because we don't want to position the cursor behind the
175 object what we clicked on, but before - otherwise it looks
177 return (xpos
> 2) ? offs
-2 : 0;
181 wxLayoutObjectText::Layout(wxDC
&dc
)
185 dc
.GetTextExtent(m_Text
,&m_Width
, &m_Height
, &descent
);
187 m_Top
= m_Height
- m_Bottom
;
190 #ifdef WXLAYOUT_DEBUG
192 wxLayoutObjectText::Debug(void)
194 wxLayoutObject::Debug();
195 WXLO_DEBUG((" `%s`", m_Text
.c_str()));
199 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
203 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
205 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
const &icon
)
207 m_Icon
= new wxBitmap(icon
);
211 wxLayoutObjectIcon::Copy(void)
213 wxLayoutObjectIcon
*obj
= new wxLayoutObjectIcon(new
215 obj
->SetUserData(m_UserData
);
219 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap
*icon
)
225 wxLayoutObjectIcon::Draw(wxDC
&dc
, wxPoint
const &coords
)
227 dc
.DrawBitmap(*m_Icon
, coords
.x
, coords
.y
-m_Icon
->GetHeight(),
228 (m_Icon
->GetMask() == NULL
) ? FALSE
: TRUE
);
232 wxLayoutObjectIcon::Layout(wxDC
& /* dc */)
237 wxLayoutObjectIcon::GetSize(CoordType
*top
, CoordType
*bottom
) const
239 *top
= m_Icon
->GetHeight();
241 return wxPoint(m_Icon
->GetWidth(), m_Icon
->GetHeight());
246 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
250 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
252 wxLayoutObjectCmd::wxLayoutObjectCmd(int size
, int family
, int style
, int
253 weight
, bool underline
,
254 wxColour
&fg
, wxColour
&bg
)
257 m_font
= new wxFont(size
,family
,style
,weight
,underline
);
263 wxLayoutObjectCmd::Copy(void)
265 wxLayoutStyleInfo si
;
268 wxLayoutObjectCmd
*obj
= new wxLayoutObjectCmd(
269 si
.size
, si
.family
, si
.style
, si
.weight
, si
.underline
,
270 m_ColourFG
, m_ColourBG
);
271 obj
->SetUserData(m_UserData
);
276 wxLayoutObjectCmd::~wxLayoutObjectCmd()
282 wxLayoutObjectCmd::GetStyle(wxLayoutStyleInfo
*si
) const
284 si
->size
= m_font
->GetPointSize();
285 si
->family
= m_font
->GetFamily();
286 si
->style
= m_font
->GetStyle();
287 si
->underline
= m_font
->GetUnderlined();
288 si
->weight
= m_font
->GetWeight();
290 si
->fg_red
= m_ColourFG
.Red();
291 si
->fg_green
= m_ColourFG
.Green();
292 si
->fg_blue
= m_ColourFG
.Blue();
293 si
->bg_red
= m_ColourBG
.Red();
294 si
->bg_green
= m_ColourBG
.Green();
295 si
->bg_blue
= m_ColourBG
.Blue();
299 wxLayoutObjectCmd::Draw(wxDC
&dc
, wxPoint
const & /* coords */)
303 dc
.SetTextForeground(m_ColourFG
);
304 dc
.SetTextBackground(m_ColourBG
);
308 wxLayoutObjectCmd::Layout(wxDC
&dc
)
310 // this get called, so that recalculation uses right font sizes
311 Draw(dc
, wxPoint(0,0));
315 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
317 The wxLayoutLine object
319 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
321 wxLayoutLine::wxLayoutLine(wxLayoutLine
*prev
, wxLayoutList
*llist
)
324 m_Width
= m_Height
= 0;
329 RecalculatePosition(llist
);
332 m_LineNumber
= m_Previous
->GetLineNumber()+1;
333 m_Next
= m_Previous
->GetNextLine();
334 m_Previous
->m_Next
= this;
335 m_Height
= m_Previous
->GetHeight();
339 m_Next
->m_Previous
= this;
340 m_Next
->MoveLines(+1);
341 m_Next
->RecalculatePositions(1,llist
);
345 wxLayoutLine::~wxLayoutLine()
347 // kbList cleans itself
351 wxLayoutLine::RecalculatePosition(wxLayoutList
*llist
)
354 m_Position
= m_Previous
->GetPosition() +
355 wxPoint(0,m_Previous
->GetHeight());
357 m_Position
= wxPoint(0,0);
358 llist
->SetUpdateRect(m_Position
);
363 wxLayoutLine::RecalculatePositions(int recurse
, wxLayoutList
*llist
)
365 wxASSERT(recurse
>= 0);
366 wxPoint pos
= m_Position
;
367 CoordType height
= m_Height
;
369 // WXLO_TRACE("RecalculatePositions()");
370 RecalculatePosition(llist
);
374 m_Next
->RecalculatePositions(--recurse
, llist
);
375 else if(pos
!= m_Position
|| m_Height
!= height
)
376 m_Next
->RecalculatePositions(0, llist
);
380 wxLayoutObjectList::iterator
381 wxLayoutLine::FindObject(CoordType xpos
, CoordType
*offset
) const
385 wxLayoutObjectList::iterator
388 CoordType x
= 0, len
;
390 /* We search through the objects. As we don't like returning the
391 object that the cursor is behind, we just remember such an
392 object in "found" so we can return it if there is really no
393 further object following it. */
394 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
396 len
= (**i
).GetLength();
397 if( x
<= xpos
&& xpos
<= x
+ len
)
400 if(xpos
== x
+ len
) // is there another object behind?
402 else // we are really inside this object
405 x
+= (**i
).GetLength();
407 return found
; // ==NULL if really none found
410 wxLayoutObjectList::iterator
411 wxLayoutLine::FindObjectScreen(wxDC
&dc
,
412 CoordType xpos
, CoordType
*cxpos
,
417 wxLayoutObjectList::iterator i
;
418 CoordType x
= 0, cx
= 0, width
;
420 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
423 width
= (**i
).GetWidth();
424 if( x
<= xpos
&& xpos
<= x
+ width
)
426 *cxpos
= cx
+ (**i
).GetOffsetScreen(dc
, xpos
-x
);
427 wxLogDebug("wxLayoutLine::FindObjectScreen: cursor xpos = %ld", *cxpos
);
428 if(found
) *found
= true;
431 x
+= (**i
).GetWidth();
432 cx
+= (**i
).GetLength();
434 // behind last object:
436 if(found
) *found
= false;
437 return m_ObjectList
.tail();
441 wxLayoutLine::Insert(CoordType xpos
, wxLayoutObject
*obj
)
444 wxASSERT(obj
!= NULL
);
446 wxLOiterator i
= FindObject(xpos
, &offset
);
449 if(xpos
== 0 ) // aha, empty line!
451 m_ObjectList
.push_back(obj
);
452 m_Length
+= obj
->GetLength();
459 CoordType len
= (**i
).GetLength();
460 if(offset
== 0 /*&& i != m_ObjectList.begin()*/) // why?
461 { // insert before this object
462 m_ObjectList
.insert(i
,obj
);
463 m_Length
+= obj
->GetLength();
468 if( i
== m_ObjectList
.tail()) // last object?
469 m_ObjectList
.push_back(obj
);
471 { // insert after current object
473 m_ObjectList
.insert(i
,obj
);
475 m_Length
+= obj
->GetLength();
478 /* Otherwise we need to split the current object.
479 Fortunately this can only be a text object. */
480 wxASSERT((**i
).GetType() == WXLO_TYPE_TEXT
);
481 wxString left
, right
;
482 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
483 left
= tobj
->GetText().substr(0,offset
);
484 right
= tobj
->GetText().substr(offset
,len
-offset
);
485 // current text object gets set to right half
486 tobj
->GetText() = right
; // set new text
487 // before it we insert the new object
488 m_ObjectList
.insert(i
,obj
);
489 m_Length
+= obj
->GetLength();
490 // and before that we insert the left half
491 m_ObjectList
.insert(i
,new wxLayoutObjectText(left
));
496 wxLayoutLine::Insert(CoordType xpos
, wxString text
)
500 wxLOiterator i
= FindObject(xpos
, &offset
);
501 if(i
!= NULLIT
&& (**i
).GetType() == WXLO_TYPE_TEXT
)
503 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
504 tobj
->GetText().insert(offset
, text
);
505 m_Length
+= text
.Length();
510 return Insert(xpos
, new wxLayoutObjectText(text
));
514 wxLayoutLine::Delete(CoordType xpos
, CoordType npos
)
516 CoordType offset
, len
;
520 wxLOiterator i
= FindObject(xpos
, &offset
);
523 if(i
== NULLIT
) return npos
;
524 // now delete from that object:
525 if((**i
).GetType() != WXLO_TYPE_TEXT
)
527 if(offset
!= 0) // at end of line after a non-text object
530 len
= (**i
).GetLength();
533 m_ObjectList
.erase(i
);
537 // tidy up: remove empty text objects
538 if((**i
).GetLength() == 0)
540 m_ObjectList
.erase(i
);
544 CoordType max
= (**i
).GetLength() - offset
;
545 if(npos
< max
) max
= npos
;
548 if(xpos
== GetLength())
551 { // at the end of an object
552 // move to begin of next object:
554 continue; // start over
559 if(offset
== 0 && max
== (**i
).GetLength())
560 m_ObjectList
.erase(i
); // remove the whole object
562 ((wxLayoutObjectText
*)(*i
))->GetText().Remove(offset
,max
);
569 wxLayoutLine::DeleteWord(CoordType xpos
)
574 wxLOiterator i
= FindObject(xpos
, &offset
);
578 if(i
== NULLIT
) return false;
579 if((**i
).GetType() != WXLO_TYPE_TEXT
)
581 // This should only happen when at end of line, behind a non-text
583 if(offset
== (**i
).GetLength()) return false;
584 m_Length
-= (**i
).GetLength(); // -1
585 m_ObjectList
.erase(i
);
586 return true; // we are done
590 if(offset
== (**i
).GetLength()) // at end of object
595 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*)*i
;
597 wxString str
= tobj
->GetText();
598 str
= str
.substr(offset
,str
.Length()-offset
);
599 // Find out how many positions we need to delete:
600 // 1. eat leading space
601 while(isspace(str
.c_str()[count
])) count
++;
602 // 2. eat the word itself:
603 while(isalnum(str
.c_str()[count
])) count
++;
605 wxASSERT(count
+offset
<= (size_t) (**i
).GetLength());
606 ((wxLayoutObjectText
*)*i
)->GetText().erase(offset
,count
);
611 wxASSERT(0); // we should never arrive here
615 wxLayoutLine::DeleteLine(bool update
, wxLayoutList
*llist
)
617 if(m_Next
) m_Next
->m_Previous
= m_Previous
;
618 if(m_Previous
) m_Previous
->m_Next
= m_Next
;
621 m_Next
->MoveLines(-1);
622 m_Next
->RecalculatePositions(1, llist
);
624 wxLayoutLine
*next
= m_Next
;
630 wxLayoutLine::Draw(wxDC
&dc
,
632 const wxPoint
& offset
) const
634 wxLayoutObjectList::iterator i
;
635 wxPoint pos
= offset
;
636 pos
= pos
+ GetPosition();
640 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
643 pos
.x
+= (**i
).GetWidth();
648 wxLayoutLine::Layout(wxDC
&dc
,
654 wxLayoutObjectList::iterator i
;
657 oldHeight
= m_Height
;
659 topHeight
, bottomHeight
; // above and below baseline
662 objTopHeight
, objBottomHeight
;
665 m_Height
= 0; m_BaseLine
= 0;
667 topHeight
= 0; bottomHeight
= 0;
669 bool cursorFound
= false;
673 *cursorPos
= m_Position
;
674 if(cursorSize
) *cursorSize
= wxPoint(0,0);
677 for(i
= m_ObjectList
.begin(); i
!= NULLIT
; i
++)
680 size
= (**i
).GetSize(&objTopHeight
, &objBottomHeight
);
682 if(cursorPos
&& ! cursorFound
)
683 { // we need to check whether the text cursor is here
684 len
= (**i
).GetLength();
685 if(count
<= cx
&& count
+len
> cx
)
687 if((**i
).GetType() == WXLO_TYPE_TEXT
)
689 len
= cx
- count
; // pos in object
690 CoordType width
, height
, descent
;
691 dc
.GetTextExtent((*(wxLayoutObjectText
*)*i
).GetText().substr(0,len
),
692 &width
, &height
, &descent
);
693 cursorPos
->x
+= width
;
694 cursorPos
->y
= m_Position
.y
;
696 if(len
< (**i
).GetLength())
697 str
= (*(wxLayoutObjectText
*)*i
).GetText().substr(len
,1);
699 str
= WXLO_CURSORCHAR
;
700 dc
.GetTextExtent(str
, &width
, &height
, &descent
);
701 wxASSERT(cursorSize
);
702 // Just in case some joker inserted an empty string object:
703 if(width
== 0) width
= WXLO_MINIMUM_CURSOR_WIDTH
;
704 if(height
== 0) height
= objHeight
;
705 cursorSize
->x
= width
;
706 cursorSize
->y
= height
;
707 cursorFound
= true; // no more checks
710 { // on some other object
711 CoordType top
, bottom
; // unused
712 *cursorSize
= (**i
).GetSize(&top
,&bottom
);
713 cursorPos
->y
= m_Position
.y
;
714 cursorFound
= true; // no more checks
720 cursorPos
->x
+= (**i
).GetWidth();
725 if(objHeight
> m_Height
) m_Height
= objHeight
;
726 if(objTopHeight
> topHeight
) topHeight
= objTopHeight
;
727 if(objBottomHeight
> bottomHeight
) bottomHeight
= objBottomHeight
;
729 if(topHeight
+ bottomHeight
> m_Height
) m_Height
=
730 topHeight
+bottomHeight
;
731 m_BaseLine
= topHeight
;
735 if(GetPreviousLine()) // empty line
737 m_Height
= GetPreviousLine()->GetHeight();
738 m_BaseLine
= GetPreviousLine()->m_BaseLine
;
742 CoordType width
, height
, descent
;
743 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
745 m_BaseLine
= m_Height
- descent
;
750 // tell next line about coordinate change
751 if(m_Next
&& objHeight
!= oldHeight
)
752 m_Next
->RecalculatePositions(0, llist
);
754 // We need to check whether we found a valid cursor size:
757 // this might be the case if the cursor is at the end of the
758 // line or on a command object:
759 if(cursorSize
->y
< WXLO_MINIMUM_CURSOR_WIDTH
)
761 CoordType width
, height
, descent
;
762 dc
.GetTextExtent(WXLO_CURSORCHAR
, &width
, &height
, &descent
);
763 cursorSize
->x
= width
;
764 cursorSize
->y
= height
;
766 if(m_BaseLine
>= cursorSize
->y
) // the normal case anyway
767 cursorPos
->y
+= m_BaseLine
-cursorSize
->y
;
773 wxLayoutLine::Break(CoordType xpos
, wxLayoutList
*llist
)
778 { // insert an empty line before this one
779 wxLayoutLine
*prev
= new wxLayoutLine(m_Previous
, llist
);
780 if(m_Previous
== NULL
)
781 { // We were in first line, need to link in new empty line
785 m_Previous
->m_Height
= GetHeight(); // this is a wild guess
789 m_Next
->RecalculatePositions(1, llist
);
794 wxLOiterator i
= FindObject(xpos
, &offset
);
796 // must be at the end of the line then
797 return new wxLayoutLine(this, llist
);
800 wxLayoutLine
*newLine
= new wxLayoutLine(this, llist
);
801 // split object at i:
802 if((**i
).GetType() == WXLO_TYPE_TEXT
&& offset
!= 0)
804 wxString left
, right
;
805 wxLayoutObjectText
*tobj
= (wxLayoutObjectText
*) *i
;
806 left
= tobj
->GetText().substr(0,offset
);
807 right
= tobj
->GetText().substr(offset
,tobj
->GetLength()-offset
);
808 // current text object gets set to left half
809 tobj
->GetText() = left
; // set new text
810 newLine
->Append(new wxLayoutObjectText(right
));
811 m_Length
-= right
.Length();
812 i
++; // don't move this object to the new list
816 i
++; // move objects from here to new list
818 while(i
!= m_ObjectList
.end())
821 m_Length
-= (**i
).GetLength();
822 m_ObjectList
.remove(i
); // remove without deleting it
825 m_Next
->RecalculatePositions(2, llist
);
831 wxLayoutLine::MergeNextLine(wxLayoutList
*llist
)
833 wxASSERT(GetNextLine());
834 wxLayoutObjectList
&list
= GetNextLine()->m_ObjectList
;
837 for(i
= list
.begin(); i
!= list
.end();)
840 list
.remove(i
); // remove without deleting it
842 wxASSERT(list
.empty());
843 wxLayoutLine
*oldnext
= GetNextLine();
844 SetNext(GetNextLine()->GetNextLine());
846 RecalculatePositions(1, llist
);
850 wxLayoutLine::GetWrapPosition(CoordType column
)
853 wxLOiterator i
= FindObject(column
, &offset
);
854 if(i
== NULLIT
) return -1; // cannot wrap
856 // go backwards through the list and look for space in text objects
859 if((**i
).GetType() == WXLO_TYPE_TEXT
)
863 if( isspace(((wxLayoutObjectText
*)*i
)->GetText().c_str()[(size_t)offset
]))
870 }while(offset
!= -1);
871 i
--; // move on to previous object
875 column
-= (**i
).GetLength();
879 offset
= (**i
).GetLength();
881 /* If we reached the begin of the list and have more than one
882 object, that one is longer than the margin, so break behind
885 i
= m_ObjectList
.begin();
886 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
888 pos
+= (**i
).GetLength();
891 if(i
== NULLIT
) return -1; //why should this happen?
892 pos
+= (**i
).GetLength();
894 while(i
!= NULLIT
&& (**i
).GetType() != WXLO_TYPE_TEXT
)
896 pos
+= (**i
).GetLength();
899 if(i
== NULLIT
) return -1; //this is possible, if there is only one text object
900 // now we are at the second text object:
901 pos
-= (**i
).GetLength();
902 return pos
; // in front of it
906 #ifdef WXLAYOUT_DEBUG
908 wxLayoutLine::Debug(void)
911 wxPoint pos
= GetPosition();
912 tmp
.Printf("Line %ld, Pos (%ld,%ld), Height %ld",
913 (long int) GetLineNumber(),
914 (long int) pos
.x
, (long int) pos
.y
,
915 (long int) GetHeight());
921 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
923 The wxLayoutList object
925 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
927 wxLayoutList::wxLayoutList()
929 m_DefaultSetting
= NULL
;
931 m_ColourFG
= *wxBLACK
;
932 m_ColourBG
= *wxWHITE
;
933 InvalidateUpdateRect();
937 wxLayoutList::~wxLayoutList()
940 m_FirstLine
->DeleteLine(false, this);
944 wxLayoutList::Empty(void)
947 m_FirstLine
= m_FirstLine
->DeleteLine(false, this);
949 m_CursorPos
= wxPoint(0,0);
950 m_CursorScreenPos
= wxPoint(0,0);
951 m_CursorSize
= wxPoint(0,0);
952 m_FirstLine
= new wxLayoutLine(NULL
, this); // empty first line
953 m_CursorLine
= m_FirstLine
;
954 InvalidateUpdateRect();
959 wxLayoutList::InternalClear(void)
964 delete m_DefaultSetting
;
965 m_DefaultSetting
= NULL
;
970 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
971 int underline
, wxColour
*fg
,
974 if(family
!= -1) m_FontFamily
= family
;
975 if(size
!= -1) m_FontPtSize
= size
;
976 if(style
!= -1) m_FontStyle
= style
;
977 if(weight
!= -1) m_FontWeight
= weight
;
978 if(underline
!= -1) m_FontUnderline
= underline
!= 0;
980 if(fg
!= NULL
) m_ColourFG
= *fg
;
981 if(bg
!= NULL
) m_ColourBG
= *bg
;
984 new wxLayoutObjectCmd(m_FontPtSize
,m_FontFamily
,m_FontStyle
,m_FontWeight
,m_FontUnderline
,
985 m_ColourFG
, m_ColourBG
));
989 wxLayoutList::SetFont(int family
, int size
, int style
, int weight
,
990 int underline
, char const *fg
, char const *bg
)
998 cfg
= wxTheColourDatabase
->FindColour(fg
);
1000 cbg
= wxTheColourDatabase
->FindColour(bg
);
1002 SetFont(family
,size
,style
,weight
,underline
,cfg
,cbg
);
1006 wxLayoutList::Clear(int family
, int size
, int style
, int weight
,
1007 int /* underline */, wxColour
*fg
, wxColour
*bg
)
1012 m_FontPtSize
= size
;
1013 m_FontUnderline
= false;
1014 m_FontFamily
= family
;
1015 m_FontStyle
= style
;
1016 m_FontWeight
= weight
;
1017 if(fg
) m_ColourFG
= *fg
;
1018 if(bg
) m_ColourBG
= *bg
;
1020 m_ColourFG
= *wxBLACK
;
1021 m_ColourBG
= *wxWHITE
;
1023 if(m_DefaultSetting
)
1024 delete m_DefaultSetting
;
1026 m_DefaultSetting
= new
1027 wxLayoutObjectCmd(m_FontPtSize
,m_FontFamily
,m_FontStyle
,
1028 m_FontWeight
,m_FontUnderline
,
1029 m_ColourFG
, m_ColourBG
);
1035 wxLayoutList::MoveCursorTo(wxPoint
const &p
)
1037 SetUpdateRect(m_CursorScreenPos
);
1038 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1039 wxLayoutLine
*line
= m_FirstLine
;
1040 while(line
&& line
->GetLineNumber() != p
.y
)
1041 line
= line
->GetNextLine();
1042 if(line
&& line
->GetLineNumber() == p
.y
) // found it
1044 m_CursorPos
.y
= p
.y
;
1045 m_CursorLine
= line
;
1046 CoordType len
= line
->GetLength();
1049 m_CursorPos
.x
= p
.x
;
1054 m_CursorPos
.x
= len
;
1062 wxLayoutList::MoveCursorVertically(int n
)
1064 SetUpdateRect(m_CursorScreenPos
);
1065 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1067 if(n
< 0) // move up
1069 if(m_CursorLine
== m_FirstLine
) return false;
1070 while(n
< 0 && m_CursorLine
)
1072 m_CursorLine
= m_CursorLine
->GetPreviousLine();
1078 m_CursorLine
= m_FirstLine
;
1084 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1085 m_CursorPos
.x
= m_CursorLine
->GetLength();
1091 wxLayoutLine
*last
= m_CursorLine
;
1092 if(! m_CursorLine
->GetNextLine()) return false;
1093 while(n
> 0 && m_CursorLine
)
1097 m_CursorLine
= m_CursorLine
->GetNextLine();
1101 m_CursorLine
= last
;
1107 if(m_CursorPos
.x
> m_CursorLine
->GetLength())
1108 m_CursorPos
.x
= m_CursorLine
->GetLength();
1116 wxLayoutList::MoveCursorHorizontally(int n
)
1118 SetUpdateRect(m_CursorScreenPos
);
1119 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1123 if(m_CursorPos
.x
== 0) // at begin of line
1125 if(! MoveCursorVertically(-1))
1127 MoveCursorToEndOfLine();
1132 if(move
> m_CursorPos
.x
) move
= m_CursorPos
.x
;
1133 m_CursorPos
.x
-= move
; n
+= move
;
1138 int len
= m_CursorLine
->GetLength();
1139 if(m_CursorPos
.x
== len
) // at end of line
1141 if(! MoveCursorVertically(1))
1143 MoveCursorToBeginOfLine();
1148 if( move
>= len
-m_CursorPos
.x
) move
= len
-m_CursorPos
.x
;
1149 m_CursorPos
.x
+= move
;
1156 wxLayoutList::Insert(wxString
const &text
)
1158 wxASSERT(m_CursorLine
);
1159 SetUpdateRect(m_CursorScreenPos
);
1160 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1161 m_CursorLine
->Insert(m_CursorPos
.x
, text
);
1162 m_CursorPos
.x
+= text
.Length();
1167 wxLayoutList::Insert(wxLayoutObject
*obj
)
1169 wxASSERT(m_CursorLine
);
1170 SetUpdateRect(m_CursorScreenPos
);
1171 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1172 m_CursorLine
->Insert(m_CursorPos
.x
, obj
);
1173 m_CursorPos
.x
+= obj
->GetLength();
1178 wxLayoutList::LineBreak(void)
1180 wxASSERT(m_CursorLine
);
1181 bool setFirst
= (m_CursorLine
== m_FirstLine
&& m_CursorPos
.x
== 0);
1182 SetUpdateRect(m_CursorScreenPos
);
1183 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1184 m_CursorLine
= m_CursorLine
->Break(m_CursorPos
.x
, this);
1185 if(setFirst
) // we were at beginning of first line
1186 m_FirstLine
= m_CursorLine
->GetPreviousLine();
1193 wxLayoutList::WrapLine(CoordType column
)
1195 if(m_CursorPos
.x
<= column
|| column
< 1)
1196 return false; // do nothing yet
1199 CoordType xpos
= m_CursorLine
->GetWrapPosition(column
);
1201 return false; // cannot break line
1203 CoordType newpos
= m_CursorPos
.x
- xpos
- 1;
1204 m_CursorPos
.x
= xpos
;
1205 SetUpdateRect(m_CursorScreenPos
);
1206 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1208 Delete(1); // delete the space
1209 m_CursorPos
.x
= newpos
;
1215 wxLayoutList::Delete(CoordType npos
)
1217 wxASSERT(m_CursorLine
);
1218 SetUpdateRect(m_CursorScreenPos
);
1219 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1223 left
= m_CursorLine
->Delete(m_CursorPos
.x
, npos
);
1226 // More to delete, continue on next line.
1227 // First, check if line is empty:
1228 if(m_CursorLine
->GetLength() == 0)
1229 { // in this case, updating could probably be optimised
1231 wxASSERT(DeleteLines(1) == 0);
1240 // Need to join next line
1241 if(! m_CursorLine
->GetNextLine())
1245 m_CursorLine
->MergeNextLine(this);
1255 wxLayoutList::DeleteLines(int n
)
1257 wxASSERT(m_CursorLine
);
1259 SetUpdateRect(m_CursorScreenPos
);
1260 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1263 if(!m_CursorLine
->GetNextLine())
1264 { // we cannot delete this line, but we can clear it
1265 MoveCursorToBeginOfLine();
1266 DeleteToEndOfLine();
1270 line
= m_CursorLine
;
1271 m_CursorLine
= m_CursorLine
->DeleteLine(true, this);
1273 if(line
== m_FirstLine
) m_FirstLine
= m_CursorLine
;
1274 wxASSERT(m_FirstLine
);
1275 wxASSERT(m_CursorLine
);
1277 m_CursorLine
->RecalculatePositions(2, this);
1282 wxLayoutList::Recalculate(wxDC
&dc
, CoordType bottom
)
1284 wxLayoutLine
*line
= m_FirstLine
;
1286 // first, make sure everything is calculated - this might not be
1287 // needed, optimise it later
1288 m_DefaultSetting
->Layout(dc
);
1291 line
->RecalculatePosition(this); // so we don't need to do it all the time
1292 // little condition to speed up redrawing:
1293 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1294 line
= line
->GetNextLine();
1299 wxLayoutList::UpdateCursorScreenPos(wxDC
&dc
)
1301 wxASSERT(m_CursorLine
);
1302 m_CursorLine
->Layout(dc
, this, (wxPoint
*)&m_CursorScreenPos
, (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
1306 wxLayoutList::GetCursorScreenPos(wxDC
&dc
)
1308 UpdateCursorScreenPos(dc
);
1309 return m_CursorScreenPos
;
1313 wxLayoutList::Layout(wxDC
&dc
, CoordType bottom
)
1315 wxLayoutLine
*line
= m_FirstLine
;
1317 // first, make sure everything is calculated - this might not be
1318 // needed, optimise it later
1319 m_DefaultSetting
->Layout(dc
);
1322 if(line
== m_CursorLine
)
1323 line
->Layout(dc
, this, (wxPoint
*)&m_CursorScreenPos
, (wxPoint
*)&m_CursorSize
, m_CursorPos
.x
);
1325 line
->Layout(dc
, this);
1326 // little condition to speed up redrawing:
1327 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1328 line
= line
->GetNextLine();
1331 ///FIXME: disabled for now
1333 // can only be 0 if we are on the first line and have no next line
1334 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
1335 m_CursorLine
->GetNextLine() == NULL
&&
1336 m_CursorLine
== m_FirstLine
));
1338 SetUpdateRect(m_CursorScreenPos
);
1339 SetUpdateRect(m_CursorScreenPos
+m_CursorSize
);
1343 wxLayoutList::Draw(wxDC
&dc
,
1344 wxPoint
const &offset
,
1348 wxLayoutLine
*line
= m_FirstLine
;
1351 m_DefaultSetting
->Draw(dc
, wxPoint(0,0));
1352 wxBrush
brush(m_ColourBG
, wxSOLID
);
1357 // only draw if between top and bottom:
1358 if((top
== -1 || line
->GetPosition().y
+ line
->GetHeight() >= top
))
1359 line
->Draw(dc
, this, offset
);
1360 // little condition to speed up redrawing:
1361 if(bottom
!= -1 && line
->GetPosition().y
> bottom
) break;
1362 line
= line
->GetNextLine();
1364 // can only be 0 if we are on the first line and have no next line
1365 wxASSERT(m_CursorSize
.x
!= 0 || (m_CursorLine
&&
1366 m_CursorLine
->GetNextLine() == NULL
&&
1367 m_CursorLine
== m_FirstLine
));
1368 InvalidateUpdateRect();
1372 wxLayoutList::FindObjectScreen(wxDC
&dc
, wxPoint
const pos
,
1376 // First, find the right line:
1377 wxLayoutLine
*line
= m_FirstLine
;
1380 // we need to run a layout here to get font sizes right :-(
1381 m_DefaultSetting
->Layout(dc
);
1384 p
= line
->GetPosition();
1385 if(p
.y
<= pos
.y
&& p
.y
+line
->GetHeight() >= pos
.y
)
1387 line
->Layout(dc
, this);
1388 line
= line
->GetNextLine();
1392 if(found
) *found
= false;
1393 return NULL
; // not found
1395 if(cursorPos
) cursorPos
->y
= line
->GetLineNumber();
1396 // Now, find the object in the line:
1397 wxLOiterator i
= line
->FindObjectScreen(dc
, pos
.x
,
1398 cursorPos
? & cursorPos
->x
: NULL
,
1400 return (i
== NULLIT
) ? NULL
: *i
;
1405 wxLayoutList::GetSize(void) const
1408 *line
= m_FirstLine
,
1411 return wxPoint(0,0);
1413 wxPoint
maxPoint(0,0);
1418 if(line
->GetWidth() > maxPoint
.x
)
1419 maxPoint
.x
= line
->GetWidth();
1421 line
= line
->GetNextLine();
1424 maxPoint
.y
= last
->GetPosition().y
+ last
->GetHeight();
1429 wxLayoutList::DrawCursor(wxDC
&dc
, bool active
, wxPoint
const &translate
)
1432 coords
= m_CursorScreenPos
;
1433 coords
.x
+= translate
.x
;
1434 coords
.y
+= translate
.y
;
1436 #ifdef WXLAYOUT_DEBUG
1437 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
1438 (long)m_CursorPos
.x
, (long)m_CursorPos
.y
,
1439 (long)coords
.x
, (long)coords
.y
,
1440 (long)m_CursorSize
.x
, (long)m_CursorSize
.y
,
1441 (long)m_CursorLine
->GetLineNumber(),
1442 (long)m_CursorLine
->GetLength()));
1445 dc
.SetBrush(*wxBLACK_BRUSH
);
1446 dc
.SetLogicalFunction(wxXOR
);
1447 dc
.SetPen(wxPen(*wxBLACK
,1,wxSOLID
));
1449 dc
.DrawRectangle(coords
.x
, coords
.y
, m_CursorSize
.x
,
1452 dc
.DrawLine(coords
.x
, coords
.y
+m_CursorSize
.y
-1,
1453 coords
.x
+m_CursorSize
.x
, coords
.y
+m_CursorSize
.y
-1);
1454 dc
.SetLogicalFunction(wxCOPY
);
1455 //dc.SetBrush(wxNullBrush);
1459 wxLayoutList::SetUpdateRect(CoordType x
, CoordType y
)
1461 if(m_UpdateRectValid
)
1462 GrowRect(m_UpdateRect
, x
, y
);
1467 m_UpdateRect
.width
= 4; // large enough to avoid surprises from
1468 m_UpdateRect
.height
= 4;// wxGTK :-)
1469 m_UpdateRectValid
= true;
1474 wxLayoutList::StartSelection(void)
1476 wxLogDebug("Starting selection at %ld/%ld", m_CursorPos
.x
, m_CursorPos
.y
);
1477 m_Selection
.m_CursorA
= m_CursorPos
;
1478 m_Selection
.m_selecting
= true;
1479 m_Selection
.m_valid
= false;
1483 wxLayoutList::EndSelection(void)
1485 wxLogDebug("Ending selection at %ld/%ld", m_CursorPos
.x
, m_CursorPos
.y
);
1486 m_Selection
.m_CursorB
= m_CursorPos
;
1487 m_Selection
.m_selecting
= false;
1488 m_Selection
.m_valid
= true;
1492 wxLayoutList::IsSelecting(void)
1494 return m_Selection
.m_selecting
;
1498 wxLayoutList::IsSelected(const wxPoint
&cursor
)
1500 return m_Selection
.m_CursorA
<= cursor
1501 && cursor
<= m_Selection
.m_CursorB
;
1504 #ifdef WXLAYOUT_DEBUG
1507 wxLayoutList::Debug(void)
1512 for(line
= m_FirstLine
;
1514 line
= line
->GetNextLine())
1521 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1525 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1527 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList
*llist
,
1528 wxString
const & title
)
1534 m_ProgressDialog
= NULL
;
1538 wxLayoutPrintout::~wxLayoutPrintout()
1541 if(m_ProgressDialog
) delete m_ProgressDialog
;
1546 wxLayoutPrintout::ScaleDC(wxDC
*dc
)
1548 // The following bit is taken from the printing sample, let's see
1549 // whether it works for us.
1551 /* You might use THIS code to set the printer DC to ROUGHLY reflect
1552 * the screen text size. This page also draws lines of actual length 5cm
1555 // Get the logical pixels per inch of screen and printer
1556 int ppiScreenX
, ppiScreenY
;
1557 GetPPIScreen(&ppiScreenX
, &ppiScreenY
);
1558 int ppiPrinterX
, ppiPrinterY
;
1559 GetPPIPrinter(&ppiPrinterX
, &ppiPrinterY
);
1561 if(ppiScreenX
== 0) // not yet set, need to guess
1566 if(ppiPrinterX
== 0) // not yet set, need to guess
1572 // This scales the DC so that the printout roughly represents the
1573 // the screen scaling. The text point size _should_ be the right size
1574 // but in fact is too small for some reason. This is a detail that will
1575 // need to be addressed at some point but can be fudged for the
1577 float scale
= (float)((float)ppiPrinterX
/(float)ppiScreenX
);
1579 // Now we have to check in case our real page size is reduced
1580 // (e.g. because we're drawing to a print preview memory DC)
1581 int pageWidth
, pageHeight
;
1583 dc
->GetSize(&w
, &h
);
1584 GetPageSizePixels(&pageWidth
, &pageHeight
);
1585 if(pageWidth
!= 0) // doesn't work always
1587 // If printer pageWidth == current DC width, then this doesn't
1588 // change. But w might be the preview bitmap width, so scale down.
1589 scale
= scale
* (float)(w
/(float)pageWidth
);
1591 dc
->SetUserScale(scale
, scale
);
1595 bool wxLayoutPrintout::OnPrintPage(int page
)
1599 msg
.Printf(_("Printing page %d..."), page
);
1600 if(! m_ProgressDialog
->Update(page
, msg
))
1610 top
= (page
- 1)*m_PrintoutHeight
;
1611 bottom
= top
+ m_PrintoutHeight
;
1612 // SetDeviceOrigin() doesn't work here, so we need to manually
1613 // translate all coordinates.
1614 wxPoint
translate(m_Offset
.x
,m_Offset
.y
-top
);
1615 m_llist
->Draw(*dc
, translate
, top
, bottom
);
1622 void wxLayoutPrintout::GetPageInfo(int *minPage
, int *maxPage
, int *selPageFrom
, int *selPageTo
)
1624 /* We allocate a temporary wxDC for printing, so that we can
1625 determine the correct paper size and scaling. We don't actually
1626 print anything on it. */
1628 wxPrinterDC
psdc("","",WXLLIST_TEMPFILE
,false);
1630 wxPostScriptDC
psdc(WXLLIST_TEMPFILE
,false);
1633 float scale
= ScaleDC(&psdc
);
1635 psdc
.GetSize(&m_PageWidth
, &m_PageHeight
);
1636 // This sets a left/top origin of 15% and 20%:
1637 m_Offset
= wxPoint((15*m_PageWidth
)/100, m_PageHeight
/20);
1639 // This is the length of the printable area.
1640 m_PrintoutHeight
= m_PageHeight
- (int) (m_PageHeight
* 0.15);
1641 m_PrintoutHeight
= (int)( m_PrintoutHeight
/ scale
); // we want to use the real paper height
1645 (int)( m_llist
->GetSize().y
/ (float)(m_PrintoutHeight
));
1648 *maxPage
= m_NumOfPages
;
1651 *selPageTo
= m_NumOfPages
;
1652 wxRemoveFile(WXLLIST_TEMPFILE
);
1655 m_ProgressDialog
= new MProgressDialog(
1656 title
, _("Printing..."),m_NumOfPages
, NULL
, false, true);
1661 bool wxLayoutPrintout::HasPage(int pageNum
)
1663 return pageNum
<= m_NumOfPages
;
1667 Stupid wxWindows doesn't draw proper ellipses, so we comment this
1668 out. It's a waste of paper anyway.
1672 wxLayoutPrintout::DrawHeader(wxDC
&dc
,
1673 wxPoint topleft
, wxPoint bottomright
,
1676 // make backups of all essential parameters
1677 const wxBrush
& brush
= dc
.GetBrush();
1678 const wxPen
& pen
= dc
.GetPen();
1679 const wxFont
& font
= dc
.GetFont();
1681 dc
.SetBrush(*wxWHITE_BRUSH
);
1682 dc
.SetPen(wxPen(*wxBLACK
,0,wxSOLID
));
1683 dc
.DrawRoundedRectangle(topleft
.x
,
1684 topleft
.y
,bottomright
.x
-topleft
.x
,
1685 bottomright
.y
-topleft
.y
);
1686 dc
.SetBrush(*wxBLACK_BRUSH
);
1687 wxFont myfont
= wxFont((WXLO_DEFAULTFONTSIZE
*12)/10,
1688 wxSWISS
,wxNORMAL
,wxBOLD
,false,"Helvetica");
1692 page
= "9999/9999 "; // many pages...
1694 dc
.GetTextExtent(page
,&w
,&h
);
1695 page
.Printf("%d/%d", pageno
, (int) m_NumOfPages
);
1696 dc
.DrawText(page
,bottomright
.x
-w
,topleft
.y
+h
/2);
1697 dc
.GetTextExtent("XXXX", &w
,&h
);
1698 dc
.DrawText(m_title
, topleft
.x
+w
,topleft
.y
+h
/2);