1 /*
2  * This source file is part of MyGUI. For the latest info, see http://mygui.info/
3  * Distributed under the MIT License
4  * (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT)
5  */
6 
7 #include "MyGUI_Precompiled.h"
8 #include "MyGUI_TextIterator.h"
9 
10 namespace MyGUI
11 {
12 
TextIterator()13 	TextIterator::TextIterator() :
14 		mPosition(0),
15 		mSize(ITEM_NONE),
16 		mFirst(true),
17 		mHistory(nullptr)
18 	{
19 	}
20 
TextIterator(const UString & _text,VectorChangeInfo * _history)21 	TextIterator::TextIterator(const UString& _text, VectorChangeInfo* _history) :
22 		mText(_text),
23 		mCurrent(mText.begin()),
24 		mEnd(mText.end()),
25 		mSave(mEnd),
26 		mPosition(0),
27 		mSize(ITEM_NONE),
28 		mFirst(true),
29 		mHistory(_history)
30 	{
31 	}
32 
moveNext()33 	bool TextIterator::moveNext()
34 	{
35 		if (mCurrent == mEnd) return false;
36 		else if (mFirst)
37 		{
38 			mFirst = false;
39 			return true;
40 		}
41 
42 		// ставим на следующий символ проскакивая все тэги
43 		for (UString::iterator iter = mCurrent; iter != mEnd; ++iter)
44 		{
45 
46 			if ((*iter) == L'#')
47 			{
48 
49 				// следующий символ
50 				++ iter;
51 				if (iter == mEnd)
52 				{
53 					mCurrent = mEnd;
54 					return false;
55 				}
56 
57 				// две решетки подряд
58 				if ((*iter) == L'#')
59 				{
60 
61 					// следующий символ
62 					mPosition ++;
63 					++iter;
64 					if (iter == mEnd)
65 					{
66 						mCurrent = mEnd;
67 						return false;
68 					}
69 
70 					// указатель на следующий символ
71 					mCurrent = iter;
72 					return true;
73 				}
74 
75 				// остальные 5 символов цвета
76 				for (size_t pos = 0; pos < 5; pos++)
77 				{
78 					// следующий символ
79 					++ iter;
80 					if (iter == mEnd)
81 					{
82 						mCurrent = mEnd;
83 						return false;
84 					}
85 				}
86 
87 			}
88 			else
89 			{
90 
91 				// обыкновенный символ
92 				mPosition ++;
93 				++iter;
94 				if (iter == mEnd)
95 				{
96 					mCurrent = mEnd;
97 					return false;
98 				}
99 
100 				// указатель на следующий символ
101 				mCurrent = iter;
102 				return true;
103 			}
104 		}
105 
106 		return false;
107 	}
108 
109 	// возвращает цвет
getTagColour(UString & _colour) const110 	bool TextIterator::getTagColour(UString& _colour) const
111 	{
112 		if (mCurrent == mEnd) return false;
113 
114 		UString::iterator iter = mCurrent;
115 
116 		// нам нужен последний цвет
117 		bool ret = false;
118 		while (getTagColour(_colour, iter))
119 		{
120 			ret = true;
121 		}
122 
123 		return ret;
124 	}
125 
setTagColour(const Colour & _colour)126 	bool TextIterator::setTagColour(const Colour& _colour)
127 	{
128 		if (mCurrent == mEnd) return false;
129 		// очищаем все цвета
130 		clearTagColour();
131 		// на всякий
132 		if (mCurrent == mEnd) return false;
133 
134 		const size_t SIZE = 16;
135 		wchar_t buff[SIZE];
136 
137 #ifdef __MINGW32__
138 		swprintf(buff, L"#%.2X%.2X%.2X\0", (int)(_colour.red * 255), (int)(_colour.green * 255), (int)(_colour.blue * 255));
139 #else
140 		swprintf(buff, SIZE, L"#%.2X%.2X%.2X\0", (int)(_colour.red * 255), (int)(_colour.green * 255), (int)(_colour.blue * 255));
141 #endif
142 		// непосредственная вставка
143 		UString tmpStr = UString(buff);
144 		insert(mCurrent, tmpStr);
145 
146 		return true;
147 	}
148 
setTagColour(UString _colour)149 	bool TextIterator::setTagColour(UString _colour)
150 	{
151 		if (mCurrent == mEnd) return false;
152 		// очищаем все цвета
153 		clearTagColour();
154 		// на всякий
155 		if (mCurrent == mEnd) return false;
156 
157 		// проверяем на цвет хоть чуть чуть
158 		if ( (_colour.size() != 7) || (_colour.find(L'#', 1) != MyGUI::UString::npos) ) return false;
159 
160 		// непосредственная вставка
161 		insert(mCurrent, _colour);
162 
163 		return true;
164 	}
165 
166 	// возвращает размер строки
getSize() const167 	size_t TextIterator::getSize() const
168 	{
169 		if (mSize != ITEM_NONE) return mSize;
170 		mSize = mPosition;
171 
172 		for (UString::iterator iter = mCurrent; iter != mEnd; ++iter)
173 		{
174 
175 			if ((*iter) == L'#')
176 			{
177 				// следующий символ
178 				++ iter;
179 				if (iter == mEnd) break;
180 
181 				// тэг цвета
182 				if ((*iter) != L'#')
183 				{
184 					// остальные 5 символов цвета
185 					for (size_t pos = 0; pos < 5; pos++)
186 					{
187 						++ iter;
188 						if (iter == mEnd)
189 						{
190 							--iter;
191 							break;
192 						}
193 					}
194 					continue;
195 				}
196 			}
197 
198 			// обыкновенный символ
199 			mSize ++;
200 		}
201 
202 		return mSize;
203 	}
204 
205 	// возвращает текст без тегов
getOnlyText(const UString & _text)206 	UString TextIterator::getOnlyText(const UString& _text)
207 	{
208 		UString ret;
209 		ret.reserve(_text.size());
210 
211 		UString::const_iterator end = _text.end();
212 		for (UString::const_iterator iter = _text.begin(); iter != end; ++iter)
213 		{
214 
215 			if ((*iter) == L'#')
216 			{
217 				// следующий символ
218 				++ iter;
219 				if (iter == end) break;
220 
221 				// тэг цвета
222 				if ((*iter) != L'#')
223 				{
224 					// остальные 5 символов цвета
225 					for (size_t pos = 0; pos < 5; pos++)
226 					{
227 						++ iter;
228 						if (iter == end)
229 						{
230 							--iter;
231 							break;
232 						}
233 					}
234 					continue;
235 				}
236 			}
237 
238 			// обыкновенный символ
239 			ret.push_back(*iter);
240 		}
241 
242 		return ret;
243 	}
244 
245 	// возвращает цвет
getTagColour(UString & _colour,UString::iterator & _iter) const246 	bool TextIterator::getTagColour(UString& _colour, UString::iterator& _iter) const
247 	{
248 		if ( (_iter == mEnd) || ((*_iter) != L'#') ) return false;
249 
250 		// следующий символ
251 		++_iter;
252 		if ( (_iter == mEnd) || ((*_iter) == L'#') ) return false;
253 
254 		// берем цвет
255 		wchar_t buff[16] = L"#FFFFFF\0";
256 		buff[1] = (wchar_t)(*_iter);
257 		for (size_t pos = 2; pos < 7; pos++)
258 		{
259 			++_iter;
260 			if ( _iter == mEnd ) return false;
261 			buff[pos] = (wchar_t)(*_iter);
262 		}
263 
264 		// ставим на следующий тег или символ
265 		++_iter;
266 
267 		// возвращаем цвет
268 		_colour = buff;
269 		return true;
270 	}
271 
clearNewLine(UString & _text)272 	void TextIterator::clearNewLine(UString& _text)
273 	{
274 		for (UString::iterator iter = _text.begin(); iter != _text.end(); ++iter)
275 		{
276 			if ( ((*iter) == FontCodeType::NEL) ||
277 				((*iter) == FontCodeType::CR) ||
278 				((*iter) == FontCodeType::LF) )
279 			{
280 				(*iter) = FontCodeType::Space;
281 			}
282 		}
283 	}
284 
saveStartPoint()285 	bool TextIterator::saveStartPoint()
286 	{
287 		if (mCurrent == mEnd) return false;
288 		mSave = mCurrent;
289 		return true;
290 	}
291 
getFromStart()292 	UString TextIterator::getFromStart()
293 	{
294 		if (mSave == mEnd) return L"";
295 		size_t start = mSave - mText.begin();
296 		return mText.substr(start, mCurrent - mText.begin() - start);
297 	}
298 
eraseFromStart()299 	bool TextIterator::eraseFromStart()
300 	{
301 		if (mSave == mEnd) return false;
302 		mCurrent = erase(mSave, mCurrent);
303 		mSave = mEnd = mText.end();
304 		return true;
305 	}
306 
insertText(const UString & _insert,bool _multiLine)307 	void TextIterator::insertText(const UString& _insert, bool _multiLine)
308 	{
309 		UString text = _insert;
310 
311 		// нормализуем
312 		normaliseNewLine(text);
313 
314 		if (!_multiLine)
315 			clearNewLine(text);
316 
317 		insert(mCurrent, text);
318 	}
319 
setText(const UString & _text,bool _multiLine)320 	void TextIterator::setText(const UString& _text, bool _multiLine)
321 	{
322 		// сначала все очищаем
323 		clear();
324 
325 		// а теперь вставляем
326 		UString text = _text;
327 
328 		// нормализуем
329 		normaliseNewLine(text);
330 
331 		if (!_multiLine)
332 			clearNewLine(text);
333 
334 		insert(mCurrent, text);
335 	}
336 
getTextCharInfo(Char _char)337 	UString TextIterator::getTextCharInfo(Char _char)
338 	{
339 		if (_char == L'#') return L"##";
340 		wchar_t buff[16] = L"_\0";
341 		buff[0] = (wchar_t)_char;
342 		return buff;
343 	}
344 
convertTagColour(const Colour & _colour)345 	UString TextIterator::convertTagColour(const Colour& _colour)
346 	{
347 		const size_t SIZE = 16;
348 		wchar_t buff[SIZE];
349 //FIXME
350 #ifdef __MINGW32__
351 		swprintf(buff, L"#%.2X%.2X%.2X\0", (int)(_colour.red * 255), (int)(_colour.green * 255), (int)(_colour.blue * 255));
352 #else
353 		swprintf(buff, SIZE, L"#%.2X%.2X%.2X\0", (int)(_colour.red * 255), (int)(_colour.green * 255), (int)(_colour.blue * 255));
354 #endif
355 		return buff;
356 	}
357 
toTagsString(const UString & _text)358 	UString TextIterator::toTagsString(const UString& _text)
359 	{
360 		// преобразуем в строку с тегами
361 		UString text(_text);
362 		for (UString::iterator iter = text.begin(); iter != text.end(); ++iter)
363 		{
364 			// потом переделать через TextIterator чтобы отвязать понятие тег от эдита
365 			if (L'#' == (*iter)) iter = text.insert(++iter, L'#');
366 		}
367 		return text;
368 	}
369 
insert(UString::iterator & _start,UString & _insert)370 	void TextIterator::insert(UString::iterator& _start, UString& _insert)
371 	{
372 		// сбрасываем размер
373 		mSize = ITEM_NONE;
374 		// записываем в историю
375 		if (mHistory) mHistory->push_back(TextCommandInfo(_insert, _start - mText.begin(), TextCommandInfo::COMMAND_INSERT));
376 		// запоминаем позицию итератора
377 		size_t pos = _start - mText.begin();
378 		size_t pos_save = (mSave == mEnd) ? ITEM_NONE : _start - mText.begin();
379 		// непосредственно вставляем
380 		mText.insert(_start, _insert.begin(), _insert.end());
381 		// возвращаем итераторы
382 		_start = mText.begin() + pos;
383 		mEnd = mText.end();
384 		(pos_save == ITEM_NONE) ? mSave = mEnd : mSave = mText.begin() + pos_save;
385 	}
386 
erase(UString::iterator _start,UString::iterator _end)387 	UString::iterator TextIterator::erase(UString::iterator _start, UString::iterator _end)
388 	{
389 		// сбрасываем размер
390 		mSize = ITEM_NONE;
391 		// сохраняем в историю
392 		size_t start = _start - mText.begin();
393 		if (mHistory) mHistory->push_back(TextCommandInfo(mText.substr(start, _end - _start), start, TextCommandInfo::COMMAND_ERASE));
394 		// возвращаем итератор
395 		return mText.erase(_start, _end);
396 	}
397 
clear()398 	void TextIterator::clear()
399 	{
400 		if (mText.empty()) return;
401 
402 		// записываем в историю
403 		if (mHistory) mHistory->push_back(TextCommandInfo(mText, 0, TextCommandInfo::COMMAND_ERASE));
404 
405 		// все сбрасываем
406 		mText.clear();
407 		mCurrent = mText.begin();
408 		mEnd = mSave = mText.end();
409 		mSize = ITEM_NONE;
410 	}
411 
cutMaxLength(size_t _max)412 	void TextIterator::cutMaxLength(size_t _max)
413 	{
414 		if ( (mSize != ITEM_NONE) && (mSize <= _max) ) return;
415 		if (mPosition > _max)
416 		{
417 			// придется считать сначала
418 			mSize = mPosition = 0;
419 			mCurrent = mText.begin();
420 			mEnd = mSave = mText.end();
421 		}
422 
423 		mSize = mPosition;
424 
425 		for (UString::iterator iter = mCurrent; iter != mEnd; ++iter)
426 		{
427 
428 			if ((*iter) == L'#')
429 			{
430 				// следующий символ
431 				++ iter;
432 				if (iter == mEnd) break;
433 
434 				// тэг цвета
435 				if ((*iter) != L'#')
436 				{
437 					// остальные 5 символов цвета
438 					for (size_t pos = 0; pos < 5; pos++)
439 					{
440 						++ iter;
441 						if (iter == mEnd)
442 						{
443 							-- iter;
444 							break;
445 						}
446 					}
447 					continue;
448 				}
449 			}
450 
451 			// проверяем и обрезаем
452 			if (mSize == _max)
453 			{
454 				mPosition = mSize; // сохраняем
455 				mCurrent = erase(iter, mEnd);
456 				mSave = mEnd = mText.end();
457 				mSize = mPosition; // восстанавливаем
458 				return;
459 			}
460 
461 			// увеличиваем
462 			mSize ++;
463 		}
464 	}
465 
cutMaxLengthFromBeginning(size_t _max)466 	void TextIterator::cutMaxLengthFromBeginning(size_t _max)
467 	{
468 		// узнаем размер без тегов
469 		size_t size = getSize();
470 		if (size <= _max) return;
471 
472 		// разница
473 		size_t diff = size - _max;
474 
475 		// последний цвет
476 		UString::iterator iter_colour = mEnd;
477 
478 		// теперь пройдем от начала и узнаем реальную позицию разницы
479 		UString::iterator iter = mText.begin();
480 		for (; iter != mEnd; ++iter)
481 		{
482 			if ((*iter) == L'#')
483 			{
484 				UString::iterator save = iter;
485 
486 				// следующий символ
487 				++ iter;
488 				if (iter == mEnd) break;
489 
490 				// тэг цвета
491 				if ((*iter) != L'#')
492 				{
493 					// остальные 5 символов цвета
494 					for (size_t pos = 0; pos < 5; pos++)
495 					{
496 						++ iter;
497 						if (iter == mEnd)
498 						{
499 							-- iter;
500 							break;
501 						}
502 					}
503 					// сохраняем цвет
504 					iter_colour = save;
505 				}
506 				continue;
507 			}
508 			// обычный символ был
509 			if (diff == 0) break;
510 			-- diff;
511 		}
512 
513 		UString colour;
514 		// если бы цвет, то вставляем назад
515 		if (iter_colour != mEnd)
516 		{
517 			colour.append(iter_colour, iter_colour + size_t(7));
518 		}
519 
520 		mCurrent = erase(mText.begin(), iter);
521 		mEnd = mText.end();
522 		mSave = mText.end(); //FIXME
523 		mPosition = 0;
524 		mSize = _max;
525 
526 		if ( ! colour.empty() ) setTagColour(colour);
527 
528 	}
529 
clearTagColour()530 	void TextIterator::clearTagColour()
531 	{
532 		if (mCurrent == mEnd) return;
533 
534 		UString::iterator iter = mCurrent;
535 		UString colour;
536 		// нам нужен последний цвет
537 		while (getTagColour(colour, iter))
538 		{
539 			// обязательно обновляем итераторы
540 			iter = mCurrent = erase(mCurrent, iter);
541 			mEnd = mText.end();
542 		}
543 	}
544 
getPosition() const545 	size_t TextIterator::getPosition() const
546 	{
547 		return mPosition;
548 	}
549 
getText() const550 	const UString& TextIterator::getText() const
551 	{
552 		return mText;
553 	}
554 
clearText()555 	void TextIterator::clearText()
556 	{
557 		clear();
558 	}
559 
getTextNewLine()560 	UString TextIterator::getTextNewLine()
561 	{
562 		return L"\n";
563 	}
564 
normaliseNewLine(UString & _text)565 	void TextIterator::normaliseNewLine(UString& _text)
566 	{
567 		for (size_t index = 0; index < _text.size(); ++index)
568 		{
569 			Char character = _text[index];
570 			if ((character == FontCodeType::CR) &&
571 				((index + 1) < _text.size()) &&
572 				(_text[index + 1] == FontCodeType::LF))
573 			{
574 				_text.erase(index, 1);
575 			}
576 		}
577 	}
578 
579 } // namespace MyGUI
580