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