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