]> git.saurik.com Git - wxWidgets.git/blob - user/wxLayout/wxllist.cpp
a56aad668dba31291d8c81179b69cd5dfd87997b
[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 // doesn't help m_CursorLine.MarkDirty();
1414 m_CursorLine->RecalculatePositions(true, this); //FIXME needed?
1415 return true;
1416 }
1417
1418 bool
1419 wxLayoutList::WrapLine(CoordType column)
1420 {
1421 if(m_CursorPos.x <= column || column < 1)
1422 return false; // do nothing yet
1423 else
1424 {
1425 CoordType xpos = m_CursorLine->GetWrapPosition(column);
1426 if(xpos == -1)
1427 return false; // cannot break line
1428 //else:
1429 CoordType newpos = m_CursorPos.x - xpos - 1;
1430 m_CursorPos.x = xpos;
1431 SetUpdateRect(m_CursorScreenPos);
1432 SetUpdateRect(m_CursorScreenPos+m_CursorSize);
1433 LineBreak();
1434 Delete(1); // delete the space
1435 m_CursorPos.x = newpos;
1436 m_CursorLine->RecalculatePositions(true, this); //FIXME needed?
1437 return true;
1438 }
1439 }
1440
1441 bool
1442 wxLayoutList::Delete(CoordType npos)
1443 {
1444 wxASSERT(m_CursorLine);
1445 SetUpdateRect(m_CursorScreenPos);
1446 SetUpdateRect(m_CursorScreenPos+m_CursorSize);
1447 CoordType left;
1448 do
1449 {
1450 left = m_CursorLine->Delete(m_CursorPos.x, npos);
1451 if(left == 0)
1452 return true;
1453 // More to delete, continue on next line.
1454 // First, check if line is empty:
1455 if(m_CursorLine->GetLength() == 0)
1456 { // in this case, updating could probably be optimised
1457 #ifdef WXLO_DEBUG
1458 wxASSERT(DeleteLines(1) == 0);
1459 #else
1460 DeleteLines(1);
1461 #endif
1462
1463 left--;
1464 }
1465 else
1466 {
1467 // Need to join next line
1468 if(! m_CursorLine->GetNextLine())
1469 break; // cannot
1470 else
1471 {
1472 m_CursorLine->MergeNextLine(this);
1473 left--;
1474 }
1475 }
1476 }
1477 while(left);
1478 m_CursorLine->RecalculatePositions(true, this); //FIXME needed?
1479 return left == 0;
1480 }
1481
1482 int
1483 wxLayoutList::DeleteLines(int n)
1484 {
1485 wxASSERT(m_CursorLine);
1486 wxLayoutLine *line;
1487 SetUpdateRect(m_CursorScreenPos);
1488 SetUpdateRect(m_CursorScreenPos+m_CursorSize);
1489 while(n > 0)
1490 {
1491 if(!m_CursorLine->GetNextLine())
1492 { // we cannot delete this line, but we can clear it
1493 MoveCursorToBeginOfLine();
1494 DeleteToEndOfLine();
1495 m_CursorLine->RecalculatePositions(2, this);
1496 return n-1;
1497 }
1498 //else:
1499 line = m_CursorLine;
1500 m_CursorLine = m_CursorLine->DeleteLine(true, this);
1501 n--;
1502 if(line == m_FirstLine) m_FirstLine = m_CursorLine;
1503 wxASSERT(m_FirstLine);
1504 wxASSERT(m_CursorLine);
1505 }
1506 m_CursorLine->RecalculatePositions(2, this);
1507 return n;
1508 }
1509
1510 void
1511 wxLayoutList::Recalculate(wxDC &dc, CoordType bottom)
1512 {
1513 wxLayoutLine *line = m_FirstLine;
1514
1515 // first, make sure everything is calculated - this might not be
1516 // needed, optimise it later
1517 ApplyStyle(&m_DefaultSetting, dc);
1518 while(line)
1519 {
1520 line->RecalculatePosition(this); // so we don't need to do it all the time
1521 // little condition to speed up redrawing:
1522 if(bottom != -1 && line->GetPosition().y > bottom) break;
1523 line = line->GetNextLine();
1524 }
1525 }
1526
1527 void
1528 wxLayoutList::UpdateCursorScreenPos(wxDC &dc)
1529 {
1530 wxASSERT(m_CursorLine);
1531 m_CursorLine->Layout(dc, this, (wxPoint *)&m_CursorScreenPos, (wxPoint *)&m_CursorSize, m_CursorPos.x);
1532 }
1533
1534 wxPoint
1535 wxLayoutList::GetCursorScreenPos(wxDC &dc)
1536 {
1537 UpdateCursorScreenPos(dc);
1538 return m_CursorScreenPos;
1539 }
1540
1541 /*
1542 Is called before each Draw(). Now, it will re-layout all lines which
1543 have changed.
1544 */
1545 void
1546 wxLayoutList::Layout(wxDC &dc, CoordType bottom, bool forceAll)
1547 {
1548 wxLayoutLine *line = m_FirstLine;
1549
1550 // first, make sure everything is calculated - this might not be
1551 // needed, optimise it later
1552 ApplyStyle(&m_DefaultSetting, dc);
1553 while(line)
1554 {
1555 if(forceAll || line->IsDirty())
1556 {
1557 line->GetStyleInfo() = m_CurrentSetting;
1558 if(line == m_CursorLine)
1559 line->Layout(dc, this, (wxPoint *)&m_CursorScreenPos,
1560 (wxPoint *)&m_CursorSize, m_CursorPos.x);
1561 else
1562 line->Layout(dc, this);
1563 line->RecalculatePosition(this);
1564 // little condition to speed up redrawing:
1565 if(bottom != -1 && line->GetPosition().y > bottom) break;
1566 }
1567 line = line->GetNextLine();
1568 }
1569
1570 ///FIXME: disabled for now
1571 #if 0
1572 // can only be 0 if we are on the first line and have no next line
1573 wxASSERT(m_CursorSize.x != 0 || (m_CursorLine &&
1574 m_CursorLine->GetNextLine() == NULL &&
1575 m_CursorLine == m_FirstLine));
1576 #endif
1577 SetUpdateRect(m_CursorScreenPos);
1578 SetUpdateRect(m_CursorScreenPos+m_CursorSize);
1579 }
1580
1581 void
1582 wxLayoutList::Draw(wxDC &dc,
1583 wxPoint const &offset,
1584 CoordType top,
1585 CoordType bottom)
1586 {
1587 wxLayoutLine *line = m_FirstLine;
1588
1589 Layout(dc);
1590 ApplyStyle(&m_DefaultSetting, dc);
1591 wxBrush brush(m_CurrentSetting.m_bg, wxSOLID);
1592 dc.SetBrush(brush);
1593
1594 while(line)
1595 {
1596 // only draw if between top and bottom:
1597 if((top == -1 || line->GetPosition().y + line->GetHeight() >= top))
1598 line->Draw(dc, this, offset);
1599 // little condition to speed up redrawing:
1600 if(bottom != -1 && line->GetPosition().y > bottom) break;
1601 line = line->GetNextLine();
1602 }
1603 InvalidateUpdateRect();
1604
1605 WXLO_DEBUG(("Selection is %s : l%d,%ld/%ld,%ld",
1606 m_Selection.m_valid ? "valid" : "invalid",
1607 m_Selection.m_CursorA.x, m_Selection.m_CursorA.y,
1608 m_Selection.m_CursorB.x, m_Selection.m_CursorB.y));
1609 }
1610
1611 wxLayoutObject *
1612 wxLayoutList::FindObjectScreen(wxDC &dc, wxPoint const pos,
1613 wxPoint *cursorPos,
1614 bool *found)
1615 {
1616 // First, find the right line:
1617 wxLayoutLine *line = m_FirstLine;
1618 wxPoint p;
1619
1620 // we need to run a layout here to get font sizes right :-(
1621 ApplyStyle(&m_DefaultSetting, dc);
1622 while(line)
1623 {
1624 p = line->GetPosition();
1625 if(p.y <= pos.y && p.y+line->GetHeight() >= pos.y)
1626 break;
1627 line->Layout(dc, this);
1628 line = line->GetNextLine();
1629 }
1630 if(line == NULL)
1631 {
1632 if(found) *found = false;
1633 return NULL; // not found
1634 }
1635 if(cursorPos) cursorPos->y = line->GetLineNumber();
1636 // Now, find the object in the line:
1637 wxLOiterator i = line->FindObjectScreen(dc, pos.x,
1638 cursorPos ? & cursorPos->x : NULL ,
1639 found);
1640 return (i == NULLIT) ? NULL : *i;
1641
1642 }
1643
1644 wxPoint
1645 wxLayoutList::GetSize(void) const
1646 {
1647 wxLayoutLine
1648 *line = m_FirstLine,
1649 *last = line;
1650 if(! line)
1651 return wxPoint(0,0);
1652
1653 wxPoint maxPoint(0,0);
1654
1655 // find last line:
1656 while(line)
1657 {
1658 if(line->GetWidth() > maxPoint.x)
1659 maxPoint.x = line->GetWidth();
1660 last = line;
1661 line = line->GetNextLine();
1662 }
1663
1664 maxPoint.y = last->GetPosition().y + last->GetHeight();
1665 return maxPoint;
1666 }
1667
1668
1669 void
1670 wxLayoutList::DrawCursor(wxDC &dc, bool active, wxPoint const &translate)
1671 {
1672 wxPoint coords;
1673 coords = m_CursorScreenPos;
1674 coords.x += translate.x;
1675 coords.y += translate.y;
1676
1677 #ifdef WXLAYOUT_DEBUG
1678 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
1679 (long)m_CursorPos.x, (long)m_CursorPos.y,
1680 (long)coords.x, (long)coords.y,
1681 (long)m_CursorSize.x, (long)m_CursorSize.y,
1682 (long)m_CursorLine->GetLineNumber(),
1683 (long)m_CursorLine->GetLength()));
1684 #endif
1685
1686 dc.SetBrush(*wxBLACK_BRUSH);
1687 dc.SetLogicalFunction(wxXOR);
1688 dc.SetPen(wxPen(*wxBLACK,1,wxSOLID));
1689 if(active)
1690 {
1691 dc.DrawRectangle(coords.x, coords.y,
1692 m_CursorSize.x, m_CursorSize.y);
1693 SetUpdateRect(coords.x, coords.y);
1694 SetUpdateRect(coords.x+m_CursorSize.x, coords.y+m_CursorSize.y);
1695 }
1696 else
1697 {
1698 dc.DrawLine(coords.x, coords.y+m_CursorSize.y-1,
1699 coords.x, coords.y);
1700 SetUpdateRect(coords.x, coords.y+m_CursorSize.y-1);
1701 SetUpdateRect(coords.x, coords.y);
1702 }
1703 dc.SetLogicalFunction(wxCOPY);
1704 //dc.SetBrush(wxNullBrush);
1705 }
1706
1707 void
1708 wxLayoutList::SetUpdateRect(CoordType x, CoordType y)
1709 {
1710 if(m_UpdateRectValid)
1711 GrowRect(m_UpdateRect, x, y);
1712 else
1713 {
1714 m_UpdateRect.x = x;
1715 m_UpdateRect.y = y;
1716 m_UpdateRect.width = 4; // large enough to avoid surprises from
1717 m_UpdateRect.height = 4;// wxGTK :-)
1718 m_UpdateRectValid = true;
1719 }
1720 }
1721
1722 void
1723 wxLayoutList::StartSelection(void)
1724 {
1725 WXLO_DEBUG(("Starting selection at %ld/%ld", m_CursorPos.x, m_CursorPos.y));
1726 m_Selection.m_CursorA = m_CursorPos;
1727 m_Selection.m_CursorB = m_CursorPos;
1728 m_Selection.m_selecting = true;
1729 m_Selection.m_valid = false;
1730 }
1731
1732 void
1733 wxLayoutList::ContinueSelection(void)
1734 {
1735 wxASSERT(m_Selection.m_selecting == true);
1736 wxASSERT(m_Selection.m_valid == false);
1737 WXLO_DEBUG(("Continuing selection at %ld/%ld", m_CursorPos.x, m_CursorPos.y));
1738 m_Selection.m_CursorB = m_CursorPos;
1739 // We always want m_CursorA <= m_CursorB!
1740 if(! (m_Selection.m_CursorA <= m_Selection.m_CursorB))
1741 {
1742 wxPoint help = m_Selection.m_CursorB;
1743 m_Selection.m_CursorB = m_Selection.m_CursorA;
1744 m_Selection.m_CursorA = help;
1745 }
1746 }
1747
1748 void
1749 wxLayoutList::EndSelection(void)
1750 {
1751 ContinueSelection();
1752 WXLO_DEBUG(("Ending selection at %ld/%ld", m_CursorPos.x, m_CursorPos.y));
1753 m_Selection.m_selecting = false;
1754 m_Selection.m_valid = true;
1755 }
1756
1757
1758 bool
1759 wxLayoutList::IsSelecting(void)
1760 {
1761 return m_Selection.m_selecting;
1762 }
1763
1764 bool
1765 wxLayoutList::IsSelected(const wxPoint &cursor)
1766 {
1767 if(! m_Selection.m_valid && ! m_Selection.m_selecting)
1768 return false;
1769 return m_Selection.m_CursorA <= cursor
1770 && cursor <= m_Selection.m_CursorB;
1771 }
1772
1773
1774 /** Tests whether this layout line is selected and needs
1775 highlighting.
1776 @param line to test for
1777 @return 0 = not selected, 1 = fully selected, -1 = partially
1778 selected
1779 */
1780 int
1781 wxLayoutList::IsSelected(const wxLayoutLine *line, CoordType *from,
1782 CoordType *to)
1783 {
1784 wxASSERT(line); wxASSERT(to); wxASSERT(from);
1785
1786 if(! m_Selection.m_valid && ! m_Selection.m_selecting)
1787 return 0;
1788
1789 CoordType y = line->GetLineNumber();
1790 if(m_Selection.m_CursorA.y < y && m_Selection.m_CursorB.y > y)
1791 return 1;
1792 else if(m_Selection.m_CursorA.y == y)
1793 {
1794 *from = m_Selection.m_CursorA.x;
1795 if(m_Selection.m_CursorB.y == y)
1796 *to = m_Selection.m_CursorB.x;
1797 else
1798 *to = line->GetLength();
1799 return -1;
1800 }
1801 else if(m_Selection.m_CursorB.y == y)
1802 {
1803 *to = m_Selection.m_CursorB.x;
1804 if(m_Selection.m_CursorA.y == y)
1805 *from = m_Selection.m_CursorA.x;
1806 else
1807 *from = 0;
1808 return -1;
1809 }
1810 else
1811 return 0;
1812 }
1813
1814 void
1815 wxLayoutList::DeleteSelection(void)
1816 {
1817 if(! m_Selection.m_valid)
1818 return;
1819
1820 m_Selection.m_valid = false;
1821
1822 // Only delete part of the current line?
1823 if(m_Selection.m_CursorA.y == m_Selection.m_CursorB.y)
1824 {
1825 MoveCursorTo(m_Selection.m_CursorA);
1826 Delete(m_Selection.m_CursorB.x - m_Selection.m_CursorA.x);
1827 return;
1828 }
1829
1830
1831 wxLayoutLine
1832 * firstLine = NULL,
1833 * lastLine = NULL;
1834
1835 for(firstLine = m_FirstLine;
1836 firstLine && firstLine->GetLineNumber() < m_Selection.m_CursorA.y;
1837 firstLine=firstLine->GetNextLine())
1838 ;
1839 if(!firstLine || firstLine->GetLineNumber() != m_Selection.m_CursorA.y)
1840 return;
1841
1842
1843 for(lastLine = m_FirstLine;
1844 lastLine && lastLine->GetLineNumber() < m_Selection.m_CursorB.y;
1845 lastLine=lastLine->GetNextLine())
1846 ;
1847 if(!lastLine || lastLine->GetLineNumber() != m_Selection.m_CursorB.y)
1848 return;
1849
1850
1851 // We now know that the two lines are different:
1852
1853 // First, delete what's left of this line:
1854 MoveCursorTo(m_Selection.m_CursorA);
1855 DeleteToEndOfLine();
1856
1857 wxLayoutLine *nextLine = firstLine->GetNextLine();
1858 while(nextLine && nextLine != lastLine)
1859 nextLine = nextLine->DeleteLine(false, this);
1860
1861 // Now nextLine = lastLine;
1862 Delete(1); // This joins firstLine and nextLine
1863 Delete(m_Selection.m_CursorB.x); // This deletes the first x
1864 // positions
1865
1866 /// Recalculate:
1867 firstLine->RecalculatePositions(1, this);
1868 }
1869
1870 /// Starts highlighting the selection
1871 void
1872 wxLayoutList::StartHighlighting(wxDC &dc)
1873 {
1874 #if SHOW_SELECTIONS
1875 dc.SetTextForeground(m_CurrentSetting.m_bg);
1876 dc.SetTextBackground(m_CurrentSetting.m_fg);
1877 #endif
1878 }
1879
1880 /// Ends highlighting the selection
1881 void
1882 wxLayoutList::EndHighlighting(wxDC &dc)
1883 {
1884 #if SHOW_SELECTIONS
1885 dc.SetTextForeground(m_CurrentSetting.m_fg);
1886 dc.SetTextBackground(m_CurrentSetting.m_bg);
1887 #endif
1888 }
1889
1890
1891 wxLayoutList *
1892 wxLayoutList::Copy(const wxPoint &from,
1893 const wxPoint &to)
1894 {
1895 wxLayoutLine
1896 * firstLine = NULL,
1897 * lastLine = NULL;
1898
1899 for(firstLine = m_FirstLine;
1900 firstLine && firstLine->GetLineNumber() < from.y;
1901 firstLine=firstLine->GetNextLine())
1902 ;
1903 if(!firstLine || firstLine->GetLineNumber() != from.y)
1904 return NULL;
1905
1906 for(lastLine = m_FirstLine;
1907 lastLine && lastLine->GetLineNumber() < to.y;
1908 lastLine=lastLine->GetNextLine())
1909 ;
1910 if(!lastLine || lastLine->GetLineNumber() != to.y)
1911 return NULL;
1912
1913 if(to <= from)
1914 {
1915 wxLayoutLine *tmp = firstLine;
1916 firstLine = lastLine;
1917 lastLine = tmp;
1918 }
1919
1920 wxLayoutList *llist = new wxLayoutList();
1921
1922 if(firstLine == lastLine)
1923 {
1924 firstLine->Copy(llist, from.x, to.x);
1925 }
1926 else
1927 {
1928 // Extract objects from first line
1929 firstLine->Copy(llist, from.x);
1930 llist->LineBreak();
1931 // Extract all lines between
1932 for(wxLayoutLine *line = firstLine->GetNextLine();
1933 line != lastLine;
1934 line = line->GetNextLine())
1935 {
1936 line->Copy(llist);
1937 llist->LineBreak();
1938 }
1939 // Extract objects from last line
1940 lastLine->Copy(llist, 0, to.x);
1941 }
1942 return llist;
1943 }
1944
1945 wxLayoutList *
1946 wxLayoutList::GetSelection(void)
1947 {
1948 if(! m_Selection.m_valid)
1949 {
1950 if(m_Selection.m_selecting)
1951 EndSelection();
1952 else
1953 return NULL;
1954 }
1955
1956 m_Selection.m_valid = false;
1957 return Copy( m_Selection.m_CursorA, m_Selection.m_CursorB );
1958 }
1959
1960
1961
1962 #define COPY_SI(what) if(si->what != -1) { m_CurrentSetting.what = si->what; fontChanged = TRUE; }
1963
1964 void
1965 wxLayoutList::ApplyStyle(wxLayoutStyleInfo *si, wxDC &dc)
1966 {
1967 bool fontChanged = FALSE;
1968 COPY_SI(family);
1969 COPY_SI(size);
1970 COPY_SI(style);
1971 COPY_SI(weight);
1972 COPY_SI(underline);
1973 if(fontChanged)
1974 dc.SetFont( m_FontCache.GetFont(m_CurrentSetting) );
1975
1976 if(si->m_fg_valid)
1977 {
1978 m_CurrentSetting.m_fg = si->m_fg;
1979 dc.SetTextForeground(m_CurrentSetting.m_fg);
1980 }
1981 if(si->m_bg_valid)
1982 {
1983 m_CurrentSetting.m_bg = si->m_bg;
1984 dc.SetTextBackground(m_CurrentSetting.m_bg);
1985 }
1986 }
1987
1988
1989 #ifdef WXLAYOUT_DEBUG
1990
1991 void
1992 wxLayoutList::Debug(void)
1993 {
1994 wxLayoutLine *line;
1995
1996
1997 for(line = m_FirstLine;
1998 line;
1999 line = line->GetNextLine())
2000 line->Debug();
2001 }
2002
2003 #endif
2004
2005
2006 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2007
2008 wxLayoutPrintout
2009
2010 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011
2012 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList *llist,
2013 wxString const & title)
2014 :wxPrintout(title)
2015 {
2016 m_llist = llist;
2017 m_title = title;
2018 }
2019
2020 wxLayoutPrintout::~wxLayoutPrintout()
2021 {
2022 }
2023
2024 float
2025 wxLayoutPrintout::ScaleDC(wxDC *dc)
2026 {
2027 // The following bit is taken from the printing sample, let's see
2028 // whether it works for us.
2029
2030 /* You might use THIS code to set the printer DC to ROUGHLY reflect
2031 * the screen text size. This page also draws lines of actual length 5cm
2032 * on the page.
2033 */
2034 // Get the logical pixels per inch of screen and printer
2035 int ppiScreenX, ppiScreenY;
2036 GetPPIScreen(&ppiScreenX, &ppiScreenY);
2037 int ppiPrinterX, ppiPrinterY;
2038 GetPPIPrinter(&ppiPrinterX, &ppiPrinterY);
2039
2040 if(ppiScreenX == 0) // not yet set, need to guess
2041 {
2042 ppiScreenX = 100;
2043 ppiScreenY = 100;
2044 }
2045 if(ppiPrinterX == 0) // not yet set, need to guess
2046 {
2047 ppiPrinterX = 72;
2048 ppiPrinterY = 72;
2049 }
2050
2051 // This scales the DC so that the printout roughly represents the
2052 // the screen scaling. The text point size _should_ be the right size
2053 // but in fact is too small for some reason. This is a detail that will
2054 // need to be addressed at some point but can be fudged for the
2055 // moment.
2056 float scale = (float)((float)ppiPrinterX/(float)ppiScreenX);
2057
2058 // Now we have to check in case our real page size is reduced
2059 // (e.g. because we're drawing to a print preview memory DC)
2060 int pageWidth, pageHeight;
2061 int w, h;
2062 dc->GetSize(&w, &h);
2063 GetPageSizePixels(&pageWidth, &pageHeight);
2064 if(pageWidth != 0) // doesn't work always
2065 {
2066 // If printer pageWidth == current DC width, then this doesn't
2067 // change. But w might be the preview bitmap width, so scale down.
2068 scale = scale * (float)(w/(float)pageWidth);
2069 }
2070 dc->SetUserScale(scale, scale);
2071 return scale;
2072 }
2073
2074 bool wxLayoutPrintout::OnPrintPage(int page)
2075 {
2076 wxDC *dc = GetDC();
2077
2078 ScaleDC(dc);
2079
2080 if (dc)
2081 {
2082 int top, bottom;
2083 top = (page - 1)*m_PrintoutHeight;
2084 bottom = top + m_PrintoutHeight;
2085 // SetDeviceOrigin() doesn't work here, so we need to manually
2086 // translate all coordinates.
2087 wxPoint translate(m_Offset.x,m_Offset.y-top);
2088 m_llist->Draw(*dc, translate, top, bottom);
2089 return true;
2090 }
2091 else
2092 return false;
2093 }
2094
2095 void wxLayoutPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo)
2096 {
2097 /* We allocate a temporary wxDC for printing, so that we can
2098 determine the correct paper size and scaling. We don't actually
2099 print anything on it. */
2100 #ifdef __WXMSW__
2101 wxPrinterDC psdc("","",WXLLIST_TEMPFILE,false);
2102 #else
2103 wxPostScriptDC psdc(WXLLIST_TEMPFILE,false);
2104 #endif
2105
2106 float scale = ScaleDC(&psdc);
2107
2108 psdc.GetSize(&m_PageWidth, &m_PageHeight);
2109 // This sets a left/top origin of 15% and 20%:
2110 m_Offset = wxPoint((15*m_PageWidth)/100, m_PageHeight/20);
2111
2112 // This is the length of the printable area.
2113 m_PrintoutHeight = m_PageHeight - (int) (m_PageHeight * 0.15);
2114 m_PrintoutHeight = (int)( m_PrintoutHeight / scale); // we want to use the real paper height
2115
2116
2117 m_NumOfPages = 1 +
2118 (int)( m_llist->GetSize().y / (float)(m_PrintoutHeight));
2119
2120 *minPage = 1;
2121 *maxPage = m_NumOfPages;
2122
2123 *selPageFrom = 1;
2124 *selPageTo = m_NumOfPages;
2125 wxRemoveFile(WXLLIST_TEMPFILE);
2126 }
2127
2128 bool wxLayoutPrintout::HasPage(int pageNum)
2129 {
2130 return pageNum <= m_NumOfPages;
2131 }
2132
2133 /*
2134 Stupid wxWindows doesn't draw proper ellipses, so we comment this
2135 out. It's a waste of paper anyway.
2136 */
2137 #if 0
2138 void
2139 wxLayoutPrintout::DrawHeader(wxDC &dc,
2140 wxPoint topleft, wxPoint bottomright,
2141 int pageno)
2142 {
2143 // make backups of all essential parameters
2144 const wxBrush& brush = dc.GetBrush();
2145 const wxPen& pen = dc.GetPen();
2146 const wxFont& font = dc.GetFont();
2147
2148 dc.SetBrush(*wxWHITE_BRUSH);
2149 dc.SetPen(wxPen(*wxBLACK,0,wxSOLID));
2150 dc.DrawRoundedRectangle(topleft.x,
2151 topleft.y,bottomright.x-topleft.x,
2152 bottomright.y-topleft.y);
2153 dc.SetBrush(*wxBLACK_BRUSH);
2154 wxFont myfont = wxFont((WXLO_DEFAULTFONTSIZE*12)/10,
2155 wxSWISS,wxNORMAL,wxBOLD,false,"Helvetica");
2156 dc.SetFont(myfont);
2157
2158 wxString page;
2159 page = "9999/9999 "; // many pages...
2160 long w,h;
2161 dc.GetTextExtent(page,&w,&h);
2162 page.Printf("%d/%d", pageno, (int) m_NumOfPages);
2163 dc.DrawText(page,bottomright.x-w,topleft.y+h/2);
2164 dc.GetTextExtent("XXXX", &w,&h);
2165 dc.DrawText(m_title, topleft.x+w,topleft.y+h/2);
2166
2167 // restore settings
2168 dc.SetPen(pen);
2169 dc.SetBrush(brush);
2170 dc.SetFont(font);
2171 }
2172 #endif
2173
2174
2175 wxFont &
2176 wxFontCache::GetFont(int family, int size, int style, int weight,
2177 bool underline)
2178 {
2179 for(wxFCEList::iterator i = m_FontList.begin();
2180 i != m_FontList.end(); i++)
2181 if( (**i).Matches(family, size, style, weight, underline) )
2182 return (**i).GetFont();
2183 // not found:
2184 wxFontCacheEntry *fce = new wxFontCacheEntry(family, size, style,
2185 weight, underline);
2186 m_FontList.push_back(fce);
2187 return fce->GetFont();
2188 }
2189