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