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