]> git.saurik.com Git - wxWidgets.git/blob - samples/richedit/wxllist.cpp
Added demo of handling EVT_GRID_SELECT_CELL.
[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 // Now, find the object in the line:
2386 wxLOiterator i = line->FindObjectScreen(dc, this,
2387 pos.x,
2388 cursorPos ? &cursorPos->x : NULL,
2389 &foundinline);
2390 if ( found )
2391 *found = didFind && foundinline;
2392
2393 return (i == NULLIT) ? NULL : *i;
2394
2395 }
2396
2397 wxPoint
2398 wxLayoutList::GetSize(void) const
2399 {
2400 wxLayoutLine
2401 *line = m_FirstLine,
2402 *last = line;
2403 if(! line)
2404 return wxPoint(0,0);
2405
2406 wxPoint maxPoint(0,0);
2407
2408 // find last line:
2409 while(line)
2410 {
2411 if(line->GetWidth() > maxPoint.x)
2412 maxPoint.x = line->GetWidth();
2413 last = line;
2414 line = line->GetNextLine();
2415 }
2416
2417 maxPoint.y = last->GetPosition().y + last->GetHeight();
2418
2419 // if the line was just added, its height would be 0 and we can't call
2420 // Layout() from here because we don't have a dc and we might be not drawing
2421 // at all, besides... So take the cursor height by default (taking 0 is bad
2422 // because then the scrollbars won't be resized and the new line won't be
2423 // shown at all)
2424 if ( last->IsDirty() )
2425 {
2426 if ( last->GetHeight() == 0 )
2427 maxPoint.y += m_CursorSize.y;
2428 if ( last->GetWidth() == 0 && maxPoint.x < m_CursorSize.x )
2429 maxPoint.x = m_CursorSize.x;
2430 }
2431
2432 return maxPoint;
2433 }
2434
2435
2436 void
2437 wxLayoutList::DrawCursor(wxDC &dc, bool active, wxPoint const &translate)
2438 {
2439 if ( m_movedCursor )
2440 m_movedCursor = false;
2441
2442 wxPoint coords(m_CursorScreenPos);
2443 coords += translate;
2444
2445 #ifdef WXLAYOUT_DEBUG
2446 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
2447 (long)m_CursorPos.x, (long)m_CursorPos.y,
2448 (long)coords.x, (long)coords.y,
2449 (long)m_CursorSize.x, (long)m_CursorSize.y,
2450 (long)m_CursorLine->GetLineNumber(),
2451 (long)m_CursorLine->GetLength()));
2452
2453 wxLogStatus("Cursor is at (%d, %d)", m_CursorPos.x, m_CursorPos.y);
2454 #endif
2455
2456 #ifdef WXLAYOUT_USE_CARET
2457 m_caret->Move(coords);
2458 #else // !WXLAYOUT_USE_CARET
2459 dc.SetBrush(*wxWHITE_BRUSH);
2460 //FIXME: wxGTK XOR is borken at the moment!!!dc.SetLogicalFunction(wxXOR);
2461 dc.SetPen(wxPen(*wxBLACK,1,wxSOLID));
2462 if(active)
2463 {
2464 dc.SetLogicalFunction(wxXOR);
2465 dc.DrawRectangle(coords.x, coords.y,
2466 m_CursorSize.x, m_CursorSize.y);
2467 SetUpdateRect(coords.x, coords.y);
2468 SetUpdateRect(coords.x+m_CursorSize.x, coords.y+m_CursorSize.y);
2469 }
2470 else
2471 {
2472 dc.SetLogicalFunction(wxCOPY);
2473 dc.DrawLine(coords.x, coords.y+m_CursorSize.y-1,
2474 coords.x, coords.y);
2475 SetUpdateRect(coords.x, coords.y+m_CursorSize.y-1);
2476 SetUpdateRect(coords.x, coords.y);
2477 }
2478 dc.SetLogicalFunction(wxCOPY);
2479 //dc.SetBrush(wxNullBrush);
2480 #endif // WXLAYOUT_USE_CARET/!WXLAYOUT_USE_CARET
2481 }
2482
2483 void
2484 wxLayoutList::SetUpdateRect(CoordType x, CoordType y)
2485 {
2486 if(m_UpdateRectValid)
2487 GrowRect(m_UpdateRect, x, y);
2488 else
2489 {
2490 m_UpdateRect.x = x;
2491 m_UpdateRect.y = y;
2492 m_UpdateRect.width = 4; // large enough to avoid surprises from
2493 m_UpdateRect.height = 4;// wxGTK :-)
2494 m_UpdateRectValid = true;
2495 }
2496 }
2497
2498 void
2499 wxLayoutList::StartSelection(const wxPoint& cposOrig, const wxPoint& spos)
2500 {
2501 wxPoint cpos(cposOrig);
2502 if ( cpos.x == -1 )
2503 cpos = m_CursorPos;
2504 WXLO_DEBUG(("Starting selection at %ld/%ld", cpos.x, cpos.y));
2505 m_Selection.m_CursorA = cpos;
2506 m_Selection.m_CursorB = cpos;
2507 m_Selection.m_ScreenA = spos;
2508 m_Selection.m_ScreenB = spos;
2509 m_Selection.m_selecting = true;
2510 m_Selection.m_valid = false;
2511 }
2512
2513 void
2514 wxLayoutList::ContinueSelection(const wxPoint& cposOrig, const wxPoint& spos)
2515 {
2516 wxPoint cpos(cposOrig);
2517 if(cpos.x == -1)
2518 cpos = m_CursorPos;
2519
2520 wxASSERT(m_Selection.m_selecting == true);
2521 wxASSERT(m_Selection.m_valid == false);
2522 WXLO_DEBUG(("Continuing selection at %ld/%ld", cpos.x, cpos.y));
2523
2524 m_Selection.m_ScreenB = spos;
2525 m_Selection.m_CursorB = cpos;}
2526
2527 void
2528 wxLayoutList::EndSelection(const wxPoint& cposOrig, const wxPoint& spos)
2529 {
2530 wxPoint cpos(cposOrig);
2531 if(cpos.x == -1)
2532 cpos = m_CursorPos;
2533 ContinueSelection(cpos);
2534 WXLO_DEBUG(("Ending selection at %ld/%ld", cpos.x, cpos.y));
2535 // we always want m_CursorA <= m_CursorB!
2536 if( m_Selection.m_CursorA > m_Selection.m_CursorB )
2537 {
2538 // exchange the start/end points
2539 wxPoint help = m_Selection.m_CursorB;
2540 m_Selection.m_CursorB = m_Selection.m_CursorA;
2541 m_Selection.m_CursorA = help;
2542
2543 help = m_Selection.m_ScreenB;
2544 m_Selection.m_ScreenB = m_Selection.m_ScreenA;
2545 m_Selection.m_ScreenA = help;
2546 }
2547 m_Selection.m_selecting = false;
2548 m_Selection.m_valid = true;
2549 /// In case we just clicked somewhere, the selection will have zero
2550 /// size, so we discard it immediately.
2551 if(m_Selection.m_CursorA == m_Selection.m_CursorB)
2552 DiscardSelection();
2553 }
2554
2555 void
2556 wxLayoutList::DiscardSelection()
2557 {
2558 if ( !HasSelection() )
2559 return;
2560
2561 m_Selection.m_valid =
2562 m_Selection.m_selecting = false;
2563 m_Selection.m_discarded = true;
2564 }
2565
2566 bool
2567 wxLayoutList::IsSelecting(void) const
2568 {
2569 return m_Selection.m_selecting;
2570 }
2571
2572 bool
2573 wxLayoutList::IsSelected(const wxPoint &cursor) const
2574 {
2575 if ( !HasSelection() )
2576 return false;
2577
2578 return (
2579 (m_Selection.m_CursorA <= cursor
2580 && cursor <= m_Selection.m_CursorB)
2581 || (m_Selection.m_CursorB <= cursor
2582 && cursor <= m_Selection.m_CursorA)
2583 );
2584 }
2585
2586
2587 /** Tests whether this layout line is selected and needs
2588 highlighting.
2589 @param line to test for
2590 @return 0 = not selected, 1 = fully selected, -1 = partially
2591 selected
2592 */
2593 int
2594 wxLayoutList::IsSelected(const wxLayoutLine *line, CoordType *from,
2595 CoordType *to)
2596 {
2597 wxASSERT(line); wxASSERT(to); wxASSERT(from);
2598
2599 if(! m_Selection.m_valid && ! m_Selection.m_selecting)
2600 return 0;
2601
2602 CoordType y = line->GetLineNumber();
2603 if(
2604 (m_Selection.m_CursorA.y < y && m_Selection.m_CursorB.y > y)
2605 || (m_Selection.m_CursorB.y < y && m_Selection.m_CursorA.y > y)
2606 )
2607 return 1;
2608 else if(m_Selection.m_CursorA.y == y)
2609 {
2610 *from = m_Selection.m_CursorA.x;
2611 if(m_Selection.m_CursorB.y == y)
2612 *to = m_Selection.m_CursorB.x;
2613 else
2614 {
2615 if(m_Selection.m_CursorB > m_Selection.m_CursorA)
2616 *to = line->GetLength();
2617 else
2618 *to = 0;
2619 }
2620 if(*to < *from)
2621 {
2622 CoordType help = *to;
2623 *to = *from;
2624 *from = help;
2625 }
2626 return -1;
2627 }
2628 else if(m_Selection.m_CursorB.y == y)
2629 {
2630 *to = m_Selection.m_CursorB.x;
2631 if(m_Selection.m_CursorA.y == y)
2632 *from = m_Selection.m_CursorA.x;
2633 else
2634 {
2635 if(m_Selection.m_CursorB > m_Selection.m_CursorA)
2636 *from = 0;
2637 else
2638 *from = line->GetLength();
2639 }
2640 if(*to < *from)
2641 {
2642 CoordType help = *to;
2643 *to = *from;
2644 *from = help;
2645 }
2646 return -1;
2647 }
2648 else
2649 return 0;
2650 }
2651
2652 void
2653 wxLayoutList::DeleteSelection(void)
2654 {
2655 if(! m_Selection.m_valid)
2656 return;
2657
2658 m_Selection.m_valid = false;
2659
2660 // Only delete part of the current line?
2661 if(m_Selection.m_CursorA.y == m_Selection.m_CursorB.y)
2662 {
2663 MoveCursorTo(m_Selection.m_CursorA);
2664 Delete(m_Selection.m_CursorB.x - m_Selection.m_CursorA.x);
2665 return;
2666 }
2667
2668 // We now know that the two lines are different:
2669
2670 wxLayoutLine
2671 * firstLine = GetLine(m_Selection.m_CursorA.y),
2672 * lastLine = GetLine(m_Selection.m_CursorB.y);
2673 // be a bit paranoid:
2674 if(! firstLine || ! lastLine)
2675 return;
2676
2677 // First, delete what's left of this line:
2678 MoveCursorTo(m_Selection.m_CursorA);
2679 DeleteToEndOfLine();
2680
2681 wxLayoutLine *prevLine = firstLine->GetPreviousLine(),
2682 *nextLine = firstLine->GetNextLine();
2683 while(nextLine && nextLine != lastLine)
2684 nextLine = nextLine->DeleteLine(false, this);
2685
2686 // Now nextLine = lastLine;
2687 Delete(1); // This joins firstLine and nextLine
2688 Delete(m_Selection.m_CursorB.x); // This deletes the first x positions
2689
2690 // Recalculate the line positions and numbers but notice that firstLine
2691 // might not exist any more - it could be deleted by Delete(1) above
2692 wxLayoutLine *firstLine2 = prevLine ? prevLine->GetNextLine() : m_FirstLine;
2693 firstLine2->MarkDirty();
2694 }
2695
2696 /// Starts highlighting the selection
2697 void
2698 wxLayoutList::StartHighlighting(wxDC &dc)
2699 {
2700 #if SHOW_SELECTIONS
2701 dc.SetTextForeground(m_CurrentStyleInfo.m_bg);
2702 dc.SetTextBackground(m_CurrentStyleInfo.m_fg);
2703 dc.SetBackgroundMode(wxSOLID);
2704 #endif
2705 }
2706
2707 /// Ends highlighting the selection
2708 void
2709 wxLayoutList::EndHighlighting(wxDC &dc)
2710 {
2711 #if SHOW_SELECTIONS
2712 dc.SetTextForeground(m_CurrentStyleInfo.m_fg);
2713 dc.SetTextBackground(m_CurrentStyleInfo.m_bg);
2714 dc.SetBackgroundMode(wxTRANSPARENT);
2715 #endif
2716 }
2717
2718
2719 wxLayoutLine *
2720 wxLayoutList::GetLine(CoordType index) const
2721 {
2722 wxASSERT_MSG( (0 <= index) && (index < (CoordType)m_numLines),
2723 "invalid index" );
2724
2725 wxLayoutLine *line;
2726 CoordType n = index;
2727 #ifdef DEBUG
2728 CoordType lineNo = 0;
2729 #endif
2730
2731 for ( line = m_FirstLine; line && n-- > 0; line =
2732 line->GetNextLine() )
2733 {
2734 #ifdef DEBUG
2735 wxASSERT(line->GetLineNumber() == lineNo );
2736 lineNo++;
2737 #endif
2738 }
2739
2740 if ( line )
2741 {
2742 // should be the right one
2743 wxASSERT( line->GetLineNumber() == index );
2744 }
2745
2746 return line;
2747 }
2748
2749
2750 wxLayoutList *
2751 wxLayoutList::Copy(const wxPoint &from,
2752 const wxPoint &to)
2753 {
2754 wxLayoutLine
2755 * firstLine = NULL,
2756 * lastLine = NULL;
2757
2758 for(firstLine = m_FirstLine;
2759 firstLine && firstLine->GetLineNumber() < from.y;
2760 firstLine=firstLine->GetNextLine())
2761 ;
2762 if(!firstLine || firstLine->GetLineNumber() != from.y)
2763 return NULL;
2764
2765 for(lastLine = m_FirstLine;
2766 lastLine && lastLine->GetLineNumber() < to.y;
2767 lastLine=lastLine->GetNextLine())
2768 ;
2769 if(!lastLine || lastLine->GetLineNumber() != to.y)
2770 return NULL;
2771
2772 if(to <= from)
2773 {
2774 wxLayoutLine *tmp = firstLine;
2775 firstLine = lastLine;
2776 lastLine = tmp;
2777 }
2778
2779 wxLayoutList *llist = new wxLayoutList();
2780
2781 if(firstLine == lastLine)
2782 {
2783 firstLine->Copy(llist, from.x, to.x);
2784 }
2785 else
2786 {
2787 // Extract objects from first line
2788 firstLine->Copy(llist, from.x);
2789 llist->LineBreak();
2790 // Extract all lines between
2791 for(wxLayoutLine *line = firstLine->GetNextLine();
2792 line != lastLine;
2793 line = line->GetNextLine())
2794 {
2795 line->Copy(llist);
2796 llist->LineBreak();
2797 }
2798 // Extract objects from last line
2799 lastLine->Copy(llist, 0, to.x);
2800 }
2801 return llist;
2802 }
2803
2804 wxLayoutList *
2805 wxLayoutList::GetSelection(wxLayoutDataObject *wxlo, bool invalidate)
2806 {
2807 if(! m_Selection.m_valid)
2808 {
2809 if(m_Selection.m_selecting)
2810 EndSelection();
2811 else
2812 return NULL;
2813 }
2814
2815 if(invalidate) m_Selection.m_valid = false;
2816
2817 wxLayoutList *llist = Copy( m_Selection.m_CursorA,
2818 m_Selection.m_CursorB );
2819
2820 if(llist && wxlo) // export as data object, too
2821 {
2822 wxString string;
2823
2824 wxLayoutExportObject *exp;
2825 wxLayoutExportStatus status(llist);
2826 while((exp = wxLayoutExport( &status, WXLO_EXPORT_AS_OBJECTS)) != NULL)
2827 {
2828 if(exp->type == WXLO_EXPORT_EMPTYLINE)
2829 ; //FIXME missing support for linebreaks in string format
2830 else
2831 exp->content.object->Write(string);
2832 delete exp;
2833 }
2834
2835 wxlo->SetData(string.c_str(), string.Length()+1);
2836 }
2837 return llist;
2838 }
2839
2840
2841
2842 #define COPY_SI(what) if(si.what != -1) { m_CurrentStyleInfo.what = si.what; fontChanged = TRUE; }
2843
2844 void
2845 wxLayoutList::ApplyStyle(wxLayoutStyleInfo const &si, wxDC &dc)
2846 {
2847 bool fontChanged = FALSE;
2848 COPY_SI(family);
2849 COPY_SI(size);
2850 COPY_SI(style);
2851 COPY_SI(weight);
2852 COPY_SI(underline);
2853 if(fontChanged)
2854 dc.SetFont( m_FontCache.GetFont(m_CurrentStyleInfo) );
2855
2856 if(si.m_fg_valid)
2857 {
2858 m_CurrentStyleInfo.m_fg = si.m_fg;
2859 dc.SetTextForeground(m_CurrentStyleInfo.m_fg);
2860 }
2861 if(si.m_bg_valid)
2862 {
2863 m_CurrentStyleInfo.m_bg = si.m_bg;
2864 dc.SetTextBackground(m_CurrentStyleInfo.m_bg);
2865 }
2866 }
2867
2868
2869 #ifdef WXLAYOUT_DEBUG
2870
2871 void
2872 wxLayoutList::Debug(void)
2873 {
2874 WXLO_DEBUG(("Cursor is in line %d, screen pos = (%d, %d)",
2875 m_CursorLine->GetLineNumber(),
2876 m_CursorScreenPos.x, m_CursorScreenPos.y));
2877
2878 wxLayoutLine *line;
2879 for(line = m_FirstLine; line; line = line->GetNextLine())
2880 {
2881 line->Debug();
2882 }
2883 }
2884
2885 #endif
2886
2887
2888 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2889
2890 wxLayoutPrintout
2891
2892 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2893
2894 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList *llist,
2895 wxString const & title)
2896 :wxPrintout(title)
2897 {
2898 m_llist = llist;
2899 m_title = title;
2900 // remove any highlighting which could interfere with printing:
2901 m_llist->StartSelection();
2902 m_llist->EndSelection();
2903 // force a full layout of the list:
2904 m_llist->ForceTotalLayout();
2905 // layout is called in ScaleDC() when we have a DC
2906 }
2907
2908 wxLayoutPrintout::~wxLayoutPrintout()
2909 {
2910 }
2911
2912 float
2913 wxLayoutPrintout::ScaleDC(wxDC *dc)
2914 {
2915 // The following bit is taken from the printing sample, let's see
2916 // whether it works for us.
2917
2918 /* You might use THIS code to set the printer DC to ROUGHLY reflect
2919 * the screen text size. This page also draws lines of actual length 5cm
2920 * on the page.
2921 */
2922 // Get the logical pixels per inch of screen and printer
2923 int ppiScreenX, ppiScreenY;
2924 GetPPIScreen(&ppiScreenX, &ppiScreenY);
2925 int ppiPrinterX, ppiPrinterY;
2926 GetPPIPrinter(&ppiPrinterX, &ppiPrinterY);
2927
2928 if(ppiScreenX == 0) // not yet set, need to guess
2929 {
2930 ppiScreenX = 100;
2931 ppiScreenY = 100;
2932 }
2933 if(ppiPrinterX == 0) // not yet set, need to guess
2934 {
2935 ppiPrinterX = 72;
2936 ppiPrinterY = 72;
2937 }
2938
2939 // This scales the DC so that the printout roughly represents the
2940 // the screen scaling. The text point size _should_ be the right size
2941 // but in fact is too small for some reason. This is a detail that will
2942 // need to be addressed at some point but can be fudged for the
2943 // moment.
2944 float scale = (float)((float)ppiPrinterX/(float)ppiScreenX);
2945
2946 // Now we have to check in case our real page size is reduced
2947 // (e.g. because we're drawing to a print preview memory DC)
2948 int pageWidth, pageHeight;
2949 int w, h;
2950 dc->GetSize(&w, &h);
2951 GetPageSizePixels(&pageWidth, &pageHeight);
2952 if(pageWidth != 0) // doesn't work always
2953 {
2954 // If printer pageWidth == current DC width, then this doesn't
2955 // change. But w might be the preview bitmap width, so scale down.
2956 scale = scale * (float)(w/(float)pageWidth);
2957 }
2958 dc->SetUserScale(scale, scale);
2959 return scale;
2960 }
2961
2962 bool wxLayoutPrintout::OnPrintPage(int page)
2963 {
2964 wxDC *dc = GetDC();
2965
2966 ScaleDC(dc);
2967
2968 if (dc)
2969 {
2970 int top, bottom;
2971 top = (page - 1)*m_PrintoutHeight;
2972 bottom = top + m_PrintoutHeight;
2973
2974 WXLO_DEBUG(("OnPrintPage(%d) printing from %d to %d", page, top,
2975 bottom));
2976 // SetDeviceOrigin() doesn't work here, so we need to manually
2977 // translate all coordinates.
2978 wxPoint translate(m_Offset.x,m_Offset.y-top);
2979 m_llist->Draw(*dc, translate, top, bottom, TRUE /* clip strictly
2980 */);
2981 return true;
2982 }
2983 else
2984 return false;
2985 }
2986
2987 void wxLayoutPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo)
2988 {
2989 /* We allocate a temporary wxDC for printing, so that we can
2990 determine the correct paper size and scaling. We don't actually
2991 print anything on it. */
2992 #ifdef __WXMSW__
2993 wxPrinterDC *psdc = new wxPrinterDC("","",WXLLIST_TEMPFILE,false);
2994 #else
2995 wxPostScriptDC *psdc = new wxPostScriptDC(WXLLIST_TEMPFILE,false);
2996 #endif
2997
2998 psdc->StartDoc(m_title);
2999 // before we draw anything, me must make sure the list is properly
3000 // laid out
3001 m_llist->Layout(*psdc);
3002
3003 float scale = ScaleDC(psdc);
3004
3005 psdc->GetSize(&m_PageWidth, &m_PageHeight);
3006
3007 // This sets a left/top origin of 15% and 5%:
3008 m_Offset = wxPoint((15*m_PageWidth)/100, (5*m_PageHeight)/100);
3009
3010 // This is the length of the printable area.
3011 m_PrintoutHeight = m_PageHeight - 2*m_Offset.y;
3012 m_PrintoutHeight = (int)( m_PrintoutHeight / scale); // we want to use the real paper height
3013
3014 m_NumOfPages = 1 +
3015 (int)( m_llist->GetSize().y / (float)(m_PrintoutHeight));
3016
3017 *minPage = 1;
3018 *maxPage = m_NumOfPages;
3019
3020 *selPageFrom = 1;
3021 *selPageTo = m_NumOfPages;
3022 psdc->EndDoc();
3023 delete psdc;
3024 wxRemoveFile(WXLLIST_TEMPFILE);
3025 }
3026
3027 bool wxLayoutPrintout::HasPage(int pageNum)
3028 {
3029 return pageNum <= m_NumOfPages;
3030 }
3031
3032 /*
3033 Stupid wxWindows doesn't draw proper ellipses, so we comment this
3034 out. It's a waste of paper anyway.
3035 */
3036 #if 0
3037 void
3038 wxLayoutPrintout::DrawHeader(wxDC &dc,
3039 wxPoint topleft, wxPoint bottomright,
3040 int pageno)
3041 {
3042 // make backups of all essential parameters
3043 const wxBrush& brush = dc.GetBrush();
3044 const wxPen& pen = dc.GetPen();
3045 const wxFont& font = dc.GetFont();
3046
3047 dc.SetBrush(*wxWHITE_BRUSH);
3048 dc.SetPen(wxPen(*wxBLACK,0,wxSOLID));
3049 dc.DrawRoundedRectangle(topleft.x,
3050 topleft.y,bottomright.x-topleft.x,
3051 bottomright.y-topleft.y);
3052 dc.SetBrush(*wxBLACK_BRUSH);
3053 wxFont myfont = wxFont((WXLO_DEFAULTFONTSIZE*12)/10,
3054 wxSWISS,wxNORMAL,wxBOLD,false,"Helvetica");
3055 dc.SetFont(myfont);
3056
3057 wxString page;
3058 page = "9999/9999 "; // many pages...
3059 long w,h;
3060 dc.GetTextExtent(page,&w,&h);
3061 page.Printf("%d/%d", pageno, (int) m_NumOfPages);
3062 dc.DrawText(page,bottomright.x-w,topleft.y+h/2);
3063 dc.GetTextExtent("XXXX", &w,&h);
3064 dc.DrawText(m_title, topleft.x+w,topleft.y+h/2);
3065
3066 // restore settings
3067 dc.SetPen(pen);
3068 dc.SetBrush(brush);
3069 dc.SetFont(font);
3070 }
3071 #endif
3072
3073
3074 wxFont &
3075 wxFontCache::GetFont(int family, int size, int style, int weight,
3076 bool underline)
3077 {
3078 for(wxFCEList::iterator i = m_FontList.begin();
3079 i != m_FontList.end(); i++)
3080 if( (**i).Matches(family, size, style, weight, underline) )
3081 return (**i).GetFont();
3082 // not found:
3083 wxFontCacheEntry *fce = new wxFontCacheEntry(family, size, style,
3084 weight, underline);
3085 m_FontList.push_back(fce);
3086 return fce->GetFont();
3087 }
3088