README: explain further musl-specific tweaks
[rofl0r-df-libgraphics.git] / g_src / KeybindingScreen.cpp
blob4f64bd21757faf1fef90cb809b7d43c3e17c2372
1 #ifdef __APPLE__
2 # include "osx_messagebox.h"
3 #elif defined(unix) && defined(HAVE_GTK2)
4 # include <gtk/gtk.h>
5 #endif
7 #ifdef WIN32
9 #ifndef INTEGER_TYPES
10 #define INTEGER_TYPES
11 typedef short int16_t;
12 typedef int int32_t;
13 typedef long long int64_t;
14 typedef unsigned short uint16_t;
15 typedef unsigned int uint32_t;
16 typedef unsigned long long uint64_t;
17 #endif
19 typedef int32_t VIndex;
20 typedef int32_t Ordinal;
22 #endif
24 #include "graphics.h"
25 #include "init.h"
26 #include "keybindings.h"
27 #include "KeybindingScreen.h"
29 #include <list>
30 #include <map>
31 #include <iostream>
32 #include <sstream>
33 #include <ctype.h>
35 using namespace std;
37 struct BindingGroup {
38 string name;
39 InterfaceKey start, end;
42 const BindingGroup groups[] = {
43 {"General" , INTERFACEKEY_NONE, WORLDKEY_START-1},
44 {"World" , WORLDKEY_START, ADVENTURERKEY_START-1},
45 {"Adventurer" , ADVENTURERKEY_START, EMBARKKEY_START-1},
46 {"Dwarf mode" , DWARFMAINKEY_START, MILITIAKEY_START-1},
47 {"Embark" , EMBARKKEY_START, BUILDINGKEY_START-1},
48 {"Building" , BUILDINGKEY_START, WORKSHOPKEY_START-1},
49 {"Workshop" , WORKSHOPKEY_START, PILEZONEKEY_START-1},
50 {"Pilezone" , PILEZONEKEY_START, STOCKORDERKEY_START-1},
51 {"Stockorder" , STOCKORDERKEY_START, DWARFMAINKEY_START-1},
52 {"Militia" , MILITIAKEY_START, INTERFACEKEY_STRING_A000-1},
53 {"Text entry" , INTERFACEKEY_STRING_A000, INTERFACEKEY_STRING_A255}
56 KeybindingScreen::KeybindingScreen() {
57 gview.addscreen(this, INTERFACE_PUSH_AT_BACK, NULL); // HACK
58 mode = mode_main;
60 main.add("Macros", sel_macros);
61 for (int i = 0; i < ARRSZ(groups); i++)
62 main.set(i+2, groups[i].name, sel_first_group + i);
63 main.set(ARRSZ(groups)+3, "Save and exit", sel_save_exit);
64 main.add("Exit, discard changes when DF quits", sel_just_exit);
65 enabler.flag |= ENABLERFLAG_RENDER;
68 void KeybindingScreen::feed(set<InterfaceKey> &input) {
69 enabler.flag|=ENABLERFLAG_RENDER;
70 if (input.count(INTERFACEKEY_KEYBINDING_COMPLETE)) {
71 list<RegisteredKey> keys = enabler.getRegisteredKey();
72 if (keys.size() == 0) {
73 puts("No keys registered ?!");
74 mode = mode_keyR;
75 } else {
76 keyRegister.clear();
77 list<RegisteredKey> keys = enabler.getRegisteredKey();
78 for (list<RegisteredKey>::iterator it = keys.begin(); it != keys.end(); ++it) {
79 string display;
80 switch (it->type) {
81 case type_button: display = "Mouse button: "; break;
82 case type_key: display = "By position: "; break;
83 case type_unicode: display = "By letter: "; break;
85 keyRegister.add(display + it->display, it->type);
88 } else if (input.count(INTERFACEKEY_STANDARDSCROLL_PAGEUP) ||
89 input.count(INTERFACEKEY_STANDARDSCROLL_PAGEDOWN) ||
90 input.count(INTERFACEKEY_STANDARDSCROLL_UP) ||
91 input.count(INTERFACEKEY_STANDARDSCROLL_DOWN)) {
92 switch (mode) {
93 case mode_main: main.feed(input); break;
94 case mode_keyL: keyL.feed(input); reset_keyR(); break;
95 case mode_keyR: keyR.feed(input); break;
96 case mode_macro: macro.feed(input); break;
97 case mode_register: keyRegister.feed(input); break;
99 } else if (mode == mode_keyL && input.count(INTERFACEKEY_STANDARDSCROLL_RIGHT))
100 mode = mode_keyR;
101 else if (mode == mode_main && input.count(INTERFACEKEY_STANDARDSCROLL_RIGHT)) {
102 if (main.get_selection() == sel_macros) enter_macros();
103 if (main.get_selection() >= sel_first_group)
104 enter_key(main.get_selection() - sel_first_group);
105 } else if (mode == mode_keyR && input.count(INTERFACEKEY_STANDARDSCROLL_LEFT))
106 mode = mode_keyL;
107 else if ((mode == mode_keyL || mode == mode_macro) && input.count(INTERFACEKEY_STANDARDSCROLL_LEFT))
108 mode = mode_main;
109 else if (input.count(INTERFACEKEY_STRING_A000)) { // Backspace: Delete something.
110 switch (mode) {
111 case mode_macro:
112 if (macro.get_selection() != "") {
113 enabler.delete_macro(macro.get_selection());
114 macro.del_selection();
115 if (!macro.size())
116 macro.add("No macros!", "");
118 break;
119 case mode_keyR:
120 keyR_selector sel = keyR.get_selection();
121 if (sel.sel == sel_event) {
122 enabler.remove_key(keyL.get_selection(), sel.event);
123 reset_keyR();
125 break;
127 } else if (input.count(INTERFACEKEY_SELECT)) {
128 switch (mode) {
129 case mode_main:
130 if (main.get_selection() == sel_macros) { // Macros
131 enter_macros();
132 } else if (main.get_selection() == sel_save_exit) { // Save and exit
133 enabler.save_keybindings();
134 breakdownlevel = INTERFACE_BREAKDOWN_STOPSCREEN;
135 return;
136 } else if (main.get_selection() == sel_just_exit) { // Just exit
137 breakdownlevel = INTERFACE_BREAKDOWN_STOPSCREEN;
138 return;
139 } else { // Some key-binding group
140 enter_key(main.get_selection() - sel_first_group);
142 break;
143 case mode_keyR: {
144 InterfaceKey key = keyL.get_selection();
145 switch (keyR.get_selection().sel) {
146 case sel_add:
147 enabler.register_key();
148 mode = mode_register;
149 break;
150 case sel_rep_none:
151 enabler.key_repeat(key, REPEAT_NOT);
152 reset_keyR();
153 break;
154 case sel_rep_slow:
155 enabler.key_repeat(key, REPEAT_SLOW);
156 reset_keyR();
157 break;
158 case sel_rep_fast:
159 enabler.key_repeat(key, REPEAT_FAST);
160 reset_keyR();
161 break;
163 break;
164 case mode_register:
165 enabler.bindRegisteredKey(keyRegister.get_selection(), keyL.get_selection());
166 mode = mode_keyR;
167 reset_keyR();
168 break;
170 } else if (input.count(INTERFACEKEY_LEAVESCREEN) || input.count(INTERFACEKEY_OPTIONS)) {
171 if (mode == mode_register)
172 mode = mode_keyR;
173 else
174 mode = mode_main;
178 void KeybindingScreen::logic() {
179 if (mode == mode_register)
180 enabler.flag|=ENABLERFLAG_RENDER;
183 void KeybindingScreen::enter_macros() {
184 mode = mode_macro;
185 macro.clear();
186 list<string> macros = enabler.list_macros();
187 for (list<string>::iterator it = macros.begin(); it != macros.end(); ++it)
188 macro.add(*it, *it);
189 if (!macros.size())
190 macro.add("No macros!", "");
193 void KeybindingScreen::enter_key(int group) {
194 mode = mode_keyL;
195 keyL.clear();
196 for (InterfaceKey i = groups[group].start; i <= groups[group].end; i++) {
197 if (i != INTERFACEKEY_NONE)
198 keyL.add(enabler.GetBindingTextDisplay(i), i);
200 reset_keyR();
203 void KeybindingScreen::reset_keyR() {
204 int lastpos = keyR.get_pos();
205 keyR.clear();
206 struct keyR_selector sel;
207 sel.sel = sel_add;
208 keyR.add("Add binding", sel);
209 InterfaceKey key = keyL.get_selection();
210 list<EventMatch> matchers = enabler.list_keys(key);
211 Repeat rep = enabler.key_repeat(key);
212 sel.sel = sel_rep_none;
213 keyR.set(2, "Don't repeat", sel);
214 if (rep == REPEAT_NOT) keyR.set_color(2, 4, 0);
215 sel.sel = sel_rep_slow;
216 keyR.set(3, "Delayed repeat", sel);
217 if (rep == REPEAT_SLOW) keyR.set_color(3, 4, 0);
218 sel.sel = sel_rep_fast;
219 keyR.set(4, "Immediate repeat", sel);
220 if (rep == REPEAT_FAST) keyR.set_color(4, 4, 0);
221 int i = 6;
222 for (list<EventMatch>::iterator it = matchers.begin(); it != matchers.end(); ++it, ++i) {
223 ostringstream desc;
224 switch (it->type) {
225 case type_unicode:
226 desc << "By letter: ";
227 if (it->unicode < 256 && isgraph(it->unicode)) // Is it printable?
228 desc << (char)it->unicode;
229 else
230 desc << "U+" << hex << uppercase << it->unicode;
231 break;
232 case type_key:
233 desc << "By position: " << translate_mod(it->mod) << sdlNames.left[it->key];
234 break;
235 case type_button:
236 desc << "Mouse: " << (int)it->button;
237 break;
239 sel.sel = sel_event;
240 sel.event = *it;
241 keyR.set(i, desc.str(), sel);
243 keyR.set_pos(lastpos);
246 void KeybindingScreen::render_macro() {
247 drawborder("Macros");
248 gps.locate(3, 3);
249 gps.changecolor(4,0,1);
250 gps.addst("Select a macro, then press " + enabler.GetKeyDisplay(INTERFACEKEY_STRING_A000) + " to delete.");
251 macro.render(6, init.display.grid_x-2, 5, init.display.grid_y-2);
254 void KeybindingScreen::render_key() {
255 if (enabler.is_registering()) {
256 gps.changecolor(4,0,1);
257 drawborder("Keybinding - currently registering new key");
258 } else
259 drawborder("Keybinding");
260 gps.locate(3, 6);
261 gps.changecolor(4,0,1);
262 gps.addst("Select a binding, then press " + enabler.GetKeyDisplay(INTERFACEKEY_STRING_A000) + " to delete.");
263 keyL.render(6, init.display.grid_x/2 - 1, 5, init.display.grid_y-2);
264 if (mode == mode_keyL || mode == mode_register)
265 keyR.bleach(true);
266 else
267 keyR.bleach(false);
268 keyR.render(init.display.grid_x/2 + 1, init.display.grid_x-2, 5, init.display.grid_y-2);
271 void KeybindingScreen::render_register() {
272 int x1 = init.display.grid_x / 2 - 20,
273 x2 = init.display.grid_x / 2 + 20,
274 y1 = init.display.grid_y / 2 - 1,
275 y2 = init.display.grid_y / 2 + 1;
276 if (!enabler.is_registering()) {
277 y2 = y1 + keyRegister.size() + 1;
279 gps.erasescreen_rect(x1, x2, y1, y2);
280 gps.changecolor(1,1,1);
281 for (int x = x1; x <= x2; x++) {
282 gps.locate(y1, x); gps.addchar(' ');
283 gps.locate(y2, x); gps.addchar(' ');
285 for (int y = y1 + 1; y < y2; y++) {
286 gps.locate(y, x1); gps.addchar(' ');
287 gps.locate(y, x2); gps.addchar(' ');
289 if (enabler.is_registering()) {
290 gps.changecolor(7,0,1);
291 gps.locate(y1+1, x1+2);
292 gps.addst(translate_mod(getModState()));
293 } else {
294 keyRegister.render(x1+1, x2-1, y1+1, y2-1);
295 gps.locate(y2, x1+2);
296 gps.changecolor(7,1,1);
297 gps.addst("Select binding, or press space to abort");
301 // Render the main menu
302 void KeybindingScreen::render_main() {
303 drawborder("Key binding & macro center");
304 main.render(6, init.display.grid_x - 3, 3, init.display.grid_y - 4);
307 void KeybindingScreen::render() {
308 switch(mode) {
309 case mode_main: render_main(); break;
310 case mode_keyL: case mode_keyR: render_key(); break;
311 case mode_macro: render_macro(); break;
312 case mode_register:
313 render_key();
314 render_register();
315 break;
319 void KeybindingScreen::help() {
323 MacroScreenLoad::MacroScreenLoad() {
324 list<string> macros = enabler.list_macros();
325 width = 10;
326 if (!macros.size()) {
327 menu.add("No macros!", "");
328 height = 1;
329 } else
330 height = macros.size();
332 for (list<string>::iterator it = macros.begin(); it != macros.end(); ++it) {
333 if (it->length() > width) width = it->length();
334 menu.add(*it, *it);
336 enabler.flag |= ENABLERFLAG_RENDER;
337 // render();
338 // gps.renewscreen();
341 void MacroScreenLoad::feed(set<InterfaceKey> &input) {
342 enabler.flag|=ENABLERFLAG_RENDER;
343 if (input.count(INTERFACEKEY_SELECT)) {
344 string id = menu.get_selection();
345 if (id != "") enabler.load_macro(id);
346 breakdownlevel = INTERFACE_BREAKDOWN_STOPSCREEN;
347 return;
348 } else if (input.count(INTERFACEKEY_LEAVESCREEN)) {
349 breakdownlevel = INTERFACE_BREAKDOWN_STOPSCREEN;
350 return;
351 } else {
352 menu.feed(input);
354 if (input.count(INTERFACEKEY_OPTIONS)) {
355 breakdownlevel = INTERFACE_BREAKDOWN_STOPSCREEN;
359 void MacroScreenLoad::logic() {
362 void MacroScreenLoad::render() {
363 if (parent) parent->render();
364 const int x1 = MAX(init.display.grid_x/2 - ((width + 2) / 2), 0);
365 const int x2 = MIN(x1+width+1, init.display.grid_x-1);
366 const int y1 = MAX(init.display.grid_y/2 - ((height + 2) / 2), 0);
367 const int y2 = MIN(y1 + height + 1, init.display.grid_y-1);
368 gps.changecolor(0,3,1);
369 gps.draw_border(x1, x2, y1, y2);
370 menu.render(x1+1, x2-1, y1+1, y2-1);
371 // gps.renewscreen();
374 MacroScreenSave::MacroScreenSave() {
375 enabler.flag |= ENABLERFLAG_RENDER;
378 void MacroScreenSave::logic() {
381 void MacroScreenSave::feed(set<InterfaceKey> &input) {
382 enabler.flag|=ENABLERFLAG_RENDER;
383 id.feed(input);
384 if (input.count(INTERFACEKEY_SELECT)) {
385 string n = id.get_text();
386 if (n.length())
387 enabler.save_macro(n);
388 breakdownlevel = INTERFACE_BREAKDOWN_STOPSCREEN;
389 return;
391 if (input.count(INTERFACEKEY_OPTIONS)) {
392 breakdownlevel = INTERFACE_BREAKDOWN_STOPSCREEN;
396 void MacroScreenSave::render() {
397 if (parent) parent->render();
398 const int x1 = 3,
399 x2 = init.display.grid_x-4,
400 y1 = init.display.grid_y/2-1,
401 y2 = init.display.grid_y/2+1;
402 gps.changecolor(0,3,1);
403 gps.draw_border(x1, x2, y1, y2);
404 id.render(x1+1,x2-1,y1+1,y2-1);
405 // gps.renewscreen();