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