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