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