]> git.saurik.com Git - wxWidgets.git/blob - user/wxLayout/wxllist.cpp
repositioning the cursor with mouse clicks works
[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 <wx/wxprec.h>
18
19 #ifdef __BORLANDC__
20 # pragma hdrstop
21 #endif
22
23 #include "Mpch.h"
24
25 #ifdef M_BASEDIR
26 # include "gui/wxllist.h"
27 # include "gui/wxlparser.h"
28 # define SHOW_SELECTIONS 1
29 #else
30 # include "wxllist.h"
31 # include "wxlparser.h"
32 # define SHOW_SELECTIONS 1
33 #endif
34
35 #ifndef USE_PCH
36 # include <iostream.h>
37
38 # include <wx/dc.h>
39 # include <wx/dcps.h>
40 # include <wx/print.h>
41 # include <wx/log.h>
42 # include <wx/filefn.h>
43 #endif
44
45 #ifdef WXLAYOUT_USE_CARET
46 # include <wx/caret.h>
47 #endif // WXLAYOUT_USE_CARET
48
49 #include <ctype.h>
50
51 /// This should never really get created
52 #define WXLLIST_TEMPFILE "__wxllist.tmp"
53
54 #ifdef WXLAYOUT_DEBUG
55
56 # define TypeString(t) g_aTypeStrings[t]
57 # define WXLO_DEBUG(x) wxLogDebug x
58
59 static const char *g_aTypeStrings[] =
60 {
61 "invalid", "text", "cmd", "icon"
62 };
63 void
64 wxLayoutObject::Debug(void)
65 {
66 WXLO_DEBUG(("%s",g_aTypeStrings[GetType()]));
67 }
68 #else
69 # define TypeString(t) ""
70 # define WXLO_DEBUG(x)
71 #endif
72
73 // FIXME under MSW, this constant is needed to make the thing properly redraw
74 // itself - I don't know where the size calculation error is and I can't
75 // waste time looking for it right now. Search for occurences of
76 // MSW_CORRECTION to find all the places where I did it.
77 #ifdef __WXMSW__
78 static const int MSW_CORRECTION = 10;
79 #else
80 static const int MSW_CORRECTION = 0;
81 #endif
82
83 /// Cursors smaller than this disappear in XOR drawing mode
84 #define WXLO_MINIMUM_CURSOR_WIDTH 4
85
86 /// Use this character to estimate a cursor size when none is available.
87 #define WXLO_CURSORCHAR "E"
88 /** @name Helper functions */
89 //@{
90 /// allows me to compare to wxPoints
91 bool operator <=(wxPoint const &p1, wxPoint const &p2)
92 {
93 return p1.y < p2.y || (p1.y == p2.y && p1.x <= p2.x);
94 }
95
96 /// grows a wxRect so that it includes the given point
97
98 static
99 void GrowRect(wxRect &r, CoordType x, CoordType y)
100 {
101 if(r.x > x)
102 r.x = x;
103 else if(r.x + r.width < x)
104 r.width = x - r.x;
105
106 if(r.y > y)
107 r.y = y;
108 else if(r.y + r.height < y)
109 r.height = y - r.y;
110 }
111
112 #if 0
113 // unused
114 /// returns true if the point is in the rectangle
115 static
116 bool Contains(const wxRect &r, const wxPoint &p)
117 {
118 return r.x <= p.x && r.y <= p.y && (r.x+r.width) >= p.x && (r.y + r.height) >= p.y;
119 }
120 #endif
121
122
123 //@}
124
125
126 void ReadString(wxString &to, wxString &from)
127 {
128 to = "";
129 const char *cptr = from.c_str();
130 while(*cptr && *cptr != '\n')
131 to += *cptr++;
132 if(*cptr) cptr++;
133 from = cptr;
134 }
135
136 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
137
138 wxLayoutObject
139
140 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
141
142 /* static */
143 wxLayoutObject *
144 wxLayoutObject::Read(wxString &istr)
145 {
146 wxString tmp;
147 ReadString(tmp, istr);
148 int type = -1;
149 sscanf(tmp.c_str(),"%d", &type);
150
151 switch(type)
152 {
153 case WXLO_TYPE_TEXT:
154 return wxLayoutObjectText::Read(istr);
155 case WXLO_TYPE_CMD:
156 return wxLayoutObjectCmd::Read(istr);
157 case WXLO_TYPE_ICON:
158 return wxLayoutObjectIcon::Read(istr);
159 default:
160 return NULL;
161 }
162 }
163
164 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
165
166 wxLayoutObjectText
167
168 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
169
170 wxLayoutObjectText::wxLayoutObjectText(const wxString &txt)
171 {
172 m_Text = txt;
173 m_Width = 0;
174 m_Height = 0;
175 m_Top = 0;
176 m_Bottom = 0;
177 }
178
179 wxLayoutObject *
180 wxLayoutObjectText::Copy(void)
181 {
182 wxLayoutObjectText *obj = new wxLayoutObjectText(m_Text);
183 obj->m_Width = m_Width;
184 obj->m_Height = m_Height;
185 obj->m_Top = m_Top;
186 obj->m_Bottom = m_Bottom;
187 obj->SetUserData(m_UserData);
188 return obj;
189 }
190
191
192 void
193 wxLayoutObjectText::Write(wxString &ostr)
194 {
195 ostr << (int) WXLO_TYPE_TEXT << '\n'
196 << m_Text << '\n';
197 }
198 /* static */
199 wxLayoutObjectText *
200 wxLayoutObjectText::Read(wxString &istr)
201 {
202 wxString text;
203 ReadString(text, istr);
204
205 return new wxLayoutObjectText(text);
206 }
207
208 wxPoint
209 wxLayoutObjectText::GetSize(CoordType *top, CoordType *bottom) const
210 {
211
212 *top = m_Top; *bottom = m_Bottom;
213 return wxPoint(m_Width, m_Height);
214 }
215
216 void
217 wxLayoutObjectText::Draw(wxDC &dc, wxPoint const &coords,
218 wxLayoutList *wxllist,
219 CoordType begin, CoordType end)
220 {
221 if( end <= 0)
222 dc.DrawText(m_Text, coords.x, coords.y-m_Top);
223 else
224 {
225 // highlight the bit between begin and len
226 wxString str;
227 CoordType
228 xpos = coords.x,
229 ypos = coords.y-m_Top;
230 long width, height, descent;
231
232 if(begin < 0) begin = 0;
233 if( end > (signed)m_Text.Length() )
234 end = m_Text.Length();
235
236 str = m_Text.Mid(0, begin);
237 dc.DrawText(str, xpos, ypos);
238 dc.GetTextExtent(str, &width, &height, &descent);
239 xpos += width;
240 wxllist->StartHighlighting(dc);
241 str = m_Text.Mid(begin, end-begin);
242 dc.DrawText(str, xpos, ypos);
243 dc.GetTextExtent(str, &width, &height, &descent);
244 xpos += width;
245 wxllist->EndHighlighting(dc);
246 str = m_Text.Mid(end, m_Text.Length()-end);
247 dc.DrawText(str, xpos, ypos);
248 }
249 }
250
251 CoordType
252 wxLayoutObjectText::GetOffsetScreen(wxDC &dc, CoordType xpos) const
253 {
254 CoordType
255 offs = 1,
256 maxlen = m_Text.Length();
257 long
258 width = 0,
259 height, descent = 0l;
260
261 if(xpos == 0) return 0; // easy
262
263 while(width < xpos && offs < maxlen)
264 {
265 dc.GetTextExtent(m_Text.substr(0,offs),
266 &width, &height, &descent);
267 offs++;
268 }
269 /* We have to substract 1 to compensate for the offs++, and another
270 one because we don't want to position the cursor behind the
271 object what we clicked on, but before - otherwise it looks
272 funny. */
273 return (xpos > 2) ? offs-2 : 0;
274 }
275
276 void
277 wxLayoutObjectText::Layout(wxDC &dc, class wxLayoutList *llist)
278 {
279 long descent = 0l;
280
281 // now this is done in wxLayoutLine::Layout(), but this code might be
282 // reenabled later - in principle, it's more efficient
283 #if 0
284 CoordType widthOld = m_Width,
285 heightOld = m_Height;
286 #endif // 0
287
288 dc.GetTextExtent(m_Text, &m_Width, &m_Height, &descent);
289
290 #if 0
291 if ( widthOld != m_Width || heightOld != m_Height )
292 {
293 // as the text length changed, it must be refreshed
294 wxLayoutLine *line = GetLine();
295
296 wxCHECK_RET( line, "wxLayoutObjectText can't refresh itself" );
297
298 // as our size changed, we need to repaint the part which was appended
299 wxPoint position(line->GetPosition());
300
301 // this is not the most efficient way (we repaint the whole line), but
302 // it's not too slow and is *simple*
303 if ( widthOld < m_Width )
304 widthOld = m_Width;
305 if ( heightOld < m_Height )
306 heightOld = m_Height;
307
308 llist->SetUpdateRect(position.x + widthOld + MSW_CORRECTION,
309 position.y + heightOld + MSW_CORRECTION);
310 }
311 #endif // 0
312
313 m_Bottom = descent;
314 m_Top = m_Height - m_Bottom;
315 }
316
317
318 #ifdef WXLAYOUT_DEBUG
319 void
320 wxLayoutObjectText::Debug(void)
321 {
322 wxLayoutObject::Debug();
323 WXLO_DEBUG((" `%s`", m_Text.c_str()));
324 }
325 #endif
326
327 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
328
329 wxLayoutObjectIcon
330
331 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
332
333 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap const &icon)
334 {
335 m_Icon = new wxBitmap(icon);
336 }
337
338
339 void
340 wxLayoutObjectIcon::Write(wxString &ostr)
341 {
342 /* Exports icon through a temporary file. */
343
344 wxString file = wxGetTempFileName("wxloexport");
345
346 ostr << WXLO_TYPE_ICON << '\n'
347 << file << '\n';
348 m_Icon->SaveFile(file, WXLO_BITMAP_FORMAT);
349 }
350 /* static */
351 wxLayoutObjectIcon *
352 wxLayoutObjectIcon::Read(wxString &istr)
353 {
354 wxString file;
355 ReadString(file, istr);
356
357 if(! wxFileExists(file))
358 return NULL;
359 wxLayoutObjectIcon *obj = new wxLayoutObjectIcon;
360
361 if(!obj->m_Icon->LoadFile(file, WXLO_BITMAP_FORMAT))
362 {
363 delete obj;
364 return NULL;
365 }
366 else
367 return obj;
368 }
369
370 wxLayoutObject *
371 wxLayoutObjectIcon::Copy(void)
372 {
373 wxLayoutObjectIcon *obj = new wxLayoutObjectIcon(new
374 wxBitmap(*m_Icon));
375 obj->SetUserData(m_UserData);
376 return obj;
377 }
378
379 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap *icon)
380 {
381 m_Icon = icon;
382 }
383
384 void
385 wxLayoutObjectIcon::Draw(wxDC &dc, wxPoint const &coords,
386 wxLayoutList *wxllist,
387 CoordType begin, CoordType /* len */)
388 {
389 dc.DrawBitmap(*m_Icon, coords.x, coords.y-m_Icon->GetHeight(),
390 (m_Icon->GetMask() == NULL) ? FALSE : TRUE);
391 }
392
393 void
394 wxLayoutObjectIcon::Layout(wxDC & /* dc */, class wxLayoutList * )
395 {
396 }
397
398 wxPoint
399 wxLayoutObjectIcon::GetSize(CoordType *top, CoordType *bottom) const
400 {
401 *top = m_Icon->GetHeight();
402 *bottom = 0;
403 return wxPoint(m_Icon->GetWidth(), m_Icon->GetHeight());
404 }
405
406
407
408 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
409
410 wxLayoutObjectCmd
411
412 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
413
414
415 wxLayoutStyleInfo::wxLayoutStyleInfo(int ifamily,
416 int isize,
417 int istyle,
418 int iweight,
419 int iul,
420 wxColour *fg,
421 wxColour *bg)
422 {
423 family = ifamily; size = isize;
424 style = istyle; weight = iweight;
425 underline = iul != 0;
426 if(fg)
427 {
428 m_fg = *fg;
429 m_fg_valid = TRUE;
430 }
431 else
432 m_fg = *wxBLACK;
433 if(bg)
434 {
435 m_bg = *bg;
436 m_bg_valid = TRUE;
437 }
438 else
439 m_bg = *wxWHITE;
440 }
441
442 #define COPY_SI_(what) if(right.what != -1) what = right.what;
443
444 wxLayoutStyleInfo &
445 wxLayoutStyleInfo::operator=(const wxLayoutStyleInfo &right)
446 {
447 COPY_SI_(family);
448 COPY_SI_(style);
449 COPY_SI_(size);
450 COPY_SI_(weight);
451 COPY_SI_(underline);
452 if(right.m_fg_valid) m_fg = right.m_fg;
453 if(right.m_bg_valid) m_bg = right.m_bg;
454 return *this;
455 }
456
457 wxLayoutObjectCmd::wxLayoutObjectCmd(int family, int size, int style, int
458 weight, int underline,
459 wxColour *fg, wxColour *bg)
460
461 {
462 m_StyleInfo = new wxLayoutStyleInfo(family, size,style,weight,underline,fg,bg);
463 }
464
465 wxLayoutObject *
466 wxLayoutObjectCmd::Copy(void)
467 {
468 wxLayoutObjectCmd *obj = new wxLayoutObjectCmd(
469 m_StyleInfo->size,
470 m_StyleInfo->family,
471 m_StyleInfo->style,
472 m_StyleInfo->weight,
473 m_StyleInfo->underline,
474 m_StyleInfo->m_fg_valid ?
475 &m_StyleInfo->m_fg : NULL,
476 m_StyleInfo->m_bg_valid ?
477 &m_StyleInfo->m_bg : NULL);
478 obj->SetUserData(m_UserData);
479 return obj;
480 }
481
482 void
483 wxLayoutObjectCmd::Write(wxString &ostr)
484 {
485 ostr << WXLO_TYPE_CMD << '\n'
486 << m_StyleInfo->size << '\n'
487 << m_StyleInfo->family << '\n'
488 << m_StyleInfo->style << '\n'
489 << m_StyleInfo->weight << '\n'
490 << m_StyleInfo->underline << '\n'
491 << m_StyleInfo->m_fg_valid << '\n'
492 << m_StyleInfo->m_bg_valid << '\n';
493 if(m_StyleInfo->m_fg_valid)
494 {
495 ostr << m_StyleInfo->m_fg.Red() << '\n'
496 << m_StyleInfo->m_fg.Green() << '\n'
497 << m_StyleInfo->m_fg.Blue() << '\n';
498 }
499 if(m_StyleInfo->m_bg_valid)
500 {
501 ostr << m_StyleInfo->m_bg.Red() << '\n'
502 << m_StyleInfo->m_bg.Green() << '\n'
503 << m_StyleInfo->m_bg.Blue() << '\n';
504 }
505 }
506 /* static */
507 wxLayoutObjectCmd *
508 wxLayoutObjectCmd::Read(wxString &istr)
509 {
510 wxLayoutObjectCmd *obj = new wxLayoutObjectCmd;
511
512 wxString tmp;
513 ReadString(tmp, istr);
514 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->size);
515 ReadString(tmp, istr);
516 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->family);
517 ReadString(tmp, istr);
518 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->style);
519 ReadString(tmp, istr);
520 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->weight);
521 ReadString(tmp, istr);
522 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->underline);
523 ReadString(tmp, istr);
524 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->m_fg_valid);
525 ReadString(tmp, istr);
526 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->m_bg_valid);
527 if(obj->m_StyleInfo->m_fg_valid)
528 {
529 int red, green, blue;
530 ReadString(tmp, istr);
531 sscanf(tmp.c_str(),"%d", &red);
532 ReadString(tmp, istr);
533 sscanf(tmp.c_str(),"%d", &green);
534 ReadString(tmp, istr);
535 sscanf(tmp.c_str(),"%d", &blue);
536 obj->m_StyleInfo->m_fg = wxColour(red, green, blue);
537 }
538 if(obj->m_StyleInfo->m_bg_valid)
539 {
540 int red, green, blue;
541 ReadString(tmp, istr);
542 sscanf(tmp.c_str(),"%d", &red);
543 ReadString(tmp, istr);
544 sscanf(tmp.c_str(),"%d", &green);
545 ReadString(tmp, istr);
546 sscanf(tmp.c_str(),"%d", &blue);
547 obj->m_StyleInfo->m_bg = wxColour(red, green, blue);
548 }
549 return obj;
550 }
551
552
553 wxLayoutObjectCmd::~wxLayoutObjectCmd()
554 {
555 delete m_StyleInfo;
556 }
557
558 wxLayoutStyleInfo *
559 wxLayoutObjectCmd::GetStyle(void) const
560 {
561 return m_StyleInfo;
562 }
563
564 void
565 wxLayoutObjectCmd::Draw(wxDC &dc, wxPoint const & /* coords */,
566 wxLayoutList *wxllist,
567 CoordType begin, CoordType /* len */)
568 {
569 wxASSERT(m_StyleInfo);
570 wxllist->ApplyStyle(m_StyleInfo, dc);
571 }
572
573 void
574 wxLayoutObjectCmd::Layout(wxDC &dc, class wxLayoutList * llist)
575 {
576 // this get called, so that recalculation uses right font sizes
577 Draw(dc, wxPoint(0,0), llist);
578 }
579
580
581 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
582
583 The wxLayoutLine object
584
585 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
586
587 wxLayoutLine::wxLayoutLine(wxLayoutLine *prev, wxLayoutList *llist)
588 {
589 m_LineNumber = 0;
590 m_Width = m_Height = 0;
591 m_Length = 0;
592 MarkDirty(0);
593 m_Previous = prev;
594 m_Next = NULL;
595 RecalculatePosition(llist);
596 if(m_Previous)
597 {
598 m_LineNumber = m_Previous->GetLineNumber()+1;
599 m_Next = m_Previous->GetNextLine();
600 m_Previous->m_Next = this;
601 }
602 if(m_Next)
603 {
604 m_Next->m_Previous = this;
605 m_Next->MoveLines(+1);
606 m_Next->RecalculatePositions(1,llist);
607 }
608 }
609
610 wxLayoutLine::~wxLayoutLine()
611 {
612 // kbList cleans itself
613 }
614
615 wxPoint
616 wxLayoutLine::RecalculatePosition(wxLayoutList *llist)
617 {
618 wxASSERT(m_Previous || GetLineNumber() == 0);
619
620 wxPoint posOld(m_Position);
621
622 if(m_Previous)
623 {
624 m_Position = m_Previous->GetPosition();
625 m_Position.y += m_Previous->GetHeight();
626 }
627 else
628 m_Position = wxPoint(0,0);
629
630 if ( m_Position != posOld )
631 {
632 // the whole line moved and must be repainted
633 llist->SetUpdateRect(m_Position);
634 llist->SetUpdateRect(m_Position.x + GetWidth() + MSW_CORRECTION,
635 m_Position.y + GetHeight() + MSW_CORRECTION);
636 llist->SetUpdateRect(posOld);
637 llist->SetUpdateRect(posOld.x + GetWidth() + MSW_CORRECTION,
638 posOld.y + GetHeight() + MSW_CORRECTION);
639 }
640
641 return m_Position;
642 }
643
644 void
645 wxLayoutLine::RecalculatePositions(int recurse, wxLayoutList *llist)
646 {
647 //FIXME: is this really needed? We run Layout() anyway.
648 // Recursing here, drives computation time up exponentially, as
649 // each line will cause all following lines to be recalculated.
650 // Yes, or linenumbers go wrong.
651
652 wxASSERT(recurse >= 0);
653 wxPoint pos = m_Position;
654 CoordType height = m_Height;
655
656 // WXLO_TRACE("RecalculatePositions()");
657 RecalculatePosition(llist);
658 if(m_Next)
659 {
660 if(recurse > 0)
661 m_Next->RecalculatePositions(--recurse, llist);
662 else if(pos != m_Position || m_Height != height)
663 m_Next->RecalculatePositions(0, llist);
664 }
665 }
666
667 wxLayoutObjectList::iterator
668 wxLayoutLine::FindObject(CoordType xpos, CoordType *offset) const
669 {
670 wxASSERT(xpos >= 0);
671 wxASSERT(offset);
672 wxLayoutObjectList::iterator
673 i,
674 found = NULLIT;
675 CoordType x = 0, len;
676
677 /* We search through the objects. As we don't like returning the
678 object that the cursor is behind, we just remember such an
679 object in "found" so we can return it if there is really no
680 further object following it. */
681 for(i = m_ObjectList.begin(); i != NULLIT; i++)
682 {
683 len = (**i).GetLength();
684 if( x <= xpos && xpos <= x + len )
685 {
686 *offset = xpos-x;
687 if(xpos == x + len) // is there another object behind?
688 found = i;
689 else // we are really inside this object
690 return i;
691 }
692 x += (**i).GetLength();
693 }
694 return found; // ==NULL if really none found
695 }
696
697 wxLayoutObjectList::iterator
698 wxLayoutLine::FindObjectScreen(wxDC &dc,
699 CoordType xpos, CoordType *cxpos,
700 bool *found) const
701 {
702 wxASSERT(cxpos);
703 wxASSERT(cxpos);
704 wxLayoutObjectList::iterator i;
705 CoordType x = 0, cx = 0, width;
706
707 for(i = m_ObjectList.begin(); i != NULLIT; i++)
708 {
709 width = (**i).GetWidth();
710 if( x <= xpos && xpos <= x + width )
711 {
712 *cxpos = cx + (**i).GetOffsetScreen(dc, xpos-x);
713 if(found) *found = true;
714 return i;
715 }
716 x += (**i).GetWidth();
717 cx += (**i).GetLength();
718 }
719 // behind last object:
720 *cxpos = cx;
721 if(found) *found = false;
722 return m_ObjectList.tail();
723 }
724
725 /** Finds text in this line.
726 @param needle the text to find
727 @param xpos the position where to start the search
728 @return the cursoor coord where it was found or -1
729 */
730 CoordType
731 wxLayoutLine::FindText(const wxString &needle, CoordType xpos) const
732 {
733 int
734 cpos = 0,
735 relpos = -1;
736 wxString const *text;
737
738 for(wxLOiterator i = m_ObjectList.begin(); i != m_ObjectList.end(); i++)
739 {
740 if(cpos >= xpos) // search from here!
741 {
742 if((**i).GetType() == WXLO_TYPE_TEXT)
743 {
744 text = & ((wxLayoutObjectText*)(*i))->GetText();
745 relpos = text->Find(needle);
746 if(relpos >= cpos-xpos) // -1 if not found
747 {
748 return cpos+relpos;
749 }
750 }
751 cpos += (**i).GetLength();
752 }
753 }
754 return -1; // not found
755 }
756
757 bool
758 wxLayoutLine::Insert(CoordType xpos, wxLayoutObject *obj)
759 {
760 wxASSERT(xpos >= 0);
761 wxASSERT(obj != NULL);
762
763 MarkDirty(xpos);
764
765 CoordType offset;
766 wxLOiterator i = FindObject(xpos, &offset);
767 if(i == NULLIT)
768 {
769 if(xpos == 0 ) // aha, empty line!
770 {
771 m_ObjectList.push_back(obj);
772 m_Length += obj->GetLength();
773 return true;
774 }
775 else
776 return false;
777 }
778
779 CoordType len = (**i).GetLength();
780 if(offset == 0 /*&& i != m_ObjectList.begin()*/) // why?
781 { // insert before this object
782 m_ObjectList.insert(i,obj);
783 m_Length += obj->GetLength();
784 return true;
785 }
786 if(offset == len )
787 {
788 if( i == m_ObjectList.tail()) // last object?
789 m_ObjectList.push_back(obj);
790 else
791 { // insert after current object
792 i++;
793 m_ObjectList.insert(i,obj);
794 }
795 m_Length += obj->GetLength();
796 return true;
797 }
798 /* Otherwise we need to split the current object.
799 Fortunately this can only be a text object. */
800 wxASSERT((**i).GetType() == WXLO_TYPE_TEXT);
801 wxString left, right;
802 wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
803 left = tobj->GetText().substr(0,offset);
804 right = tobj->GetText().substr(offset,len-offset);
805 // current text object gets set to right half
806 tobj->GetText() = right; // set new text
807 // before it we insert the new object
808 m_ObjectList.insert(i,obj);
809 m_Length += obj->GetLength();
810 // and before that we insert the left half
811 m_ObjectList.insert(i,new wxLayoutObjectText(left));
812 return true;
813 }
814
815 bool
816 wxLayoutLine::Insert(CoordType xpos, const wxString& text)
817 {
818 wxASSERT(xpos >= 0);
819
820 MarkDirty(xpos);
821
822 CoordType offset;
823 wxLOiterator i = FindObject(xpos, &offset);
824 if(i != NULLIT && (**i).GetType() == WXLO_TYPE_TEXT)
825 {
826 wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
827 tobj->GetText().insert(offset, text);
828 m_Length += text.Length();
829 }
830 else
831 {
832 if ( !Insert(xpos, new wxLayoutObjectText(text)) )
833 return false;
834 }
835
836 return true;
837 }
838
839 CoordType
840 wxLayoutLine::Delete(CoordType xpos, CoordType npos)
841 {
842 CoordType offset, len;
843
844 wxASSERT(xpos >= 0);
845 wxASSERT(npos >= 0);
846 MarkDirty(xpos);
847 wxLOiterator i = FindObject(xpos, &offset);
848 while(npos > 0)
849 {
850 if(i == NULLIT) return npos;
851 // now delete from that object:
852 if((**i).GetType() != WXLO_TYPE_TEXT)
853 {
854 if(offset != 0) // at end of line after a non-text object
855 return npos;
856 // always len == 1:
857 len = (**i).GetLength();
858 m_Length -= len;
859 npos -= len;
860 m_ObjectList.erase(i);
861 }
862 else
863 {
864 // tidy up: remove empty text objects
865 if((**i).GetLength() == 0)
866 {
867 m_ObjectList.erase(i);
868 continue;
869 }
870 // Text object:
871 CoordType max = (**i).GetLength() - offset;
872 if(npos < max) max = npos;
873 if(max == 0)
874 {
875 if(xpos == GetLength())
876 return npos;
877 else
878 { // at the end of an object
879 // move to begin of next object:
880 i++; offset = 0;
881 continue; // start over
882 }
883 }
884 npos -= max;
885 m_Length -= max;
886 if(offset == 0 && max == (**i).GetLength())
887 m_ObjectList.erase(i); // remove the whole object
888 else
889 ((wxLayoutObjectText *)(*i))->GetText().Remove(offset,max);
890 }
891 }
892
893 return npos;
894 }
895
896 bool
897 wxLayoutLine::DeleteWord(CoordType xpos)
898 {
899 wxASSERT(xpos >= 0);
900 CoordType offset;
901 MarkDirty(xpos);
902
903 wxLOiterator i = FindObject(xpos, &offset);
904
905 for(;;)
906 {
907 if(i == NULLIT) return false;
908 if((**i).GetType() != WXLO_TYPE_TEXT)
909 {
910 // This should only happen when at end of line, behind a non-text
911 // object:
912 if(offset == (**i).GetLength()) return false;
913 m_Length -= (**i).GetLength(); // -1
914 m_ObjectList.erase(i);
915 return true; // we are done
916 }
917 else
918 { // text object:
919 if(offset == (**i).GetLength()) // at end of object
920 {
921 i++; offset = 0;
922 continue;
923 }
924 wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i;
925 size_t count = 0;
926 wxString str = tobj->GetText();
927 str = str.substr(offset,str.Length()-offset);
928 // Find out how many positions we need to delete:
929 // 1. eat leading space
930 while(isspace(str.c_str()[count])) count++;
931 // 2. eat the word itself:
932 while(isalnum(str.c_str()[count])) count++;
933 // now delete it:
934 wxASSERT(count+offset <= (size_t) (**i).GetLength());
935 ((wxLayoutObjectText *)*i)->GetText().erase(offset,count);
936 m_Length -= count;
937 return true;
938 }
939 }
940
941 wxFAIL_MSG("unreachable");
942 }
943
944 wxLayoutLine *
945 wxLayoutLine::DeleteLine(bool update, wxLayoutList *llist)
946 {
947 if(m_Next) m_Next->m_Previous = m_Previous;
948 if(m_Previous) m_Previous->m_Next = m_Next;
949 if(update)
950 {
951 m_Next->MoveLines(-1);
952 m_Next->RecalculatePositions(1, llist);
953 }
954 wxLayoutLine *next = m_Next;
955 delete this;
956 return next;
957 }
958
959 void
960 wxLayoutLine::Draw(wxDC &dc,
961 wxLayoutList *llist,
962 const wxPoint & offset) const
963 {
964 wxLayoutObjectList::iterator i;
965 wxPoint pos = offset;
966 pos = pos + GetPosition();
967
968 pos.y += m_BaseLine;
969
970 CoordType xpos = 0; // cursorpos, lenght of line
971
972 CoordType from, to, tempto;
973 //FIXME This doesn't work yet, needs updating afterr default
974 //settings for list or a wxLayoutObjectCmd have changed:
975 //llist->ApplyStyle(&((wxLayoutLine *)this)->m_StyleInfo, dc);
976 int highlight = llist->IsSelected(this, &from, &to);
977 // WXLO_DEBUG(("highlight=%d", highlight ));
978 if(highlight == 1) // we need to draw the whole line inverted!
979 llist->StartHighlighting(dc);
980 else
981 llist->EndHighlighting(dc);
982
983 for(i = m_ObjectList.begin(); i != NULLIT; i++)
984 {
985 if(highlight == -1) // partially highlight line
986 {
987 // parts of the line need highlighting
988 tempto = xpos+(**i).GetLength();
989 (**i).Draw(dc, pos, llist, from-xpos, to-xpos);
990 }
991 else
992 (**i).Draw(dc, pos, llist);
993 pos.x += (**i).GetWidth();
994 xpos += (**i).GetLength();
995 }
996 }
997
998 void
999 wxLayoutLine::Layout(wxDC &dc,
1000 wxLayoutList *llist,
1001 wxPoint *cursorPos,
1002 wxPoint *cursorSize,
1003 int cx)
1004 {
1005 wxLayoutObjectList::iterator i;
1006
1007 // when a line becomes dirty, we redraw it from the place where it was
1008 // changed till the end of line (because the following wxLayoutObjects are
1009 // moved when the preceding one changes) - calculate the update rectangle.
1010 CoordType updateTop = m_Position.y,
1011 updateLeft = -1,
1012 updateWidth = m_Width,
1013 updateHeight = m_Height;
1014
1015 CoordType
1016 topHeight = 0,
1017 bottomHeight = 0; // above and below baseline
1018 CoordType
1019 objTopHeight, objBottomHeight; // above and below baseline
1020 CoordType
1021 len, count = 0;
1022
1023 CoordType heightOld = m_Height;
1024
1025 m_Height = 0;
1026 m_Width = 0;
1027 m_BaseLine = 0;
1028
1029 bool cursorFound = false;
1030
1031 if(cursorPos)
1032 {
1033 *cursorPos = m_Position;
1034 if(cursorSize) *cursorSize = wxPoint(0,0);
1035 }
1036
1037 //FIXME This doesn't work yet, needs updating afterr default
1038 //settings for list or a wxLayoutObjectCmd have changed:
1039 //llist->ApplyStyle(&m_StyleInfo, dc);
1040 for(i = m_ObjectList.begin(); i != NULLIT; i++)
1041 {
1042 wxLayoutObject *obj = *i;
1043 obj->Layout(dc, llist);
1044 wxPoint sizeObj = obj->GetSize(&objTopHeight, &objBottomHeight);
1045
1046 if(cursorPos && ! cursorFound)
1047 {
1048 // we need to check whether the text cursor is here
1049 len = obj->GetLength();
1050 if(count <= cx && count+len > cx)
1051 {
1052 if(obj->GetType() == WXLO_TYPE_TEXT)
1053 {
1054 len = cx - count; // pos in object
1055 CoordType width, height, descent;
1056 dc.GetTextExtent((*(wxLayoutObjectText*)*i).GetText().substr(0,len),
1057 &width, &height, &descent);
1058 cursorPos->x += width;
1059 cursorPos->y = m_Position.y;
1060 wxString str;
1061 if(len < obj->GetLength())
1062 str = (*(wxLayoutObjectText*)*i).GetText().substr(len,1);
1063 else
1064 str = WXLO_CURSORCHAR;
1065 dc.GetTextExtent(str, &width, &height, &descent);
1066 wxASSERT(cursorSize);
1067 // Just in case some joker inserted an empty string object:
1068 if(width == 0) width = WXLO_MINIMUM_CURSOR_WIDTH;
1069 if(height == 0) height = sizeObj.y;
1070 cursorSize->x = width;
1071 cursorSize->y = height;
1072 cursorFound = true; // no more checks
1073 }
1074 else
1075 {
1076 // on some other object
1077 CoordType top, bottom; // unused
1078 *cursorSize = obj->GetSize(&top,&bottom);
1079 cursorPos->y = m_Position.y;
1080 cursorFound = true; // no more checks
1081 }
1082 }
1083 else
1084 {
1085 count += len;
1086 cursorPos->x += obj->GetWidth();
1087 }
1088 } // cursor finding
1089
1090 m_Width += sizeObj.x;
1091 if(sizeObj.y > m_Height)
1092 {
1093 m_Height = sizeObj.y;
1094 }
1095
1096 if(objTopHeight > topHeight)
1097 topHeight = objTopHeight;
1098 if(objBottomHeight > bottomHeight)
1099 bottomHeight = objBottomHeight;
1100 }
1101
1102 if ( IsDirty() )
1103 {
1104 if ( updateHeight < m_Height )
1105 updateHeight = m_Height;
1106 if ( updateWidth < m_Width )
1107 updateWidth = m_Width;
1108
1109 // update all line if we don't know where to start from
1110 if ( updateLeft == -1 )
1111 updateLeft = 0;
1112
1113 llist->SetUpdateRect(updateLeft, updateTop);
1114 llist->SetUpdateRect(updateLeft + updateWidth + MSW_CORRECTION,
1115 updateTop + updateHeight + MSW_CORRECTION);
1116 }
1117
1118 if(topHeight + bottomHeight > m_Height)
1119 {
1120 m_Height = topHeight+bottomHeight;
1121 }
1122
1123 m_BaseLine = topHeight;
1124
1125 if(m_Height == 0)
1126 {
1127 CoordType width, height, descent;
1128 dc.GetTextExtent(WXLO_CURSORCHAR, &width, &height, &descent);
1129 m_Height = height;
1130 m_BaseLine = m_Height - descent;
1131 }
1132
1133 // tell next line about coordinate change
1134 if(m_Next && m_Height != heightOld)
1135 {
1136 // FIXME isn't this done in RecalculatePositions() below anyhow?
1137 m_Next->RecalculatePositions(0, llist);
1138 }
1139
1140 // We need to check whether we found a valid cursor size:
1141 if(cursorPos)
1142 {
1143 // this might be the case if the cursor is at the end of the
1144 // line or on a command object:
1145 if(cursorSize->y < WXLO_MINIMUM_CURSOR_WIDTH)
1146 {
1147 CoordType width, height, descent;
1148 dc.GetTextExtent(WXLO_CURSORCHAR, &width, &height, &descent);
1149 cursorSize->x = width;
1150 cursorSize->y = height;
1151 }
1152 if(m_BaseLine >= cursorSize->y) // the normal case anyway
1153 cursorPos->y += m_BaseLine-cursorSize->y;
1154 }
1155
1156 RecalculatePositions(1, llist);
1157
1158 MarkClean();
1159 }
1160
1161
1162 wxLayoutLine *
1163 wxLayoutLine::Break(CoordType xpos, wxLayoutList *llist)
1164 {
1165 wxASSERT(xpos >= 0);
1166
1167 MarkDirty(xpos);
1168
1169 /* If we are at the begin of a line, we want to move all other
1170 lines down and stay with the cursor where we are. However, if we
1171 are in an empty line, we want to move down with it. */
1172 if(xpos == 0 && GetLength() > 0)
1173 { // insert an empty line before this one
1174 wxLayoutLine *prev = new wxLayoutLine(m_Previous, llist);
1175 if(m_Previous == NULL)
1176 { // We were in first line, need to link in new empty line
1177 // before this.
1178 prev->m_Next = this;
1179 m_Previous = prev;
1180 m_Previous->m_Height = 0; // this is a wild guess
1181 }
1182 if(m_Next)
1183 m_Next->RecalculatePositions(1, llist);
1184 return m_Previous;
1185 }
1186
1187 CoordType offset;
1188 wxLOiterator i = FindObject(xpos, &offset);
1189 if(i == NULLIT)
1190 // must be at the end of the line then
1191 return new wxLayoutLine(this, llist);
1192 // split this line:
1193
1194 wxLayoutLine *newLine = new wxLayoutLine(this, llist);
1195 // split object at i:
1196 if((**i).GetType() == WXLO_TYPE_TEXT && offset != 0)
1197 {
1198 wxString left, right;
1199 wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
1200 left = tobj->GetText().substr(0,offset);
1201 right = tobj->GetText().substr(offset,tobj->GetLength()-offset);
1202 // current text object gets set to left half
1203 tobj->GetText() = left; // set new text
1204 newLine->Append(new wxLayoutObjectText(right));
1205 m_Length -= right.Length();
1206 i++; // don't move this object to the new list
1207 }
1208 else
1209 {
1210 if(offset > 0)
1211 i++; // move objects from here to new list
1212 }
1213
1214 while(i != m_ObjectList.end())
1215 {
1216 wxLayoutObject *obj = *i;
1217 newLine->Append(obj);
1218 m_Length -= obj->GetLength();
1219
1220 m_ObjectList.remove(i); // remove without deleting it
1221 }
1222 if(m_Next)
1223 m_Next->RecalculatePositions(2, llist);
1224 return newLine;
1225 }
1226
1227
1228 void
1229 wxLayoutLine::MergeNextLine(wxLayoutList *llist)
1230 {
1231 wxCHECK_RET(GetNextLine(),"wxLayout internal error: no next line to merge");
1232 wxLayoutObjectList &list = GetNextLine()->m_ObjectList;
1233 wxLOiterator i;
1234
1235 MarkDirty(GetWidth());
1236
1237 wxLayoutObject *last = NULL;
1238 for(i = list.begin(); i != list.end();)
1239 {
1240 wxLayoutObject *current = *i;
1241
1242 // merge text objects together for efficiency
1243 if ( last && last->GetType() == WXLO_TYPE_TEXT &&
1244 current->GetType() == WXLO_TYPE_TEXT )
1245 {
1246 wxLayoutObjectText *textObj = (wxLayoutObjectText *)last;
1247 wxString text(textObj->GetText());
1248 text += ((wxLayoutObjectText *)current)->GetText();
1249 textObj->SetText(text);
1250
1251 list.erase(i); // remove and delete it
1252 }
1253 else
1254 {
1255 // just append the object "as was"
1256 Append(current);
1257
1258 list.remove(i); // remove without deleting it
1259 }
1260 }
1261 wxASSERT(list.empty());
1262
1263 wxLayoutLine *oldnext = GetNextLine();
1264 wxLayoutLine *nextLine = oldnext->GetNextLine();
1265 SetNext(nextLine);
1266 if ( nextLine )
1267 {
1268 nextLine->MoveLines(-1);
1269 }
1270 else
1271 {
1272 // this is now done in Delete(), but if this function is ever called
1273 // from elsewhere, we might have to move refresh code back here (in
1274 // order not to duplicate it)
1275 #if 0
1276 wxPoint pos(oldnext->GetPosition());
1277 llist->SetUpdateRect(pos);
1278 llist->SetUpdateRect(pos.x + oldnext->GetWidth() + MSW_CORRECTION,
1279 pos.y + oldnext->GetHeight() + MSW_CORRECTION);
1280 #endif // 0
1281 }
1282
1283 delete oldnext;
1284 }
1285
1286 CoordType
1287 wxLayoutLine::GetWrapPosition(CoordType column)
1288 {
1289 CoordType offset;
1290 wxLOiterator i = FindObject(column, &offset);
1291 if(i == NULLIT) return -1; // cannot wrap
1292
1293 // go backwards through the list and look for space in text objects
1294 do
1295 {
1296 if((**i).GetType() == WXLO_TYPE_TEXT)
1297 {
1298 do
1299 {
1300 if( isspace(((wxLayoutObjectText*)*i)->GetText().c_str()[(size_t)offset]))
1301 return column;
1302 else
1303 {
1304 offset--;
1305 column--;
1306 }
1307 }while(offset != -1);
1308 i--; // move on to previous object
1309 }
1310 else
1311 {
1312 column -= (**i).GetLength();
1313 i--;
1314 }
1315 if( i != NULLIT)
1316 offset = (**i).GetLength();
1317 }while(i != NULLIT);
1318 /* If we reached the begin of the list and have more than one
1319 object, that one is longer than the margin, so break behind
1320 it. */
1321 CoordType pos = 0;
1322 i = m_ObjectList.begin();
1323 while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT)
1324 {
1325 pos += (**i).GetLength();
1326 i++;
1327 }
1328 if(i == NULLIT) return -1; //why should this happen?
1329 pos += (**i).GetLength();
1330 i++;
1331 while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT)
1332 {
1333 pos += (**i).GetLength();
1334 i++;
1335 }
1336 if(i == NULLIT) return -1; //this is possible, if there is only one text object
1337 // now we are at the second text object:
1338 pos -= (**i).GetLength();
1339 return pos; // in front of it
1340 }
1341
1342
1343 #ifdef WXLAYOUT_DEBUG
1344 void
1345 wxLayoutLine::Debug(void)
1346 {
1347 wxString tmp;
1348 wxPoint pos = GetPosition();
1349 WXLO_DEBUG(("Line %ld, Pos (%ld,%ld), Height %ld",
1350 (long int) GetLineNumber(),
1351 (long int) pos.x, (long int) pos.y,
1352 (long int) GetHeight()));
1353 if(m_ObjectList.begin() != NULLIT)
1354 (**m_ObjectList.begin()).Debug();
1355
1356 }
1357 #endif
1358
1359 void
1360 wxLayoutLine::Copy(wxLayoutList *llist,
1361 CoordType from,
1362 CoordType to)
1363 {
1364 CoordType firstOffset, lastOffset;
1365
1366 if(to == -1) to = GetLength();
1367 if(from == to) return;
1368
1369 wxLOiterator first = FindObject(from, &firstOffset);
1370 wxLOiterator last = FindObject(to, &lastOffset);
1371
1372 // Common special case: only one object
1373 if( first != NULLIT && last != NULLIT && *first == *last )
1374 {
1375 if( (**first).GetType() == WXLO_TYPE_TEXT )
1376 {
1377 llist->Insert(new wxLayoutObjectText(
1378 ((wxLayoutObjectText
1379 *)*first)->GetText().substr(firstOffset,
1380 lastOffset-firstOffset))
1381 );
1382 return;
1383 }
1384 else // what can we do?
1385 {
1386 if(lastOffset > firstOffset) // i.e. +1 :-)
1387 llist->Insert( (**first).Copy() );
1388 return;
1389 }
1390 }
1391
1392 // If we reach here, we can safely copy the whole first object from
1393 // the firstOffset position on:
1394 if((**first).GetType() == WXLO_TYPE_TEXT && firstOffset != 0)
1395 {
1396 llist->Insert(new wxLayoutObjectText(
1397 ((wxLayoutObjectText *)*first)->GetText().substr(firstOffset))
1398 );
1399 }
1400 else if(firstOffset == 0)
1401 llist->Insert( (**first).Copy() );
1402 // else nothing to copy :-(
1403
1404 // Now we copy all objects before the last one:
1405 wxLOiterator i = first; i++;
1406 for( ; i != last; i++)
1407 llist->Insert( (**i).Copy() );
1408
1409 // And now the last object:
1410 if(lastOffset != 0)
1411 {
1412 if( (**last).GetType() == WXLO_TYPE_TEXT )
1413 {
1414 llist->Insert(new wxLayoutObjectText(
1415 ((wxLayoutObjectText *)*last)->GetText().substr(0,lastOffset))
1416 );
1417 }
1418 else
1419 llist->Insert( (**last).Copy() );
1420 }
1421 }
1422
1423
1424 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1425
1426 The wxLayoutList object
1427
1428 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1429
1430 wxLayoutList::wxLayoutList()
1431 {
1432 #ifdef WXLAYOUT_USE_CARET
1433 m_caret = NULL;
1434 #endif // WXLAYOUT_USE_CARET
1435
1436 m_FirstLine = NULL;
1437 InvalidateUpdateRect();
1438 Clear();
1439 }
1440
1441 wxLayoutList::~wxLayoutList()
1442 {
1443 InternalClear();
1444 m_FirstLine->DeleteLine(false, this);
1445 }
1446
1447 void
1448 wxLayoutList::Empty(void)
1449 {
1450 while(m_FirstLine)
1451 m_FirstLine = m_FirstLine->DeleteLine(false, this);
1452
1453 m_CursorPos = wxPoint(0,0);
1454 m_CursorScreenPos = wxPoint(0,0);
1455 m_CursorSize = wxPoint(0,0);
1456 m_movedCursor = true;
1457 m_FirstLine = new wxLayoutLine(NULL, this); // empty first line
1458 m_CursorLine = m_FirstLine;
1459 InvalidateUpdateRect();
1460 }
1461
1462
1463 void
1464 wxLayoutList::InternalClear(void)
1465 {
1466 Empty();
1467 m_Selection.m_selecting = false;
1468 m_Selection.m_valid = false;
1469
1470 m_DefaultSetting.family = wxSWISS;
1471 m_DefaultSetting.size = WXLO_DEFAULTFONTSIZE;
1472 m_DefaultSetting.style = wxNORMAL;
1473 m_DefaultSetting.weight = wxNORMAL;
1474 m_DefaultSetting.underline = 0;
1475 m_DefaultSetting.m_fg_valid = TRUE;
1476 m_DefaultSetting.m_fg = *wxBLACK;
1477 m_DefaultSetting.m_bg_valid = TRUE;
1478 m_DefaultSetting.m_bg = *wxWHITE;
1479
1480 m_CurrentSetting = m_DefaultSetting;
1481 }
1482
1483 void
1484 wxLayoutList::SetFont(int family, int size, int style, int weight,
1485 int underline, wxColour *fg,
1486 wxColour *bg)
1487 {
1488 if(family != -1) m_CurrentSetting.family = family;
1489 if(size != -1) m_CurrentSetting.size = size;
1490 if(style != -1) m_CurrentSetting.style = style;
1491 if(weight != -1) m_CurrentSetting.weight = weight;
1492 if(underline != -1) m_CurrentSetting.underline = underline != 0;
1493 if(fg) m_CurrentSetting.m_fg = *fg;
1494 if(bg) m_CurrentSetting.m_bg = *bg;
1495 Insert(
1496 new wxLayoutObjectCmd(
1497 m_CurrentSetting.family,
1498 m_CurrentSetting.size,
1499 m_CurrentSetting.style,
1500 m_CurrentSetting.weight,
1501 m_CurrentSetting.underline,
1502 fg, bg));
1503 }
1504
1505 void
1506 wxLayoutList::SetFont(int family, int size, int style, int weight,
1507 int underline, char const *fg, char const *bg)
1508
1509 {
1510 wxColour
1511 *cfg = NULL,
1512 *cbg = NULL;
1513
1514 if( fg )
1515 cfg = wxTheColourDatabase->FindColour(fg);
1516 if( bg )
1517 cbg = wxTheColourDatabase->FindColour(bg);
1518
1519 SetFont(family,size,style,weight,underline,cfg,cbg);
1520 }
1521
1522 void
1523 wxLayoutList::Clear(int family, int size, int style, int weight,
1524 int underline, wxColour *fg, wxColour *bg)
1525 {
1526 InternalClear();
1527 m_DefaultSetting = wxLayoutStyleInfo(family, size, style, weight,
1528 underline, fg, bg);
1529 m_CurrentSetting = m_DefaultSetting;
1530 }
1531
1532 wxPoint
1533 wxLayoutList::FindText(const wxString &needle, const wxPoint &cpos) const
1534 {
1535 int xpos;
1536
1537 wxLayoutLine *line;
1538 for(line = m_FirstLine;
1539 line;
1540 line = line->GetNextLine())
1541 {
1542 if(line->GetLineNumber() >= cpos.y)
1543 {
1544 xpos = line->FindText(needle,
1545 (line->GetLineNumber() == cpos.y) ?
1546 cpos.x : 0);
1547 if(xpos != -1)
1548 return wxPoint(xpos, line->GetLineNumber());
1549 }
1550 }
1551 return wxPoint(-1,-1);
1552 }
1553
1554
1555 bool
1556 wxLayoutList::MoveCursorTo(wxPoint const &p)
1557 {
1558 AddCursorPosToUpdateRect();
1559
1560 wxPoint cursorPosOld = m_CursorPos;
1561
1562 wxLayoutLine *line = m_FirstLine;
1563 while(line && line->GetLineNumber() != p.y)
1564 line = line->GetNextLine();
1565 if(line && line->GetLineNumber() == p.y) // found it
1566 {
1567 m_CursorPos.y = p.y;
1568 m_CursorLine = line;
1569 CoordType len = line->GetLength();
1570 if(len >= p.x)
1571 {
1572 m_CursorPos.x = p.x;
1573 }
1574 else
1575 {
1576 m_CursorPos.x = len;
1577 }
1578 }
1579
1580 m_movedCursor = m_CursorPos != cursorPosOld;
1581
1582 return m_CursorPos == p;
1583 }
1584
1585 bool
1586 wxLayoutList::MoveCursorVertically(int n)
1587 {
1588 AddCursorPosToUpdateRect();
1589
1590 wxPoint cursorPosOld = m_CursorPos;
1591
1592 bool rc;
1593 if(n < 0) // move up
1594 {
1595 if(m_CursorLine == m_FirstLine) return false;
1596 while(n < 0 && m_CursorLine)
1597 {
1598 m_CursorLine = m_CursorLine->GetPreviousLine();
1599 m_CursorPos.y--;
1600 n++;
1601 }
1602 if(! m_CursorLine)
1603 {
1604 m_CursorLine = m_FirstLine;
1605 m_CursorPos.y = 0;
1606 rc = false;
1607 }
1608 else
1609 {
1610 if(m_CursorPos.x > m_CursorLine->GetLength())
1611 m_CursorPos.x = m_CursorLine->GetLength();
1612 rc = true;
1613 }
1614 }
1615 else // move down
1616 {
1617 wxLayoutLine *last = m_CursorLine;
1618 if(! m_CursorLine->GetNextLine()) return false;
1619 while(n > 0 && m_CursorLine)
1620 {
1621 n--;
1622 m_CursorPos.y ++;
1623 m_CursorLine = m_CursorLine->GetNextLine();
1624 }
1625 if(! m_CursorLine)
1626 {
1627 m_CursorLine = last;
1628 m_CursorPos.y ++;
1629 rc = false;
1630 }
1631 else
1632 {
1633 if(m_CursorPos.x > m_CursorLine->GetLength())
1634 m_CursorPos.x = m_CursorLine->GetLength();
1635 rc = true;
1636 }
1637 }
1638
1639 m_movedCursor = m_CursorPos != cursorPosOld;
1640
1641 return rc;
1642 }
1643
1644 bool
1645 wxLayoutList::MoveCursorHorizontally(int n)
1646 {
1647 AddCursorPosToUpdateRect();
1648
1649 wxPoint cursorPosOld = m_CursorPos;
1650
1651 int move;
1652 while(n < 0)
1653 {
1654 if(m_CursorPos.x == 0) // at begin of line
1655 {
1656 if(! MoveCursorVertically(-1))
1657 break;
1658 MoveCursorToEndOfLine();
1659 n++;
1660 continue;
1661 }
1662 move = -n;
1663 if(move > m_CursorPos.x) move = m_CursorPos.x;
1664 m_CursorPos.x -= move; n += move;
1665 }
1666
1667 while(n > 0)
1668 {
1669 int len = m_CursorLine->GetLength();
1670 if(m_CursorPos.x == len) // at end of line
1671 {
1672 if(! MoveCursorVertically(1))
1673 break;
1674 MoveCursorToBeginOfLine();
1675 n--;
1676 continue;
1677 }
1678 move = n;
1679 if( move >= len-m_CursorPos.x) move = len-m_CursorPos.x;
1680 m_CursorPos.x += move;
1681 n -= move;
1682 }
1683
1684 m_movedCursor = m_CursorPos != cursorPosOld;
1685
1686 return n == 0;
1687 }
1688
1689 bool
1690 wxLayoutList::MoveCursorWord(int n)
1691 {
1692 wxCHECK_MSG( m_CursorLine, false, "no current line" );
1693 wxCHECK_MSG( n == -1 || n == +1, false, "not implemented yet" );
1694
1695 CoordType moveDistance = 0;
1696 CoordType offset;
1697 for ( wxLOiterator i = m_CursorLine->FindObject(m_CursorPos.x, &offset);
1698 n != 0;
1699 n > 0 ? i++ : i-- )
1700 {
1701 if ( i == NULLIT )
1702 return false;
1703
1704 wxLayoutObject *obj = *i;
1705 if( obj->GetType() != WXLO_TYPE_TEXT )
1706 {
1707 // any non text objects count as one word
1708 n > 0 ? n-- : n++;
1709
1710 moveDistance += obj->GetLength();
1711 }
1712 else
1713 {
1714 // text object
1715 wxLayoutObjectText *tobj = (wxLayoutObjectText *)obj;
1716
1717 if ( offset == tobj->GetLength() )
1718 {
1719 // at end of object
1720 n > 0 ? n-- : n++;
1721 }
1722 else
1723 {
1724 const char *start = tobj->GetText().c_str();
1725 const char *p = start + offset;
1726
1727 // to the beginning/end of the next/prev word
1728 while ( isspace(*p) )
1729 {
1730 n > 0 ? p++ : p--;
1731 }
1732
1733 // go to the end/beginning of the word (in a broad sense...)
1734 while ( p >= start && !isspace(*p) )
1735 {
1736 n > 0 ? p++ : p--;
1737 }
1738
1739 if ( n > 0 )
1740 {
1741 // now advance to the beginning of the next word
1742 while ( isspace(*p) )
1743 p++;
1744 }
1745
1746 n > 0 ? n-- : n++;
1747
1748 moveDistance = p - start - offset;
1749 }
1750 }
1751
1752 // except for the first iteration, offset is 0
1753 offset = 0;
1754 }
1755
1756 MoveCursorHorizontally(moveDistance);
1757
1758 return true;
1759 }
1760
1761 bool
1762 wxLayoutList::Insert(wxString const &text)
1763 {
1764 wxASSERT(m_CursorLine);
1765 wxASSERT_MSG( text.Find('\n') == wxNOT_FOUND, "use wxLayoutImportText!" );
1766
1767 if ( !text )
1768 return true;
1769
1770 AddCursorPosToUpdateRect();
1771
1772 if ( !m_CursorLine->Insert(m_CursorPos.x, text) )
1773 return false;
1774
1775 m_CursorPos.x += text.Length();
1776
1777 m_movedCursor = true;
1778
1779 m_CursorLine->RecalculatePositions(0, this);
1780
1781 return true;
1782 }
1783
1784 bool
1785 wxLayoutList::Insert(wxLayoutObject *obj)
1786 {
1787 wxASSERT(m_CursorLine);
1788
1789 if(! m_CursorLine)
1790 m_CursorLine = GetFirstLine();
1791
1792 AddCursorPosToUpdateRect();
1793
1794 m_CursorLine->Insert(m_CursorPos.x, obj);
1795 m_CursorPos.x += obj->GetLength();
1796 m_movedCursor = true;
1797
1798 m_CursorLine->RecalculatePositions(0, this);
1799
1800 return true;
1801 }
1802
1803 bool
1804 wxLayoutList::Insert(wxLayoutList *llist)
1805 {
1806 wxASSERT(llist);
1807 bool rc = TRUE;
1808
1809 for(wxLayoutLine *line = llist->GetFirstLine();
1810 line;
1811 line = line->GetNextLine()
1812 )
1813 {
1814 for(wxLOiterator i = line->GetFirstObject();
1815 i != NULLIT;
1816 i++)
1817 rc |= Insert(*i);
1818 LineBreak();
1819 }
1820 return rc;
1821 }
1822
1823 bool
1824 wxLayoutList::LineBreak(void)
1825 {
1826 wxASSERT(m_CursorLine);
1827 bool setFirst = (m_CursorLine == m_FirstLine && m_CursorPos.x == 0);
1828
1829 AddCursorPosToUpdateRect();
1830
1831 wxPoint position(m_CursorLine->GetPosition());
1832
1833 wxCoord width = m_CursorLine->GetWidth(),
1834 height = m_CursorLine->GetHeight();
1835
1836 m_CursorLine = m_CursorLine->Break(m_CursorPos.x, this);
1837 if(setFirst) // we were at beginning of first line
1838 m_FirstLine = m_CursorLine->GetPreviousLine();
1839 if(m_CursorPos.x != 0)
1840 m_CursorPos.y++;
1841 m_CursorPos.x = 0;
1842
1843 wxLayoutLine *prev = m_CursorLine->GetPreviousLine();
1844 wxCHECK_MSG(prev, false, "just broke the line, where is the previous one?");
1845
1846 height += prev->GetHeight();
1847
1848 m_movedCursor = true;
1849
1850 SetUpdateRect(position);
1851 SetUpdateRect(position.x + width + MSW_CORRECTION,
1852 position.y + height + MSW_CORRECTION);
1853
1854 return true;
1855 }
1856
1857 bool
1858 wxLayoutList::WrapLine(CoordType column)
1859 {
1860 if(m_CursorPos.x <= column || column < 1)
1861 return false; // do nothing yet
1862 else
1863 {
1864 CoordType xpos = m_CursorLine->GetWrapPosition(column);
1865 if(xpos == -1)
1866 return false; // cannot break line
1867 //else:
1868 CoordType newpos = m_CursorPos.x - xpos - 1;
1869 m_CursorPos.x = xpos;
1870
1871 AddCursorPosToUpdateRect();
1872
1873 LineBreak();
1874 Delete(1); // delete the space
1875 m_CursorPos.x = newpos;
1876
1877 m_CursorLine->RecalculatePositions(1, this);
1878
1879 m_movedCursor = true;
1880
1881 return true;
1882 }
1883 }
1884
1885 bool
1886 wxLayoutList::Delete(CoordType npos)
1887 {
1888 wxCHECK_MSG(m_CursorLine, false, "can't delete in non existing line");
1889
1890 if ( npos == 0 )
1891 return true;
1892
1893 AddCursorPosToUpdateRect();
1894
1895 // were other lines appended to this one (this is important to know because
1896 // this means that our width _increased_ as the result of deletion)
1897 bool wasMerged = false;
1898
1899 // the size of the region to update
1900 CoordType totalHeight = m_CursorLine->GetHeight(),
1901 totalWidth = m_CursorLine->GetWidth();
1902
1903 CoordType left;
1904 do
1905 {
1906 left = m_CursorLine->Delete(m_CursorPos.x, npos);
1907
1908 if( left > 0 )
1909 {
1910 // More to delete, continue on next line.
1911
1912 // First, check if line is empty:
1913 if(m_CursorLine->GetLength() == 0)
1914 {
1915 // in this case, updating could probably be optimised
1916 #ifdef WXLO_DEBUG
1917 wxASSERT(DeleteLines(1) == 0);
1918 #else
1919 DeleteLines(1);
1920 #endif
1921
1922 left--;
1923 }
1924 else
1925 {
1926 // Need to join next line
1927 if(! m_CursorLine->GetNextLine())
1928 break; // cannot
1929 else
1930 {
1931 wasMerged = true;
1932 wxLayoutLine *next = m_CursorLine->GetNextLine();
1933 if ( next )
1934 {
1935 totalHeight += next->GetHeight();
1936 totalWidth += next->GetWidth();
1937
1938 m_CursorLine->MergeNextLine(this);
1939 left--;
1940 }
1941 else
1942 {
1943 wxFAIL_MSG("can't delete all this");
1944
1945 return false;
1946 }
1947 }
1948 }
1949 }
1950 }
1951 while ( left> 0 );
1952
1953 // we need to update the whole tail of the line and the lines which
1954 // disappeared
1955 if ( wasMerged )
1956 {
1957 wxPoint position(m_CursorLine->GetPosition());
1958 SetUpdateRect(position);
1959 SetUpdateRect(position.x + totalWidth + MSW_CORRECTION,
1960 position.y + totalHeight + MSW_CORRECTION);
1961 }
1962
1963 return left == 0;
1964 }
1965
1966 int
1967 wxLayoutList::DeleteLines(int n)
1968 {
1969 wxASSERT(m_CursorLine);
1970 wxLayoutLine *line;
1971
1972 AddCursorPosToUpdateRect();
1973
1974 while(n > 0)
1975 {
1976 if(!m_CursorLine->GetNextLine())
1977 { // we cannot delete this line, but we can clear it
1978 MoveCursorToBeginOfLine();
1979 DeleteToEndOfLine();
1980 m_CursorLine->RecalculatePositions(2, this);
1981 return n-1;
1982 }
1983 //else:
1984 line = m_CursorLine;
1985 m_CursorLine = m_CursorLine->DeleteLine(true, this);
1986 n--;
1987 if(line == m_FirstLine) m_FirstLine = m_CursorLine;
1988 wxASSERT(m_FirstLine);
1989 wxASSERT(m_CursorLine);
1990 }
1991 m_CursorLine->RecalculatePositions(2, this);
1992 return n;
1993 }
1994
1995 void
1996 wxLayoutList::Recalculate(wxDC &dc, CoordType bottom)
1997 {
1998 wxLayoutLine *line = m_FirstLine;
1999
2000 // first, make sure everything is calculated - this might not be
2001 // needed, optimise it later
2002 ApplyStyle(&m_DefaultSetting, dc);
2003 while(line)
2004 {
2005 line->RecalculatePosition(this); // so we don't need to do it all the time
2006 // little condition to speed up redrawing:
2007 if(bottom != -1 && line->GetPosition().y > bottom) break;
2008 line = line->GetNextLine();
2009 }
2010 }
2011
2012 void
2013 wxLayoutList::UpdateCursorScreenPos(wxDC &dc,
2014 bool resetCursorMovedFlag,
2015 const wxPoint& translate)
2016 {
2017 wxCHECK_RET( m_CursorLine, "no cursor line" );
2018
2019 if ( m_movedCursor )
2020 {
2021 m_CursorLine->Layout(dc, this,
2022 &m_CursorScreenPos, &m_CursorSize,
2023 m_CursorPos.x);
2024
2025 if ( resetCursorMovedFlag )
2026 {
2027 #ifdef WXLAYOUT_USE_CARET
2028 // adjust the caret position
2029 wxPoint coords(m_CursorScreenPos);
2030 coords += translate;
2031
2032 // and set it
2033 m_caret->Move(coords);
2034 #endif // WXLAYOUT_USE_CARET
2035
2036 m_movedCursor = false;
2037 }
2038 }
2039 }
2040
2041 wxPoint
2042 wxLayoutList::GetCursorScreenPos(wxDC &dc)
2043 {
2044 // this function is called with wxMemoryDC argument from ScrollToCursor(),
2045 // for example, so it shouldn't clear "cursor moved" flag - or else the
2046 // cursor won't be moved when UpdateCursorScreenPos() is called with the
2047 // "real" (i.e. the one used for drawing) wxDC.
2048 UpdateCursorScreenPos(dc, false /* don't reset the flag */);
2049
2050 return m_CursorScreenPos;
2051 }
2052
2053 /*
2054 Is called before each Draw(). Now, it will re-layout all lines which
2055 have changed.
2056 */
2057 void
2058 wxLayoutList::Layout(wxDC &dc, CoordType bottom, bool forceAll)
2059 {
2060 // first, make sure everything is calculated - this might not be
2061 // needed, optimise it later
2062 ApplyStyle(&m_DefaultSetting, dc);
2063
2064 // FIXME this is completely wrong - we should start by first *visible* line
2065 // (and stop on the last one) instead of looping over all lines!!
2066 wxLayoutLine *line = m_FirstLine;
2067 while(line)
2068 {
2069 if(forceAll || line->IsDirty())
2070 {
2071 line->GetStyleInfo() = m_CurrentSetting;
2072 if(line == m_CursorLine)
2073 line->Layout(dc, this, (wxPoint *)&m_CursorScreenPos,
2074 (wxPoint *)&m_CursorSize, m_CursorPos.x);
2075 else
2076 line->Layout(dc, this);
2077
2078 // little condition to speed up redrawing:
2079 if(bottom != -1 && line->GetPosition().y > bottom)
2080 break;
2081 }
2082
2083 line->RecalculatePositions(1, this);
2084 line = line->GetNextLine();
2085 }
2086
2087 ///FIXME: disabled for now
2088 #if 0
2089 // can only be 0 if we are on the first line and have no next line
2090 wxASSERT(m_CursorSize.x != 0 || (m_CursorLine &&
2091 m_CursorLine->GetNextLine() == NULL &&
2092 m_CursorLine == m_FirstLine));
2093 #endif
2094 AddCursorPosToUpdateRect();
2095 }
2096
2097 void
2098 wxLayoutList::Draw(wxDC &dc,
2099 wxPoint const &offset,
2100 CoordType top,
2101 CoordType bottom)
2102 {
2103 wxLayoutLine *line = m_FirstLine;
2104
2105 Layout(dc);
2106 ApplyStyle(&m_DefaultSetting, dc);
2107 wxBrush brush(m_CurrentSetting.m_bg, wxSOLID);
2108 dc.SetBrush(brush);
2109 dc.SetBackgroundMode(wxTRANSPARENT);
2110
2111 while(line)
2112 {
2113 // only draw if between top and bottom:
2114 if((top == -1 || line->GetPosition().y + line->GetHeight() >= top))
2115 line->Draw(dc, this, offset);
2116 else
2117 line->Layout(dc, this);
2118 // little condition to speed up redrawing:
2119 if(bottom != -1 && line->GetPosition().y > bottom) break;
2120 line = line->GetNextLine();
2121 }
2122 InvalidateUpdateRect();
2123
2124 WXLO_DEBUG(("Selection is %s : l%d,%ld/%ld,%ld",
2125 m_Selection.m_valid ? "valid" : "invalid",
2126 m_Selection.m_CursorA.x, m_Selection.m_CursorA.y,
2127 m_Selection.m_CursorB.x, m_Selection.m_CursorB.y));
2128 }
2129
2130 wxLayoutObject *
2131 wxLayoutList::FindObjectScreen(wxDC &dc, wxPoint const pos,
2132 wxPoint *cursorPos,
2133 bool *found)
2134 {
2135 // First, find the right line:
2136 wxLayoutLine *line = m_FirstLine;
2137 wxPoint p;
2138
2139 ApplyStyle(&m_DefaultSetting, dc);
2140 while(line)
2141 {
2142 p = line->GetPosition();
2143 if(p.y <= pos.y && p.y+line->GetHeight() >= pos.y)
2144 break;
2145 #if 0
2146 // we need to run a layout here to get font sizes right :-(
2147
2148 // VZ: we can't call Layout() from here because it marks the line as
2149 // clean and it is not refreshed when it's called from wxLayoutList::
2150 // Layout() - if we really need to do this, we should introduce an
2151 // extra argument to Layout() to prevent the line from MarkClean()ing
2152 // itself here
2153 line->Layout(dc, this);
2154 #endif
2155 line = line->GetNextLine();
2156 }
2157 if(line == NULL)
2158 {
2159 if(found) *found = false;
2160 return NULL; // not found
2161 }
2162 if(cursorPos) cursorPos->y = line->GetLineNumber();
2163 // Now, find the object in the line:
2164 wxLOiterator i = line->FindObjectScreen(dc, pos.x,
2165 cursorPos ? & cursorPos->x : NULL ,
2166 found);
2167 return (i == NULLIT) ? NULL : *i;
2168
2169 }
2170
2171 wxPoint
2172 wxLayoutList::GetSize(void) const
2173 {
2174 wxLayoutLine
2175 *line = m_FirstLine,
2176 *last = line;
2177 if(! line)
2178 return wxPoint(0,0);
2179
2180 wxPoint maxPoint(0,0);
2181
2182 // find last line:
2183 while(line)
2184 {
2185 if(line->GetWidth() > maxPoint.x)
2186 maxPoint.x = line->GetWidth();
2187 last = line;
2188 line = line->GetNextLine();
2189 }
2190
2191 maxPoint.y = last->GetPosition().y + last->GetHeight();
2192
2193 // if the line was just added, its height would be 0 and we can't call
2194 // Layout() from here because we don't have a dc and we might be not drawing
2195 // at all, besides... So take the cursor height by default (taking 0 is bad
2196 // because then the scrollbars won't be resized and the new line won't be
2197 // shown at all)
2198 if ( last->IsDirty() )
2199 {
2200 if ( last->GetHeight() == 0 )
2201 maxPoint.y += m_CursorSize.y;
2202 if ( last->GetWidth() == 0 && maxPoint.x < m_CursorSize.x )
2203 maxPoint.x = m_CursorSize.x;
2204 }
2205
2206 return maxPoint;
2207 }
2208
2209
2210 void
2211 wxLayoutList::DrawCursor(wxDC &dc, bool active, wxPoint const &translate)
2212 {
2213 wxPoint coords(m_CursorScreenPos);
2214 coords += translate;
2215
2216 #ifdef WXLAYOUT_DEBUG
2217 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
2218 (long)m_CursorPos.x, (long)m_CursorPos.y,
2219 (long)coords.x, (long)coords.y,
2220 (long)m_CursorSize.x, (long)m_CursorSize.y,
2221 (long)m_CursorLine->GetLineNumber(),
2222 (long)m_CursorLine->GetLength()));
2223
2224 wxLogStatus("Cursor is at (%d, %d)", m_CursorPos.x, m_CursorPos.y);
2225 #endif
2226
2227 #ifndef WXLAYOUT_USE_CARET
2228 dc.SetBrush(*wxBLACK_BRUSH);
2229 dc.SetLogicalFunction(wxXOR);
2230 dc.SetPen(wxPen(*wxBLACK,1,wxSOLID));
2231 if(active)
2232 {
2233 dc.DrawRectangle(coords.x, coords.y,
2234 m_CursorSize.x, m_CursorSize.y);
2235 SetUpdateRect(coords.x, coords.y);
2236 SetUpdateRect(coords.x+m_CursorSize.x, coords.y+m_CursorSize.y);
2237 }
2238 else
2239 {
2240 dc.DrawLine(coords.x, coords.y+m_CursorSize.y-1,
2241 coords.x, coords.y);
2242 SetUpdateRect(coords.x, coords.y+m_CursorSize.y-1);
2243 SetUpdateRect(coords.x, coords.y);
2244 }
2245 dc.SetLogicalFunction(wxCOPY);
2246 //dc.SetBrush(wxNullBrush);
2247 #endif // WXLAYOUT_USE_CARET/!WXLAYOUT_USE_CARET
2248 }
2249
2250 void
2251 wxLayoutList::SetUpdateRect(CoordType x, CoordType y)
2252 {
2253 if(m_UpdateRectValid)
2254 GrowRect(m_UpdateRect, x, y);
2255 else
2256 {
2257 m_UpdateRect.x = x;
2258 m_UpdateRect.y = y;
2259 m_UpdateRect.width = 4; // large enough to avoid surprises from
2260 m_UpdateRect.height = 4;// wxGTK :-)
2261 m_UpdateRectValid = true;
2262 }
2263 }
2264
2265 void
2266 wxLayoutList::StartSelection(wxPoint cpos)
2267 {
2268 if(cpos.x == -1)
2269 cpos = m_CursorPos;
2270 WXLO_DEBUG(("Starting selection at %ld/%ld", cpos.x, cpos.y));
2271 m_Selection.m_CursorA = cpos;
2272 m_Selection.m_CursorB = cpos;
2273 m_Selection.m_selecting = true;
2274 m_Selection.m_valid = false;
2275 }
2276
2277 void
2278 wxLayoutList::ContinueSelection(wxPoint cpos)
2279 {
2280 if(cpos.x == -1)
2281 cpos = m_CursorPos;
2282 wxASSERT(m_Selection.m_selecting == true);
2283 wxASSERT(m_Selection.m_valid == false);
2284 WXLO_DEBUG(("Continuing selection at %ld/%ld", cpos.x, cpos.y));
2285 if(m_Selection.m_CursorB <= cpos)
2286 m_Selection.m_CursorB = cpos;
2287 else
2288 m_Selection.m_CursorA = cpos;
2289 // We always want m_CursorA <= m_CursorB!
2290 if(! (m_Selection.m_CursorA <= m_Selection.m_CursorB))
2291 {
2292 wxPoint help = m_Selection.m_CursorB;
2293 m_Selection.m_CursorB = m_Selection.m_CursorA;
2294 m_Selection.m_CursorA = help;
2295 }
2296 }
2297
2298 void
2299 wxLayoutList::EndSelection(wxPoint cpos)
2300 {
2301 if(cpos.x == -1)
2302 cpos = m_CursorPos;
2303 ContinueSelection(cpos);
2304 WXLO_DEBUG(("Ending selection at %ld/%ld", cpos.x, cpos.y));
2305 m_Selection.m_selecting = false;
2306 m_Selection.m_valid = true;
2307 }
2308
2309
2310 bool
2311 wxLayoutList::IsSelecting(void)
2312 {
2313 return m_Selection.m_selecting;
2314 }
2315
2316 bool
2317 wxLayoutList::IsSelected(const wxPoint &cursor)
2318 {
2319 if(! m_Selection.m_valid && ! m_Selection.m_selecting)
2320 return false;
2321 return m_Selection.m_CursorA <= cursor
2322 && cursor <= m_Selection.m_CursorB;
2323 }
2324
2325
2326 /** Tests whether this layout line is selected and needs
2327 highlighting.
2328 @param line to test for
2329 @return 0 = not selected, 1 = fully selected, -1 = partially
2330 selected
2331 */
2332 int
2333 wxLayoutList::IsSelected(const wxLayoutLine *line, CoordType *from,
2334 CoordType *to)
2335 {
2336 wxASSERT(line); wxASSERT(to); wxASSERT(from);
2337
2338 if(! m_Selection.m_valid && ! m_Selection.m_selecting)
2339 return 0;
2340
2341 CoordType y = line->GetLineNumber();
2342 if(m_Selection.m_CursorA.y < y && m_Selection.m_CursorB.y > y)
2343 return 1;
2344 else if(m_Selection.m_CursorA.y == y)
2345 {
2346 *from = m_Selection.m_CursorA.x;
2347 if(m_Selection.m_CursorB.y == y)
2348 *to = m_Selection.m_CursorB.x;
2349 else
2350 *to = line->GetLength();
2351 return -1;
2352 }
2353 else if(m_Selection.m_CursorB.y == y)
2354 {
2355 *to = m_Selection.m_CursorB.x;
2356 if(m_Selection.m_CursorA.y == y)
2357 *from = m_Selection.m_CursorA.x;
2358 else
2359 *from = 0;
2360 return -1;
2361 }
2362 else
2363 return 0;
2364 }
2365
2366 void
2367 wxLayoutList::DeleteSelection(void)
2368 {
2369 if(! m_Selection.m_valid)
2370 return;
2371
2372 m_Selection.m_valid = false;
2373
2374 // Only delete part of the current line?
2375 if(m_Selection.m_CursorA.y == m_Selection.m_CursorB.y)
2376 {
2377 MoveCursorTo(m_Selection.m_CursorA);
2378 Delete(m_Selection.m_CursorB.x - m_Selection.m_CursorA.x);
2379 return;
2380 }
2381
2382
2383 wxLayoutLine
2384 * firstLine = NULL,
2385 * lastLine = NULL;
2386
2387 for(firstLine = m_FirstLine;
2388 firstLine && firstLine->GetLineNumber() < m_Selection.m_CursorA.y;
2389 firstLine=firstLine->GetNextLine())
2390 ;
2391 if(!firstLine || firstLine->GetLineNumber() != m_Selection.m_CursorA.y)
2392 return;
2393
2394
2395 for(lastLine = m_FirstLine;
2396 lastLine && lastLine->GetLineNumber() < m_Selection.m_CursorB.y;
2397 lastLine=lastLine->GetNextLine())
2398 ;
2399 if(!lastLine || lastLine->GetLineNumber() != m_Selection.m_CursorB.y)
2400 return;
2401
2402
2403 // We now know that the two lines are different:
2404
2405 // First, delete what's left of this line:
2406 MoveCursorTo(m_Selection.m_CursorA);
2407 DeleteToEndOfLine();
2408
2409 wxLayoutLine *nextLine = firstLine->GetNextLine();
2410 while(nextLine && nextLine != lastLine)
2411 nextLine = nextLine->DeleteLine(false, this);
2412
2413 // Now nextLine = lastLine;
2414 Delete(1); // This joins firstLine and nextLine
2415 Delete(m_Selection.m_CursorB.x); // This deletes the first x
2416 // positions
2417
2418 /// Recalculate:
2419 firstLine->RecalculatePositions(1, this);
2420 }
2421
2422 /// Starts highlighting the selection
2423 void
2424 wxLayoutList::StartHighlighting(wxDC &dc)
2425 {
2426 #if SHOW_SELECTIONS
2427 dc.SetTextForeground(m_CurrentSetting.m_bg);
2428 dc.SetTextBackground(m_CurrentSetting.m_fg);
2429 dc.SetBackgroundMode(wxSOLID);
2430 #endif
2431 }
2432
2433 /// Ends highlighting the selection
2434 void
2435 wxLayoutList::EndHighlighting(wxDC &dc)
2436 {
2437 #if SHOW_SELECTIONS
2438 dc.SetTextForeground(m_CurrentSetting.m_fg);
2439 dc.SetTextBackground(m_CurrentSetting.m_bg);
2440 dc.SetBackgroundMode(wxTRANSPARENT);
2441 #endif
2442 }
2443
2444
2445 wxLayoutList *
2446 wxLayoutList::Copy(const wxPoint &from,
2447 const wxPoint &to)
2448 {
2449 wxLayoutLine
2450 * firstLine = NULL,
2451 * lastLine = NULL;
2452
2453 for(firstLine = m_FirstLine;
2454 firstLine && firstLine->GetLineNumber() < from.y;
2455 firstLine=firstLine->GetNextLine())
2456 ;
2457 if(!firstLine || firstLine->GetLineNumber() != from.y)
2458 return NULL;
2459
2460 for(lastLine = m_FirstLine;
2461 lastLine && lastLine->GetLineNumber() < to.y;
2462 lastLine=lastLine->GetNextLine())
2463 ;
2464 if(!lastLine || lastLine->GetLineNumber() != to.y)
2465 return NULL;
2466
2467 if(to <= from)
2468 {
2469 wxLayoutLine *tmp = firstLine;
2470 firstLine = lastLine;
2471 lastLine = tmp;
2472 }
2473
2474 wxLayoutList *llist = new wxLayoutList();
2475
2476 if(firstLine == lastLine)
2477 {
2478 firstLine->Copy(llist, from.x, to.x);
2479 }
2480 else
2481 {
2482 // Extract objects from first line
2483 firstLine->Copy(llist, from.x);
2484 llist->LineBreak();
2485 // Extract all lines between
2486 for(wxLayoutLine *line = firstLine->GetNextLine();
2487 line != lastLine;
2488 line = line->GetNextLine())
2489 {
2490 line->Copy(llist);
2491 llist->LineBreak();
2492 }
2493 // Extract objects from last line
2494 lastLine->Copy(llist, 0, to.x);
2495 }
2496 return llist;
2497 }
2498
2499 wxLayoutList *
2500 wxLayoutList::GetSelection(wxLayoutDataObject *wxlo, bool invalidate)
2501 {
2502 if(! m_Selection.m_valid)
2503 {
2504 if(m_Selection.m_selecting)
2505 EndSelection();
2506 else
2507 return NULL;
2508 }
2509
2510 if(invalidate) m_Selection.m_valid = false;
2511
2512 wxLayoutList *llist = Copy( m_Selection.m_CursorA,
2513 m_Selection.m_CursorB );
2514
2515 if(llist && wxlo) // export as data object, too
2516 {
2517 wxString string;
2518
2519 wxLayoutExportObject *export;
2520 wxLayoutExportStatus status(llist);
2521 while((export = wxLayoutExport( &status, WXLO_EXPORT_AS_OBJECTS)) != NULL)
2522 {
2523 if(export->type == WXLO_EXPORT_EMPTYLINE)
2524 ; //FIXME missing support for linebreaks in string format
2525 else
2526 export->content.object->Write(string);
2527 delete export;
2528 }
2529
2530 wxlo->SetData(string.c_str(), string.Length()+1);
2531 }
2532 return llist;
2533 }
2534
2535
2536
2537 #define COPY_SI(what) if(si->what != -1) { m_CurrentSetting.what = si->what; fontChanged = TRUE; }
2538
2539 void
2540 wxLayoutList::ApplyStyle(wxLayoutStyleInfo *si, wxDC &dc)
2541 {
2542 bool fontChanged = FALSE;
2543 COPY_SI(family);
2544 COPY_SI(size);
2545 COPY_SI(style);
2546 COPY_SI(weight);
2547 COPY_SI(underline);
2548 if(fontChanged)
2549 dc.SetFont( m_FontCache.GetFont(m_CurrentSetting) );
2550
2551 if(si->m_fg_valid)
2552 {
2553 m_CurrentSetting.m_fg = si->m_fg;
2554 dc.SetTextForeground(m_CurrentSetting.m_fg);
2555 }
2556 if(si->m_bg_valid)
2557 {
2558 m_CurrentSetting.m_bg = si->m_bg;
2559 dc.SetTextBackground(m_CurrentSetting.m_bg);
2560 }
2561 }
2562
2563
2564 #ifdef WXLAYOUT_DEBUG
2565
2566 void
2567 wxLayoutList::Debug(void)
2568 {
2569 wxLayoutLine *line;
2570
2571
2572 for(line = m_FirstLine;
2573 line;
2574 line = line->GetNextLine())
2575 line->Debug();
2576 }
2577
2578 #endif
2579
2580
2581 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2582
2583 wxLayoutPrintout
2584
2585 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2586
2587 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList *llist,
2588 wxString const & title)
2589 :wxPrintout(title)
2590 {
2591 m_llist = llist;
2592 m_title = title;
2593 // remove any highlighting which could interfere with printing:
2594 m_llist->StartSelection();
2595 m_llist->EndSelection();
2596 }
2597
2598 wxLayoutPrintout::~wxLayoutPrintout()
2599 {
2600 }
2601
2602 float
2603 wxLayoutPrintout::ScaleDC(wxDC *dc)
2604 {
2605 // The following bit is taken from the printing sample, let's see
2606 // whether it works for us.
2607
2608 /* You might use THIS code to set the printer DC to ROUGHLY reflect
2609 * the screen text size. This page also draws lines of actual length 5cm
2610 * on the page.
2611 */
2612 // Get the logical pixels per inch of screen and printer
2613 int ppiScreenX, ppiScreenY;
2614 GetPPIScreen(&ppiScreenX, &ppiScreenY);
2615 int ppiPrinterX, ppiPrinterY;
2616 GetPPIPrinter(&ppiPrinterX, &ppiPrinterY);
2617
2618 if(ppiScreenX == 0) // not yet set, need to guess
2619 {
2620 ppiScreenX = 100;
2621 ppiScreenY = 100;
2622 }
2623 if(ppiPrinterX == 0) // not yet set, need to guess
2624 {
2625 ppiPrinterX = 72;
2626 ppiPrinterY = 72;
2627 }
2628
2629 // This scales the DC so that the printout roughly represents the
2630 // the screen scaling. The text point size _should_ be the right size
2631 // but in fact is too small for some reason. This is a detail that will
2632 // need to be addressed at some point but can be fudged for the
2633 // moment.
2634 float scale = (float)((float)ppiPrinterX/(float)ppiScreenX);
2635
2636 // Now we have to check in case our real page size is reduced
2637 // (e.g. because we're drawing to a print preview memory DC)
2638 int pageWidth, pageHeight;
2639 int w, h;
2640 dc->GetSize(&w, &h);
2641 GetPageSizePixels(&pageWidth, &pageHeight);
2642 if(pageWidth != 0) // doesn't work always
2643 {
2644 // If printer pageWidth == current DC width, then this doesn't
2645 // change. But w might be the preview bitmap width, so scale down.
2646 scale = scale * (float)(w/(float)pageWidth);
2647 }
2648 dc->SetUserScale(scale, scale);
2649 return scale;
2650 }
2651
2652 bool wxLayoutPrintout::OnPrintPage(int page)
2653 {
2654 wxDC *dc = GetDC();
2655
2656 ScaleDC(dc);
2657
2658 if (dc)
2659 {
2660 int top, bottom;
2661 top = (page - 1)*m_PrintoutHeight;
2662 bottom = top + m_PrintoutHeight;
2663 // SetDeviceOrigin() doesn't work here, so we need to manually
2664 // translate all coordinates.
2665 wxPoint translate(m_Offset.x,m_Offset.y-top);
2666 m_llist->Draw(*dc, translate, top, bottom);
2667 return true;
2668 }
2669 else
2670 return false;
2671 }
2672
2673 void wxLayoutPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo)
2674 {
2675 /* We allocate a temporary wxDC for printing, so that we can
2676 determine the correct paper size and scaling. We don't actually
2677 print anything on it. */
2678 #ifdef __WXMSW__
2679 wxPrinterDC psdc("","",WXLLIST_TEMPFILE,false);
2680 #else
2681 wxPostScriptDC psdc(WXLLIST_TEMPFILE,false);
2682 #endif
2683
2684 float scale = ScaleDC(&psdc);
2685
2686 psdc.GetSize(&m_PageWidth, &m_PageHeight);
2687 // This sets a left/top origin of 15% and 20%:
2688 m_Offset = wxPoint((15*m_PageWidth)/100, m_PageHeight/20);
2689
2690 // This is the length of the printable area.
2691 m_PrintoutHeight = m_PageHeight - (int) (m_PageHeight * 0.15);
2692 m_PrintoutHeight = (int)( m_PrintoutHeight / scale); // we want to use the real paper height
2693
2694
2695 m_NumOfPages = 1 +
2696 (int)( m_llist->GetSize().y / (float)(m_PrintoutHeight));
2697
2698 *minPage = 1;
2699 *maxPage = m_NumOfPages;
2700
2701 *selPageFrom = 1;
2702 *selPageTo = m_NumOfPages;
2703 wxRemoveFile(WXLLIST_TEMPFILE);
2704 }
2705
2706 bool wxLayoutPrintout::HasPage(int pageNum)
2707 {
2708 return pageNum <= m_NumOfPages;
2709 }
2710
2711 /*
2712 Stupid wxWindows doesn't draw proper ellipses, so we comment this
2713 out. It's a waste of paper anyway.
2714 */
2715 #if 0
2716 void
2717 wxLayoutPrintout::DrawHeader(wxDC &dc,
2718 wxPoint topleft, wxPoint bottomright,
2719 int pageno)
2720 {
2721 // make backups of all essential parameters
2722 const wxBrush& brush = dc.GetBrush();
2723 const wxPen& pen = dc.GetPen();
2724 const wxFont& font = dc.GetFont();
2725
2726 dc.SetBrush(*wxWHITE_BRUSH);
2727 dc.SetPen(wxPen(*wxBLACK,0,wxSOLID));
2728 dc.DrawRoundedRectangle(topleft.x,
2729 topleft.y,bottomright.x-topleft.x,
2730 bottomright.y-topleft.y);
2731 dc.SetBrush(*wxBLACK_BRUSH);
2732 wxFont myfont = wxFont((WXLO_DEFAULTFONTSIZE*12)/10,
2733 wxSWISS,wxNORMAL,wxBOLD,false,"Helvetica");
2734 dc.SetFont(myfont);
2735
2736 wxString page;
2737 page = "9999/9999 "; // many pages...
2738 long w,h;
2739 dc.GetTextExtent(page,&w,&h);
2740 page.Printf("%d/%d", pageno, (int) m_NumOfPages);
2741 dc.DrawText(page,bottomright.x-w,topleft.y+h/2);
2742 dc.GetTextExtent("XXXX", &w,&h);
2743 dc.DrawText(m_title, topleft.x+w,topleft.y+h/2);
2744
2745 // restore settings
2746 dc.SetPen(pen);
2747 dc.SetBrush(brush);
2748 dc.SetFont(font);
2749 }
2750 #endif
2751
2752
2753 wxFont &
2754 wxFontCache::GetFont(int family, int size, int style, int weight,
2755 bool underline)
2756 {
2757 for(wxFCEList::iterator i = m_FontList.begin();
2758 i != m_FontList.end(); i++)
2759 if( (**i).Matches(family, size, style, weight, underline) )
2760 return (**i).GetFont();
2761 // not found:
2762 wxFontCacheEntry *fce = new wxFontCacheEntry(family, size, style,
2763 weight, underline);
2764 m_FontList.push_back(fce);
2765 return fce->GetFont();
2766 }
2767