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