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