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