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