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