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