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 #include "ui/screen/screen_setup_controls.h"
21 
22 #include "app/app.h"
23 
24 #include "common/settings.h"
25 
26 #include "ui/controls/button.h"
27 #include "ui/controls/check.h"
28 #include "ui/controls/editvalue.h"
29 #include "ui/controls/group.h"
30 #include "ui/controls/interface.h"
31 #include "ui/controls/key.h"
32 #include "ui/controls/label.h"
33 #include "ui/controls/list.h"
34 #include "ui/controls/scroll.h"
35 #include "ui/controls/window.h"
36 
37 namespace Ui
38 {
39 
40 const int KEY_VISIBLE = 8;      // number of visible keys redefinable
41 
CScreenSetupControls()42 CScreenSetupControls::CScreenSetupControls()
43 {
44     m_input = CInput::GetInstancePointer();
45 }
46 
SetActive()47 void CScreenSetupControls::SetActive()
48 {
49     m_tab = PHASE_SETUPc;
50 }
51 
CreateInterface()52 void CScreenSetupControls::CreateInterface()
53 {
54     CWindow*        pw;
55     CLabel*         pl;
56     CCheck*         pc;
57     CScroll*        ps;
58     CButton*        pb;
59     CGroup*         pg;
60     CList*          pli;
61     CEditValue* pev;
62     Math::Point     pos, ddim;
63     std::string     name;
64 
65     CScreenSetup::CreateInterface();
66     pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
67     if ( pw == nullptr )  return;
68 
69     pos.x = ox+sx*3;
70     pos.y = 320.0f/480.0f;
71     ddim.x = dim.x*15.0f;
72     ddim.y = 18.0f/480.0f;
73     GetResource(RES_TEXT, RT_SETUP_KEY1, name);
74     pl = pw->CreateLabel(pos, ddim, 0, EVENT_INTERFACE_KINFO1, name);
75     pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT);
76 
77     pos.x = ox+sx*3;
78     pos.y = 302.0f/480.0f;
79     ddim.x = dim.x*15.0f;
80     ddim.y = 18.0f/480.0f;
81     GetResource(RES_TEXT, RT_SETUP_KEY2, name);
82     pl = pw->CreateLabel(pos, ddim, 0, EVENT_INTERFACE_KINFO2, name);
83     pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT);
84 
85     ddim.x = 273.0f/640.0f;
86     ddim.y = 168.0f/480.0f;
87     pos.x = 105.0f/640.0f;
88     pos.y = 124.0f/480.0f;
89     pg = pw->CreateGroup(pos, ddim, 0, EVENT_INTERFACE_KGROUP);
90     pg->ClearState(STATE_ENABLE);
91     pg->SetState(STATE_DEAD);
92     pg->SetState(STATE_SHADOW);
93 
94     ddim.x =  18.0f/640.0f;
95     ddim.y = (20.0f/480.0f)*KEY_VISIBLE;
96     pos.x = 355.0f/640.0f;
97     pos.y = 128.0f/480.0f;
98     ps = pw->CreateScroll(pos, ddim, -1, EVENT_INTERFACE_KSCROLL);
99     ps->SetVisibleRatio(static_cast<float>(KEY_VISIBLE/INPUT_SLOT_MAX));
100     ps->SetArrowStep(1.0f/(static_cast<float>(INPUT_SLOT_MAX-KEY_VISIBLE)));
101     UpdateKey();
102 
103     ddim.x = 160.0f/640.0f;
104     ddim.y = 80.0f/480.0f;
105     pos.x = 400.0f/640.0f;
106     pos.y = 273.0f/480.0f;
107     pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_JOYSTICK);
108     pli->SetState(STATE_SHADOW);
109 
110     ddim.x = dim.x*1.5f;
111     ddim.y = 18.0f/480.0f;
112     pos.y = 240.0f/480.0f;
113 
114     auto CreateJoystickControls = [&](const std::string& label, EventType bindingControl, EventType invertControl)
115     {
116         pos.y -= 20.0f/480.0f;
117         pos.x = 390.0f/640.0f;
118         pos.y -= 5.0f/480.0f;
119         pw->CreateLabel(pos, ddim, 0, EVENT_LABEL0, label);
120         pos.y += 5.0f/480.0f;
121         pos.x = 442.0f/640.0f;
122         pev = pw->CreateEditValue(pos, ddim, 0, bindingControl);
123         pev->SetState(STATE_SHADOW);
124         pev->SetType(EVT_INT);
125         pev->SetMinValue(-1);
126         pev->SetMaxValue(2);
127         pev->SetStepValue(1);
128         pev->SetValue(1);
129         pos.x = 500.0f/640.0f;
130         pc = pw->CreateCheck(pos, ddim, 0, invertControl);
131         pc->SetState(STATE_SHADOW);
132     };
133     pos.y += 15.0f/480.0f;
134     CreateJoystickControls("X:", EVENT_INTERFACE_JOYSTICK_X, EVENT_INTERFACE_JOYSTICK_X_INVERT);
135     CreateJoystickControls("Y:", EVENT_INTERFACE_JOYSTICK_Y, EVENT_INTERFACE_JOYSTICK_Y_INVERT);
136     CreateJoystickControls("Z:", EVENT_INTERFACE_JOYSTICK_Z, EVENT_INTERFACE_JOYSTICK_Z_INVERT);
137     CreateJoystickControls("CamX:", EVENT_INTERFACE_JOYSTICK_CAM_X, EVENT_INTERFACE_JOYSTICK_CAM_X_INVERT);
138     CreateJoystickControls("CamY:", EVENT_INTERFACE_JOYSTICK_CAM_Y, EVENT_INTERFACE_JOYSTICK_CAM_Y_INVERT);
139     CreateJoystickControls("CamZ:", EVENT_INTERFACE_JOYSTICK_CAM_Z, EVENT_INTERFACE_JOYSTICK_CAM_Z_INVERT);
140 
141     pos.y -= 25.0f/480.0f;
142     pos.x = 420.0f/640.0f;
143     pos.y -= 5.0f/480.0f;
144     pw->CreateLabel(pos, ddim, 0, EVENT_LABEL3, "Deadzone:");
145     pos.y += 5.0f/480.0f;
146     pos.x = 480.0f/640.0f;
147     ddim.x = dim.x*2.2f;
148     pev = pw->CreateEditValue(pos, ddim, 0, EVENT_INTERFACE_JOYSTICK_DEADZONE);
149     pev->SetState(STATE_SHADOW);
150     pev->SetType(EVT_100);
151     pev->SetMinValue(0);
152     pev->SetMaxValue(1);
153     pev->SetStepValue(0.01);
154 
155     ddim.x = dim.x*6;
156     ddim.y = dim.y*1;
157     pos.x = ox+sx*10;
158     pos.y = oy+sy*2;
159     pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_KDEF);
160     pb->SetState(STATE_SHADOW);
161 
162     UpdateSetupButtons();
163 }
164 
EventProcess(const Event & event)165 bool CScreenSetupControls::EventProcess(const Event &event)
166 {
167     if (!CScreenSetup::EventProcess(event)) return false;
168 
169     switch( event.type )
170     {
171         case EVENT_INTERFACE_KSCROLL:
172             UpdateKey();
173             break;
174 
175         case EVENT_INTERFACE_KDEF:
176             m_input->SetDefaultInputBindings();
177             UpdateKey();
178             break;
179 
180         case EVENT_INTERFACE_JOYSTICK_X_INVERT:
181         case EVENT_INTERFACE_JOYSTICK_Y_INVERT:
182         case EVENT_INTERFACE_JOYSTICK_Z_INVERT:
183         case EVENT_INTERFACE_JOYSTICK_CAM_X_INVERT:
184         case EVENT_INTERFACE_JOYSTICK_CAM_Y_INVERT:
185         case EVENT_INTERFACE_JOYSTICK_CAM_Z_INVERT:
186             ToggleJoystickInvert(event.type);
187             ChangeSetupButtons();
188             UpdateSetupButtons();
189             break;
190 
191         case EVENT_INTERFACE_JOYSTICK:
192         case EVENT_INTERFACE_JOYSTICK_X:
193         case EVENT_INTERFACE_JOYSTICK_Y:
194         case EVENT_INTERFACE_JOYSTICK_Z:
195         case EVENT_INTERFACE_JOYSTICK_CAM_X:
196         case EVENT_INTERFACE_JOYSTICK_CAM_Y:
197         case EVENT_INTERFACE_JOYSTICK_CAM_Z:
198         case EVENT_INTERFACE_JOYSTICK_DEADZONE:
199             ChangeSetupButtons();
200             UpdateSetupButtons();
201             break;
202 
203         default:
204             if (event.type >= EVENT_INTERFACE_KEY && event.type <= EVENT_INTERFACE_KEY_END)
205             {
206                 ChangeKey(event.type);
207                 UpdateKey();
208                 break;
209             }
210             return true;
211     }
212     return false;
213 }
214 
ChangeSetupButtons()215 void CScreenSetupControls::ChangeSetupButtons()
216 {
217     CWindow*    pw;
218     CList*      pli;
219     CEditValue* pev;
220     CCheck* pc;
221 
222     pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
223     if ( pw == nullptr )  return;
224 
225     pli = static_cast<CList*>(pw->SearchControl(EVENT_INTERFACE_JOYSTICK));
226     if ( pli != nullptr )
227     {
228         if (pli->GetSelect() > 0)
229         {
230             m_app->SetJoystickEnabled(false);
231             m_app->ChangeJoystick(m_app->GetJoystickList().at(pli->GetSelect()-1));
232             m_app->SetJoystickEnabled(true);
233         }
234         else
235         {
236             m_app->SetJoystickEnabled(false);
237         }
238     }
239 
240     auto HandleJoystickControls = [&](JoyAxisSlot joyAxis, EventType bindingControl, EventType invertControl)
241     {
242         if (nullptr != (pev = static_cast<CEditValue*>(pw->SearchControl(bindingControl))))
243         {
244             JoyAxisBinding binding = m_input->GetJoyAxisBinding(joyAxis);
245             binding.axis = static_cast<int>(round(pev->GetValue()));
246             m_input->SetJoyAxisBinding(joyAxis, binding);
247         }
248         if (nullptr != (pc = static_cast<CCheck*>(pw->SearchControl(invertControl))))
249         {
250             JoyAxisBinding binding = m_input->GetJoyAxisBinding(joyAxis);
251             binding.invert = pc->TestState(STATE_CHECK);
252             m_input->SetJoyAxisBinding(joyAxis, binding);
253         }
254     };
255     HandleJoystickControls(JOY_AXIS_SLOT_X, EVENT_INTERFACE_JOYSTICK_X, EVENT_INTERFACE_JOYSTICK_X_INVERT);
256     HandleJoystickControls(JOY_AXIS_SLOT_Y, EVENT_INTERFACE_JOYSTICK_Y, EVENT_INTERFACE_JOYSTICK_Y_INVERT);
257     HandleJoystickControls(JOY_AXIS_SLOT_Z, EVENT_INTERFACE_JOYSTICK_Z, EVENT_INTERFACE_JOYSTICK_Z_INVERT);
258     HandleJoystickControls(JOY_AXIS_SLOT_CAM_X, EVENT_INTERFACE_JOYSTICK_CAM_X, EVENT_INTERFACE_JOYSTICK_CAM_X_INVERT);
259     HandleJoystickControls(JOY_AXIS_SLOT_CAM_Y, EVENT_INTERFACE_JOYSTICK_CAM_Y, EVENT_INTERFACE_JOYSTICK_CAM_Y_INVERT);
260     HandleJoystickControls(JOY_AXIS_SLOT_CAM_Z, EVENT_INTERFACE_JOYSTICK_CAM_Z, EVENT_INTERFACE_JOYSTICK_CAM_Z_INVERT);
261 
262     if (nullptr != (pev = static_cast<CEditValue*>(pw->SearchControl(EVENT_INTERFACE_JOYSTICK_DEADZONE))))
263     {
264         m_input->SetJoystickDeadzone(pev->GetValue());
265     }
266 }
267 
ToggleJoystickInvert(EventType type)268 void CScreenSetupControls::ToggleJoystickInvert(EventType type)
269 {
270     CWindow* pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
271     if (pw == nullptr) return;
272 
273     CCheck* pc = static_cast<CCheck*>(pw->SearchControl(type));
274     if (pc == nullptr) return;
275 
276     pc->SetState(STATE_CHECK, !pc->TestState(STATE_CHECK));
277 }
278 
279 // Updates the buttons during the setup phase.
280 
UpdateSetupButtons()281 void CScreenSetupControls::UpdateSetupButtons()
282 {
283     CWindow* pw;
284     CList* pli;
285     CEditValue* pev;
286     CCheck* pc;
287 
288     pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
289     if (pw == nullptr) return;
290 
291     pli = static_cast<CList*>(pw->SearchControl(EVENT_INTERFACE_JOYSTICK));
292     if (pli != nullptr)
293     {
294         pli->Flush();
295         pli->SetItemName(0, "[No joystick]");
296         auto joysticks = m_app->GetJoystickList();
297         for (unsigned int i = 0; i < joysticks.size(); i++)
298         {
299             pli->SetItemName(1 + i, joysticks[i].name);
300         }
301         pli->SetSelect(m_app->GetJoystickEnabled() ? m_app->GetJoystick().index + 1 : 0);
302     }
303 
304     auto HandleJoystickControls = [&](JoyAxisSlot joyAxis, EventType bindingControl, EventType invertControl)
305     {
306         if (nullptr != (pev = static_cast<CEditValue*>(pw->SearchControl(bindingControl))))
307         {
308             pev->SetState(STATE_ENABLE, m_app->GetJoystickEnabled());
309             pev->SetMaxValue(m_app->GetJoystick().axisCount-1);
310             pev->SetValue(m_input->GetJoyAxisBinding(joyAxis).axis);
311         }
312         if (nullptr != (pc = static_cast<CCheck*>(pw->SearchControl(invertControl))))
313         {
314             pc->SetState(STATE_ENABLE, m_app->GetJoystickEnabled());
315             pc->SetState(STATE_CHECK, m_input->GetJoyAxisBinding(joyAxis).invert);
316         }
317     };
318     HandleJoystickControls(JOY_AXIS_SLOT_X, EVENT_INTERFACE_JOYSTICK_X, EVENT_INTERFACE_JOYSTICK_X_INVERT);
319     HandleJoystickControls(JOY_AXIS_SLOT_Y, EVENT_INTERFACE_JOYSTICK_Y, EVENT_INTERFACE_JOYSTICK_Y_INVERT);
320     HandleJoystickControls(JOY_AXIS_SLOT_Z, EVENT_INTERFACE_JOYSTICK_Z, EVENT_INTERFACE_JOYSTICK_Z_INVERT);
321     HandleJoystickControls(JOY_AXIS_SLOT_CAM_X, EVENT_INTERFACE_JOYSTICK_CAM_X, EVENT_INTERFACE_JOYSTICK_CAM_X_INVERT);
322     HandleJoystickControls(JOY_AXIS_SLOT_CAM_Y, EVENT_INTERFACE_JOYSTICK_CAM_Y, EVENT_INTERFACE_JOYSTICK_CAM_Y_INVERT);
323     HandleJoystickControls(JOY_AXIS_SLOT_CAM_Z, EVENT_INTERFACE_JOYSTICK_CAM_Z, EVENT_INTERFACE_JOYSTICK_CAM_Z_INVERT);
324 
325     if (nullptr != (pev = static_cast<CEditValue*>(pw->SearchControl(EVENT_INTERFACE_JOYSTICK_DEADZONE))))
326     {
327         pev->SetState(STATE_ENABLE, m_app->GetJoystickEnabled());
328         pev->SetValue(m_input->GetJoystickDeadzone());
329     }
330 }
331 
332 // Updates the list of keys.
333 
UpdateKey()334 void CScreenSetupControls::UpdateKey()
335 {
336     CWindow* pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
337     if (pw == nullptr) return;
338 
339     CScroll* ps = static_cast<CScroll*>(pw->SearchControl(EVENT_INTERFACE_KSCROLL));
340     if (ps == nullptr) return;
341 
342     int first = static_cast<int>(ps->GetVisibleValue()*(INPUT_SLOT_MAX-KEY_VISIBLE));
343 
344     for (int i = 0; i < INPUT_SLOT_MAX; i++)
345         pw->DeleteControl(static_cast<EventType>(EVENT_INTERFACE_KEY+i));
346 
347     Math::Point dim;
348     dim.x = 250.0f/640.0f;
349     dim.y =  20.0f/480.0f;
350     Math::Point pos;
351     pos.x = 110.0f/640.0f;
352     pos.y = 128.0f/480.0f + dim.y*(KEY_VISIBLE-1);
353     for (int i = 0; i < KEY_VISIBLE; i++)
354     {
355         pw->CreateKey(pos, dim, -1, static_cast<EventType>(EVENT_INTERFACE_KEY+first+i));
356         CKey* pk = static_cast<CKey*>(pw->SearchControl(static_cast<EventType>(EVENT_INTERFACE_KEY+first+i)));
357         if (pk == nullptr) break;
358 
359         pk->SetBinding(m_input->GetInputBinding(static_cast<InputSlot>(first+i)));
360         pos.y -= dim.y;
361     }
362 }
363 
364 // Change a key.
365 
ChangeKey(EventType event)366 void CScreenSetupControls::ChangeKey(EventType event)
367 {
368     CWindow* pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
369     if (pw == nullptr) return;
370 
371     CScroll* ps = static_cast<CScroll*>(pw->SearchControl(EVENT_INTERFACE_KSCROLL));
372     if (ps == nullptr) return;
373 
374     for (int i = 0; i < INPUT_SLOT_MAX; i++)
375     {
376         if ( EVENT_INTERFACE_KEY+i == event )
377         {
378             CKey* pk = static_cast<CKey*>(pw->SearchControl(static_cast<EventType>(EVENT_INTERFACE_KEY+i)));
379             if (pk == nullptr) break;
380 
381             m_input->SetInputBinding(static_cast<InputSlot>(i), pk->GetBinding());
382         }
383     }
384 }
385 
386 } // namespace Ui
387