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