]> git.saurik.com Git - wxWidgets.git/blob - samples/richedit/wxllist.cpp
600b7d60ffb7e17eb816656f3cbbe55af9a8a222
[wxWidgets.git] / samples / richedit / wxllist.cpp
1 /*-*- c++ -*-********************************************************
2 * wxllist: wxLayoutList, a layout engine for text and graphics *
3 * *
4 * (C) 1998-1999 by Karsten Ballüder (Ballueder@usa.net) *
5 * *
6 * $Id$
7 *******************************************************************/
8
9 /*
10
11 Some docs:
12
13 Layout() recalculates the objects, sizes, etc.
14 Draw() just draws them with the current settings, without
15 re-layout()ing them again
16
17 Each line has its own wxLayoutStyleInfo structure which gets updated
18 from within Layout(). Thanks to this, we don't need to re-layout all
19 lines if we want to draw one, but can just use its styleinfo to set
20 the right font.
21
22 */
23
24 #ifdef __GNUG__
25 # pragma implementation "wxllist.h"
26 #endif
27
28 #include <wx/wxprec.h>
29
30 #ifdef __BORLANDC__
31 # pragma hdrstop
32 #endif
33
34 #include "Mpch.h"
35
36 #ifdef M_BASEDIR
37 # include "gui/wxllist.h"
38 # include "gui/wxlparser.h"
39 # define SHOW_SELECTIONS 1
40 #else
41 # include "wxllist.h"
42 # include "wxlparser.h"
43 # define SHOW_SELECTIONS 1
44 #endif
45
46 #ifndef USE_PCH
47 # include <iostream.h>
48
49 # include <wx/dc.h>
50 # include <wx/dcps.h>
51 # include <wx/print.h>
52 # include <wx/log.h>
53 # include <wx/filefn.h>
54 #endif
55
56 #ifdef WXLAYOUT_USE_CARET
57 # include <wx/caret.h>
58 #endif // WXLAYOUT_USE_CARET
59
60 #include <ctype.h>
61
62 /// This should never really get created
63 #define WXLLIST_TEMPFILE "__wxllist.tmp"
64
65 #ifdef WXLAYOUT_DEBUG
66
67 # define TypeString(t) g_aTypeStrings[t]
68 # define WXLO_DEBUG(x) wxLogDebug x
69
70 static const char *g_aTypeStrings[] =
71 {
72 "invalid", "text", "cmd", "icon"
73 };
74 void
75 wxLayoutObject::Debug(void)
76 {
77 WXLO_DEBUG(("%s",g_aTypeStrings[GetType()]));
78 }
79 #else
80 # define TypeString(t) ""
81 # define WXLO_DEBUG(x)
82 #endif
83
84 // FIXME under MSW, this constant is needed to make the thing properly redraw
85 // itself - I don't know where the size calculation error is and I can't
86 // waste time looking for it right now. Search for occurences of
87 // MSW_CORRECTION to find all the places where I did it.
88 #ifdef __WXMSW__
89 static const int MSW_CORRECTION = 10;
90 #else
91 static const int MSW_CORRECTION = 0;
92 #endif
93
94 /// Cursors smaller than this disappear in XOR drawing mode
95 #define WXLO_MINIMUM_CURSOR_WIDTH 4
96
97 /// Use this character to estimate a cursor size when none is available.
98 #define WXLO_CURSORCHAR "E"
99 /** @name Helper functions */
100 //@{
101 /// allows me to compare to wxPoints
102 bool operator <=(wxPoint const &p1, wxPoint const &p2)
103 {
104 return p1.y < p2.y || (p1.y == p2.y && p1.x <= p2.x);
105 }
106
107 /*
108 The following STAY HERE until we have a working wxGTK again!!!
109 */
110 #ifndef wxWANTS_CHARS
111 /// allows me to compare to wxPoints
112 bool operator ==(wxPoint const &p1, wxPoint const &p2)
113 {
114 return p1.x == p2.x && p1.y == p2.y;
115 }
116
117 /// allows me to compare to wxPoints
118 bool operator !=(wxPoint const &p1, wxPoint const &p2)
119 {
120 return p1.x != p2.x || p1.y != p2.y;
121 }
122
123 wxPoint & operator += (wxPoint &p1, wxPoint const &p2)
124 {
125 p1.x += p2.x;
126 p1.y += p2.y;
127 return p1;
128 }
129 #endif // old wxGTK
130
131 /// allows me to compare to wxPoints
132 bool operator>(wxPoint const &p1, wxPoint const &p2)
133 {
134 return !(p1 <= p2);
135 }
136
137 /// grows a wxRect so that it includes the given point
138
139 static
140 void GrowRect(wxRect &r, CoordType x, CoordType y)
141 {
142 if(r.x > x)
143 r.x = x;
144 else if(r.x + r.width < x)
145 r.width = x - r.x;
146
147 if(r.y > y)
148 r.y = y;
149 else if(r.y + r.height < y)
150 r.height = y - r.y;
151 }
152
153 #if 0
154 // unused
155 /// returns true if the point is in the rectangle
156 static
157 bool Contains(const wxRect &r, const wxPoint &p)
158 {
159 return r.x <= p.x && r.y <= p.y && (r.x+r.width) >= p.x && (r.y + r.height) >= p.y;
160 }
161 #endif
162
163
164 //@}
165
166
167 void ReadString(wxString &to, wxString &from)
168 {
169 to = "";
170 const char *cptr = from.c_str();
171 while(*cptr && *cptr != '\n')
172 to += *cptr++;
173 if(*cptr) cptr++;
174 from = cptr;
175 }
176
177 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
178
179 wxLayoutObject
180
181 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
182
183 /* static */
184 wxLayoutObject *
185 wxLayoutObject::Read(wxString &istr)
186 {
187 wxString tmp;
188 ReadString(tmp, istr);
189 int type = -1;
190 sscanf(tmp.c_str(),"%d", &type);
191
192 switch(type)
193 {
194 case WXLO_TYPE_TEXT:
195 return wxLayoutObjectText::Read(istr);
196 case WXLO_TYPE_CMD:
197 return wxLayoutObjectCmd::Read(istr);
198 case WXLO_TYPE_ICON:
199 return wxLayoutObjectIcon::Read(istr);
200 default:
201 return NULL;
202 }
203 }
204
205 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
206
207 wxLayoutObjectText
208
209 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
210
211 wxLayoutObjectText::wxLayoutObjectText(const wxString &txt)
212 {
213 m_Text = txt;
214 m_Width = 0;
215 m_Height = 0;
216 m_Top = 0;
217 m_Bottom = 0;
218 }
219
220 wxLayoutObject *
221 wxLayoutObjectText::Copy(void)
222 {
223 wxLayoutObjectText *obj = new wxLayoutObjectText(m_Text);
224 obj->m_Width = m_Width;
225 obj->m_Height = m_Height;
226 obj->m_Top = m_Top;
227 obj->m_Bottom = m_Bottom;
228 obj->SetUserData(m_UserData);
229 return obj;
230 }
231
232
233 void
234 wxLayoutObjectText::Write(wxString &ostr)
235 {
236 ostr << (int) WXLO_TYPE_TEXT << '\n'
237 << m_Text << '\n';
238 }
239 /* static */
240 wxLayoutObjectText *
241 wxLayoutObjectText::Read(wxString &istr)
242 {
243 wxString text;
244 ReadString(text, istr);
245
246 return new wxLayoutObjectText(text);
247 }
248
249 wxPoint
250 wxLayoutObjectText::GetSize(CoordType *top, CoordType *bottom) const
251 {
252
253 *top = m_Top; *bottom = m_Bottom;
254 return wxPoint(m_Width, m_Height);
255 }
256
257 void
258 wxLayoutObjectText::Draw(wxDC &dc, wxPoint const &coords,
259 wxLayoutList *wxllist,
260 CoordType begin, CoordType end)
261 {
262 if( end <= 0 )
263 {
264 // draw the whole object normally
265 dc.DrawText(m_Text, coords.x, coords.y-m_Top);
266 }
267 else
268 {
269 // highlight the bit between begin and len
270 CoordType
271 xpos = coords.x,
272 ypos = coords.y-m_Top;
273 long width, height, descent;
274
275 if(begin < 0) begin = 0;
276 if( end > (signed)m_Text.Length() )
277 end = m_Text.Length();
278
279 wxString str = m_Text.Mid(0, begin);
280 dc.DrawText(str, xpos, ypos);
281 dc.GetTextExtent(str, &width, &height, &descent);
282 xpos += width;
283 wxllist->StartHighlighting(dc);
284 str = m_Text.Mid(begin, end-begin);
285 dc.DrawText(str, xpos, ypos);
286 dc.GetTextExtent(str, &width, &height, &descent);
287 xpos += width;
288 wxllist->EndHighlighting(dc);
289 str = m_Text.Mid(end, m_Text.Length()-end);
290 dc.DrawText(str, xpos, ypos);
291 }
292 }
293
294 CoordType
295 wxLayoutObjectText::GetOffsetScreen(wxDC &dc, CoordType xpos) const
296 {
297 CoordType
298 offs = 1,
299 maxlen = m_Text.Length();
300 long
301 width = 0,
302 height, descent = 0l;
303
304 if(xpos == 0) return 0; // easy
305
306 while(width < xpos && offs < maxlen)
307 {
308 dc.GetTextExtent(m_Text.substr(0,offs),
309 &width, &height, &descent);
310 offs++;
311 }
312 /* We have to substract 1 to compensate for the offs++, and another
313 one because we don't want to position the cursor behind the
314 object what we clicked on, but before - otherwise it looks
315 funny. */
316 return (xpos > 2) ? offs-2 : 0;
317 }
318
319 void
320 wxLayoutObjectText::Layout(wxDC &dc, class wxLayoutList *llist)
321 {
322 long descent = 0l;
323
324 // now this is done in wxLayoutLine::Layout(), but this code might be
325 // reenabled later - in principle, it's more efficient
326 #if 0
327 CoordType widthOld = m_Width,
328 heightOld = m_Height;
329 #endif // 0
330
331 dc.GetTextExtent(m_Text, &m_Width, &m_Height, &descent);
332
333 #if 0
334 if ( widthOld != m_Width || heightOld != m_Height )
335 {
336 // as the text length changed, it must be refreshed
337 wxLayoutLine *line = GetLine();
338
339 wxCHECK_RET( line, "wxLayoutObjectText can't refresh itself" );
340
341 // as our size changed, we need to repaint the part which was appended
342 wxPoint position(line->GetPosition());
343
344 // this is not the most efficient way (we repaint the whole line), but
345 // it's not too slow and is *simple*
346 if ( widthOld < m_Width )
347 widthOld = m_Width;
348 if ( heightOld < m_Height )
349 heightOld = m_Height;
350
351 llist->SetUpdateRect(position.x + widthOld + MSW_CORRECTION,
352 position.y + heightOld + MSW_CORRECTION);
353 }
354 #endif // 0
355
356 m_Bottom = descent;
357 m_Top = m_Height - m_Bottom;
358 }
359
360
361 #ifdef WXLAYOUT_DEBUG
362 void
363 wxLayoutObjectText::Debug(void)
364 {
365 wxLayoutObject::Debug();
366 WXLO_DEBUG((" `%s`", m_Text.c_str()));
367 }
368 #endif
369
370 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
371
372 wxLayoutObjectIcon
373
374 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
375
376 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap const &icon)
377 {
378 m_Icon = new wxBitmap(icon);
379 }
380
381
382 void
383 wxLayoutObjectIcon::Write(wxString &ostr)
384 {
385 /* Exports icon through a temporary file. */
386
387 wxString file = wxGetTempFileName("wxloexport");
388
389 ostr << WXLO_TYPE_ICON << '\n'
390 << file << '\n';
391 m_Icon->SaveFile(file, WXLO_BITMAP_FORMAT);
392 }
393 /* static */
394 wxLayoutObjectIcon *
395 wxLayoutObjectIcon::Read(wxString &istr)
396 {
397 wxString file;
398 ReadString(file, istr);
399
400 if(! wxFileExists(file))
401 return NULL;
402 wxLayoutObjectIcon *obj = new wxLayoutObjectIcon;
403
404 if(!obj->m_Icon->LoadFile(file, WXLO_BITMAP_FORMAT))
405 {
406 delete obj;
407 return NULL;
408 }
409 else
410 return obj;
411 }
412
413 wxLayoutObject *
414 wxLayoutObjectIcon::Copy(void)
415 {
416 wxLayoutObjectIcon *obj = new wxLayoutObjectIcon(new
417 wxBitmap(*m_Icon));
418 obj->SetUserData(m_UserData);
419 return obj;
420 }
421
422 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap *icon)
423 {
424 m_Icon = icon;
425 }
426
427 void
428 wxLayoutObjectIcon::Draw(wxDC &dc, wxPoint const &coords,
429 wxLayoutList *wxllist,
430 CoordType begin, CoordType /* len */)
431 {
432 dc.DrawBitmap(*m_Icon, coords.x, coords.y-m_Icon->GetHeight(),
433 (m_Icon->GetMask() == NULL) ? FALSE : TRUE);
434 }
435
436 void
437 wxLayoutObjectIcon::Layout(wxDC & /* dc */, class wxLayoutList * )
438 {
439 }
440
441 wxPoint
442 wxLayoutObjectIcon::GetSize(CoordType *top, CoordType *bottom) const
443 {
444 *top = m_Icon->GetHeight();
445 *bottom = 0;
446 return wxPoint(m_Icon->GetWidth(), m_Icon->GetHeight());
447 }
448
449
450
451 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
452
453 wxLayoutObjectCmd
454
455 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
456
457
458 wxLayoutStyleInfo::wxLayoutStyleInfo(int ifamily,
459 int isize,
460 int istyle,
461 int iweight,
462 int iul,
463 wxColour *fg,
464 wxColour *bg)
465 {
466 family = ifamily;
467 size = isize;
468 style = istyle;
469 weight = iweight;
470 underline = iul != 0;
471
472 m_fg_valid = fg != 0;
473 m_bg_valid = bg != 0;
474 m_fg = m_fg_valid ? *fg : *wxBLACK;
475 m_bg = m_fg_valid ? *bg : *wxWHITE;
476 }
477
478 #define COPY_SI_(what) if(right.what != -1) what = right.what;
479
480 wxLayoutStyleInfo &
481 wxLayoutStyleInfo::operator=(const wxLayoutStyleInfo &right)
482 {
483 COPY_SI_(family);
484 COPY_SI_(style);
485 COPY_SI_(size);
486 COPY_SI_(weight);
487 COPY_SI_(underline);
488 if(right.m_fg_valid) m_fg = right.m_fg;
489 if(right.m_bg_valid) m_bg = right.m_bg;
490 return *this;
491 }
492
493 wxLayoutObjectCmd::wxLayoutObjectCmd(int family, int size, int style, int
494 weight, int underline,
495 wxColour *fg, wxColour *bg)
496
497 {
498 m_StyleInfo = new wxLayoutStyleInfo(family, size,style,weight,underline,fg,bg);
499 }
500
501 wxLayoutObject *
502 wxLayoutObjectCmd::Copy(void)
503 {
504 wxLayoutObjectCmd *obj = new wxLayoutObjectCmd(
505 m_StyleInfo->size,
506 m_StyleInfo->family,
507 m_StyleInfo->style,
508 m_StyleInfo->weight,
509 m_StyleInfo->underline,
510 m_StyleInfo->m_fg_valid ?
511 &m_StyleInfo->m_fg : NULL,
512 m_StyleInfo->m_bg_valid ?
513 &m_StyleInfo->m_bg : NULL);
514 obj->SetUserData(m_UserData);
515 return obj;
516 }
517
518 void
519 wxLayoutObjectCmd::Write(wxString &ostr)
520 {
521 ostr << WXLO_TYPE_CMD << '\n'
522 << m_StyleInfo->size << '\n'
523 << m_StyleInfo->family << '\n'
524 << m_StyleInfo->style << '\n'
525 << m_StyleInfo->weight << '\n'
526 << m_StyleInfo->underline << '\n'
527 << m_StyleInfo->m_fg_valid << '\n'
528 << m_StyleInfo->m_bg_valid << '\n';
529 if(m_StyleInfo->m_fg_valid)
530 {
531 ostr << m_StyleInfo->m_fg.Red() << '\n'
532 << m_StyleInfo->m_fg.Green() << '\n'
533 << m_StyleInfo->m_fg.Blue() << '\n';
534 }
535 if(m_StyleInfo->m_bg_valid)
536 {
537 ostr << m_StyleInfo->m_bg.Red() << '\n'
538 << m_StyleInfo->m_bg.Green() << '\n'
539 << m_StyleInfo->m_bg.Blue() << '\n';
540 }
541 }
542 /* static */
543 wxLayoutObjectCmd *
544 wxLayoutObjectCmd::Read(wxString &istr)
545 {
546 wxLayoutObjectCmd *obj = new wxLayoutObjectCmd;
547
548 wxString tmp;
549 ReadString(tmp, istr);
550 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->size);
551 ReadString(tmp, istr);
552 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->family);
553 ReadString(tmp, istr);
554 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->style);
555 ReadString(tmp, istr);
556 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->weight);
557 ReadString(tmp, istr);
558 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->underline);
559 ReadString(tmp, istr);
560 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->m_fg_valid);
561 ReadString(tmp, istr);
562 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->m_bg_valid);
563 if(obj->m_StyleInfo->m_fg_valid)
564 {
565 int red, green, blue;
566 ReadString(tmp, istr);
567 sscanf(tmp.c_str(),"%d", &red);
568 ReadString(tmp, istr);
569 sscanf(tmp.c_str(),"%d", &green);
570 ReadString(tmp, istr);
571 sscanf(tmp.c_str(),"%d", &blue);
572 obj->m_StyleInfo->m_fg = wxColour(red, green, blue);
573 }
574 if(obj->m_StyleInfo->m_bg_valid)
575 {
576 int red, green, blue;
577 ReadString(tmp, istr);
578 sscanf(tmp.c_str(),"%d", &red);
579 ReadString(tmp, istr);
580 sscanf(tmp.c_str(),"%d", &green);
581 ReadString(tmp, istr);
582 sscanf(tmp.c_str(),"%d", &blue);
583 obj->m_StyleInfo->m_bg = wxColour(red, green, blue);
584 }
585 return obj;
586 }
587
588
589 wxLayoutObjectCmd::~wxLayoutObjectCmd()
590 {
591 delete m_StyleInfo;
592 }
593
594 wxLayoutStyleInfo *
595 wxLayoutObjectCmd::GetStyle(void) const
596 {
597 return m_StyleInfo;
598 }
599
600 void
601 wxLayoutObjectCmd::Draw(wxDC &dc, wxPoint const & /* coords */,
602 wxLayoutList *wxllist,
603 CoordType begin, CoordType /* len */)
604 {
605 wxASSERT(m_StyleInfo);
606 wxllist->ApplyStyle(*m_StyleInfo, dc);
607 }
608
609 void
610 wxLayoutObjectCmd::Layout(wxDC &dc, class wxLayoutList * llist)
611 {
612 // this get called, so that recalculation uses right font sizes
613 Draw(dc, wxPoint(0,0), llist);
614 }
615
616
617 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
618
619 The wxLayoutLine object
620
621 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
622
623 wxLayoutLine::wxLayoutLine(wxLayoutLine *prev, wxLayoutList *llist)
624 {
625 m_LineNumber = 0;
626 m_Width = m_Height = 0;
627 m_Length = 0;
628 m_updateLeft = -1;
629 MarkDirty(0);
630 m_Previous = prev;
631 m_Next = NULL;
632 RecalculatePosition(llist);
633 if(m_Previous)
634 {
635 m_LineNumber = m_Previous->GetLineNumber()+1;
636 m_Next = m_Previous->GetNextLine();
637 m_Previous->m_Next = this;
638 }
639 if(m_Next)
640 {
641 m_Next->m_Previous = this;
642 m_Next->MoveLines(+1);
643 m_Next->RecalculatePositions(1,llist);
644 }
645
646 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 void
2122 wxLayoutList::UpdateCursorScreenPos(wxDC &dc)
2123 {
2124 wxCHECK_RET( m_CursorLine, "no cursor line" );
2125
2126 // we need to save the current style, in case the layout() of the line
2127 // changes it
2128 wxLayoutStyleInfo SiBackup = m_CurrentStyleInfo;
2129 m_CursorLine->Layout(dc, this,
2130 &m_CursorScreenPos, &m_CursorSize,
2131 m_CursorPos.x,
2132 true /* suppress update */);
2133 ApplyStyle(SiBackup, dc); // restore it
2134 }
2135
2136 wxPoint
2137 wxLayoutList::GetCursorScreenPos(wxDC &dc)
2138 {
2139 UpdateCursorScreenPos(dc);
2140
2141 return m_CursorScreenPos;
2142 }
2143
2144 /*
2145 Is called before each Draw(). Now, it will re-layout all lines which
2146 have changed.
2147 */
2148 void
2149 wxLayoutList::Layout(wxDC &dc, CoordType bottom, bool forceAll,
2150 wxPoint *cpos, wxPoint *csize)
2151 {
2152 // first, make sure everything is calculated - this might not be
2153 // needed, optimise it later
2154 ApplyStyle(m_DefaultStyleInfo, dc);
2155
2156 // This one we always Layout() to get the current cursor
2157 // coordinates on the screen:
2158 m_CursorLine->MarkDirty();
2159 bool wasDirty = false;
2160 wxLayoutLine *line = m_FirstLine;
2161 while(line)
2162 {
2163 if(! wasDirty)
2164 ApplyStyle(line->GetStyleInfo(), dc);
2165 if(forceAll || line->IsDirty()
2166 || (cpos && line->GetLineNumber() == cpos->y))
2167 {
2168 // The following Layout() calls will update our
2169 // m_CurrentStyleInfo if needed.
2170 if(line == m_CursorLine)
2171 line->Layout(dc, this,
2172 (wxPoint *)&m_CursorScreenPos,
2173 (wxPoint *)&m_CursorSize, m_CursorPos.x);
2174 if(cpos && line->GetLineNumber() == cpos->y)
2175 line->Layout(dc, this,
2176 cpos,
2177 csize, cpos->x);
2178 else
2179 line->Layout(dc, this);
2180 // little condition to speed up redrawing:
2181 if(bottom != -1 && line->GetPosition().y > bottom)
2182 break;
2183 wasDirty = true;
2184 }
2185 line->RecalculatePositions(1, this);
2186 line = line->GetNextLine();
2187 }
2188
2189 // can only be 0 if we are on the first line and have no next line
2190 wxASSERT(m_CursorSize.x != 0 || (m_CursorLine &&
2191 m_CursorLine->GetNextLine() == NULL &&
2192 m_CursorLine == m_FirstLine));
2193 AddCursorPosToUpdateRect();
2194 }
2195
2196 wxPoint
2197 wxLayoutList::GetScreenPos(wxDC &dc, const wxPoint &cpos, wxPoint *csize)
2198 {
2199 wxPoint pos = cpos;
2200 Layout(dc, -1, false, &pos, csize);
2201 return pos;
2202 }
2203
2204 void
2205 wxLayoutList::Draw(wxDC &dc,
2206 wxPoint const &offset,
2207 CoordType top,
2208 CoordType bottom)
2209 {
2210 wxLayoutLine *line = m_FirstLine;
2211
2212 if ( m_Selection.m_discarded )
2213 {
2214 // calculate them if we don't have them already
2215 if ( !m_Selection.HasValidScreenCoords() )
2216 {
2217 m_Selection.m_ScreenA = GetScreenPos(dc, m_Selection.m_CursorA);
2218 m_Selection.m_ScreenB = GetScreenPos(dc, m_Selection.m_CursorB);
2219 }
2220
2221 // invalidate the area which was previousle selected - and which is not
2222 // selected any more
2223 SetUpdateRect(m_Selection.m_ScreenA);
2224 SetUpdateRect(m_Selection.m_ScreenB);
2225
2226 m_Selection.m_discarded = false;
2227 }
2228
2229 /* We need to re-layout all dirty lines to update styleinfos
2230 etc. However, somehow we don't find all dirty lines... */
2231 Layout(dc); //,-1,true); //FIXME
2232 ApplyStyle(m_DefaultStyleInfo, dc);
2233 wxBrush brush(m_CurrentStyleInfo.m_bg, wxSOLID);
2234 dc.SetBrush(brush);
2235 dc.SetBackgroundMode(wxTRANSPARENT);
2236
2237 bool style_set = false;
2238 while(line)
2239 {
2240 // only draw if between top and bottom:
2241 if((top == -1 ||
2242 line->GetPosition().y + line->GetHeight() >= top))
2243 {
2244 // if(! style_set)
2245 {
2246 ApplyStyle(line->GetStyleInfo(), dc);
2247 style_set = true;
2248 }
2249 line->Draw(dc, this, offset);
2250 }
2251 #if 0
2252 else
2253 line->Layout(dc, this);
2254 #endif
2255 // little condition to speed up redrawing:
2256 if(bottom != -1 && line->GetPosition().y > bottom) break;
2257 line = line->GetNextLine();
2258 }
2259 InvalidateUpdateRect();
2260
2261 WXLO_DEBUG(("Selection is %s : l%d,%ld/%ld,%ld",
2262 m_Selection.m_valid ? "valid" : "invalid",
2263 m_Selection.m_CursorA.x, m_Selection.m_CursorA.y,
2264 m_Selection.m_CursorB.x, m_Selection.m_CursorB.y));
2265 }
2266
2267 wxLayoutObject *
2268 wxLayoutList::FindObjectScreen(wxDC &dc, wxPoint const pos,
2269 wxPoint *cursorPos,
2270 bool *found)
2271 {
2272 // First, find the right line:
2273 wxLayoutLine *line = m_FirstLine;
2274 wxPoint p;
2275
2276 ApplyStyle(m_DefaultStyleInfo, dc);
2277 while(line)
2278 {
2279 p = line->GetPosition();
2280 if(p.y <= pos.y && p.y+line->GetHeight() >= pos.y)
2281 break;
2282 #if 0
2283 // we need to run a layout here to get font sizes right :-(
2284
2285 // VZ: we can't call Layout() from here because it marks the line as
2286 // clean and it is not refreshed when it's called from wxLayoutList::
2287 // Layout() - if we really need to do this, we should introduce an
2288 // extra argument to Layout() to prevent the line from MarkClean()ing
2289 // itself here
2290 line->Layout(dc, this);
2291 #endif
2292 line = line->GetNextLine();
2293 }
2294 if(line == NULL)
2295 {
2296 if(found) *found = false;
2297 return NULL; // not found
2298 }
2299 if(cursorPos) cursorPos->y = line->GetLineNumber();
2300 // Now, find the object in the line:
2301 wxLOiterator i = line->FindObjectScreen(dc, pos.x,
2302 cursorPos ? & cursorPos->x : NULL ,
2303 found);
2304 return (i == NULLIT) ? NULL : *i;
2305
2306 }
2307
2308 wxPoint
2309 wxLayoutList::GetSize(void) const
2310 {
2311 wxLayoutLine
2312 *line = m_FirstLine,
2313 *last = line;
2314 if(! line)
2315 return wxPoint(0,0);
2316
2317 wxPoint maxPoint(0,0);
2318
2319 // find last line:
2320 while(line)
2321 {
2322 if(line->GetWidth() > maxPoint.x)
2323 maxPoint.x = line->GetWidth();
2324 last = line;
2325 line = line->GetNextLine();
2326 }
2327
2328 maxPoint.y = last->GetPosition().y + last->GetHeight();
2329
2330 // if the line was just added, its height would be 0 and we can't call
2331 // Layout() from here because we don't have a dc and we might be not drawing
2332 // at all, besides... So take the cursor height by default (taking 0 is bad
2333 // because then the scrollbars won't be resized and the new line won't be
2334 // shown at all)
2335 if ( last->IsDirty() )
2336 {
2337 if ( last->GetHeight() == 0 )
2338 maxPoint.y += m_CursorSize.y;
2339 if ( last->GetWidth() == 0 && maxPoint.x < m_CursorSize.x )
2340 maxPoint.x = m_CursorSize.x;
2341 }
2342
2343 return maxPoint;
2344 }
2345
2346
2347 void
2348 wxLayoutList::DrawCursor(wxDC &dc, bool active, wxPoint const &translate)
2349 {
2350 if ( m_movedCursor )
2351 {
2352 UpdateCursorScreenPos(dc);
2353
2354 m_movedCursor = false;
2355 }
2356
2357 wxPoint coords(m_CursorScreenPos);
2358 coords += translate;
2359
2360 #ifdef WXLAYOUT_DEBUG
2361 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
2362 (long)m_CursorPos.x, (long)m_CursorPos.y,
2363 (long)coords.x, (long)coords.y,
2364 (long)m_CursorSize.x, (long)m_CursorSize.y,
2365 (long)m_CursorLine->GetLineNumber(),
2366 (long)m_CursorLine->GetLength()));
2367
2368 wxLogStatus("Cursor is at (%d, %d)", m_CursorPos.x, m_CursorPos.y);
2369 #endif
2370
2371 #ifdef WXLAYOUT_USE_CARET
2372 m_caret->Move(coords);
2373 #else // !WXLAYOUT_USE_CARET
2374 dc.SetBrush(*wxBLACK_BRUSH);
2375 dc.SetLogicalFunction(wxXOR);
2376 dc.SetPen(wxPen(*wxBLACK,1,wxSOLID));
2377 if(active)
2378 {
2379 dc.DrawRectangle(coords.x, coords.y,
2380 m_CursorSize.x, m_CursorSize.y);
2381 SetUpdateRect(coords.x, coords.y);
2382 SetUpdateRect(coords.x+m_CursorSize.x, coords.y+m_CursorSize.y);
2383 }
2384 else
2385 {
2386 dc.DrawLine(coords.x, coords.y+m_CursorSize.y-1,
2387 coords.x, coords.y);
2388 SetUpdateRect(coords.x, coords.y+m_CursorSize.y-1);
2389 SetUpdateRect(coords.x, coords.y);
2390 }
2391 dc.SetLogicalFunction(wxCOPY);
2392 //dc.SetBrush(wxNullBrush);
2393 #endif // WXLAYOUT_USE_CARET/!WXLAYOUT_USE_CARET
2394 }
2395
2396 void
2397 wxLayoutList::SetUpdateRect(CoordType x, CoordType y)
2398 {
2399 if(m_UpdateRectValid)
2400 GrowRect(m_UpdateRect, x, y);
2401 else
2402 {
2403 m_UpdateRect.x = x;
2404 m_UpdateRect.y = y;
2405 m_UpdateRect.width = 4; // large enough to avoid surprises from
2406 m_UpdateRect.height = 4;// wxGTK :-)
2407 m_UpdateRectValid = true;
2408 }
2409 }
2410
2411 void
2412 wxLayoutList::StartSelection(const wxPoint& cposOrig, const wxPoint& spos)
2413 {
2414 wxPoint cpos(cposOrig);
2415 if ( cpos.x == -1 )
2416 cpos = m_CursorPos;
2417 WXLO_DEBUG(("Starting selection at %ld/%ld", cpos.x, cpos.y));
2418 m_Selection.m_CursorA = cpos;
2419 m_Selection.m_CursorB = cpos;
2420 m_Selection.m_ScreenA = spos;
2421 m_Selection.m_ScreenB = spos;
2422 m_Selection.m_selecting = true;
2423 m_Selection.m_valid = false;
2424 }
2425
2426 void
2427 wxLayoutList::ContinueSelection(const wxPoint& cposOrig, const wxPoint& spos)
2428 {
2429 wxPoint cpos(cposOrig);
2430 if(cpos.x == -1)
2431 cpos = m_CursorPos;
2432
2433 wxASSERT(m_Selection.m_selecting == true);
2434 wxASSERT(m_Selection.m_valid == false);
2435 WXLO_DEBUG(("Continuing selection at %ld/%ld", cpos.x, cpos.y));
2436
2437 if ( m_Selection.m_CursorB <= cpos )
2438 {
2439 m_Selection.m_ScreenB = spos;
2440 m_Selection.m_CursorB = cpos;
2441 }
2442 else
2443 {
2444 m_Selection.m_ScreenA = spos;
2445 m_Selection.m_CursorA = cpos;
2446 }
2447
2448 // we always want m_CursorA <= m_CursorB!
2449 if( m_Selection.m_CursorA > m_Selection.m_CursorB )
2450 {
2451 // exchange the start/end points
2452 wxPoint help = m_Selection.m_CursorB;
2453 m_Selection.m_CursorB = m_Selection.m_CursorA;
2454 m_Selection.m_CursorA = help;
2455
2456 help = m_Selection.m_ScreenB;
2457 m_Selection.m_ScreenB = m_Selection.m_ScreenA;
2458 m_Selection.m_ScreenA = help;
2459 }
2460 }
2461
2462 void
2463 wxLayoutList::EndSelection(const wxPoint& cposOrig, const wxPoint& spos)
2464 {
2465 wxPoint cpos(cposOrig);
2466 if(cpos.x == -1)
2467 cpos = m_CursorPos;
2468 ContinueSelection(cpos);
2469 WXLO_DEBUG(("Ending selection at %ld/%ld", cpos.x, cpos.y));
2470 m_Selection.m_selecting = false;
2471 m_Selection.m_valid = true;
2472 }
2473
2474 void
2475 wxLayoutList::DiscardSelection()
2476 {
2477 if ( !HasSelection() )
2478 return;
2479
2480 m_Selection.m_valid =
2481 m_Selection.m_selecting = false;
2482 m_Selection.m_discarded = true;
2483 }
2484
2485 bool
2486 wxLayoutList::IsSelecting(void) const
2487 {
2488 return m_Selection.m_selecting;
2489 }
2490
2491 bool
2492 wxLayoutList::IsSelected(const wxPoint &cursor) const
2493 {
2494 if ( !HasSelection() )
2495 return false;
2496
2497 return m_Selection.m_CursorA <= cursor && cursor <= m_Selection.m_CursorB;
2498 }
2499
2500
2501 /** Tests whether this layout line is selected and needs
2502 highlighting.
2503 @param line to test for
2504 @return 0 = not selected, 1 = fully selected, -1 = partially
2505 selected
2506 */
2507 int
2508 wxLayoutList::IsSelected(const wxLayoutLine *line, CoordType *from,
2509 CoordType *to)
2510 {
2511 wxASSERT(line); wxASSERT(to); wxASSERT(from);
2512
2513 if(! m_Selection.m_valid && ! m_Selection.m_selecting)
2514 return 0;
2515
2516 CoordType y = line->GetLineNumber();
2517 if(m_Selection.m_CursorA.y < y && m_Selection.m_CursorB.y > y)
2518 return 1;
2519 else if(m_Selection.m_CursorA.y == y)
2520 {
2521 *from = m_Selection.m_CursorA.x;
2522 if(m_Selection.m_CursorB.y == y)
2523 *to = m_Selection.m_CursorB.x;
2524 else
2525 *to = line->GetLength();
2526 return -1;
2527 }
2528 else if(m_Selection.m_CursorB.y == y)
2529 {
2530 *to = m_Selection.m_CursorB.x;
2531 if(m_Selection.m_CursorA.y == y)
2532 *from = m_Selection.m_CursorA.x;
2533 else
2534 *from = 0;
2535 return -1;
2536 }
2537 else
2538 return 0;
2539 }
2540
2541 void
2542 wxLayoutList::DeleteSelection(void)
2543 {
2544 if(! m_Selection.m_valid)
2545 return;
2546
2547 m_Selection.m_valid = false;
2548
2549 // Only delete part of the current line?
2550 if(m_Selection.m_CursorA.y == m_Selection.m_CursorB.y)
2551 {
2552 MoveCursorTo(m_Selection.m_CursorA);
2553 Delete(m_Selection.m_CursorB.x - m_Selection.m_CursorA.x);
2554 return;
2555 }
2556
2557
2558 wxLayoutLine
2559 * firstLine = NULL,
2560 * lastLine = NULL;
2561
2562 for(firstLine = m_FirstLine;
2563 firstLine && firstLine->GetLineNumber() < m_Selection.m_CursorA.y;
2564 firstLine=firstLine->GetNextLine())
2565 ;
2566 if(!firstLine || firstLine->GetLineNumber() != m_Selection.m_CursorA.y)
2567 return;
2568
2569
2570 for(lastLine = m_FirstLine;
2571 lastLine && lastLine->GetLineNumber() < m_Selection.m_CursorB.y;
2572 lastLine=lastLine->GetNextLine())
2573 ;
2574 if(!lastLine || lastLine->GetLineNumber() != m_Selection.m_CursorB.y)
2575 return;
2576
2577
2578 // We now know that the two lines are different:
2579
2580 // First, delete what's left of this line:
2581 MoveCursorTo(m_Selection.m_CursorA);
2582 DeleteToEndOfLine();
2583
2584 wxLayoutLine *nextLine = firstLine->GetNextLine();
2585 while(nextLine && nextLine != lastLine)
2586 nextLine = nextLine->DeleteLine(false, this);
2587
2588 // Now nextLine = lastLine;
2589 Delete(1); // This joins firstLine and nextLine
2590 Delete(m_Selection.m_CursorB.x); // This deletes the first x
2591 // positions
2592
2593 /// Recalculate:
2594 firstLine->RecalculatePositions(1, this);
2595 }
2596
2597 /// Starts highlighting the selection
2598 void
2599 wxLayoutList::StartHighlighting(wxDC &dc)
2600 {
2601 #if SHOW_SELECTIONS
2602 dc.SetTextForeground(m_CurrentStyleInfo.m_bg);
2603 dc.SetTextBackground(m_CurrentStyleInfo.m_fg);
2604 dc.SetBackgroundMode(wxSOLID);
2605 #endif
2606 }
2607
2608 /// Ends highlighting the selection
2609 void
2610 wxLayoutList::EndHighlighting(wxDC &dc)
2611 {
2612 #if SHOW_SELECTIONS
2613 dc.SetTextForeground(m_CurrentStyleInfo.m_fg);
2614 dc.SetTextBackground(m_CurrentStyleInfo.m_bg);
2615 dc.SetBackgroundMode(wxTRANSPARENT);
2616 #endif
2617 }
2618
2619
2620 wxLayoutList *
2621 wxLayoutList::Copy(const wxPoint &from,
2622 const wxPoint &to)
2623 {
2624 wxLayoutLine
2625 * firstLine = NULL,
2626 * lastLine = NULL;
2627
2628 for(firstLine = m_FirstLine;
2629 firstLine && firstLine->GetLineNumber() < from.y;
2630 firstLine=firstLine->GetNextLine())
2631 ;
2632 if(!firstLine || firstLine->GetLineNumber() != from.y)
2633 return NULL;
2634
2635 for(lastLine = m_FirstLine;
2636 lastLine && lastLine->GetLineNumber() < to.y;
2637 lastLine=lastLine->GetNextLine())
2638 ;
2639 if(!lastLine || lastLine->GetLineNumber() != to.y)
2640 return NULL;
2641
2642 if(to <= from)
2643 {
2644 wxLayoutLine *tmp = firstLine;
2645 firstLine = lastLine;
2646 lastLine = tmp;
2647 }
2648
2649 wxLayoutList *llist = new wxLayoutList();
2650
2651 if(firstLine == lastLine)
2652 {
2653 firstLine->Copy(llist, from.x, to.x);
2654 }
2655 else
2656 {
2657 // Extract objects from first line
2658 firstLine->Copy(llist, from.x);
2659 llist->LineBreak();
2660 // Extract all lines between
2661 for(wxLayoutLine *line = firstLine->GetNextLine();
2662 line != lastLine;
2663 line = line->GetNextLine())
2664 {
2665 line->Copy(llist);
2666 llist->LineBreak();
2667 }
2668 // Extract objects from last line
2669 lastLine->Copy(llist, 0, to.x);
2670 }
2671 return llist;
2672 }
2673
2674 wxLayoutList *
2675 wxLayoutList::GetSelection(wxLayoutDataObject *wxlo, bool invalidate)
2676 {
2677 if(! m_Selection.m_valid)
2678 {
2679 if(m_Selection.m_selecting)
2680 EndSelection();
2681 else
2682 return NULL;
2683 }
2684
2685 if(invalidate) m_Selection.m_valid = false;
2686
2687 wxLayoutList *llist = Copy( m_Selection.m_CursorA,
2688 m_Selection.m_CursorB );
2689
2690 if(llist && wxlo) // export as data object, too
2691 {
2692 wxString string;
2693
2694 wxLayoutExportObject *export;
2695 wxLayoutExportStatus status(llist);
2696 while((export = wxLayoutExport( &status, WXLO_EXPORT_AS_OBJECTS)) != NULL)
2697 {
2698 if(export->type == WXLO_EXPORT_EMPTYLINE)
2699 ; //FIXME missing support for linebreaks in string format
2700 else
2701 export->content.object->Write(string);
2702 delete export;
2703 }
2704
2705 wxlo->SetData(string.c_str(), string.Length()+1);
2706 }
2707 return llist;
2708 }
2709
2710
2711
2712 #define COPY_SI(what) if(si.what != -1) { m_CurrentStyleInfo.what = si.what; fontChanged = TRUE; }
2713
2714 void
2715 wxLayoutList::ApplyStyle(wxLayoutStyleInfo const &si, wxDC &dc)
2716 {
2717 bool fontChanged = FALSE;
2718 COPY_SI(family);
2719 COPY_SI(size);
2720 COPY_SI(style);
2721 COPY_SI(weight);
2722 COPY_SI(underline);
2723 if(fontChanged)
2724 dc.SetFont( m_FontCache.GetFont(m_CurrentStyleInfo) );
2725
2726 if(si.m_fg_valid)
2727 {
2728 m_CurrentStyleInfo.m_fg = si.m_fg;
2729 dc.SetTextForeground(m_CurrentStyleInfo.m_fg);
2730 }
2731 if(si.m_bg_valid)
2732 {
2733 m_CurrentStyleInfo.m_bg = si.m_bg;
2734 dc.SetTextBackground(m_CurrentStyleInfo.m_bg);
2735 }
2736 }
2737
2738
2739 #ifdef WXLAYOUT_DEBUG
2740
2741 void
2742 wxLayoutList::Debug(void)
2743 {
2744 WXLO_DEBUG(("Cursor is in line %d, screen pos = (%d, %d)",
2745 m_CursorLine->GetLineNumber(),
2746 m_CursorScreenPos.x, m_CursorScreenPos.y));
2747
2748 wxLayoutLine *line;
2749 for(line = m_FirstLine; line; line = line->GetNextLine())
2750 {
2751 line->Debug();
2752 }
2753 }
2754
2755 #endif
2756
2757
2758 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2759
2760 wxLayoutPrintout
2761
2762 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2763
2764 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList *llist,
2765 wxString const & title)
2766 :wxPrintout(title)
2767 {
2768 m_llist = llist;
2769 m_title = title;
2770 // remove any highlighting which could interfere with printing:
2771 m_llist->StartSelection();
2772 m_llist->EndSelection();
2773 }
2774
2775 wxLayoutPrintout::~wxLayoutPrintout()
2776 {
2777 }
2778
2779 float
2780 wxLayoutPrintout::ScaleDC(wxDC *dc)
2781 {
2782 // The following bit is taken from the printing sample, let's see
2783 // whether it works for us.
2784
2785 /* You might use THIS code to set the printer DC to ROUGHLY reflect
2786 * the screen text size. This page also draws lines of actual length 5cm
2787 * on the page.
2788 */
2789 // Get the logical pixels per inch of screen and printer
2790 int ppiScreenX, ppiScreenY;
2791 GetPPIScreen(&ppiScreenX, &ppiScreenY);
2792 int ppiPrinterX, ppiPrinterY;
2793 GetPPIPrinter(&ppiPrinterX, &ppiPrinterY);
2794
2795 if(ppiScreenX == 0) // not yet set, need to guess
2796 {
2797 ppiScreenX = 100;
2798 ppiScreenY = 100;
2799 }
2800 if(ppiPrinterX == 0) // not yet set, need to guess
2801 {
2802 ppiPrinterX = 72;
2803 ppiPrinterY = 72;
2804 }
2805
2806 // This scales the DC so that the printout roughly represents the
2807 // the screen scaling. The text point size _should_ be the right size
2808 // but in fact is too small for some reason. This is a detail that will
2809 // need to be addressed at some point but can be fudged for the
2810 // moment.
2811 float scale = (float)((float)ppiPrinterX/(float)ppiScreenX);
2812
2813 // Now we have to check in case our real page size is reduced
2814 // (e.g. because we're drawing to a print preview memory DC)
2815 int pageWidth, pageHeight;
2816 int w, h;
2817 dc->GetSize(&w, &h);
2818 GetPageSizePixels(&pageWidth, &pageHeight);
2819 if(pageWidth != 0) // doesn't work always
2820 {
2821 // If printer pageWidth == current DC width, then this doesn't
2822 // change. But w might be the preview bitmap width, so scale down.
2823 scale = scale * (float)(w/(float)pageWidth);
2824 }
2825 dc->SetUserScale(scale, scale);
2826 return scale;
2827 }
2828
2829 bool wxLayoutPrintout::OnPrintPage(int page)
2830 {
2831 wxDC *dc = GetDC();
2832
2833 ScaleDC(dc);
2834
2835 if (dc)
2836 {
2837 int top, bottom;
2838 top = (page - 1)*m_PrintoutHeight;
2839 bottom = top + m_PrintoutHeight;
2840 // SetDeviceOrigin() doesn't work here, so we need to manually
2841 // translate all coordinates.
2842 wxPoint translate(m_Offset.x,m_Offset.y-top);
2843 m_llist->Draw(*dc, translate, top, bottom);
2844 return true;
2845 }
2846 else
2847 return false;
2848 }
2849
2850 void wxLayoutPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo)
2851 {
2852 /* We allocate a temporary wxDC for printing, so that we can
2853 determine the correct paper size and scaling. We don't actually
2854 print anything on it. */
2855 #ifdef __WXMSW__
2856 wxPrinterDC psdc("","",WXLLIST_TEMPFILE,false);
2857 #else
2858 wxPostScriptDC psdc(WXLLIST_TEMPFILE,false);
2859 #endif
2860
2861 float scale = ScaleDC(&psdc);
2862
2863 psdc.GetSize(&m_PageWidth, &m_PageHeight);
2864 // This sets a left/top origin of 15% and 20%:
2865 m_Offset = wxPoint((15*m_PageWidth)/100, m_PageHeight/20);
2866
2867 // This is the length of the printable area.
2868 m_PrintoutHeight = m_PageHeight - (int) (m_PageHeight * 0.15);
2869 m_PrintoutHeight = (int)( m_PrintoutHeight / scale); // we want to use the real paper height
2870
2871
2872 m_NumOfPages = 1 +
2873 (int)( m_llist->GetSize().y / (float)(m_PrintoutHeight));
2874
2875 *minPage = 1;
2876 *maxPage = m_NumOfPages;
2877
2878 *selPageFrom = 1;
2879 *selPageTo = m_NumOfPages;
2880 wxRemoveFile(WXLLIST_TEMPFILE);
2881 }
2882
2883 bool wxLayoutPrintout::HasPage(int pageNum)
2884 {
2885 return pageNum <= m_NumOfPages;
2886 }
2887
2888 /*
2889 Stupid wxWindows doesn't draw proper ellipses, so we comment this
2890 out. It's a waste of paper anyway.
2891 */
2892 #if 0
2893 void
2894 wxLayoutPrintout::DrawHeader(wxDC &dc,
2895 wxPoint topleft, wxPoint bottomright,
2896 int pageno)
2897 {
2898 // make backups of all essential parameters
2899 const wxBrush& brush = dc.GetBrush();
2900 const wxPen& pen = dc.GetPen();
2901 const wxFont& font = dc.GetFont();
2902
2903 dc.SetBrush(*wxWHITE_BRUSH);
2904 dc.SetPen(wxPen(*wxBLACK,0,wxSOLID));
2905 dc.DrawRoundedRectangle(topleft.x,
2906 topleft.y,bottomright.x-topleft.x,
2907 bottomright.y-topleft.y);
2908 dc.SetBrush(*wxBLACK_BRUSH);
2909 wxFont myfont = wxFont((WXLO_DEFAULTFONTSIZE*12)/10,
2910 wxSWISS,wxNORMAL,wxBOLD,false,"Helvetica");
2911 dc.SetFont(myfont);
2912
2913 wxString page;
2914 page = "9999/9999 "; // many pages...
2915 long w,h;
2916 dc.GetTextExtent(page,&w,&h);
2917 page.Printf("%d/%d", pageno, (int) m_NumOfPages);
2918 dc.DrawText(page,bottomright.x-w,topleft.y+h/2);
2919 dc.GetTextExtent("XXXX", &w,&h);
2920 dc.DrawText(m_title, topleft.x+w,topleft.y+h/2);
2921
2922 // restore settings
2923 dc.SetPen(pen);
2924 dc.SetBrush(brush);
2925 dc.SetFont(font);
2926 }
2927 #endif
2928
2929
2930 wxFont &
2931 wxFontCache::GetFont(int family, int size, int style, int weight,
2932 bool underline)
2933 {
2934 for(wxFCEList::iterator i = m_FontList.begin();
2935 i != m_FontList.end(); i++)
2936 if( (**i).Matches(family, size, style, weight, underline) )
2937 return (**i).GetFont();
2938 // not found:
2939 wxFontCacheEntry *fce = new wxFontCacheEntry(family, size, style,
2940 weight, underline);
2941 m_FontList.push_back(fce);
2942 return fce->GetFont();
2943 }
2944