]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/menu-div0test/nexuiz/serverlist.c
make menu more keyboard aware; improve skinnability
[divverent/nexuiz.git] / data / qcsrc / menu-div0test / nexuiz / serverlist.c
1 #ifdef INTERFACE
2 CLASS(NexuizServerList) EXTENDS(NexuizListBox)
3         METHOD(NexuizServerList, configureNexuizServerList, void(entity))
4         ATTRIB(NexuizServerList, rowsPerItem, float, 1)
5         METHOD(NexuizServerList, draw, void(entity))
6         METHOD(NexuizServerList, drawListBoxItem, void(entity, float, vector, float))
7         METHOD(NexuizServerList, clickListBoxItem, void(entity, float, vector))
8         METHOD(NexuizServerList, resizeNotify, void(entity, vector, vector, vector, vector))
9         METHOD(NexuizServerList, keyDown, float(entity, float, float, float))
10
11         ATTRIB(NexuizServerList, realFontSize, vector, '0 0 0')
12         ATTRIB(NexuizServerList, realUpperMargin, float, 0)
13         ATTRIB(NexuizServerList, columnPingOrigin, float, 0)
14         ATTRIB(NexuizServerList, columnPingSize, float, 0)
15         ATTRIB(NexuizServerList, columnNameOrigin, float, 0)
16         ATTRIB(NexuizServerList, columnNameSize, float, 0)
17         ATTRIB(NexuizServerList, columnMapOrigin, float, 0)
18         ATTRIB(NexuizServerList, columnMapSize, float, 0)
19         ATTRIB(NexuizServerList, columnPlayersOrigin, float, 0)
20         ATTRIB(NexuizServerList, columnPlayersSize, float, 0)
21
22         ATTRIB(NexuizServerList, selectedServer, string, string_null) // to restore selected server when needed
23         METHOD(NexuizServerList, setSelected, void(entity, float))
24         METHOD(NexuizServerList, setSortOrder, void(entity, float, float))
25         ATTRIB(NexuizServerList, filterShowEmpty, float, 1)
26         ATTRIB(NexuizServerList, filterShowFull, float, 1)
27         ATTRIB(NexuizServerList, filterString, string, string_null)
28         ATTRIB(NexuizServerList, nextRefreshTime, float, 0)
29         METHOD(NexuizServerList, refreshServerList, void(entity, float)) // refresh mode: 0 = just reparametrize, 1 = send new requests, 2 = clear
30         ATTRIB(NexuizServerList, needsRefresh, float, 1)
31         METHOD(NexuizServerList, focusEnter, void(entity))
32         METHOD(NexuizServerList, positionSortButton, void(entity, entity, float, float, string, void(entity, entity)))
33         ATTRIB(NexuizServerList, sortButton1, entity, NULL)
34         ATTRIB(NexuizServerList, sortButton2, entity, NULL)
35         ATTRIB(NexuizServerList, sortButton3, entity, NULL)
36         ATTRIB(NexuizServerList, sortButton4, entity, NULL)
37         ATTRIB(NexuizServerList, connectButton, entity, NULL)
38         ATTRIB(NexuizServerList, currentSortOrder, float, 0)
39         ATTRIB(NexuizServerList, currentSortField, float, -1)
40         ATTRIB(NexuizServerList, lastClickedServer, float, -1)
41         ATTRIB(NexuizServerList, lastClickedTime, float, 0)
42 ENDCLASS(NexuizServerList)
43 entity makeNexuizServerList();
44 void ServerList_Connect_Click(entity btn, entity me);
45 void ServerList_ShowEmpty_Click(entity box, entity me);
46 void ServerList_ShowFull_Click(entity box, entity me);
47 void ServerList_Filter_Change(entity box, entity me);
48 #endif
49
50 #ifdef IMPLEMENTATION
51 float SLIST_FIELD_CNAME;
52 float SLIST_FIELD_PING;
53 float SLIST_FIELD_GAME;
54 float SLIST_FIELD_MOD;
55 float SLIST_FIELD_MAP;
56 float SLIST_FIELD_NAME;
57 float SLIST_FIELD_MAXPLAYERS;
58 float SLIST_FIELD_NUMPLAYERS;
59 float SLIST_FIELD_NUMHUMANS;
60 float SLIST_FIELD_NUMBOTS;
61 float SLIST_FIELD_PROTOCOL;
62 float SLIST_FIELD_FREESLOTS;
63 void ServerList_UpdateFieldIDs()
64 {
65         SLIST_FIELD_CNAME = gethostcacheindexforkey( "cname" );
66         SLIST_FIELD_PING = gethostcacheindexforkey( "ping" );
67         SLIST_FIELD_GAME = gethostcacheindexforkey( "game" );
68         SLIST_FIELD_MOD = gethostcacheindexforkey( "mod" );
69         SLIST_FIELD_MAP = gethostcacheindexforkey( "map" );
70         SLIST_FIELD_NAME = gethostcacheindexforkey( "name" );
71         SLIST_FIELD_MAXPLAYERS = gethostcacheindexforkey( "maxplayers" );
72         SLIST_FIELD_NUMPLAYERS = gethostcacheindexforkey( "numplayers" );
73         SLIST_FIELD_NUMHUMANS = gethostcacheindexforkey( "numhumans" );
74         SLIST_FIELD_NUMBOTS = gethostcacheindexforkey( "numbots" );
75         SLIST_FIELD_PROTOCOL = gethostcacheindexforkey( "protocol" );
76         SLIST_FIELD_FREESLOTS = gethostcacheindexforkey( "freeslots" );
77 }
78
79 entity makeNexuizServerList()
80 {
81         entity me;
82         me = spawnNexuizServerList();
83         me.configureNexuizServerList(me);
84         return me;
85 }
86 void configureNexuizServerListNexuizServerList(entity me)
87 {
88         me.configureNexuizListBox(me);
89
90         ServerList_UpdateFieldIDs();
91
92         me.nItems = 0;
93 }
94 void setSelectedNexuizServerList(entity me, float i)
95 {
96         float save;
97         save = me.selectedItem;
98         setSelectedListBox(me, i);
99         /*
100         if(me.selectedItem == save)
101                 return;
102         */
103         if(me.nItems == 0)
104                 return;
105         if(gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT) != me.nItems)
106                 return; // sorry, it would be wrong
107         if(me.selectedServer)
108                 strunzone(me.selectedServer);
109         me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
110 }
111 void refreshServerListNexuizServerList(entity me, float mode)
112 {
113         // 0: just reparametrize
114         // 1: also ask for new servers
115         // 2: clear
116         //print("refresh of type ", ftos(mode), "\n");
117         /* if(mode == 2) // borken
118         {
119                 // clear list
120                 localcmd("net_slist\n");
121                 me.needsRefresh = 1; // net_slist kills sort order, so we need to restore it later
122         }
123         else */
124         {
125                 float m;
126                 m = SLIST_MASK_AND;
127                 resethostcachemasks();
128                 if(!me.filterShowFull)
129                         sethostcachemasknumber(m++, SLIST_FIELD_FREESLOTS, 1, SLIST_TEST_GREATEREQUAL);
130                 if(!me.filterShowEmpty)
131                         sethostcachemasknumber(m++, SLIST_FIELD_NUMHUMANS, 1, SLIST_TEST_GREATEREQUAL);
132                 m = SLIST_MASK_OR;
133                 if(me.filterString)
134                 {
135                         sethostcachemaskstring(m++, SLIST_FIELD_NAME, me.filterString, SLIST_TEST_CONTAINS);
136                         sethostcachemaskstring(m++, SLIST_FIELD_MAP, me.filterString, SLIST_TEST_CONTAINS);
137                 }
138                 sethostcachesort(me.currentSortField, me.currentSortOrder < 0);
139                 resorthostcache();
140                 if(mode >= 1)
141                         refreshhostcache();
142         }
143 }
144 void focusEnterNexuizServerList(entity me)
145 {
146         if(time < me.nextRefreshTime)
147         {
148                 //print("sorry, no refresh yet\n");
149                 return;
150         }
151         me.nextRefreshTime = time + 10;
152         me.refreshServerList(me, 1);
153 }
154 void drawNexuizServerList(entity me)
155 {
156         float i, found;
157
158         if(me.currentSortField == -1)
159         {
160                 me.setSortOrder(me, SLIST_FIELD_PING, +1);
161                 me.refreshServerList(me, 2);
162         }
163         else if(me.needsRefresh == 1)
164         {
165                 me.needsRefresh = 2; // delay by one frame to make sure "slist" has been executed
166         }
167         else if(me.needsRefresh == 2)
168         {
169                 me.needsRefresh = 0;
170                 me.refreshServerList(me, 0);
171         }
172
173         me.nItems = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
174         me.connectButton.disabled = (me.nItems == 0);
175
176         found = 0;
177         if(me.selectedServer)
178         {
179                 for(i = 0; i < me.nItems; ++i)
180                         if(gethostcachestring(SLIST_FIELD_CNAME, i) == me.selectedServer)
181                         {
182                                 if(i != me.selectedItem)
183                                 {
184                                         me.lastClickedServer = -1;
185                                         me.selectedItem = i;
186                                 }
187                                 found = 1;
188                                 break;
189                         }
190         }
191         if(!found)
192                 if(me.nItems > 0)
193                 {
194                         if(me.selectedItem >= me.nItems)
195                                 me.selectedItem = me.nItems - 1;
196                         if(me.selectedServer)
197                                 strunzone(me.selectedServer);
198                         me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
199                 }
200
201         drawListBox(me);
202 }
203 void ServerList_PingSort_Click(entity btn, entity me)
204 {
205         me.setSortOrder(me, SLIST_FIELD_PING, +1);
206 }
207 void ServerList_NameSort_Click(entity btn, entity me)
208 {
209         me.setSortOrder(me, SLIST_FIELD_NAME, -1); // why?
210 }
211 void ServerList_MapSort_Click(entity btn, entity me)
212 {
213         me.setSortOrder(me, SLIST_FIELD_MAP, -1); // why?
214 }
215 void ServerList_PlayerSort_Click(entity btn, entity me)
216 {
217         me.setSortOrder(me, SLIST_FIELD_NUMHUMANS, -1);
218 }
219 void ServerList_Filter_Change(entity box, entity me)
220 {
221         if(me.filterString)
222                 strunzone(me.filterString);
223         if(box.text != "")
224                 me.filterString = strzone(box.text);
225         else
226                 me.filterString = string_null;
227         me.refreshServerList(me, 0);
228 }
229 void ServerList_ShowEmpty_Click(entity box, entity me)
230 {
231         box.checked = me.filterShowEmpty = !me.filterShowEmpty;
232         me.refreshServerList(me, 0);
233 }
234 void ServerList_ShowFull_Click(entity box, entity me)
235 {
236         box.checked = me.filterShowFull = !me.filterShowFull;
237         me.refreshServerList(me, 0);
238 }
239 void setSortOrderNexuizServerList(entity me, float field, float direction)
240 {
241         if(me.currentSortField == field)
242                 direction = -me.currentSortOrder;
243         me.currentSortOrder = direction;
244         me.currentSortField = field;
245         me.sortButton1.forcePressed = (field == SLIST_FIELD_PING);
246         me.sortButton2.forcePressed = (field == SLIST_FIELD_NAME);
247         me.sortButton3.forcePressed = (field == SLIST_FIELD_MAP);
248         me.sortButton4.forcePressed = (field == SLIST_FIELD_NUMHUMANS);
249         me.selectedItem = 0;
250         if(me.selectedServer)
251                 strunzone(me.selectedServer);
252         me.selectedServer = string_null;
253         me.refreshServerList(me, 0);
254 }
255 void positionSortButtonNexuizServerList(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
256 {
257         vector originInLBSpace, sizeInLBSpace;
258         originInLBSpace = eY * (-me.itemHeight);
259         sizeInLBSpace = eY * me.itemHeight + eX * (1 - me.controlWidth);
260
261         vector originInDialogSpace, sizeInDialogSpace;
262         originInDialogSpace = boxToGlobal(originInLBSpace, me.Container_origin, me.Container_size);
263         sizeInDialogSpace = boxToGlobalSize(sizeInLBSpace, me.Container_size);
264
265         btn.Container_origin_x = originInDialogSpace_x + sizeInDialogSpace_x * theOrigin;
266         btn.Container_size_x   =                         sizeInDialogSpace_x * theSize;
267         btn.setText(btn, theTitle);
268         btn.onClick = theFunc;
269         btn.onClickEntity = me;
270         btn.resized = 1;
271 }
272 void resizeNotifyNexuizServerList(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
273 {
274         resizeNotifyNexuizListBox(me, relOrigin, relSize, absOrigin, absSize);
275
276         me.realFontSize_y = me.fontSize / (absSize_y * me.itemHeight);
277         me.realFontSize_x = me.fontSize / (absSize_x * (1 - me.controlWidth));
278         me.realUpperMargin = 0.5 * (1 - me.realFontSize_y);
279
280         me.columnPingOrigin = 0;
281         me.columnPingSize = me.realFontSize_x * 4;
282         me.columnMapSize = me.realFontSize_x * 12;
283         me.columnPlayersSize = me.realFontSize_x * 6;
284         me.columnNameSize = 1 - me.columnPlayersSize - me.columnMapSize - me.columnPingSize - 3 * me.realFontSize_x;
285         me.columnNameOrigin = me.columnPingOrigin + me.columnPingSize + me.realFontSize_x;
286         me.columnMapOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize_x;
287         me.columnPlayersOrigin = me.columnMapOrigin + me.columnMapSize + me.realFontSize_x;
288
289         me.positionSortButton(me, me.sortButton1, me.columnPingOrigin, me.columnPingSize, "Ping", ServerList_PingSort_Click);
290         me.positionSortButton(me, me.sortButton2, me.columnNameOrigin, me.columnNameSize, "Host name", ServerList_NameSort_Click);
291         me.positionSortButton(me, me.sortButton3, me.columnMapOrigin, me.columnMapSize, "Map", ServerList_MapSort_Click);
292         me.positionSortButton(me, me.sortButton4, me.columnPlayersOrigin, me.columnPlayersSize, "Players", ServerList_PlayerSort_Click);
293
294         float f;
295         f = me.currentSortField;
296         if(f >= 0)
297         {
298                 me.currentSortField = -1;
299                 me.setSortOrder(me, f, me.currentSortOrder); // force resetting the sort order
300         }
301 }
302 void ServerList_Connect_Click(entity btn, entity me)
303 {
304         if(me.nItems > 0)
305                 localcmd("connect ", me.selectedServer, "\n");
306 }
307 void clickListBoxItemNexuizServerList(entity me, float i, vector where)
308 {
309         if(i == me.lastClickedServer)
310                 if(time < me.lastClickedTime + 0.3)
311                 {
312                         // DOUBLE CLICK!
313                         ServerList_Connect_Click(NULL, me);
314                 }
315         me.lastClickedServer = i;
316         me.lastClickedTime = time;
317 }
318 void drawListBoxItemNexuizServerList(entity me, float i, vector absSize, float isSelected)
319 {
320         // layout: Ping, Server name, Map name, NP, TP, MP
321         string s;
322         float p;
323         vector theColor;
324         float theAlpha;
325
326         if(isSelected)
327                 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
328
329         if(gethostcachenumber(SLIST_FIELD_NUMPLAYERS, i) >= gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i))
330                 theAlpha = SKINALPHA_SERVERLIST_FULL;
331         else if not(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i))
332                 theAlpha = SKINALPHA_SERVERLIST_EMPTY;
333         else
334                 theAlpha = 1;
335         
336         p = gethostcachenumber(SLIST_FIELD_PING, i);
337         if(p < 50)
338                 theColor = SKINCOLOR_SERVERLIST_LOWPING + (SKINCOLOR_SERVERLIST_MEDPING - SKINCOLOR_SERVERLIST_LOWPING) * (p / 50);
339         else if(p < 150)
340                 theColor = SKINCOLOR_SERVERLIST_MEDPING + (SKINCOLOR_SERVERLIST_HIGHPING - SKINCOLOR_SERVERLIST_MEDPING) * ((p - 50) / 100);
341         else if(p < 650)
342         {
343                 theColor = SKINCOLOR_SERVERLIST_HIGHPING;
344                 theAlpha *= 1 + (SKINALPHA_SERVERLIST_HIGHPING - 1) * ((p - 150) / 500);
345         }
346         else
347         {
348                 theColor = eX;
349                 theAlpha *= SKINALPHA_SERVERLIST_HIGHPING;
350         }
351         
352         s = ftos(p);
353         draw_Text(me.realUpperMargin * eY + (me.columnPingSize - draw_TextWidth(s, 0) * me.realFontSize_x) * eX, s, me.realFontSize, theColor, theAlpha, 0);
354         s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, i), me.columnNameSize / me.realFontSize_x, 0);
355         draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
356         s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, i), me.columnMapSize / me.realFontSize_x, 0);
357         draw_Text(me.realUpperMargin * eY + (me.columnMapOrigin + (me.columnMapSize - draw_TextWidth(s, 0) * me.realFontSize_x) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
358         s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i)));
359         draw_Text(me.realUpperMargin * eY + (me.columnPlayersOrigin + (me.columnPlayersSize - draw_TextWidth(s, 0) * me.realFontSize_x) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
360 }
361
362 float keyDownNexuizServerList(entity me, float scan, float ascii, float shift)
363 {
364         if(scan == K_ENTER || scan == K_SPACE)
365                 ServerList_Connect_Click(NULL, me);
366         else
367                 return keyDownListBox(me, scan, ascii, shift);
368         return 1;
369 }
370 #endif