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