1 /*
2 * This file is part of the Colobot: Gold Edition source code
3 * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam
4 * http://epsitec.ch; http://colobot.info; http://github.com/colobot
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see http://gnu.org/licenses
18 */
19
20
21 #include "ui/controls/edit.h"
22
23 #include "common/config.h"
24
25 #include "app/app.h"
26 #include "app/input.h"
27
28 #include "common/logger.h"
29 #include "common/make_unique.h"
30
31 #include "common/resources/inputstream.h"
32 #include "common/resources/outputstream.h"
33
34 #include "graphics/engine/engine.h"
35
36 #include "level/parser/parser.h"
37
38 #include "script/script.h"
39
40 #include "ui/controls/scroll.h"
41
42 #include <SDL.h>
43 #include <boost/algorithm/string.hpp>
44
45 #include <cstring>
46
47 namespace Ui
48 {
49 namespace
50 {
51 const float MARGX = (3.75f/640.0f);
52 const float MARGY = (3.75f/480.0f);
53 const float MARGYS = (2.75f/480.0f);
54 const float MARGY1 = (1.0f/480.0f);
55 //! time limit for double-click
56 const float DELAY_DBCLICK = 0.75f;
57 //! time limit for scroll
58 const float DELAY_SCROLL = 0.1f;
59 //! expansion for \b;
60 const float BIG_FONT = 1.6f;
61
62
63
64 //! Indicates whether a character is a space.
65
IsSpace(int character)66 bool IsSpace(int character)
67 {
68 return ( character == ' ' ||
69 character == '\t' ||
70 character == '\n' );
71 }
72
73 //! Indicates whether a character is part of a word.
74
IsWord(char c)75 bool IsWord(char c)
76 {
77 return ( isalnum(c) || c == '_');
78 }
79
80 //! Indicates whether a character is a word separator.
81
IsSep(int character)82 bool IsSep(int character)
83 {
84 if ( IsSpace(character) ) return false;
85 return !IsWord(character);
86 }
87
IsBreaker(char c)88 bool IsBreaker(char c)
89 {
90 return ( c == '.' || c == '{' || c == '}' ||
91 c == ';' || c == ':' || c == '[' || c == ']' ||
92 c == '(' || c == ')' || c == '=' || c == '"' || c == '\'' );
93 }
94
IsDelimiter(char c)95 bool IsDelimiter(char c)
96 {
97 return IsSpace( c ) || IsBreaker( c );
98 }
99
100 } // namespace
101
102 //! Object's constructor.
CEdit()103 CEdit::CEdit()
104 : CControl(),
105 m_maxChar( std::numeric_limits<int>::max() ),
106 m_text(),
107 m_lineOffset(),
108 m_lineIndent()
109 {
110 m_len = 0;
111
112 m_fontType = Gfx::FONT_STUDIO;
113 m_bEdit = true;
114 m_bHilite = true;
115 m_bInsideScroll = true;
116 m_bCapture = false;
117 m_bDisplaySpec = false;
118 m_bSoluce = false;
119 m_bGeneric = false;
120 m_bAutoIndent = false;
121 m_cursor1 = 0;
122 m_cursor2 = 0;
123 m_column = 0;
124
125 m_timeLastScroll = 0.0f;
126 m_timeBlink = 0.0f;
127 m_time = 0.0f;
128 m_historyCurrent = 0;
129 m_bMulti = false;
130 m_lineDescent = 0.0f;
131 m_timeLastClick = 0.0f;
132 m_bMultiFont = false;
133 m_lineAscent = 0.0f;
134 m_historyTotal = 0;
135 m_lineTotal = 0;
136 m_lineHeight = 0.0f;
137 m_lineVisible = 0;
138 m_lineFirst = 0;
139
140 HyperFlush();
141
142 m_bUndoForce = true;
143 m_undoOper = OPERUNDO_SPEC;
144 }
145
146 // Object's destructor.
147
~CEdit()148 CEdit::~CEdit()
149 {
150 FreeImage();
151
152 if (m_bFocus)
153 {
154 CApplication::GetInstancePointer()->SetTextInput(false, m_eventType);
155 }
156 }
157
158
159 // Creates a new editable line.
160
Create(Math::Point pos,Math::Point dim,int icon,EventType eventType)161 bool CEdit::Create(Math::Point pos, Math::Point dim, int icon, EventType eventType)
162 {
163 if ( eventType == EVENT_NULL ) eventType = GetUniqueEventType();
164 CControl::Create(pos, dim, icon, eventType);
165
166 m_len = 0;
167 m_lineFirst = 0;
168 m_time = 0.0f;
169 m_timeBlink = 0.0f;
170 m_timeLastClick = 0.0f;
171 m_timeLastScroll = 0.0f;
172
173 m_bMulti = false;
174 MoveAdjust();
175 if ( m_lineVisible <= 1 )
176 {
177 m_bMulti = false;
178 }
179 else
180 {
181 m_bMulti = true;
182 MoveAdjust(); // readjusts multi-line mode
183 m_scroll = MakeUnique<Ui::CScroll>();
184 m_scroll->Create(pos, dim, -1, EVENT_NULL);
185 MoveAdjust();
186 }
187
188 return true;
189 }
190
191
SetPos(Math::Point pos)192 void CEdit::SetPos(Math::Point pos)
193 {
194 CControl::SetPos(pos);
195 MoveAdjust();
196 }
197
SetDim(Math::Point dim)198 void CEdit::SetDim(Math::Point dim)
199 {
200 CControl::SetDim(dim);
201 MoveAdjust();
202 }
203
MoveAdjust()204 void CEdit::MoveAdjust()
205 {
206 Math::Point pos, dim;
207 float height;
208
209 m_lineDescent = m_engine->GetText()->GetDescent(m_fontType, m_fontSize);
210 m_lineAscent = m_engine->GetText()->GetAscent(m_fontType, m_fontSize);
211 m_lineHeight = m_engine->GetText()->GetHeight(m_fontType, m_fontSize);
212
213 height = m_dim.y-(m_bMulti?MARGY*2.0f:MARGY1);
214 m_lineVisible = static_cast<int>((height/m_lineHeight));
215
216 if (m_scroll != nullptr)
217 {
218 if ( m_bInsideScroll )
219 {
220 pos.x = m_pos.x + m_dim.x - MARGX-SCROLL_WIDTH;
221 pos.y = m_pos.y + MARGYS;
222 dim.x = SCROLL_WIDTH;
223 dim.y = m_dim.y - MARGYS*2.0f;
224 }
225 else
226 {
227 pos.x = m_pos.x + m_dim.x - SCROLL_WIDTH;
228 pos.y = m_pos.y;
229 dim.x = SCROLL_WIDTH;
230 dim.y = m_dim.y;
231 }
232 m_scroll->SetPos(pos);
233 m_scroll->SetDim(dim);
234 }
235
236 Justif();
237
238 if ( m_lineFirst > m_lineTotal-m_lineVisible )
239 {
240 m_lineFirst = m_lineTotal-m_lineVisible;
241 if ( m_lineFirst < 0 ) m_lineFirst = 0;
242 }
243
244 pos.x = m_pos.x+m_dim.x-(m_bMulti?SCROLL_WIDTH:0.0f);
245 pos.y = m_pos.y;
246 GlintCreate(pos, false, false);
247 }
248
249
250 // Management of an event.
251
EventProcess(const Event & event)252 bool CEdit::EventProcess(const Event &event)
253 {
254 bool bShift = false, bControl = false;
255
256 if ( (m_state & STATE_VISIBLE) == 0 ) return true;
257
258 if (event.type == EVENT_MOUSE_WHEEL &&
259 Detect(event.mousePos))
260 {
261 auto data = event.GetData<MouseWheelEventData>();
262 Scroll(m_lineFirst - data->y, true);
263 return false;
264 }
265
266 CControl::EventProcess(event);
267
268 if ( event.type == EVENT_FRAME )
269 {
270 m_time += event.rTime;
271 m_timeBlink += event.rTime;
272 }
273
274 if ( event.type == EVENT_MOUSE_MOVE || event.type == EVENT_MOUSE_BUTTON_DOWN || event.type == EVENT_MOUSE_BUTTON_UP )
275 {
276 if ( Detect(event.mousePos) &&
277 event.mousePos.x < m_pos.x+m_dim.x-(m_bMulti?MARGX+SCROLL_WIDTH:0.0f) )
278 {
279 if ( m_bEdit )
280 {
281 m_engine->SetMouseType(Gfx::ENG_MOUSE_EDIT);
282 }
283 else
284 {
285 if ( IsLinkPos(event.mousePos) )
286 {
287 m_engine->SetMouseType(Gfx::ENG_MOUSE_HAND);
288 }
289 else
290 {
291 m_engine->SetMouseType(Gfx::ENG_MOUSE_NORM);
292 }
293 }
294 }
295 }
296
297 if (m_scroll != nullptr && !m_bGeneric)
298 {
299 m_scroll->EventProcess(event);
300
301 if (event.type == m_scroll->GetEventType())
302 {
303 Scroll();
304 return true;
305 }
306 }
307
308 if (event.type == EVENT_KEY_DOWN)
309 {
310 bShift = ( (event.kmodState & KEY_MOD(SHIFT) ) != 0 );
311 #if PLATFORM_MACOSX
312 bControl = ( (event.kmodState & KEY_MOD(GUI) ) != 0);
313 #else
314 bControl = ( (event.kmodState & KEY_MOD(CTRL) ) != 0);
315 #endif
316 }
317
318 if ( event.type == EVENT_KEY_DOWN && m_bFocus )
319 {
320 auto data = event.GetData<KeyEventData>();
321
322 if ( (data->key == KEY(x) && !bShift && bControl) ||
323 (data->key == KEY(DELETE) && bShift && !bControl) )
324 {
325 Cut();
326 return true;
327 }
328 if ( (data->key == KEY(c) && !bShift && bControl) ||
329 (data->key == KEY(INSERT) && !bShift && bControl) )
330 {
331 Copy();
332 return true;
333 }
334 if ( (data->key == KEY(v) && !bShift && bControl) ||
335 (data->key == KEY(INSERT) && bShift && !bControl) )
336 {
337 Paste();
338 return true;
339 }
340
341 if ( data->key == KEY(a) && !bShift && bControl )
342 {
343 SetCursor(999999, 0);
344 return true;
345 }
346
347 if ( data->key == KEY(o) && !bShift && bControl )
348 {
349 m_event->AddEvent(Event(EVENT_STUDIO_OPEN));
350 }
351 if ( data->key == KEY(s) && !bShift && bControl )
352 {
353 m_event->AddEvent(Event(EVENT_STUDIO_SAVE));
354 }
355
356 if ( data->key == KEY(z) && !bShift && bControl )
357 {
358 Undo();
359 return true;
360 }
361
362 if ( data->key == KEY(u) && !bShift && bControl )
363 {
364 if ( MinMaj(false) ) return true;
365 }
366 if ( data->key == KEY(u) && bShift && bControl )
367 {
368 if ( MinMaj(true) ) return true;
369 }
370
371 if ( data->key == KEY(TAB) && !bShift && !bControl && !m_bAutoIndent )
372 {
373 if ( Shift(false) ) return true;
374 }
375 if ( data->key == KEY(TAB) && bShift && !bControl && !m_bAutoIndent )
376 {
377 if ( Shift(true) ) return true;
378 }
379
380 if ( m_bEdit )
381 {
382 if ( data->key == KEY(LEFT) )
383 {
384 MoveChar(-1, bControl, bShift);
385 return true;
386 }
387 if ( data->key == KEY(RIGHT) )
388 {
389 MoveChar(1, bControl, bShift);
390 return true;
391 }
392 if ( data->key == KEY(UP) && m_bMulti )
393 {
394 MoveLine(-1, bControl, bShift);
395 return true;
396 }
397 if ( data->key == KEY(DOWN) && m_bMulti )
398 {
399 MoveLine(1, bControl, bShift);
400 return true;
401 }
402
403 if ( data->key == KEY(PAGEUP) && m_bMulti ) // PageUp ?
404 {
405 MoveLine(-(m_lineVisible-1), bControl, bShift);
406 return true;
407 }
408 if ( data->key == KEY(PAGEDOWN) && m_bMulti ) // PageDown ?
409 {
410 MoveLine(m_lineVisible-1, bControl, bShift);
411 return true;
412 }
413 }
414 else
415 {
416 if ( data->key == KEY(LEFT) ||
417 data->key == KEY(UP) )
418 {
419 Scroll(m_lineFirst-1, true);
420 return true;
421 }
422 if ( data->key == KEY(RIGHT) ||
423 data->key == KEY(DOWN) )
424 {
425 Scroll(m_lineFirst+1, true);
426 return true;
427 }
428
429 if ( data->key == KEY(PAGEUP) ) // PageUp ?
430 {
431 Scroll(m_lineFirst-(m_lineVisible-1), true);
432 return true;
433 }
434 if ( data->key == KEY(PAGEDOWN) ) // PageDown ?
435 {
436 Scroll(m_lineFirst+(m_lineVisible-1), true);
437 return true;
438 }
439 }
440
441 if ( data->key == KEY(HOME) )
442 {
443 MoveHome(bControl, bShift);
444 return true;
445 }
446 if ( data->key == KEY(END) )
447 {
448 MoveEnd(bControl, bShift);
449 return true;
450 }
451
452 if ( data->key == KEY(BACKSPACE) && !bControl ) // backspace ( <- ) ?
453 {
454 Delete(-1);
455 SendModifEvent();
456 return true;
457 }
458 if ( data->key == KEY(DELETE) && !bControl )
459 {
460 Delete(1);
461 SendModifEvent();
462 return true;
463 }
464
465 if ( data->key == KEY(BACKSPACE) && bControl )
466 {
467 DeleteWord(-1);
468 SendModifEvent();
469 return true;
470 }
471 if ( data->key == KEY(DELETE) && bControl )
472 {
473 DeleteWord(1);
474 SendModifEvent();
475 return true;
476 }
477
478 if ( data->key == KEY(RETURN) && !bControl )
479 {
480 Insert('\n');
481 SendModifEvent();
482 return true;
483 }
484 if ( data->key == KEY(TAB) && !bControl )
485 {
486 Insert('\t');
487 SendModifEvent();
488 return true;
489 }
490 }
491
492 if ( event.type == EVENT_TEXT_INPUT && !bControl && m_bFocus )
493 {
494 auto data = event.GetData<TextInputData>();
495 Insert(data->text[0]); // TODO: insert utf-8 char
496 SendModifEvent();
497 return true;
498 }
499
500 if ( event.type == EVENT_FOCUS )
501 {
502 if ( event.customParam == m_eventType )
503 {
504 m_bFocus = true;
505 UpdateFocus();
506 }
507 else
508 {
509 m_bFocus = false;
510 UpdateFocus();
511 }
512 }
513
514 if (event.type == EVENT_MOUSE_BUTTON_DOWN &&
515 event.GetData<MouseButtonEventData>()->button == MOUSE_BUTTON_LEFT)
516 {
517 m_mouseFirstPos = event.mousePos;
518 m_mouseLastPos = event.mousePos;
519 if ( Detect(event.mousePos) )
520 {
521 if ( event.mousePos.x < m_pos.x+m_dim.x-(m_bMulti?MARGX+SCROLL_WIDTH:0.0f) )
522 {
523 MouseClick(event.mousePos);
524 if ( m_bEdit || m_bHilite ) m_bCapture = true;
525 }
526
527 if (!m_bFocus)
528 {
529 m_bFocus = true;
530 UpdateFocus();
531 }
532 }
533 else
534 {
535 if (m_bFocus)
536 {
537 m_bFocus = false;
538 UpdateFocus();
539 }
540 }
541 }
542
543 if ( event.type == EVENT_MOUSE_MOVE && m_bCapture )
544 {
545 m_mouseLastPos = event.mousePos;
546 MouseMove(event.mousePos);
547 }
548
549 if ( event.type == EVENT_FRAME && m_bCapture )
550 {
551 MouseMove(m_mouseLastPos);
552 }
553
554 if (event.type == EVENT_MOUSE_BUTTON_UP &&
555 event.GetData<MouseButtonEventData>()->button == MOUSE_BUTTON_LEFT)
556 {
557 if ( Detect(event.mousePos) )
558 {
559 if ( event.mousePos.x < m_pos.x+m_dim.x-(m_bMulti?MARGX+SCROLL_WIDTH:0.0f) )
560 {
561 MouseRelease(m_mouseFirstPos);
562 }
563 }
564 if ( m_bCapture )
565 {
566 if ( m_timeLastClick+DELAY_DBCLICK > m_time ) // double-click ?
567 {
568 MouseDoubleClick(event.mousePos);
569 }
570 m_timeLastClick = m_time;
571 m_bCapture = false;
572 }
573 }
574
575 return true;
576 }
577
578
579 // Sends an event to indicate that the text was modified.
580
SendModifEvent()581 void CEdit::SendModifEvent()
582 {
583 m_event->AddEvent(Event(m_eventType));
584 }
585
586
587 // Detects whether the mouse is over a hyperlink character.
588
IsLinkPos(Math::Point pos)589 bool CEdit::IsLinkPos(Math::Point pos)
590 {
591 int i;
592
593 if ( m_format.empty() ) return false;
594
595 i = MouseDetect(pos);
596 if ( i == -1 ) return false;
597 if ( i >= m_len ) return false;
598
599 if ( m_format.size() > static_cast<unsigned int>(i) && ((m_format[i] & Gfx::FONT_MASK_LINK) != 0)) return true; // TODO
600 return false;
601 }
602
603
604 // Positions the cursor after a double click.
605
MouseDoubleClick(Math::Point mouse)606 void CEdit::MouseDoubleClick(Math::Point mouse)
607 {
608 int i, character;
609
610 if ( m_bMulti ) // Multi-line?
611 {
612 i = MouseDetect(mouse);
613 if ( i == -1 ) return;
614
615 while ( i > 0 )
616 {
617 character = static_cast<unsigned char>(m_text[i-1]);
618 if ( !IsWord(character) ) break;
619 i --;
620 }
621 m_cursor2 = i;
622
623 while ( i < m_len )
624 {
625 character = static_cast<unsigned char>(m_text[i]);
626 if ( !IsWord(character) ) break;
627 i ++;
628 }
629 m_cursor1 = i;
630 }
631 else // single-line?
632 {
633 m_cursor2 = 0;
634 m_cursor1 = m_len; // selects all
635 }
636
637 m_bUndoForce = true;
638
639 Justif();
640 ColumnFix();
641 }
642
643 // Positions the cursor when clicked.
644
MouseClick(Math::Point mouse)645 void CEdit::MouseClick(Math::Point mouse)
646 {
647 int i;
648
649 i = MouseDetect(mouse);
650 if ( i == -1 ) return;
651
652 if ( m_bEdit || m_bHilite )
653 {
654 m_cursor1 = i;
655 m_cursor2 = i;
656 m_bUndoForce = true;
657 m_timeBlink = 0.0f; // lights the cursor immediately
658 ColumnFix();
659 }
660 }
661
662 // Positions the cursor when clicked released.
663
MouseRelease(Math::Point mouse)664 void CEdit::MouseRelease(Math::Point mouse)
665 {
666 int i = MouseDetect(mouse);
667 if ( i == -1 ) return;
668
669 if ( !m_bEdit )
670 {
671 if ( m_format.size() > 0 && i < m_len && m_cursor1 == m_cursor2 &&
672 (m_format[i]&Gfx::FONT_MASK_LINK) != 0) //TODO
673 {
674 int rank = -1;
675 for ( int j=0 ; j<=i ; j++ )
676 {
677 if ( (j == 0 || (m_format[j-1]&Gfx::FONT_MASK_LINK) == 0) && // TODO check if good
678 (m_format[j+0]&Gfx::FONT_MASK_LINK) != 0) // TODO
679 {
680 rank ++;
681 }
682 }
683 assert(static_cast<unsigned int>(rank) < m_link.size());
684 HyperJump(m_link[rank].name, m_link[rank].marker);
685 }
686 }
687 }
688
689 // Positions the cursor after movement.
690
MouseMove(Math::Point mouse)691 void CEdit::MouseMove(Math::Point mouse)
692 {
693 int i;
694
695 if ( m_bMulti &&
696 m_timeLastScroll+DELAY_SCROLL <= m_time )
697 {
698 if ( mouse.y > m_pos.y+m_dim.y ) // above?
699 {
700 Scroll(m_lineFirst-1, false);
701 mouse.y = m_pos.y+m_dim.y-MARGY-m_lineHeight/2.0f;
702 }
703 if ( mouse.y < m_pos.y ) // lower?
704 {
705 Scroll(m_lineFirst+1, false);
706 mouse.y = m_pos.y+m_dim.y-MARGY-m_lineVisible*m_lineHeight+m_lineHeight/2.0f;
707 }
708 m_timeLastScroll = m_time;
709 }
710
711 i = MouseDetect(mouse);
712 if ( i != -1 )
713 {
714 m_cursor1 = i;
715 m_bUndoForce = true;
716 m_timeBlink = 0.0f; // lights the cursor immediately
717 ColumnFix();
718 }
719 }
720
721 // Positions the cursor when clicked.
722
MouseDetect(Math::Point mouse)723 int CEdit::MouseDetect(Math::Point mouse)
724 {
725 Math::Point pos;
726 float indentLength = 0.0f, offset, size;
727 int i, len, c;
728 bool bTitle;
729
730 if ( m_bAutoIndent )
731 {
732 indentLength = m_engine->GetText()->GetCharWidth(static_cast<Gfx::UTF8Char>(' '), m_fontType, m_fontSize, 0.0f)
733 * m_engine->GetEditIndentValue();
734 }
735
736 pos.y = m_pos.y+m_dim.y-m_lineHeight-(m_bMulti?MARGY:MARGY1);
737 for ( i=m_lineFirst ; i<m_lineTotal ; i++ )
738 {
739 bTitle = ( m_format.size() > 0 && (m_format[m_lineOffset[i]]&Gfx::FONT_MASK_TITLE) == Gfx::FONT_TITLE_BIG );
740
741 if ( i >= m_lineFirst+m_lineVisible ) break;
742
743 pos.x = m_pos.x+(7.5f/640.0f)*(m_fontSize/Gfx::FONT_SIZE_SMALL);
744 if ( m_bAutoIndent )
745 {
746 pos.x += indentLength*m_lineIndent[i];
747 }
748 offset = mouse.x-pos.x;
749
750 if ( bTitle ) pos.y -= m_lineHeight;
751
752 if ( mouse.y > pos.y )
753 {
754 len = m_lineOffset[i+1] - m_lineOffset[i];
755
756 if ( m_format.empty() )
757 {
758 // c = m_engine->GetText()->Detect(m_text.data()+m_lineOffset[i],
759 // len, offset, m_fontSize,
760 // m_fontStretch, m_fontType);
761 c = m_engine->GetText()->Detect(std::string(m_text.data()+m_lineOffset[i]).substr(0, len), m_fontType, m_fontSize, offset); // TODO check if good
762 }
763 else
764 {
765 size = m_fontSize;
766 if ( bTitle ) size *= Gfx::FONT_SIZE_BIG;
767
768 // c = m_engine->GetText()->Detect(m_text.data()+m_lineOffset[i],
769 // m_format+m_lineOffset[i],
770 // len, offset, size,
771 // m_fontStretch);
772 c = m_engine->GetText()->Detect(std::string(m_text.data()+m_lineOffset[i]).substr(0, len),
773 m_format.begin() + m_lineOffset[i],
774 m_format.end(),
775 size,
776 offset); // TODO check if good
777 }
778 return m_lineOffset[i]+c;
779 }
780
781 if ( bTitle ) i ++;
782 pos.y -= m_lineHeight;
783 }
784 return -1;
785 }
786
787
788 // Clears all history.
789
HyperFlush()790 void CEdit::HyperFlush()
791 {
792 m_historyTotal = 0;
793 m_historyCurrent = -1;
794 }
795
796 // Indicates which is the home page.
797
HyperHome(std::string filename)798 void CEdit::HyperHome(std::string filename)
799 {
800 HyperFlush();
801 HyperAdd(filename, 0);
802 }
803
804 // Performs a hyper jump through a link.
805
HyperJump(std::string name,std::string marker)806 void CEdit::HyperJump(std::string name, std::string marker)
807 {
808 if ( m_historyCurrent >= 0 )
809 {
810 m_history[m_historyCurrent].firstLine = m_lineFirst;
811 }
812
813 std::string filename = name + std::string(".txt");
814 filename = InjectLevelPathsForCurrentLevel(filename, "help/%lng%");
815 boost::replace_all(filename, "\\", "/"); //TODO: Fix this in files
816
817 if ( ReadText(filename) )
818 {
819 Justif();
820
821 int line = 0;
822 auto it = std::find_if(m_marker.begin(), m_marker.end(), [&marker](HyperMarker hyperMarker) { return hyperMarker.name == marker; });
823 if(it != m_marker.end())
824 {
825 int pos = it->pos;
826 for ( int i=0 ; i<m_lineTotal ; i++ )
827 {
828 if ( pos >= m_lineOffset[i] )
829 {
830 line = i;
831 }
832 }
833 }
834
835 SetFirstLine(line);
836 HyperAdd(filename, line);
837 }
838 }
839
840 // Adds text to the history of visited.
841
HyperAdd(std::string filename,int firstLine)842 bool CEdit::HyperAdd(std::string filename, int firstLine)
843 {
844 if ( m_historyCurrent >= EDITHISTORYMAX-1 ) return false;
845
846 m_historyCurrent ++;
847 m_history[m_historyCurrent].filename = filename;
848 m_history[m_historyCurrent].firstLine = firstLine;
849
850 m_historyTotal = m_historyCurrent+1;
851 return true;
852 }
853
854 // Indicates whether a button EVENT_HYPER_ * is active or not.
855
HyperTest(EventType event)856 bool CEdit::HyperTest(EventType event)
857 {
858 if ( event == EVENT_HYPER_HOME )
859 {
860 return ( m_historyCurrent > 0 );
861 }
862
863 if ( event == EVENT_HYPER_PREV )
864 {
865 return ( m_historyCurrent > 0 );
866 }
867
868 if ( event == EVENT_HYPER_NEXT )
869 {
870 return ( m_historyCurrent < m_historyTotal-1 );
871 }
872
873 return false;
874 }
875
876 // Performs the action corresponding to a button EVENT_HYPER_ *.
877
HyperGo(EventType event)878 bool CEdit::HyperGo(EventType event)
879 {
880 if ( !HyperTest(event) ) return false;
881
882 m_history[m_historyCurrent].firstLine = m_lineFirst;
883
884 if ( event == EVENT_HYPER_HOME )
885 {
886 m_historyCurrent = 0;
887 }
888
889 if ( event == EVENT_HYPER_PREV )
890 {
891 m_historyCurrent --;
892 }
893
894 if ( event == EVENT_HYPER_NEXT )
895 {
896 m_historyCurrent ++;
897 }
898
899 ReadText(m_history[m_historyCurrent].filename);
900 Justif();
901 SetFirstLine(m_history[m_historyCurrent].firstLine);
902 return true;
903 }
904
905
906 // Draw the editable line.
907
Draw()908 void CEdit::Draw()
909 {
910 Math::Point pos, ppos, dim, start, end;
911 float size = 0.0f, indentLength = 0.0f;
912 int i, j, beg, len, c1, c2, o1, o2, eol, line;
913
914 if ( (m_state & STATE_VISIBLE) == 0 ) return;
915
916 if ( m_state & STATE_SHADOW )
917 {
918 DrawShadow(m_pos, m_dim);
919 }
920
921 pos.x = m_pos.x;
922 pos.y = m_pos.y;
923 dim.x = m_dim.x;
924 if ( !m_bInsideScroll ) dim.x -= m_bMulti?SCROLL_WIDTH:0.0f;
925 dim.y = m_dim.y;
926 DrawBack(pos, dim); // background
927
928 if ( (m_state & STATE_ENABLE) == 0 ) return;
929
930 // Displays all lines.
931 c1 = m_cursor1;
932 c2 = m_cursor2;
933 if ( c1 > c2 ) Math::Swap(c1, c2); // always c1 <= c2
934
935 if ( m_bInsideScroll )
936 {
937 dim.x -= m_bMulti?SCROLL_WIDTH:0.0f + (1.0f/640.0f);
938 }
939
940 if ( m_bAutoIndent )
941 {
942 indentLength = m_engine->GetText()->GetCharWidth(static_cast<Gfx::UTF8Char>(' '), m_fontType, m_fontSize, 0.0f)
943 * m_engine->GetEditIndentValue();
944 }
945
946 pos.y = m_pos.y+m_dim.y-m_lineHeight-(m_bMulti?MARGY:MARGY1);
947 for ( i=m_lineFirst ; i<m_lineTotal ; i++ )
948 {
949 if ( i == m_lineFirst && i < m_lineTotal-1 &&
950 m_lineOffset[i] == m_lineOffset[i+1] )
951 {
952 pos.y -= m_lineHeight; // Double jump line \b;
953 i ++;
954 }
955
956 if ( i >= m_lineFirst+m_lineVisible ) break;
957
958 pos.x = m_pos.x+(7.5f/640.0f)*(m_fontSize/Gfx::FONT_SIZE_SMALL);
959 if ( m_bAutoIndent )
960 {
961 const char *s = "\t"; // line | dotted
962 for ( j=0 ; j<m_lineIndent[i] ; j++ )
963 {
964 m_engine->GetText()->DrawText(s, m_fontType, m_fontSize, pos, 1.0f, Gfx::TEXT_ALIGN_LEFT, 0);
965 pos.x += indentLength;
966 }
967 }
968
969 beg = m_lineOffset[i];
970 len = m_lineOffset[i+1] - m_lineOffset[i];
971
972 ppos = pos;
973 size = m_fontSize;
974
975 // Headline \b;?
976 if ( beg+len < m_len && m_format.size() > static_cast<unsigned int>(beg) &&
977 (m_format[beg]&Gfx::FONT_MASK_TITLE) == Gfx::FONT_TITLE_BIG )
978 {
979 start.x = ppos.x-MARGX;
980 end.x = dim.x-MARGX*2.0f;
981 start.y = ppos.y-(m_bMulti?0.0f:MARGY1)-m_lineHeight*(BIG_FONT-1.0f);
982 end.y = m_lineHeight*BIG_FONT;
983 DrawHorizontalGradient(start, end, Gfx::Color(0.549f, 0.514f, 0.376f, 1.0f), Gfx::Color(0.733f, 0.706f, 0.600f, 1.0f)); // blue gradient background
984
985 size *= BIG_FONT;
986 ppos.y -= m_lineHeight*(BIG_FONT-1.0f);
987 }
988
989 // As \t;?
990 if ( beg+len < m_len && m_format.size() > static_cast<unsigned int>(beg) &&
991 (m_format[beg]&Gfx::FONT_MASK_TITLE) == Gfx::FONT_TITLE_NORM )
992 {
993 start.x = ppos.x-MARGX;
994 end.x = dim.x-MARGX*2.0f;
995 start.y = ppos.y-(m_bMulti?0.0f:MARGY1);
996 end.y = m_lineHeight;
997 DrawHorizontalGradient(start, end, Gfx::Color(0.549f, 0.514f, 0.376f, 1.0f), Gfx::Color(0.733f, 0.706f, 0.600f, 1.0f)); // blue gradient background
998 }
999
1000 // Subtitle \s;?
1001 if ( beg+len < m_len && m_format.size() > static_cast<unsigned int>(beg) &&
1002 (m_format[beg]&Gfx::FONT_MASK_TITLE) == Gfx::FONT_TITLE_LITTLE )
1003 {
1004 start.x = ppos.x-MARGX;
1005 end.x = dim.x-MARGX*2.0f;
1006 start.y = ppos.y-(m_bMulti?0.0f:MARGY1);
1007 end.y = m_lineHeight;
1008 DrawHorizontalGradient(start, end, Gfx::Color(0.847f, 0.847f, 0.847f, 1.0f), Gfx::Color(0.996f, 0.953f, 0.792f, 1.0f)); // yellow background gradient
1009 }
1010
1011 // Table \tab;?
1012 if ( beg+len < m_len && m_format.size() > static_cast<unsigned int>(beg) &&
1013 (m_format[beg]&Gfx::FONT_MASK_HIGHLIGHT) == Gfx::FONT_HIGHLIGHT_TABLE )
1014 {
1015 start.x = ppos.x-MARGX;
1016 end.x = dim.x-MARGX*2.0f;
1017 start.y = ppos.y-(m_bMulti?0.0f:MARGY1);
1018 end.y = m_lineHeight;
1019 DrawHorizontalGradient(start, end, Gfx::Color(0.996f, 0.675f, 0.329f, 1.0f), Gfx::Color(1.000f, 0.898f, 0.788f, 1.0f)); // fond orange d�grad� ->
1020 }
1021
1022 // Image \image; ?
1023 if ( beg+len < m_len && m_format.size() > static_cast<unsigned int>(beg) &&
1024 (m_format[beg]&Gfx::FONT_MASK_IMAGE) != 0 )
1025 {
1026 line = 1;
1027 while ( true ) // includes the image slices
1028 {
1029 if ( i+line >= m_lineTotal ||
1030 i+line >= m_lineFirst+m_lineVisible ||
1031 (m_format.size() > static_cast<unsigned int>(beg+line) && m_format[beg+line]&Gfx::FONT_MASK_IMAGE) == 0 ) break;
1032 line ++;
1033 }
1034
1035 unsigned int iIndex = m_text[beg]; // character = index in m_image
1036 assert(iIndex < m_image.size());
1037 pos.y -= m_lineHeight*(line-1);
1038 DrawImage(pos, m_image[iIndex].name,
1039 m_image[iIndex].width*(m_fontSize/Gfx::FONT_SIZE_SMALL),
1040 m_image[iIndex].offset, m_image[iIndex].height*line, line);
1041 pos.y -= m_lineHeight;
1042 i += line-1;
1043 continue;
1044 }
1045
1046 if ( ((m_bEdit && m_bFocus && m_bHilite) ||
1047 (!m_bEdit && m_bHilite) ) &&
1048 c1 != c2 && beg <= c2 && beg+len >= c1 ) // selected area?
1049 {
1050 o1 = c1; if ( o1 < beg ) o1 = beg;
1051 o2 = c2; if ( o2 > beg+len ) o2 = beg+len;
1052
1053 if ( m_format.empty() )
1054 {
1055 start.x = ppos.x+m_engine->GetText()->GetStringWidth(std::string(m_text.data()+beg).substr(0, o1-beg), m_fontType, size);
1056 end.x = m_engine->GetText()->GetStringWidth(std::string(m_text.data()+o1).substr(0, o2-o1), m_fontType, size);
1057 }
1058 else
1059 {
1060 start.x = ppos.x+m_engine->GetText()->GetStringWidth(std::string(m_text.data()+beg).substr(0, o1-beg),
1061 m_format.begin() + beg,
1062 m_format.end(),
1063 size);
1064 end.x = m_engine->GetText()->GetStringWidth(std::string(m_text.data()+o1).substr(0, o2-o1),
1065 m_format.begin() + o1,
1066 m_format.end(),
1067 size);
1068 }
1069
1070 start.y = ppos.y-(m_bMulti?0.0f:MARGY1);
1071 end.y = m_lineHeight;
1072 if ( m_format.size() > static_cast<unsigned int>(beg) && (m_format[beg]&Gfx::FONT_MASK_TITLE) == Gfx::FONT_TITLE_BIG) end.y *= BIG_FONT;
1073 DrawColor(start, end, Gfx::Color(1.000f, 0.620f, 0.075f, 1.0f)); // plain yellow background
1074 }
1075
1076 eol = 16; // >
1077 if ( len > 0 && m_text[beg+len-1] == '\n' )
1078 {
1079 len --; // does not display the '\ n'
1080 eol = 0; // nothing
1081 }
1082 if ( beg+len >= m_len )
1083 {
1084 eol = 2; // square (eot)
1085 }
1086 if ( !m_bMulti || !m_bDisplaySpec ) eol = 0;
1087 if ( m_format.empty() )
1088 {
1089 m_engine->GetText()->DrawText(std::string(m_text.data()+beg).substr(0, len), m_fontType, size, ppos, m_dim.x, Gfx::TEXT_ALIGN_LEFT, eol);
1090 }
1091 else
1092 {
1093 m_engine->GetText()->DrawText(std::string(m_text.data()+beg).substr(0, len),
1094 m_format.begin() + beg,
1095 m_format.end(),
1096 size,
1097 ppos,
1098 m_dim.x,
1099 Gfx::TEXT_ALIGN_LEFT,
1100 eol);
1101 }
1102
1103 pos.y -= m_lineHeight;
1104
1105 if ( i < m_lineTotal-2 && m_lineOffset[i+1] == m_lineOffset[i+2] )
1106 {
1107 pos.y -= m_lineHeight; // double jump line \b;
1108 i ++;
1109 }
1110 }
1111
1112 // Shows the cursor.
1113 if ( (m_bEdit && m_bFocus && m_bHilite && Math::Mod(m_timeBlink, 1.0f) <= 0.5f) ) // it blinks
1114 {
1115 pos.y = m_pos.y+m_dim.y-m_lineHeight-(m_bMulti?MARGY:MARGY1*2.0f);
1116 for ( i=m_lineFirst ; i<m_lineTotal ; i++ )
1117 {
1118 if ( i == m_lineTotal-1 || m_cursor1 < m_lineOffset[i+1] )
1119 {
1120 pos.x = m_pos.x+(7.5f/640.0f)*(m_fontSize/Gfx::FONT_SIZE_SMALL);
1121 if ( m_bAutoIndent )
1122 {
1123 pos.x += indentLength*m_lineIndent[i];
1124 }
1125
1126 len = m_cursor1 - m_lineOffset[i];
1127
1128 if ( m_format.empty() )
1129 {
1130 m_engine->GetText()->SizeText(std::string(m_text.data()+m_lineOffset[i]).substr(0, len), m_fontType,
1131 size, pos, Gfx::TEXT_ALIGN_LEFT,
1132 start, end);
1133 }
1134 else
1135 {
1136 m_engine->GetText()->SizeText(std::string(m_text.data()+m_lineOffset[i]).substr(0, len),
1137 m_format.begin() + m_lineOffset[i],
1138 m_format.end(),
1139 size, pos, Gfx::TEXT_ALIGN_LEFT,
1140 start, end);
1141 }
1142
1143 pos.x = end.x;
1144 break;
1145 }
1146 pos.y -= m_lineHeight;
1147 }
1148 pos.x -= 1.0f / 640.0f;
1149 dim.x = 2.0f / 640.0f;
1150 dim.y = m_lineHeight;
1151 DrawColor(pos, dim, Gfx::Color(1.0f, 0.0f, 0.0f, 1.0f)); // red
1152 }
1153
1154 if (m_scroll != nullptr && !m_bGeneric)
1155 {
1156 m_scroll->Draw();
1157 }
1158 }
1159
1160 // Draw an image part.
1161
PrepareImageFilename(std::string name)1162 static std::string PrepareImageFilename(std::string name)
1163 {
1164 std::string filename;
1165 filename = name + ".png";
1166 filename = InjectLevelPathsForCurrentLevel(filename, "icons");
1167 boost::replace_all(filename, "\\", "/"); // TODO: Fix this in files
1168 return filename;
1169 }
1170
DrawImage(Math::Point pos,std::string name,float width,float offset,float height,int nbLine)1171 void CEdit::DrawImage(Math::Point pos, std::string name, float width,
1172 float offset, float height, int nbLine)
1173 {
1174 Math::Point uv1, uv2, dim;
1175 float dp;
1176
1177 m_engine->SetState(Gfx::ENG_RSTATE_NORMAL);
1178
1179 Gfx::TextureCreateParams params;
1180 params.format = Gfx::TEX_IMG_AUTO;
1181 params.filter = Gfx::TEX_FILTER_BILINEAR;
1182 params.padToNearestPowerOfTwo = true;
1183 Gfx::Texture tex = m_engine->LoadTexture(PrepareImageFilename(name), params);
1184
1185 m_engine->SetTexture(tex);
1186
1187 uv1.x = 0.0f;
1188 uv2.x = 1.0f;
1189 uv1.y = offset;
1190 uv2.y = offset+height;
1191
1192 uv1.x *= static_cast<float>(tex.originalSize.x) / static_cast<float>(tex.size.x);
1193 uv2.x *= static_cast<float>(tex.originalSize.x) / static_cast<float>(tex.size.x);
1194 uv1.y *= static_cast<float>(tex.originalSize.y) / static_cast<float>(tex.size.y);
1195 uv2.y *= static_cast<float>(tex.originalSize.y) / static_cast<float>(tex.size.y);
1196
1197 dp = 0.5f/256.0f;
1198 uv1.x += dp;
1199 uv1.y += dp;
1200 uv2.x -= dp;
1201 uv2.y -= dp;
1202
1203 dim.x = width;
1204 dim.y = m_lineHeight*nbLine;
1205 DrawIcon(pos, dim, uv1, uv2);
1206 }
1207
1208 // Draw the background.
1209
DrawBack(Math::Point pos,Math::Point dim)1210 void CEdit::DrawBack(Math::Point pos, Math::Point dim)
1211 {
1212 Math::Point uv1,uv2, corner;
1213 float dp;
1214
1215 if ( m_bGeneric ) return;
1216
1217 m_engine->SetTexture("textures/interface/button2.png");
1218 m_engine->SetState(Gfx::ENG_RSTATE_NORMAL);
1219
1220 if ( m_bMulti )
1221 {
1222 uv1.x = 128.0f/256.0f; // light blue
1223 uv1.y = 64.0f/256.0f;
1224 uv2.x = 160.0f/256.0f;
1225 uv2.y = 96.0f/256.0f;
1226 }
1227 else
1228 {
1229 uv1.x = 160.0f/256.0f; // medium blue
1230 uv1.y = 192.0f/256.0f;
1231 uv2.x = 192.0f/256.0f;
1232 uv2.y = 224.0f/256.0f;
1233 }
1234 if ( m_icon == 1 )
1235 {
1236 uv1.x = 192.0f/256.0f; // orange
1237 uv1.y = 96.0f/256.0f;
1238 uv2.x = 224.0f/256.0f;
1239 uv2.y = 128.0f/256.0f;
1240 }
1241
1242 dp = 0.5f/256.0f;
1243 uv1.x += dp;
1244 uv1.y += dp;
1245 uv2.x -= dp;
1246 uv2.y -= dp;
1247
1248 if ( m_bMulti )
1249 {
1250 corner.x = 10.0f/640.0f;
1251 corner.y = 10.0f/480.0f;
1252 DrawIcon(pos, dim, uv1, uv2, corner, 8.0f/256.0f);
1253 }
1254 else
1255 {
1256 DrawIcon(pos, dim, uv1, uv2, 8.0f/256.0f);
1257 }
1258 }
1259
DrawHorizontalGradient(Math::Point pos,Math::Point dim,Gfx::Color color1,Gfx::Color color2)1260 void CEdit::DrawHorizontalGradient(Math::Point pos, Math::Point dim, Gfx::Color color1, Gfx::Color color2)
1261 {
1262 m_engine->SetState(Gfx::ENG_RSTATE_OPAQUE_COLOR);
1263
1264 Math::Point p1, p2;
1265 p1.x = pos.x;
1266 p1.y = pos.y;
1267 p2.x = pos.x + dim.x;
1268 p2.y = pos.y + dim.y;
1269
1270 Gfx::VertexCol quad[] =
1271 {
1272 Gfx::VertexCol(Math::Vector(p1.x, p1.y, 0.0f), color1),
1273 Gfx::VertexCol(Math::Vector(p1.x, p2.y, 0.0f), color1),
1274 Gfx::VertexCol(Math::Vector(p2.x, p1.y, 0.0f), color2),
1275 Gfx::VertexCol(Math::Vector(p2.x, p2.y, 0.0f), color2)
1276 };
1277
1278 m_engine->GetDevice()->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLE_STRIP, quad, 4);
1279 m_engine->AddStatisticTriangle(2);
1280 }
1281
DrawColor(Math::Point pos,Math::Point dim,Gfx::Color color)1282 void CEdit::DrawColor(Math::Point pos, Math::Point dim, Gfx::Color color)
1283 {
1284 DrawHorizontalGradient(pos, dim, color, color);
1285 }
1286
1287
1288 // Give the text to edit.
1289
SetText(const std::string & text,bool bNew)1290 void CEdit::SetText(const std::string& text, bool bNew)
1291 {
1292 int i, j, font;
1293 bool bBOL;
1294
1295 if ( !bNew ) UndoMemorize(OPERUNDO_SPEC);
1296
1297 m_len = text.size();
1298
1299 if( m_len >= GetMaxChar() ) m_len = GetMaxChar();
1300
1301 m_text.resize( m_len + 1, '\0' );
1302 m_text[m_len] = '\0';
1303 m_format.resize( m_len + 1, m_fontType );
1304 m_format[m_len] = m_fontType;
1305
1306 font = m_fontType;
1307 j = 0;
1308 bBOL = true;
1309 for ( i=0 ; i<m_len ; i++ )
1310 {
1311 if ( m_bAutoIndent )
1312 {
1313 if ( text[i] == '\t' )
1314 {
1315 if ( !bBOL )
1316 {
1317 m_text[j] = ' ';
1318 m_format[j] = font;
1319 j ++;
1320 }
1321 continue; // removes tabs
1322 }
1323 bBOL = ( text[i] == '\n' );
1324 }
1325
1326 if ( text[i] == '\\' && text[i+2] == ';' )
1327 {
1328 if ( text[i+1] == 'n' ) // normal ?
1329 {
1330 font &= ~Gfx::FONT_MASK_FONT;
1331 font |= Gfx::FONT_COMMON;
1332 i += 2;
1333 }
1334 else if ( text[i+1] == 'c' ) // cbot ?
1335 {
1336 font &= ~Gfx::FONT_MASK_FONT;
1337 font |= Gfx::FONT_STUDIO;
1338 i += 2;
1339 }
1340 else if ( text[i+1] == 'b' ) // big title ?
1341 {
1342 font &= ~Gfx::FONT_MASK_TITLE;
1343 font |= Gfx::FONT_TITLE_BIG;
1344 i += 2;
1345 }
1346 else if ( text[i+1] == 't' ) // title ?
1347 {
1348 font &= ~Gfx::FONT_MASK_TITLE;
1349 font |= Gfx::FONT_TITLE_NORM;
1350 i += 2;
1351 }
1352 else if ( text[i+1] == 's' ) // subtitle ?
1353 {
1354 font &= ~Gfx::FONT_MASK_TITLE;
1355 font |= Gfx::FONT_TITLE_LITTLE;
1356 i += 2;
1357 }
1358 }
1359 else
1360 {
1361 m_text[j] = text[i];
1362 m_format[j] = font;
1363 j ++;
1364 font &= ~Gfx::FONT_MASK_TITLE; // reset title
1365 }
1366 }
1367 m_len = j;
1368
1369 if ( bNew ) UndoFlush();
1370
1371 m_cursor1 = 0;
1372 m_cursor2 = 0; // cursor to the beginning
1373 Justif();
1374 ColumnFix();
1375 }
1376
1377 // Returns a const reference to the edited text.
1378
GetText()1379 const std::string& CEdit::GetText()
1380 {
1381 return m_text;
1382 }
1383
1384 // Returns the edited text.
1385
GetText(int max)1386 std::string CEdit::GetText(int max)
1387 {
1388 if ( m_len < max ) max = m_len;
1389 if ( m_len > max ) max = max-1;
1390
1391 return std::string( m_text, 0, max );
1392 }
1393
1394 // Returns the length of the text.
1395
GetTextLength()1396 int CEdit::GetTextLength()
1397 {
1398 return m_len;
1399 }
1400
1401
1402
1403 // Returns a name in a command.
1404 // \x nom1 nom2 nom3;
1405
GetNameParam(std::string cmd,int rank)1406 static std::string GetNameParam(std::string cmd, int rank)
1407 {
1408 std::vector<std::string> results;
1409 boost::split(results, cmd, boost::is_any_of(" ;"));
1410
1411 if (results.size() > static_cast<unsigned int>(rank))
1412 {
1413 return results.at(rank);
1414 }
1415
1416 return "";
1417 }
1418
1419 // Returns a number of a command.
1420 // \x nom n1 n2;
1421
GetValueParam(std::string cmd,int rank)1422 static int GetValueParam(std::string cmd, int rank)
1423 {
1424 std::vector<std::string> results;
1425 boost::split(results, cmd, boost::is_any_of(" ;"));
1426 int return_value = 0;
1427
1428 if (results.size() > static_cast<unsigned int>(rank))
1429 {
1430 return_value = atoi(results.at(rank).c_str());
1431 }
1432
1433 return return_value;
1434 }
1435
1436 // Frees all images.
1437
FreeImage()1438 void CEdit::FreeImage()
1439 {
1440 for (auto& image : m_image)
1441 {
1442 m_engine->DeleteTexture(PrepareImageFilename(image.name));
1443 }
1444 }
1445
1446 // Read from a text file.
1447
ReadText(std::string filename)1448 bool CEdit::ReadText(std::string filename)
1449 {
1450 int len, len2, i, j, n, font, iLines, iCount;
1451 char iName[50];
1452 float iWidth;
1453 InputSlot slot;
1454 bool bInSoluce, bBOL;
1455
1456 if ( filename.empty() ) return false;
1457
1458 CInputStream stream;
1459 stream.open(filename);
1460
1461 if (!stream.is_open())
1462 {
1463 GetLogger()->Error("Failed to load text file %s\n", filename.c_str());
1464 return false;
1465 }
1466
1467 len = stream.size();
1468 len2 = len + 1;
1469
1470 m_len = len;
1471 m_cursor1 = 0;
1472 m_cursor2 = 0;
1473
1474 FreeImage();
1475
1476 m_text = std::string(len2+1, '\0');
1477
1478 std::vector<char> buffer(len2+1, '\0');
1479
1480 stream.read(buffer.data(), len);
1481
1482 m_format.clear();
1483 m_format.resize(len2+1, m_fontType);
1484
1485 stream.close();
1486
1487 bInSoluce = false;
1488 font = m_fontType;
1489 m_image.clear();
1490 m_marker.clear();
1491 m_link.clear();
1492 i = j = 0;
1493 bBOL = true;
1494 int cbotStart = 0;
1495 bool inCbotBackground = false;
1496 bool inCbot = false;
1497 while ( i < m_len )
1498 {
1499 if ( m_bAutoIndent )
1500 {
1501 if ( buffer[i] == '\t' )
1502 {
1503 if ( !bBOL )
1504 {
1505 m_text[j] = buffer[i];
1506 //if ( m_format.size() > 0 )
1507 m_format[j] = font;
1508 j ++;
1509 }
1510 i ++;
1511 continue; // removes the tabs
1512 }
1513 bBOL = ( buffer[i] == '\n' || buffer[i] == '\r' );
1514 }
1515
1516 if ( buffer[i] == '\r' ) // removes \ r
1517 {
1518 i ++;
1519 }
1520 else if ( buffer[i] == '\\' && buffer[i+2] == ';' )
1521 {
1522 if ( buffer[i+1] == 'n' ) // normal ?
1523 {
1524 if ( m_bSoluce || !bInSoluce )
1525 {
1526 font &= ~Gfx::FONT_MASK_FONT;
1527 font |= Gfx::FONT_COMMON;
1528 inCbot = false;
1529 }
1530 i += 3;
1531 }
1532 else if ( buffer[i+1] == 'c' ) // cbot ?
1533 {
1534 if ( m_bSoluce || !bInSoluce )
1535 {
1536 font &= ~Gfx::FONT_MASK_FONT;
1537 font |= Gfx::FONT_STUDIO;
1538 if (!inCbot)
1539 {
1540 if (inCbotBackground)
1541 {
1542 cbotStart = j;
1543 }
1544 inCbot = true;
1545 }
1546 }
1547 i += 3;
1548 }
1549 else if ( buffer[i+1] == 'b' ) // big title ?
1550 {
1551 if ( m_bSoluce || !bInSoluce )
1552 {
1553 font &= ~Gfx::FONT_MASK_TITLE;
1554 font |= Gfx::FONT_TITLE_BIG;
1555 }
1556 i += 3;
1557 }
1558 else if ( buffer[i+1] == 't' ) // title ?
1559 {
1560 if ( m_bSoluce || !bInSoluce )
1561 {
1562 font &= ~Gfx::FONT_MASK_TITLE;
1563 font |= Gfx::FONT_TITLE_NORM;
1564 }
1565 i += 3;
1566 }
1567 else if ( buffer[i+1] == 's' ) // subtitle ?
1568 {
1569 if ( m_bSoluce || !bInSoluce )
1570 {
1571 font &= ~Gfx::FONT_MASK_TITLE;
1572 font |= Gfx::FONT_TITLE_LITTLE;
1573 if (!inCbotBackground)
1574 {
1575 if (inCbot)
1576 {
1577 cbotStart = j;
1578 }
1579 inCbotBackground = true;
1580 }
1581 }
1582 i += 3;
1583 }
1584 else if ( buffer[i+1] == 'l' ) // link ?
1585 {
1586 if ( m_bSoluce || !bInSoluce )
1587 {
1588 font |= Gfx::FONT_MASK_LINK;
1589 }
1590 i += 3;
1591 }
1592 else
1593 {
1594 i += 3;
1595 }
1596 }
1597 else if ( //m_format.size() > 0 &&
1598 buffer[i+0] == '\\' && // \u marker name; ?
1599 buffer[i+1] == 'u' &&
1600 buffer[i+2] == ' ' )
1601 {
1602 if ( m_bSoluce || !bInSoluce )
1603 {
1604 HyperLink link;
1605 link.name = GetNameParam(buffer.data()+i+3, 0);
1606 link.marker = GetNameParam(buffer.data()+i+3, 1);
1607 m_link.push_back(link);
1608 font &= ~Gfx::FONT_MASK_LINK;
1609 }
1610 i += strchr(buffer.data()+i, ';')-(buffer.data()+i)+1;
1611 }
1612 else if (// m_format.size() > 0 &&
1613 buffer[i+0] == '\\' && // \m marker; ?
1614 buffer[i+1] == 'm' &&
1615 buffer[i+2] == ' ' )
1616 {
1617 if ( m_bSoluce || !bInSoluce )
1618 {
1619 HyperMarker marker;
1620 marker.name = GetNameParam(buffer.data()+i+3, 0);
1621 marker.pos = j;
1622 m_marker.push_back(marker);
1623 }
1624 i += strchr(buffer.data()+i, ';')-(buffer.data()+i)+1;
1625 }
1626 else if ( //m_format.size() > 0 &&
1627 buffer[i+0] == '\\' && // \image name lx ly; ?
1628 buffer[i+1] == 'i' &&
1629 buffer[i+2] == 'm' &&
1630 buffer[i+3] == 'a' &&
1631 buffer[i+4] == 'g' &&
1632 buffer[i+5] == 'e' &&
1633 buffer[i+6] == ' ' )
1634 {
1635 if ( m_bSoluce || !bInSoluce )
1636 {
1637 strcpy(iName, GetNameParam(buffer.data()+i+7, 0).c_str());
1638
1639 //? iWidth = m_lineHeight*RetValueParam(buffer.data()+i+7, 1);
1640 iWidth = static_cast<float>(GetValueParam(buffer.data()+i+7, 1));
1641 iWidth *= m_engine->GetText()->GetHeight(Gfx::FONT_COMMON, Gfx::FONT_SIZE_SMALL);
1642 iLines = GetValueParam(buffer.data()+i+7, 2);
1643
1644 // A part of image per line of text.
1645 for ( iCount=0 ; iCount<iLines ; iCount++ )
1646 {
1647 ImageLine image;
1648 image.name = iName;
1649 image.offset = static_cast<float>(iCount) / static_cast<float>(iLines);
1650 image.height = 1.0f/iLines;
1651 image.width = iWidth*0.75f;
1652
1653 m_image.push_back(image);
1654 m_text[j] = static_cast<char>(m_image.size()-1); // as an index into m_image
1655 m_format[j] = Gfx::FONT_MASK_IMAGE;
1656 j ++;
1657 }
1658 }
1659 i += strchr(buffer.data()+i, ';')-(buffer.data()+i)+1;
1660 }
1661 else if ( //m_format.size() > 0 &&
1662 buffer[i+0] == '\\' && // \button; ?
1663 buffer[i+1] == 'b' &&
1664 buffer[i+2] == 'u' &&
1665 buffer[i+3] == 't' &&
1666 buffer[i+4] == 't' &&
1667 buffer[i+5] == 'o' &&
1668 buffer[i+6] == 'n' &&
1669 buffer[i+7] == ' ' )
1670 {
1671 if ( m_bSoluce || !bInSoluce )
1672 {
1673 m_text[j] = GetValueParam(buffer.data()+i+8, 0);
1674 m_format[j] = font|Gfx::FONT_BUTTON;
1675 j ++;
1676 }
1677 i += strchr(buffer.data()+i, ';')-(buffer.data()+i)+1;
1678 }
1679 else if ( //m_format.size() > 0 &&
1680 buffer[i+0] == '\\' && // \token; ?
1681 buffer[i+1] == 't' &&
1682 buffer[i+2] == 'o' &&
1683 buffer[i+3] == 'k' &&
1684 buffer[i+4] == 'e' &&
1685 buffer[i+5] == 'n' &&
1686 buffer[i+6] == ';' )
1687 {
1688 if ( m_bSoluce || !bInSoluce )
1689 {
1690 font &= ~Gfx::FONT_MASK_HIGHLIGHT;
1691 font |= Gfx::FONT_HIGHLIGHT_TOKEN;
1692 }
1693 i += 7;
1694 }
1695 else if ( //m_format.size() > 0 &&
1696 buffer[i+0] == '\\' && // \type; ?
1697 buffer[i+1] == 't' &&
1698 buffer[i+2] == 'y' &&
1699 buffer[i+3] == 'p' &&
1700 buffer[i+4] == 'e' &&
1701 buffer[i+5] == ';' )
1702 {
1703 if ( m_bSoluce || !bInSoluce )
1704 {
1705 font &= ~Gfx::FONT_MASK_HIGHLIGHT;
1706 font |= Gfx::FONT_HIGHLIGHT_TYPE;
1707 }
1708 i += 6;
1709 }
1710 else if ( //m_format.size() > 0 &&
1711 buffer[i+0] == '\\' && // \const; ?
1712 buffer[i+1] == 'c' &&
1713 buffer[i+2] == 'o' &&
1714 buffer[i+3] == 'n' &&
1715 buffer[i+4] == 's' &&
1716 buffer[i+5] == 't' &&
1717 buffer[i+6] == ';' )
1718 {
1719 if ( m_bSoluce || !bInSoluce )
1720 {
1721 font &= ~Gfx::FONT_MASK_HIGHLIGHT;
1722 font |= Gfx::FONT_HIGHLIGHT_CONST;
1723 }
1724 i += 7;
1725 }
1726 else if ( //m_format.size() > 0 &&
1727 buffer[i+0] == '\\' && // \key; ?
1728 buffer[i+1] == 'k' &&
1729 buffer[i+2] == 'e' &&
1730 buffer[i+3] == 'y' &&
1731 buffer[i+4] == ';' )
1732 {
1733 if ( m_bSoluce || !bInSoluce )
1734 {
1735 font &= ~Gfx::FONT_MASK_HIGHLIGHT;
1736 font |= Gfx::FONT_HIGHLIGHT_KEY;
1737 }
1738 i += 5;
1739 }
1740 else if ( //m_format.size() > 0 &&
1741 buffer[i+0] == '\\' && // \tab; ?
1742 buffer[i+1] == 't' &&
1743 buffer[i+2] == 'a' &&
1744 buffer[i+3] == 'b' &&
1745 buffer[i+4] == ';' )
1746 {
1747 if ( m_bSoluce || !bInSoluce )
1748 {
1749 font |= Gfx::FONT_HIGHLIGHT_TABLE;
1750 }
1751 i += 5;
1752 }
1753 else if (// m_format.size() > 0 &&
1754 buffer[i+0] == '\\' && // \norm; ?
1755 buffer[i+1] == 'n' &&
1756 buffer[i+2] == 'o' &&
1757 buffer[i+3] == 'r' &&
1758 buffer[i+4] == 'm' &&
1759 buffer[i+5] == ';' )
1760 {
1761 if ( m_bSoluce || !bInSoluce )
1762 {
1763 font &= ~Gfx::FONT_MASK_HIGHLIGHT;
1764 }
1765 i += 6;
1766 }
1767 else if ( //m_format.size() > 0 &&
1768 buffer[i+0] == '\\' && // \begin soluce; ?
1769 buffer[i+1] == 'b' &&
1770 buffer[i+2] == 's' &&
1771 buffer[i+3] == ';' )
1772 {
1773 bInSoluce = true;
1774 i += 4;
1775 }
1776 else if ( //m_format.size() > 0 &&
1777 buffer[i+0] == '\\' && // \end soluce; ?
1778 buffer[i+1] == 'e' &&
1779 buffer[i+2] == 's' &&
1780 buffer[i+3] == ';' )
1781 {
1782 bInSoluce = false;
1783 i += 4;
1784 }
1785 else if ( //m_format.size() > 0 &&
1786 buffer[i+0] == '\\' && // \key name; ?
1787 buffer[i+1] == 'k' &&
1788 buffer[i+2] == 'e' &&
1789 buffer[i+3] == 'y' &&
1790 buffer[i+4] == ' ' )
1791 {
1792 int count;
1793 for (count = 0; buffer[i+5+count] != ';'; count++);
1794 if ( m_bSoluce || !bInSoluce ) //TODO: ???
1795 {
1796 CInput* input = CInput::GetInstancePointer();
1797 slot = input->SearchKeyById(std::string(&buffer[i+5], count));
1798 if ( slot != INPUT_SLOT_MAX )
1799 {
1800 std::string iNameStr = input->GetKeysString(slot);
1801 strcpy(iName, iNameStr.c_str());
1802 m_text[j] = ' ';
1803 m_format[j] = font;
1804 j ++;
1805 n = 0;
1806 while ( iName[n] != 0 )
1807 {
1808 m_text[j] = iName[n++];
1809 m_format[j] = font;
1810 j ++;
1811 }
1812 m_text[j] = ' ';
1813 m_format[j] = font;
1814 j ++;
1815 }
1816 else
1817 {
1818 m_text[j] = '?';
1819 m_format[j] = font;
1820 j ++;
1821 }
1822 }
1823 i = i+5+count+1;
1824 }
1825 else
1826 {
1827 if (buffer[i] == '\n' && inCbotBackground)
1828 {
1829 CScript::ColorizeScript(this, cbotStart, j);
1830 inCbotBackground = false;
1831 }
1832
1833 if ( m_bSoluce || !bInSoluce )
1834 {
1835 m_text[j] = buffer[i];
1836 //if ( m_format.size() > 0 )
1837 m_format[j] = font;
1838 j ++;
1839 }
1840 i ++;
1841
1842 font &= ~Gfx::FONT_MASK_TITLE; // reset title
1843
1844 if ( (font&Gfx::FONT_MASK_HIGHLIGHT) == Gfx::FONT_HIGHLIGHT_TABLE )
1845 {
1846 font &= ~Gfx::FONT_HIGHLIGHT_TABLE;
1847 }
1848 }
1849 }
1850 m_len = j;
1851
1852 Justif();
1853 ColumnFix();
1854 return true;
1855 }
1856
1857 // Writes all the text in a file.
1858
WriteText(std::string filename)1859 bool CEdit::WriteText(std::string filename)
1860 {
1861 if (filename.empty()) return false;
1862
1863 COutputStream stream;
1864 stream.open(filename);
1865
1866 if (!stream.is_open())
1867 {
1868 GetLogger()->Error("Failed to open output file: '%s'", filename.c_str());
1869 return false;
1870 }
1871
1872 GetIndentedText(stream, 0, m_len);
1873
1874 stream.close();
1875
1876 return true;
1877 }
1878
GetIndentedText(std::ostream & stream,unsigned int start,unsigned int end)1879 void CEdit::GetIndentedText(std::ostream& stream, unsigned int start, unsigned int end)
1880 {
1881 float iDim = 0.0f;
1882
1883 if ( m_bAutoIndent )
1884 {
1885 iDim = m_dim.x;
1886 m_dim.x = 1000.0f; // puts an infinite width!
1887 Justif();
1888 }
1889
1890 unsigned int i = 0, line = 0;
1891 while ( m_text[i] != 0 && i < end && i < static_cast<unsigned int>(m_len) ) // TODO: fix this (un)signed comparation
1892 {
1893 if ( m_bAutoIndent && i == static_cast<unsigned int>(m_lineOffset[line]) ) // TODO: fix this (un)signed comparation
1894 {
1895 for (int n = 0; n < m_lineIndent[line]; n++)
1896 {
1897 if (i > start)
1898 {
1899 stream << '\t';
1900 }
1901 }
1902 line++;
1903 }
1904
1905 if (i >= start)
1906 {
1907 stream << m_text[i];
1908 }
1909
1910 i ++;
1911 }
1912
1913 if ( m_bAutoIndent )
1914 {
1915 m_dim.x = iDim; // presents the initial width
1916 Justif();
1917 }
1918 }
1919
1920
1921 // Manage the number of max characters editable.
1922
SetMaxChar(int max)1923 void CEdit::SetMaxChar(int max)
1924 {
1925 FreeImage();
1926
1927 m_maxChar = max;
1928
1929 m_text.resize( m_maxChar + 1, '\0' );
1930
1931 m_format.clear();
1932 m_format.resize(m_maxChar + 1, m_fontType);
1933
1934 m_len = 0;
1935 m_cursor1 = 0;
1936 m_cursor2 = 0;
1937 Justif();
1938 UndoFlush();
1939 }
1940
GetMaxChar()1941 int CEdit::GetMaxChar()
1942 {
1943 return m_maxChar;
1944 }
1945
1946
1947 // Mode management "editable".
1948
SetEditCap(bool bMode)1949 void CEdit::SetEditCap(bool bMode)
1950 {
1951 m_bEdit = bMode;
1952 }
1953
GetEditCap()1954 bool CEdit::GetEditCap()
1955 {
1956 return m_bEdit;
1957 }
1958
1959 // Mode management "hilitable" (that's the franch).
1960
SetHighlightCap(bool bEnable)1961 void CEdit::SetHighlightCap(bool bEnable)
1962 {
1963 m_bHilite = bEnable;
1964 }
1965
GetHighlightCap()1966 bool CEdit::GetHighlightCap()
1967 {
1968 return m_bHilite;
1969 }
1970
1971 // Lift in / out connection.
1972
SetInsideScroll(bool bInside)1973 void CEdit::SetInsideScroll(bool bInside)
1974 {
1975 m_bInsideScroll = bInside;
1976 }
1977
GetInsideScroll()1978 bool CEdit::GetInsideScroll()
1979 {
1980 return m_bInsideScroll;
1981 }
1982
1983 // Specifies whether to display the links showing the solution.
1984
SetSoluceMode(bool bSoluce)1985 void CEdit::SetSoluceMode(bool bSoluce)
1986 {
1987 m_bSoluce = bSoluce;
1988 }
1989
GetSoluceMode()1990 bool CEdit::GetSoluceMode()
1991 {
1992 return m_bSoluce;
1993 }
1994
1995 // Indicates whether the text is a defile that generic.
1996
SetGenericMode(bool bGeneric)1997 void CEdit::SetGenericMode(bool bGeneric)
1998 {
1999 m_bGeneric = bGeneric;
2000 }
2001
GetGenericMode()2002 bool CEdit::GetGenericMode()
2003 {
2004 return m_bGeneric;
2005 }
2006
2007
2008 // Management of automatic indentation mode with {}.
2009
SetAutoIndent(bool bMode)2010 void CEdit::SetAutoIndent(bool bMode)
2011 {
2012 m_bAutoIndent = bMode;
2013 }
2014
GetAutoIndent()2015 bool CEdit::GetAutoIndent()
2016 {
2017 return m_bAutoIndent;
2018 }
2019
2020
2021
2022 // Moves the cursors.
2023
SetCursor(int cursor1,int cursor2)2024 void CEdit::SetCursor(int cursor1, int cursor2)
2025 {
2026 if ( cursor1 > m_len ) cursor1 = m_len;
2027 if ( cursor2 > m_len ) cursor2 = m_len;
2028
2029 m_cursor1 = cursor1;
2030 m_cursor2 = cursor2;
2031 m_bUndoForce = true;
2032 ColumnFix();
2033 }
2034
2035 // Returns the sliders.
2036
GetCursor(int & cursor1,int & cursor2)2037 void CEdit::GetCursor(int &cursor1, int &cursor2)
2038 {
2039 cursor1 = m_cursor1;
2040 cursor2 = m_cursor2;
2041 }
2042
2043
2044 // Displayed line modifies the first.
2045
SetFirstLine(int rank)2046 void CEdit::SetFirstLine(int rank)
2047 {
2048 Scroll(rank, true);
2049 }
2050
2051 // Returns the first displayed line.
2052
GetFirstLine()2053 int CEdit::GetFirstLine()
2054 {
2055 if ( m_historyTotal > 0 )
2056 {
2057 if ( m_historyCurrent == 0 )
2058 {
2059 return m_lineFirst;
2060 }
2061 else
2062 {
2063 return m_history[0].firstLine;
2064 }
2065 }
2066 return m_lineFirst;
2067 }
2068
2069
2070 // Shows the selected area.
2071
ShowSelect()2072 void CEdit::ShowSelect()
2073 {
2074 int cursor1, cursor2, line;
2075
2076 if ( m_cursor1 < m_cursor2 )
2077 {
2078 cursor1 = m_cursor1;
2079 cursor2 = m_cursor2;
2080 }
2081 else
2082 {
2083 cursor1 = m_cursor2;
2084 cursor2 = m_cursor1;
2085 }
2086
2087 line = GetCursorLine(cursor2);
2088 if ( line >= m_lineFirst+m_lineVisible )
2089 {
2090 line -= m_lineVisible-1;
2091 if ( line < 0 ) line = 0;
2092 Scroll(line, false);
2093 }
2094
2095 line = GetCursorLine(cursor1);
2096 if ( line < m_lineFirst )
2097 {
2098 Scroll(line, false);
2099 }
2100 }
2101
2102
2103 // Management of the display mode of special characters.
2104
SetDisplaySpec(bool bDisplay)2105 void CEdit::SetDisplaySpec(bool bDisplay)
2106 {
2107 m_bDisplaySpec = bDisplay;
2108 }
2109
GetDisplaySpec()2110 bool CEdit::GetDisplaySpec()
2111 {
2112 return m_bDisplaySpec;
2113 }
2114
2115
2116 // Multi-fonts mode management.
2117
SetMultiFont(bool bMulti)2118 void CEdit::SetMultiFont(bool bMulti)
2119 {
2120 m_format.clear();
2121
2122 if (bMulti)
2123 {
2124 m_format.resize( m_text.size() + 1, m_fontType );
2125 }
2126 }
2127
2128 // TODO check if it works correctly; was checking if variable is null
GetMultiFont()2129 bool CEdit::GetMultiFont()
2130 {
2131 return ( m_format.size() > 0 );
2132 }
2133
2134
2135 // Management of the character size.
2136
SetFontSize(float size)2137 void CEdit::SetFontSize(float size)
2138 {
2139 CControl::SetFontSize(size);
2140
2141 MoveAdjust();
2142 }
2143
2144
2145 // Moves according to the visible lift.
2146
Scroll()2147 void CEdit::Scroll()
2148 {
2149 if (m_scroll != nullptr)
2150 {
2151 float value = m_scroll->GetVisibleValue();
2152 value *= m_lineTotal - m_lineVisible;
2153 Scroll(static_cast<int>(value + 0.5f), true);
2154 }
2155 }
2156
2157 // Moves according to the visible lift.
2158
Scroll(int pos,bool bAdjustCursor)2159 void CEdit::Scroll(int pos, bool bAdjustCursor)
2160 {
2161 int max, line;
2162
2163 m_lineFirst = pos;
2164
2165 if ( m_lineFirst < 0 ) m_lineFirst = 0;
2166
2167 max = m_lineTotal-m_lineVisible;
2168 if ( max < 0 ) max = 0;
2169 if ( m_lineFirst > max ) m_lineFirst = max;
2170
2171 line = GetCursorLine(m_cursor1);
2172
2173 if ( bAdjustCursor && m_bEdit )
2174 {
2175 // Cursor too high?
2176 if ( line < m_lineFirst )
2177 {
2178 MoveLine(m_lineFirst-line, false, false);
2179 return;
2180 }
2181
2182 // Cursor too low?
2183 if ( line >= m_lineFirst+m_lineVisible )
2184 {
2185 MoveLine(m_lineFirst+m_lineVisible-line-1, false, false);
2186 return;
2187 }
2188 }
2189
2190 Justif();
2191 }
2192
2193 // Moves the cursor to the beginning of the line.
2194
MoveHome(bool bWord,bool bSelect)2195 void CEdit::MoveHome(bool bWord, bool bSelect)
2196 {
2197 int begin, tab;
2198
2199 if ( bWord )
2200 {
2201 m_cursor1 = 0;
2202 }
2203 else
2204 {
2205 begin = m_cursor1;
2206 while ( begin > 0 && m_text[begin-1] != '\n' )
2207 {
2208 begin --;
2209 }
2210
2211 tab = begin;
2212 while ( tab < m_len && (m_text[tab] == '\t' || m_text[tab] == ' ') )
2213 {
2214 tab ++;
2215 }
2216
2217 if ( m_cursor1 == tab )
2218 {
2219 m_cursor1 = begin;
2220 }
2221 else
2222 {
2223 m_cursor1 = tab;
2224 }
2225 }
2226 if ( !bSelect ) m_cursor2 = m_cursor1;
2227
2228 m_bUndoForce = true;
2229 Justif();
2230 ColumnFix();
2231 }
2232
2233 // Moves the cursor to the end of the line.
2234
MoveEnd(bool bWord,bool bSelect)2235 void CEdit::MoveEnd(bool bWord, bool bSelect)
2236 {
2237 if ( bWord )
2238 {
2239 m_cursor1 = m_len;
2240 }
2241 else
2242 {
2243 while ( m_cursor1 < m_len && m_text[m_cursor1] != '\n' )
2244 {
2245 m_cursor1 ++;
2246 }
2247 }
2248 if ( !bSelect ) m_cursor2 = m_cursor1;
2249
2250 m_bUndoForce = true;
2251 Justif();
2252 ColumnFix();
2253 }
2254
2255 // Moves the cursor through characters.
2256
MoveChar(int move,bool bWord,bool bSelect)2257 void CEdit::MoveChar(int move, bool bWord, bool bSelect)
2258 {
2259 int character;
2260
2261 if ( move == -1 ) // back?
2262 {
2263 if ( bWord )
2264 {
2265 while ( m_cursor1 > 0 )
2266 {
2267 character = static_cast<unsigned char>(m_text[m_cursor1-1]);
2268 if ( !IsSpace(character) ) break;
2269 m_cursor1 --;
2270 }
2271
2272 if ( m_cursor1 > 0 )
2273 {
2274 character = static_cast<unsigned char>(m_text[m_cursor1-1]);
2275 if ( IsSpace(character) )
2276 {
2277 while ( m_cursor1 > 0 )
2278 {
2279 character = static_cast<unsigned char>(m_text[m_cursor1-1]);
2280 if ( !IsSpace(character) ) break;
2281 m_cursor1 --;
2282 }
2283 }
2284 else if ( IsWord(character) )
2285 {
2286 while ( m_cursor1 > 0 )
2287 {
2288 character = static_cast<unsigned char>(m_text[m_cursor1-1]);
2289 if ( !IsWord(character) ) break;
2290 m_cursor1 --;
2291 }
2292 }
2293 else if ( IsSep(character) )
2294 {
2295 while ( m_cursor1 > 0 )
2296 {
2297 character = static_cast<unsigned char>(m_text[m_cursor1-1]);
2298 if ( !IsSep(character) ) break;
2299 m_cursor1 --;
2300 }
2301 }
2302 }
2303 }
2304 else
2305 {
2306 m_cursor1 --;
2307 if ( m_cursor1 < 0 ) m_cursor1 = 0;
2308 }
2309 }
2310
2311 if ( move == 1 ) // advance?
2312 {
2313 if ( bWord )
2314 {
2315 if ( m_cursor1 < m_len )
2316 {
2317 character = static_cast<unsigned char>(m_text[m_cursor1]);
2318 if ( IsSpace(character) )
2319 {
2320 while ( m_cursor1 < m_len )
2321 {
2322 character = static_cast<unsigned char>(m_text[m_cursor1]);
2323 if ( !IsSpace(character) ) break;
2324 m_cursor1 ++;
2325 }
2326 }
2327 else if ( IsWord(character) )
2328 {
2329 while ( m_cursor1 < m_len )
2330 {
2331 character = static_cast<unsigned char>(m_text[m_cursor1]);
2332 if ( !IsWord(character) ) break;
2333 m_cursor1 ++;
2334 }
2335 }
2336 else if ( IsSep(character) )
2337 {
2338 while ( m_cursor1 < m_len )
2339 {
2340 character = static_cast<unsigned char>(m_text[m_cursor1]);
2341 if ( !IsSep(character) ) break;
2342 m_cursor1 ++;
2343 }
2344 }
2345 }
2346
2347 while ( m_cursor1 < m_len )
2348 {
2349 character = static_cast<unsigned char>(m_text[m_cursor1]);
2350 if ( !IsSpace(character) ) break;
2351 m_cursor1 ++;
2352 }
2353 }
2354 else
2355 {
2356 m_cursor1 ++;
2357 if ( m_cursor1 > m_len ) m_cursor1 = m_len;
2358 }
2359 }
2360
2361 if ( !bSelect ) m_cursor2 = m_cursor1;
2362
2363 m_bUndoForce = true;
2364 Justif();
2365 ColumnFix();
2366 }
2367
2368 // Moves the cursor lines.
2369
MoveLine(int move,bool bWord,bool bSelect)2370 void CEdit::MoveLine(int move, bool bWord, bool bSelect)
2371 {
2372 float column, indentLength = 0.0f;
2373 int i, line, c;
2374
2375 if ( move == 0 ) return;
2376
2377 for ( i=0 ; i>move ; i-- ) // back?
2378 {
2379 while ( m_cursor1 > 0 && m_text[m_cursor1-1] != '\n' )
2380 {
2381 m_cursor1 --;
2382 }
2383 if ( m_cursor1 != 0 )
2384 {
2385 m_cursor1 --;
2386 while ( m_cursor1 > 0 )
2387 {
2388 if ( m_text[--m_cursor1] == '\n' )
2389 {
2390 m_cursor1 ++;
2391 break;
2392 }
2393 }
2394 }
2395 }
2396
2397 for ( i=0 ; i<move ; i++ ) // advance?
2398 {
2399 while ( m_cursor1 < m_len )
2400 {
2401 if ( m_text[m_cursor1++] == '\n' )
2402 {
2403 break;
2404 }
2405 }
2406 }
2407
2408 line = GetCursorLine(m_cursor1);
2409
2410 column = m_column;
2411 if ( m_bAutoIndent )
2412 {
2413 indentLength = m_engine->GetText()->GetCharWidth(static_cast<Gfx::UTF8Char>(' '), m_fontType, m_fontSize, 0.0f)
2414 * m_engine->GetEditIndentValue();
2415 column -= indentLength*m_lineIndent[line];
2416 }
2417
2418 if ( m_format.empty() )
2419 {
2420 c = m_engine->GetText()->Detect(std::string(m_text.data()+m_lineOffset[line]),
2421 m_fontType, m_fontSize,
2422 m_lineOffset[line+1]-m_lineOffset[line]);
2423 }
2424 else
2425 {
2426 c = m_engine->GetText()->Detect(std::string(m_text.data()+m_lineOffset[line]),
2427 m_format.begin() + m_lineOffset[line],
2428 m_format.end(),
2429 m_fontSize,
2430 m_lineOffset[line+1]-m_lineOffset[line]);
2431 }
2432
2433 m_cursor1 = m_lineOffset[line]+c;
2434 if ( !bSelect ) m_cursor2 = m_cursor1;
2435
2436 m_bUndoForce = true;
2437 Justif();
2438 }
2439
2440 // Sets the horizontal position.
2441
ColumnFix()2442 void CEdit::ColumnFix()
2443 {
2444 float indentLength;
2445 int line;
2446
2447 line = GetCursorLine(m_cursor1);
2448
2449 if ( m_format.empty() )
2450 {
2451 m_column = m_engine->GetText()->GetStringWidth(
2452 std::string(m_text.data()+m_lineOffset[line]),
2453 m_fontType, m_fontSize);
2454 }
2455 else
2456 {
2457 m_column = m_engine->GetText()->GetStringWidth(
2458 std::string(m_text.data()+m_lineOffset[line]),
2459 m_format.begin() + m_lineOffset[line],
2460 m_format.end(),
2461 m_fontSize
2462 );
2463 }
2464
2465 if ( m_bAutoIndent )
2466 {
2467 indentLength = m_engine->GetText()->GetCharWidth(static_cast<Gfx::UTF8Char>(' '), m_fontType, m_fontSize, 0.0f)
2468 * m_engine->GetEditIndentValue();
2469 m_column += indentLength*m_lineIndent[line];
2470 }
2471 }
2472
2473
2474 // Cut the selected characters or entire line.
2475
Cut()2476 bool CEdit::Cut()
2477 {
2478 UndoMemorize(OPERUNDO_SPEC);
2479 Copy(true);
2480
2481 DeleteOne(0); // deletes the selected characters
2482 Justif();
2483 ColumnFix();
2484 SendModifEvent();
2485 return true;
2486 }
2487
2488 // Copy the selected characters or entire line.
2489
Copy(bool memorize_cursor)2490 bool CEdit::Copy(bool memorize_cursor)
2491 {
2492 int c1, c2;
2493
2494 c1 = m_cursor1;
2495 c2 = m_cursor2;
2496 if ( c1 > c2 )
2497 {
2498 Math::Swap(c1, c2); // always c1 <= c2
2499 }
2500
2501 if ( c1 == c2 )
2502 {
2503 while ( c1 > 0 )
2504 {
2505 if ( m_text[c1 - 1] == '\n' )
2506 {
2507 break;
2508 }
2509 c1--;
2510 }
2511 while ( c2 < m_len )
2512 {
2513 c2++;
2514 if ( m_text[c2 - 1] == '\n' )
2515 {
2516 break;
2517 }
2518 }
2519 }
2520
2521 if ( c1 == c2 )
2522 {
2523 return false;
2524 }
2525
2526 std::stringstream ss;
2527 GetIndentedText(ss, c1, c2);
2528 SDL_SetClipboardText(ss.str().c_str()); //TODO: Move to CApplication
2529
2530 if (memorize_cursor)
2531 {
2532 m_cursor1 = c1;
2533 m_cursor2 = c2;
2534 }
2535
2536 return true;
2537 }
2538
2539 // Paste the contents of the notebook.
2540
Paste()2541 bool CEdit::Paste()
2542 {
2543 char c;
2544 char* text;
2545 int iTabToInsert=0;
2546 int iTmp; //temp for tab space equivalant insertion
2547
2548 if ( !m_bEdit )
2549 {
2550 return false;
2551 }
2552
2553 text = SDL_GetClipboardText(); // TODO: Move to CApplication
2554
2555 if ( text == nullptr )
2556 {
2557 return false;
2558 }
2559
2560 UndoMemorize(OPERUNDO_SPEC);
2561 for (unsigned int i = 0; i<strlen(text); ++i)
2562 {
2563 c = text[i];
2564 switch(c)
2565 {
2566 case '\r':
2567 continue;
2568 case '\t':
2569 if (m_bAutoIndent)
2570 {
2571 if (0<m_cursor1 && m_cursor1<=m_len && '\n'!=m_text[m_cursor1-1])
2572 iTabToInsert++;
2573 continue;
2574 }
2575 break;
2576 case '\n':
2577 iTabToInsert=0;
2578 }
2579 if (0<iTabToInsert && m_bAutoIndent)
2580 {
2581 for (iTmp=m_engine->GetEditIndentValue()*iTabToInsert; iTmp>0; --iTmp)
2582 InsertOne(' ');
2583 iTabToInsert=0;
2584 }
2585 InsertOne(c);
2586 }
2587 if (0<iTabToInsert && m_bAutoIndent && 0<m_cursor1
2588 && (m_cursor1>=m_len || '\n'!=m_text[m_cursor1]))
2589 for (iTmp=m_engine->GetEditIndentValue() ; iTmp>0; --iTmp)
2590 InsertOne(' ');
2591 SDL_free(text);
2592 Justif();
2593 ColumnFix();
2594 SendModifEvent();
2595 return true;
2596 }
2597
2598
2599 // Cancels the last action.
2600
Undo()2601 bool CEdit::Undo()
2602 {
2603 if ( !m_bEdit )
2604 {
2605 return false;
2606 }
2607
2608 return UndoRecall();
2609 }
2610
2611
2612 // Inserts a character.
2613
Insert(char character)2614 void CEdit::Insert(char character)
2615 {
2616 int i, level, tab;
2617
2618 if ( !m_bEdit )
2619 {
2620 return;
2621 }
2622
2623 if ( !m_bMulti ) // single-line?
2624 {
2625 if ( character == '\n' ||
2626 character == '\t' ) return;
2627 }
2628
2629 UndoMemorize(OPERUNDO_INSERT);
2630
2631 if ( m_bMulti && !m_bAutoIndent )
2632 {
2633 if ( character == '\n' )
2634 {
2635 InsertOne(character);
2636 level = IndentCompute();
2637 for ( i=0 ; i<level ; i++ )
2638 {
2639 InsertOne('\t');
2640 }
2641 }
2642 else if ( character == '{' )
2643 {
2644 tab = IndentTabCount();
2645 if ( tab != -1 )
2646 {
2647 level = IndentCompute();
2648 IndentTabAdjust(level-tab);
2649 }
2650 InsertOne(character);
2651 }
2652 else if ( character == '}' )
2653 {
2654 tab = IndentTabCount();
2655 if ( tab != -1 )
2656 {
2657 level = IndentCompute()-1;
2658 IndentTabAdjust(level-tab);
2659 }
2660 InsertOne(character);
2661 }
2662 else
2663 {
2664 InsertOne(character);
2665 }
2666 }
2667 else if ( m_bAutoIndent )
2668 {
2669 if (character == '{')
2670 {
2671 InsertOne(character);
2672 InsertOne('}');
2673 MoveChar(-1, false, false);
2674 }
2675 else if (character == '\t')
2676 {
2677 for ( i=0 ; i<m_engine->GetEditIndentValue() ; i++ )
2678 {
2679 InsertOne(' ');
2680 }
2681 }
2682 else if (character == '\n')
2683 {
2684 if (m_cursor1 > 1 && m_text[m_cursor1-1] == '{')
2685 {
2686 InsertOne(character);
2687 InsertOne('\n');
2688 MoveChar(-1, false, false);
2689 }
2690 else
2691 {
2692 InsertOne(character);
2693 }
2694 }
2695 else
2696 {
2697 InsertOne(character);
2698 }
2699 }
2700 else
2701 {
2702 InsertOne(character);
2703 }
2704
2705 Justif();
2706 ColumnFix();
2707 }
2708
2709 // Inserts a plain character.
2710
InsertOne(char character)2711 void CEdit::InsertOne(char character)
2712 {
2713 int i;
2714
2715 if ( !m_bEdit ) return;
2716 if ( !m_bMulti && character == '\n' ) return;
2717
2718 if ( m_cursor1 != m_cursor2 )
2719 {
2720 DeleteOne(0); // deletes the selected characters
2721 }
2722
2723 if ( m_len >= GetMaxChar() ) return;
2724
2725 m_text.resize( m_text.size() + 1, '\0' );
2726 m_format.resize( m_format.size() + 1, m_fontType );
2727
2728 for ( i=m_len ; i>m_cursor1 ; i-- )
2729 {
2730 m_text[i] = m_text[i-1]; // shoot
2731
2732 if ( m_format.size() > static_cast<unsigned int>(i) )
2733 {
2734 m_format[i] = m_format[i-1]; // shoot
2735 }
2736 }
2737
2738 m_len ++;
2739
2740 m_text[m_cursor1] = character;
2741
2742 if ( static_cast<unsigned int>(m_cursor1) < m_format.size() )
2743 {
2744 m_format[m_cursor1] = m_fontType;
2745 }
2746
2747 m_cursor1++;
2748 m_cursor2 = m_cursor1;
2749 }
2750
2751 // Deletes the character left of cursor or all selected characters.
2752
Delete(int dir)2753 void CEdit::Delete(int dir)
2754 {
2755 if ( !m_bEdit ) return;
2756
2757 UndoMemorize(OPERUNDO_DELETE);
2758 DeleteOne(dir);
2759
2760 Justif();
2761 ColumnFix();
2762 }
2763
2764 // Deletes the character left of cursor or all selected plain characters.
2765
DeleteOne(int dir)2766 void CEdit::DeleteOne(int dir)
2767 {
2768 int i, end, hole;
2769
2770 if ( !m_bEdit ) return;
2771
2772 if ( m_cursor1 == m_cursor2 )
2773 {
2774 if ( dir < 0 )
2775 {
2776 if ( m_cursor1 == 0 ) return;
2777 m_cursor1 --;
2778 }
2779 else
2780 {
2781 if ( m_cursor2 == m_len ) return;
2782 m_cursor2 ++;
2783 }
2784 }
2785
2786 if ( m_cursor1 > m_cursor2 ) Math::Swap(m_cursor1, m_cursor2);
2787 hole = m_cursor2-m_cursor1;
2788 end = m_len-hole;
2789 for ( i=m_cursor1 ; i<end ; i++ )
2790 {
2791 m_text[i] = m_text[i+hole];
2792
2793 if ( m_format.size() > static_cast<unsigned int>(i + hole) )
2794 {
2795 m_format[i] = m_format[i+hole];
2796 }
2797 }
2798 m_len -= hole;
2799 m_cursor2 = m_cursor1;
2800 }
2801
2802 // Delete word
2803
DeleteWord(int dir)2804 void CEdit::DeleteWord(int dir)
2805 {
2806 if ( !m_bEdit ) return;
2807
2808 if ( dir < 0 )
2809 {
2810 if ( m_cursor1 > 0) m_cursor2 = --m_cursor1;
2811 else m_cursor2 = m_cursor1;
2812
2813 if ( IsBreaker(m_text[m_cursor1]) )
2814 {
2815 Delete(1);
2816 return;
2817 }
2818 else ++m_cursor1;
2819
2820 while ( m_cursor1 < m_len && !IsDelimiter(m_text[m_cursor1]) ) ++m_cursor1;
2821
2822 while ( m_cursor2 > 0 && IsSpace(m_text[m_cursor2]) ) --m_cursor2;
2823
2824 if ( !IsDelimiter(m_text[m_cursor2]) )
2825 {
2826 while ( m_cursor2 > 0 && !IsDelimiter(m_text[m_cursor2]) ) --m_cursor2;
2827 if ( IsBreaker(m_text[m_cursor2]) ) ++m_cursor2;
2828 }
2829
2830 Delete(-1);
2831 }
2832 else
2833 {
2834 m_cursor2 = m_cursor1;
2835
2836 while ( m_cursor1 < m_len && IsSpace(m_text[m_cursor1]) ) ++m_cursor1;
2837
2838 if ( IsBreaker(m_text[m_cursor1]) )
2839 {
2840 ++m_cursor1;
2841 Delete(1);
2842 return;
2843 }
2844
2845 while ( m_cursor1 < m_len && !IsDelimiter(m_text[m_cursor1]) ) ++m_cursor1;
2846
2847 if ( !IsDelimiter(m_text[m_cursor2]) )
2848 {
2849 while ( m_cursor2 > 0 && !IsDelimiter(m_text[m_cursor2]) ) --m_cursor2;
2850 if ( IsBreaker(m_text[m_cursor2]) ) ++m_cursor2;
2851 }
2852
2853 Delete(-1);
2854 }
2855 }
2856
2857 // Calculates the indentation level of brackets {and}.
2858
IndentCompute()2859 int CEdit::IndentCompute()
2860 {
2861 int i, level;
2862
2863 level = 0;
2864 for ( i=0 ; i<m_cursor1 ; i++ )
2865 {
2866 if ( m_text[i] == '{' ) level ++;
2867 if ( m_text[i] == '}' ) level --;
2868 }
2869
2870 if ( level < 0 ) level = 0;
2871 return level;
2872 }
2873
2874 // Counts the number of tabs before the cursor.
2875 // Returns -1 if there is something else.
2876
IndentTabCount()2877 int CEdit::IndentTabCount()
2878 {
2879 int i, nb;
2880
2881 if ( m_cursor1 != m_cursor2 ) return -1;
2882
2883 i = m_cursor1;
2884 nb = 0;
2885 while ( i > 0 )
2886 {
2887 if ( m_text[i-1] == '\n' ) return nb;
2888 if ( m_text[i-1] != '\t' ) return -1;
2889 nb ++;
2890 i --;
2891 }
2892 return nb;
2893 }
2894
2895 // Adds or removes some tabs.
2896
IndentTabAdjust(int number)2897 void CEdit::IndentTabAdjust(int number)
2898 {
2899 int i;
2900
2901 for ( i=0 ; i<number ; i++ ) // add?
2902 {
2903 InsertOne('\t');
2904 }
2905
2906 for ( i=0 ; i>number ; i-- ) // delete?
2907 {
2908 DeleteOne(-1);
2909 }
2910 }
2911
2912
2913 // Indent the left or right the entire selection.
2914
Shift(bool bLeft)2915 bool CEdit::Shift(bool bLeft)
2916 {
2917 bool bInvert = false;
2918 int c1, c2, i;
2919
2920 if ( m_cursor1 == m_cursor2 ) return false;
2921
2922 UndoMemorize(OPERUNDO_SPEC);
2923
2924 c1 = m_cursor1;
2925 c2 = m_cursor2;
2926 if ( c1 > c2 )
2927 {
2928 Math::Swap(c1, c2); // always c1 <= c2
2929 bInvert = true;
2930 }
2931
2932 if ( c1 > 0 )
2933 {
2934 if ( m_text[c1-1] != '\n' ) return false;
2935 }
2936 if ( c2 < m_len )
2937 {
2938 if ( m_text[c2-1] != '\n' ) return false;
2939 }
2940
2941 if ( bLeft ) // shifts left?
2942 {
2943 i = c1;
2944 while ( i < c2 )
2945 {
2946 if ( m_text[i] == '\t' )
2947 {
2948 m_cursor1 = i;
2949 m_cursor2 = i+1;
2950 DeleteOne(0);
2951 c2 --;
2952 }
2953 while ( i < c2 && m_text[i++] != '\n' );
2954 }
2955 }
2956 else // shifts right?
2957 {
2958 i = c1;
2959 while ( i < c2 )
2960 {
2961 m_cursor1 = m_cursor2 = i;
2962 InsertOne('\t');
2963 c2 ++;
2964 while ( i < c2 && m_text[i++] != '\n' );
2965 }
2966 }
2967
2968 if ( bInvert ) Math::Swap(c1, c2);
2969 m_cursor1 = c1;
2970 m_cursor2 = c2;
2971
2972 Justif();
2973 ColumnFix();
2974 SendModifEvent();
2975 return true;
2976 }
2977
2978 // Math::Min conversion <-> shift the selection.
2979
MinMaj(bool bMaj)2980 bool CEdit::MinMaj(bool bMaj)
2981 {
2982 int c1, c2, i, character;
2983
2984 if ( m_cursor1 == m_cursor2 ) return false;
2985
2986 UndoMemorize(OPERUNDO_SPEC);
2987
2988 c1 = m_cursor1;
2989 c2 = m_cursor2;
2990 if ( c1 > c2 ) Math::Swap(c1, c2); // always c1 <= c2
2991
2992 for ( i=c1 ; i<c2 ; i++ )
2993 {
2994 character = static_cast<unsigned char>(m_text[i]);
2995 if ( bMaj ) character = toupper(character);
2996 else character = tolower(character);
2997 m_text[i] = character;
2998 }
2999
3000 Justif();
3001 ColumnFix();
3002 SendModifEvent();
3003 return true;
3004 }
3005
3006
3007 // Cut all text lines.
3008
Justif()3009 void CEdit::Justif()
3010 {
3011 float width, size, indentLength = 0.0f;
3012 int i, j, k, line, indent;
3013 bool bDual, bString, bRem;
3014
3015 m_lineOffset.clear();
3016 m_lineIndent.clear();
3017
3018 indent = 0;
3019 m_lineTotal = 0;
3020 m_lineOffset.push_back( 0 );
3021 m_lineIndent.push_back( indent );
3022 m_lineTotal ++;
3023
3024 if ( m_bAutoIndent )
3025 {
3026 indentLength = m_engine->GetText()->GetCharWidth(static_cast<Gfx::UTF8Char>(' '), m_fontType, m_fontSize, 0.0f)
3027 * m_engine->GetEditIndentValue();
3028 }
3029
3030 bString = bRem = false;
3031 i = k = 0;
3032 while ( true )
3033 {
3034 bDual = false;
3035
3036 width = m_dim.x-(7.5f/640.0f)*(m_fontSize/Gfx::FONT_SIZE_SMALL)*2.0f-(m_bMulti?MARGX*2.0f+SCROLL_WIDTH:0.0f);
3037 if ( m_bAutoIndent )
3038 {
3039 width -= indentLength*m_lineIndent[m_lineTotal-1];
3040 }
3041
3042 if ( m_format.empty() )
3043 {
3044 // TODO check if good
3045
3046 i += m_engine->GetText()->Justify(m_text.data()+i, m_fontType,
3047 m_fontSize, width);
3048 }
3049 else
3050 {
3051 size = m_fontSize;
3052
3053 if ( m_format.size() > static_cast<unsigned int>(i) && (m_format[i]&Gfx::FONT_MASK_TITLE) == Gfx::FONT_TITLE_BIG ) // headline?
3054 {
3055 size *= BIG_FONT;
3056 bDual = true;
3057 }
3058
3059 if ( m_format.size() > static_cast<unsigned int>(i) && (m_format[i]&Gfx::FONT_MASK_IMAGE) != 0 ) // image part?
3060 {
3061 i ++; // jumps just a character (index in m_image)
3062 }
3063 else
3064 {
3065 // TODO check if good
3066 i += m_engine->GetText()->Justify(std::string(m_text.data()+i),
3067 m_format.begin() + i,
3068 m_format.end(),
3069 size,
3070 width);
3071 }
3072 }
3073
3074 if ( i >= m_len ) break;
3075
3076 if ( m_bAutoIndent )
3077 {
3078 for ( j=m_lineOffset[m_lineTotal-1] ; j<i ; j++ )
3079 {
3080 if ( !bRem && m_text[j] == '\"' ) bString = !bString;
3081 if ( !bString &&
3082 m_text[j] == '/' &&
3083 m_text[j+1] == '/' ) bRem = true;
3084 if ( m_text[j] == '\n' ) bString = bRem = false;
3085 if ( m_text[j] == '{' && !bString && !bRem ) indent ++;
3086 if ( m_text[j] == '}' && !bString && !bRem ) indent --;
3087 }
3088 if ( indent < 0 ) indent = 0;
3089 }
3090
3091 m_lineOffset.push_back( i );
3092 m_lineIndent.push_back( indent );
3093 m_lineTotal ++;
3094 if ( bDual )
3095 {
3096 m_lineOffset.push_back( i );
3097 m_lineIndent.push_back( indent );
3098 m_lineTotal ++;
3099 }
3100 if ( k == i ) break;
3101 k = i;
3102 }
3103
3104 if ( m_len > 0 && m_text[m_len-1] == '\n' )
3105 {
3106 m_lineOffset.push_back( m_len );
3107 m_lineIndent.push_back( 0 );
3108 m_lineTotal ++;
3109 }
3110 m_lineOffset.push_back( m_len );
3111 m_lineIndent.push_back( 0 );
3112
3113 if ( m_bAutoIndent )
3114 {
3115 for ( i=0 ; i<=m_lineTotal ; i++ )
3116 {
3117 if ( m_text[m_lineOffset[i]] == '}' )
3118 {
3119 if ( m_lineIndent[i] > 0 ) m_lineIndent[i] --;
3120 }
3121 }
3122 }
3123
3124 if ( m_bMulti )
3125 {
3126 if ( m_bEdit )
3127 {
3128 line = GetCursorLine(m_cursor1);
3129 if ( line < m_lineFirst )
3130 {
3131 m_lineFirst = line;
3132 }
3133 if ( line >= m_lineFirst+m_lineVisible )
3134 {
3135 m_lineFirst = line-m_lineVisible+1;
3136 }
3137 }
3138 }
3139 else
3140 {
3141 m_lineFirst = 0;
3142 }
3143
3144 UpdateScroll();
3145
3146 m_timeBlink = 0.0f; // lights the cursor immediately
3147 }
3148
3149 // Returns the rank of the line where the cursor is located.
3150
GetCursorLine(int cursor)3151 int CEdit::GetCursorLine(int cursor)
3152 {
3153 int line, i;
3154
3155 line = 0;
3156 for ( i=0 ; i<m_lineTotal ; i++ )
3157 {
3158 if ( cursor >= m_lineOffset[i] )
3159 {
3160 line = i;
3161 }
3162 }
3163 return line;
3164 }
3165
3166
3167 // Flush the buffer undo.
3168
UndoFlush()3169 void CEdit::UndoFlush()
3170 {
3171 int i;
3172
3173 for ( i=0 ; i<EDITUNDOMAX ; i++ )
3174 {
3175 m_undo[i].text.clear();
3176 }
3177
3178 m_bUndoForce = true;
3179 m_undoOper = OPERUNDO_SPEC;
3180 }
3181
3182 // Memorize the current state before a change.
3183
UndoMemorize(OperUndo oper)3184 void CEdit::UndoMemorize(OperUndo oper)
3185 {
3186 int i, len;
3187
3188 if ( !m_bUndoForce &&
3189 oper != OPERUNDO_SPEC &&
3190 m_undoOper != OPERUNDO_SPEC &&
3191 oper == m_undoOper ) return;
3192
3193 m_bUndoForce = false;
3194 m_undoOper = oper;
3195
3196 m_undo[EDITUNDOMAX-1].text.clear();
3197
3198 for ( i=EDITUNDOMAX-1 ; i>=1 ; i-- )
3199 {
3200 m_undo[i] = m_undo[i-1];
3201 }
3202
3203 len = m_len;
3204 if ( len == 0 ) len ++;
3205 m_undo[0].text = m_text;
3206 m_undo[0].len = m_len;
3207
3208 m_undo[0].cursor1 = m_cursor1;
3209 m_undo[0].cursor2 = m_cursor2;
3210 m_undo[0].lineFirst = m_lineFirst;
3211 }
3212
3213 // Back to previous state.
3214
UndoRecall()3215 bool CEdit::UndoRecall()
3216 {
3217 int i;
3218
3219 if ( m_undo[0].text.empty() ) return false;
3220
3221 m_len = m_undo[0].len;
3222 m_text = m_undo[0].text;
3223
3224 m_cursor1 = m_undo[0].cursor1;
3225 m_cursor2 = m_undo[0].cursor2;
3226 m_lineFirst = m_undo[0].lineFirst;
3227
3228 for ( i=0 ; i<EDITUNDOMAX-1 ; i++ )
3229 {
3230 m_undo[i] = m_undo[i+1];
3231 }
3232 m_undo[EDITUNDOMAX-1].text.clear();
3233
3234 m_bUndoForce = true;
3235 Justif();
3236 ColumnFix();
3237 SendModifEvent();
3238 return true;
3239 }
3240
3241
3242 // Clears the format of all characters.
3243
ClearFormat()3244 bool CEdit::ClearFormat()
3245 {
3246 if ( m_format.empty() )
3247 {
3248 SetMultiFont(true);
3249 }
3250 m_format.clear();
3251
3252 return true;
3253 }
3254
3255 // Changes the format of a sequence of characters.
3256
SetFormat(int cursor1,int cursor2,int format)3257 bool CEdit::SetFormat(int cursor1, int cursor2, int format)
3258 {
3259 int i;
3260
3261 if ( m_format.size() < static_cast<unsigned int>(cursor2) )
3262 SetMultiFont(true);
3263
3264 for ( i=cursor1 ; i<cursor2 ; i++ )
3265 {
3266 m_format.at(i) = (m_format.at(i) & ~Gfx::FONT_MASK_HIGHLIGHT) | format;
3267 }
3268
3269 return true;
3270 }
3271
UpdateScroll()3272 void CEdit::UpdateScroll()
3273 {
3274 if (m_scroll != nullptr)
3275 {
3276 if ( m_lineTotal <= m_lineVisible )
3277 {
3278 m_scroll->SetVisibleRatio(1.0f);
3279 m_scroll->SetVisibleValue(0.0f);
3280 m_scroll->SetArrowStep(0.0f);
3281 }
3282 else
3283 {
3284 float value = static_cast<float>(m_lineVisible) / m_lineTotal;
3285 m_scroll->SetVisibleRatio(value);
3286
3287 value = static_cast<float>(m_lineFirst) / (m_lineTotal - m_lineVisible);
3288 m_scroll->SetVisibleValue(value);
3289
3290 value = 1.0f / (m_lineTotal - m_lineVisible);
3291 m_scroll->SetArrowStep(value);
3292 }
3293 }
3294 }
3295
SetFocus(CControl * control)3296 void CEdit::SetFocus(CControl* control)
3297 {
3298 bool oldFocus = m_bFocus;
3299 CControl::SetFocus(control);
3300
3301 if (oldFocus != m_bFocus)
3302 {
3303 UpdateFocus();
3304 }
3305 }
3306
UpdateFocus()3307 void CEdit::UpdateFocus()
3308 {
3309 CApplication::GetInstancePointer()->SetTextInput(m_bFocus, m_eventType);
3310 }
3311
3312 }
3313