]> git.saurik.com Git - wxWidgets.git/blob - samples/richedit/wxllist.cpp
6511e8f7259711bdfc0af2c7cb6bdf1e14cd8a6c
[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(*wxBLACK_BRUSH);
2354 dc.SetLogicalFunction(wxXOR);
2355 dc.SetPen(wxPen(*wxBLACK,1,wxSOLID));
2356 if(active)
2357 {
2358 dc.DrawRectangle(coords.x, coords.y,
2359 m_CursorSize.x, m_CursorSize.y);
2360 SetUpdateRect(coords.x, coords.y);
2361 SetUpdateRect(coords.x+m_CursorSize.x, coords.y+m_CursorSize.y);
2362 }
2363 else
2364 {
2365 dc.DrawLine(coords.x, coords.y+m_CursorSize.y-1,
2366 coords.x, coords.y);
2367 SetUpdateRect(coords.x, coords.y+m_CursorSize.y-1);
2368 SetUpdateRect(coords.x, coords.y);
2369 }
2370 dc.SetLogicalFunction(wxCOPY);
2371 //dc.SetBrush(wxNullBrush);
2372 #endif // WXLAYOUT_USE_CARET/!WXLAYOUT_USE_CARET
2373 }
2374
2375 void
2376 wxLayoutList::SetUpdateRect(CoordType x, CoordType y)
2377 {
2378 if(m_UpdateRectValid)
2379 GrowRect(m_UpdateRect, x, y);
2380 else
2381 {
2382 m_UpdateRect.x = x;
2383 m_UpdateRect.y = y;
2384 m_UpdateRect.width = 4; // large enough to avoid surprises from
2385 m_UpdateRect.height = 4;// wxGTK :-)
2386 m_UpdateRectValid = true;
2387 }
2388 }
2389
2390 void
2391 wxLayoutList::StartSelection(const wxPoint& cposOrig, const wxPoint& spos)
2392 {
2393 wxPoint cpos(cposOrig);
2394 if ( cpos.x == -1 )
2395 cpos = m_CursorPos;
2396 WXLO_DEBUG(("Starting selection at %ld/%ld", cpos.x, cpos.y));
2397 m_Selection.m_CursorA = cpos;
2398 m_Selection.m_CursorB = cpos;
2399 m_Selection.m_ScreenA = spos;
2400 m_Selection.m_ScreenB = spos;
2401 m_Selection.m_selecting = true;
2402 m_Selection.m_valid = false;
2403 }
2404
2405 void
2406 wxLayoutList::ContinueSelection(const wxPoint& cposOrig, const wxPoint& spos)
2407 {
2408 wxPoint cpos(cposOrig);
2409 if(cpos.x == -1)
2410 cpos = m_CursorPos;
2411
2412 wxASSERT(m_Selection.m_selecting == true);
2413 wxASSERT(m_Selection.m_valid == false);
2414 WXLO_DEBUG(("Continuing selection at %ld/%ld", cpos.x, cpos.y));
2415
2416 if ( m_Selection.m_CursorB <= cpos )
2417 {
2418 m_Selection.m_ScreenB = spos;
2419 m_Selection.m_CursorB = cpos;
2420 }
2421 else
2422 {
2423 m_Selection.m_ScreenA = spos;
2424 m_Selection.m_CursorA = cpos;
2425 }
2426
2427 // we always want m_CursorA <= m_CursorB!
2428 if( m_Selection.m_CursorA > m_Selection.m_CursorB )
2429 {
2430 // exchange the start/end points
2431 wxPoint help = m_Selection.m_CursorB;
2432 m_Selection.m_CursorB = m_Selection.m_CursorA;
2433 m_Selection.m_CursorA = help;
2434
2435 help = m_Selection.m_ScreenB;
2436 m_Selection.m_ScreenB = m_Selection.m_ScreenA;
2437 m_Selection.m_ScreenA = help;
2438 }
2439 }
2440
2441 void
2442 wxLayoutList::EndSelection(const wxPoint& cposOrig, const wxPoint& spos)
2443 {
2444 wxPoint cpos(cposOrig);
2445 if(cpos.x == -1)
2446 cpos = m_CursorPos;
2447 ContinueSelection(cpos);
2448 WXLO_DEBUG(("Ending selection at %ld/%ld", cpos.x, cpos.y));
2449 m_Selection.m_selecting = false;
2450 m_Selection.m_valid = true;
2451 }
2452
2453 void
2454 wxLayoutList::DiscardSelection()
2455 {
2456 if ( !HasSelection() )
2457 return;
2458
2459 m_Selection.m_valid =
2460 m_Selection.m_selecting = false;
2461 m_Selection.m_discarded = true;
2462 }
2463
2464 bool
2465 wxLayoutList::IsSelecting(void) const
2466 {
2467 return m_Selection.m_selecting;
2468 }
2469
2470 bool
2471 wxLayoutList::IsSelected(const wxPoint &cursor) const
2472 {
2473 if ( !HasSelection() )
2474 return false;
2475
2476 return m_Selection.m_CursorA <= cursor && cursor <= m_Selection.m_CursorB;
2477 }
2478
2479
2480 /** Tests whether this layout line is selected and needs
2481 highlighting.
2482 @param line to test for
2483 @return 0 = not selected, 1 = fully selected, -1 = partially
2484 selected
2485 */
2486 int
2487 wxLayoutList::IsSelected(const wxLayoutLine *line, CoordType *from,
2488 CoordType *to)
2489 {
2490 wxASSERT(line); wxASSERT(to); wxASSERT(from);
2491
2492 if(! m_Selection.m_valid && ! m_Selection.m_selecting)
2493 return 0;
2494
2495 CoordType y = line->GetLineNumber();
2496 if(m_Selection.m_CursorA.y < y && m_Selection.m_CursorB.y > y)
2497 return 1;
2498 else if(m_Selection.m_CursorA.y == y)
2499 {
2500 *from = m_Selection.m_CursorA.x;
2501 if(m_Selection.m_CursorB.y == y)
2502 *to = m_Selection.m_CursorB.x;
2503 else
2504 *to = line->GetLength();
2505 return -1;
2506 }
2507 else if(m_Selection.m_CursorB.y == y)
2508 {
2509 *to = m_Selection.m_CursorB.x;
2510 if(m_Selection.m_CursorA.y == y)
2511 *from = m_Selection.m_CursorA.x;
2512 else
2513 *from = 0;
2514 return -1;
2515 }
2516 else
2517 return 0;
2518 }
2519
2520 void
2521 wxLayoutList::DeleteSelection(void)
2522 {
2523 if(! m_Selection.m_valid)
2524 return;
2525
2526 m_Selection.m_valid = false;
2527
2528 // Only delete part of the current line?
2529 if(m_Selection.m_CursorA.y == m_Selection.m_CursorB.y)
2530 {
2531 MoveCursorTo(m_Selection.m_CursorA);
2532 Delete(m_Selection.m_CursorB.x - m_Selection.m_CursorA.x);
2533 return;
2534 }
2535
2536
2537 wxLayoutLine
2538 * firstLine = NULL,
2539 * lastLine = NULL;
2540
2541 for(firstLine = m_FirstLine;
2542 firstLine && firstLine->GetLineNumber() < m_Selection.m_CursorA.y;
2543 firstLine=firstLine->GetNextLine())
2544 ;
2545 if(!firstLine || firstLine->GetLineNumber() != m_Selection.m_CursorA.y)
2546 return;
2547
2548
2549 for(lastLine = m_FirstLine;
2550 lastLine && lastLine->GetLineNumber() < m_Selection.m_CursorB.y;
2551 lastLine=lastLine->GetNextLine())
2552 ;
2553 if(!lastLine || lastLine->GetLineNumber() != m_Selection.m_CursorB.y)
2554 return;
2555
2556
2557 // We now know that the two lines are different:
2558
2559 // First, delete what's left of this line:
2560 MoveCursorTo(m_Selection.m_CursorA);
2561 DeleteToEndOfLine();
2562
2563 wxLayoutLine *nextLine = firstLine->GetNextLine();
2564 while(nextLine && nextLine != lastLine)
2565 nextLine = nextLine->DeleteLine(false, this);
2566
2567 // Now nextLine = lastLine;
2568 Delete(1); // This joins firstLine and nextLine
2569 Delete(m_Selection.m_CursorB.x); // This deletes the first x
2570 // positions
2571
2572 /// Recalculate:
2573 firstLine->RecalculatePositions(1, this);
2574 }
2575
2576 /// Starts highlighting the selection
2577 void
2578 wxLayoutList::StartHighlighting(wxDC &dc)
2579 {
2580 #if SHOW_SELECTIONS
2581 dc.SetTextForeground(m_CurrentStyleInfo.m_bg);
2582 dc.SetTextBackground(m_CurrentStyleInfo.m_fg);
2583 dc.SetBackgroundMode(wxSOLID);
2584 #endif
2585 }
2586
2587 /// Ends highlighting the selection
2588 void
2589 wxLayoutList::EndHighlighting(wxDC &dc)
2590 {
2591 #if SHOW_SELECTIONS
2592 dc.SetTextForeground(m_CurrentStyleInfo.m_fg);
2593 dc.SetTextBackground(m_CurrentStyleInfo.m_bg);
2594 dc.SetBackgroundMode(wxTRANSPARENT);
2595 #endif
2596 }
2597
2598
2599 wxLayoutList *
2600 wxLayoutList::Copy(const wxPoint &from,
2601 const wxPoint &to)
2602 {
2603 wxLayoutLine
2604 * firstLine = NULL,
2605 * lastLine = NULL;
2606
2607 for(firstLine = m_FirstLine;
2608 firstLine && firstLine->GetLineNumber() < from.y;
2609 firstLine=firstLine->GetNextLine())
2610 ;
2611 if(!firstLine || firstLine->GetLineNumber() != from.y)
2612 return NULL;
2613
2614 for(lastLine = m_FirstLine;
2615 lastLine && lastLine->GetLineNumber() < to.y;
2616 lastLine=lastLine->GetNextLine())
2617 ;
2618 if(!lastLine || lastLine->GetLineNumber() != to.y)
2619 return NULL;
2620
2621 if(to <= from)
2622 {
2623 wxLayoutLine *tmp = firstLine;
2624 firstLine = lastLine;
2625 lastLine = tmp;
2626 }
2627
2628 wxLayoutList *llist = new wxLayoutList();
2629
2630 if(firstLine == lastLine)
2631 {
2632 firstLine->Copy(llist, from.x, to.x);
2633 }
2634 else
2635 {
2636 // Extract objects from first line
2637 firstLine->Copy(llist, from.x);
2638 llist->LineBreak();
2639 // Extract all lines between
2640 for(wxLayoutLine *line = firstLine->GetNextLine();
2641 line != lastLine;
2642 line = line->GetNextLine())
2643 {
2644 line->Copy(llist);
2645 llist->LineBreak();
2646 }
2647 // Extract objects from last line
2648 lastLine->Copy(llist, 0, to.x);
2649 }
2650 return llist;
2651 }
2652
2653 wxLayoutList *
2654 wxLayoutList::GetSelection(wxLayoutDataObject *wxlo, bool invalidate)
2655 {
2656 if(! m_Selection.m_valid)
2657 {
2658 if(m_Selection.m_selecting)
2659 EndSelection();
2660 else
2661 return NULL;
2662 }
2663
2664 if(invalidate) m_Selection.m_valid = false;
2665
2666 wxLayoutList *llist = Copy( m_Selection.m_CursorA,
2667 m_Selection.m_CursorB );
2668
2669 if(llist && wxlo) // export as data object, too
2670 {
2671 wxString string;
2672
2673 wxLayoutExportObject *export;
2674 wxLayoutExportStatus status(llist);
2675 while((export = wxLayoutExport( &status, WXLO_EXPORT_AS_OBJECTS)) != NULL)
2676 {
2677 if(export->type == WXLO_EXPORT_EMPTYLINE)
2678 ; //FIXME missing support for linebreaks in string format
2679 else
2680 export->content.object->Write(string);
2681 delete export;
2682 }
2683
2684 wxlo->SetData(string.c_str(), string.Length()+1);
2685 }
2686 return llist;
2687 }
2688
2689
2690
2691 #define COPY_SI(what) if(si.what != -1) { m_CurrentStyleInfo.what = si.what; fontChanged = TRUE; }
2692
2693 void
2694 wxLayoutList::ApplyStyle(wxLayoutStyleInfo const &si, wxDC &dc)
2695 {
2696 bool fontChanged = FALSE;
2697 COPY_SI(family);
2698 COPY_SI(size);
2699 COPY_SI(style);
2700 COPY_SI(weight);
2701 COPY_SI(underline);
2702 if(fontChanged)
2703 dc.SetFont( m_FontCache.GetFont(m_CurrentStyleInfo) );
2704
2705 if(si.m_fg_valid)
2706 {
2707 m_CurrentStyleInfo.m_fg = si.m_fg;
2708 dc.SetTextForeground(m_CurrentStyleInfo.m_fg);
2709 }
2710 if(si.m_bg_valid)
2711 {
2712 m_CurrentStyleInfo.m_bg = si.m_bg;
2713 dc.SetTextBackground(m_CurrentStyleInfo.m_bg);
2714 }
2715 }
2716
2717
2718 #ifdef WXLAYOUT_DEBUG
2719
2720 void
2721 wxLayoutList::Debug(void)
2722 {
2723 WXLO_DEBUG(("Cursor is in line %d, screen pos = (%d, %d)",
2724 m_CursorLine->GetLineNumber(),
2725 m_CursorScreenPos.x, m_CursorScreenPos.y));
2726
2727 wxLayoutLine *line;
2728 for(line = m_FirstLine; line; line = line->GetNextLine())
2729 {
2730 line->Debug();
2731 }
2732 }
2733
2734 #endif
2735
2736
2737 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2738
2739 wxLayoutPrintout
2740
2741 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2742
2743 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList *llist,
2744 wxString const & title)
2745 :wxPrintout(title)
2746 {
2747 m_llist = llist;
2748 m_title = title;
2749 // remove any highlighting which could interfere with printing:
2750 m_llist->StartSelection();
2751 m_llist->EndSelection();
2752 }
2753
2754 wxLayoutPrintout::~wxLayoutPrintout()
2755 {
2756 }
2757
2758 float
2759 wxLayoutPrintout::ScaleDC(wxDC *dc)
2760 {
2761 // The following bit is taken from the printing sample, let's see
2762 // whether it works for us.
2763
2764 /* You might use THIS code to set the printer DC to ROUGHLY reflect
2765 * the screen text size. This page also draws lines of actual length 5cm
2766 * on the page.
2767 */
2768 // Get the logical pixels per inch of screen and printer
2769 int ppiScreenX, ppiScreenY;
2770 GetPPIScreen(&ppiScreenX, &ppiScreenY);
2771 int ppiPrinterX, ppiPrinterY;
2772 GetPPIPrinter(&ppiPrinterX, &ppiPrinterY);
2773
2774 if(ppiScreenX == 0) // not yet set, need to guess
2775 {
2776 ppiScreenX = 100;
2777 ppiScreenY = 100;
2778 }
2779 if(ppiPrinterX == 0) // not yet set, need to guess
2780 {
2781 ppiPrinterX = 72;
2782 ppiPrinterY = 72;
2783 }
2784
2785 // This scales the DC so that the printout roughly represents the
2786 // the screen scaling. The text point size _should_ be the right size
2787 // but in fact is too small for some reason. This is a detail that will
2788 // need to be addressed at some point but can be fudged for the
2789 // moment.
2790 float scale = (float)((float)ppiPrinterX/(float)ppiScreenX);
2791
2792 // Now we have to check in case our real page size is reduced
2793 // (e.g. because we're drawing to a print preview memory DC)
2794 int pageWidth, pageHeight;
2795 int w, h;
2796 dc->GetSize(&w, &h);
2797 GetPageSizePixels(&pageWidth, &pageHeight);
2798 if(pageWidth != 0) // doesn't work always
2799 {
2800 // If printer pageWidth == current DC width, then this doesn't
2801 // change. But w might be the preview bitmap width, so scale down.
2802 scale = scale * (float)(w/(float)pageWidth);
2803 }
2804 dc->SetUserScale(scale, scale);
2805 return scale;
2806 }
2807
2808 bool wxLayoutPrintout::OnPrintPage(int page)
2809 {
2810 wxDC *dc = GetDC();
2811
2812 ScaleDC(dc);
2813
2814 if (dc)
2815 {
2816 int top, bottom;
2817 top = (page - 1)*m_PrintoutHeight;
2818 bottom = top + m_PrintoutHeight;
2819 // SetDeviceOrigin() doesn't work here, so we need to manually
2820 // translate all coordinates.
2821 wxPoint translate(m_Offset.x,m_Offset.y-top);
2822 m_llist->Draw(*dc, translate, top, bottom);
2823 return true;
2824 }
2825 else
2826 return false;
2827 }
2828
2829 void wxLayoutPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo)
2830 {
2831 /* We allocate a temporary wxDC for printing, so that we can
2832 determine the correct paper size and scaling. We don't actually
2833 print anything on it. */
2834 #ifdef __WXMSW__
2835 wxPrinterDC psdc("","",WXLLIST_TEMPFILE,false);
2836 #else
2837 wxPostScriptDC psdc(WXLLIST_TEMPFILE,false);
2838 #endif
2839
2840 float scale = ScaleDC(&psdc);
2841
2842 psdc.GetSize(&m_PageWidth, &m_PageHeight);
2843 // This sets a left/top origin of 15% and 20%:
2844 m_Offset = wxPoint((15*m_PageWidth)/100, m_PageHeight/20);
2845
2846 // This is the length of the printable area.
2847 m_PrintoutHeight = m_PageHeight - (int) (m_PageHeight * 0.15);
2848 m_PrintoutHeight = (int)( m_PrintoutHeight / scale); // we want to use the real paper height
2849
2850
2851 m_NumOfPages = 1 +
2852 (int)( m_llist->GetSize().y / (float)(m_PrintoutHeight));
2853
2854 *minPage = 1;
2855 *maxPage = m_NumOfPages;
2856
2857 *selPageFrom = 1;
2858 *selPageTo = m_NumOfPages;
2859 wxRemoveFile(WXLLIST_TEMPFILE);
2860 }
2861
2862 bool wxLayoutPrintout::HasPage(int pageNum)
2863 {
2864 return pageNum <= m_NumOfPages;
2865 }
2866
2867 /*
2868 Stupid wxWindows doesn't draw proper ellipses, so we comment this
2869 out. It's a waste of paper anyway.
2870 */
2871 #if 0
2872 void
2873 wxLayoutPrintout::DrawHeader(wxDC &dc,
2874 wxPoint topleft, wxPoint bottomright,
2875 int pageno)
2876 {
2877 // make backups of all essential parameters
2878 const wxBrush& brush = dc.GetBrush();
2879 const wxPen& pen = dc.GetPen();
2880 const wxFont& font = dc.GetFont();
2881
2882 dc.SetBrush(*wxWHITE_BRUSH);
2883 dc.SetPen(wxPen(*wxBLACK,0,wxSOLID));
2884 dc.DrawRoundedRectangle(topleft.x,
2885 topleft.y,bottomright.x-topleft.x,
2886 bottomright.y-topleft.y);
2887 dc.SetBrush(*wxBLACK_BRUSH);
2888 wxFont myfont = wxFont((WXLO_DEFAULTFONTSIZE*12)/10,
2889 wxSWISS,wxNORMAL,wxBOLD,false,"Helvetica");
2890 dc.SetFont(myfont);
2891
2892 wxString page;
2893 page = "9999/9999 "; // many pages...
2894 long w,h;
2895 dc.GetTextExtent(page,&w,&h);
2896 page.Printf("%d/%d", pageno, (int) m_NumOfPages);
2897 dc.DrawText(page,bottomright.x-w,topleft.y+h/2);
2898 dc.GetTextExtent("XXXX", &w,&h);
2899 dc.DrawText(m_title, topleft.x+w,topleft.y+h/2);
2900
2901 // restore settings
2902 dc.SetPen(pen);
2903 dc.SetBrush(brush);
2904 dc.SetFont(font);
2905 }
2906 #endif
2907
2908
2909 wxFont &
2910 wxFontCache::GetFont(int family, int size, int style, int weight,
2911 bool underline)
2912 {
2913 for(wxFCEList::iterator i = m_FontList.begin();
2914 i != m_FontList.end(); i++)
2915 if( (**i).Matches(family, size, style, weight, underline) )
2916 return (**i).GetFont();
2917 // not found:
2918 wxFontCacheEntry *fce = new wxFontCacheEntry(family, size, style,
2919 weight, underline);
2920 m_FontList.push_back(fce);
2921 return fce->GetFont();
2922 }
2923