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