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