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