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/list.h"
22 
23 #include "common/make_unique.h"
24 
25 #include "graphics/engine/engine.h"
26 
27 #include <cassert>
28 #include <cstring>
29 
30 namespace Ui
31 {
32 
33 const float MARGING = 4.0f;
34 
35 
36 // Object's constructor.
37 
CList()38 CList::CList()
39     : CControl(),
40       m_tabs(),
41       m_justifs()
42 {
43     for (int i = 0; i < 10; i++)
44     {
45         m_tabs[i] = 0.0f;
46         m_justifs[i] = Gfx::TEXT_ALIGN_LEFT;
47     }
48 
49     m_expand = 0.0f;
50     m_totalLine = 0;
51     m_displayLine = 0;
52     m_selectLine = -1;
53     m_firstLine = 0;
54     m_bBlink = false;
55     m_bSelectCap = true;
56     m_blinkTime = 0.0f;
57 }
58 
59 // Object's destructor.
60 
~CList()61 CList::~CList()
62 {
63 }
64 
65 
66 // Creates a new list.
67 // if expand is less then zero, then the list would try to use expand's absolute value,
68 // and try to scale items to some size, so that dim of the list would not change after
69 // adjusting
70 
Create(Math::Point pos,Math::Point dim,int icon,EventType eventMsg,float expand)71 bool CList::Create(Math::Point pos, Math::Point dim, int icon, EventType eventMsg, float expand)
72 {
73     m_expand = expand;
74 
75     if (eventMsg == EVENT_NULL)
76         eventMsg = GetUniqueEventType();
77 
78     CControl::Create(pos, dim, icon, eventMsg);
79 
80     m_scroll = MakeUnique<CScroll>();
81     m_scroll->Create(pos, dim, 0, EVENT_NULL);
82 
83     return MoveAdjust();
84 }
85 
86 // Should never be called
Create(Math::Point pos,Math::Point dim,int icon,EventType eventType)87 bool CList::Create(Math::Point pos, Math::Point dim, int icon, EventType eventType)
88 {
89     assert(false);
90     return false;
91 }
92 
93 
94 // Adjusted after a change of dimensions.
95 
MoveAdjust()96 bool CList::MoveAdjust()
97 {
98     Math::Point ipos, idim, ppos, ddim;
99     float marging, h;
100 
101     for (auto& button : m_buttons)
102         button.reset();
103 
104     if (m_icon == 0)
105         marging = MARGING;
106     else
107         marging = 0.0f;
108 
109     ipos.x = m_pos.x + marging / 640.f;
110     ipos.y = m_pos.y + marging / 480.f;
111     idim.x = m_dim.x - marging * 2.0f / 640.f;
112     idim.y = m_dim.y - marging * 2.0f / 480.f;
113 
114     //If m_expand is less then zero, then try to apply it's absolute value
115     h = m_engine->GetText()->GetHeight(m_fontType, m_fontSize) * ((m_expand < 0) ? -m_expand : m_expand);
116     m_displayLine = static_cast<int>(idim.y / h);
117 
118     if (m_displayLine == 0)
119         return false;
120     if (m_displayLine > LISTMAXDISPLAY)
121         m_displayLine = LISTMAXDISPLAY;
122 
123     // Stretch lines to fill whole area of a list, if needed
124     if (m_expand < 0 && (idim.y - (h * m_displayLine) < h))
125     {
126         h = idim.y / m_displayLine;
127     }
128 
129     idim.y = h * m_displayLine; //Here cuts list size if height of shown elements is less then designed height
130     m_dim.y = idim.y + marging * 2.0f / 480.f;
131 
132     ppos.x = ipos.x;
133     ppos.y = ipos.y + idim.y - h;
134     ddim.x = idim.x - SCROLL_WIDTH;
135     ddim.y = h;
136     for (int i = 0; i < m_displayLine; i++)
137     {
138         auto button = MakeUnique<CButton>();
139         button->Create(ppos, ddim, -1, EVENT_NULL);
140         button->SetTextAlign(Gfx::TEXT_ALIGN_LEFT);
141         button->SetState(STATE_SIMPLY);
142         button->SetFontType(m_fontType);
143         button->SetFontSize(m_fontSize);
144         ppos.y -= h;
145 
146         m_buttons[i] = std::move(button);
147     }
148 
149     if (m_scroll != nullptr)
150     {
151         ppos.x = ipos.x + idim.x - SCROLL_WIDTH;
152         ppos.y = ipos.y;
153         ddim.x = SCROLL_WIDTH;
154         ddim.y = idim.y;
155         m_scroll->SetPos(ppos);
156         m_scroll->SetDim(ddim);
157     }
158 
159     UpdateScroll();
160     UpdateButton();
161     return true;
162 }
163 
164 
165 // Returns the message of a button.
166 
GetEventMsgButton(int i)167 EventType CList::GetEventMsgButton(int i)
168 {
169     if (i < 0 || i >= m_displayLine)
170         return EVENT_NULL;
171     if (m_buttons[i] == nullptr)
172         return EVENT_NULL;
173     return m_buttons[i]->GetEventType();
174 }
175 
176 // Returns the message from the elevator.
177 
GetEventMsgScroll()178 EventType CList::GetEventMsgScroll()
179 {
180     if (m_scroll == nullptr)
181         return EVENT_NULL;
182     return m_scroll->GetEventType();
183 }
184 
185 
SetPos(Math::Point pos)186 void CList::SetPos(Math::Point pos)
187 {
188     CControl::SetPos(pos);
189 }
190 
191 
SetDim(Math::Point dim)192 void CList::SetDim(Math::Point dim)
193 {
194     m_dim = dim;
195     MoveAdjust();
196     CControl::SetDim(dim);
197 }
198 
199 
SetState(int state,bool bState)200 bool CList::SetState(int state, bool bState)
201 {
202     if (state & STATE_ENABLE)
203     {
204         for (int i = 0; i < m_displayLine; i++)
205         {
206             if (m_buttons[i] != nullptr)
207                 m_buttons[i]->SetState(state, bState);
208         }
209         if (m_scroll != nullptr)
210             m_scroll->SetState(state, bState);
211     }
212 
213     return CControl::SetState(state, bState);
214 }
215 
216 
SetState(int state)217 bool CList::SetState(int state)
218 {
219     if (state & STATE_ENABLE)
220     {
221         for (int i = 0; i < m_displayLine; i++)
222         {
223             if (m_buttons[i] != nullptr)
224                 m_buttons[i]->SetState(state);
225         }
226         if (m_scroll != nullptr)
227             m_scroll->SetState(state);
228     }
229 
230     return CControl::SetState(state);
231 }
232 
233 
ClearState(int state)234 bool CList::ClearState(int state)
235 {
236     if (state & STATE_ENABLE)
237     {
238         for (int i = 0; i < m_displayLine; i++)
239         {
240             if (m_buttons[i] != nullptr)
241                 m_buttons[i]->ClearState(state);
242         }
243         if (m_scroll != nullptr)
244             m_scroll->ClearState(state);
245     }
246 
247     return CControl::ClearState(state);
248 }
249 
250 
251 // Management of an event.
252 
EventProcess(const Event & event)253 bool CList::EventProcess(const Event &event)
254 {
255     if (m_bBlink && event.type == EVENT_FRAME)
256     {
257         int i = m_selectLine-m_firstLine;
258 
259         if (i >= 0 && i < 4  && m_buttons[i] != nullptr)
260         {
261             m_blinkTime += event.rTime;
262             if (Math::Mod(m_blinkTime, 0.7f) < 0.3f)
263             {
264                 m_buttons[i]->ClearState(STATE_ENABLE);
265                 m_buttons[i]->ClearState(STATE_CHECK);
266             }
267             else
268             {
269                 m_buttons[i]->SetState(STATE_ENABLE);
270                 m_buttons[i]->SetState(STATE_CHECK);
271             }
272         }
273     }
274 
275     if ((m_state & STATE_VISIBLE) == 0)
276         return true;
277     if ((m_state & STATE_ENABLE) == 0)
278         return true;
279 
280     if (event.type == EVENT_MOUSE_WHEEL && Detect(event.mousePos))
281     {
282         auto data = event.GetData<MouseWheelEventData>();
283         m_firstLine -= data->y;
284         if (m_firstLine > m_totalLine - m_displayLine)
285             m_firstLine = m_totalLine - m_displayLine;
286         if (m_firstLine < 0)
287             m_firstLine = 0;
288 
289         UpdateScroll();
290         UpdateButton();
291         return false;
292     }
293 
294     CControl::EventProcess(event);
295 
296     if (event.type == EVENT_MOUSE_MOVE || event.type == EVENT_MOUSE_BUTTON_DOWN || event.type == EVENT_MOUSE_BUTTON_UP)
297     {
298         if (Detect(event.mousePos))
299         {
300             m_engine->SetMouseType(Gfx::ENG_MOUSE_NORM);
301             for (int i = 0; i < m_displayLine; i++)
302             {
303                 if (i + m_firstLine >= m_totalLine)
304                     break;
305                 if (m_buttons[i] != nullptr)
306                     m_buttons[i]->EventProcess(event);
307             }
308         }
309     }
310 
311     if (m_bSelectCap)
312     {
313         for (int i = 0; i < m_displayLine; i++)
314         {
315             if (i + m_firstLine >= m_totalLine)
316                 break;
317 
318             if (m_buttons[i] != nullptr)
319             {
320                 if (!m_buttons[i]->EventProcess(event))
321                     return false;
322 
323                 if (event.type == m_buttons[i]->GetEventType())
324                 {
325                     SetSelect(m_firstLine + i);
326 
327                     m_event->AddEvent(Event(m_eventType));  // selected line changes
328                 }
329             }
330         }
331     }
332 
333     if (m_scroll != nullptr)
334     {
335         if (!m_scroll->EventProcess(event))
336             return false;
337 
338         if (event.type == m_scroll->GetEventType())
339         {
340             MoveScroll();
341             UpdateButton();
342         }
343     }
344 
345     return true;
346 }
347 
348 
349 // Draws the list.
350 
Draw()351 void CList::Draw()
352 {
353     Math::Point uv1, uv2, corner, pos, dim, ppos, ddim;
354     float   dp;
355     int     i;
356     char    text[100];
357     const char    *pb, *pe;
358 
359     if ((m_state & STATE_VISIBLE) == 0)
360         return;
361 
362     if (m_state & STATE_SHADOW)
363         DrawShadow(m_pos, m_dim);
364 
365     dp = 0.5f / 256.0f;
366 
367     if (m_icon != -1)
368     {
369         dim = m_dim;
370 
371         if (m_icon == 0)
372         {
373             m_engine->SetTexture("textures/interface/button2.png");
374             m_engine->SetState(Gfx::ENG_RSTATE_NORMAL);
375 
376             uv1.x = 128.0f / 256.0f;
377             uv1.y =  64.0f / 256.0f;  // u-v texture
378             uv2.x = 160.0f / 256.0f;
379             uv2.y =  96.0f / 256.0f;
380         }
381         else
382         {
383             m_engine->SetTexture("textures/interface/button2.png");
384             m_engine->SetState(Gfx::ENG_RSTATE_NORMAL);
385 
386             uv1.x = 132.0f / 256.0f;
387             uv1.y =  68.0f / 256.0f;  // u-v texture
388             uv2.x = 156.0f / 256.0f;
389             uv2.y =  92.0f / 256.0f;
390 
391             if (m_buttons[0] != nullptr)
392             {
393                 dim = m_buttons[0]->GetDim();
394                 dim.y *= m_displayLine;  // background sounds spot behind
395             }
396         }
397 
398         uv1.x += dp;
399         uv1.y += dp;
400         uv2.x -= dp;
401         uv2.y -= dp;
402 
403         corner.x = 10.0f / 640.0f;
404         corner.y = 10.0f / 480.0f;
405         DrawIcon(m_pos, dim, uv1, uv2, corner, 8.0f / 256.0f);
406     }
407 
408     if ( m_totalLine < m_displayLine ) // no buttons to the bottom?
409     {
410         i = m_totalLine;
411         if (m_buttons[i] != nullptr)
412         {
413             pos = m_buttons[i]->GetPos();
414             dim = m_buttons[i]->GetDim();
415             pos.y += dim.y * 1.1f;
416             dim.y *= 0.4f;
417             pos.y -= dim.y;
418 
419             m_engine->SetTexture("textures/interface/button2.png");
420             m_engine->SetState(Gfx::ENG_RSTATE_TTEXTURE_WHITE); // was D3DSTATETTw
421             uv1.x = 120.0f / 256.0f;
422             uv1.y =  64.0f / 256.0f;
423             uv2.x = 128.0f / 256.0f;
424             uv2.y =  48.0f / 256.0f;
425             uv1.x += dp;
426             uv1.y -= dp;
427             uv2.x -= dp;
428             uv2.y += dp;
429             DrawIcon(pos, dim, uv1, uv2);  // ch'tite shadow cute  (?)
430         }
431     }
432 
433     for (i = 0; i < m_displayLine; i++)
434     {
435         if ( i + m_firstLine >= m_totalLine )
436             break;
437 
438         if ( m_buttons[i] != nullptr )
439         {
440             if ( !m_bBlink && i + m_firstLine < m_totalLine )
441                 m_buttons[i]->SetState(STATE_ENABLE, m_items[i+m_firstLine].enable && (m_state & STATE_ENABLE) );
442 
443             m_buttons[i]->Draw();  // draws a box without text
444 
445             // draws text in the box
446             pos = m_buttons[i]->GetPos();
447             dim = m_buttons[i]->GetDim();
448             if ( m_tabs[0] == 0.0f )
449             {
450                 ppos.x = pos.x + dim.y * 0.5f;
451                 ppos.y = pos.y + dim.y * 0.5f;
452                 ppos.y -= m_engine->GetText()->GetHeight(m_fontType, m_fontSize) / 2.0f;
453                 ddim.x = dim.x-dim.y;
454                 DrawCase(m_items[i + m_firstLine].text.c_str(), ppos, ddim.x, Gfx::TEXT_ALIGN_LEFT);
455             }
456             else
457             {
458                 ppos.x = pos.x + dim.y * 0.5f;
459                 ppos.y = pos.y + dim.y * 0.5f;
460                 ppos.y -= m_engine->GetText()->GetHeight(m_fontType, m_fontSize) / 2.0f;
461                 pb = m_items[i + m_firstLine].text.c_str();
462                 for (int j = 0; j < 10; j++)
463                 {
464                     pe = strchr(pb, '\t');
465                     if ( pe == nullptr )
466                         strcpy(text, pb);
467                     else
468                     {
469                         strncpy(text, pb, pe - pb);
470                         text[pe - pb] = 0;
471                     }
472                     DrawCase(text, ppos, m_tabs[j], m_justifs[j]);
473 
474                     if ( pe == nullptr )
475                         break;
476                     ppos.x += m_tabs[j];
477                     pb = pe + 1;
478                 }
479             }
480 
481             if ( (m_state & STATE_EXTEND) && i < m_totalLine)
482             {
483                 pos = m_buttons[i]->GetPos();
484                 dim = m_buttons[i]->GetDim();
485                 pos.x += dim.x - dim.y * 0.75f;
486                 dim.x = dim.y * 0.75f;
487                 pos.x += 2.0f / 640.0f;
488                 pos.y += 2.0f / 480.0f;
489                 dim.x -= 4.0f / 640.0f;
490                 dim.y -= 4.0f / 480.0f;
491 
492                 if (m_items[i + m_firstLine].check)
493                 {
494                     m_engine->SetTexture("textures/interface/button1.png");
495                     m_engine->SetState(Gfx::ENG_RSTATE_NORMAL);
496                     uv1.x = 64.0f / 256.0f;
497                     uv1.y =  0.0f / 256.0f;
498                     uv2.x = 96.0f / 256.0f;
499                     uv2.y = 32.0f / 256.0f;
500                     uv1.x += dp;
501                     uv1.y += dp;
502                     uv2.x -= dp;
503                     uv2.y -= dp;
504                     DrawIcon(pos, dim, uv1, uv2);  // square shape
505 
506                     m_engine->SetState(Gfx::ENG_RSTATE_TTEXTURE_WHITE); // was D3DSTATETTw
507                     uv1.x =  0.0f / 256.0f;  // v
508                     uv1.y = 64.0f / 256.0f;
509                     uv2.x = 32.0f / 256.0f;
510                     uv2.y = 96.0f / 256.0f;
511                     uv1.x += dp;
512                     uv1.y += dp;
513                     uv2.x -= dp;
514                     uv2.y -= dp;
515                     DrawIcon(pos, dim, uv1, uv2);  // draws v
516                 }
517                 else
518                 {
519                     m_engine->SetTexture("textures/interface/button1.png");
520                     m_engine->SetState(Gfx::ENG_RSTATE_TTEXTURE_WHITE); // was D3DSTATETTw
521                     if ( i + m_firstLine == m_selectLine )
522                     {
523                         uv1.x =224.0f / 256.0f;  // <
524                         uv1.y =192.0f / 256.0f;
525                         uv2.x =256.0f / 256.0f;
526                         uv2.y =224.0f / 256.0f;
527                     }
528                     else
529                     {
530                         uv1.x = 96.0f / 256.0f;  // x
531                         uv1.y = 32.0f / 256.0f;
532                         uv2.x =128.0f / 256.0f;
533                         uv2.y = 64.0f / 256.0f;
534                     }
535                     uv1.x += dp;
536                     uv1.y += dp;
537                     uv2.x -= dp;
538                     uv2.y -= dp;
539                     DrawIcon(pos, dim, uv1, uv2);  // draws x
540                 }
541             }
542         }
543     }
544 
545     if (m_scroll != nullptr)
546         m_scroll->Draw();  // draws the lift
547 }
548 
549 // Displays text in a box.
550 
DrawCase(const char * text,Math::Point pos,float width,Gfx::TextAlign justif)551 void CList::DrawCase(const char* text, Math::Point pos, float width, Gfx::TextAlign justif)
552 {
553     if (justif == Gfx::TEXT_ALIGN_CENTER)
554         pos.x += width / 2.0f;
555     else if (justif == Gfx::TEXT_ALIGN_RIGHT)
556         pos.x += width;
557     m_engine->GetText()->DrawText(std::string(text), m_fontType, m_fontSize, pos, width, justif, 0);
558 }
559 
560 
561 // Empty the list completely.
562 
Flush()563 void CList::Flush()
564 {
565     m_totalLine = 0;
566     m_selectLine = -1;
567     m_firstLine = 0;
568     UpdateButton();
569     UpdateScroll();
570 }
571 
572 
573 // Specifies the total number of lines.
574 
SetTotal(int i)575 void CList::SetTotal(int i)
576 {
577     m_totalLine = i;
578 }
579 
580 // Returns the total number of lines.
581 
GetTotal()582 int CList::GetTotal()
583 {
584     return m_totalLine;
585 }
586 
587 
588 // Selects a line.
589 
SetSelect(int i)590 void CList::SetSelect(int i)
591 {
592     if ( m_bSelectCap )
593         m_selectLine = i;
594     else
595     {
596         m_firstLine = i;
597         UpdateScroll();
598     }
599 
600     UpdateButton();
601 }
602 
603 // Returns the selected line.
604 
GetSelect()605 int CList::GetSelect()
606 {
607     if ( m_bSelectCap )
608         return m_selectLine;
609     else
610         return m_firstLine;
611 }
612 
613 
614 // Management of capability has a select box.
615 
SetSelectCap(bool bEnable)616 void CList::SetSelectCap(bool bEnable)
617 {
618     m_bSelectCap = bEnable;
619 }
620 
GetSelectCap()621 bool CList::GetSelectCap()
622 {
623     return m_bSelectCap;
624 }
625 
626 
627 // Blink a line.
628 
SetBlink(bool bEnable)629 void CList::SetBlink(bool bEnable)
630 {
631     m_bBlink = bEnable;
632     m_blinkTime = 0.0f;
633 
634     int i = m_selectLine - m_firstLine;
635 
636     if (i >= 0 && i < 4 && m_buttons[i] != nullptr)
637     {
638         if ( !bEnable )
639         {
640             m_buttons[i]->SetState(STATE_CHECK);
641             m_buttons[i]->ClearState(STATE_ENABLE);
642         }
643     }
644 }
645 
GetBlink()646 bool CList::GetBlink()
647 {
648     return m_bBlink;
649 }
650 
651 
652 // Specifies the text of a line.
653 
SetItemName(int i,const std::string & name)654 void CList::SetItemName(int i, const std::string& name)
655 {
656     if ( i < 0 )
657         return;
658 
659     if ( i >= m_totalLine )
660         m_totalLine = i+1;  // expands the list
661 
662     m_items.resize(m_totalLine);
663     if ( name.empty() )
664         m_items[i].text =  " ";
665     else
666         m_items[i].text = name;
667 
668     UpdateButton();
669     UpdateScroll();
670 }
671 
672 // Returns the text of a line.
673 
GetItemName(int i)674 const std::string& CList::GetItemName(int i)
675 {
676     if ( i < 0 || i >= m_totalLine )
677         assert(false);
678 
679     return m_items[i].text;
680 }
681 
682 
683 // Specifies the bit "check" for a box.
684 
SetCheck(int i,bool bMode)685 void CList::SetCheck(int i, bool bMode)
686 {
687     if ( i < 0 || i >= m_totalLine )
688         return;
689 
690     m_items[i].check = bMode;
691 }
692 
693 // Returns the bit "check" for a box.
694 
GetCheck(int i)695 bool CList::GetCheck(int i)
696 {
697     if ( i < 0 || i >= m_totalLine )
698         return false;
699 
700     return m_items[i].check;
701 }
702 
703 
704 // Specifies the bit "enable" for a box.
705 
SetEnable(int i,bool enable)706 void CList::SetEnable(int i, bool enable)
707 {
708     if ( i < 0 || i >= m_totalLine )
709         return;
710 
711     m_items[i].enable = enable;
712 }
713 
714 // Returns the bit "enable" for a box.
715 
GetEnable(int i)716 bool CList::GetEnable(int i)
717 {
718     if ( i < 0 || i >= m_totalLine )
719         return false;
720 
721     return m_items[i].enable;
722 }
723 
724 
725 // Management of the position of the tabs.
726 
SetTabs(int i,float pos,Gfx::TextAlign justif)727 void CList::SetTabs(int i, float pos, Gfx::TextAlign justif)
728 {
729     if ( i < 0 || i >= 10 )
730         return;
731     m_tabs[i] = pos;
732     m_justifs[i] = justif;
733 }
734 
GetTabs(int i)735 float  CList::GetTabs(int i)
736 {
737     if ( i < 0 || i >= 10 )
738         return 0.0f;
739     return m_tabs[i];
740 }
741 
742 
743 // Moves the lift to see the list of the selected line.
744 
ShowSelect(bool bFixed)745 void CList::ShowSelect(bool bFixed)
746 {
747     int     sel;
748 
749     if ( bFixed && m_selectLine >= m_firstLine && m_selectLine <  m_firstLine+m_displayLine )
750         return;  // all good
751 
752     sel = m_selectLine;
753 
754     // Down from 1/2 * h.
755     sel += m_displayLine / 2;
756     if ( sel > m_totalLine - 1 )
757         sel = m_totalLine - 1;
758 
759     // Back to h-1.
760     sel -= m_displayLine - 1;
761     if ( sel < 0 )
762         sel = 0;
763 
764     m_firstLine = sel;
765 
766     UpdateButton();
767     UpdateScroll();
768 }
769 
770 
771 // Updates all button names.
772 
UpdateButton()773 void CList::UpdateButton()
774 {
775     int state, i, j;
776 
777     state = CControl::GetState();
778 
779     j = m_firstLine;
780     for (i = 0; i < m_displayLine; i++)
781     {
782         if (m_buttons[i] == nullptr)
783             continue;
784 
785         m_buttons[i]->SetState(STATE_CHECK, (j == m_selectLine));
786 
787         if ( j < m_totalLine )
788         {
789 //?         m_buttons[i]->SetName(m_text[j]);
790             m_buttons[i]->SetName(" ");  // blank button
791             m_buttons[i]->SetState(STATE_ENABLE, (state & STATE_ENABLE));
792         }
793         else
794         {
795             m_buttons[i]->SetName(" ");  // blank button
796             m_buttons[i]->ClearState(STATE_ENABLE);
797         }
798         j ++;
799     }
800 }
801 
802 // Updates the lift.
803 
UpdateScroll()804 void CList::UpdateScroll()
805 {
806     float ratio, value, step;
807 
808     if (m_scroll == nullptr)
809         return;
810 
811     if (m_totalLine <= m_displayLine)
812     {
813         ratio = 1.0f;
814         value = 0.0f;
815         step = 0.0f;
816     }
817     else
818     {
819         ratio = static_cast<float>(m_displayLine) / m_totalLine;
820         if ( ratio > 1.0f )  ratio = 1.0f;
821 
822         value = static_cast<float>(m_firstLine) / (m_totalLine - m_displayLine);
823         if ( value < 0.0f )
824             value = 0.0f;
825         if ( value > 1.0f )
826             value = 1.0f;
827 
828         step = static_cast<float>(1.0f)/ (m_totalLine - m_displayLine);
829         if ( step < 0.0f )
830             step = 0.0f;
831     }
832 
833     m_scroll->SetVisibleRatio(ratio);
834     m_scroll->SetVisibleValue(value);
835     m_scroll->SetArrowStep(step);
836 }
837 
838 // Update when the lift is moving.
839 
MoveScroll()840 void CList::MoveScroll()
841 {
842     if (m_scroll == nullptr)
843         return;
844 
845     int n = m_totalLine - m_displayLine;
846     float pos = m_scroll->GetVisibleValue();
847     pos += m_scroll->GetArrowStep() / 2.0f;  // it's magic!
848     m_firstLine = static_cast<int>(pos * n);
849     if ( m_firstLine < 0 )
850         m_firstLine = 0;
851     if ( m_firstLine > n )
852         m_firstLine = n;
853 }
854 
855 
856 } // namespace Ui
857