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