]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/menu/xonotic/keybinder.c
Merge branch 'master' into terencehill/arena_stuff
[xonotic/xonotic-data.pk3dir.git] / qcsrc / menu / xonotic / keybinder.c
1 #ifdef INTERFACE
2 CLASS(XonoticKeyBinder) EXTENDS(XonoticListBox)
3         METHOD(XonoticKeyBinder, configureXonoticKeyBinder, void(entity))
4         ATTRIB(XonoticKeyBinder, rowsPerItem, float, 1)
5         METHOD(XonoticKeyBinder, drawListBoxItem, void(entity, float, vector, float))
6         METHOD(XonoticKeyBinder, clickListBoxItem, void(entity, float, vector))
7         METHOD(XonoticKeyBinder, resizeNotify, void(entity, vector, vector, vector, vector))
8         METHOD(XonoticKeyBinder, setSelected, void(entity, float))
9         METHOD(XonoticKeyBinder, keyDown, float(entity, float, float, float))
10         METHOD(XonoticKeyBinder, keyGrabbed, void(entity, float, float))
11
12         ATTRIB(XonoticKeyBinder, realFontSize, vector, '0 0 0')
13         ATTRIB(XonoticKeyBinder, realUpperMargin, float, 0)
14         ATTRIB(XonoticKeyBinder, columnFunctionOrigin, float, 0)
15         ATTRIB(XonoticKeyBinder, columnFunctionSize, float, 0)
16         ATTRIB(XonoticKeyBinder, columnKeysOrigin, float, 0)
17         ATTRIB(XonoticKeyBinder, columnKeysSize, float, 0)
18
19         ATTRIB(XonoticKeyBinder, lastClickedKey, float, -1)
20         ATTRIB(XonoticKeyBinder, lastClickedTime, float, 0)
21         ATTRIB(XonoticKeyBinder, previouslySelected, float, -1)
22         ATTRIB(XonoticKeyBinder, inMouseHandler, float, 0)
23         ATTRIB(XonoticKeyBinder, userbindEditButton, entity, NULL)
24         ATTRIB(XonoticKeyBinder, keyGrabButton, entity, NULL)
25         ATTRIB(XonoticKeyBinder, userbindEditDialog, entity, NULL)
26         METHOD(XonoticKeyBinder, editUserbind, void(entity, string, string, string))
27 ENDCLASS(XonoticKeyBinder)
28 entity makeXonoticKeyBinder();
29 void KeyBinder_Bind_Change(entity btn, entity me);
30 void KeyBinder_Bind_Clear(entity btn, entity me);
31 void KeyBinder_Bind_Edit(entity btn, entity me);
32 #endif
33
34 #ifdef IMPLEMENTATION
35
36 string KEY_NOT_BOUND_CMD = "// not bound";
37
38 #define MAX_KEYS_PER_FUNCTION 2
39 #define MAX_KEYBINDS 256
40 string Xonotic_KeyBinds_Functions[MAX_KEYBINDS];
41 string Xonotic_KeyBinds_Descriptions[MAX_KEYBINDS];
42 var float Xonotic_KeyBinds_Count = -1;
43
44 void Xonotic_KeyBinds_Read()
45 {
46         float fh;
47         string s;
48
49         Xonotic_KeyBinds_Count = 0;
50         fh = fopen(language_filename("keybinds.txt"), FILE_READ);
51         if(fh < 0)
52                 return;
53         while((s = fgets(fh)))
54         {
55                 if(tokenize_console(s) != 2)
56                         continue;
57                 Xonotic_KeyBinds_Functions[Xonotic_KeyBinds_Count] = strzone(argv(0));
58                 Xonotic_KeyBinds_Descriptions[Xonotic_KeyBinds_Count] = strzone(argv(1));
59                 ++Xonotic_KeyBinds_Count;
60                 if(Xonotic_KeyBinds_Count >= MAX_KEYBINDS)
61                         break;
62         }
63         fclose(fh);
64 }
65
66 entity makeXonoticKeyBinder()
67 {
68         entity me;
69         me = spawnXonoticKeyBinder();
70         me.configureXonoticKeyBinder(me);
71         return me;
72 }
73 void replace_bind(string from, string to)
74 {
75         float n, j, k;
76         n = tokenize(findkeysforcommand(from)); // uses '...' strings
77         for(j = 0; j < n; ++j)
78         {
79                 k = stof(argv(j));
80                 if(k != -1)
81                         localcmd("\nbind \"", keynumtostring(k), "\" \"", to, "\"\n");
82         }
83 }
84 void XonoticKeyBinder_configureXonoticKeyBinder(entity me)
85 {
86         me.configureXonoticListBox(me);
87         if(Xonotic_KeyBinds_Count < 0)
88                 Xonotic_KeyBinds_Read();
89         me.nItems = Xonotic_KeyBinds_Count;
90         me.setSelected(me, 0);
91
92         // TEMP: Xonotic 0.1 to later
93         replace_bind("impulse 1", "weapon_group_1");
94         replace_bind("impulse 2", "weapon_group_2");
95         replace_bind("impulse 3", "weapon_group_3");
96         replace_bind("impulse 4", "weapon_group_4");
97         replace_bind("impulse 5", "weapon_group_5");
98         replace_bind("impulse 6", "weapon_group_6");
99         replace_bind("impulse 7", "weapon_group_7");
100         replace_bind("impulse 8", "weapon_group_8");
101         replace_bind("impulse 9", "weapon_group_9");
102         replace_bind("impulse 14", "weapon_group_0");
103 }
104 void XonoticKeyBinder_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
105 {
106         SUPER(XonoticKeyBinder).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
107
108         me.realFontSize_y = me.fontSize / (absSize_y * me.itemHeight);
109         me.realFontSize_x = me.fontSize / (absSize_x * (1 - me.controlWidth));
110         me.realUpperMargin = 0.5 * (1 - me.realFontSize_y);
111
112         me.columnFunctionOrigin = 0;
113         me.columnKeysSize = me.realFontSize_x * 12;
114         me.columnFunctionSize = 1 - me.columnKeysSize - 2 * me.realFontSize_x;
115         me.columnKeysOrigin = me.columnFunctionOrigin + me.columnFunctionSize + me.realFontSize_x;
116
117         if(me.userbindEditButton)
118                 me.userbindEditButton.disabled = (substring(Xonotic_KeyBinds_Descriptions[me.selectedItem], 0, 1) != "$");
119 }
120 void KeyBinder_Bind_Change(entity btn, entity me)
121 {
122         string func;
123
124         func = Xonotic_KeyBinds_Functions[me.selectedItem];
125         if(func == "")
126                 return;
127
128         me.keyGrabButton.forcePressed = 1;
129         keyGrabber = me;
130 }
131 void XonoticKeyBinder_keyGrabbed(entity me, float key, float ascii)
132 {
133         float n, j, k, nvalid;
134         string func;
135
136         me.keyGrabButton.forcePressed = 0;
137         if(key == K_ESCAPE)
138                 return;
139
140         func = Xonotic_KeyBinds_Functions[me.selectedItem];
141         if(func == "")
142                 return;
143
144         n = tokenize(findkeysforcommand(func)); // uses '...' strings
145         nvalid = 0;
146         for(j = 0; j < n; ++j)
147         {
148                 k = stof(argv(j));
149                 if(k != -1)
150                         ++nvalid;
151         }
152         if(nvalid >= MAX_KEYS_PER_FUNCTION)
153         {
154                 for(j = 0; j < n; ++j)
155                 {
156                         k = stof(argv(j));
157                         if(k != -1)
158                                 //localcmd("\nunbind \"", keynumtostring(k), "\"\n");
159                                 localcmd("\nbind \"", keynumtostring(k), "\" \"", KEY_NOT_BOUND_CMD, "\"\n");
160                 }
161         }
162         localcmd("\nbind \"", keynumtostring(key), "\" \"", func, "\"\n");
163         localcmd("-zoom\n"); // to make sure we aren't in togglezoom'd state
164 }
165 void XonoticKeyBinder_editUserbind(entity me, string theName, string theCommandPress, string theCommandRelease)
166 {
167         string func, descr;
168
169         if(!me.userbindEditDialog)
170                 return;
171         
172         func = Xonotic_KeyBinds_Functions[me.selectedItem];
173         if(func == "")
174                 return;
175         
176         descr = Xonotic_KeyBinds_Descriptions[me.selectedItem];
177         if(substring(descr, 0, 1) != "$")
178                 return;
179         descr = substring(descr, 1, strlen(descr) - 1);
180
181         // Hooray! It IS a user bind!
182         cvar_set(strcat(descr, "_description"), theName);
183         cvar_set(strcat(descr, "_press"), theCommandPress);
184         cvar_set(strcat(descr, "_release"), theCommandRelease);
185 }
186 void KeyBinder_Bind_Edit(entity btn, entity me)
187 {
188         string func, descr;
189
190         if(!me.userbindEditDialog)
191                 return;
192         
193         func = Xonotic_KeyBinds_Functions[me.selectedItem];
194         if(func == "")
195                 return;
196         
197         descr = Xonotic_KeyBinds_Descriptions[me.selectedItem];
198         if(substring(descr, 0, 1) != "$")
199                 return;
200         descr = substring(descr, 1, strlen(descr) - 1);
201
202         // Hooray! It IS a user bind!
203         me.userbindEditDialog.loadUserBind(me.userbindEditDialog, cvar_string(strcat(descr, "_description")), cvar_string(strcat(descr, "_press")), cvar_string(strcat(descr, "_release")));
204
205         DialogOpenButton_Click(btn, me.userbindEditDialog);
206 }
207 void KeyBinder_Bind_Clear(entity btn, entity me)
208 {
209         float n, j, k;
210         string func;
211
212         func = Xonotic_KeyBinds_Functions[me.selectedItem];
213         if(func == "")
214                 return;
215
216         n = tokenize(findkeysforcommand(func)); // uses '...' strings
217         for(j = 0; j < n; ++j)
218         {
219                 k = stof(argv(j));
220                 if(k != -1)
221                         //localcmd("\nunbind \"", keynumtostring(k), "\"\n");
222                         localcmd("\nbind \"", keynumtostring(k), "\" \"", KEY_NOT_BOUND_CMD, "\"\n");
223         }
224         localcmd("-zoom\n"); // to make sure we aren't in togglezoom'd state
225 }
226 void XonoticKeyBinder_clickListBoxItem(entity me, float i, vector where)
227 {
228         if(i == me.lastClickedServer)
229                 if(time < me.lastClickedTime + 0.3)
230                 {
231                         // DOUBLE CLICK!
232                         KeyBinder_Bind_Change(NULL, me);
233                 }
234         me.lastClickedServer = i;
235         me.lastClickedTime = time;
236 }
237 void XonoticKeyBinder_setSelected(entity me, float i)
238 {
239         // handling of "unselectable" items
240         i = floor(0.5 + bound(0, i, me.nItems - 1));
241         if(me.pressed == 0 || me.pressed == 1) // keyboard or scrolling - skip unselectable items
242         {
243                 if(i > me.previouslySelected)
244                 {
245                         while((i < me.nItems - 1) && (Xonotic_KeyBinds_Functions[i] == ""))
246                                 ++i;
247                 }
248                 while((i > 0) && (Xonotic_KeyBinds_Functions[i] == ""))
249                         --i;
250                 while((i < me.nItems - 1) && (Xonotic_KeyBinds_Functions[i] == ""))
251                         ++i;
252         }
253         if(me.pressed == 3) // released the mouse - fall back to last valid item
254         {
255                 if(Xonotic_KeyBinds_Functions[i] == "")
256                         i = me.previouslySelected;
257         }
258         if(Xonotic_KeyBinds_Functions[i] != "")
259                 me.previouslySelected = i;
260         if(me.userbindEditButton)
261                 me.userbindEditButton.disabled = (substring(Xonotic_KeyBinds_Descriptions[i], 0, 1) != "$");
262         SUPER(XonoticKeyBinder).setSelected(me, i);
263 }
264 float XonoticKeyBinder_keyDown(entity me, float key, float ascii, float shift)
265 {
266         float r;
267         r = 1;
268         switch(key)
269         {
270                 case K_ENTER:
271                 case K_KP_ENTER:
272                 case K_SPACE:
273                         KeyBinder_Bind_Change(me, me);
274                         break;
275                 case K_DEL:
276                 case K_KP_DEL:
277                 case K_BACKSPACE:
278                         KeyBinder_Bind_Clear(me, me);
279                         break;
280                 default:
281                         r = SUPER(XonoticKeyBinder).keyDown(me, key, ascii, shift);
282                         break;
283         }
284         return r;
285 }
286 void XonoticKeyBinder_drawListBoxItem(entity me, float i, vector absSize, float isSelected)
287 {
288         string s;
289         float j, k, n;
290         vector theColor;
291         float theAlpha;
292         string func, descr;
293         float extraMargin;
294
295         descr = Xonotic_KeyBinds_Descriptions[i];
296         func = Xonotic_KeyBinds_Functions[i];
297
298         if(func == "")
299         {
300                 theAlpha = 1;
301                 theColor = SKINCOLOR_KEYGRABBER_TITLES;
302                 theAlpha = SKINALPHA_KEYGRABBER_TITLES;
303                 extraMargin = 0;
304         }
305         else
306         {
307                 if(isSelected)
308                 {
309                         if(keyGrabber == me)
310                                 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_WAITING, SKINALPHA_LISTBOX_WAITING);
311                         else
312                                 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
313                 }
314                 theAlpha = SKINALPHA_KEYGRABBER_KEYS;
315                 theColor = SKINCOLOR_KEYGRABBER_KEYS;
316                 extraMargin = me.realFontSize_x * 0.5;
317         }
318
319         if(substring(descr, 0, 1) == "$")
320         {
321                 s = substring(descr, 1, strlen(descr) - 1);
322                 descr = cvar_string(strcat(s, "_description"));
323                 if(descr == "")
324                         descr = s;
325                 if(cvar_string(strcat(s, "_press")) == "")
326                         if(cvar_string(strcat(s, "_release")) == "")
327                                 theAlpha *= SKINALPHA_DISABLED;
328         }
329
330         s = draw_TextShortenToWidth(descr, me.columnFunctionSize, 0, me.realFontSize);
331         draw_Text(me.realUpperMargin * eY + extraMargin * eX, s, me.realFontSize, theColor, theAlpha, 0);
332         if(func != "")
333         {
334                 n = tokenize(findkeysforcommand(func)); // uses '...' strings
335                 s = "";
336                 for(j = 0; j < n; ++j)
337                 {
338                         k = stof(argv(j));
339                         if(k != -1)
340                         {
341                                 if(s != "")
342                                         s = strcat(s, ", ");
343                                 s = strcat(s, keynumtostring(k));
344                         }
345                 }
346                 s = draw_TextShortenToWidth(s, me.columnKeysSize, 0, me.realFontSize);
347                 draw_CenterText(me.realUpperMargin * eY + (me.columnKeysOrigin + 0.5 * me.columnKeysSize) * eX, s, me.realFontSize, theColor, theAlpha, 0);
348         }
349 }
350 #endif