]> git.saurik.com Git - wxWidgets.git/blob - user/wxLayout/wxllist.cpp
fixes
[wxWidgets.git] / user / wxLayout / wxllist.cpp
1 /*-*- c++ -*-********************************************************
2 * wxFTCanvas: a canvas for editing formatted text *
3 * *
4 * (C) 1998 by Karsten Ballüder (Ballueder@usa.net) *
5 * *
6 * $Id$
7 *******************************************************************/
8
9 /*
10 - each Object knows its size and how to draw itself
11 - the list is responsible for calculating positions
12 - the draw coordinates for each object are the top left corner
13 - coordinates only get calculated when things get redrawn
14 - The cursor position is the position before an object, i.e. if the
15 buffer starts with a text-object, cursor 0,0 is just before the
16 first character. For all non-text objects, the cursor positions
17 are 0==before or 1==behind. So that all non-text objects count as
18 one cursor position.
19 */
20
21 /*
22 TODO:
23
24 - new cursor movement calculations
25 - moving lines and moving across linebreaks still broken
26
27 - blinking cursor
28 - mouse click positions cursor
29 - selection (SetMark(), GetSelection())
30 - DND acceptance of text
31
32 - wxlwindow: formatting menu: problem with checked/unchecked consistency gtk bug?
33 */
34
35 #define USE_NEW_CURSORCODE 1
36
37 /*
38 Known wxGTK bugs:
39 - MaxX()/MaxY() don't get set
40 */
41
42
43 #ifdef __GNUG__
44 #pragma implementation "wxllist.h"
45 #endif
46
47 //#include "Mpch.h"
48 #ifdef M_BASEDIR
49 # include "gui/wxllist.h"
50 #else
51 # include "wxllist.h"
52 #endif
53
54 #ifndef USE_PCH
55 # include "iostream.h"
56 # include <wx/dc.h>
57 # include <wx/postscrp.h>
58 # include <wx/print.h>
59 # include <wx/log.h>
60 #endif
61
62 #define BASELINESTRETCH 12
63
64 // This should never really get created
65 #define WXLLIST_TEMPFILE "__wxllist.tmp"
66
67 #ifdef WXLAYOUT_DEBUG
68 static const char *g_aTypeStrings[] =
69 {
70 "invalid", "text", "cmd", "icon", "linebreak"
71 };
72
73 # define wxLayoutDebug wxLogDebug
74 # define WXL_VAR(x) cerr << #x " = " << x << endl;
75 # define WXL_DBG_POINT(p) wxLogDebug(#p ": (%d, %d)", p.x, p.y)
76 # define WXL_TRACE(f) wxLogDebug(#f ": ")
77 # define TypeString(t) g_aTypeStrings[t]
78
79 void
80 wxLayoutObjectBase::Debug(void)
81 {
82 CoordType bl = 0;
83 wxLogDebug("%s: size = %dx%d, bl = %d",
84 TypeString(GetType()), GetSize(&bl).x, GetSize(&bl).y, bl);
85 }
86
87 #else
88 # define WXL_VAR(x)
89 # define WXL_DBG_POINT(p)
90 # define WXL_TRACE(f)
91 # define ShowCurrentObject()
92 # define TypeString(t) ""
93 inline void wxLayoutDebug(const char *, ...) { }
94 #endif
95
96
97 //-------------------------- wxLayoutObjectText
98
99 wxLayoutObjectText::wxLayoutObjectText(const String &txt)
100 {
101 m_Text = txt;
102 m_Width = 0;
103 m_Height = 0;
104 m_Position = wxPoint(-1,-1);
105 }
106
107
108 wxPoint
109 wxLayoutObjectText::GetSize(CoordType *baseLine) const
110 {
111 if(baseLine) *baseLine = m_BaseLine;
112 return wxPoint(m_Width, m_Height);
113 }
114
115 void
116 wxLayoutObjectText::Draw(wxDC &dc, wxPoint const &translate)
117 {
118 dc.DrawText(Str(m_Text), m_Position.x + translate.x, m_Position.y+translate.y);
119 m_IsDirty = false;
120 }
121
122
123 void
124 wxLayoutObjectText::Layout(wxDC &dc, wxPoint position, CoordType baseLine)
125 {
126 long descent = 0l;
127
128 if(m_Position.x != position.x || m_Position.y != position.y)
129 m_IsDirty = true;
130
131 m_Position = position;
132 dc.GetTextExtent(Str(m_Text),&m_Width, &m_Height, &descent);
133 m_BaseLine = m_Height - descent;
134 if(m_Position.x != position.x || m_Position.y != position.y)
135 m_IsDirty = true;
136 }
137
138 #ifdef WXLAYOUT_DEBUG
139 void
140 wxLayoutObjectText::Debug(void)
141 {
142 wxLayoutObjectBase::Debug();
143 wxLogDebug(" `%s`", m_Text.c_str());
144 }
145 #endif
146
147 //-------------------------- wxLayoutObjectIcon
148
149 wxLayoutObjectIcon::wxLayoutObjectIcon(wxIcon const &icon)
150 {
151 m_Position = wxPoint(-1,-1);
152 m_Icon = new wxIcon(icon);
153 }
154
155 wxLayoutObjectIcon::wxLayoutObjectIcon(wxIcon *icon)
156 {
157 m_Icon = icon;
158 }
159
160 void
161 wxLayoutObjectIcon::Draw(wxDC &dc, wxPoint const &translate)
162 {
163 dc.DrawIcon(m_Icon,m_Position.x+translate.x, m_Position.y+translate.y);
164 }
165
166 void
167 wxLayoutObjectIcon::Layout(wxDC &dc, wxPoint position, CoordType baseLine)
168 {
169 if(m_Position.x != position.x || m_Position.y != position.y)
170 m_IsDirty = true;
171 m_Position = position;
172 }
173
174 wxPoint
175 wxLayoutObjectIcon::GetSize(CoordType *baseLine) const
176 {
177 if(baseLine) *baseLine = m_Icon->GetHeight();
178 return wxPoint(m_Icon->GetWidth(), m_Icon->GetHeight());
179 }
180
181 //-------------------------- wxLayoutObjectCmd
182
183
184 wxLayoutObjectCmd::wxLayoutObjectCmd(int size, int family, int style, int
185 weight, bool underline,
186 wxColour const *fg, wxColour const *bg)
187
188 {
189 m_font = new wxFont(size,family,style,weight,underline);
190 m_ColourFG = fg;
191 m_ColourBG = bg;
192 }
193
194 wxLayoutObjectCmd::~wxLayoutObjectCmd()
195 {
196 delete m_font;
197 }
198
199 wxLayoutStyleInfo *
200 wxLayoutObjectCmd::GetStyle(void) const
201 {
202 wxLayoutStyleInfo *si = new wxLayoutStyleInfo();
203
204
205 si->size = m_font->GetPointSize();
206 si->family = m_font->GetFamily();
207 si->style = m_font->GetStyle();
208 si->underline = m_font->GetUnderlined();
209 si->weight = m_font->GetWeight();
210
211 si->fg_red = m_ColourFG->Red();
212 si->fg_green = m_ColourFG->Green();
213 si->fg_blue = m_ColourFG->Blue();
214 si->bg_red = m_ColourBG->Red();
215 si->bg_green = m_ColourBG->Green();
216 si->bg_blue = m_ColourBG->Blue();
217
218 return si;
219 }
220
221 void
222 wxLayoutObjectCmd::Draw(wxDC &dc, wxPoint const &translate)
223 {
224 wxASSERT(m_font);
225 dc.SetFont(m_font);
226 if(m_ColourFG)
227 dc.SetTextForeground(*m_ColourFG);
228 if(m_ColourBG)
229 dc.SetTextBackground(*m_ColourBG);
230 }
231 void
232 wxLayoutObjectCmd::Layout(wxDC &dc, wxPoint p, CoordType baseline)
233 {
234 m_Position = p; // required so we can find the right object for cursor
235 // this get called, so that recalculation uses right font sizes
236 Draw(dc,wxPoint(0,0));
237 }
238
239 //-------------------------- wxLayoutList
240
241 wxLayoutList::wxLayoutList()
242 {
243 m_DefaultSetting = NULL;
244 Clear();
245 }
246
247 wxLayoutList::~wxLayoutList()
248 {
249 if(m_DefaultSetting)
250 delete m_DefaultSetting;
251 // no deletion of objects, they are owned by the list
252 }
253
254 void
255 wxLayoutList::LineBreak(void)
256 {
257 Insert(new wxLayoutObjectLineBreak);
258 m_CursorPos.x = 0; m_CursorPos.y++;
259 }
260
261 void
262 wxLayoutList::SetFont(int family, int size, int style, int weight,
263 int underline, wxColour const *fg,
264 wxColour const *bg)
265 {
266 if(family != -1) m_FontFamily = family;
267 if(size != -1) m_FontPtSize = size;
268 if(style != -1) m_FontStyle = style;
269 if(weight != -1) m_FontWeight = weight;
270 if(underline != -1) m_FontUnderline = underline != 0;
271
272 if(fg != NULL) m_ColourFG = fg;
273 if(bg != NULL) m_ColourBG = bg;
274
275 Insert(
276 new wxLayoutObjectCmd(m_FontPtSize,m_FontFamily,m_FontStyle,m_FontWeight,m_FontUnderline,
277 m_ColourFG, m_ColourBG));
278 }
279
280 void
281 wxLayoutList::SetFont(int family, int size, int style, int weight,
282 int underline, char const *fg, char const *bg)
283
284 {
285 wxColour const
286 * cfg = NULL,
287 * cbg = NULL;
288
289 if( fg )
290 cfg = wxTheColourDatabase->FindColour(fg);
291 if( bg )
292 cbg = wxTheColourDatabase->FindColour(bg);
293
294 SetFont(family,size,style,weight,underline,cfg,cbg);
295 }
296
297
298 /// for access by wxLayoutWindow:
299 void
300 wxLayoutList::GetSize(CoordType *max_x, CoordType *max_y,
301 CoordType *lineHeight)
302 {
303
304 if(max_x) *max_x = m_MaxX;
305 if(max_y) *max_y = m_MaxY;
306 if(lineHeight) *lineHeight = m_LineHeight;
307 }
308
309 void
310 wxLayoutList::ResetSettings(wxDC &dc)
311 {
312 // setting up the default:
313 dc.SetTextForeground( *wxBLACK );
314 dc.SetTextBackground( *wxWHITE );
315 dc.SetBackgroundMode( wxSOLID ); // to enable setting of text background
316 dc.SetFont( *wxNORMAL_FONT );
317 if(m_DefaultSetting)
318 m_DefaultSetting->Draw(dc,wxPoint(0,0));
319 }
320
321 void
322 wxLayoutList::Layout(wxDC &dc, wxLayoutMargins *margins)
323 {
324 iterator i;
325
326 // first object in current line
327 wxLayoutObjectList::iterator headOfLine;
328 // where we draw next
329 wxPoint position, position_HeadOfLine;
330 // size of last object
331 wxPoint size;
332 CoordType baseLine = m_FontPtSize;
333 CoordType baseLineSkip = (BASELINESTRETCH * baseLine)/10;
334 CoordType objBaseLine = baseLine;
335 wxLayoutObjectType type;
336
337 // we need to count cursor positions
338 wxPoint cursorPos = wxPoint(0,0);
339
340 wxLayoutObjectBase *cursorObject = NULL; // let's find it again
341
342 if(margins)
343 {
344 position.y = margins->top;
345 position.x = margins->left;
346 }
347 else
348 {
349 position.y = 0;
350 position.x = 0;
351 }
352
353 ResetSettings(dc);
354
355 i = begin();
356 headOfLine = i;
357 position_HeadOfLine = position;
358
359 do
360 {
361 if(i == end())
362 return;
363
364 type = (*i)->GetType();
365 (*i)->Layout(dc, position, baseLine);
366 size = (*i)->GetSize(&objBaseLine);
367 // calculate next object's position:
368 position.x += size.x;
369
370 // do we need to increase the line's height?
371 if(size.y > baseLineSkip)
372 {
373 baseLineSkip = size.y;
374 i = headOfLine; position = position_HeadOfLine;
375 continue;
376 }
377 if(objBaseLine > baseLine)
378 {
379 baseLine = objBaseLine;
380 i = headOfLine; position = position_HeadOfLine;
381 continue;
382 }
383
384 // when we reach here, the coordinates are valid, this part of
385 // the loop gets run only once per object
386 if(position.x > m_MaxX)
387 m_MaxX = position.x;
388 if(type == WXLO_TYPE_LINEBREAK)
389 {
390 cursorPos.x = 0; cursorPos.y ++;
391 }
392 else
393 cursorPos.x += (**i).CountPositions();
394
395 // now check whether we have finished handling this line:
396 if(type == WXLO_TYPE_LINEBREAK && i != tail())
397 {
398 position.x = margins ? margins->left : 0;
399 position.y += baseLineSkip;
400 baseLine = m_FontPtSize;
401 objBaseLine = baseLine; // not all objects set it
402 baseLineSkip = (BASELINESTRETCH * baseLine)/10;
403 headOfLine = i;
404 headOfLine++;
405 position_HeadOfLine = position;
406 }
407 if(cursorObject == NULL && cursorPos.y == m_CursorPos.y) // look for cursor
408 {
409 if(cursorPos.x >= m_CursorPos.x &&
410 m_CursorPos.x-cursorPos.x+(**i).CountPositions()) // cursor is in current object
411 {
412 cursorObject = *i;
413 CalculateCursor(dc);
414 }
415 }
416 i++;
417 }
418 while(i != end());
419 m_MaxY = position.y + baseLineSkip;
420 }
421
422 void
423 wxLayoutList::Draw(wxDC &dc,
424 CoordType fromLine, CoordType toLine,
425 iterator start,
426 wxPoint const &translate)
427 {
428 Layout(dc); // FIXME just for now
429
430 ResetSettings(dc);
431
432 wxLayoutObjectList::iterator i;
433
434 if(start == iterator(NULL))
435 start = begin();
436 else // we need to restore font settings
437 {
438 for( i = begin() ; i != start; i++)
439 if((**i).GetType() == WXLO_TYPE_CMD)
440 (**i).Draw(dc,translate); // apply font settings
441 }
442
443 while( start != end() && (**start).GetPosition().y < fromLine)
444 {
445 if((**start).GetType() == WXLO_TYPE_CMD)
446 (**start).Draw(dc,translate); // apply font settings
447 start++;
448 }
449 for( i = start ;
450 i != end() && (toLine == -1 || (**i).GetPosition().y < toLine) ;
451 i++ )
452 (*i)->Draw(dc,translate);
453 }
454
455 /** Erase at least to end of line */
456 void
457 wxLayoutList::EraseAndDraw(wxDC &dc, iterator start)
458 {
459 //look for begin of line
460 while(start != end() && start != begin() && (**start).GetType() !=
461 WXLO_TYPE_LINEBREAK)
462 start--;
463 if(start == iterator(NULL))
464 start = begin();
465 if(start == iterator(NULL))
466 return;
467
468 wxPoint p = (**start).GetPosition();
469
470 WXL_VAR(p.x);WXL_VAR(p.y);
471 //FIXME: wxGTK: MaxX()/MaxY() broken
472 //WXL_VAR(dc.MaxX()); WXL_VAR(dc.MaxY());
473 dc.SetBrush(*wxWHITE_BRUSH);
474 dc.SetPen(wxPen(*wxWHITE,0,wxTRANSPARENT));
475 dc.DrawRectangle(p.x,p.y,2000,2000); //dc.MaxX(),dc.MaxY());
476 Draw(dc,-1,-1,start,wxPoint(0,0));
477 //dc.DrawRectangle(p.x,p.y,2000,2000); //dc.MaxX(),dc.MaxY());
478 }
479
480
481 void
482 wxLayoutList::CalculateCursor(wxDC &dc)
483 {
484 CoordType width, height, descent;
485 CoordType baseLineSkip = 20; //FIXME
486
487 CoordType offset;
488 if( FindCurrentObject() == iterator(NULL)) // empty list
489 {
490 DrawCursor(dc,true); // erase it
491 m_CursorCoords = wxPoint(0,0);
492 m_CursorSize = wxPoint(2,baseLineSkip);
493 m_CursorMoved = false; // coords are valid
494 return;
495 }
496 wxLayoutObjectBase &obj = **FindCurrentObject(&offset);
497
498 WXL_VAR(offset);
499 DrawCursor(dc,true); // erase it
500 m_CursorCoords = obj.GetPosition();
501 WXL_VAR(m_CursorCoords.x);
502 if(obj.GetType() == WXLO_TYPE_TEXT)
503 {
504 wxLayoutObjectText *tobj = (wxLayoutObjectText *)&obj;
505 String & str = tobj->GetText();
506 String sstr = str.substr(0,offset);
507 WXL_VAR(sstr);
508 dc.GetTextExtent(sstr,&width,&height,&descent);
509 WXL_VAR(width);
510 m_CursorCoords = wxPoint(m_CursorCoords.x+width,
511 m_CursorCoords.y);
512 m_CursorSize = wxPoint(2,height);
513 }
514 else if(obj.GetType() == WXLO_TYPE_LINEBREAK)
515 {
516 m_CursorCoords = wxPoint(0, m_CursorCoords.y);
517 m_CursorSize = wxPoint(2,baseLineSkip);
518 }
519 else
520 {
521 // this is not necessarily the most "beautiful" solution:
522 //cursorPosition = wxPoint(position.x, position.y);
523 //cursorSize = wxPoint(size.x > 0 ? size.x : 1,size.y > 0 ? size.y : baseLineSkip);
524 m_CursorCoords = wxPoint(m_CursorCoords.x+obj.GetSize().x, m_CursorCoords.y);
525 m_CursorSize = wxPoint(2, obj.GetSize().y);
526 if(m_CursorSize.y < 1) m_CursorSize.y = baseLineSkip;
527 }
528 WXL_VAR(m_CursorCoords.x);
529 m_CursorMoved = false; // coords are valid
530 }
531
532 void
533 wxLayoutList::DrawCursor(wxDC &dc, bool erase)
534 {
535 if(! m_Editable)
536 return;
537
538 if(erase)
539 {
540 //dc.SetBrush(*wxWHITE_BRUSH);
541 //dc.SetPen(wxPen(*wxWHITE,1,wxSOLID));
542 //dc.DrawRectangle(m_CursorCoords.x, m_CursorCoords.y, m_CursorSize.x, m_CursorSize.y);
543 dc.Blit(m_CursorCoords.x, m_CursorCoords.y, m_CursorSize.x,
544 m_CursorSize.y, &m_CursorMemDC,
545 0, 0, 0, 0);
546 }
547 else
548 {
549 if(IsDirty() || CursorMoved())
550 {
551 DrawCursor(dc,true);
552 Layout(dc);
553 }
554 // Save background:
555 wxBitmap bm(m_CursorSize.x+1,m_CursorSize.y+1);
556 m_CursorMemDC.SelectObject(bm);
557 m_CursorMemDC.Blit(0, 0, m_CursorSize.x, m_CursorSize.y,
558 &dc, m_CursorCoords.x,
559 m_CursorCoords.y, 0, 0);
560 dc.SetBrush(*wxBLACK_BRUSH);
561 dc.SetPen(wxPen(*wxBLACK,1,wxSOLID));
562 dc.DrawRectangle(m_CursorCoords.x, m_CursorCoords.y,
563 m_CursorSize.x, m_CursorSize.y);
564 }
565 }
566
567
568
569
570
571
572
573 #ifdef WXLAYOUT_DEBUG
574 void
575 wxLayoutList::Debug(void)
576 {
577 CoordType offs;
578 wxLayoutObjectList::iterator i;
579
580 wxLogDebug("------------------------debug start-------------------------");
581 for(i = begin(); i != end(); i++)
582 (*i)->Debug();
583 wxLogDebug("-----------------------debug end----------------------------");
584
585 // show current object:
586 ShowCurrentObject();
587 i = FindCurrentObject(&offs);
588 wxLogDebug(" line length: %l", (long int) GetLineLength(i,offs));
589 if(i == end())
590 {
591 wxLogDebug("<<no object found>>");
592 return; // FIXME we should set cursor position to maximum allowed
593 // value then
594 }
595 if((*i)->GetType() == WXLO_TYPE_TEXT)
596 wxLogDebug(" \"%s\", offs=%d",((wxLayoutObjectText *)(*i))->GetText().c_str(), (int) offs);
597 else
598 wxLogDebug(g_aTypeStrings[(*i)->GetType()]);
599
600 }
601
602 void
603 wxLayoutList::ShowCurrentObject()
604 {
605 CoordType offs;
606 wxLayoutObjectList::iterator i = FindCurrentObject(&offs);
607
608 i = FindCurrentObject(&offs);
609 wxLogDebug(" Line length: %d", GetLineLength(i));
610
611 if(i == end())
612 {
613 wxLogDebug("<<no object found>>");
614 return; // FIXME we should set cursor position to maximum allowed
615 // value then
616 }
617 if((*i)->GetType() == WXLO_TYPE_TEXT)
618 wxLogDebug(" \"%s\", offs: %d",
619 ((wxLayoutObjectText *)(*i))->GetText().c_str(), offs);
620 else
621 wxLogDebug(" %s", TypeString((*i)->GetType()));
622 wxLayoutDebug("CursorPos (%d, %d)", (int) m_CursorPos.x, (int) m_CursorPos.y);
623 wxLayoutDebug("CursorOffset = %d", (int) m_CursorOffset);
624 wxLayoutDebug("CursorObject = %p", m_CursorObject);
625
626 }
627
628 #endif
629
630 /******************** editing stuff ********************/
631
632 // don't change this, I know how to optimise this and will do it real
633 // soon (KB)
634
635 /*
636 * FindObjectCursor:
637 * Finds the object belonging to a given cursor position cpos and
638 * returns an iterator to that object and stores the relative cursor
639 * position in offset.
640 *
641 * For linebreaks, the offset can be 0=before or 1=after.
642 *
643 * If the cpos coordinates don't exist, they are modified.
644 */
645
646 wxLayoutObjectList::iterator
647 wxLayoutList::FindObjectCursor(wxPoint *cpos, CoordType *offset)
648 {
649 wxPoint object = wxPoint(0,0); // runs along the objects
650 CoordType width = 0;
651 wxLayoutObjectList::iterator i, begin_it;
652 int go_up;
653
654 //#ifdef WXLAYOUT_DEBUG
655 // wxLayoutDebug("Looking for object at (%d, %d)", cpos->x, cpos->y);
656 //#endif
657
658 // optimisation: compare to last looked at object:
659 if(cpos->y > m_FoundCursor.y || (cpos->y == m_FoundCursor.y &&
660 cpos->x >= m_FoundCursor.x))
661 go_up = 1;
662 else
663 go_up = 0;
664
665 //broken at the moment
666 //begin_it = m_FoundIterator;
667 //m_FoundCursor = *cpos;
668 begin_it = begin();
669 go_up = 1;
670 for(i = begin_it; i != end() && object.y <= cpos->y; )
671 {
672 width = (**i).CountPositions();
673 if(cpos->y == object.y) // a possible candidate
674 {
675 if((**i).GetType() ==WXLO_TYPE_LINEBREAK)
676 {
677 if(cpos->x == object.x)
678 {
679 if(offset) *offset = 0;
680 return m_FoundIterator = i;
681 }
682 if(offset) *offset=1;
683 cpos->x = object.x;
684 return m_FoundIterator = i;
685 }
686 if(cpos->x >= object.x && cpos->x <= object.x+width) // overlap
687 {
688 if(offset) *offset = cpos->x-object.x;
689 //#ifdef WXLAYOUT_DEBUG
690 // wxLayoutDebug(" found object at (%d, %d), type: %s",
691 // object.x, object.y, TypeString((*i)->GetType()));
692 //#endif
693 return m_FoundIterator = i;
694 }
695 }
696 // no overlap, increment coordinates
697 object.x += width;
698 if((**i).GetType() == WXLO_TYPE_LINEBREAK)
699 {
700 object.x = 0;
701 object.y++;
702 }
703 if(go_up)
704 i++;
705 else
706 i--;
707 }//for
708 //#ifdef WXLAYOUT_DEBUG
709 // wxLayoutDebug(" not found");
710 //#endif
711 // return last object, coordinates of that one:
712 i = tail();
713 if(i == end())
714 return m_FoundIterator = i;
715 if((**i).GetType()==WXLO_TYPE_LINEBREAK)
716 {
717 if(offset)
718 *offset = 1;
719 return m_FoundIterator = i;
720 }
721 cpos->x = object.x; // would be the coordinate of next object
722 cpos->y = object.y;
723 cpos->x += width; // last object's width
724 if(*offset) *offset = cpos->x-object.x;
725 return m_FoundIterator = i; // not found
726 }
727
728 wxLayoutObjectList::iterator
729 wxLayoutList::FindCurrentObject(CoordType *offset)
730 {
731 #if ! defined( USE_NEW_CURSORCODE )
732 wxLayoutObjectList::iterator obj = end();
733
734 obj = FindObjectCursor(&m_CursorPos, offset);
735 if(obj == end()) // not ideal yet
736 {
737 obj = tail();
738 if(obj != end()) // tail really exists
739 *offset = (*obj)->CountPositions(); // at the end of it
740 }
741 return obj;
742 #else
743 if(offset)
744 *offset = m_CursorOffset;
745 return m_CursorObject;
746 #endif
747 }
748
749 #if ! defined( USE_NEW_CURSORCODE )
750
751 bool
752 wxLayoutList::MoveCursor(int dx, int dy)
753 {
754 CoordType offs, lineLength;
755 wxLayoutObjectList::iterator i;
756
757 m_CursorMoved = true;
758
759 bool rc = true; // have we moved?
760
761 //FIXME calculate cursor object & offset for y movements
762 if(dy > 0 && m_CursorPos.y < m_MaxLine)
763 m_CursorPos.y += dy;
764 else if(dy < 0 && m_CursorPos.y > 0)
765 m_CursorPos.y += dy; // dy is negative
766 if(m_CursorPos.y < 0)
767 {
768 m_CursorPos.y = 0;
769 rc = false;
770 }
771 else if (m_CursorPos.y > m_MaxLine)
772 {
773 m_CursorPos.y = m_MaxLine;
774 rc = false;
775 }
776
777 while(dx > 0)
778 {
779 i = FindCurrentObject(&offs);
780 lineLength = GetLineLength(i,offs);
781 if(m_CursorPos.x < lineLength)
782 {
783 m_CursorPos.x ++;
784 dx--;
785 continue;
786 }
787 else
788 {
789 if(m_CursorPos.y < m_MaxLine)
790 {
791 m_CursorPos.y++;
792 m_CursorPos.x = 0;
793 dx--;
794 }
795 else
796 {
797 rc = false;
798 break; // cannot move there
799 }
800 }
801 }
802 while(dx < 0)
803 {
804 if(m_CursorPos.x > 0)
805 {
806 m_CursorPos.x --;
807 dx++;
808 }
809 else
810 {
811 if(m_CursorPos.y > 0)
812 {
813 m_CursorPos.y --;
814 m_CursorPos.x = 0;
815 i = FindCurrentObject(&offs);
816 lineLength = GetLineLength(i,offs);
817 m_CursorPos.x = lineLength;
818 dx++;
819 continue;
820 }
821 else
822 {
823 rc = false;
824 break; // cannot move left any more
825 }
826 }
827 }
828 // final adjustment:
829 i = FindCurrentObject(&offs);
830 lineLength = GetLineLength(i,offs);
831 if(m_CursorPos.x > lineLength)
832 {
833 m_CursorPos.x = lineLength;
834 rc = false;
835 }
836 #ifdef WXLAYOUT_DEBUG
837 ShowCurrentObject();
838 #endif
839 return rc;
840 }
841
842 #else
843
844 bool
845 wxLayoutList::MoveCursor(int dx, int dy)
846 {
847 CoordType diff;
848
849 m_CursorMoved = true;
850
851 enum { up, down} direction;
852
853 wxPoint newPos = wxPoint(m_CursorPos.x + dx,
854 m_CursorPos.y + dy);
855
856 // check for bounds
857 if(newPos.x < 0) newPos.x = 0;
858 if(newPos.y < 0) newPos.y = 0;
859 else if(newPos.y > m_MaxLine) newPos.y = m_MaxLine;
860
861 if(newPos.y > m_CursorPos.y ||
862 newPos.y == m_CursorPos.y &&
863 newPos.x >= m_CursorPos.x)
864 direction = up;
865 else
866 direction = down;
867
868 // now move cursor forwards until at the new position:
869
870 // first, go to the right line:
871 while(newPos.y != m_CursorPos.y)
872 {
873 if(direction == up)
874 {
875 m_CursorPos.x +=
876 (**m_CursorObject).CountPositions() - m_CursorOffset;
877 if(m_CursorObject == tail())
878 break; // can't go any further
879 if((**m_CursorObject).GetType() == WXLO_TYPE_LINEBREAK
880 && m_CursorOffset == 1)
881 {
882 m_CursorPos.y++; m_CursorPos.x = 0;
883 }
884 m_CursorObject ++; m_CursorOffset = 0;
885 }
886 else // down
887 {
888 m_CursorPos.x -= m_CursorOffset;
889 if(m_CursorObject == begin())
890 break; // can't go any further
891 if((**m_CursorObject).GetType() == WXLO_TYPE_LINEBREAK &&
892 m_CursorOffset == 0)
893 {
894 m_CursorPos.y--;
895 m_CursorPos.x = GetLineLength(m_CursorObject);
896 }
897 m_CursorObject --; m_CursorOffset = (**m_CursorObject).CountPositions();
898 }
899 }
900 if(newPos.y != m_CursorPos.y) // reached begin/end of list,
901 newPos.y = m_CursorPos.y; // exited by break
902
903 // now line is right, go to right column:
904 direction = newPos.x >= m_CursorPos.x ? up : down;
905 while(newPos.x != m_CursorPos.x)
906 {
907 if(direction == up)
908 {
909 diff = newPos.x - m_CursorPos.x;
910 if(diff >= (**m_CursorObject).CountPositions())
911 {
912 m_CursorPos.x += (**m_CursorObject).CountPositions();
913 if(m_CursorObject == tail())
914 {
915 m_CursorOffset = (**m_CursorObject).CountPositions();
916 break; // cannot go further
917 }
918 m_CursorObject++; m_CursorOffset = 0;
919 }
920 else
921 {
922 m_CursorPos.x += diff;
923 m_CursorOffset += diff;
924 }
925 }
926 else // down
927 {
928 diff = m_CursorPos.x - newPos.x;
929 if(diff >= m_CursorOffset)
930 {
931 if(m_CursorObject == begin())
932 {
933 m_CursorOffset = 0;
934 m_CursorPos.x = 0;
935 break; // cannot go further
936 }
937 m_CursorObject--;
938 m_CursorOffset = (**m_CursorObject).CountPositions();
939 }
940 else
941 {
942 m_CursorPos.x -= diff;
943 m_CursorOffset -= diff;
944 }
945 }
946 }
947 return true; // FIXME: when return what?
948 }
949 #endif
950
951 void
952 wxLayoutList::Delete(CoordType count)
953 {
954 WXL_TRACE(Delete);
955
956 if(!m_Editable)
957 return;
958
959 WXL_VAR(count);
960
961 m_bModified = true;
962
963 CoordType offs;
964 wxLayoutObjectList::iterator i;
965
966 do
967 {
968 i = FindCurrentObject(&offs);
969 startover: // ugly, but easiest way to do it
970 if(i == end())
971 return; // we cannot delete anything more
972
973 /* Here we need to treat linebreaks differently.
974 If offs==0 we are before the linebreak, otherwise behind. */
975 if((*i)->GetType() == WXLO_TYPE_LINEBREAK)
976 {
977 if(offs == 0)
978 {
979 m_MaxLine--;
980 erase(i);
981 m_CursorObject = i; // new i!
982 m_CursorOffset = 0; // Pos unchanged
983 count--;
984 continue; // we're done
985 }
986 else // delete the object behind the linebreak
987 {
988 i++; // we increment and continue as normal
989 offs=0;
990 goto startover;
991 }
992 }
993 else if((*i)->GetType() == WXLO_TYPE_TEXT)
994 {
995 wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i;
996 CoordType len = tobj->CountPositions();
997 // If we find the end of a text object, this means that we
998 // have to delete from the object following it.
999 if(len == offs)
1000 {
1001 i++;
1002 offs = 0;
1003 goto startover;
1004 }
1005 else if(len <= count) // delete this object
1006 {
1007 count -= len;
1008 erase(i);
1009 m_CursorObject = i;
1010 m_CursorOffset = 0;
1011 continue;
1012 }
1013 else
1014 {
1015 len = count;
1016 tobj->GetText().erase(offs,len);
1017 // cursor unchanged
1018 return; // we are done
1019 }
1020 }
1021 else// all other objects: delete the object
1022 // this only works as expected if the non-text object has 0/1
1023 // as offset values. Not tested with "longer" objects.
1024 {
1025 CoordType len = (*i)->CountPositions();
1026 if(offs == 0)
1027 {
1028 count = count > len ? count -= len : 0;
1029 erase(i); // after this, i is the iterator for the
1030 // following object
1031 m_CursorObject = i;
1032 m_CursorOffset = 0;
1033 continue;
1034 }
1035 else // delete the following object
1036 {
1037 i++; // we increment and continue as normal
1038 offs=0;
1039 goto startover;
1040 }
1041 }
1042 }
1043 while(count && i != end());
1044 }
1045
1046 void
1047 wxLayoutList::Insert(wxLayoutObjectBase *obj)
1048 {
1049 wxCHECK_RET( obj, "no object to insert" );
1050
1051 m_bModified = true;
1052
1053 CoordType offs;
1054 wxLayoutObjectList::iterator i = FindCurrentObject(&offs);
1055
1056 // WXL_TRACE(Insert(obj));
1057
1058 if(i == end())
1059 {
1060 push_back(obj);
1061 m_CursorObject = tail();
1062 }
1063 else if(offs == 0)
1064 {
1065 insert(i,obj);
1066 m_CursorObject = i;
1067 }
1068 // do we have to split a text object?
1069 else if((*i)->GetType() == WXLO_TYPE_TEXT && offs != (*i)->CountPositions())
1070 {
1071 wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
1072 #ifdef WXLAYOUT_DEBUG
1073 wxLayoutDebug("text: %s", tobj->GetText().c_str());
1074 WXL_VAR(offs);
1075 #endif
1076 String left = tobj->GetText().substr(0,offs); // get part before cursor
1077 WXL_VAR(left.c_str());
1078 tobj->GetText() = tobj->GetText().substr(offs,(*i)->CountPositions()-offs); // keeps the right half
1079 WXL_VAR(tobj->GetText().c_str());
1080 insert(i,obj);
1081 insert(i,new wxLayoutObjectText(left)); // inserts before
1082 }
1083 else
1084 {
1085 // all other cases, append after object:
1086 wxLayoutObjectList::iterator j = i; // we want to apend after this object
1087 j++;
1088 if(j != end())
1089 {
1090 insert(j, obj);
1091 m_CursorObject = j;
1092 }
1093 else
1094 {
1095 push_back(obj);
1096 m_CursorObject = tail();
1097 }
1098 }
1099 m_CursorPos.x += obj->CountPositions();
1100 m_CursorOffset = obj->CountPositions();
1101
1102 if(obj->GetType() == WXLO_TYPE_LINEBREAK)
1103 m_MaxLine++;
1104 m_CursorMoved = true;
1105 }
1106
1107 void
1108 wxLayoutList::Insert(String const &text)
1109 {
1110 wxLayoutObjectText *tobj = NULL;
1111 wxLayoutObjectList::iterator j;
1112
1113 // WXL_TRACE(Insert(text));
1114
1115 if(! m_Editable)
1116 return;
1117
1118 m_bModified = true;
1119
1120 CoordType offs;
1121 wxLayoutObjectList::iterator i = FindCurrentObject(&offs);
1122
1123 if(i == end())
1124 {
1125 Insert(new wxLayoutObjectText(text));
1126 return;
1127 }
1128
1129 switch((**i).GetType())
1130 {
1131 case WXLO_TYPE_TEXT:
1132 // insert into an existing text object:
1133 WXL_TRACE(inserting into existing object);
1134 tobj = (wxLayoutObjectText *)*i ;
1135 wxASSERT(tobj);
1136 tobj->GetText().insert(offs,text);
1137 m_CursorObject = i;
1138 m_CursorOffset = offs + text.length();
1139 m_CursorPos.x += text.length();
1140 break;
1141 case WXLO_TYPE_LINEBREAK:
1142 default:
1143 j = i;
1144 if(offs == 0) // try to append to previous object
1145 {
1146 j--;
1147 if(j != end() && (**j).GetType() == WXLO_TYPE_TEXT)
1148 {
1149 tobj = (wxLayoutObjectText *)*j;
1150 tobj->GetText()+=text;
1151 m_CursorObject = j;
1152 m_CursorOffset = (**j).CountPositions();
1153 m_CursorPos.x += text.length();
1154 }
1155 else
1156 {
1157 insert(i,new wxLayoutObjectText(text));
1158 m_CursorObject = i;
1159 m_CursorOffset = (**i).CountPositions();
1160 m_CursorPos.x += m_CursorOffset;
1161 }
1162 }
1163 else // offset == 1 : cursor after linebreak
1164 {
1165 j++;
1166 m_CursorPos.x ++;
1167 m_CursorObject = j;
1168 m_CursorOffset = 0;
1169 if(j != end() && (**j).GetType() == WXLO_TYPE_TEXT)
1170 {
1171 tobj = (wxLayoutObjectText *)*j;
1172 tobj->GetText()=text+tobj->GetText();
1173 m_CursorOffset = text.length();
1174 m_CursorPos.x += m_CursorOffset;
1175 }
1176 else
1177 {
1178 if(j == end())
1179 {
1180 push_back(new wxLayoutObjectText(text));
1181 m_CursorObject = tail();
1182 m_CursorOffset = (**m_CursorObject).CountPositions();
1183 m_CursorPos.x += text.length();
1184 }
1185 else
1186 {
1187 insert(j,new wxLayoutObjectText(text));
1188 m_CursorObject = j;
1189 m_CursorOffset = (**j).CountPositions();
1190 m_CursorPos.x += text.length();
1191 }
1192 }
1193 }
1194 break;
1195 }
1196 m_CursorMoved = true;
1197 }
1198
1199 CoordType
1200 wxLayoutList::GetLineLength(wxLayoutObjectList::iterator i, CoordType offs)
1201 {
1202 if(i == end())
1203 return 0;
1204
1205 CoordType len = 0;
1206
1207 if(offs == 0 && (**i).GetType() == WXLO_TYPE_LINEBREAK)
1208 // we are before a linebrak
1209 return 0;
1210 // search backwards for beginning of line:
1211 while(i != begin() && (*i)->GetType() != WXLO_TYPE_LINEBREAK)
1212 i--;
1213 if((*i)->GetType() == WXLO_TYPE_LINEBREAK)
1214 i++;
1215 // now we can start counting:
1216 while(i != end() && (*i)->GetType() != WXLO_TYPE_LINEBREAK)
1217 {
1218 len += (*i)->CountPositions();
1219 i++;
1220 }
1221 return len;
1222 }
1223
1224 void
1225 wxLayoutList::Clear(int family, int size, int style, int weight,
1226 int underline, char const *fg, char const *bg)
1227 {
1228 m_bModified = true;
1229 m_CursorMoved = true;
1230 m_dirty = true; // force redraw/recalc
1231 wxLayoutObjectList::iterator i = begin();
1232
1233 wxBitmap bm(1,1);
1234 m_CursorMemDC.SelectObject(bm);
1235
1236 while(i != end()) // == while valid
1237 erase(i);
1238
1239 // set defaults
1240 m_FontPtSize = size;
1241 m_FontUnderline = false;
1242 m_FontFamily = family;
1243 m_FontStyle = style;
1244 m_FontWeight = weight;
1245 m_ColourFG = wxTheColourDatabase->FindColour(fg);
1246 m_ColourBG = wxTheColourDatabase->FindColour(bg);
1247
1248 if(! m_ColourFG) m_ColourFG = wxBLACK;
1249 if(! m_ColourBG) m_ColourBG = wxWHITE;
1250
1251 m_Position = wxPoint(0,0);
1252 m_CursorPos = wxPoint(0,0);
1253 m_CursorObject = iterator(NULL);
1254 m_CursorOffset = 0;
1255
1256 m_MaxLine = 0;
1257 m_LineHeight = (BASELINESTRETCH*m_FontPtSize)/10;
1258 m_MaxX = 0; m_MaxY = 0;
1259
1260
1261 m_FoundCursor = wxPoint(0,0);
1262 m_FoundIterator = begin();
1263
1264 if(m_DefaultSetting)
1265 delete m_DefaultSetting;
1266
1267 m_DefaultSetting = new
1268 wxLayoutObjectCmd(m_FontPtSize,m_FontFamily,m_FontStyle,
1269 m_FontWeight,m_FontUnderline,
1270 m_ColourFG, m_ColourBG);
1271 }
1272
1273
1274 wxLayoutObjectBase *
1275 wxLayoutList::Find(wxPoint coords) const
1276 {
1277 wxLayoutObjectList::iterator i = begin();
1278
1279 wxPoint topleft, bottomright;
1280
1281 while(i != end()) // == while valid
1282 {
1283 wxLayoutObjectBase *object = *i;
1284 topleft = object->GetPosition();
1285 if(coords.y >= topleft.y && coords.x >= topleft.x)
1286 {
1287 bottomright = topleft;
1288 bottomright.x += object->GetSize().x;
1289 bottomright.y += object->GetSize().y;
1290 if(coords.x <= bottomright.x && coords.y <= bottomright.y)
1291 return *i;
1292 }
1293 i++;
1294 }
1295 return NULL;
1296 }
1297
1298
1299 /******************** printing stuff ********************/
1300
1301 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList &llist,
1302 wxString const & title)
1303 :wxPrintout(title)
1304 {
1305 m_llist = &llist;
1306 m_title = title;
1307 }
1308
1309 bool wxLayoutPrintout::OnPrintPage(int page)
1310 {
1311 wxDC *dc = GetDC();
1312 if (dc)
1313 {
1314 DrawHeader(*dc,wxPoint(m_Margins.left,m_Margins.top/2),wxPoint(m_Margins.right,m_Margins.top),page);
1315 int top, bottom;
1316 top = (page - 1)*m_PrintoutHeight;
1317 bottom = top + m_PrintoutHeight;
1318 // SetDeviceOrigin() doesn't work here, so we need to manually
1319 // translate all coordinates.
1320 wxPoint translate(m_Margins.left,-top+m_Margins.top);
1321 m_llist->Draw(*dc,top,bottom,wxLayoutObjectList::iterator(NULL),translate);
1322 return true;
1323 }
1324 else
1325 return false;
1326 }
1327
1328 bool wxLayoutPrintout::OnBeginDocument(int startPage, int endPage)
1329 {
1330 if (!wxPrintout::OnBeginDocument(startPage, endPage))
1331 return false;
1332
1333 return true;
1334 }
1335
1336 void
1337 wxLayoutPrintout::OnPreparePrinting(void)
1338 {
1339
1340 }
1341
1342
1343 void wxLayoutPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo)
1344 {
1345 // ugly hack to get number of pages
1346 #ifdef __WXMSW__
1347 wxPrinterDC psdc("", "", WXLLIST_TEMPFILE,false);
1348 #else
1349 wxPostScriptDC psdc(WXLLIST_TEMPFILE,false);
1350 #endif
1351 psdc.GetSize(&m_PageWidth, &m_PageHeight); // that's all we need it for
1352
1353 // We do 5% margins on top and bottom, and a 5% high header line.
1354 m_Margins.top = m_PageHeight / 10 ; // 10%, half of it header
1355 m_Margins.bottom = m_PageHeight - m_PageHeight / 20; // 95%
1356 // On the sides we reserve 10% each for the margins.
1357 m_Margins.left = m_PageWidth / 10;
1358 m_Margins.right = m_PageWidth - m_PageWidth / 10;
1359
1360 // This is the length of the printable area.
1361 m_PrintoutHeight = m_PageHeight - (int) (m_PageHeight * 0.15);
1362
1363 //FIXME this is wrong but not used at the moment
1364 m_PageWidth = m_Margins.right - m_Margins.left;
1365
1366 m_NumOfPages = (int)( m_llist->GetSize().y / (float)(m_PrintoutHeight) + 0.5);
1367 *minPage = 1;
1368 *maxPage = m_NumOfPages;
1369
1370 *selPageFrom = 1;
1371 *selPageTo = m_NumOfPages;
1372 wxRemoveFile(WXLLIST_TEMPFILE);
1373 }
1374
1375 bool wxLayoutPrintout::HasPage(int pageNum)
1376 {
1377 return pageNum < m_NumOfPages;
1378 }
1379
1380
1381 void
1382 wxLayoutPrintout::DrawHeader(wxDC &dc,
1383 wxPoint topleft, wxPoint bottomright,
1384 int pageno)
1385 {
1386 // make backups of all essential parameters
1387 wxBrush *brush = dc.GetBrush();
1388 wxPen *pen = dc.GetPen();
1389 wxFont *font = dc.GetFont(),
1390 *myfont;;
1391
1392 dc.SetBrush(*wxWHITE_BRUSH);
1393 dc.SetPen(wxPen(*wxBLACK,0,wxSOLID));
1394 dc.DrawRoundedRectangle(topleft.x,
1395 topleft.y,bottomright.x-topleft.x,
1396 bottomright.y-topleft.y);
1397 dc.SetBrush(*wxBLACK_BRUSH);
1398 myfont = new wxFont((WXLO_DEFAULTFONTSIZE*12)/10,wxSWISS,wxNORMAL,wxBOLD,false,"Helvetica");
1399 dc.SetFont(*myfont);
1400
1401 wxString page;
1402 page = "9999/9999 "; // many pages...
1403 long w,h;
1404 dc.GetTextExtent(page,&w,&h);
1405 page.Printf("%d/%d", pageno, (int) m_NumOfPages);
1406 dc.DrawText(page,bottomright.x-w,topleft.y+h/2);
1407 dc.GetTextExtent("XXXX", &w,&h);
1408 dc.DrawText(m_title, topleft.x+w,topleft.y+h/2);
1409
1410 // restore settings
1411 dc.SetPen(*pen);
1412 dc.SetBrush(*brush);
1413 dc.SetFont(*font);
1414
1415 delete myfont;
1416 }
1417
1418
1419 wxLayoutPrintout *
1420 wxLayoutList::MakePrintout(wxString const &name)
1421 {
1422 return new wxLayoutPrintout(*this,name);
1423 }