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