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