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