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