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