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