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