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