MezzanineEngine 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
textlayer.cpp
1 //© Copyright 2010 - 2012 BlackTopp Studios Inc.
2 /* This file is part of The Mezzanine Engine.
3 
4  The Mezzanine Engine is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  The Mezzanine Engine is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with The Mezzanine Engine. If not, see <http://www.gnu.org/licenses/>.
16 */
17 /* The original authors have included a copy of the license specified above in the
18  'Docs' folder. See 'gpl.txt'
19 */
20 /* We welcome the use of the Mezzanine engine to anyone, including companies who wish to
21  Build professional software and charge for their product.
22 
23  However there are some practical restrictions, so if your project involves
24  any of the following you should contact us and we will try to work something
25  out:
26  - DRM or Copy Protection of any kind(except Copyrights)
27  - Software Patents You Do Not Wish to Freely License
28  - Any Kind of Linking to Non-GPL licensed Works
29  - Are Currently In Violation of Another Copyright Holder's GPL License
30  - If You want to change our code and not add a few hundred MB of stuff to
31  your distribution
32 
33  These and other limitations could cause serious legal problems if you ignore
34  them, so it is best to simply contact us or the Free Software Foundation, if
35  you have any questions.
36 
37  Joseph Toppi - toppij@gmail.com
38  John Blackwood - makoenergy02@gmail.com
39 */
40 
41 #ifndef _uitextlayer_cpp
42 #define _uitextlayer_cpp
43 
44 #include "UI/textlayer.h"
45 #include "UI/textline.h"
46 #include "UI/textcursor.h"
47 #include "UI/texttoken.h"
48 #include "UI/defaultmarkupparser.h"
49 #include "UI/widget.h"
50 #include "UI/screen.h"
51 
52 #include "UI/uimanager.h"
53 #include "unicode.h"
54 #include "stringtool.h"
55 #include "mathtool.h"
56 
57 namespace Mezzanine
58 {
59  namespace UI
60  {
61  TextLayer::TextLayer(QuadRenderable* ParentRenderable) :
62  RenderLayer(ParentRenderable),
63  ManualCharScaling(1.0,1.0),
64 
65  MUParser(NULL),
66  Cursor(NULL),
67  TextTokens(NULL),
68 
69  HighlightStart(-1),
70  HighlightEnd(-1),
71  AutoCharScaling(0.0),
72 
73  AutoCharScalingMode(TextLayer::SM_NoAutoScaling),
74  HorizontalOrder(UI::TO_Left_To_Right), // Default to english
75  VerticalAlign(UI::LA_TopLeft),
76  ScalingChanged(false)
77  {
78  // Set our colour defaults
82  this->ActiveHLColour.AlphaChannel = 0.5;
83  this->InactiveHLColour.AlphaChannel = 0.5;
84 
85  // Create our one free line for text
86  this->CreateTextLine();
87 
88  // Set the default parser
89  this->MUParser = Parent->GetScreen()->GetMarkupParser("Default");
90 
91  // This constructor does not set a font. A font will need to be set prior to setting any text.
92  }
93 
94  TextLayer::TextLayer(const String& FontName, QuadRenderable* ParentRenderable) :
95  RenderLayer(ParentRenderable),
96  ManualCharScaling(1.0,1.0),
97 
98  MUParser(NULL),
99  Cursor(NULL),
100  TextTokens(NULL),
101 
102  HighlightStart(-1),
103  HighlightEnd(-1),
104  AutoCharScaling(0.0),
105 
106  AutoCharScalingMode(TextLayer::SM_NoAutoScaling),
107  HorizontalOrder(UI::TO_Left_To_Right), // Default to english
108  VerticalAlign(UI::LA_TopLeft),
109  ScalingChanged(false)
110  {
111  // Set our colour defaults
115  this->ActiveHLColour.AlphaChannel = 0.5;
116  this->InactiveHLColour.AlphaChannel = 0.5;
117 
118  // Set our font
120 
121  // Create our one free line for text
122  this->CreateTextLine();
123 
124  // Set the default parser
125  this->MUParser = Parent->GetScreen()->GetMarkupParser("Default");
126  }
127 
128  TextLayer::TextLayer(const Real& LineHeight, QuadRenderable* ParentRenderable) :
129  RenderLayer(ParentRenderable),
130  ManualCharScaling(1.0,1.0),
131 
132  MUParser(NULL),
133  Cursor(NULL),
134  TextTokens(NULL),
135 
136  HighlightStart(-1),
137  HighlightEnd(-1),
138  AutoCharScaling(LineHeight),
139 
140  AutoCharScalingMode(TextLayer::SM_ScreenRelative),
141  HorizontalOrder(UI::TO_Left_To_Right), // Default to english
142  VerticalAlign(UI::LA_TopLeft),
143  ScalingChanged(false)
144  {
145  // Set our colour defaults
149  this->ActiveHLColour.AlphaChannel = 0.5;
150  this->InactiveHLColour.AlphaChannel = 0.5;
151 
152  // Set our font
153  UIManager::FontResult Result = UIManager::GetSingletonPtr()->SuggestGlyphIndex(static_cast<UInt32>(this->AutoCharScaling * Parent->GetActualSize().Y),PriAtlas);
154  this->DefaultCharTraits.CharFont = this->Parent->GetScreen()->GetFont(Result.first,PriAtlas);
155  //this->SetTextScale(Vector2(Result.second,Result.second));
156 
157  // Create our one free line for text
158  this->CreateTextLine();
159 
160  // Set the default parser
161  this->MUParser = Parent->GetScreen()->GetMarkupParser("Default");
162  }
163 
165  {
166  this->ClearAllTextLines();
167  this->DestroyAllCharacters();
168  this->DestroyAllTextLines();
169  }
170 
171  void TextLayer::RedrawImpl(bool Force)
172  {
173  // Update our text
174  if(Force || this->ScalingChanged)
175  {
176  // In general the only time this will be forced is on a resizing of the parent
177  // quad renderable. So we must repopulate for the change of space.
178  this->PopulateTextLines();
179  this->ScalingChanged = false;
180  }
181 
182  // Get the Texel Offsets
183  Real TexelOffsetX = this->Parent->GetScreen()->GetTexelOffsetX();
184  Real TexelOffsetY = this->Parent->GetScreen()->GetTexelOffsetY();
185 
186  // Get the parent rect and apply the scaling
187  Rect ActDims = this->GetAreaRect();
188 
189  // Setup the text specific data we'll use
190  Vector2 TopLeft, TopRight, BottomLeft, BottomRight;
191  Vector2 WhitePixelCoords;
192  Real CursorX = ActDims.Position.X, CursorY = ActDims.Position.Y;
193  ColourValue HighlightColour = this->InactiveHLColour;
194  Character* CurrChar = NULL;
195  TextLine* CurrLine = NULL;
196 
197  // Check if we need the active highlight colour
198  if( Renderable::RT_Widget == this->Parent->GetRenderableType() && (static_cast<Widget*>(this->Parent))->HasFocus() )
199  HighlightColour = ActiveHLColour;
200 
201  for( TextLineIterator LineIt = this->TextLines.begin() ; LineIt != this->TextLines.end() ; ++LineIt )
202  {
203  CurrLine = (*LineIt);
204  CursorY = ActDims.Position.Y + CurrLine->GetPositionOffset();
205 
206  // If we have ran out of room, halt rendering
207  if( CursorY >= ActDims.Position.Y + ActDims.Size.Y )
208  break;
209 
210  // Update the X cursor starting position
211  CursorX = ActDims.Position.X + CurrLine->GetCursorStartPosition();
212 
213  for( TextLine::CharacterIterator CharIt = CurrLine->BeginCharacter() ; CharIt != CurrLine->EndCharacter() ; ++CharIt )
214  {
215  CurrChar = (*CharIt);
216  if( !CurrChar->IsRenderable() )
217  continue;
218 
219  String CharAtlas = CurrChar->GetAtlasName();
220  Real VertOffset = CurrChar->GetVerticalOffset();
221  Vector2 CharSize = CurrChar->GetCharacterSize();
222  ColourValue CharColour = CurrChar->GetCharacterColour();
223  CursorX = ActDims.Position.X + CurrChar->GetLengthOffset();
224 
225  TopLeft.SetValues( MathTools::Floor( CursorX - TexelOffsetX ),
226  MathTools::Floor( (CursorY + TexelOffsetY + VertOffset) - CharSize.Y ) );
227  BottomRight.SetValues( MathTools::Floor( (CursorX - TexelOffsetX) + CharSize.X ),
228  MathTools::Floor( CursorY + TexelOffsetY + VertOffset ) );
229  TopRight.SetValues( BottomRight.X, TopLeft.Y );
230  BottomLeft.SetValues( TopLeft.X, BottomRight.Y );
231 
232  this->RotationTransform(TopLeft,TopRight,BottomLeft,BottomRight);
233 
234  if( CurrChar->GetHighlighted() )
235  {
236  WhitePixelCoords = CurrChar->GetAtlasWhitePixel();
237 
238  // Triangle A
239  PushVertex(BottomLeft.X, BottomLeft.Y, WhitePixelCoords, HighlightColour, CharAtlas); // Left/Bottom 3
240  PushVertex(TopRight.X, TopRight.Y, WhitePixelCoords, HighlightColour, CharAtlas); // Right/Top 1
241  PushVertex(TopLeft.X, TopLeft.Y, WhitePixelCoords, HighlightColour, CharAtlas); // Left/Top 0
242 
243  // Triangle B
244  PushVertex(BottomLeft.X, BottomLeft.Y, WhitePixelCoords, HighlightColour, CharAtlas); // Left/Bottom 3
245  PushVertex(BottomRight.X, BottomRight.Y, WhitePixelCoords, HighlightColour, CharAtlas); // Right/Bottom 2
246  PushVertex(TopRight.X, TopRight.Y, WhitePixelCoords, HighlightColour, CharAtlas); // Right/Top 1
247  }
248 
249  // Triangle A
250  PushVertex(BottomLeft.X, BottomLeft.Y, CurrChar->GetRelativeAtlasCoords(UI::QC_BottomLeft), CharColour, CharAtlas); // Left/Bottom 3
251  PushVertex(TopRight.X, TopRight.Y, CurrChar->GetRelativeAtlasCoords(UI::QC_TopRight), CharColour, CharAtlas); // Right/Top 1
252  PushVertex(TopLeft.X, TopLeft.Y, CurrChar->GetRelativeAtlasCoords(UI::QC_TopLeft), CharColour, CharAtlas); // Left/Top 0
253 
254  // Triangle B
255  PushVertex(BottomLeft.X, BottomLeft.Y, CurrChar->GetRelativeAtlasCoords(UI::QC_BottomLeft), CharColour, CharAtlas); // Left/Bottom 3
256  PushVertex(BottomRight.X, BottomRight.Y, CurrChar->GetRelativeAtlasCoords(UI::QC_BottomRight), CharColour, CharAtlas); // Right/Bottom 2
257  PushVertex(TopRight.X, TopRight.Y, CurrChar->GetRelativeAtlasCoords(UI::QC_TopRight), CharColour, CharAtlas); // Right/Top 1
258  }//for each character
259  }//for each text line
260 
261  // Last thing to do is rendering the text cursor
262  if( this->GetCursorEnabled() ) {
263  if( this->Cursor->GetVisible() ) {
264  Rect CursorRect = Cursor->GetCursorRect();
265  ColourValue CursorColour = Cursor->GetColour();
266  WhitePixelCoords = Parent->GetScreen()->GetWhitePixel(PriAtlas);
267 
268  BottomLeft = ActDims.Position + CursorRect.Position;
269  TopRight.SetValues( BottomLeft.X + CursorRect.Size.X , BottomLeft.Y - CursorRect.Size.Y );
270  TopLeft.SetValues( BottomLeft.X , TopRight.Y );
271  BottomRight.SetValues( TopRight.X , BottomLeft.Y );
272 
273  // Triangle A
274  PushVertex(BottomLeft.X, BottomLeft.Y, WhitePixelCoords, CursorColour, PriAtlas); // Left/Bottom 3
275  PushVertex(TopRight.X, TopRight.Y, WhitePixelCoords, CursorColour, PriAtlas); // Right/Top 1
276  PushVertex(TopLeft.X, TopLeft.Y, WhitePixelCoords, CursorColour, PriAtlas); // Left/Top 0
277 
278  // Triangle B
279  PushVertex(BottomLeft.X, BottomLeft.Y, WhitePixelCoords, CursorColour, PriAtlas); // Left/Bottom 3
280  PushVertex(BottomRight.X, BottomRight.Y, WhitePixelCoords, CursorColour, PriAtlas); // Right/Bottom 2
281  PushVertex(TopRight.X, TopRight.Y, WhitePixelCoords, CursorColour, PriAtlas); // Right/Top 1
282  }
283  }
284  }
285 
287  {
288  // Get the actual starting position on the Y axis
289  Real CursorY = 0;
290  Real TotalHeight = this->GetTotalHeight();
291  Rect ActDims = this->GetAreaRect();
292  if( TotalHeight < ActDims.Size.Y )
293  {
294  switch(VerticalAlign)
295  {
296  case UI::LA_TopLeft: /* do nothing, we're at the top */ break;
297  case UI::LA_BottomRight: CursorY = ActDims.Size.Y - TotalHeight; break;
298  case UI::LA_Center: CursorY = (ActDims.Size.Y * 0.5) - (TotalHeight * 0.5); break;
299  }
300  }
301  // Set all the actual offsets
302  for( TextLineIterator TLIt = TextLines.begin() ; TLIt != TextLines.end() ; ++TLIt )
303  {
304  CursorY += (*TLIt)->GetLineHeight();
305  (*TLIt)->SetPositionOffset(CursorY);
306  }
307  }
308 
309  ///////////////////////////////////////////////////////////////////////////////
310  // Parser Methods
311 
313  {
314  if( Parser != NULL ) {
315  this->MUParser = Parser;
316  return true;
317  }else{
318  return false;
319  }
320  }
321 
322  bool TextLayer::SetMarkupParser(const String& ParserName)
323  {
324  MarkupParser* Parser = Parent->GetScreen()->GetMarkupParser(ParserName);
325  return this->SetMarkupParser(Parser);
326  }
327 
329  {
330  return this->MUParser;
331  }
332 
333  ///////////////////////////////////////////////////////////////////////////////
334  // Utility
335 
336  void TextLayer::SetScale(const Vector2& Scaling)
337  {
338  this->RenderLayer::SetScale(Scaling);
339  this->ScalingChanged = true;
340  }
341 
343  {
344  Real Ret = 0;
345  for( ConstTextLineIterator TextIt = this->TextLines.begin() ; TextIt != this->TextLines.end() ; ++TextIt )
346  {
347  Ret += (*TextIt)->GetLineHeight();
348  }
349  return Ret;
350  }
351 
353  {
354  switch( this->AutoCharScalingMode )
355  {
356  case TextLayer::SM_ScreenRelative: return ( this->Parent->GetScreen()->GetActualSize().Y * this->AutoCharScaling ); break;
357  case TextLayer::SM_ParentRelative: return ( this->Parent->GetActualSize().Y * this->AutoCharScaling ); break;
358  case TextLayer::SM_LayerRelative: return ( this->GetAreaRect().Size.Y * this->AutoCharScaling ); break;
360  default: return 0; break;
361  }
362  }
363 
364  ///////////////////////////////////////////////////////////////////////////////
365  // Offset - Index Conversion Methods
366 
368  {
369  // Get the parent rect and apply the scaling
370  Rect LayerDims = this->GetAreaRect();
371  if( !LayerDims.IsInside(LayerDims.Position + Offset) || 0 == this->GetNumTextLines() )
372  return CharIndexPair(false,0);
373 
374  return this->GetIndexAtOffsetImpl(Offset);
375  }
376 
378  {
379  return this->GetOffsetAtIndexImpl(Index);
380  }
381 
382  ///////////////////////////////////////////////////////////////////////////////
383  // Text Methods
384 
385  void TextLayer::SetText(const String& Text)
386  {
387  if( this->DefaultCharTraits.CharFont == NULL ) {
388  StringStream ExceptionStream;
389  ExceptionStream << "Attempting to set text on a TextLayer without a default font set. Layer owned by renderable \"" << Parent->GetName() << "\".";
390  MEZZ_EXCEPTION(Exception::INVALID_STATE_EXCEPTION,ExceptionStream.str());
391  }
392 
393  /// @todo Character generation and storage is a good candidate for pool allocation.
394  this->DestroyAllCharacters();
395 
396  if( this->TextTokens != NULL ) {
397  delete this->TextTokens;
398  this->TextTokens = NULL;
399  }
400 
401  this->TextTokens = this->MUParser->Lex(Text);
402  this->Characters = this->MUParser->ParseTextTokens(this->TextTokens,this->DefaultCharTraits,this);
403  this->PopulateTextLines();
404 
405  this->_MarkDirty();
406  }
407 
409  {
410  return this->TextTokens->GetRawString();
411  }
412 
414  {
415  if( this->DefaultCharTraits.CharColour != Colour ) {
416  this->DefaultCharTraits.CharColour = Colour;
417  this->_MarkDirty();
418  }
419  }
420 
422  {
423  return this->DefaultCharTraits.CharColour;
424  }
425 
427  {
428  this->ManualCharScaling = Scale;
429  this->ScalingChanged = true;
430  this->_MarkDirty();
431  }
432 
434  {
435  return this->ManualCharScaling;
436  }
437 
439  {
440  if( this->AutoCharScalingMode != Mode || this->AutoCharScaling != Scalar ) {
441  this->AutoCharScalingMode = Mode;
442  this->AutoCharScaling = Scalar;
443  this->ScalingChanged = true;
444  }
445  }
446 
448  {
449  return this->AutoCharScalingMode;
450  }
451 
453  {
454  return this->AutoCharScaling;
455  }
456 
457  ///////////////////////////////////////////////////////////////////////////////
458  // Font Methods
459 
461  {
462  if(NewFont != this->DefaultCharTraits.CharFont && NewFont) {
463  this->DefaultCharTraits.CharFont = NewFont;
464  this->_MarkDirty();
465  }else{
466  /// @todo Throw an error?
467  }
468  }
469 
470  void TextLayer::SetDefaultFont(const String& FontName)
471  {
472  FontData* NewData = Parent->GetScreen()->GetFont(FontName,PriAtlas);
473  this->SetDefaultFont(NewData);
474  }
475 
476  void TextLayer::SetDefaultFont(const String& FontName, const String& Atlas)
477  {
478  FontData* NewData = Parent->GetScreen()->GetFont(FontName,Atlas);
479  this->SetDefaultFont(NewData);
480  }
481 
483  {
484  return this->DefaultCharTraits.CharFont;
485  }
486 
487  ///////////////////////////////////////////////////////////////////////////////
488  // Highlight Methods
489 
491  {
492  if( this->ActiveHLColour != Colour ) {
493  this->ActiveHLColour = Colour;
494  this->_MarkDirty();
495  }
496  }
497 
499  {
500  return this->ActiveHLColour;
501  }
502 
504  {
505  if( this->InactiveHLColour != Colour ) {
506  this->InactiveHLColour = Colour;
507  this->_MarkDirty();
508  }
509  }
510 
512  {
513  return this->InactiveHLColour;
514  }
515 
517  {
518  this->HighlightStart = 0;
519  this->HighlightEnd = this->Characters.size() - 1;
521  }
522 
523  void TextLayer::Highlight(const Integer Index)
524  {
525  this->ClearHighlights();
526  if( Index < this->Characters.size() ) {
527  CharacterIterator ToHighlight = this->GetCharacterIteratorAtIndex(Index);
528  (*ToHighlight)->SetHighlighted(true);
529 
530  this->HighlightStart = Index;
531  this->HighlightEnd = Index;
532  }
533  }
534 
535  void TextLayer::Highlight(const Integer StartIndex, const Integer EndIndex)
536  {
537  this->ClearHighlights();
538  CharacterIterator StartIterator = GetCharacterIteratorAtIndex(StartIndex);
539  CharacterIterator EndIterator = ( EndIndex + 1 >= this->Characters.size() ? this->Characters.end() : GetCharacterIteratorAtIndex(EndIndex + 1) );
540 
541  while( StartIterator != EndIterator )
542  {
543  (*StartIterator)->SetHighlighted(true);
544  ++StartIterator;
545  }
546 
547  this->HighlightStart = StartIndex;
548  this->HighlightEnd = EndIndex;
549  }
550 
552  {
553  return this->HighlightStart;
554  }
555 
557  {
558  return this->HighlightEnd;
559  }
560 
562  {
563  if( this->HighlightStart == -1 || this->HighlightEnd == -1 )
564  return;
565 
568 
569  while( StartIterator != EndIterator )
570  {
571  (*StartIterator)->SetHighlighted(false);
572  ++StartIterator;
573  }
574 
575  this->HighlightStart = -1;
576  this->HighlightEnd = -1;
577  }
578 
579  ///////////////////////////////////////////////////////////////////////////////
580  // Ordering Methods
581 
583  {
584  if( this->HorizontalOrder == Order )
585  return;
586 
587  this->DestroyAllTextLines();
588  this->CreateTextLine();
589  this->PopulateTextLines();
590 
591  this->HorizontalOrder = Order;
592  this->_MarkDirty();
593  }
594 
596  {
597  return this->HorizontalOrder;
598  }
599 
600  ///////////////////////////////////////////////////////////////////////////////
601  // Cursor Methods
602 
603  void TextLayer::SetCursorEnabled(bool Enable)
604  {
605  if( Enable == this->GetCursorEnabled() )
606  return;
607 
608  if(Enable) {
609  TextLine* Line = this->TextLines.at(0);
611  }else{
612  delete this->Cursor;
613  this->Cursor = NULL;
614  }
615 
616  this->_MarkDirty();
617  }
618 
620  {
621  return ( this->Cursor != NULL );
622  }
623 
625  {
626  return this->Cursor;
627  }
628 
629  ///////////////////////////////////////////////////////////////////////////////
630  // TextLine Methods
631 
633  {
634  TextLine* NewLine = NULL;
635  if( this->HorizontalOrder == UI::TO_Left_To_Right ) NewLine = new LeftToRightTextLine(this);
636  else NewLine = new RightToLeftTextLine(this);
637 
638  this->TextLines.push_back(NewLine);
639  return NewLine;
640  }
641 
643  {
644  TextLineIterator LineIt = TextLines.begin();
645  // Check if we're too far to the top to get anything
646  if( Offset < (*LineIt)->GetPositionOffset() - (*LineIt)->GetLineHeight() )
647  return NULL;
648 
649  while( LineIt != this->TextLines.end() && Offset < (*LineIt)->GetPositionOffset() )
650  {
651  ++LineIt;
652  }
653 
654  // Check if we're too low to get anything
655  if( LineIt == (--this->TextLines.end()) && Offset > (*LineIt)->GetPositionOffset() )
656  return NULL;
657 
658  return (*LineIt);
659  }
660 
662  {
663  return TextLines.size();
664  }
665 
667  {
668  this->ClearAllTextLines();
669  this->PopulateTextLinesImpl();
670  this->RecalculateOffsets();
671  this->_MarkDirty();
672  }
673 
675  {
676  for( TextLineIterator TLIt = TextLines.begin() ; TLIt != TextLines.end() ; ++TLIt )
677  {
678  (*TLIt)->RemoveAllCharacters();
679  }
680  this->_MarkDirty();
681  }
682 
684  {
685  for( TextLineIterator TLIt = TextLines.begin() ; TLIt != TextLines.end() ; ++TLIt )
686  {
687  delete (*TLIt);
688  }
689  TextLines.clear();
690  this->_MarkDirty();
691  }
692 
694  {
695  if( this->VerticalAlign == Align )
696  return;
697 
698  this->VerticalAlign = Align;
699  this->RecalculateOffsets();
700  this->_MarkDirty();
701  }
702 
704  {
705  return this->VerticalAlign;
706  }
707 
709  { return this->TextLines.begin(); }
710 
712  { return this->TextLines.end(); }
713 
715  { return this->TextLines.begin(); }
716 
718  { return this->TextLines.end(); }
719 
720  ///////////////////////////////////////////////////////////////////////////////
721  // Character Methods
722 
724  {
725  return *(this->GetCharacterIteratorAtIndex(Index));
726  }
727 
729  {
730  // Get the parent rect and apply the scaling
731  Rect ActDims = this->GetAreaRect();
732  if( !ActDims.IsInside(ActDims.Position + Offset) || this->TextLines.empty() )
733  return NULL;
734 
735  // Get our line
736  ConstTextLineIterator LineIt = this->TextLines.begin();
737  while( LineIt != this->TextLines.end() && Offset.Y > (*LineIt)->GetPositionOffset() )
738  {
739  /// @todo The way this logic works, in the event of a slight bit of clipping at the bottom of the quad space that
740  /// isn't enough for the next line, this logic will assume that space is a part of the last line. Is that a problem?
741  ++LineIt;
742  }
743 
744  // Quickly check to see if we're to the side of any lines.
745  if( Offset.X < (*LineIt)->GetLeftMostCursorPosition() ||
746  Offset.X > (*LineIt)->GetRightMostCursorPosition() )
747  {
748  return NULL;
749  }
750 
751  // Get the character in that line
752  CharacterIterator CharIt = (*LineIt)->BeginCharacter();
753  while( CharIt != (*LineIt)->EndCharacter() && Offset.X > (*CharIt)->GetLengthOffset() + (*CharIt)->GetCharacterSize().X )
754  {
755  /// @todo In the event that letter spacing for the font is big ( >0 ), and the point provided rests on a gap in the spacing,
756  /// this algorithm will grab the character to the right as the hovered character. Is that a problem?
757  ++CharIt;
758  }
759 
760  if( CharIt == (*LineIt)->EndCharacter() ) return NULL;
761  else return (*CharIt);
762  }
763 
765  {
766  if( 0 > Index || Index >= this->Characters.size() ) {
767  return --(this->Characters.end());
768  }else{
769  UInt32 Count = Index;
770  CharacterIterator CharIt = this->Characters.begin();
771  while( 0 != Count )
772  {
773  ++CharIt;
774  --Count;
775  }
776  return CharIt;
777  }
778  }
779 
781  {
782  if( 0 > Index || Index >= this->Characters.size() ) {
783  return --(this->Characters.end());
784  }else{
785  UInt32 Count = Index;
786  ConstCharacterIterator CharIt = this->Characters.begin();
787  while( 0 != Count )
788  {
789  ++CharIt;
790  --Count;
791  }
792  return CharIt;
793  }
794  }
795 
797  {
798  return this->Characters.size();
799  }
800 
801  void TextLayer::InsertCharacterAtIndex(const Integer Index, const UInt32 GlyphID)
802  {
803  // Do our work
804  this->TextTokens->InsertCharacter(Index,GlyphID);
805  // Regenerate the text
806  String RawText = this->GetText();
807  this->SetText(RawText);
808  }
809 
810  void TextLayer::InsertCharactersAtIndex(const Integer Index, const Char8* Characters, const UInt32 BufSize)
811  {
812  // Do our work
813  this->TextTokens->InsertCharacters(Index,Characters,BufSize);
814  // Regenerate the text
815  String RawText = this->GetText();
816  this->SetText(RawText);
817  }
818 
819  void TextLayer::InsertCharactersAtIndex(const Integer Index, const UInt32* Characters, const UInt32 BufSize)
820  {
821  // Do our work
822  this->TextTokens->InsertCharacters(Index,Characters,BufSize);
823  // Regenerate the text
824  String RawText = this->GetText();
825  this->SetText(RawText);
826  }
827 
829  {
830  // Do our work
831  this->TextTokens->RemoveCharacter(Index);
832  // Regenerate the text
833  String RawText = this->GetText();
834  this->SetText(RawText);
835  }
836 
837  void TextLayer::RemoveCharactersAtIndex(const Integer Index, const UInt32 Length)
838  {
839  // Do our work
840  this->TextTokens->RemoveCharacters(Index,Length);
841  // Regenerate the text
842  String RawText = this->GetText();
843  this->SetText(RawText);
844  }
845 
846  void TextLayer::RemoveCharacterRange(const Integer First, const Integer Last)
847  {
848  this->RemoveCharactersAtIndex(First,(Last - First) + 1);
849  }
850 
852  {
853  // Remove from the TextLines first
854  this->ClearAllTextLines();
855  // Now clean up the characters
856  for( CharacterIterator CharIt = this->Characters.begin() ; CharIt != this->Characters.end() ; ++CharIt )
857  {
858  delete (*CharIt);
859  }
860  this->Characters.clear();
861 
862  this->HighlightStart = -1;
863  this->HighlightEnd = -1;
864 
865  this->_MarkDirty();
866  }
867 
869  { return this->Characters.begin(); }
870 
872  { return this->Characters.end(); }
873 
875  { return this->Characters.begin(); }
876 
878  { return this->Characters.end(); }
879 
880  ///////////////////////////////////////////////////////////////////////////////
881  // Serialization
882 
883  void TextLayer::ProtoSerialize(XML::Node& ParentNode) const
884  {
885  XML::Node SelfRoot = ParentNode.AppendChild( this->GetDerivedSerializableName() );
886 
887  this->ProtoSerializeProperties(SelfRoot);
888  this->ProtoSerializeCursor(SelfRoot);
889  this->ProtoSerializeText(SelfRoot);
890  }
891 
893  {
895  XML::Node PropertiesNode = SelfRoot.AppendChild( TextLayer::GetSerializableName() + "Properties" );
896 
897  if( PropertiesNode.AppendAttribute("Version").SetValue("1") &&
898  PropertiesNode.AppendAttribute("HighlightStart").SetValue(this->HighlightStart) &&
899  PropertiesNode.AppendAttribute("HighlightEnd").SetValue(this->HighlightEnd) &&
900  PropertiesNode.AppendAttribute("AutoCharScaling").SetValue(this->AutoCharScaling) &&
901  PropertiesNode.AppendAttribute("AutoCharScalingMode").SetValue(this->AutoCharScalingMode) &&
902  PropertiesNode.AppendAttribute("HorizontalOrder").SetValue(this->HorizontalOrder) &&
903  PropertiesNode.AppendAttribute("VerticalAlign").SetValue(this->VerticalAlign) &&
904  PropertiesNode.AppendAttribute("ScalingChanged").SetValue(this->ScalingChanged) &&
905  PropertiesNode.AppendAttribute("ParserName").SetValue( ( this->MUParser ? this->MUParser->GetName() : "" ) ) &&
906  PropertiesNode.AppendAttribute("RawText").SetValue(this->GetText()) )
907  {
908  XML::Node DefaultCharTraitsNode = PropertiesNode.AppendChild("DefaultCharTraits");
909  this->DefaultCharTraits.ProtoSerialize( DefaultCharTraitsNode );
910  XML::Node ActiveHLColourNode = PropertiesNode.AppendChild("ActiveHLColour");
911  this->ActiveHLColour.ProtoSerialize( ActiveHLColourNode );
912  XML::Node InactiveHLColourNode = PropertiesNode.AppendChild("InactiveHLColour");
913  this->InactiveHLColour.ProtoSerialize( InactiveHLColourNode );
914  XML::Node CharScalingNode = PropertiesNode.AppendChild("ManualCharScaling");
915  this->ManualCharScaling.ProtoSerialize( CharScalingNode );
916 
917  return;
918  }else{
919  SerializeError("Create XML Attribute Values",TextLayer::GetSerializableName() + "Properties",true);
920  }
921  }
922 
924  {
925  if( this->Cursor ) {
926  XML::Node CursorNode = SelfRoot.AppendChild("Cursor");
927  this->Cursor->ProtoSerialize( CursorNode );
928  }
929  }
930 
932  {
933  XML::Node TextNode = SelfRoot.AppendChild( TextLayer::GetSerializableName() + "Text" );
934 
935  if( TextNode.AppendAttribute("Version").SetValue(1) &&
936  TextNode.AppendAttribute("Text").SetValue( this->GetText() ) ) {
937  return;
938  }else{
939  SerializeError("Create XML Attribute Values",TextLayer::GetSerializableName() + "Text",true);
940  }
941  }
942 
944  {
945  this->ProtoDeSerializeProperties(SelfRoot);
946  this->ProtoDeSerializeCursor(SelfRoot);
947  this->ProtoDeSerializeText(SelfRoot);
948 
949  // Set Highlights
950  if( this->HighlightStart > -1 && this->HighlightEnd > -1 ) {
951  Integer StartCopy = this->HighlightStart;
952  Integer EndCopy = this->HighlightEnd;
953  this->Highlight(StartCopy,EndCopy);
954  }
955  }
956 
958  {
959  this->ClearAllTextLines();
960  this->DestroyAllCharacters();
961  this->DestroyAllTextLines();
962 
964  XML::Attribute CurrAttrib;
965  XML::Node PropertiesNode = SelfRoot.GetChild( TextLayer::GetSerializableName() + "Properties" );
966 
967  if( !PropertiesNode.Empty() ) {
968  if( PropertiesNode.GetAttribute("Version").AsInt() == 1 ) {
969  CurrAttrib = PropertiesNode.GetAttribute("HighlightStart");
970  if( !CurrAttrib.Empty() )
971  this->HighlightStart = CurrAttrib.AsInt();
972 
973  CurrAttrib = PropertiesNode.GetAttribute("HighlightEnd");
974  if( !CurrAttrib.Empty() )
975  this->HighlightEnd = CurrAttrib.AsInt();
976 
977  CurrAttrib = PropertiesNode.GetAttribute("AutoCharScaling");
978  if( !CurrAttrib.Empty() )
979  this->AutoCharScaling = CurrAttrib.AsReal();
980 
981  CurrAttrib = PropertiesNode.GetAttribute("AutoCharScalingMode");
982  if( !CurrAttrib.Empty() )
983  this->AutoCharScalingMode = static_cast<TextLayer::ScalingMode>( CurrAttrib.AsWhole() );
984 
985  CurrAttrib = PropertiesNode.GetAttribute("HorizontalOrder");
986  if( !CurrAttrib.Empty() )
987  this->HorizontalOrder = static_cast<UI::TextOrdering>( CurrAttrib.AsWhole() );
988 
989  CurrAttrib = PropertiesNode.GetAttribute("VerticalAlign");
990  if( !CurrAttrib.Empty() )
991  this->VerticalAlign = static_cast<UI::LinearAlignment>( CurrAttrib.AsWhole() );
992 
993  CurrAttrib = PropertiesNode.GetAttribute("ScalingChanged");
994  if( !CurrAttrib.Empty() )
995  this->ScalingChanged = StringTools::ConvertToBool( CurrAttrib.AsString() );
996 
997  XML::Node DefaultCharTraitsNode = PropertiesNode.GetChild("DefaultCharTraits").GetFirstChild();
998  if( !DefaultCharTraitsNode.Empty() )
999  this->DefaultCharTraits.ProtoDeSerialize(DefaultCharTraitsNode);
1000 
1001  XML::Node ActiveHLColourNode = PropertiesNode.GetChild("ActiveHLColour").GetFirstChild();
1002  if( !ActiveHLColourNode.Empty() )
1003  this->ActiveHLColour.ProtoDeSerialize(ActiveHLColourNode);
1004 
1005  XML::Node InactiveHLColourNode = PropertiesNode.GetChild("InactiveHLColour").GetFirstChild();
1006  if( !InactiveHLColourNode.Empty() )
1007  this->InactiveHLColour.ProtoDeSerialize(InactiveHLColourNode);
1008 
1009  XML::Node CharScalingNode = PropertiesNode.GetChild("ManualCharScaling").GetFirstChild();
1010  if( !CharScalingNode.Empty() )
1011  this->ManualCharScaling.ProtoDeSerialize(CharScalingNode);
1012  }else{
1013  MEZZ_EXCEPTION(Exception::INVALID_VERSION_EXCEPTION,"Incompatible XML Version for " + (TextLayer::GetSerializableName() + "Properties") + ": Not Version 1.");
1014  }
1015  }else{
1016  MEZZ_EXCEPTION(Exception::II_IDENTITY_NOT_FOUND_EXCEPTION,TextLayer::GetSerializableName() + "Properties" + " was not found in the provided XML node, which was expected.");
1017  }
1018  }
1019 
1021  {
1022  XML::Node CursorNode = SelfRoot.GetChild("Cursor");
1023  if( !CursorNode.Empty() ) {
1024  if( this->GetCursorEnabled() == false ) {
1025  this->SetCursorEnabled(true);
1026  }
1027  this->Cursor->ProtoDeSerialize( CursorNode.GetFirstChild() );
1028  }
1029  }
1030 
1032  {
1033  XML::Attribute CurrAttrib;
1034  XML::Node TextNode = SelfRoot.GetChild( TextLayer::GetSerializableName() + "Text" );
1035 
1036  if( !TextNode.Empty() ) {
1037  if( TextNode.GetAttribute("Version").AsInt() == 1 ) {
1038  CurrAttrib = TextNode.GetAttribute("Text");
1039  if( !CurrAttrib.Empty() )
1040  this->SetText( CurrAttrib.AsString() );
1041  }else{
1042  MEZZ_EXCEPTION(Exception::INVALID_VERSION_EXCEPTION,"Incompatible XML Version for " + (TextLayer::GetSerializableName() + "Text") + ": Not Version 1.");
1043  }
1044  }else{
1045  MEZZ_EXCEPTION(Exception::II_IDENTITY_NOT_FOUND_EXCEPTION,TextLayer::GetSerializableName() + "Text" + " was not found in the provided XML node, which was expected.");
1046  }
1047  }
1048 
1050  { return TextLayer::GetSerializableName(); }
1051 
1053  { return "TextLayer"; }
1054  }//UI
1055 }//Mezzanine
1056 
1057 #endif