]> git.saurik.com Git - wxWidgets.git/blob - samples/richedit/wxllist.cpp
Removed bug that made wxWindow call OnPaint
[wxWidgets.git] / samples / richedit / wxllist.cpp
1 /*-*- c++ -*-********************************************************
2 * wxllist: wxLayoutList, a layout engine for text and graphics *
3 * *
4 * (C) 1998-1999 by Karsten Ballüder (Ballueder@usa.net) *
5 * *
6 * $Id$
7 *******************************************************************/
8
9 /*
10
11 Some docs:
12
13 Layout() recalculates the objects, sizes, etc.
14 Draw() just draws them with the current settings, without
15 re-layout()ing them again
16
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
20 the right font.
21
22 */
23
24 #ifdef __GNUG__
25 # pragma implementation "wxllist.h"
26 #endif
27
28 #include <wx/wxprec.h>
29
30 #ifdef __BORLANDC__
31 # pragma hdrstop
32 #endif
33
34 #include "Mpch.h"
35
36 #ifdef M_BASEDIR
37 # include "gui/wxllist.h"
38 # include "gui/wxlparser.h"
39 # define SHOW_SELECTIONS 1
40 #else
41 # include "wxllist.h"
42 # include "wxlparser.h"
43 # define SHOW_SELECTIONS 1
44 #endif
45
46 #ifndef USE_PCH
47 # include <iostream.h>
48
49 # include <wx/dc.h>
50 # include <wx/dcps.h>
51 # include <wx/print.h>
52 # include <wx/log.h>
53 # include <wx/filefn.h>
54 #endif
55
56 #ifdef WXLAYOUT_USE_CARET
57 # include <wx/caret.h>
58 #endif // WXLAYOUT_USE_CARET
59
60 #include <ctype.h>
61
62 /// This should never really get created
63 #define WXLLIST_TEMPFILE "__wxllist.tmp"
64
65 #ifdef WXLAYOUT_DEBUG
66
67 # define TypeString(t) g_aTypeStrings[t]
68 # define WXLO_DEBUG(x) wxLogDebug x
69
70 static const char *g_aTypeStrings[] =
71 {
72 "invalid", "text", "cmd", "icon"
73 };
74 void
75 wxLayoutObject::Debug(void)
76 {
77 WXLO_DEBUG(("%s",g_aTypeStrings[GetType()]));
78 }
79 #else
80 # define TypeString(t) ""
81 # define WXLO_DEBUG(x)
82 #endif
83
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.
88 #ifdef __WXMSW__
89 static const int MSW_CORRECTION = 10;
90 #else
91 static const int MSW_CORRECTION = 0;
92 #endif
93
94 /// Cursors smaller than this disappear in XOR drawing mode
95 #define WXLO_MINIMUM_CURSOR_WIDTH 4
96
97 /// Use this character to estimate a cursor size when none is available.
98 #define WXLO_CURSORCHAR "E"
99 /** @name Helper functions */
100 //@{
101 /// allows me to compare to wxPoints
102 bool operator <=(wxPoint const &p1, wxPoint const &p2)
103 {
104 return p1.y < p2.y || (p1.y == p2.y && p1.x <= p2.x);
105 }
106
107 /*
108 The following STAY HERE until we have a working wxGTK again!!!
109 */
110 #ifndef wxWANTS_CHARS
111 /// allows me to compare to wxPoints
112 bool operator ==(wxPoint const &p1, wxPoint const &p2)
113 {
114 return p1.x == p2.x && p1.y == p2.y;
115 }
116
117 /// allows me to compare to wxPoints
118 bool operator !=(wxPoint const &p1, wxPoint const &p2)
119 {
120 return p1.x != p2.x || p1.y != p2.y;
121 }
122
123 wxPoint & operator += (wxPoint &p1, wxPoint const &p2)
124 {
125 p1.x += p2.x;
126 p1.y += p2.y;
127 return p1;
128 }
129 #endif // old wxGTK
130
131 /// allows me to compare to wxPoints
132 bool operator>(wxPoint const &p1, wxPoint const &p2)
133 {
134 return !(p1 <= p2);
135 }
136
137 /// grows a wxRect so that it includes the given point
138
139 static
140 void GrowRect(wxRect &r, CoordType x, CoordType y)
141 {
142 if(r.x > x)
143 r.x = x;
144 else if(r.x + r.width < x)
145 r.width = x - r.x;
146
147 if(r.y > y)
148 r.y = y;
149 else if(r.y + r.height < y)
150 r.height = y - r.y;
151 }
152
153 #if 0
154 // unused
155 /// returns true if the point is in the rectangle
156 static
157 bool Contains(const wxRect &r, const wxPoint &p)
158 {
159 return r.x <= p.x && r.y <= p.y && (r.x+r.width) >= p.x && (r.y + r.height) >= p.y;
160 }
161 #endif
162
163
164 //@}
165
166
167 void ReadString(wxString &to, wxString &from)
168 {
169 to = "";
170 const char *cptr = from.c_str();
171 while(*cptr && *cptr != '\n')
172 to += *cptr++;
173 if(*cptr) cptr++;
174 from = cptr;
175 }
176
177 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
178
179 wxLayoutObject
180
181 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
182
183 /* static */
184 wxLayoutObject *
185 wxLayoutObject::Read(wxString &istr)
186 {
187 wxString tmp;
188 ReadString(tmp, istr);
189 int type = -1;
190 sscanf(tmp.c_str(),"%d", &type);
191
192 switch(type)
193 {
194 case WXLO_TYPE_TEXT:
195 return wxLayoutObjectText::Read(istr);
196 case WXLO_TYPE_CMD:
197 return wxLayoutObjectCmd::Read(istr);
198 case WXLO_TYPE_ICON:
199 return wxLayoutObjectIcon::Read(istr);
200 default:
201 return NULL;
202 }
203 }
204
205 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
206
207 wxLayoutObjectText
208
209 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
210
211 wxLayoutObjectText::wxLayoutObjectText(const wxString &txt)
212 {
213 m_Text = txt;
214 m_Width = 0;
215 m_Height = 0;
216 m_Top = 0;
217 m_Bottom = 0;
218 }
219
220 wxLayoutObject *
221 wxLayoutObjectText::Copy(void)
222 {
223 wxLayoutObjectText *obj = new wxLayoutObjectText(m_Text);
224 obj->m_Width = m_Width;
225 obj->m_Height = m_Height;
226 obj->m_Top = m_Top;
227 obj->m_Bottom = m_Bottom;
228 obj->SetUserData(m_UserData);
229 return obj;
230 }
231
232
233 void
234 wxLayoutObjectText::Write(wxString &ostr)
235 {
236 ostr << (int) WXLO_TYPE_TEXT << '\n'
237 << m_Text << '\n';
238 }
239 /* static */
240 wxLayoutObjectText *
241 wxLayoutObjectText::Read(wxString &istr)
242 {
243 wxString text;
244 ReadString(text, istr);
245
246 return new wxLayoutObjectText(text);
247 }
248
249 wxPoint
250 wxLayoutObjectText::GetSize(CoordType *top, CoordType *bottom) const
251 {
252
253 *top = m_Top; *bottom = m_Bottom;
254 return wxPoint(m_Width, m_Height);
255 }
256
257 void
258 wxLayoutObjectText::Draw(wxDC &dc, wxPoint const &coords,
259 wxLayoutList *wxllist,
260 CoordType begin, CoordType end)
261 {
262 if( end <= 0 )
263 {
264 // draw the whole object normally
265 dc.DrawText(m_Text, coords.x, coords.y-m_Top);
266 }
267 else
268 {
269 // highlight the bit between begin and len
270 CoordType
271 xpos = coords.x,
272 ypos = coords.y-m_Top;
273 long width, height, descent;
274
275 if(begin < 0) begin = 0;
276 if( end > (signed)m_Text.Length() )
277 end = m_Text.Length();
278
279 wxString str = m_Text.Mid(0, begin);
280 dc.DrawText(str, xpos, ypos);
281 dc.GetTextExtent(str, &width, &height, &descent);
282 xpos += width;
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);
287 xpos += width;
288 wxllist->EndHighlighting(dc);
289 str = m_Text.Mid(end, m_Text.Length()-end);
290 dc.DrawText(str, xpos, ypos);
291 }
292 }
293
294 CoordType
295 wxLayoutObjectText::GetOffsetScreen(wxDC &dc, CoordType xpos) const
296 {
297 CoordType
298 offs = 1,
299 maxlen = m_Text.Length();
300 long
301 width = 0,
302 height, descent = 0l;
303
304 if(xpos == 0) return 0; // easy
305
306 while(width < xpos && offs < maxlen)
307 {
308 dc.GetTextExtent(m_Text.substr(0,offs),
309 &width, &height, &descent);
310 offs++;
311 }
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
315 funny. */
316 return (xpos > 2) ? offs-2 : 0;
317 }
318
319 void
320 wxLayoutObjectText::Layout(wxDC &dc, class wxLayoutList *llist)
321 {
322 long descent = 0l;
323
324 // now this is done in wxLayoutLine::Layout(), but this code might be
325 // reenabled later - in principle, it's more efficient
326 #if 0
327 CoordType widthOld = m_Width,
328 heightOld = m_Height;
329 #endif // 0
330
331 dc.GetTextExtent(m_Text, &m_Width, &m_Height, &descent);
332
333 #if 0
334 if ( widthOld != m_Width || heightOld != m_Height )
335 {
336 // as the text length changed, it must be refreshed
337 wxLayoutLine *line = GetLine();
338
339 wxCHECK_RET( line, "wxLayoutObjectText can't refresh itself" );
340
341 // as our size changed, we need to repaint the part which was appended
342 wxPoint position(line->GetPosition());
343
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 )
347 widthOld = m_Width;
348 if ( heightOld < m_Height )
349 heightOld = m_Height;
350
351 llist->SetUpdateRect(position.x + widthOld + MSW_CORRECTION,
352 position.y + heightOld + MSW_CORRECTION);
353 }
354 #endif // 0
355
356 m_Bottom = descent;
357 m_Top = m_Height - m_Bottom;
358 }
359
360
361 #ifdef WXLAYOUT_DEBUG
362 void
363 wxLayoutObjectText::Debug(void)
364 {
365 wxLayoutObject::Debug();
366 WXLO_DEBUG((" `%s`", m_Text.c_str()));
367 }
368 #endif
369
370 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
371
372 wxLayoutObjectIcon
373
374 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
375
376 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap const &icon)
377 {
378 m_Icon = new wxBitmap(icon);
379 }
380
381
382 void
383 wxLayoutObjectIcon::Write(wxString &ostr)
384 {
385 /* Exports icon through a temporary file. */
386
387 wxString file = wxGetTempFileName("wxloexport");
388
389 ostr << WXLO_TYPE_ICON << '\n'
390 << file << '\n';
391 m_Icon->SaveFile(file, WXLO_BITMAP_FORMAT);
392 }
393 /* static */
394 wxLayoutObjectIcon *
395 wxLayoutObjectIcon::Read(wxString &istr)
396 {
397 wxString file;
398 ReadString(file, istr);
399
400 if(! wxFileExists(file))
401 return NULL;
402 wxLayoutObjectIcon *obj = new wxLayoutObjectIcon;
403
404 if(!obj->m_Icon->LoadFile(file, WXLO_BITMAP_FORMAT))
405 {
406 delete obj;
407 return NULL;
408 }
409 else
410 return obj;
411 }
412
413 wxLayoutObject *
414 wxLayoutObjectIcon::Copy(void)
415 {
416 wxLayoutObjectIcon *obj = new wxLayoutObjectIcon(new
417 wxBitmap(*m_Icon));
418 obj->SetUserData(m_UserData);
419 return obj;
420 }
421
422 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap *icon)
423 {
424 m_Icon = icon;
425 }
426
427 void
428 wxLayoutObjectIcon::Draw(wxDC &dc, wxPoint const &coords,
429 wxLayoutList *wxllist,
430 CoordType begin, CoordType /* len */)
431 {
432 dc.DrawBitmap(*m_Icon, coords.x, coords.y-m_Icon->GetHeight(),
433 (m_Icon->GetMask() == NULL) ? FALSE : TRUE);
434 }
435
436 void
437 wxLayoutObjectIcon::Layout(wxDC & /* dc */, class wxLayoutList * )
438 {
439 }
440
441 wxPoint
442 wxLayoutObjectIcon::GetSize(CoordType *top, CoordType *bottom) const
443 {
444 *top = m_Icon->GetHeight();
445 *bottom = 0;
446 return wxPoint(m_Icon->GetWidth(), m_Icon->GetHeight());
447 }
448
449
450
451 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
452
453 wxLayoutObjectCmd
454
455 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
456
457
458 wxLayoutStyleInfo::wxLayoutStyleInfo(int ifamily,
459 int isize,
460 int istyle,
461 int iweight,
462 int iul,
463 wxColour *fg,
464 wxColour *bg)
465 {
466 family = ifamily;
467 size = isize;
468 style = istyle;
469 weight = iweight;
470 underline = iul != 0;
471
472 m_fg_valid = fg != 0;
473 m_bg_valid = bg != 0;
474 m_fg = m_fg_valid ? *fg : *wxBLACK;
475 m_bg = m_bg_valid ? *bg : *wxWHITE;
476 }
477
478 #define COPY_SI_(what) if(right.what != -1) what = right.what;
479
480 wxLayoutStyleInfo &
481 wxLayoutStyleInfo::operator=(const wxLayoutStyleInfo &right)
482 {
483 COPY_SI_(family);
484 COPY_SI_(style);
485 COPY_SI_(size);
486 COPY_SI_(weight);
487 COPY_SI_(underline);
488 if(right.m_fg_valid) m_fg = right.m_fg;
489 if(right.m_bg_valid) m_bg = right.m_bg;
490 return *this;
491 }
492
493 wxLayoutObjectCmd::wxLayoutObjectCmd(int family, int size, int style, int
494 weight, int underline,
495 wxColour *fg, wxColour *bg)
496
497 {
498 m_StyleInfo = new wxLayoutStyleInfo(family, size,style,weight,underline,fg,bg);
499 }
500
501 wxLayoutObject *
502 wxLayoutObjectCmd::Copy(void)
503 {
504 wxLayoutObjectCmd *obj = new wxLayoutObjectCmd(
505 m_StyleInfo->size,
506 m_StyleInfo->family,
507 m_StyleInfo->style,
508 m_StyleInfo->weight,
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);
515 return obj;
516 }
517
518 void
519 wxLayoutObjectCmd::Write(wxString &ostr)
520 {
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)
530 {
531 ostr << m_StyleInfo->m_fg.Red() << '\n'
532 << m_StyleInfo->m_fg.Green() << '\n'
533 << m_StyleInfo->m_fg.Blue() << '\n';
534 }
535 if(m_StyleInfo->m_bg_valid)
536 {
537 ostr << m_StyleInfo->m_bg.Red() << '\n'
538 << m_StyleInfo->m_bg.Green() << '\n'
539 << m_StyleInfo->m_bg.Blue() << '\n';
540 }
541 }
542 /* static */
543 wxLayoutObjectCmd *
544 wxLayoutObjectCmd::Read(wxString &istr)
545 {
546 wxLayoutObjectCmd *obj = new wxLayoutObjectCmd;
547
548 wxString tmp;
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)
564 {
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);
573 }
574 if(obj->m_StyleInfo->m_bg_valid)
575 {
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);
584 }
585 return obj;
586 }
587
588
589 wxLayoutObjectCmd::~wxLayoutObjectCmd()
590 {
591 delete m_StyleInfo;
592 }
593
594 wxLayoutStyleInfo *
595 wxLayoutObjectCmd::GetStyle(void) const
596 {
597 return m_StyleInfo;
598 }
599
600 void
601 wxLayoutObjectCmd::Draw(wxDC &dc, wxPoint const & /* coords */,
602 wxLayoutList *wxllist,
603 CoordType begin, CoordType /* len */)
604 {
605 wxASSERT(m_StyleInfo);
606 wxllist->ApplyStyle(*m_StyleInfo, dc);
607 }
608
609 void
610 wxLayoutObjectCmd::Layout(wxDC &dc, class wxLayoutList * llist)
611 {
612 // this get called, so that recalculation uses right font sizes
613 Draw(dc, wxPoint(0,0), llist);
614 }
615
616
617 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
618
619 The wxLayoutLine object
620
621 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
622
623 wxLayoutLine::wxLayoutLine(wxLayoutLine *prev, wxLayoutList *llist)
624 {
625 m_Width = m_Height = 0;
626 m_Length = 0;
627
628 m_updateLeft = -1;
629 MarkDirty(0);
630
631 m_Previous = prev;
632 m_Next = NULL;
633
634 m_LineNumber = 0;
635 RecalculatePosition(llist);
636
637 if(m_Previous)
638 {
639 m_LineNumber = m_Previous->GetLineNumber() + 1;
640 m_Next = m_Previous->GetNextLine();
641 m_Previous->m_Next = this;
642 }
643
644 if(m_Next)
645 {
646 m_Next->m_Previous = this;
647 m_Next->MoveLines(+1);
648 m_Next->RecalculatePositions(1,llist);
649 }
650
651 m_StyleInfo = llist->GetDefaultStyleInfo();
652
653 llist->IncNumLines();
654 }
655
656 wxLayoutLine::~wxLayoutLine()
657 {
658 // kbList cleans itself
659 }
660
661 wxPoint
662 wxLayoutLine::RecalculatePosition(wxLayoutList *llist)
663 {
664 wxASSERT(m_Previous || GetLineNumber() == 0);
665
666 wxPoint posOld(m_Position);
667
668 if(m_Previous)
669 {
670 m_Position = m_Previous->GetPosition();
671 m_Position.y += m_Previous->GetHeight();
672 }
673 else
674 m_Position = wxPoint(0,0);
675
676 if ( m_Position != posOld )
677 {
678 // the whole line moved and must be repainted
679 llist->SetUpdateRect(m_Position);
680 llist->SetUpdateRect(m_Position.x + GetWidth() + MSW_CORRECTION,
681 m_Position.y + GetHeight() + MSW_CORRECTION);
682 llist->SetUpdateRect(posOld);
683 llist->SetUpdateRect(posOld.x + GetWidth() + MSW_CORRECTION,
684 posOld.y + GetHeight() + MSW_CORRECTION);
685 }
686
687 return m_Position;
688 }
689
690 void
691 wxLayoutLine::RecalculatePositions(int recurse, wxLayoutList *llist)
692 {
693 //FIXME: is this really needed? We run Layout() anyway.
694 // Recursing here, drives computation time up exponentially, as
695 // each line will cause all following lines to be recalculated.
696 // Yes, or linenumbers go wrong.
697
698 wxASSERT(recurse >= 0);
699 wxPoint pos = m_Position;
700 CoordType height = m_Height;
701
702 // WXLO_TRACE("RecalculatePositions()");
703 RecalculatePosition(llist);
704 if(m_Next)
705 {
706 if(recurse > 0)
707 m_Next->RecalculatePositions(--recurse, llist);
708 else if(pos != m_Position || m_Height != height)
709 m_Next->RecalculatePositions(0, llist);
710 }
711 }
712
713 wxLayoutObjectList::iterator
714 wxLayoutLine::FindObject(CoordType xpos, CoordType *offset) const
715 {
716 wxASSERT(xpos >= 0);
717 wxASSERT(offset);
718 wxLayoutObjectList::iterator
719 i,
720 found = NULLIT;
721 CoordType x = 0, len;
722
723 /* We search through the objects. As we don't like returning the
724 object that the cursor is behind, we just remember such an
725 object in "found" so we can return it if there is really no
726 further object following it. */
727 for(i = m_ObjectList.begin(); i != NULLIT; i++)
728 {
729 len = (**i).GetLength();
730 if( x <= xpos && xpos <= x + len )
731 {
732 *offset = xpos-x;
733 if(xpos == x + len) // is there another object behind?
734 found = i;
735 else // we are really inside this object
736 return i;
737 }
738 x += (**i).GetLength();
739 }
740 return found; // ==NULL if really none found
741 }
742
743 wxLayoutObjectList::iterator
744 wxLayoutLine::FindObjectScreen(wxDC &dc, wxLayoutList *llist,
745 CoordType xpos, CoordType *cxpos,
746 bool *found) const
747 {
748 wxASSERT(cxpos);
749
750 llist->ApplyStyle(GetStyleInfo(), dc);
751
752 wxLayoutObjectList::iterator i;
753 CoordType x = 0, cx = 0, width;
754
755 for(i = m_ObjectList.begin(); i != NULLIT; i++)
756 {
757 wxLayoutObject *obj = *i;
758 if ( obj->GetType() == WXLO_TYPE_CMD )
759 {
760 // this will set the correct font for the objects which follow
761 obj->Layout(dc, llist);
762 }
763
764 width = obj->GetWidth();
765 if( x <= xpos && xpos <= x + width )
766 {
767 *cxpos = cx + obj->GetOffsetScreen(dc, xpos-x);
768
769 if ( found )
770 *found = true;
771 return i;
772 }
773
774 x += obj->GetWidth();
775 cx += obj->GetLength();
776 }
777
778 // behind last object:
779 *cxpos = cx;
780
781 if (found)
782 *found = false;
783 return m_ObjectList.tail();
784 }
785
786 /** Finds text in this line.
787 @param needle the text to find
788 @param xpos the position where to start the search
789 @return the cursoor coord where it was found or -1
790 */
791 CoordType
792 wxLayoutLine::FindText(const wxString &needle, CoordType xpos) const
793 {
794 int
795 cpos = 0,
796 relpos = -1;
797 wxString const *text;
798
799 for(wxLOiterator i = m_ObjectList.begin(); i != m_ObjectList.end(); i++)
800 {
801 if(cpos >= xpos) // search from here!
802 {
803 if((**i).GetType() == WXLO_TYPE_TEXT)
804 {
805 text = & ((wxLayoutObjectText*)(*i))->GetText();
806 relpos = text->Find(needle);
807 if(relpos >= cpos-xpos) // -1 if not found
808 {
809 return cpos+relpos;
810 }
811 }
812 cpos += (**i).GetLength();
813 }
814 }
815 return -1; // not found
816 }
817
818 bool
819 wxLayoutLine::Insert(CoordType xpos, wxLayoutObject *obj)
820 {
821 wxASSERT(xpos >= 0);
822 wxASSERT(obj != NULL);
823
824 MarkDirty(xpos);
825
826 // If we insert a command object, we need to recalculate all lines
827 // to update their styleinfo structure.
828 if(obj->GetType() == WXLO_TYPE_CMD)
829 MarkNextDirty(-1);
830
831 CoordType offset;
832 wxLOiterator i = FindObject(xpos, &offset);
833 if(i == NULLIT)
834 {
835 if(xpos == 0 ) // aha, empty line!
836 {
837 m_ObjectList.push_back(obj);
838 m_Length += obj->GetLength();
839 return true;
840 }
841 else
842 return false;
843 }
844
845 CoordType len = (**i).GetLength();
846 if(offset == 0 /*&& i != m_ObjectList.begin()*/) // why?
847 { // insert before this object
848 m_ObjectList.insert(i,obj);
849 m_Length += obj->GetLength();
850 return true;
851 }
852 if(offset == len )
853 {
854 if( i == m_ObjectList.tail()) // last object?
855 m_ObjectList.push_back(obj);
856 else
857 { // insert after current object
858 i++;
859 m_ObjectList.insert(i,obj);
860 }
861 m_Length += obj->GetLength();
862 return true;
863 }
864 /* Otherwise we need to split the current object.
865 Fortunately this can only be a text object. */
866 wxASSERT((**i).GetType() == WXLO_TYPE_TEXT);
867 wxString left, right;
868 wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
869 left = tobj->GetText().substr(0,offset);
870 right = tobj->GetText().substr(offset,len-offset);
871 // current text object gets set to right half
872 tobj->GetText() = right; // set new text
873 // before it we insert the new object
874 m_ObjectList.insert(i,obj);
875 m_Length += obj->GetLength();
876 // and before that we insert the left half
877 m_ObjectList.insert(i,new wxLayoutObjectText(left));
878 return true;
879 }
880
881 bool
882 wxLayoutLine::Insert(CoordType xpos, const wxString& text)
883 {
884 wxASSERT(xpos >= 0);
885
886 MarkDirty(xpos);
887
888 CoordType offset;
889 wxLOiterator i = FindObject(xpos, &offset);
890 if(i != NULLIT && (**i).GetType() == WXLO_TYPE_TEXT)
891 {
892 wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
893 tobj->GetText().insert(offset, text);
894 m_Length += text.Length();
895 }
896 else
897 {
898 if ( !Insert(xpos, new wxLayoutObjectText(text)) )
899 return false;
900 }
901
902 return true;
903 }
904
905 CoordType
906 wxLayoutLine::Delete(CoordType xpos, CoordType npos)
907 {
908 CoordType offset, len;
909
910 wxASSERT(xpos >= 0);
911 wxASSERT(npos >= 0);
912 MarkDirty(xpos);
913 wxLOiterator i = FindObject(xpos, &offset);
914 while(npos > 0)
915 {
916 if(i == NULLIT) return npos;
917 // now delete from that object:
918 if((**i).GetType() != WXLO_TYPE_TEXT)
919 {
920 if(offset != 0) // at end of line after a non-text object
921 return npos;
922 // always len == 1:
923 len = (**i).GetLength();
924 m_Length -= len;
925 npos -= len;
926 // If we delete a command object, we need to recalculate all lines
927 // to update their styleinfo structure.
928 if((**i).GetType() == WXLO_TYPE_CMD)
929 MarkNextDirty(-1);
930 m_ObjectList.erase(i);
931 }
932 else
933 {
934 // tidy up: remove empty text objects
935 if((**i).GetLength() == 0)
936 {
937 m_ObjectList.erase(i);
938 continue;
939 }
940 // Text object:
941 CoordType max = (**i).GetLength() - offset;
942 if(npos < max) max = npos;
943 if(max == 0)
944 {
945 if(xpos == GetLength())
946 return npos;
947 else
948 { // at the end of an object
949 // move to begin of next object:
950 i++; offset = 0;
951 continue; // start over
952 }
953 }
954 npos -= max;
955 m_Length -= max;
956 if(offset == 0 && max == (**i).GetLength())
957 m_ObjectList.erase(i); // remove the whole object
958 else
959 ((wxLayoutObjectText *)(*i))->GetText().Remove(offset,max);
960 }
961 }
962
963 return npos;
964 }
965
966 void
967 wxLayoutLine::MarkNextDirty(int recurse)
968 {
969 wxLayoutLine *line = GetNextLine();
970 while(line && (recurse == -1 || recurse >= 0))
971 {
972 line->MarkDirty();
973 line = line->GetNextLine();
974 if(recurse > 0) recurse --;
975 }
976 }
977
978 bool
979 wxLayoutLine::DeleteWord(CoordType xpos)
980 {
981 wxASSERT(xpos >= 0);
982 CoordType offset;
983 MarkDirty(xpos);
984
985 wxLOiterator i = FindObject(xpos, &offset);
986
987 for(;;)
988 {
989 if(i == NULLIT) return false;
990 if((**i).GetType() != WXLO_TYPE_TEXT)
991 {
992 // This should only happen when at end of line, behind a non-text
993 // object:
994 if(offset == (**i).GetLength()) return false;
995 m_Length -= (**i).GetLength(); // -1
996 m_ObjectList.erase(i);
997 return true; // we are done
998 }
999 else
1000 { // text object:
1001 if(offset == (**i).GetLength()) // at end of object
1002 {
1003 i++; offset = 0;
1004 continue;
1005 }
1006 wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i;
1007 size_t count = 0;
1008 wxString str = tobj->GetText();
1009 str = str.substr(offset,str.Length()-offset);
1010 // Find out how many positions we need to delete:
1011 // 1. eat leading space
1012 while(isspace(str.c_str()[count])) count++;
1013 // 2. eat the word itself:
1014 while(isalnum(str.c_str()[count])) count++;
1015 // now delete it:
1016 wxASSERT(count+offset <= (size_t) (**i).GetLength());
1017 ((wxLayoutObjectText *)*i)->GetText().erase(offset,count);
1018 m_Length -= count;
1019 return true;
1020 }
1021 }
1022
1023 wxFAIL_MSG("unreachable");
1024 }
1025
1026 wxLayoutLine *
1027 wxLayoutLine::DeleteLine(bool update, wxLayoutList *llist)
1028 {
1029 // maintain linked list integrity
1030 if(m_Next)
1031 m_Next->m_Previous = m_Previous;
1032 if(m_Previous)
1033 m_Previous->m_Next = m_Next;
1034
1035 wxLayoutLine *next = m_Next;
1036 if ( next )
1037 {
1038 // get the line numbers right again
1039 next->MoveLines(-1);
1040 }
1041
1042 if(update)
1043 {
1044 if ( next )
1045 next->RecalculatePositions(1, llist);
1046
1047 /* We assume that if we have more than one object in the list,
1048 this means that we have a command object, so we need to
1049 update the following lines. */
1050 if(m_ObjectList.size() > 1 ||
1051 ( m_ObjectList.begin() != NULLIT &&
1052 (**m_ObjectList.begin()).GetType() == WXLO_TYPE_CMD)
1053 )
1054 MarkNextDirty(-1);
1055 }
1056
1057 delete this;
1058
1059 llist->DecNumLines();
1060
1061 return next;
1062 }
1063
1064 void
1065 wxLayoutLine::Draw(wxDC &dc,
1066 wxLayoutList *llist,
1067 const wxPoint & offset) const
1068 {
1069 wxLayoutObjectList::iterator i;
1070 wxPoint pos = offset;
1071 pos = pos + GetPosition();
1072
1073 pos.y += m_BaseLine;
1074
1075 CoordType xpos = 0; // cursorpos, lenght of line
1076
1077 CoordType from, to, tempto;
1078
1079 int highlight = llist->IsSelected(this, &from, &to);
1080 // WXLO_DEBUG(("highlight=%d", highlight ));
1081 if(highlight == 1) // we need to draw the whole line inverted!
1082 llist->StartHighlighting(dc);
1083 else
1084 llist->EndHighlighting(dc);
1085
1086 for(i = m_ObjectList.begin(); i != NULLIT; i++)
1087 {
1088 if(highlight == -1) // partially highlight line
1089 {
1090 // parts of the line need highlighting
1091 tempto = xpos+(**i).GetLength();
1092 (**i).Draw(dc, pos, llist, from-xpos, to-xpos);
1093 }
1094 else
1095 (**i).Draw(dc, pos, llist);
1096 pos.x += (**i).GetWidth();
1097 xpos += (**i).GetLength();
1098 }
1099 }
1100
1101 /*
1102 This function does all the recalculation, that is, it should only be
1103 called from within wxLayoutList::Layout(), as it uses the current
1104 list's styleinfo and updates it.
1105 */
1106 void
1107 wxLayoutLine::Layout(wxDC &dc,
1108 wxLayoutList *llist,
1109 wxPoint *cursorPos,
1110 wxPoint *cursorSize,
1111 wxLayoutStyleInfo *cursorStyle,
1112 int cx,
1113 bool suppressSIupdate)
1114 {
1115 wxLayoutObjectList::iterator i;
1116
1117 // when a line becomes dirty, we redraw it from the place where it was
1118 // changed till the end of line (because the following wxLayoutObjects are
1119 // moved when the preceding one changes) - calculate the update rectangle.
1120 CoordType updateTop = m_Position.y,
1121 updateLeft = -1,
1122 updateWidth = m_Width,
1123 updateHeight = m_Height;
1124
1125 CoordType
1126 topHeight = 0,
1127 bottomHeight = 0; // above and below baseline
1128 CoordType
1129 objTopHeight, objBottomHeight; // above and below baseline
1130 CoordType
1131 len, count = 0;
1132
1133 CoordType heightOld = m_Height;
1134
1135 m_Height = 0;
1136 m_Width = 0;
1137 m_BaseLine = 0;
1138
1139 bool cursorFound = false;
1140
1141 if(cursorPos)
1142 {
1143 *cursorPos = m_Position;
1144 if(cursorSize) *cursorSize = wxPoint(0,0);
1145 }
1146
1147 m_StyleInfo = llist->GetStyleInfo(); // save current style
1148 for(i = m_ObjectList.begin(); i != NULLIT; i++)
1149 {
1150 wxLayoutObject *obj = *i;
1151 obj->Layout(dc, llist);
1152 wxPoint sizeObj = obj->GetSize(&objTopHeight, &objBottomHeight);
1153
1154 if(cursorPos && ! cursorFound)
1155 {
1156 // we need to check whether the text cursor is here
1157 len = obj->GetLength();
1158 if(count <= cx && count+len > cx)
1159 {
1160 if(obj->GetType() == WXLO_TYPE_TEXT)
1161 {
1162 len = cx - count; // pos in object
1163 CoordType width, height, descent;
1164 dc.GetTextExtent((*(wxLayoutObjectText*)*i).GetText().substr(0,len),
1165 &width, &height, &descent);
1166 cursorPos->x += width;
1167 cursorPos->y = m_Position.y;
1168 wxString str;
1169 if(len < obj->GetLength())
1170 str = (*(wxLayoutObjectText*)*i).GetText().substr(len,1);
1171 else
1172 str = WXLO_CURSORCHAR;
1173 dc.GetTextExtent(str, &width, &height, &descent);
1174
1175 if(cursorStyle) // set style info
1176 *cursorStyle = llist->GetStyleInfo();
1177 if ( cursorSize )
1178 {
1179 // Just in case some joker inserted an empty string object:
1180 if(width == 0)
1181 width = WXLO_MINIMUM_CURSOR_WIDTH;
1182 if(height == 0)
1183 height = sizeObj.y;
1184 cursorSize->x = width;
1185 cursorSize->y = height;
1186 }
1187
1188 cursorFound = true; // no more checks
1189 }
1190 else
1191 {
1192 // on some other object
1193 CoordType top, bottom; // unused
1194 if(cursorSize)
1195 *cursorSize = obj->GetSize(&top,&bottom);
1196 cursorPos->y = m_Position.y;
1197 cursorFound = true; // no more checks
1198 }
1199 }
1200 else
1201 {
1202 count += len;
1203 cursorPos->x += obj->GetWidth();
1204 }
1205 } // cursor finding
1206
1207 m_Width += sizeObj.x;
1208 if(sizeObj.y > m_Height)
1209 {
1210 m_Height = sizeObj.y;
1211 }
1212
1213 if(objTopHeight > topHeight)
1214 topHeight = objTopHeight;
1215 if(objBottomHeight > bottomHeight)
1216 bottomHeight = objBottomHeight;
1217 }
1218
1219 if ( IsDirty() )
1220 {
1221 if ( updateHeight < m_Height )
1222 updateHeight = m_Height;
1223 if ( updateWidth < m_Width )
1224 updateWidth = m_Width;
1225
1226 // update all line if we don't know where to start from
1227 if ( updateLeft == -1 )
1228 updateLeft = 0;
1229
1230 llist->SetUpdateRect(updateLeft, updateTop);
1231 llist->SetUpdateRect(updateLeft + updateWidth + MSW_CORRECTION,
1232 updateTop + updateHeight + MSW_CORRECTION);
1233 }
1234
1235 if(topHeight + bottomHeight > m_Height)
1236 {
1237 m_Height = topHeight+bottomHeight;
1238 }
1239
1240 m_BaseLine = topHeight;
1241
1242 if(m_Height == 0)
1243 {
1244 CoordType width, height, descent;
1245 dc.GetTextExtent(WXLO_CURSORCHAR, &width, &height, &descent);
1246 m_Height = height;
1247 m_BaseLine = m_Height - descent;
1248 }
1249
1250 // tell next line about coordinate change
1251 if(m_Next && m_Height != heightOld)
1252 {
1253 // FIXME isn't this done in RecalculatePositions() below anyhow?
1254 m_Next->RecalculatePositions(0, llist);
1255 }
1256
1257 // We need to check whether we found a valid cursor size:
1258 if(cursorPos && cursorSize)
1259 {
1260 // this might be the case if the cursor is at the end of the
1261 // line or on a command object:
1262 if(cursorSize->y < WXLO_MINIMUM_CURSOR_WIDTH)
1263 {
1264 CoordType width, height, descent;
1265 dc.GetTextExtent(WXLO_CURSORCHAR, &width, &height, &descent);
1266 cursorSize->x = width;
1267 cursorSize->y = height;
1268 }
1269 if(m_BaseLine >= cursorSize->y) // the normal case anyway
1270 cursorPos->y += m_BaseLine-cursorSize->y;
1271 }
1272 RecalculatePositions(1, llist);
1273 MarkClean();
1274 }
1275
1276
1277 wxLayoutLine *
1278 wxLayoutLine::Break(CoordType xpos, wxLayoutList *llist)
1279 {
1280 wxASSERT(xpos >= 0);
1281
1282 MarkDirty(xpos);
1283
1284 /* If we are at the begin of a line, we want to move all other
1285 lines down and stay with the cursor where we are. However, if we
1286 are in an empty line, we want to move down with it. */
1287 if(xpos == 0 && GetLength() > 0)
1288 { // insert an empty line before this one
1289 wxLayoutLine *prev = new wxLayoutLine(m_Previous, llist);
1290 if(m_Previous == NULL)
1291 { // We were in first line, need to link in new empty line
1292 // before this.
1293 prev->m_Next = this;
1294 m_Previous = prev;
1295 m_Previous->m_Height = 0; // this is a wild guess
1296 }
1297 if(m_Next)
1298 m_Next->RecalculatePositions(1, llist);
1299 return m_Previous;
1300 }
1301
1302 CoordType offset;
1303 wxLOiterator i = FindObject(xpos, &offset);
1304 if(i == NULLIT)
1305 // must be at the end of the line then
1306 return new wxLayoutLine(this, llist);
1307 // split this line:
1308
1309 wxLayoutLine *newLine = new wxLayoutLine(this, llist);
1310 // split object at i:
1311 if((**i).GetType() == WXLO_TYPE_TEXT && offset != 0)
1312 {
1313 wxString left, right;
1314 wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
1315 left = tobj->GetText().substr(0,offset);
1316 right = tobj->GetText().substr(offset,tobj->GetLength()-offset);
1317 // current text object gets set to left half
1318 tobj->GetText() = left; // set new text
1319 newLine->Append(new wxLayoutObjectText(right));
1320 m_Length -= right.Length();
1321 i++; // don't move this object to the new list
1322 }
1323 else
1324 {
1325 if(offset > 0)
1326 i++; // move objects from here to new list
1327 }
1328
1329 while(i != m_ObjectList.end())
1330 {
1331 wxLayoutObject *obj = *i;
1332 newLine->Append(obj);
1333 m_Length -= obj->GetLength();
1334
1335 m_ObjectList.remove(i); // remove without deleting it
1336 }
1337 if(m_Next)
1338 m_Next->RecalculatePositions(2, llist);
1339 return newLine;
1340 }
1341
1342
1343 void
1344 wxLayoutLine::MergeNextLine(wxLayoutList *llist)
1345 {
1346 wxCHECK_RET(GetNextLine(),"wxLayout internal error: no next line to merge");
1347 wxLayoutObjectList &list = GetNextLine()->m_ObjectList;
1348 wxLOiterator i;
1349
1350 MarkDirty(GetWidth());
1351
1352 wxLayoutObject *last = NULL;
1353 for(i = list.begin(); i != list.end();)
1354 {
1355 wxLayoutObject *current = *i;
1356
1357 // merge text objects together for efficiency
1358 if ( last && last->GetType() == WXLO_TYPE_TEXT &&
1359 current->GetType() == WXLO_TYPE_TEXT )
1360 {
1361 wxLayoutObjectText *textObj = (wxLayoutObjectText *)last;
1362 wxString text(textObj->GetText());
1363 text += ((wxLayoutObjectText *)current)->GetText();
1364 textObj->SetText(text);
1365
1366 list.erase(i); // remove and delete it
1367 }
1368 else
1369 {
1370 // just append the object "as was"
1371 Append(current);
1372
1373 list.remove(i); // remove without deleting it
1374 }
1375 }
1376 wxASSERT(list.empty());
1377
1378 wxLayoutLine *oldnext = GetNextLine();
1379 wxLayoutLine *nextLine = oldnext->GetNextLine();
1380 SetNext(nextLine);
1381 if ( nextLine )
1382 {
1383 nextLine->MoveLines(-1);
1384 }
1385 else
1386 {
1387 // this is now done in Delete(), but if this function is ever called
1388 // from elsewhere, we might have to move refresh code back here (in
1389 // order not to duplicate it)
1390 #if 0
1391 wxPoint pos(oldnext->GetPosition());
1392 llist->SetUpdateRect(pos);
1393 llist->SetUpdateRect(pos.x + oldnext->GetWidth() + MSW_CORRECTION,
1394 pos.y + oldnext->GetHeight() + MSW_CORRECTION);
1395 #endif // 0
1396 }
1397
1398 llist->DecNumLines();
1399
1400 delete oldnext;
1401 }
1402
1403 CoordType
1404 wxLayoutLine::GetWrapPosition(CoordType column)
1405 {
1406 CoordType offset;
1407 wxLOiterator i = FindObject(column, &offset);
1408 if(i == NULLIT) return -1; // cannot wrap
1409
1410 // go backwards through the list and look for space in text objects
1411 do
1412 {
1413 if((**i).GetType() == WXLO_TYPE_TEXT)
1414 {
1415 do
1416 {
1417 if( isspace(((wxLayoutObjectText*)*i)->GetText().c_str()[(size_t)offset]))
1418 return column;
1419 else
1420 {
1421 offset--;
1422 column--;
1423 }
1424 }while(offset != -1);
1425 i--; // move on to previous object
1426 }
1427 else
1428 {
1429 column -= (**i).GetLength();
1430 i--;
1431 }
1432 if( i != NULLIT)
1433 offset = (**i).GetLength();
1434 }while(i != NULLIT);
1435 /* If we reached the begin of the list and have more than one
1436 object, that one is longer than the margin, so break behind
1437 it. */
1438 CoordType pos = 0;
1439 i = m_ObjectList.begin();
1440 while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT)
1441 {
1442 pos += (**i).GetLength();
1443 i++;
1444 }
1445 if(i == NULLIT) return -1; //why should this happen?
1446 pos += (**i).GetLength();
1447 i++;
1448 while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT)
1449 {
1450 pos += (**i).GetLength();
1451 i++;
1452 }
1453 if(i == NULLIT) return -1; //this is possible, if there is only one text object
1454 // now we are at the second text object:
1455 pos -= (**i).GetLength();
1456 return pos; // in front of it
1457 }
1458
1459
1460 #ifdef WXLAYOUT_DEBUG
1461 void
1462 wxLayoutLine::Debug(void)
1463 {
1464 wxString tmp;
1465 wxPoint pos = GetPosition();
1466 WXLO_DEBUG(("Line %ld, Pos (%ld,%ld), Height %ld, BL %ld, Font: %d",
1467 (long int) GetLineNumber(),
1468 (long int) pos.x, (long int) pos.y,
1469 (long int) GetHeight(),
1470 (long int) m_BaseLine,
1471 (int) m_StyleInfo.family));
1472 if(m_ObjectList.begin() != NULLIT)
1473 (**m_ObjectList.begin()).Debug();
1474
1475 }
1476 #endif
1477
1478 void
1479 wxLayoutLine::Copy(wxLayoutList *llist,
1480 CoordType from,
1481 CoordType to)
1482 {
1483 CoordType firstOffset, lastOffset;
1484
1485 if(to == -1) to = GetLength();
1486 if(from == to) return;
1487
1488 wxLOiterator first = FindObject(from, &firstOffset);
1489 wxLOiterator last = FindObject(to, &lastOffset);
1490
1491 // Common special case: only one object
1492 if( first != NULLIT && last != NULLIT && *first == *last )
1493 {
1494 if( (**first).GetType() == WXLO_TYPE_TEXT )
1495 {
1496 llist->Insert(new wxLayoutObjectText(
1497 ((wxLayoutObjectText
1498 *)*first)->GetText().substr(firstOffset,
1499 lastOffset-firstOffset))
1500 );
1501 return;
1502 }
1503 else // what can we do?
1504 {
1505 if(lastOffset > firstOffset) // i.e. +1 :-)
1506 llist->Insert( (**first).Copy() );
1507 return;
1508 }
1509 }
1510
1511 // If we reach here, we can safely copy the whole first object from
1512 // the firstOffset position on:
1513 if((**first).GetType() == WXLO_TYPE_TEXT && firstOffset != 0)
1514 {
1515 llist->Insert(new wxLayoutObjectText(
1516 ((wxLayoutObjectText *)*first)->GetText().substr(firstOffset))
1517 );
1518 }
1519 else if(firstOffset == 0)
1520 llist->Insert( (**first).Copy() );
1521 // else nothing to copy :-(
1522
1523 // Now we copy all objects before the last one:
1524 wxLOiterator i = first; i++;
1525 for( ; i != last; i++)
1526 llist->Insert( (**i).Copy() );
1527
1528 // And now the last object:
1529 if(lastOffset != 0)
1530 {
1531 if( (**last).GetType() == WXLO_TYPE_TEXT )
1532 {
1533 llist->Insert(new wxLayoutObjectText(
1534 ((wxLayoutObjectText *)*last)->GetText().substr(0,lastOffset))
1535 );
1536 }
1537 else
1538 llist->Insert( (**last).Copy() );
1539 }
1540 }
1541
1542
1543 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1544
1545 The wxLayoutList object
1546
1547 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1548
1549 wxLayoutList::wxLayoutList()
1550 {
1551 #ifdef WXLAYOUT_USE_CARET
1552 m_caret = NULL;
1553 #endif // WXLAYOUT_USE_CARET
1554
1555 m_numLines = 0;
1556 m_FirstLine = NULL;
1557 InvalidateUpdateRect();
1558 Clear();
1559 }
1560
1561 wxLayoutList::~wxLayoutList()
1562 {
1563 InternalClear();
1564 Empty();
1565 m_FirstLine->DeleteLine(false, this);
1566
1567 wxASSERT_MSG( m_numLines == 0, "line count calculation broken" );
1568 }
1569
1570 void
1571 wxLayoutList::Empty(void)
1572 {
1573 while(m_FirstLine)
1574 m_FirstLine = m_FirstLine->DeleteLine(false, this);
1575
1576 m_CursorPos = wxPoint(0,0);
1577 m_CursorScreenPos = wxPoint(0,0);
1578 m_CursorSize = wxPoint(0,0);
1579 m_movedCursor = true;
1580 m_FirstLine = new wxLayoutLine(NULL, this); // empty first line
1581 m_CursorLine = m_FirstLine;
1582 InvalidateUpdateRect();
1583 }
1584
1585
1586 void
1587 wxLayoutList::InternalClear(void)
1588 {
1589 m_Selection.m_selecting = false;
1590 m_Selection.m_valid = false;
1591
1592 m_DefaultStyleInfo.family = wxSWISS;
1593 m_DefaultStyleInfo.size = WXLO_DEFAULTFONTSIZE;
1594 m_DefaultStyleInfo.style = wxNORMAL;
1595 m_DefaultStyleInfo.weight = wxNORMAL;
1596 m_DefaultStyleInfo.underline = 0;
1597 m_DefaultStyleInfo.m_fg_valid = TRUE;
1598 m_DefaultStyleInfo.m_fg = *wxBLACK;
1599 m_DefaultStyleInfo.m_bg_valid = TRUE;
1600 m_DefaultStyleInfo.m_bg = *wxWHITE;
1601
1602 m_CurrentStyleInfo = m_DefaultStyleInfo;
1603 m_CursorStyleInfo = m_DefaultStyleInfo;
1604 }
1605
1606 void
1607 wxLayoutList::SetFont(int family, int size, int style, int weight,
1608 int underline, wxColour *fg,
1609 wxColour *bg)
1610 {
1611 if(family != -1) m_CurrentStyleInfo.family = family;
1612 if(size != -1) m_CurrentStyleInfo.size = size;
1613 if(style != -1) m_CurrentStyleInfo.style = style;
1614 if(weight != -1) m_CurrentStyleInfo.weight = weight;
1615 if(underline != -1) m_CurrentStyleInfo.underline = underline != 0;
1616 if(fg) m_CurrentStyleInfo.m_fg = *fg;
1617 if(bg) m_CurrentStyleInfo.m_bg = *bg;
1618 Insert(
1619 new wxLayoutObjectCmd(
1620 m_CurrentStyleInfo.family,
1621 m_CurrentStyleInfo.size,
1622 m_CurrentStyleInfo.style,
1623 m_CurrentStyleInfo.weight,
1624 m_CurrentStyleInfo.underline,
1625 fg, bg));
1626 }
1627
1628 void
1629 wxLayoutList::SetFont(int family, int size, int style, int weight,
1630 int underline, char const *fg, char const *bg)
1631
1632 {
1633 wxColour
1634 *cfg = NULL,
1635 *cbg = NULL;
1636
1637 if( fg )
1638 cfg = wxTheColourDatabase->FindColour(fg);
1639 if( bg )
1640 cbg = wxTheColourDatabase->FindColour(bg);
1641
1642 SetFont(family,size,style,weight,underline,cfg,cbg);
1643 }
1644
1645 void
1646 wxLayoutList::Clear(int family, int size, int style, int weight,
1647 int underline, wxColour *fg, wxColour *bg)
1648 {
1649 InternalClear();
1650 m_DefaultStyleInfo = wxLayoutStyleInfo(family, size, style, weight,
1651 underline, fg, bg);
1652 m_CurrentStyleInfo = m_DefaultStyleInfo;
1653
1654 // Empty() should be called after we set m_DefaultStyleInfo because
1655 // otherwise the style info for the first line (created in Empty()) would be
1656 // incorrect
1657 Empty();
1658 }
1659
1660 wxPoint
1661 wxLayoutList::FindText(const wxString &needle, const wxPoint &cpos) const
1662 {
1663 int xpos;
1664
1665 wxLayoutLine *line;
1666 for(line = m_FirstLine;
1667 line;
1668 line = line->GetNextLine())
1669 {
1670 if(line->GetLineNumber() >= cpos.y)
1671 {
1672 xpos = line->FindText(needle,
1673 (line->GetLineNumber() == cpos.y) ?
1674 cpos.x : 0);
1675 if(xpos != -1)
1676 return wxPoint(xpos, line->GetLineNumber());
1677 }
1678 }
1679 return wxPoint(-1,-1);
1680 }
1681
1682
1683 bool
1684 wxLayoutList::MoveCursorTo(wxPoint const &p)
1685 {
1686 AddCursorPosToUpdateRect();
1687
1688 wxPoint cursorPosOld = m_CursorPos;
1689
1690 wxLayoutLine *line = m_FirstLine;
1691 while(line && line->GetLineNumber() != p.y)
1692 line = line->GetNextLine();
1693 if(line && line->GetLineNumber() == p.y) // found it
1694 {
1695 m_CursorPos.y = p.y;
1696 m_CursorLine = line;
1697 CoordType len = line->GetLength();
1698 if(len >= p.x)
1699 {
1700 m_CursorPos.x = p.x;
1701 }
1702 else
1703 {
1704 m_CursorPos.x = len;
1705 }
1706 }
1707
1708 m_movedCursor = m_CursorPos != cursorPosOld;
1709
1710 return m_CursorPos == p;
1711 }
1712
1713 bool
1714 wxLayoutList::MoveCursorVertically(int n)
1715 {
1716 AddCursorPosToUpdateRect();
1717
1718 wxPoint cursorPosOld = m_CursorPos;
1719
1720 bool rc;
1721 if(n < 0) // move up
1722 {
1723 if(m_CursorLine == m_FirstLine) return false;
1724 while(n < 0 && m_CursorLine)
1725 {
1726 m_CursorLine = m_CursorLine->GetPreviousLine();
1727 m_CursorPos.y--;
1728 n++;
1729 }
1730 if(! m_CursorLine)
1731 {
1732 m_CursorLine = m_FirstLine;
1733 m_CursorPos.y = 0;
1734 rc = false;
1735 }
1736 else
1737 {
1738 if(m_CursorPos.x > m_CursorLine->GetLength())
1739 m_CursorPos.x = m_CursorLine->GetLength();
1740 rc = true;
1741 }
1742 }
1743 else // move down
1744 {
1745 wxLayoutLine *last = m_CursorLine;
1746 if(! m_CursorLine->GetNextLine()) return false;
1747 while(n > 0 && m_CursorLine)
1748 {
1749 n--;
1750 m_CursorPos.y ++;
1751 m_CursorLine = m_CursorLine->GetNextLine();
1752 }
1753 if(! m_CursorLine)
1754 {
1755 m_CursorLine = last;
1756 m_CursorPos.y ++;
1757 rc = false;
1758 }
1759 else
1760 {
1761 if(m_CursorPos.x > m_CursorLine->GetLength())
1762 m_CursorPos.x = m_CursorLine->GetLength();
1763 rc = true;
1764 }
1765 }
1766
1767 m_movedCursor = m_CursorPos != cursorPosOld;
1768
1769 return rc;
1770 }
1771
1772 bool
1773 wxLayoutList::MoveCursorHorizontally(int n)
1774 {
1775 AddCursorPosToUpdateRect();
1776
1777 wxPoint cursorPosOld = m_CursorPos;
1778
1779 int move;
1780 while(n < 0)
1781 {
1782 if(m_CursorPos.x == 0) // at begin of line
1783 {
1784 if(! MoveCursorVertically(-1))
1785 break;
1786 MoveCursorToEndOfLine();
1787 n++;
1788 continue;
1789 }
1790 move = -n;
1791 if(move > m_CursorPos.x) move = m_CursorPos.x;
1792 m_CursorPos.x -= move; n += move;
1793 }
1794
1795 while(n > 0)
1796 {
1797 int len = m_CursorLine->GetLength();
1798 if(m_CursorPos.x == len) // at end of line
1799 {
1800 if(! MoveCursorVertically(1))
1801 break;
1802 MoveCursorToBeginOfLine();
1803 n--;
1804 continue;
1805 }
1806 move = n;
1807 if( move >= len-m_CursorPos.x) move = len-m_CursorPos.x;
1808 m_CursorPos.x += move;
1809 n -= move;
1810 }
1811
1812 m_movedCursor = m_CursorPos != cursorPosOld;
1813
1814 return n == 0;
1815 }
1816
1817 bool
1818 wxLayoutList::MoveCursorWord(int n, bool untilNext)
1819 {
1820 wxCHECK_MSG( m_CursorLine, false, "no current line" );
1821 wxCHECK_MSG( n == -1 || n == +1, false, "not implemented yet" );
1822
1823 CoordType moveDistance = 0;
1824 CoordType offset;
1825 wxLayoutLine *lineCur = m_CursorLine;
1826 for ( wxLOiterator i = lineCur->FindObject(m_CursorPos.x, &offset);
1827 n != 0;
1828 n > 0 ? i++ : i-- )
1829 {
1830 if ( i == NULLIT )
1831 {
1832 if ( n > 0 )
1833 {
1834 // moving forward, pass to the first object of the next line
1835 moveDistance++;
1836 lineCur = lineCur->GetNextLine();
1837 if ( lineCur )
1838 i = lineCur->GetFirstObject();
1839 }
1840 else
1841 {
1842 // moving backwards, pass to the last object of the prev line
1843 moveDistance--;
1844 lineCur = lineCur->GetPreviousLine();
1845 if ( lineCur )
1846 i = lineCur->GetLastObject();
1847 }
1848
1849 if ( i == NULLIT )
1850 {
1851 // moved to the end/beginning of text
1852 return false;
1853 }
1854
1855 offset = -1;
1856 }
1857
1858 wxLayoutObject *obj = *i;
1859
1860 if ( offset == -1 )
1861 {
1862 // calculate offset: we are either at the very beginning or the very
1863 // end of the object, so it isn't very difficult (the only time when
1864 // offset is != -1 is for the very first iteration when its value is
1865 // returned by FindObject)
1866 if ( n > 0 )
1867 offset = 0;
1868 else
1869 offset = obj->GetLength();
1870 }
1871
1872 if( obj->GetType() != WXLO_TYPE_TEXT )
1873 {
1874 // any visible non text objects count as one word
1875 if ( obj->IsVisibleObject() )
1876 {
1877 n > 0 ? n-- : n++;
1878
1879 moveDistance += obj->GetLength();
1880 }
1881 }
1882 else // text object
1883 {
1884 wxLayoutObjectText *tobj = (wxLayoutObjectText *)obj;
1885
1886 bool canAdvance = true;
1887
1888 if ( offset == tobj->GetLength() )
1889 {
1890 // at end of object
1891 if ( n > 0 )
1892 {
1893 // can't move further in this text object
1894 canAdvance = false;
1895
1896 // still should move over the object border
1897 moveDistance++;
1898 n--;
1899 }
1900 else if ( offset > 0 )
1901 {
1902 // offset is off by 1, make it a valid index
1903 offset--;
1904 }
1905 }
1906
1907 if ( canAdvance )
1908 {
1909 const wxString& text = tobj->GetText();
1910 const char *start = text.c_str();
1911 const char *end = start + text.length();
1912 const char *p = start + offset;
1913
1914 if ( n < 0 )
1915 {
1916 if ( offset > 0 )
1917 p--;
1918 }
1919
1920 // to the beginning/end of the next/prev word
1921 while ( p >= start && p < end && isspace(*p) )
1922 {
1923 n > 0 ? p++ : p--;
1924 }
1925
1926 // go to the end/beginning of the word (in a broad sense...)
1927 while ( p >= start && p < end && !isspace(*p) )
1928 {
1929 n > 0 ? p++ : p--;
1930 }
1931
1932 if ( n > 0 )
1933 {
1934 if ( untilNext )
1935 {
1936 // now advance to the beginning of the next word
1937 while ( isspace(*p) && p < end )
1938 p++;
1939 }
1940 }
1941 else // backwards
1942 {
1943 // in these 2 cases we took 1 char too much
1944 if ( (p < start) || isspace(*p) )
1945 {
1946 p++;
1947 }
1948 }
1949
1950 CoordType moveDelta = p - start - offset;
1951 if ( (n < 0) && (offset == tobj->GetLength() - 1) )
1952 {
1953 // because we substracted 1 from offset in this case above, now
1954 // compensate for it
1955 moveDelta--;
1956 }
1957
1958 if ( moveDelta != 0 )
1959 {
1960 moveDistance += moveDelta;
1961
1962 n > 0 ? n-- : n++;
1963 }
1964 }
1965 }
1966
1967 // except for the first iteration, offset is calculated in the beginning
1968 // of the loop
1969 offset = -1;
1970 }
1971
1972 MoveCursorHorizontally(moveDistance);
1973
1974 return true;
1975 }
1976
1977 bool
1978 wxLayoutList::Insert(wxString const &text)
1979 {
1980 wxASSERT(m_CursorLine);
1981 wxASSERT_MSG( text.Find('\n') == wxNOT_FOUND, "use wxLayoutImportText!" );
1982
1983 if ( !text )
1984 return true;
1985
1986 AddCursorPosToUpdateRect();
1987
1988 if ( !m_CursorLine->Insert(m_CursorPos.x, text) )
1989 return false;
1990
1991 m_CursorPos.x += text.Length();
1992
1993 m_movedCursor = true;
1994
1995 m_CursorLine->RecalculatePositions(0, this);
1996
1997 return true;
1998 }
1999
2000 bool
2001 wxLayoutList::Insert(wxLayoutObject *obj)
2002 {
2003 wxASSERT(m_CursorLine);
2004
2005 if(! m_CursorLine)
2006 m_CursorLine = GetFirstLine();
2007
2008 AddCursorPosToUpdateRect();
2009
2010 m_CursorLine->Insert(m_CursorPos.x, obj);
2011 m_CursorPos.x += obj->GetLength();
2012 m_movedCursor = true;
2013
2014 m_CursorLine->RecalculatePositions(0, this);
2015
2016 return true;
2017 }
2018
2019 bool
2020 wxLayoutList::Insert(wxLayoutList *llist)
2021 {
2022 wxASSERT(llist);
2023 bool rc = TRUE;
2024
2025 for(wxLayoutLine *line = llist->GetFirstLine();
2026 line;
2027 line = line->GetNextLine()
2028 )
2029 {
2030 for(wxLOiterator i = line->GetFirstObject();
2031 i != NULLIT;
2032 i++)
2033 rc |= Insert(*i);
2034 LineBreak();
2035 }
2036 return rc;
2037 }
2038
2039 bool
2040 wxLayoutList::LineBreak(void)
2041 {
2042 wxASSERT(m_CursorLine);
2043
2044 AddCursorPosToUpdateRect();
2045
2046 wxPoint position(m_CursorLine->GetPosition());
2047
2048 CoordType
2049 width = m_CursorLine->GetWidth(),
2050 height = m_CursorLine->GetHeight();
2051
2052 m_CursorLine = m_CursorLine->Break(m_CursorPos.x, this);
2053 if(m_CursorLine->GetPreviousLine() == NULL)
2054 m_FirstLine = m_CursorLine;
2055 if(m_CursorPos.x > 0)
2056 m_CursorPos.y++;
2057 m_CursorPos.x = 0;
2058
2059 // The following code will produce a height which is guaranteed to
2060 // be too high: old lineheight + the height of both new lines.
2061 // We can probably drop the old line height and start with height =
2062 // 0. FIXME
2063 wxLayoutLine *prev = m_CursorLine->GetPreviousLine();
2064 if(prev)
2065 height += prev->GetHeight();
2066 height += m_CursorLine->GetHeight();
2067
2068 m_movedCursor = true;
2069
2070 SetUpdateRect(position);
2071 SetUpdateRect(position.x + width + MSW_CORRECTION,
2072 position.y + height + MSW_CORRECTION);
2073
2074 return true;
2075 }
2076
2077 bool
2078 wxLayoutList::WrapLine(CoordType column)
2079 {
2080 if(m_CursorPos.x <= column || column < 1)
2081 return false; // do nothing yet
2082 else
2083 {
2084 CoordType xpos = m_CursorLine->GetWrapPosition(column);
2085 if(xpos == -1)
2086 return false; // cannot break line
2087 //else:
2088 CoordType newpos = m_CursorPos.x - xpos - 1;
2089 m_CursorPos.x = xpos;
2090
2091 AddCursorPosToUpdateRect();
2092
2093 LineBreak();
2094 Delete(1); // delete the space
2095 m_CursorPos.x = newpos;
2096
2097 m_CursorLine->RecalculatePositions(1, this);
2098
2099 m_movedCursor = true;
2100
2101 return true;
2102 }
2103 }
2104
2105 bool
2106 wxLayoutList::Delete(CoordType npos)
2107 {
2108 wxCHECK_MSG(m_CursorLine, false, "can't delete in non existing line");
2109
2110 if ( npos == 0 )
2111 return true;
2112
2113 AddCursorPosToUpdateRect();
2114
2115 // were other lines appended to this one (this is important to know because
2116 // this means that our width _increased_ as the result of deletion)
2117 bool wasMerged = false;
2118
2119 // the size of the region to update
2120 CoordType totalHeight = m_CursorLine->GetHeight(),
2121 totalWidth = m_CursorLine->GetWidth();
2122
2123 CoordType left;
2124 do
2125 {
2126 left = m_CursorLine->Delete(m_CursorPos.x, npos);
2127
2128 if( left > 0 )
2129 {
2130 // More to delete, continue on next line.
2131
2132 // First, check if line is empty:
2133 if(m_CursorLine->GetLength() == 0)
2134 {
2135 // in this case, updating could probably be optimised
2136 #ifdef WXLO_DEBUG
2137 wxASSERT(DeleteLines(1) == 0);
2138 #else
2139 DeleteLines(1);
2140 #endif
2141
2142 left--;
2143 }
2144 else
2145 {
2146 // Need to join next line
2147 if(! m_CursorLine->GetNextLine())
2148 break; // cannot
2149 else
2150 {
2151 wasMerged = true;
2152 wxLayoutLine *next = m_CursorLine->GetNextLine();
2153 if ( next )
2154 {
2155 totalHeight += next->GetHeight();
2156 totalWidth += next->GetWidth();
2157
2158 m_CursorLine->MergeNextLine(this);
2159 left--;
2160 }
2161 else
2162 {
2163 wxFAIL_MSG("can't delete all this");
2164
2165 return false;
2166 }
2167 }
2168 }
2169 }
2170 }
2171 while ( left> 0 );
2172
2173 // we need to update the whole tail of the line and the lines which
2174 // disappeared
2175 if ( wasMerged )
2176 {
2177 wxPoint position(m_CursorLine->GetPosition());
2178 SetUpdateRect(position);
2179 SetUpdateRect(position.x + totalWidth + MSW_CORRECTION,
2180 position.y + totalHeight + MSW_CORRECTION);
2181 }
2182
2183 return left == 0;
2184 }
2185
2186 int
2187 wxLayoutList::DeleteLines(int n)
2188 {
2189 wxASSERT(m_CursorLine);
2190 wxLayoutLine *line;
2191
2192 AddCursorPosToUpdateRect();
2193
2194 while(n > 0)
2195 {
2196 if(!m_CursorLine->GetNextLine())
2197 { // we cannot delete this line, but we can clear it
2198 MoveCursorToBeginOfLine();
2199 DeleteToEndOfLine();
2200 m_CursorLine->RecalculatePositions(2, this);
2201 return n-1;
2202 }
2203 //else:
2204 line = m_CursorLine;
2205 m_CursorLine = m_CursorLine->DeleteLine(true, this);
2206 n--;
2207 if(line == m_FirstLine) m_FirstLine = m_CursorLine;
2208 wxASSERT(m_FirstLine);
2209 wxASSERT(m_CursorLine);
2210 }
2211 m_CursorLine->RecalculatePositions(2, this);
2212 return n;
2213 }
2214
2215 void
2216 wxLayoutList::Recalculate(wxDC &dc, CoordType bottom)
2217 {
2218 wxLayoutLine *line = m_FirstLine;
2219
2220 // first, make sure everything is calculated - this might not be
2221 // needed, optimise it later
2222 ApplyStyle(m_DefaultStyleInfo, dc);
2223 while(line)
2224 {
2225 line->RecalculatePosition(this); // so we don't need to do it all the time
2226 // little condition to speed up redrawing:
2227 if(bottom != -1 && line->GetPosition().y > bottom) break;
2228 line = line->GetNextLine();
2229 }
2230 }
2231
2232 wxPoint
2233 wxLayoutList::GetCursorScreenPos(wxDC &dc)
2234 {
2235 return m_CursorScreenPos;
2236 }
2237
2238 /*
2239 Is called before each Draw(). Now, it will re-layout all lines which
2240 have changed.
2241 */
2242 void
2243 wxLayoutList::Layout(wxDC &dc, CoordType bottom, bool forceAll,
2244 wxPoint *cpos, wxPoint *csize)
2245 {
2246 // first, make sure everything is calculated - this might not be
2247 // needed, optimise it later
2248 ApplyStyle(m_DefaultStyleInfo, dc);
2249
2250 // This one we always Layout() to get the current cursor
2251 // coordinates on the screen:
2252 m_CursorLine->MarkDirty();
2253 bool wasDirty = false;
2254 wxLayoutLine *line = m_FirstLine;
2255 while(line)
2256 {
2257 if(! wasDirty)
2258 ApplyStyle(line->GetStyleInfo(), dc);
2259 if(forceAll || line->IsDirty()
2260 || (cpos && line->GetLineNumber() == cpos->y))
2261 {
2262 // The following Layout() calls will update our
2263 // m_CurrentStyleInfo if needed.
2264 if(line == m_CursorLine)
2265 {
2266 line->Layout(dc, this,
2267 (wxPoint *)&m_CursorScreenPos,
2268 (wxPoint *)&m_CursorSize,
2269 &m_CursorStyleInfo,
2270 m_CursorPos.x);
2271 // we cannot layout the line twice, so copy the coords:
2272 if(cpos && line ->GetLineNumber() == cpos->y)
2273 {
2274 *cpos = m_CursorScreenPos;
2275 if ( csize )
2276 *csize = m_CursorSize;
2277 }
2278 }
2279 else
2280 if(cpos && line->GetLineNumber() == cpos->y)
2281 line->Layout(dc, this,
2282 cpos,
2283 csize, NULL, cpos->x);
2284 else
2285 line->Layout(dc, this);
2286 // little condition to speed up redrawing:
2287 if(bottom != -1 && line->GetPosition().y > bottom)
2288 break;
2289 wasDirty = true;
2290 }
2291 line->RecalculatePositions(1, this);
2292 line = line->GetNextLine();
2293 }
2294
2295 // can only be 0 if we are on the first line and have no next line
2296 wxASSERT(m_CursorSize.x != 0 || (m_CursorLine &&
2297 m_CursorLine->GetNextLine() == NULL &&
2298 m_CursorLine == m_FirstLine));
2299 AddCursorPosToUpdateRect();
2300 }
2301
2302 wxPoint
2303 wxLayoutList::GetScreenPos(wxDC &dc, const wxPoint &cpos, wxPoint *csize)
2304 {
2305 wxPoint pos = cpos;
2306 Layout(dc, -1, false, &pos, csize);
2307 return pos;
2308 }
2309
2310 void
2311 wxLayoutList::Draw(wxDC &dc,
2312 wxPoint const &offset,
2313 CoordType top,
2314 CoordType bottom)
2315 {
2316 wxLayoutLine *line = m_FirstLine;
2317
2318 if ( m_Selection.m_discarded )
2319 {
2320 // calculate them if we don't have them already
2321 if ( !m_Selection.HasValidScreenCoords() )
2322 {
2323 m_Selection.m_ScreenA = GetScreenPos(dc, m_Selection.m_CursorA);
2324 m_Selection.m_ScreenB = GetScreenPos(dc, m_Selection.m_CursorB);
2325 }
2326
2327 // invalidate the area which was previousle selected - and which is not
2328 // selected any more
2329 SetUpdateRect(m_Selection.m_ScreenA);
2330 SetUpdateRect(m_Selection.m_ScreenB);
2331
2332 m_Selection.m_discarded = false;
2333 }
2334
2335 /* We need to re-layout all dirty lines to update styleinfos
2336 etc. However, somehow we don't find all dirty lines... */
2337 Layout(dc); //,-1,true); //FIXME
2338 ApplyStyle(m_DefaultStyleInfo, dc);
2339 wxBrush brush(m_CurrentStyleInfo.m_bg, wxSOLID);
2340 dc.SetBrush(brush);
2341 dc.SetBackgroundMode(wxTRANSPARENT);
2342
2343 bool style_set = false;
2344 while(line)
2345 {
2346 // only draw if between top and bottom:
2347 if((top == -1 ||
2348 line->GetPosition().y + line->GetHeight() > top))
2349 {
2350 // if(! style_set)
2351 {
2352 ApplyStyle(line->GetStyleInfo(), dc);
2353 style_set = true;
2354 }
2355 // little condition to speed up redrawing:
2356 if(bottom != -1
2357 && line->GetPosition().y+line->GetHeight() >= bottom)
2358 break;
2359 line->Draw(dc, this, offset);
2360 }
2361 line = line->GetNextLine();
2362 }
2363 InvalidateUpdateRect();
2364
2365 WXLO_DEBUG(("Selection is %s : l%d,%ld/%ld,%ld",
2366 m_Selection.m_valid ? "valid" : "invalid",
2367 m_Selection.m_CursorA.x, m_Selection.m_CursorA.y,
2368 m_Selection.m_CursorB.x, m_Selection.m_CursorB.y));
2369 }
2370
2371 wxLayoutObject *
2372 wxLayoutList::FindObjectScreen(wxDC &dc, wxPoint const pos,
2373 wxPoint *cursorPos,
2374 bool *found)
2375 {
2376 // First, find the right line:
2377 wxLayoutLine
2378 *line = m_FirstLine,
2379 *lastline = m_FirstLine;
2380 wxPoint p;
2381
2382 ApplyStyle(m_DefaultStyleInfo, dc);
2383 while(line)
2384 {
2385 p = line->GetPosition();
2386 if(p.y <= pos.y && p.y+line->GetHeight() >= pos.y)
2387 break;
2388 lastline = line;
2389 line = line->GetNextLine();
2390 }
2391
2392 bool didFind = line != NULL;
2393
2394 if ( !line )
2395 {
2396 // use the last line:
2397 line = lastline;
2398 }
2399
2400 if ( cursorPos )
2401 cursorPos->y = line->GetLineNumber();
2402
2403 bool foundinline = true;
2404 // Now, find the object in the line:
2405 wxLOiterator i = line->FindObjectScreen(dc, this,
2406 pos.x,
2407 cursorPos ? &cursorPos->x : NULL,
2408 &foundinline);
2409 if ( found )
2410 *found = didFind && foundinline;
2411
2412 return (i == NULLIT) ? NULL : *i;
2413
2414 }
2415
2416 wxPoint
2417 wxLayoutList::GetSize(void) const
2418 {
2419 wxLayoutLine
2420 *line = m_FirstLine,
2421 *last = line;
2422 if(! line)
2423 return wxPoint(0,0);
2424
2425 wxPoint maxPoint(0,0);
2426
2427 // find last line:
2428 while(line)
2429 {
2430 if(line->GetWidth() > maxPoint.x)
2431 maxPoint.x = line->GetWidth();
2432 last = line;
2433 line = line->GetNextLine();
2434 }
2435
2436 maxPoint.y = last->GetPosition().y + last->GetHeight();
2437
2438 // if the line was just added, its height would be 0 and we can't call
2439 // Layout() from here because we don't have a dc and we might be not drawing
2440 // at all, besides... So take the cursor height by default (taking 0 is bad
2441 // because then the scrollbars won't be resized and the new line won't be
2442 // shown at all)
2443 if ( last->IsDirty() )
2444 {
2445 if ( last->GetHeight() == 0 )
2446 maxPoint.y += m_CursorSize.y;
2447 if ( last->GetWidth() == 0 && maxPoint.x < m_CursorSize.x )
2448 maxPoint.x = m_CursorSize.x;
2449 }
2450
2451 return maxPoint;
2452 }
2453
2454
2455 void
2456 wxLayoutList::DrawCursor(wxDC &dc, bool active, wxPoint const &translate)
2457 {
2458 if ( m_movedCursor )
2459 m_movedCursor = false;
2460
2461 wxPoint coords(m_CursorScreenPos);
2462 coords += translate;
2463
2464 #ifdef WXLAYOUT_DEBUG
2465 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
2466 (long)m_CursorPos.x, (long)m_CursorPos.y,
2467 (long)coords.x, (long)coords.y,
2468 (long)m_CursorSize.x, (long)m_CursorSize.y,
2469 (long)m_CursorLine->GetLineNumber(),
2470 (long)m_CursorLine->GetLength()));
2471
2472 wxLogStatus("Cursor is at (%d, %d)", m_CursorPos.x, m_CursorPos.y);
2473 #endif
2474
2475 #ifdef WXLAYOUT_USE_CARET
2476 m_caret->Move(coords);
2477 #else // !WXLAYOUT_USE_CARET
2478 dc.SetBrush(*wxWHITE_BRUSH);
2479 //FIXME: wxGTK XOR is borken at the moment!!!dc.SetLogicalFunction(wxXOR);
2480 dc.SetPen(wxPen(*wxBLACK,1,wxSOLID));
2481 if(active)
2482 {
2483 dc.SetLogicalFunction(wxXOR);
2484 dc.DrawRectangle(coords.x, coords.y,
2485 m_CursorSize.x, m_CursorSize.y);
2486 SetUpdateRect(coords.x, coords.y);
2487 SetUpdateRect(coords.x+m_CursorSize.x, coords.y+m_CursorSize.y);
2488 }
2489 else
2490 {
2491 dc.SetLogicalFunction(wxCOPY);
2492 dc.DrawLine(coords.x, coords.y+m_CursorSize.y-1,
2493 coords.x, coords.y);
2494 SetUpdateRect(coords.x, coords.y+m_CursorSize.y-1);
2495 SetUpdateRect(coords.x, coords.y);
2496 }
2497 dc.SetLogicalFunction(wxCOPY);
2498 //dc.SetBrush(wxNullBrush);
2499 #endif // WXLAYOUT_USE_CARET/!WXLAYOUT_USE_CARET
2500 }
2501
2502 void
2503 wxLayoutList::SetUpdateRect(CoordType x, CoordType y)
2504 {
2505 if(m_UpdateRectValid)
2506 GrowRect(m_UpdateRect, x, y);
2507 else
2508 {
2509 m_UpdateRect.x = x;
2510 m_UpdateRect.y = y;
2511 m_UpdateRect.width = 4; // large enough to avoid surprises from
2512 m_UpdateRect.height = 4;// wxGTK :-)
2513 m_UpdateRectValid = true;
2514 }
2515 }
2516
2517 void
2518 wxLayoutList::StartSelection(const wxPoint& cposOrig, const wxPoint& spos)
2519 {
2520 wxPoint cpos(cposOrig);
2521 if ( cpos.x == -1 )
2522 cpos = m_CursorPos;
2523 WXLO_DEBUG(("Starting selection at %ld/%ld", cpos.x, cpos.y));
2524 m_Selection.m_CursorA = cpos;
2525 m_Selection.m_CursorB = cpos;
2526 m_Selection.m_ScreenA = spos;
2527 m_Selection.m_ScreenB = spos;
2528 m_Selection.m_selecting = true;
2529 m_Selection.m_valid = false;
2530 }
2531
2532 void
2533 wxLayoutList::ContinueSelection(const wxPoint& cposOrig, const wxPoint& spos)
2534 {
2535 wxPoint cpos(cposOrig);
2536 if(cpos.x == -1)
2537 cpos = m_CursorPos;
2538
2539 wxASSERT(m_Selection.m_selecting == true);
2540 wxASSERT(m_Selection.m_valid == false);
2541 WXLO_DEBUG(("Continuing selection at %ld/%ld", cpos.x, cpos.y));
2542
2543 if ( m_Selection.m_CursorB <= cpos )
2544 {
2545 m_Selection.m_ScreenB = spos;
2546 m_Selection.m_CursorB = cpos;
2547 }
2548 else
2549 {
2550 m_Selection.m_ScreenA = spos;
2551 m_Selection.m_CursorA = cpos;
2552 }
2553
2554 // we always want m_CursorA <= m_CursorB!
2555 if( m_Selection.m_CursorA > m_Selection.m_CursorB )
2556 {
2557 // exchange the start/end points
2558 wxPoint help = m_Selection.m_CursorB;
2559 m_Selection.m_CursorB = m_Selection.m_CursorA;
2560 m_Selection.m_CursorA = help;
2561
2562 help = m_Selection.m_ScreenB;
2563 m_Selection.m_ScreenB = m_Selection.m_ScreenA;
2564 m_Selection.m_ScreenA = help;
2565 }
2566 }
2567
2568 void
2569 wxLayoutList::EndSelection(const wxPoint& cposOrig, const wxPoint& spos)
2570 {
2571 wxPoint cpos(cposOrig);
2572 if(cpos.x == -1)
2573 cpos = m_CursorPos;
2574 ContinueSelection(cpos);
2575 WXLO_DEBUG(("Ending selection at %ld/%ld", cpos.x, cpos.y));
2576 m_Selection.m_selecting = false;
2577 m_Selection.m_valid = true;
2578 }
2579
2580 void
2581 wxLayoutList::DiscardSelection()
2582 {
2583 if ( !HasSelection() )
2584 return;
2585
2586 m_Selection.m_valid =
2587 m_Selection.m_selecting = false;
2588 m_Selection.m_discarded = true;
2589 }
2590
2591 bool
2592 wxLayoutList::IsSelecting(void) const
2593 {
2594 return m_Selection.m_selecting;
2595 }
2596
2597 bool
2598 wxLayoutList::IsSelected(const wxPoint &cursor) const
2599 {
2600 if ( !HasSelection() )
2601 return false;
2602
2603 return m_Selection.m_CursorA <= cursor && cursor <= m_Selection.m_CursorB;
2604 }
2605
2606
2607 /** Tests whether this layout line is selected and needs
2608 highlighting.
2609 @param line to test for
2610 @return 0 = not selected, 1 = fully selected, -1 = partially
2611 selected
2612 */
2613 int
2614 wxLayoutList::IsSelected(const wxLayoutLine *line, CoordType *from,
2615 CoordType *to)
2616 {
2617 wxASSERT(line); wxASSERT(to); wxASSERT(from);
2618
2619 if(! m_Selection.m_valid && ! m_Selection.m_selecting)
2620 return 0;
2621
2622 CoordType y = line->GetLineNumber();
2623 if(m_Selection.m_CursorA.y < y && m_Selection.m_CursorB.y > y)
2624 return 1;
2625 else if(m_Selection.m_CursorA.y == y)
2626 {
2627 *from = m_Selection.m_CursorA.x;
2628 if(m_Selection.m_CursorB.y == y)
2629 *to = m_Selection.m_CursorB.x;
2630 else
2631 *to = line->GetLength();
2632 return -1;
2633 }
2634 else if(m_Selection.m_CursorB.y == y)
2635 {
2636 *to = m_Selection.m_CursorB.x;
2637 if(m_Selection.m_CursorA.y == y)
2638 *from = m_Selection.m_CursorA.x;
2639 else
2640 *from = 0;
2641 return -1;
2642 }
2643 else
2644 return 0;
2645 }
2646
2647 void
2648 wxLayoutList::DeleteSelection(void)
2649 {
2650 if(! m_Selection.m_valid)
2651 return;
2652
2653 m_Selection.m_valid = false;
2654
2655 // Only delete part of the current line?
2656 if(m_Selection.m_CursorA.y == m_Selection.m_CursorB.y)
2657 {
2658 MoveCursorTo(m_Selection.m_CursorA);
2659 Delete(m_Selection.m_CursorB.x - m_Selection.m_CursorA.x);
2660 return;
2661 }
2662
2663 // We now know that the two lines are different:
2664
2665 wxLayoutLine
2666 * firstLine = GetLine(m_Selection.m_CursorA.y),
2667 * lastLine = GetLine(m_Selection.m_CursorB.y);
2668
2669 // First, delete what's left of this line:
2670 MoveCursorTo(m_Selection.m_CursorA);
2671 DeleteToEndOfLine();
2672
2673 wxLayoutLine *prevLine = firstLine->GetPreviousLine(),
2674 *nextLine = firstLine->GetNextLine();
2675 while(nextLine && nextLine != lastLine)
2676 nextLine = nextLine->DeleteLine(false, this);
2677
2678 // Now nextLine = lastLine;
2679 Delete(1); // This joins firstLine and nextLine
2680 Delete(m_Selection.m_CursorB.x); // This deletes the first x positions
2681
2682 // Recalculate the line positions and numbers but notice that firstLine
2683 // might not exist any more - it could be deleted by Delete(1) above
2684 wxLayoutLine *firstLine2 = prevLine ? prevLine->GetNextLine() : m_FirstLine;
2685 firstLine2->RecalculatePositions(1, this);
2686 }
2687
2688 /// Starts highlighting the selection
2689 void
2690 wxLayoutList::StartHighlighting(wxDC &dc)
2691 {
2692 #if SHOW_SELECTIONS
2693 dc.SetTextForeground(m_CurrentStyleInfo.m_bg);
2694 dc.SetTextBackground(m_CurrentStyleInfo.m_fg);
2695 dc.SetBackgroundMode(wxSOLID);
2696 #endif
2697 }
2698
2699 /// Ends highlighting the selection
2700 void
2701 wxLayoutList::EndHighlighting(wxDC &dc)
2702 {
2703 #if SHOW_SELECTIONS
2704 dc.SetTextForeground(m_CurrentStyleInfo.m_fg);
2705 dc.SetTextBackground(m_CurrentStyleInfo.m_bg);
2706 dc.SetBackgroundMode(wxTRANSPARENT);
2707 #endif
2708 }
2709
2710
2711 wxLayoutList *
2712 wxLayoutList::Copy(const wxPoint &from,
2713 const wxPoint &to)
2714 {
2715 wxLayoutLine
2716 * firstLine = NULL,
2717 * lastLine = NULL;
2718
2719 for(firstLine = m_FirstLine;
2720 firstLine && firstLine->GetLineNumber() < from.y;
2721 firstLine=firstLine->GetNextLine())
2722 ;
2723 if(!firstLine || firstLine->GetLineNumber() != from.y)
2724 return NULL;
2725
2726 for(lastLine = m_FirstLine;
2727 lastLine && lastLine->GetLineNumber() < to.y;
2728 lastLine=lastLine->GetNextLine())
2729 ;
2730 if(!lastLine || lastLine->GetLineNumber() != to.y)
2731 return NULL;
2732
2733 if(to <= from)
2734 {
2735 wxLayoutLine *tmp = firstLine;
2736 firstLine = lastLine;
2737 lastLine = tmp;
2738 }
2739
2740 wxLayoutList *llist = new wxLayoutList();
2741
2742 if(firstLine == lastLine)
2743 {
2744 firstLine->Copy(llist, from.x, to.x);
2745 }
2746 else
2747 {
2748 // Extract objects from first line
2749 firstLine->Copy(llist, from.x);
2750 llist->LineBreak();
2751 // Extract all lines between
2752 for(wxLayoutLine *line = firstLine->GetNextLine();
2753 line != lastLine;
2754 line = line->GetNextLine())
2755 {
2756 line->Copy(llist);
2757 llist->LineBreak();
2758 }
2759 // Extract objects from last line
2760 lastLine->Copy(llist, 0, to.x);
2761 }
2762 return llist;
2763 }
2764
2765 wxLayoutList *
2766 wxLayoutList::GetSelection(wxLayoutDataObject *wxlo, bool invalidate)
2767 {
2768 if(! m_Selection.m_valid)
2769 {
2770 if(m_Selection.m_selecting)
2771 EndSelection();
2772 else
2773 return NULL;
2774 }
2775
2776 if(invalidate) m_Selection.m_valid = false;
2777
2778 wxLayoutList *llist = Copy( m_Selection.m_CursorA,
2779 m_Selection.m_CursorB );
2780
2781 if(llist && wxlo) // export as data object, too
2782 {
2783 wxString string;
2784
2785 wxLayoutExportObject *export;
2786 wxLayoutExportStatus status(llist);
2787 while((export = wxLayoutExport( &status, WXLO_EXPORT_AS_OBJECTS)) != NULL)
2788 {
2789 if(export->type == WXLO_EXPORT_EMPTYLINE)
2790 ; //FIXME missing support for linebreaks in string format
2791 else
2792 export->content.object->Write(string);
2793 delete export;
2794 }
2795
2796 wxlo->SetData(string.c_str(), string.Length()+1);
2797 }
2798 return llist;
2799 }
2800
2801
2802
2803 #define COPY_SI(what) if(si.what != -1) { m_CurrentStyleInfo.what = si.what; fontChanged = TRUE; }
2804
2805 void
2806 wxLayoutList::ApplyStyle(wxLayoutStyleInfo const &si, wxDC &dc)
2807 {
2808 bool fontChanged = FALSE;
2809 COPY_SI(family);
2810 COPY_SI(size);
2811 COPY_SI(style);
2812 COPY_SI(weight);
2813 COPY_SI(underline);
2814 if(fontChanged)
2815 dc.SetFont( m_FontCache.GetFont(m_CurrentStyleInfo) );
2816
2817 if(si.m_fg_valid)
2818 {
2819 m_CurrentStyleInfo.m_fg = si.m_fg;
2820 dc.SetTextForeground(m_CurrentStyleInfo.m_fg);
2821 }
2822 if(si.m_bg_valid)
2823 {
2824 m_CurrentStyleInfo.m_bg = si.m_bg;
2825 dc.SetTextBackground(m_CurrentStyleInfo.m_bg);
2826 }
2827 }
2828
2829
2830 #ifdef WXLAYOUT_DEBUG
2831
2832 void
2833 wxLayoutList::Debug(void)
2834 {
2835 WXLO_DEBUG(("Cursor is in line %d, screen pos = (%d, %d)",
2836 m_CursorLine->GetLineNumber(),
2837 m_CursorScreenPos.x, m_CursorScreenPos.y));
2838
2839 wxLayoutLine *line;
2840 for(line = m_FirstLine; line; line = line->GetNextLine())
2841 {
2842 line->Debug();
2843 }
2844 }
2845
2846 #endif
2847
2848
2849 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2850
2851 wxLayoutPrintout
2852
2853 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2854
2855 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList *llist,
2856 wxString const & title)
2857 :wxPrintout(title)
2858 {
2859 m_llist = llist;
2860 m_title = title;
2861 // remove any highlighting which could interfere with printing:
2862 m_llist->StartSelection();
2863 m_llist->EndSelection();
2864 }
2865
2866 wxLayoutPrintout::~wxLayoutPrintout()
2867 {
2868 }
2869
2870 float
2871 wxLayoutPrintout::ScaleDC(wxDC *dc)
2872 {
2873 // The following bit is taken from the printing sample, let's see
2874 // whether it works for us.
2875
2876 /* You might use THIS code to set the printer DC to ROUGHLY reflect
2877 * the screen text size. This page also draws lines of actual length 5cm
2878 * on the page.
2879 */
2880 // Get the logical pixels per inch of screen and printer
2881 int ppiScreenX, ppiScreenY;
2882 GetPPIScreen(&ppiScreenX, &ppiScreenY);
2883 int ppiPrinterX, ppiPrinterY;
2884 GetPPIPrinter(&ppiPrinterX, &ppiPrinterY);
2885
2886 if(ppiScreenX == 0) // not yet set, need to guess
2887 {
2888 ppiScreenX = 100;
2889 ppiScreenY = 100;
2890 }
2891 if(ppiPrinterX == 0) // not yet set, need to guess
2892 {
2893 ppiPrinterX = 72;
2894 ppiPrinterY = 72;
2895 }
2896
2897 // This scales the DC so that the printout roughly represents the
2898 // the screen scaling. The text point size _should_ be the right size
2899 // but in fact is too small for some reason. This is a detail that will
2900 // need to be addressed at some point but can be fudged for the
2901 // moment.
2902 float scale = (float)((float)ppiPrinterX/(float)ppiScreenX);
2903
2904 // Now we have to check in case our real page size is reduced
2905 // (e.g. because we're drawing to a print preview memory DC)
2906 int pageWidth, pageHeight;
2907 int w, h;
2908 dc->GetSize(&w, &h);
2909 GetPageSizePixels(&pageWidth, &pageHeight);
2910 if(pageWidth != 0) // doesn't work always
2911 {
2912 // If printer pageWidth == current DC width, then this doesn't
2913 // change. But w might be the preview bitmap width, so scale down.
2914 scale = scale * (float)(w/(float)pageWidth);
2915 }
2916 dc->SetUserScale(scale, scale);
2917 return scale;
2918 }
2919
2920 bool wxLayoutPrintout::OnPrintPage(int page)
2921 {
2922 wxDC *dc = GetDC();
2923
2924 ScaleDC(dc);
2925
2926 if (dc)
2927 {
2928 int top, bottom;
2929 top = (page - 1)*m_PrintoutHeight;
2930 bottom = top + m_PrintoutHeight;
2931
2932 WXLO_DEBUG(("OnPrintPage(%d) printing from %d to %d", page, top,
2933 bottom));
2934 // SetDeviceOrigin() doesn't work here, so we need to manually
2935 // translate all coordinates.
2936 wxPoint translate(m_Offset.x,m_Offset.y-top);
2937 m_llist->Draw(*dc, translate, top, bottom);
2938 return true;
2939 }
2940 else
2941 return false;
2942 }
2943
2944 void wxLayoutPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo)
2945 {
2946 /* We allocate a temporary wxDC for printing, so that we can
2947 determine the correct paper size and scaling. We don't actually
2948 print anything on it. */
2949 #ifdef __WXMSW__
2950 wxPrinterDC psdc("","",WXLLIST_TEMPFILE,false);
2951 #else
2952 wxPostScriptDC psdc(WXLLIST_TEMPFILE,false);
2953 #endif
2954
2955 float scale = ScaleDC(&psdc);
2956
2957 psdc.GetSize(&m_PageWidth, &m_PageHeight);
2958
2959 // This sets a left/top origin of 15% and 5%:
2960 m_Offset = wxPoint((15*m_PageWidth)/100, (5*m_PageHeight)/100);
2961
2962 // This is the length of the printable area.
2963 m_PrintoutHeight = m_PageHeight - 2*m_Offset.y;
2964 m_PrintoutHeight = (int)( m_PrintoutHeight / scale); // we want to use the real paper height
2965 #if 0
2966 // We should really use the margin settings of wxWindows somehow.
2967 m_Offset = wxPoint(0,0);
2968 // This is the length of the printable area.
2969 m_PrintoutHeight = m_PageHeight;
2970 m_PrintoutHeight = (int)( m_PrintoutHeight / scale); // we want to use the real paper height
2971 #endif
2972
2973
2974 m_NumOfPages = 1 +
2975 (int)( m_llist->GetSize().y / (float)(m_PrintoutHeight));
2976
2977 *minPage = 1;
2978 *maxPage = m_NumOfPages;
2979
2980 *selPageFrom = 1;
2981 *selPageTo = m_NumOfPages;
2982 wxRemoveFile(WXLLIST_TEMPFILE);
2983 }
2984
2985 bool wxLayoutPrintout::HasPage(int pageNum)
2986 {
2987 return pageNum <= m_NumOfPages;
2988 }
2989
2990 /*
2991 Stupid wxWindows doesn't draw proper ellipses, so we comment this
2992 out. It's a waste of paper anyway.
2993 */
2994 #if 0
2995 void
2996 wxLayoutPrintout::DrawHeader(wxDC &dc,
2997 wxPoint topleft, wxPoint bottomright,
2998 int pageno)
2999 {
3000 // make backups of all essential parameters
3001 const wxBrush& brush = dc.GetBrush();
3002 const wxPen& pen = dc.GetPen();
3003 const wxFont& font = dc.GetFont();
3004
3005 dc.SetBrush(*wxWHITE_BRUSH);
3006 dc.SetPen(wxPen(*wxBLACK,0,wxSOLID));
3007 dc.DrawRoundedRectangle(topleft.x,
3008 topleft.y,bottomright.x-topleft.x,
3009 bottomright.y-topleft.y);
3010 dc.SetBrush(*wxBLACK_BRUSH);
3011 wxFont myfont = wxFont((WXLO_DEFAULTFONTSIZE*12)/10,
3012 wxSWISS,wxNORMAL,wxBOLD,false,"Helvetica");
3013 dc.SetFont(myfont);
3014
3015 wxString page;
3016 page = "9999/9999 "; // many pages...
3017 long w,h;
3018 dc.GetTextExtent(page,&w,&h);
3019 page.Printf("%d/%d", pageno, (int) m_NumOfPages);
3020 dc.DrawText(page,bottomright.x-w,topleft.y+h/2);
3021 dc.GetTextExtent("XXXX", &w,&h);
3022 dc.DrawText(m_title, topleft.x+w,topleft.y+h/2);
3023
3024 // restore settings
3025 dc.SetPen(pen);
3026 dc.SetBrush(brush);
3027 dc.SetFont(font);
3028 }
3029 #endif
3030
3031
3032 wxFont &
3033 wxFontCache::GetFont(int family, int size, int style, int weight,
3034 bool underline)
3035 {
3036 for(wxFCEList::iterator i = m_FontList.begin();
3037 i != m_FontList.end(); i++)
3038 if( (**i).Matches(family, size, style, weight, underline) )
3039 return (**i).GetFont();
3040 // not found:
3041 wxFontCacheEntry *fce = new wxFontCacheEntry(family, size, style,
3042 weight, underline);
3043 m_FontList.push_back(fce);
3044 return fce->GetFont();
3045 }
3046