MezzanineEngine 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
textline.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 _uitextline_cpp
42 #define _uitextline_cpp
43 
44 #include "UI/textline.h"
45 #include "UI/textlayer.h"
46 #include "UI/quadrenderable.h"
47 
48 #include <algorithm>
49 
50 namespace Mezzanine
51 {
52  namespace UI
53  {
54  ///////////////////////////////////////////////////////////////////////////////
55  // TextLine Methods
56 
57  TextLine::TextLine(TextLayer* ParentLayer) :
58  Parent(ParentLayer),
59  CurrLength(0),
60  TallestHeight(0),
61  PositionOffset(0)
62  { }
63 
65  { }
66 
68  { return this->Parent->GetParent()->GetActualSize().X * this->Parent->GetScale().X; }
69 
70  ///////////////////////////////////////////////////////////////////////////////
71  // Utility
72 
74  {
75  this->Alignment = Align;
76  this->RecalculateOffsets();
77  }
78 
80  {
81  return this->Alignment;
82  }
83 
85  {
86  OffsetResultPair Ret(NULL,Vector2(0,0));
87  ConstCharacterIterator CharIt = this->Characters.begin();
88  // Check if we're too far to the left side to get anything
89  if( Offset < (*CharIt)->GetLengthOffset() )
90  {
91  Ret.first = NULL;
92  Ret.second.X = this->GetLeftMostCursorPosition();
93  Ret.second.Y = this->PositionOffset;
94  return Ret;
95  }
96 
97  while( CharIt != this->Characters.end() && Offset < (*CharIt)->GetLengthOffset() )
98  {
99  ++CharIt;
100  }
101 
102  // Check if we're too far to the right side to get anything
103  if( CharIt == (--this->Characters.end()) && Offset > (*CharIt)->GetLengthOffset() + (*CharIt)->GetCharacterSize().X )
104  {
105  Ret.first = NULL;
106  Ret.second.X = this->GetRightMostCursorPosition();
107  Ret.second.Y = this->PositionOffset;
108  return Ret;
109  }
110 
111  // Get our character dimensions for checking which side we should be on
112  Real CharXPos = (*CharIt)->GetLengthOffset();
113  Real CharXSize = (*CharIt)->GetCharacterSize().X;
114  // Fill in our values
115  Ret.first = (*CharIt);
116  Ret.second.X = ( CharXPos + (CharXSize * 0.5) < Offset ? CharXPos + CharXSize : CharXPos );
117  Ret.second.Y = this->PositionOffset;
118 
119  return Ret;
120  }
121 
123  {
124  ConstCharacterIterator CharIt = this->Characters.begin();
125  // Check if we're too far to the left side to get anything
126  if( Offset < (*CharIt)->GetLengthOffset() )
127  return 0;
128 
129  Integer RetIndex = 0;
130  while( CharIt != this->Characters.end() && Offset < (*CharIt)->GetLengthOffset() )
131  {
132  ++CharIt;
133  ++RetIndex;
134  }
135 
136  if( CharIt == this->Characters.end() )
137  return -1;
138 
139  // Get our character dimensions for checking which side we should be on
140  Real CharXPos = (*CharIt)->GetLengthOffset();
141  Real CharXSize = (*CharIt)->GetCharacterSize().X;
142  return ( CharXPos + (CharXSize * 0.5) < Offset ? RetIndex : ++RetIndex );
143  }
144 
146  {
147  if( Index < 0 || Index > this->Characters.size() )
148  {
149  ConstCharacterIterator Last = --(this->Characters.end());
150  return (*Last)->GetLengthOffset() + (*Last)->GetCharacterSize().X;
151  }else{
152  Integer IndexCount = 0;
153  ConstCharacterIterator CharIt = this->Characters.begin();
154 
155  while( CharIt != this->Characters.end() && IndexCount < Index )
156  {
157  ++IndexCount;
158  ++CharIt;
159  }
160 
161  return (*CharIt)->GetLengthOffset();
162  }
163  }
164 
166  {
167  Real MaxWidth = GetMaxWidth();
168  switch(this->Alignment)
169  {
170  case UI::LA_TopLeft: return 0; break;
171  case UI::LA_BottomRight: return MaxWidth - CurrLength; break;
172  case UI::LA_Center: return (MaxWidth * 0.5) - (CurrLength * 0.5); break;
173  }
174  }
175 
177  {
178  Real MaxWidth = GetMaxWidth();
179  switch(this->Alignment)
180  {
181  case UI::LA_TopLeft: return CurrLength; break;
182  case UI::LA_BottomRight: return MaxWidth; break;
183  case UI::LA_Center: return (MaxWidth * 0.5) + (CurrLength * 0.5); break;
184  }
185  }
186 
188  {
189  CharacterIterator CharIt = this->Characters.begin();
190  // Check if we're too far to the left side to get anything
191  if( Position < (*CharIt)->GetLengthOffset() )
192  return this->GetLeftMostCursorPosition();
193 
194  while( CharIt != this->Characters.end() && Position < (*CharIt)->GetLengthOffset() )
195  {
196  if( (*CharIt)->IsNewLine() )
197  continue;
198 
199  ++CharIt;
200  }
201 
202  // Get some values to check/math with
203  Real CharXPos = (*CharIt)->GetLengthOffset();
204  Real CharXSize = (*CharIt)->GetCharacterSize().X;
205 
206  // Check if we're too far to the right side to get anything
207  if( CharIt == (--(this->Characters.end())) && Position > CharXPos + CharXSize )
208  return this->GetRightMostCursorPosition();
209 
210  return ( Position < CharXPos + (CharXSize * 0.5) ? CharXPos : CharXPos + CharXSize );
211  }
212 
213  ///////////////////////////////////////////////////////////////////////////////
214  // Transform Related Methods
215 
216  void TextLine::SetPositionOffset(const Real& Offset)
217  { this->PositionOffset = Offset; }
218 
220  { return this->PositionOffset; }
221 
223  { return this->CurrLength; }
224 
226  { return ( Characters.empty() ? 0 : this->TallestHeight ); }
227 
228  ///////////////////////////////////////////////////////////////////////////////
229  // Character Management
230 
232  {
233  CharacterIterator Last = this->GetLastCharacter();
234  Real CharAdvance = ToAdd->GetCharacterAdvance( Last != this->Characters.end() ? (*Last)->GetCharGlyph() : NULL );
235  Real AddedLength = this->CurrLength + CharAdvance;
236 
237  if( AddedLength <= this->GetMaxWidth() ) {
238  this->CurrLength = AddedLength;
239  this->TallestHeight = std::max(this->TallestHeight,ToAdd->GetLineHeight());
240  this->AppendToBack(ToAdd);
241  this->RecalculateOffsets();
242  return true;
243  }
244 
245  return false;
246  }
247 
249  { return this->AppendCharacters(ToAdd.begin(),ToAdd.end()); }
250 
252  { return this->AppendCharacters(Pair.first,Pair.second); }
253 
255  { return this->AppendFittingCharacters(ToAdd.begin(),ToAdd.end()); }
256 
258  { return this->AppendFittingCharacters(Pair.first,Pair.second); }
259 
261  {
262  CharacterIterator CharIt = this->Characters.begin();
263  // Check to see if the index is out of bounds
264  if( Index >= this->Characters.size() )
265  return NULL;
266 
267  UInt32 Count = 0;
268  while( CharIt != this->Characters.end() && Count < Index )
269  {
270  ++CharIt;
271  }
272 
273  return (*CharIt);
274  }
275 
277  {
278  CharacterIterator CharIt = this->Characters.begin();
279  // Check if we're too far to the left side to get anything
280  if( Offset < (*CharIt)->GetLengthOffset() )
281  return NULL;
282 
283  while( CharIt != this->Characters.end() && Offset < (*CharIt)->GetLengthOffset() )
284  {
285  if( (*CharIt)->IsNewLine() )
286  continue;
287 
288  ++CharIt;
289  }
290 
291  // Check if we're too far to the right side to get anything
292  if( CharIt == (--(this->Characters.end())) && Offset > (*CharIt)->GetLengthOffset() + (*CharIt)->GetCharacterSize().X )
293  return NULL;
294 
295  return (*CharIt);
296  }
297 
299  {
300  return this->Characters.size();
301  }
302 
304  {
305  this->CurrLength = 0;
306  this->TallestHeight = 0;
307 
308  this->Characters.clear();
309  this->Parent->_MarkDirty();
310  }
311 
313  { return this->Characters.begin(); }
314 
316  { return this->Characters.end(); }
317 
319  { return this->Characters.begin(); }
320 
322  { return this->Characters.end(); }
323 
324  ///////////////////////////////////////////////////////////////////////////////
325  // LeftToRightTextLine Methods
326 
328  TextLine(ParentLayer)
329  { this->Alignment = UI::LA_TopLeft; }
330 
332  { }
333 
335  {
336  if( this->Characters.size() > 1 ) return *(++(this->Characters.rbegin()));
337  else return NULL;
338  }
339 
341  {
342  Real CursorPosition = this->GetLeftMostCursorPosition();
343  Character* Previous = NULL;
344  for( CharacterIterator CharIt = this->Characters.begin() ; CharIt != this->Characters.end() ; ++CharIt )
345  {
346  (*CharIt)->SetLengthOffset(CursorPosition);
347  CursorPosition += (*CharIt)->GetCharacterAdvance( Previous != NULL ? Previous->GetCharGlyph() : NULL );
348  Previous = (*CharIt);
349  }
350  this->Parent->_MarkDirty();
351  }
352 
354  {
355  this->Characters.push_back(ToAppend);
356  }
357 
359  {
360  this->Characters.insert(this->Characters.end(),First,Last);
361  }
362 
363  ///////////////////////////////////////////////////////////////////////////////
364  // Utility
365 
367  {
368  return this->GetLeftMostCursorPosition();
369  }
370 
371  ///////////////////////////////////////////////////////////////////////////////
372  // Character Management
373 
375  {
376  if( First == Last )
377  return false;
378 
379  // Set up data to be used
380  Real Tallest = 0, SequenceLength = 0;
381  Character* Previous = ( this->GetLastCharacter() != this->Characters.end() ? *(this->GetLastCharacter()) : NULL );
382 
383  // Collect the data we need from the provided characters
384  for( CharacterIterator CharIt = First ; CharIt != Last ; ++CharIt )
385  {
386  SequenceLength += (*CharIt)->GetCharacterAdvance(Previous->GetCharGlyph());
387  Tallest = std::max(Tallest,(*CharIt)->GetLineHeight());
388  }
389 
390  Real AddedLength = SequenceLength + this->CurrLength;
391  if( AddedLength <= GetMaxWidth() )
392  {
393  this->CurrLength = AddedLength;
394  this->TallestHeight = std::max(TallestHeight,Tallest);
395  this->AppendToBack(First,Last);
396  this->RecalculateOffsets();
397  return true;
398  }
399 
400  return false;
401  }
402 
404  {
405  if( First == Last )
406  return First;
407 
408  // Set up data to be used
409  Real MaxWidth = GetMaxWidth();
410  Character* Previous = ( this->GetLastCharacter() != this->Characters.end() ? *(this->GetLastCharacter()) : NULL );
411 
412  // Setup our iterator that will be used with our return and append operations
413  CharacterIterator CharIt = First;
414 
415  // Loop through our range
416  for( ; CharIt != Last ; ++CharIt )
417  {
418  Real CharAdvance = (*CharIt)->GetCharacterAdvance(Previous->GetCharGlyph());
419  Real AddedLength = CurrLength + CharAdvance;
420  if( AddedLength <= MaxWidth )
421  {
422  this->CurrLength = AddedLength;
423  this->TallestHeight = std::max(TallestHeight,(*CharIt)->GetLineHeight());
424  }else{
425  break;
426  }
427  }
428 
429  // Append, adjust offsets, and return
430  this->AppendToBack(First,CharIt);
431  this->RecalculateOffsets();
432  return CharIt;
433  }
434 
436  {
437  if( !this->Characters.empty() ) return this->Characters.begin();
438  else return this->Characters.end();
439  }
440 
442  {
443  if( !this->Characters.empty() ) return --(this->Characters.end());
444  else return this->Characters.end();
445  }
446 
448  {
449  if( Current != this->Characters.end() ) {
450  CharacterIterator Copy = Current;
451  return ++Copy;
452  }
453  return this->Characters.end();
454  }
455 
457  {
458  this->Characters.pop_front();
459  //Characters.erase(Characters.begin());
460  this->RecalculateOffsets();
461  }
462 
464  {
465  this->Characters.pop_back();
466  this->RecalculateOffsets();
467  }
468 
469  ///////////////////////////////////////////////////////////////////////////////
470  // RightToLeftTextLine Methods
471 
473  TextLine(ParentLayer)
474  { this->Alignment = UI::LA_BottomRight; }
475 
477  { }
478 
480  {
481  if( this->Characters.size() > 1 ) return *(++(this->Characters.begin()));
482  else return NULL;
483  }
484 
486  {
487  Real CursorPosition = this->GetRightMostCursorPosition();
488  Character* Previous = NULL;
489  for( ReverseCharacterIterator CharIt = this->Characters.rbegin() ; CharIt != this->Characters.rend() ; ++CharIt )
490  {
491  CursorPosition -= (*CharIt)->GetCharacterAdvance( Previous != NULL ? Previous->GetCharGlyph() : NULL );
492  (*CharIt)->SetLengthOffset(CursorPosition);
493  Previous = (*CharIt);
494  }
495  this->Parent->_MarkDirty();
496  }
497 
499  {
500  this->Characters.insert(this->Characters.begin(),ToAppend);
501  }
502 
504  {
505  this->Characters.insert(this->Characters.begin(),First,Last);
506  }
507 
508  ///////////////////////////////////////////////////////////////////////////////
509  // Utility
510 
512  {
513  return this->GetRightMostCursorPosition();
514  }
515 
516  ///////////////////////////////////////////////////////////////////////////////
517  // Character Management
518 
520  {
521  if( First == Last )
522  return false;
523 
524  // Set up data to be used
525  Real Tallest = 0, SequenceLength = 0;
526  Character* Previous = ( this->GetLastCharacter() != this->Characters.end() ? *(this->GetLastCharacter()) : NULL );
527 
528  // Setup our iterators for iteration (format provided is for insertion)
529  CharacterIterator FirstCopy = First;
530  CharacterIterator LastCopy = Last;
531  --FirstCopy;
532  --LastCopy;
533 
534  // Collect the data we need from the provided characters
535  for( CharacterIterator CharIt = LastCopy ; CharIt != FirstCopy ; --CharIt )
536  {
537  SequenceLength += (*CharIt)->GetCharacterAdvance(Previous->GetCharGlyph());
538  Tallest = std::max(Tallest,(*CharIt)->GetLineHeight());
539  }
540 
541  Real AddedLength = SequenceLength + this->CurrLength;
542  if( AddedLength <= GetMaxWidth() )
543  {
544  this->CurrLength = AddedLength;
545  this->TallestHeight = std::max(TallestHeight,Tallest);
546  this->AppendToBack(First,Last);
547  this->RecalculateOffsets();
548  return true;
549  }
550 
551  return false;
552  }
553 
555  {
556  if( First == Last )
557  return First;
558 
559  // Set up data to be used
560  Real MaxWidth = GetMaxWidth();
561  Character* Previous = ( this->GetLastCharacter() != this->Characters.end() ? *(this->GetLastCharacter()) : NULL );
562 
563  // Setup our iterators for iteration (format provided is for insertion)
564  CharacterIterator FirstCopy = First;
565  CharacterIterator LastCopy = Last;
566  --FirstCopy;
567  --LastCopy;
568 
569  // Setup our iterator that will be used with our return and append operations
570  CharacterIterator CharIt = LastCopy;
571 
572  // Loop through our range
573  for( ; CharIt != FirstCopy ; --CharIt )
574  {
575  Real CharAdvance = (*CharIt)->GetCharacterAdvance(Previous->GetCharGlyph());
576  Real AddedLength = CurrLength + CharAdvance;
577  if( AddedLength <= MaxWidth )
578  {
579  this->CurrLength = AddedLength;
580  this->TallestHeight = std::max(TallestHeight,(*CharIt)->GetLineHeight());
581  }else{
582  break;
583  }
584  }
585 
586  // Append, adjust offsets, and return
587  this->AppendToBack(CharIt,Last);
588  this->RecalculateOffsets();
589  return CharIt;
590  }
591 
593  {
594  if( !this->Characters.empty() ) return --(this->Characters.end());
595  else return this->Characters.end();
596  }
597 
599  {
600  if( !this->Characters.empty() ) return this->Characters.begin();
601  else return this->Characters.end();
602  }
603 
605  {
606  CharacterIterator Copy = Current;
607  if( *Current != this->Characters.front() ) return --Copy;
608  else return Copy;
609  }
610 
612  {
613  this->Characters.pop_back();
614  this->RecalculateOffsets();
615  }
616 
618  {
619  this->Characters.pop_front();
620  //Characters.erase(Characters.begin());
621  this->RecalculateOffsets();
622  }
623  }//UI
624 }//Mezzanine
625 
626 #endif