Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
CInput.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2013 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. 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 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /*
19 CInput
20 */
21 
22 #include "precompiled.h"
23 #include "GUI.h"
24 #include "CInput.h"
25 #include "CGUIScrollBarVertical.h"
26 
27 #include "graphics/ShaderManager.h"
28 #include "graphics/TextRenderer.h"
29 #include "lib/ogl.h"
30 #include "lib/sysdep/clipboard.h"
31 #include "lib/timer.h"
32 #include "ps/CLogger.h"
33 #include "ps/ConfigDB.h"
34 #include "ps/Font.h"
35 #include "ps/Globals.h"
36 #include "ps/Hotkey.h"
37 #include "renderer/Renderer.h"
38 
39 #include <sstream>
40 
41 extern int g_yres;
42 
43 //-------------------------------------------------------------------
44 // Constructor / Destructor
45 //-------------------------------------------------------------------
47  : m_iBufferPos(-1), m_iBufferPos_Tail(-1), m_SelectingText(false), m_HorizontalScroll(0.f),
48  m_PrevTime(0.0), m_CursorVisState(true), m_CursorBlinkRate(0.5)
49 {
50  AddSetting(GUIST_float, "buffer_zone");
51  AddSetting(GUIST_CStrW, "caption");
52  AddSetting(GUIST_int, "cell_id");
53  AddSetting(GUIST_CStrW, "font");
54  AddSetting(GUIST_int, "max_length");
55  AddSetting(GUIST_bool, "multiline");
56  AddSetting(GUIST_bool, "scrollbar");
57  AddSetting(GUIST_CStr, "scrollbar_style");
58  AddSetting(GUIST_CGUISpriteInstance, "sprite");
59  AddSetting(GUIST_CGUISpriteInstance, "sprite_selectarea");
60  AddSetting(GUIST_CColor, "textcolor");
61  AddSetting(GUIST_CColor, "textcolor_selected");
62  AddSetting(GUIST_CStrW, "tooltip");
63  AddSetting(GUIST_CStr, "tooltip_style");
64 
65  CFG_GET_VAL("gui.cursorblinkrate", Double, m_CursorBlinkRate);
66 
67  // Add scroll-bar
69  bar->SetRightAligned(true);
70  bar->SetUseEdgeButtons(true);
71  AddScrollBar(bar);
72 }
73 
75 {
76 }
77 
79 {
80  ENSURE(m_iBufferPos != -1);
81 
82  if (ev->ev.type == SDL_HOTKEYDOWN)
83  {
84  return(ManuallyHandleHotkeyEvent(ev));
85  }
86  else if (ev->ev.type == SDL_KEYDOWN)
87  {
88  // Since the GUI framework doesn't handle to set settings
89  // in Unicode (CStrW), we'll simply retrieve the actual
90  // pointer and edit that.
91  CStrW *pCaption = (CStrW*)m_Settings["caption"].m_pSetting;
92  bool shiftKeyPressed = g_keys[SDLK_RSHIFT] || g_keys[SDLK_LSHIFT];
93 
94  int szChar = ev->ev.key.keysym.sym;
95  wchar_t cooked = (wchar_t)ev->ev.key.keysym.unicode;
96 
97  switch (szChar)
98  {
99  case SDLK_TAB: // '\t'
100  /* Auto Complete */
101  // TODO Gee: (2004-09-07) What to do with tab?
102  break;
103 
104  case SDLK_BACKSPACE: // '\b'
105  m_WantedX=0.0f;
106 
107  if (SelectingText())
109  else
110  {
111  m_iBufferPos_Tail = -1;
112 
113  if (pCaption->empty() || m_iBufferPos == 0)
114  {
115  break;
116  }
117  else
118  {
119  if (m_iBufferPos == (int)pCaption->length())
120  *pCaption = pCaption->Left( (long) pCaption->length()-1);
121  else
122  *pCaption = pCaption->Left( m_iBufferPos-1 ) +
123  pCaption->Right( (long) pCaption->length()-m_iBufferPos );
124 
125  --m_iBufferPos;
126 
128  }
129  }
130 
132  break;
133 
134  case SDLK_DELETE:
135  m_WantedX=0.0f;
136  // If selection:
137  if (SelectingText())
138  {
140  }
141  else
142  {
143  if (pCaption->empty() || m_iBufferPos == (int)pCaption->length())
144  {
145  break;
146  }
147  else
148  {
149  *pCaption = pCaption->Left( m_iBufferPos ) +
150  pCaption->Right( (long) pCaption->length()-(m_iBufferPos+1) );
151 
153  }
154  }
155 
157  break;
158 
159  case SDLK_HOME:
160  // If there's not a selection, we should create one now
161  if (!shiftKeyPressed)
162  {
163  // Make sure a selection isn't created.
164  m_iBufferPos_Tail = -1;
165  }
166  else if (!SelectingText())
167  {
168  // Place tail at the current point:
170  }
171 
172  m_iBufferPos = 0;
173  m_WantedX=0.0f;
174 
176  break;
177 
178  case SDLK_END:
179  // If there's not a selection, we should create one now
180  if (!shiftKeyPressed)
181  {
182  // Make sure a selection isn't created.
183  m_iBufferPos_Tail = -1;
184  }
185  else if (!SelectingText())
186  {
187  // Place tail at the current point:
189  }
190 
191  m_iBufferPos = (long) pCaption->length();
192  m_WantedX=0.0f;
193 
195  break;
196 
197  /**
198  Conventions for Left/Right when text is selected:
199 
200  References:
201 
202  Visual Studio
203  Visual Studio has the 'newer' approach, used by newer versions of
204  things, and in newer applications. A left press will always place
205  the pointer on the left edge of the selection, and then of course
206  remove the selection. Right will do the exakt same thing.
207  If you have the pointer on the right edge and press right, it will
208  in other words just remove the selection.
209 
210  Windows (eg. Notepad)
211  A left press always takes the pointer a step to the left and
212  removes the selection as if it were never there in the first place.
213  Right of course does the same thing but to the right.
214 
215  I chose the Visual Studio convention. Used also in Word, gtk 2.0, MSN
216  Messenger.
217 
218  **/
219  case SDLK_LEFT:
220  m_WantedX=0.f;
221 
222  if (shiftKeyPressed || !SelectingText())
223  {
224  if (!shiftKeyPressed)
225  {
226  m_iBufferPos_Tail = -1;
227  }
228  else if (!SelectingText())
229  {
231  }
232 
233  if (m_iBufferPos > 0)
234  --m_iBufferPos;
235  }
236  else
237  {
240 
241  m_iBufferPos_Tail = -1;
242  }
243 
245  break;
246 
247  case SDLK_RIGHT:
248  m_WantedX=0.0f;
249 
250  if (shiftKeyPressed || !SelectingText())
251  {
252  if (!shiftKeyPressed)
253  {
254  m_iBufferPos_Tail = -1;
255  }
256  else if (!SelectingText())
257  {
259  }
260 
261  if (m_iBufferPos < (int)pCaption->length())
262  ++m_iBufferPos;
263  }
264  else
265  {
268 
269  m_iBufferPos_Tail = -1;
270  }
271 
273  break;
274 
275  /**
276  Conventions for Up/Down when text is selected:
277 
278  References:
279 
280  Visual Studio
281  Visual Studio has a very strange approach, down takes you below the
282  selection to the next row, and up to the one prior to the whole
283  selection. The weird part is that it is always aligned as the
284  'pointer'. I decided this is to much work for something that is
285  a bit arbitrary
286 
287  Windows (eg. Notepad)
288  Just like with left/right, the selection is destroyed and it moves
289  just as if there never were a selection.
290 
291  I chose the Notepad convention even though I use the VS convention with
292  left/right.
293 
294  **/
295  case SDLK_UP:
296  {
297  if (!shiftKeyPressed)
298  {
299  m_iBufferPos_Tail = -1;
300  }
301  else if (!SelectingText())
302  {
304  }
305 
306  std::list<SRow>::iterator current = m_CharacterPositions.begin();
307  while (current != m_CharacterPositions.end())
308  {
309  if (m_iBufferPos >= current->m_ListStart &&
310  m_iBufferPos <= current->m_ListStart+(int)current->m_ListOfX.size())
311  break;
312 
313  ++current;
314  }
315 
316  float pos_x;
317 
318  if (m_iBufferPos-current->m_ListStart == 0)
319  pos_x = 0.f;
320  else
321  pos_x = current->m_ListOfX[m_iBufferPos-current->m_ListStart-1];
322 
323  if (m_WantedX > pos_x)
324  pos_x = m_WantedX;
325 
326  // Now change row:
327  if (current != m_CharacterPositions.begin())
328  {
329  --current;
330 
331  // Find X-position:
332  m_iBufferPos = current->m_ListStart + GetXTextPosition(current, pos_x, m_WantedX);
333  }
334  // else we can't move up
335 
337  }
338  break;
339 
340  case SDLK_DOWN:
341  {
342  if (!shiftKeyPressed)
343  {
344  m_iBufferPos_Tail = -1;
345  }
346  else if (!SelectingText())
347  {
349  }
350 
351  std::list<SRow>::iterator current = m_CharacterPositions.begin();
352  while (current != m_CharacterPositions.end())
353  {
354  if (m_iBufferPos >= current->m_ListStart &&
355  m_iBufferPos <= current->m_ListStart+(int)current->m_ListOfX.size())
356  break;
357 
358  ++current;
359  }
360 
361  float pos_x;
362 
363  if (m_iBufferPos-current->m_ListStart == 0)
364  pos_x = 0.f;
365  else
366  pos_x = current->m_ListOfX[m_iBufferPos-current->m_ListStart-1];
367 
368  if (m_WantedX > pos_x)
369  pos_x = m_WantedX;
370 
371  // Now change row:
372  // Add first, so we can check if it's .end()
373  ++current;
374  if (current != m_CharacterPositions.end())
375  {
376  // Find X-position:
377  m_iBufferPos = current->m_ListStart + GetXTextPosition(current, pos_x, m_WantedX);
378  }
379  // else we can't move up
380 
382  }
383  break;
384 
385  case SDLK_PAGEUP:
387  break;
388 
389  case SDLK_PAGEDOWN:
391  break;
392  /* END: Message History Lookup */
393 
394  case '\r':
395  // 'Return' should do a Press event for single liners (e.g. submitting forms)
396  // otherwise a '\n' character will be added.
397  {
398  bool multiline;
399  GUI<bool>::GetSetting(this, "multiline", multiline);
400  if (!multiline)
401  {
402  SendEvent(GUIM_PRESSED, "press");
403  break;
404  }
405 
406  cooked = '\n'; // Change to '\n' and do default:
407  // NOTE: Fall-through
408  }
409  default: //Insert a character
410  {
411  if (cooked == 0)
412  return IN_PASS; // Important, because we didn't use any key
413 
414  // check max length
415  int max_length;
416  GUI<int>::GetSetting(this, "max_length", max_length);
417  if (max_length != 0 && (int)pCaption->length() >= max_length)
418  break;
419 
420  m_WantedX=0.0f;
421 
422  if (SelectingText())
424  m_iBufferPos_Tail = -1;
425 
426  if (m_iBufferPos == (int)pCaption->length())
427  *pCaption += cooked;
428  else
429  *pCaption = pCaption->Left(m_iBufferPos) + cooked +
430  pCaption->Right((long) pCaption->length()-m_iBufferPos);
431 
433 
434  ++m_iBufferPos;
435 
437  }
438  break;
439  }
440 
441  return IN_HANDLED;
442  }
443 
444  return IN_PASS;
445 }
446 
447 
449 {
450  CStrW *pCaption = (CStrW*)m_Settings["caption"].m_pSetting;
451  bool shiftKeyPressed = g_keys[SDLK_RSHIFT] || g_keys[SDLK_LSHIFT];
452 
453  std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
454  if (hotkey == "paste")
455  {
456  m_WantedX=0.0f;
457 
458  wchar_t* text = sys_clipboard_get();
459  if (text)
460  {
461  if (SelectingText())
462  {
464  }
465 
466  if (m_iBufferPos == (int)pCaption->length())
467  *pCaption += text;
468  else
469  *pCaption = pCaption->Left(m_iBufferPos) + text +
470  pCaption->Right((long) pCaption->length()-m_iBufferPos);
471 
473 
474  m_iBufferPos += (int)wcslen(text);
475 
476  sys_clipboard_free(text);
477  }
478 
479  return IN_HANDLED;
480  }
481  else if (hotkey == "copy" || hotkey == "cut")
482  {
483  m_WantedX=0.0f;
484 
485  if (SelectingText())
486  {
487  int virtualFrom;
488  int virtualTo;
489 
491  {
492  virtualFrom = m_iBufferPos;
493  virtualTo = m_iBufferPos_Tail;
494  }
495  else
496  {
497  virtualFrom = m_iBufferPos_Tail;
498  virtualTo = m_iBufferPos;
499  }
500 
501  CStrW text = (pCaption->Left(virtualTo)).Right(virtualTo - virtualFrom);
502 
503  sys_clipboard_set(&text[0]);
504 
505  if (hotkey == "cut")
506  {
508  }
509  }
510 
511  return IN_HANDLED;
512  }
513  else if (hotkey == "text.delete.left")
514  {
515  m_WantedX=0.0f;
516 
517  if (SelectingText())
518  {
520  }
521  if (!pCaption->empty() && !m_iBufferPos == 0)
522  {
524  CStrW searchString = pCaption->Left( m_iBufferPos );
525 
526  // If we are starting in whitespace, adjust position until we get a non whitespace
527  while (m_iBufferPos > 0)
528  {
529  if (!iswspace(searchString[m_iBufferPos - 1]))
530  break;
531 
532  m_iBufferPos--;
533  }
534 
535  // If we end up on a puctuation char we just delete it (treat punct like a word)
536  if (iswpunct(searchString[m_iBufferPos - 1]))
537  m_iBufferPos--;
538  else
539  {
540  // Now we are on a non white space character, adjust position to char after next whitespace char is found
541  while (m_iBufferPos > 0)
542  {
543  if (iswspace(searchString[m_iBufferPos - 1]) || iswpunct(searchString[m_iBufferPos - 1]))
544  break;
545 
546  m_iBufferPos--;
547  }
548  }
549 
551  }
552  return IN_HANDLED;
553  }
554  else if (hotkey == "text.delete.right")
555  {
556  m_WantedX=0.0f;
557 
558  if (SelectingText())
559  {
561  }
562  if (!pCaption->empty() && m_iBufferPos < (int)pCaption->length())
563  {
564  // Delete the word to the right of the cursor
566 
567  // Delete chars to the right unit we hit whitespace
568  while (++m_iBufferPos < (int)pCaption->length())
569  {
570  if (iswspace((*pCaption)[m_iBufferPos]) || iswpunct((*pCaption)[m_iBufferPos]))
571  break;
572  }
573 
574  // Eliminate any whitespace behind the word we just deleted
575  while (m_iBufferPos < (int)pCaption->length())
576  {
577  if (!iswspace((*pCaption)[m_iBufferPos]))
578  break;
579 
580  m_iBufferPos++;
581  }
583  }
584  return IN_HANDLED;
585  }
586  else if (hotkey == "text.move.left")
587  {
588  m_WantedX=0.0f;
589 
590  if (shiftKeyPressed || !SelectingText())
591  {
592  if (!shiftKeyPressed)
593  {
594  m_iBufferPos_Tail = -1;
595  }
596  else if (!SelectingText())
597  {
599  }
600 
601  if (!pCaption->empty() && !m_iBufferPos == 0)
602  {
603  CStrW searchString = pCaption->Left( m_iBufferPos );
604 
605  // If we are starting in whitespace, adjust position until we get a non whitespace
606  while (m_iBufferPos > 0)
607  {
608  if (!iswspace(searchString[m_iBufferPos - 1]))
609  break;
610 
611  m_iBufferPos--;
612  }
613 
614  // If we end up on a puctuation char we just select it (treat punct like a word)
615  if (iswpunct(searchString[m_iBufferPos - 1]))
616  m_iBufferPos--;
617  else
618  {
619  // Now we are on a non white space character, adjust position to char after next whitespace char is found
620  while (m_iBufferPos > 0)
621  {
622  if (iswspace(searchString[m_iBufferPos - 1]) || iswpunct(searchString[m_iBufferPos - 1]))
623  break;
624 
625  m_iBufferPos--;
626  }
627  }
628  }
629  }
630  else
631  {
634 
635  m_iBufferPos_Tail = -1;
636  }
637 
639 
640  return IN_HANDLED;
641  }
642  else if (hotkey == "text.move.right")
643  {
644  m_WantedX=0.0f;
645 
646  if (shiftKeyPressed || !SelectingText())
647  {
648  if (!shiftKeyPressed)
649  {
650  m_iBufferPos_Tail = -1;
651  }
652  else if (!SelectingText())
653  {
655  }
656 
657  if (!pCaption->empty() && m_iBufferPos < (int)pCaption->length())
658  {
659  CStrW searchString = *pCaption;
660 
661  // Select chars to the right until we hit whitespace
662  while (++m_iBufferPos < (int)pCaption->length())
663  {
664  if (iswspace((*pCaption)[m_iBufferPos]) || iswpunct((*pCaption)[m_iBufferPos]))
665  break;
666  }
667 
668  // Also select any whitespace following the word we just selected
669  while (m_iBufferPos < (int)pCaption->length())
670  {
671  if (!iswspace((*pCaption)[m_iBufferPos]))
672  break;
673 
674  m_iBufferPos++;
675  }
676  }
677  }
678  else
679  {
682 
683  m_iBufferPos_Tail = -1;
684  }
685 
687 
688  return IN_HANDLED;
689  }
690  else
691  {
692  return IN_PASS;
693  }
694 }
695 
696 
698 {
699  // TODO Gee:
701 
702  switch (Message.type)
703  {
705  {
706  bool scrollbar;
707  GUI<bool>::GetSetting(this, "scrollbar", scrollbar);
708 
709  // Update scroll-bar
710  // TODO Gee: (2004-09-01) Is this really updated each time it should?
711  if (scrollbar &&
712  (Message.value == CStr("size") ||
713  Message.value == CStr("z") ||
714  Message.value == CStr("absolute")))
715  {
720  }
721 
722  // Update scrollbar
723  if (Message.value == CStr("scrollbar_style"))
724  {
725  CStr scrollbar_style;
726  GUI<CStr>::GetSetting(this, Message.value, scrollbar_style);
727 
728  GetScrollBar(0).SetScrollBarStyle(scrollbar_style);
729  }
730 
731  if (Message.value == CStr("size") ||
732  Message.value == CStr("z") ||
733  Message.value == CStr("font") ||
734  Message.value == CStr("absolute") ||
735  Message.value == CStr("caption") ||
736  Message.value == CStr("scrollbar") ||
737  Message.value == CStr("scrollbar_style"))
738  {
739  UpdateText();
740  }
741 
742  if (Message.value == CStr("multiline"))
743  {
744  bool multiline;
745  GUI<bool>::GetSetting(this, "multiline", multiline);
746 
747  if (multiline == false)
748  {
749  GetScrollBar(0).SetLength(0.f);
750  }
751  else
752  {
754  }
755 
756  UpdateText();
757  }
758 
759  }break;
760 
762  // Check if we're selecting the scrollbar:
763  {
764  bool scrollbar, multiline;
765  GUI<bool>::GetSetting(this, "scrollbar", scrollbar);
766  GUI<bool>::GetSetting(this, "multiline", multiline);
767 
768  if (GetScrollBar(0).GetStyle() && multiline)
769  {
770  if (GetMousePos().x > m_CachedActualSize.right - GetScrollBar(0).GetStyle()->m_Width)
771  break;
772  }
773 
774  // Okay, this section is about pressing the mouse and
775  // choosing where the point should be placed. For
776  // instance, if we press between a and b, the point
777  // should of course be placed accordingly. Other
778  // special cases are handled like the input box norms.
779  if (g_keys[SDLK_RSHIFT] || g_keys[SDLK_LSHIFT])
780  {
782  }
783  else
784  {
786  }
787 
788  m_SelectingText = true;
789 
791 
792  // If we immediately release the button it will just be seen as a click
793  // for the user though.
794 
795  }break;
796 
798  {
799  CStrW *pCaption = (CStrW*)m_Settings["caption"].m_pSetting;
800 
801  if (pCaption->length() == 0)
802  break;
803 
805 
806  if (m_iBufferPos >= (int)pCaption->length())
807  m_iBufferPos = m_iBufferPos_Tail = pCaption->length() - 1;
808 
809  // See if we are clicking over whitespace
810  if (iswspace((*pCaption)[m_iBufferPos]))
811  {
812  // see if we are in a section of whitespace greater than one character
813  if ((m_iBufferPos + 1 < (int) pCaption->length() && iswspace((*pCaption)[m_iBufferPos + 1])) ||
814  (m_iBufferPos - 1 > 0 && iswspace((*pCaption)[m_iBufferPos - 1])))
815  {
816  //
817  // We are clicking in an area with more than one whitespace character
818  // so we select both the word to the left and then the word to the right
819  //
820  // [1] First the left
821  // skip the whitespace
822  while (m_iBufferPos > 0)
823  {
824  if (!iswspace((*pCaption)[m_iBufferPos - 1]))
825  break;
826 
827  m_iBufferPos--;
828  }
829  // now go until we hit white space or punctuation
830  while (m_iBufferPos > 0)
831  {
832  if (iswspace((*pCaption)[m_iBufferPos - 1]))
833  break;
834 
835  m_iBufferPos--;
836 
837  if (iswpunct((*pCaption)[m_iBufferPos]))
838  break;
839  }
840 
841  // [2] Then the right
842  // go right until we are not in whitespace
843  while (++m_iBufferPos_Tail < (int)pCaption->length())
844  {
845  if (!iswspace((*pCaption)[m_iBufferPos_Tail]))
846  break;
847  }
848 
849  if (m_iBufferPos_Tail == (int)pCaption->length())
850  break;
851 
852  // now go to the right until we hit whitespace or punctuation
853  while (++m_iBufferPos_Tail < (int)pCaption->length())
854  {
855  if (iswspace((*pCaption)[m_iBufferPos_Tail]) || iswpunct((*pCaption)[m_iBufferPos_Tail]))
856  break;
857  }
858  }
859  else
860  {
861  // single whitespace so select word to the right
862  while (++m_iBufferPos_Tail < (int)pCaption->length())
863  {
864  if (!iswspace((*pCaption)[m_iBufferPos_Tail]))
865  break;
866  }
867 
868  if (m_iBufferPos_Tail == (int)pCaption->length())
869  break;
870 
871  // Don't include the leading whitespace
872  m_iBufferPos = m_iBufferPos_Tail;
873 
874  // now go to the right until we hit whitespace or punctuation
875  while (++m_iBufferPos_Tail < (int)pCaption->length())
876  {
877  if (iswspace((*pCaption)[m_iBufferPos_Tail]) || iswpunct((*pCaption)[m_iBufferPos_Tail]))
878  break;
879  }
880  }
881  }
882  else
883  {
884  // clicked on non-whitespace so select current word
885  // go until we hit white space or punctuation
886  while (m_iBufferPos > 0)
887  {
888  if (iswspace((*pCaption)[m_iBufferPos - 1]))
889  break;
890 
891  m_iBufferPos--;
892 
893  if (iswpunct((*pCaption)[m_iBufferPos]))
894  break;
895  }
896  // go to the right until we hit whitespace or punctuation
897  while (++m_iBufferPos_Tail < (int)pCaption->length())
898  {
899  if (iswspace((*pCaption)[m_iBufferPos_Tail]) || iswpunct((*pCaption)[m_iBufferPos_Tail]))
900  break;
901  }
902  }
903  }
904  break;
905 
907  if (m_SelectingText)
908  {
909  m_SelectingText = false;
910  }
911  break;
912  case GUIM_MOUSE_MOTION:
913  // If we just pressed down and started to move before releasing
914  // this is one way of selecting larger portions of text.
915  if (m_SelectingText)
916  {
917  // Actually, first we need to re-check that the mouse button is
918  // really pressed (it can be released while outside the control.
920  m_SelectingText = false;
921  else
923 
925  }
926 
927  break;
928 
930  {
932  // Since the scroll was changed, let's simulate a mouse movement
933  // to check if scrollbar now is hovered
935  HandleMessage(msg);
936  break;
937  }
938  case GUIM_MOUSE_WHEEL_UP:
939  {
941  // Since the scroll was changed, let's simulate a mouse movement
942  // to check if scrollbar now is hovered
944  HandleMessage(msg);
945  break;
946  }
947  case GUIM_LOAD:
948  {
953 
954  CStr scrollbar_style;
955  GUI<CStr>::GetSetting(this, "scrollbar_style", scrollbar_style);
956  GetScrollBar(0).SetScrollBarStyle( scrollbar_style );
957 
958  UpdateText();
959  }
960  break;
961 
962  case GUIM_GOT_FOCUS:
963  m_iBufferPos = 0;
964  m_PrevTime = 0.0;
965  m_CursorVisState = false;
966 
967  break;
968 
969  case GUIM_LOST_FOCUS:
970  m_iBufferPos = -1;
971  m_iBufferPos_Tail = -1;
972  break;
973 
974  default:
975  break;
976  }
977 }
978 
980 {
981  // If an ancestor's size changed, this will let us intercept the change and
982  // update our scrollbar positions
983 
985 
986  bool scrollbar;
987  GUI<bool>::GetSetting(this, "scrollbar", scrollbar);
988  if (scrollbar)
989  {
994  }
995 }
996 
998 {
999  float bz = GetBufferedZ();
1000 
1001  if (m_CursorBlinkRate > 0.0)
1002  {
1003  // check if the cursor visibility state needs to be changed
1004  double currTime = timer_Time();
1005  if ((currTime - m_PrevTime) >= m_CursorBlinkRate)
1006  {
1008  m_PrevTime = currTime;
1009  }
1010  }
1011  else
1012  {
1013  // should always be visible
1014  m_CursorVisState = true;
1015  }
1016 
1017  // First call draw on ScrollBarOwner
1018  bool scrollbar;
1019  float buffer_zone;
1020  bool multiline;
1021  GUI<bool>::GetSetting(this, "scrollbar", scrollbar);
1022  GUI<float>::GetSetting(this, "buffer_zone", buffer_zone);
1023  GUI<bool>::GetSetting(this, "multiline", multiline);
1024 
1025  if (scrollbar && multiline)
1026  {
1027  // Draw scrollbar
1029  }
1030 
1031  if (GetGUI())
1032  {
1033  CStrW font_name;
1034  CColor color, color_selected;
1035  //CStrW caption;
1036  GUI<CStrW>::GetSetting(this, "font", font_name);
1037  GUI<CColor>::GetSetting(this, "textcolor", color);
1038  GUI<CColor>::GetSetting(this, "textcolor_selected", color_selected);
1039 
1040  // Get pointer of caption, it might be very large, and we don't
1041  // want to copy it continuously.
1042  CStrW *pCaption = (CStrW*)m_Settings["caption"].m_pSetting;
1043 
1044  CGUISpriteInstance *sprite=NULL, *sprite_selectarea=NULL;
1045  int cell_id;
1046 
1047  GUI<CGUISpriteInstance>::GetSettingPointer(this, "sprite", sprite);
1048  GUI<CGUISpriteInstance>::GetSettingPointer(this, "sprite_selectarea", sprite_selectarea);
1049 
1050  GUI<int>::GetSetting(this, "cell_id", cell_id);
1051 
1052  GetGUI()->DrawSprite(*sprite, cell_id, bz, m_CachedActualSize);
1053 
1054  float scroll=0.f;
1055  if (scrollbar && multiline)
1056  {
1057  scroll = GetScrollBar(0).GetPos();
1058  }
1059 
1060  CFont font(font_name);
1061 
1062  // We'll have to setup clipping manually, since we're doing the rendering manually.
1063  CRect cliparea(m_CachedActualSize);
1064 
1065  // First we'll figure out the clipping area, which is the cached actual size
1066  // substracted by an optional scrollbar
1067  if (scrollbar)
1068  {
1069  scroll = GetScrollBar(0).GetPos();
1070 
1071  // substract scrollbar from cliparea
1072  if (cliparea.right > GetScrollBar(0).GetOuterRect().left &&
1073  cliparea.right <= GetScrollBar(0).GetOuterRect().right)
1074  cliparea.right = GetScrollBar(0).GetOuterRect().left;
1075 
1076  if (cliparea.left >= GetScrollBar(0).GetOuterRect().left &&
1077  cliparea.left < GetScrollBar(0).GetOuterRect().right)
1078  cliparea.left = GetScrollBar(0).GetOuterRect().right;
1079  }
1080 
1081  if (cliparea != CRect())
1082  {
1083  glEnable(GL_SCISSOR_TEST);
1084  glScissor(cliparea.left, g_yres - cliparea.bottom, cliparea.GetWidth(), cliparea.GetHeight());
1085  }
1086 
1087  // These are useful later.
1088  int VirtualFrom, VirtualTo;
1089 
1091  {
1092  VirtualFrom = m_iBufferPos;
1093  VirtualTo = m_iBufferPos_Tail;
1094  }
1095  else
1096  {
1097  VirtualFrom = m_iBufferPos_Tail;
1098  VirtualTo = m_iBufferPos;
1099  }
1100 
1101  // Get the height of this font.
1102  float h = (float)font.GetHeight();
1103  float ls = (float)font.GetLineSpacing();
1104 
1105  CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_gui_text);
1106 
1107  CTextRenderer textRenderer(tech->GetShader());
1108  textRenderer.Font(font_name);
1109 
1110  // Set the Z to somewhat more, so we can draw a selected area between the
1111  // the control and the text.
1112  textRenderer.Translate(
1113  (float)(int)(m_CachedActualSize.left) + buffer_zone,
1114  (float)(int)(m_CachedActualSize.top+h) + buffer_zone,
1115  bz+0.1f);
1116 
1117  // U+FE33: PRESENTATION FORM FOR VERTICAL LOW LINE
1118  // (sort of like a | which is aligned to the left of most characters)
1119 
1120  float buffered_y = -scroll+buffer_zone;
1121 
1122  // When selecting larger areas, we need to draw a rectangle box
1123  // around it, and this is to keep track of where the box
1124  // started, because we need to follow the iteration until we
1125  // reach the end, before we can actually draw it.
1126  bool drawing_box = false;
1127  float box_x=0.f;
1128 
1129  float x_pointer=0.f;
1130 
1131  // If we have a selecting box (i.e. when you have selected letters, not just when
1132  // the pointer is between two letters) we need to process all letters once
1133  // before we do it the second time and render all the text. We can't do it
1134  // in the same loop because text will have been drawn, so it will disappear when
1135  // drawn behind the text that has already been drawn. Confusing, well it's necessary
1136  // (I think).
1137 
1138  if (SelectingText())
1139  {
1140  // Now m_iBufferPos_Tail can be of both sides of m_iBufferPos,
1141  // just like you can select from right to left, as you can
1142  // left to right. Is there a difference? Yes, the pointer
1143  // be placed accordingly, so that if you select shift and
1144  // expand this selection, it will expand on appropriate side.
1145  // Anyway, since the drawing procedure needs "To" to be
1146  // greater than from, we need virtual values that might switch
1147  // place.
1148 
1149  int VirtualFrom, VirtualTo;
1150 
1152  {
1153  VirtualFrom = m_iBufferPos;
1154  VirtualTo = m_iBufferPos_Tail;
1155  }
1156  else
1157  {
1158  VirtualFrom = m_iBufferPos_Tail;
1159  VirtualTo = m_iBufferPos;
1160  }
1161 
1162 
1163  bool done = false;
1164  for (std::list<SRow>::const_iterator it = m_CharacterPositions.begin();
1165  it != m_CharacterPositions.end();
1166  ++it, buffered_y += ls, x_pointer = 0.f)
1167  {
1168  if (multiline)
1169  {
1170  if (buffered_y > m_CachedActualSize.GetHeight())
1171  break;
1172  }
1173 
1174  // We might as well use 'i' here to iterate, because we need it
1175  // (often compared against ints, so don't make it size_t)
1176  for (int i=0; i < (int)it->m_ListOfX.size()+2; ++i)
1177  {
1178  if (it->m_ListStart + i == VirtualFrom)
1179  {
1180  // we won't actually draw it now, because we don't
1181  // know the width of each glyph to that position.
1182  // we need to go along with the iteration, and
1183  // make a mark where the box started:
1184  drawing_box = true; // will turn false when finally rendered.
1185 
1186  // Get current x position
1187  box_x = x_pointer;
1188  }
1189 
1190  // no else!
1191 
1192  const bool at_end = (i == (int)it->m_ListOfX.size()+1);
1193 
1194  if (drawing_box == true &&
1195  (it->m_ListStart + i == VirtualTo || at_end))
1196  {
1197  // Depending on if it's just a row change, or if it's
1198  // the end of the select box, do slightly different things.
1199  if (at_end)
1200  {
1201  if (it->m_ListStart + i != VirtualFrom)
1202  {
1203  // and actually add a white space! yes, this is done in any common input
1204  x_pointer += (float)font.GetCharacterWidth(L' ');
1205  }
1206  }
1207  else
1208  {
1209  drawing_box = false;
1210  done = true;
1211  }
1212 
1213  CRect rect;
1214  // Set 'rect' depending on if it's a multiline control, or a one-line control
1215  if (multiline)
1216  {
1217  rect = CRect(m_CachedActualSize.left+box_x+buffer_zone,
1218  m_CachedActualSize.top+buffered_y+(h-ls)/2,
1219  m_CachedActualSize.left+x_pointer+buffer_zone,
1220  m_CachedActualSize.top+buffered_y+(h+ls)/2);
1221 
1222  if (rect.bottom < m_CachedActualSize.top)
1223  continue;
1224 
1225  if (rect.top < m_CachedActualSize.top)
1226  rect.top = m_CachedActualSize.top;
1227 
1228  if (rect.bottom > m_CachedActualSize.bottom)
1230  }
1231  else // if one-line
1232  {
1233  rect = CRect(m_CachedActualSize.left+box_x+buffer_zone-m_HorizontalScroll,
1234  m_CachedActualSize.top+buffered_y+(h-ls)/2,
1235  m_CachedActualSize.left+x_pointer+buffer_zone-m_HorizontalScroll,
1236  m_CachedActualSize.top+buffered_y+(h+ls)/2);
1237 
1238  if (rect.left < m_CachedActualSize.left)
1239  rect.left = m_CachedActualSize.left;
1240 
1241  if (rect.right > m_CachedActualSize.right)
1243  }
1244 
1245  if (sprite_selectarea)
1246  GetGUI()->DrawSprite(*sprite_selectarea, cell_id, bz+0.05f, rect);
1247  }
1248 
1249  if (i < (int)it->m_ListOfX.size())
1250  x_pointer += (float)font.GetCharacterWidth((*pCaption)[it->m_ListStart + i]);
1251  }
1252 
1253  if (done)
1254  break;
1255 
1256  // If we're about to draw a box, and all of a sudden changes
1257  // line, we need to draw that line's box, and then reset
1258  // the box drawing to the beginning of the new line.
1259  if (drawing_box)
1260  {
1261  box_x = 0.f;
1262  }
1263  }
1264  }
1265 
1266  // Reset some from previous run
1267  buffered_y = -scroll;
1268 
1269  // Setup initial color (then it might change and change back, when drawing selected area)
1270  textRenderer.Color(color);
1271 
1272  tech->BeginPass();
1273 
1274  bool using_selected_color = false;
1275 
1276  for (std::list<SRow>::const_iterator it = m_CharacterPositions.begin();
1277  it != m_CharacterPositions.end();
1278  ++it, buffered_y += ls)
1279  {
1280  if (buffered_y + buffer_zone >= -ls || !multiline)
1281  {
1282  if (multiline)
1283  {
1284  if (buffered_y + buffer_zone > m_CachedActualSize.GetHeight())
1285  break;
1286  }
1287 
1288  CMatrix3D savedTransform = textRenderer.GetTransform();
1289 
1290  // Text must always be drawn in integer values. So we have to convert scroll
1291  if (multiline)
1292  textRenderer.Translate(0.f, -(float)(int)scroll, 0.f);
1293  else
1294  textRenderer.Translate(-(float)(int)m_HorizontalScroll, 0.f, 0.f);
1295 
1296  // We might as well use 'i' here, because we need it
1297  // (often compared against ints, so don't make it size_t)
1298  for (int i=0; i < (int)it->m_ListOfX.size()+1; ++i)
1299  {
1300  if (!multiline && i < (int)it->m_ListOfX.size())
1301  {
1302  if (it->m_ListOfX[i] - m_HorizontalScroll < -buffer_zone)
1303  {
1304  // We still need to translate the OpenGL matrix
1305  if (i == 0)
1306  textRenderer.Translate(it->m_ListOfX[i], 0.f, 0.f);
1307  else
1308  textRenderer.Translate(it->m_ListOfX[i] - it->m_ListOfX[i-1], 0.f, 0.f);
1309 
1310  continue;
1311  }
1312  }
1313 
1314  // End of selected area, change back color
1315  if (SelectingText() &&
1316  it->m_ListStart + i == VirtualTo)
1317  {
1318  using_selected_color = false;
1319  textRenderer.Color(color);
1320  }
1321 
1322  if (i != (int)it->m_ListOfX.size() &&
1323  it->m_ListStart + i == m_iBufferPos)
1324  {
1325  // selecting only one, then we need only to draw a cursor.
1326  if (m_CursorVisState)
1327  textRenderer.Put(0.0f, 0.0f, L"_");
1328  }
1329 
1330  // Drawing selected area
1331  if (SelectingText() &&
1332  it->m_ListStart + i >= VirtualFrom &&
1333  it->m_ListStart + i < VirtualTo &&
1334  using_selected_color == false)
1335  {
1336  using_selected_color = true;
1337  textRenderer.Color(color_selected);
1338  }
1339 
1340  if (i != (int)it->m_ListOfX.size())
1341  textRenderer.PrintfAdvance(L"%lc", (*pCaption)[it->m_ListStart + i]);
1342 
1343  // check it's now outside a one-liner, then we'll break
1344  if (!multiline && i < (int)it->m_ListOfX.size())
1345  {
1346  if (it->m_ListOfX[i] - m_HorizontalScroll > m_CachedActualSize.GetWidth()-buffer_zone)
1347  break;
1348  }
1349  }
1350 
1351  if (it->m_ListStart + (int)it->m_ListOfX.size() == m_iBufferPos)
1352  {
1353  textRenderer.Color(color);
1354  if (m_CursorVisState)
1355  textRenderer.PutAdvance(L"_");
1356 
1357  if (using_selected_color)
1358  {
1359  textRenderer.Color(color_selected);
1360  }
1361  }
1362 
1363  textRenderer.SetTransform(savedTransform);
1364  }
1365 
1366  textRenderer.Translate(0.f, ls, 0.f);
1367  }
1368 
1369  textRenderer.Render();
1370 
1371  if (cliparea != CRect())
1372  glDisable(GL_SCISSOR_TEST);
1373 
1374  tech->EndPass();
1375  }
1376 }
1377 
1378 void CInput::UpdateText(int from, int to_before, int to_after)
1379 {
1380  CStrW caption;
1381  CStrW font_name;
1382  float buffer_zone;
1383  bool multiline;
1384  GUI<CStrW>::GetSetting(this, "font", font_name);
1385  GUI<CStrW>::GetSetting(this, "caption", caption);
1386  GUI<float>::GetSetting(this, "buffer_zone", buffer_zone);
1387  GUI<bool>::GetSetting(this, "multiline", multiline);
1388 
1389  // Ensure positions are valid after caption changes
1390  m_iBufferPos = std::min(m_iBufferPos, (int)caption.size());
1391  m_iBufferPos_Tail = std::min(m_iBufferPos_Tail, (int)caption.size());
1392 
1393  if (font_name.empty())
1394  {
1395  // Destroy everything stored, there's no font, so there can be
1396  // no data.
1397  m_CharacterPositions.clear();
1398  return;
1399  }
1400 
1401  SRow row;
1402  row.m_ListStart = 0;
1403 
1404  int to = 0; // make sure it's initialized
1405 
1406  if (to_before == -1)
1407  to = (int)caption.length();
1408 
1409  CFont font(font_name);
1410 
1411  std::list<SRow>::iterator current_line;
1412 
1413  // Used to ... TODO
1414  int check_point_row_start = -1;
1415  int check_point_row_end = -1;
1416 
1417  // Reset
1418  if (from == 0 && to_before == -1)
1419  {
1420  m_CharacterPositions.clear();
1421  current_line = m_CharacterPositions.begin();
1422  }
1423  else
1424  {
1425  ENSURE(to_before != -1);
1426 
1427  std::list<SRow>::iterator destroy_row_from, destroy_row_to;
1428  // Used to check if the above has been set to anything,
1429  // previously a comparison like:
1430  // destroy_row_from == std::list<SRow>::iterator()
1431  // ... was used, but it didn't work with GCC.
1432  bool destroy_row_from_used=false, destroy_row_to_used=false;
1433 
1434  // Iterate, and remove everything between 'from' and 'to_before'
1435  // actually remove the entire lines they are on, it'll all have
1436  // to be redone. And when going along, we'll delete a row at a time
1437  // when continuing to see how much more after 'to' we need to remake.
1438  int i=0;
1439  for (std::list<SRow>::iterator it = m_CharacterPositions.begin();
1440  it != m_CharacterPositions.end(); ++it, ++i)
1441  {
1442  if (destroy_row_from_used == false &&
1443  it->m_ListStart > from)
1444  {
1445  // Destroy the previous line, and all to 'to_before'
1446  destroy_row_from = it;
1447  --destroy_row_from;
1448 
1449  destroy_row_from_used = true;
1450 
1451  // For the rare case that we might remove characters to a word
1452  // so that it suddenly fits on the previous row,
1453  // we need to by standards re-do the whole previous line too
1454  // (if one exists)
1455  if (destroy_row_from != m_CharacterPositions.begin())
1456  --destroy_row_from;
1457  }
1458 
1459  if (destroy_row_to_used == false &&
1460  it->m_ListStart > to_before)
1461  {
1462  destroy_row_to = it;
1463 
1464  destroy_row_to_used = true;
1465 
1466  // If it isn't the last row, we'll add another row to delete,
1467  // just so we can see if the last restorted line is
1468  // identical to what it was before. If it isn't, then we'll
1469  // have to continue.
1470  // 'check_point_row_start' is where we store how the that
1471  // line looked.
1472  if (destroy_row_to != m_CharacterPositions.end())
1473  {
1474  check_point_row_start = destroy_row_to->m_ListStart;
1475  check_point_row_end = check_point_row_start + (int)destroy_row_to->m_ListOfX.size();
1476  if (destroy_row_to->m_ListOfX.empty())
1477  ++check_point_row_end;
1478  }
1479 
1480  ++destroy_row_to;
1481  break;
1482  }
1483  }
1484 
1485  if (destroy_row_from_used == false)
1486  {
1487  destroy_row_from = m_CharacterPositions.end();
1488  --destroy_row_from;
1489 
1490  // As usual, let's destroy another row back
1491  if (destroy_row_from != m_CharacterPositions.begin())
1492  --destroy_row_from;
1493 
1494  destroy_row_from_used = true;
1495 
1496  current_line = destroy_row_from;
1497  }
1498 
1499  if (destroy_row_to_used == false)
1500  {
1501  destroy_row_to = m_CharacterPositions.end();
1502  check_point_row_start = -1;
1503 
1504  destroy_row_from_used = true;
1505  }
1506 
1507  // set 'from' to the row we'll destroy from
1508  // and 'to' to the row we'll destroy to
1509  from = destroy_row_from->m_ListStart;
1510 
1511  if (destroy_row_to != m_CharacterPositions.end())
1512  to = destroy_row_to->m_ListStart; // notice it will iterate [from, to), so it will never reach to.
1513  else
1514  to = (int)caption.length();
1515 
1516 
1517  // Setup the first row
1518  row.m_ListStart = destroy_row_from->m_ListStart;
1519 
1520  std::list<SRow>::iterator temp_it = destroy_row_to;
1521  --temp_it;
1522 
1523  current_line = m_CharacterPositions.erase(destroy_row_from, destroy_row_to);
1524 
1525  // If there has been a change in number of characters
1526  // we need to change all m_ListStart that comes after
1527  // the interval we just destroyed. We'll change all
1528  // values with the delta change of the string length.
1529  int delta = to_after - to_before;
1530  if (delta != 0)
1531  {
1532  for (std::list<SRow>::iterator it = current_line;
1533  it != m_CharacterPositions.end();
1534  ++it)
1535  {
1536  it->m_ListStart += delta;
1537  }
1538 
1539  // Update our check point too!
1540  check_point_row_start += delta;
1541  check_point_row_end += delta;
1542 
1543  if (to != (int)caption.length())
1544  to += delta;
1545  }
1546  }
1547 
1548  int last_word_started=from;
1549  //int last_list_start=-1; // unused
1550  float x_pos = 0.f;
1551 
1552  //if (to_before != -1)
1553  // return;
1554 
1555  for (int i=from; i<to; ++i)
1556  {
1557  if (caption[i] == L'\n' && multiline)
1558  {
1559  if (i==to-1 && to != (int)caption.length())
1560  break; // it will be added outside
1561 
1562  current_line = m_CharacterPositions.insert( current_line, row );
1563  ++current_line;
1564 
1565 
1566  // Setup the next row:
1567  row.m_ListOfX.clear();
1568  row.m_ListStart = i+1;
1569  x_pos = 0.f;
1570 
1571  }
1572  else
1573  {
1574  if (caption[i] == L' '/* || TODO Gee (2004-10-13): the '-' disappears, fix.
1575  caption[i] == L'-'*/)
1576  last_word_started = i+1;
1577 
1578  x_pos += (float)font.GetCharacterWidth(caption[i]);
1579 
1580  if (x_pos >= GetTextAreaWidth() && multiline)
1581  {
1582  // The following decides whether it will word-wrap a word,
1583  // or if it's only one word on the line, where it has to
1584  // break the word apart.
1585  if (last_word_started == row.m_ListStart)
1586  {
1587  last_word_started = i;
1588  row.m_ListOfX.resize(row.m_ListOfX.size() - (i-last_word_started));
1589  //row.m_ListOfX.push_back( x_pos );
1590  //continue;
1591  }
1592  else
1593  {
1594  // regular word-wrap
1595  row.m_ListOfX.resize(row.m_ListOfX.size() - (i-last_word_started+1));
1596  }
1597 
1598  // Now, create a new line:
1599  // notice: when we enter a newline, you can stand with the cursor
1600  // both before and after that character, being on different
1601  // rows. With automatic word-wrapping, that is not possible. Which
1602  // is intuitively correct.
1603 
1604  current_line = m_CharacterPositions.insert( current_line, row );
1605  ++current_line;
1606 
1607  // Setup the next row:
1608  row.m_ListOfX.clear();
1609  row.m_ListStart = last_word_started;
1610 
1611  i=last_word_started-1;
1612 
1613  x_pos = 0.f;
1614  }
1615  else
1616  // Get width of this character:
1617  row.m_ListOfX.push_back( x_pos );
1618  }
1619 
1620  // Check if it's the last iteration, and we're not revising the whole string
1621  // because in that case, more word-wrapping might be needed.
1622  // also check if the current line isn't the end
1623  if (to_before != -1 && i == to-1 && current_line != m_CharacterPositions.end())
1624  {
1625  // check all rows and see if any existing
1626  if (row.m_ListStart != check_point_row_start)
1627  {
1628  std::list<SRow>::iterator destroy_row_from, destroy_row_to;
1629  // Are used to check if the above has been set to anything,
1630  // previously a comparison like:
1631  // destroy_row_from == std::list<SRow>::iterator()
1632  // was used, but it didn't work with GCC.
1633  bool destroy_row_from_used=false, destroy_row_to_used=false;
1634 
1635  // Iterate, and remove everything between 'from' and 'to_before'
1636  // actually remove the entire lines they are on, it'll all have
1637  // to be redone. And when going along, we'll delete a row at a time
1638  // when continuing to see how much more after 'to' we need to remake.
1639  int i=0;
1640  for (std::list<SRow>::iterator it=m_CharacterPositions.begin();
1641  it!=m_CharacterPositions.end(); ++it, ++i)
1642  {
1643  if (destroy_row_from_used == false &&
1644  it->m_ListStart > check_point_row_start)
1645  {
1646  // Destroy the previous line, and all to 'to_before'
1647  //if (i >= 2)
1648  // destroy_row_from = it-2;
1649  //else
1650  // destroy_row_from = it-1;
1651  destroy_row_from = it;
1652  destroy_row_from_used = true;
1653  //--destroy_row_from;
1654  }
1655 
1656  if (destroy_row_to_used == false &&
1657  it->m_ListStart > check_point_row_end)
1658  {
1659  destroy_row_to = it;
1660  destroy_row_to_used = true;
1661 
1662  // If it isn't the last row, we'll add another row to delete,
1663  // just so we can see if the last restorted line is
1664  // identical to what it was before. If it isn't, then we'll
1665  // have to continue.
1666  // 'check_point_row_start' is where we store how the that
1667  // line looked.
1668  // if (destroy_row_to !=
1669  if (destroy_row_to != m_CharacterPositions.end())
1670  {
1671  check_point_row_start = destroy_row_to->m_ListStart;
1672  check_point_row_end = check_point_row_start + (int)destroy_row_to->m_ListOfX.size();
1673  if (destroy_row_to->m_ListOfX.empty())
1674  ++check_point_row_end;
1675  }
1676  else
1677  check_point_row_start = check_point_row_end = -1;
1678 
1679  ++destroy_row_to;
1680  break;
1681  }
1682  }
1683 
1684  if (destroy_row_from_used == false)
1685  {
1686  destroy_row_from = m_CharacterPositions.end();
1687  --destroy_row_from;
1688 
1689  destroy_row_from_used = true;
1690 
1691  current_line = destroy_row_from;
1692  }
1693 
1694  if (destroy_row_to_used == false)
1695  {
1696  destroy_row_to = m_CharacterPositions.end();
1697  check_point_row_start = check_point_row_end = -1;
1698 
1699  destroy_row_to_used = true;
1700  }
1701 
1702  // set 'from' to the from row we'll destroy
1703  // and 'to' to 'to_after'
1704  from = destroy_row_from->m_ListStart;
1705 
1706  if (destroy_row_to != m_CharacterPositions.end())
1707  to = destroy_row_to->m_ListStart; // notice it will iterate [from, to[, so it will never reach to.
1708  else
1709  to = (int)caption.length();
1710 
1711 
1712  // Set current line, new rows will be added before current_line, so
1713  // we'll choose the destroy_row_to, because it won't be deleted
1714  // in the coming erase.
1715  current_line = destroy_row_to;
1716 
1717  std::list<SRow>::iterator temp = destroy_row_to;
1718 
1719  --temp;
1720 
1721  m_CharacterPositions.erase(destroy_row_from, destroy_row_to);
1722  }
1723  // else, the for loop will end naturally.
1724  }
1725  }
1726  // This is kind of special, when we renew a some lines, then the last
1727  // one will sometimes end with a space (' '), that really should
1728  // be omitted when word-wrapping. So we'll check if the last row
1729  // we'll add has got the same value as the next row.
1730  if (current_line != m_CharacterPositions.end())
1731  {
1732  if (row.m_ListStart + (int)row.m_ListOfX.size() == current_line->m_ListStart)
1733  row.m_ListOfX.resize( row.m_ListOfX.size()-1 );
1734  }
1735 
1736  // add the final row (even if empty)
1737  m_CharacterPositions.insert(current_line, row);
1738 
1739  bool scrollbar;
1740  GUI<bool>::GetSetting(this, "scrollbar", scrollbar);
1741  // Update scollbar
1742  if (scrollbar)
1743  {
1744  GetScrollBar(0).SetScrollRange(m_CharacterPositions.size() * font.GetLineSpacing() + buffer_zone*2.f);
1746  }
1747 }
1748 
1750 {
1751  if (m_CharacterPositions.empty())
1752  return 0;
1753 
1754  // Return position
1755  int retPosition;
1756 
1757  float buffer_zone;
1758  bool multiline;
1759  GUI<float>::GetSetting(this, "buffer_zone", buffer_zone);
1760  GUI<bool>::GetSetting(this, "multiline", multiline);
1761 
1762  std::list<SRow>::iterator current = m_CharacterPositions.begin();
1763 
1764  CPos mouse = GetMousePos();
1765 
1766  if (multiline)
1767  {
1768  CStrW font_name;
1769  bool scrollbar;
1770  GUI<CStrW>::GetSetting(this, "font", font_name);
1771  GUI<bool>::GetSetting(this, "scrollbar", scrollbar);
1772 
1773  float scroll=0.f;
1774  if (scrollbar)
1775  {
1776  scroll = GetScrollBar(0).GetPos();
1777  }
1778 
1779  // Pointer to caption, will come in handy
1780  CStrW *pCaption = (CStrW*)m_Settings["caption"].m_pSetting;
1781  UNUSED2(pCaption);
1782 
1783  // Now get the height of the font.
1784  // TODO: Get the real font
1785  CFont font(font_name);
1786  float spacing = (float)font.GetLineSpacing();
1787  //float height = (float)font.GetHeight(); // unused
1788 
1789  // Change mouse position relative to text.
1790  mouse -= m_CachedActualSize.TopLeft();
1791  mouse.x -= buffer_zone;
1792  mouse.y += scroll - buffer_zone;
1793 
1794  //if ((m_CharacterPositions.size()-1) * spacing + height < mouse.y)
1795  // m_iBufferPos = pCaption->Length();
1796  int row = (int)((mouse.y) / spacing);//m_CharachterPositions.size()
1797 
1798  if (row < 0)
1799  row = 0;
1800 
1801  if (row > (int)m_CharacterPositions.size()-1)
1802  row = (int)m_CharacterPositions.size()-1;
1803 
1804  // TODO Gee (2004-11-21): Okay, I need a 'std::list' for some reasons, but I would really like to
1805  // be able to get the specific element here. This is hopefully a temporary hack.
1806 
1807  for (int i=0; i<row; ++i)
1808  ++current;
1809  }
1810  else
1811  {
1812  // current is already set to begin,
1813  // but we'll change the mouse.x to fit our horizontal scrolling
1814  mouse -= m_CachedActualSize.TopLeft();
1815  mouse.x -= buffer_zone - m_HorizontalScroll;
1816  // mouse.y is moot
1817  }
1818 
1819  //m_iBufferPos = m_CharacterPositions.get.m_ListStart;
1820  retPosition = current->m_ListStart;
1821 
1822  // Okay, now loop through the glyphs to find the appropriate X position
1823  float dummy;
1824  retPosition += GetXTextPosition(current, mouse.x, dummy);
1825 
1826  return retPosition;
1827 }
1828 
1829 // Does not process horizontal scrolling, 'x' must be modified before inputted.
1830 int CInput::GetXTextPosition(const std::list<SRow>::iterator &current, const float &x, float &wanted)
1831 {
1832  int ret=0;
1833  float previous=0.f;
1834  int i=0;
1835 
1836  for (std::vector<float>::iterator it=current->m_ListOfX.begin();
1837  it!=current->m_ListOfX.end();
1838  ++it, ++i)
1839  {
1840  if (*it >= x)
1841  {
1842  if (x - previous >= *it - x)
1843  ret += i+1;
1844  else
1845  ret += i;
1846 
1847  break;
1848  }
1849  previous = *it;
1850  }
1851  // If a position wasn't found, we will assume the last
1852  // character of that line.
1853  if (i == (int)current->m_ListOfX.size())
1854  {
1855  ret += i;
1856  wanted = x;
1857  }
1858  else wanted = 0.f;
1859 
1860  return ret;
1861 }
1862 
1864 {
1865  CStrW *pCaption = (CStrW*)m_Settings["caption"].m_pSetting;
1866 
1867  int virtualFrom;
1868  int virtualTo;
1869 
1871  {
1872  virtualFrom = m_iBufferPos;
1873  virtualTo = m_iBufferPos_Tail;
1874  }
1875  else
1876  {
1877  virtualFrom = m_iBufferPos_Tail;
1878  virtualTo = m_iBufferPos;
1879  }
1880 
1881  *pCaption = pCaption->Left( virtualFrom ) +
1882  pCaption->Right( (long) pCaption->length() - (virtualTo) );
1883 
1884  UpdateText(virtualFrom, virtualTo, virtualFrom);
1885 
1886  // Remove selection
1887  m_iBufferPos_Tail = -1;
1888  m_iBufferPos = virtualFrom;
1889 }
1890 
1892 {
1893  return m_iBufferPos_Tail != -1 &&
1895 }
1896 
1898 {
1899  bool scrollbar;
1900  float buffer_zone;
1901  GUI<bool>::GetSetting(this, "scrollbar", scrollbar);
1902  GUI<float>::GetSetting(this, "buffer_zone", buffer_zone);
1903 
1904  if (scrollbar && GetScrollBar(0).GetStyle())
1905  return m_CachedActualSize.GetWidth() - buffer_zone*2.f - GetScrollBar(0).GetStyle()->m_Width;
1906  else
1907  return m_CachedActualSize.GetWidth() - buffer_zone*2.f;
1908 }
1909 
1911 {
1912  float buffer_zone;
1913  bool multiline;
1914  GUI<float>::GetSetting(this, "buffer_zone", buffer_zone);
1915  GUI<bool>::GetSetting(this, "multiline", multiline);
1916 
1917  // Autoscrolling up and down
1918  if (multiline)
1919  {
1920  CStrW font_name;
1921  bool scrollbar;
1922  GUI<CStrW>::GetSetting(this, "font", font_name);
1923  GUI<bool>::GetSetting(this, "scrollbar", scrollbar);
1924 
1925  float scroll=0.f;
1926  if (!scrollbar)
1927  return;
1928 
1929  scroll = GetScrollBar(0).GetPos();
1930 
1931  // Now get the height of the font.
1932  // TODO: Get the real font
1933  CFont font(font_name);
1934  float spacing = (float)font.GetLineSpacing();
1935  //float height = font.GetHeight();
1936 
1937  // TODO Gee (2004-11-21): Okay, I need a 'std::list' for some reasons, but I would really like to
1938  // be able to get the specific element here. This is hopefully a temporary hack.
1939 
1940  std::list<SRow>::iterator current = m_CharacterPositions.begin();
1941  int row=0;
1942  while (current != m_CharacterPositions.end())
1943  {
1944  if (m_iBufferPos >= current->m_ListStart &&
1945  m_iBufferPos <= current->m_ListStart+(int)current->m_ListOfX.size())
1946  break;
1947 
1948  ++current;
1949  ++row;
1950  }
1951 
1952  // If scrolling down
1953  if (-scroll + (float)(row+1) * spacing + buffer_zone*2.f > m_CachedActualSize.GetHeight())
1954  {
1955  // Scroll so the selected row is shown completely, also with buffer_zone length to the edge.
1956  GetScrollBar(0).SetPos((float)(row+1) * spacing - m_CachedActualSize.GetHeight() + buffer_zone*2.f);
1957  }
1958  else
1959  // If scrolling up
1960  if (-scroll + (float)row * spacing < 0.f)
1961  {
1962  // Scroll so the selected row is shown completely, also with buffer_zone length to the edge.
1963  GetScrollBar(0).SetPos((float)row * spacing);
1964  }
1965  }
1966  else // autoscrolling left and right
1967  {
1968  // Get X position of position:
1969  if (m_CharacterPositions.empty())
1970  return;
1971 
1972  float x_position = 0.f;
1973  float x_total = 0.f;
1974  if (!m_CharacterPositions.begin()->m_ListOfX.empty())
1975  {
1976 
1977  // Get position of m_iBufferPos
1978  if ((int)m_CharacterPositions.begin()->m_ListOfX.size() >= m_iBufferPos &&
1979  m_iBufferPos != 0)
1980  x_position = m_CharacterPositions.begin()->m_ListOfX[m_iBufferPos-1];
1981 
1982  // Get complete length:
1983  x_total = m_CharacterPositions.begin()->m_ListOfX[ m_CharacterPositions.begin()->m_ListOfX.size()-1 ];
1984  }
1985 
1986  // Check if outside to the right
1987  if (x_position - m_HorizontalScroll + buffer_zone*2.f > m_CachedActualSize.GetWidth())
1988  m_HorizontalScroll = x_position - m_CachedActualSize.GetWidth() + buffer_zone*2.f;
1989 
1990  // Check if outside to the left
1991  if (x_position - m_HorizontalScroll < 0.f)
1992  m_HorizontalScroll = x_position;
1993 
1994  // Check if the text doesn't even fill up to the right edge even though scrolling is done.
1995  if (m_HorizontalScroll != 0.f &&
1996  x_total - m_HorizontalScroll + buffer_zone*2.f < m_CachedActualSize.GetWidth())
1997  m_HorizontalScroll = x_total - m_CachedActualSize.GetWidth() + buffer_zone*2.f;
1998 
1999  // Now this is the fail-safe, if x_total isn't even the length of the control,
2000  // remove all scrolling
2001  if (x_total + buffer_zone*2.f < m_CachedActualSize.GetWidth())
2002  m_HorizontalScroll = 0.f;
2003  }
2004 }
double m_CursorBlinkRate
Definition: CInput.h:190
int GetLineSpacing()
Definition: Font.cpp:55
void Translate(float x, float y, float z)
Definition: Matrix3D.cpp:172
void SetScrollRange(float range)
Set content length.
void SetUseEdgeButtons(bool b)
Set use edge buttons.
float m_WantedX
Definition: CInput.h:177
CInput()
Definition: CInput.cpp:46
void DrawSprite(const CGUISpriteInstance &Sprite, int CellID, const float &Z, const CRect &Rect, const CRect &Clipping=CRect())
Draw GUI Sprite.
Definition: CGUI.cpp:467
CStr value
Optional data.
Definition: GUIbase.h:127
SDL_KeyboardEvent key
Definition: wsdl.h:305
Status sys_clipboard_set(const wchar_t *text)
Definition: android.cpp:30
float m_HorizontalScroll
Definition: CInput.h:184
float top
Definition: Overlay.h:159
bool SelectingText() const
Definition: CInput.cpp:1891
void SetZ(float z)
Set Z Position.
Definition: Overlay.h:34
virtual void Draw()
Draws the Text.
Definition: CInput.cpp:997
float m_Width
Width of bar, also both sides of the edge buttons.
Definition: IGUIScrollBar.h:67
virtual IGUIScrollBar & GetScrollBar(const int &index)
Get Scroll Bar reference (it should be transparent it&#39;s actually pointers).
int GetCharacterWidth(wchar_t c)
Definition: Font.cpp:65
float left
Returning CPos representing each corner.
Definition: Overlay.h:159
void SetY(float y)
Set Y Position.
static PSRETURN GetSetting(const IGUIObject *pObject, const CStr &Setting, T &Value)
Retrieves a setting by name from object pointer.
Definition: GUIutil.cpp:344
#define CFG_GET_VAL(name, type, destination)
Definition: ConfigDB.h:147
virtual InReaction ManuallyHandleEvent(const SDL_Event_ *ev)
Handle events manually to catch keyboard inputting.
Definition: CInput.cpp:78
void DeleteCurSelection()
Definition: CInput.cpp:1863
shared_ptr< CShaderTechnique > CShaderTechniquePtr
bool m_CursorVisState
Definition: CInput.h:193
float GetTextAreaWidth()
Definition: CInput.cpp:1897
void SetLength(float length)
Set Length of scroll bar.
virtual void AddScrollBar(IGUIScrollBar *scrollbar)
Add a scroll-bar.
Status sys_clipboard_free(wchar_t *copy)
Definition: android.cpp:40
virtual float GetBufferedZ() const
Returns not the Z value, but the actual buffered Z value, i.e.
Definition: IGUIObject.cpp:406
#define g_Renderer
Definition: Renderer.h:61
Hotkey system.
float GetWidth() const
Definition: Overlay.cpp:232
virtual void Draw()
Draws the object.
SDL_Event ev
Definition: libsdl.h:56
virtual void UpdateCachedSize()
All sizes are relative to resolution, and the calculation is not wanted in real time, therefore it is cached, update the cached size with this function.
Definition: IGUIObject.cpp:336
void SetX(float x)
Set X Position.
virtual void ScrollPlus()
Increase scroll one step.
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
#define UNUSED2(param)
mark a function local variable or parameter as unused and avoid the corresponding compiler warning...
Uint8 type
Definition: wsdl.h:302
int GetHeight()
Definition: Font.cpp:60
Definition: Font.h:28
double m_PrevTime
Definition: CInput.h:187
virtual void ScrollMinus()
Decrease scroll one step.
virtual ~CInput()
Definition: CInput.cpp:74
InReaction
Definition: input.h:34
Definition: input.h:40
std::map< CStr, SGUISetting > m_Settings
Settings pool, all an object&#39;s settings are located here If a derived object has got more settings th...
Definition: IGUIObject.h:550
int g_yres
Definition: Config.cpp:58
Vertical implementation of IGUIScrollBar.
bool g_mouse_buttons[6]
g_mouse_buttons: Mouse buttons states, indexed by SDL_BUTTON_* constants.
Definition: Globals.cpp:35
int GetXTextPosition(const std::list< SRow >::iterator &c, const float &x, float &wanted)
Definition: CInput.cpp:1830
void AddSetting(const EGUISettingType &Type, const CStr &Name)
Add a setting to m_Settings.
Definition: IGUIObject.cpp:172
void SetScrollBarStyle(const CStr &style)
Set Scroll bar style string.
u16 unicode
Definition: wsdl.h:189
int m_ListStart
Definition: CInput.h:159
const int SDL_HOTKEYDOWN
Definition: Hotkey.h:41
double timer_Time()
Definition: timer.cpp:98
CRect m_CachedActualSize
Cached size, real size m_Size is actually dependent on resolution and can have different real outcome...
Definition: IGUIObject.h:434
Made to represent screen positions and delta values.
Definition: Overlay.h:167
InReaction SendEvent(EGUIMessageType type, const CStr &EventName)
Send event to this GUI object (HandleMessage and ScriptEvent)
Definition: IGUIObject.cpp:466
wchar_t * sys_clipboard_get()
Definition: android.cpp:35
void SetScrollSpace(float space)
Set space that is visible in the scrollable control.
void UpdateAutoScroll()
Definition: CInput.cpp:1910
void UpdateText(int from=0, int to_before=-1, int to_after=-1)
Definition: CInput.cpp:1378
float GetPos() const
Get scroll-position.
float right
Definition: Overlay.h:159
CPos GetMousePos() const
Get Mouse from CGUI.
Definition: IGUIObject.cpp:207
unsigned short wchar_t
Definition: wgl.h:78
void Font(const CStrW &font)
Set the font for subsequent print calls.
CGUI * GetGUI()
Definition: IGUIObject.h:388
void SetRightAligned(const bool &align)
Set Right Aligned.
virtual void ScrollMinusPlenty()
Decrease scroll three steps.
int m_iBufferPos_Tail
Definition: CInput.h:150
std::list< SRow > m_CharacterPositions
Definition: CInput.h:167
float y
Definition: Overlay.h:195
virtual InReaction ManuallyHandleHotkeyEvent(const SDL_Event_ *ev)
Handle hotkey events (called by ManuallyHandleEvent)
Definition: CInput.cpp:448
virtual void SetPos(float f)
Set scroll-position by hand.
float GetHeight() const
Definition: Overlay.cpp:237
SDL_keysym keysym
Definition: wsdl.h:196
virtual CRect GetOuterRect() const =0
Get the rectangle of the outline of the scrollbar, every component of the scroll-bar should be inside...
bool m_SelectingText
Definition: CInput.h:181
const SGUIScrollBarStyle * GetStyle() const
Get style used by the scrollbar.
virtual void ScrollPlusPlenty()
Increase scroll three steps.
SDL_UserEvent user
Definition: wsdl.h:312
std::vector< float > m_ListOfX
Definition: CInput.h:160
virtual void HandleMessage(SGUIMessage &Message)
Definition: CInput.cpp:697
Message send to IGUIObject::HandleMessage() in order to give life to Objects manually with a derived ...
Definition: GUIbase.h:106
EGUIMessageType type
Describes what the message regards.
Definition: GUIbase.h:122
float bottom
Definition: Overlay.h:159
virtual void HandleMessage(SGUIMessage &Message)
SDLKey sym
Definition: wsdl.h:188
float x
Position.
Definition: Overlay.h:195
int m_iBufferPos
Definition: CInput.h:150
CPos TopLeft() const
Get Position equivalent to top/left corner.
Definition: Overlay.cpp:247
virtual void UpdateCachedSize()
Definition: CInput.cpp:979
int GetMouseHoveringTextPosition()
Definition: CInput.cpp:1749
std::map< int32_t, bool > g_keys
g_keys: Key states, indexed by SDLK* constants.
Definition: Globals.cpp:29
void * data1
Definition: wsdl.h:282
static PSRETURN GetSettingPointer(const IGUIObject *pObject, const CStr &Setting, T *&Value)
Definition: GUIutil.cpp:317
Rectangle class used for screen rectangles.
Definition: Overlay.h:71