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