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