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