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