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