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