]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/menu/nexuiz/serverlist.c
clear IP address box when refiltering
[divverent/nexuiz.git] / data / qcsrc / menu / 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, columnTypeOrigin, float, 0)
20         ATTRIB(NexuizServerList, columnTypeSize, float, 0)
21         ATTRIB(NexuizServerList, columnPlayersOrigin, float, 0)
22         ATTRIB(NexuizServerList, columnPlayersSize, float, 0)
23
24         ATTRIB(NexuizServerList, selectedServer, string, string_null) // to restore selected server when needed
25         METHOD(NexuizServerList, setSelected, void(entity, float))
26         METHOD(NexuizServerList, setSortOrder, void(entity, float, float))
27         ATTRIB(NexuizServerList, filterShowEmpty, float, 1)
28         ATTRIB(NexuizServerList, filterShowFull, float, 1)
29         ATTRIB(NexuizServerList, filterString, string, string_null)
30         ATTRIB(NexuizServerList, controlledTextbox, entity, NULL)
31         ATTRIB(NexuizServerList, ipAddressBox, entity, NULL)
32         ATTRIB(NexuizServerList, favoriteButton, entity, NULL)
33         ATTRIB(NexuizServerList, nextRefreshTime, float, 0)
34         METHOD(NexuizServerList, refreshServerList, void(entity, float)) // refresh mode: 0 = just reparametrize, 1 = send new requests, 2 = clear
35         ATTRIB(NexuizServerList, needsRefresh, float, 1)
36         METHOD(NexuizServerList, focusEnter, void(entity))
37         METHOD(NexuizServerList, positionSortButton, void(entity, entity, float, float, string, void(entity, entity)))
38         ATTRIB(NexuizServerList, sortButton1, entity, NULL)
39         ATTRIB(NexuizServerList, sortButton2, entity, NULL)
40         ATTRIB(NexuizServerList, sortButton3, entity, NULL)
41         ATTRIB(NexuizServerList, sortButton4, entity, NULL)
42         ATTRIB(NexuizServerList, sortButton5, entity, NULL)
43         ATTRIB(NexuizServerList, connectButton, entity, NULL)
44         ATTRIB(NexuizServerList, currentSortOrder, float, 0)
45         ATTRIB(NexuizServerList, currentSortField, float, -1)
46         ATTRIB(NexuizServerList, lastClickedServer, float, -1)
47         ATTRIB(NexuizServerList, lastClickedTime, float, 0)
48 ENDCLASS(NexuizServerList)
49 entity makeNexuizServerList();
50 void ServerList_Connect_Click(entity btn, entity me);
51 void ServerList_ShowEmpty_Click(entity box, entity me);
52 void ServerList_ShowFull_Click(entity box, entity me);
53 void ServerList_Filter_Change(entity box, entity me);
54 void ServerList_Favorite_Click(entity btn, entity me);
55 #endif
56
57 #ifdef IMPLEMENTATION
58 float SLIST_FIELD_CNAME;
59 float SLIST_FIELD_PING;
60 float SLIST_FIELD_GAME;
61 float SLIST_FIELD_MOD;
62 float SLIST_FIELD_MAP;
63 float SLIST_FIELD_NAME;
64 float SLIST_FIELD_MAXPLAYERS;
65 float SLIST_FIELD_NUMPLAYERS;
66 float SLIST_FIELD_NUMHUMANS;
67 float SLIST_FIELD_NUMBOTS;
68 float SLIST_FIELD_PROTOCOL;
69 float SLIST_FIELD_FREESLOTS;
70 float SLIST_FIELD_PLAYERS;
71 float SLIST_FIELD_QCSTATUS;
72 float SLIST_FIELD_ISFAVORITE;
73 void ServerList_UpdateFieldIDs()
74 {
75         SLIST_FIELD_CNAME = gethostcacheindexforkey( "cname" );
76         SLIST_FIELD_PING = gethostcacheindexforkey( "ping" );
77         SLIST_FIELD_GAME = gethostcacheindexforkey( "game" );
78         SLIST_FIELD_MOD = gethostcacheindexforkey( "mod" );
79         SLIST_FIELD_MAP = gethostcacheindexforkey( "map" );
80         SLIST_FIELD_NAME = gethostcacheindexforkey( "name" );
81         SLIST_FIELD_MAXPLAYERS = gethostcacheindexforkey( "maxplayers" );
82         SLIST_FIELD_NUMPLAYERS = gethostcacheindexforkey( "numplayers" );
83         SLIST_FIELD_NUMHUMANS = gethostcacheindexforkey( "numhumans" );
84         SLIST_FIELD_NUMBOTS = gethostcacheindexforkey( "numbots" );
85         SLIST_FIELD_PROTOCOL = gethostcacheindexforkey( "protocol" );
86         SLIST_FIELD_FREESLOTS = gethostcacheindexforkey( "freeslots" );
87         SLIST_FIELD_PLAYERS = gethostcacheindexforkey( "players" );
88         SLIST_FIELD_QCSTATUS = gethostcacheindexforkey( "qcstatus" );
89         SLIST_FIELD_ISFAVORITE = gethostcacheindexforkey( "isfavorite" );
90 }
91
92 float IsFavorite(string srv)
93 {
94         string s;
95         float o;
96         s = cvar_string("net_slist_favorites");
97         s = strcat(" ", s, " ");
98         srv = strcat(" ", srv, " ");
99         o = strstrofs(s, srv, 0);
100         return (o != -1);
101 }
102
103 void ToggleFavorite(string srv)
104 {
105         string s;
106         float o;
107         s = cvar_string("net_slist_favorites");
108         o = strstrofs(strcat(" ", s, " "), strcat(" ", srv, " "), 0);
109         if(o == -1)
110         {
111                 cvar_set("net_slist_favorites", strcat(s, " ", srv));
112         }
113         else
114         {
115                 cvar_set("net_slist_favorites", strcat(
116                                         substring(s, 0, o - 1), substring(s, o + strlen(srv), strlen(s) - o - strlen(srv))
117                                         ));
118         }
119         resorthostcache();
120 }
121
122 entity makeNexuizServerList()
123 {
124         entity me;
125         me = spawnNexuizServerList();
126         me.configureNexuizServerList(me);
127         return me;
128 }
129 void configureNexuizServerListNexuizServerList(entity me)
130 {
131         me.configureNexuizListBox(me);
132
133         ServerList_UpdateFieldIDs();
134
135         me.nItems = 0;
136 }
137 void setSelectedNexuizServerList(entity me, float i)
138 {
139         float save;
140         save = me.selectedItem;
141         setSelectedListBox(me, i);
142         /*
143         if(me.selectedItem == save)
144                 return;
145         */
146         if(me.nItems == 0)
147                 return;
148         if(gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT) != me.nItems)
149                 return; // sorry, it would be wrong
150
151         if(me.selectedServer)
152                 strunzone(me.selectedServer);
153         me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
154
155         me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
156         me.ipAddressBox.cursorPos = strlen(me.selectedServer);
157 }
158 void refreshServerListNexuizServerList(entity me, float mode)
159 {
160         // 0: just reparametrize
161         // 1: also ask for new servers
162         // 2: clear
163         //print("refresh of type ", ftos(mode), "\n");
164         /* if(mode == 2) // borken
165         {
166                 // clear list
167                 localcmd("net_slist\n");
168                 me.needsRefresh = 1; // net_slist kills sort order, so we need to restore it later
169         }
170         else */
171         {
172                 float m, o;
173                 string s, typestr;
174                 s = me.filterString;
175
176                 m = strstrofs(s, ":", 0);
177                 if(m >= 0)
178                 {
179                         typestr = substring(s, 0, m);
180                         s = substring(s, m + 1, strlen(s) - m - 1);
181                         while(substring(s, 0, 1) == " ")
182                                 s = substring(s, 1, strlen(s) - 1);
183                 }
184                 else
185                         typestr = "";
186
187                 m = SLIST_MASK_AND - 1;
188                 resethostcachemasks();
189                 if(!me.filterShowFull)
190                         sethostcachemasknumber(++m, SLIST_FIELD_FREESLOTS, 1, SLIST_TEST_GREATEREQUAL);
191                 if(!me.filterShowEmpty)
192                         sethostcachemasknumber(++m, SLIST_FIELD_NUMHUMANS, 1, SLIST_TEST_GREATEREQUAL);
193                 if(typestr != "")
194                         sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(typestr, ":"), SLIST_TEST_STARTSWITH);
195                 m = SLIST_MASK_OR - 1;
196                 if(s != "")
197                 {
198                         sethostcachemaskstring(++m, SLIST_FIELD_NAME, s, SLIST_TEST_CONTAINS);
199                         sethostcachemaskstring(++m, SLIST_FIELD_MAP, s, SLIST_TEST_CONTAINS);
200                         sethostcachemaskstring(++m, SLIST_FIELD_PLAYERS, s, SLIST_TEST_CONTAINS);
201                         sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(s, ":"), SLIST_TEST_STARTSWITH);
202                 }
203                 o = 2; // favorites first
204                 if(me.currentSortOrder < 0)
205                         o |= 1; // descending
206                 sethostcachesort(me.currentSortField, o);
207                 resorthostcache();
208                 if(mode >= 1)
209                         refreshhostcache();
210         }
211 }
212 void focusEnterNexuizServerList(entity me)
213 {
214         if(time < me.nextRefreshTime)
215         {
216                 //print("sorry, no refresh yet\n");
217                 return;
218         }
219         me.nextRefreshTime = time + 10;
220         me.refreshServerList(me, 1);
221 }
222 void drawNexuizServerList(entity me)
223 {
224         float i, found, owned;
225
226         if(me.currentSortField == -1)
227         {
228                 me.setSortOrder(me, SLIST_FIELD_PING, +1);
229                 me.refreshServerList(me, 2);
230         }
231         else if(me.needsRefresh == 1)
232         {
233                 me.needsRefresh = 2; // delay by one frame to make sure "slist" has been executed
234         }
235         else if(me.needsRefresh == 2)
236         {
237                 me.needsRefresh = 0;
238                 me.refreshServerList(me, 0);
239         }
240
241         owned = (me.selectedServer == me.ipAddressBox.text);
242
243         me.nItems = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
244
245         found = 0;
246         if(me.selectedServer)
247         {
248                 for(i = 0; i < me.nItems; ++i)
249                         if(gethostcachestring(SLIST_FIELD_CNAME, i) == me.selectedServer)
250                         {
251                                 if(i != me.selectedItem)
252                                 {
253                                         me.lastClickedServer = -1;
254                                         me.selectedItem = i;
255                                 }
256                                 found = 1;
257                                 break;
258                         }
259         }
260         if(!found)
261                 if(me.nItems > 0)
262                 {
263                         if(me.selectedItem >= me.nItems)
264                                 me.selectedItem = me.nItems - 1;
265                         if(me.selectedServer)
266                                 strunzone(me.selectedServer);
267                         me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
268                 }
269
270         if(owned)
271         {
272                 me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
273                 me.ipAddressBox.cursorPos = strlen(me.selectedServer);
274         }
275
276         if(IsFavorite(me.ipAddressBox.text))
277                 me.favoriteButton.setText(me.favoriteButton, "Remove");
278         else
279                 me.favoriteButton.setText(me.favoriteButton, "Bookmark");
280
281         drawListBox(me);
282 }
283 void ServerList_PingSort_Click(entity btn, entity me)
284 {
285         me.setSortOrder(me, SLIST_FIELD_PING, +1);
286 }
287 void ServerList_NameSort_Click(entity btn, entity me)
288 {
289         me.setSortOrder(me, SLIST_FIELD_NAME, -1); // why?
290 }
291 void ServerList_MapSort_Click(entity btn, entity me)
292 {
293         me.setSortOrder(me, SLIST_FIELD_MAP, -1); // why?
294 }
295 void ServerList_PlayerSort_Click(entity btn, entity me)
296 {
297         me.setSortOrder(me, SLIST_FIELD_NUMHUMANS, -1);
298 }
299 void ServerList_TypeSort_Click(entity btn, entity me)
300 {
301         string s, t;
302         float i, m;
303         s = me.filterString;
304         m = strstrofs(s, ":", 0);
305         if(m >= 0)
306         {
307                 s = substring(s, 0, m);
308                 while(substring(s, m+1, 1) == " ") // skip spaces
309                         ++m;
310         }
311         else
312                 s = "";
313
314         for(i = 1; ; ++i) // 20 modes ought to be enough for anyone
315         {
316                 t = GametypeNameFromType(i);
317                 if(i > 1)
318                         if(t == GametypeNameFromType(0)) // it repeats (default case)
319                         {
320                                 // no type was found
321                                 // choose the first one
322                                 s = t;
323                                 break;
324                         }
325                 if(s == GametypeNameFromType(i))
326                 {
327                         // the type was found
328                         // choose the next one
329                         s = GametypeNameFromType(i + 1);
330                         if(s == GametypeNameFromType(0))
331                                 s = "";
332                         break;
333                 }
334         }
335
336         if(s != "")
337                 s = strcat(s, ":");
338         s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1));
339
340         me.controlledTextbox.setText(me.controlledTextbox, s);
341         me.controlledTextbox.keyDown(me.controlledTextbox, K_END, 0, 0);
342         me.controlledTextbox.keyUp(me.controlledTextbox, K_END, 0, 0);
343         //ServerList_Filter_Change(me.controlledTextbox, me);
344 }
345 void ServerList_Filter_Change(entity box, entity me)
346 {
347         if(me.filterString)
348                 strunzone(me.filterString);
349         if(box.text != "")
350                 me.filterString = strzone(box.text);
351         else
352                 me.filterString = string_null;
353         me.refreshServerList(me, 0);
354
355         me.ipAddressBox.setText(me.ipAddressBox, "");
356         me.ipAddressBox.cursorPos = 0;
357 }
358 void ServerList_ShowEmpty_Click(entity box, entity me)
359 {
360         box.setChecked(box, me.filterShowEmpty = !me.filterShowEmpty);
361         me.refreshServerList(me, 0);
362
363         me.ipAddressBox.setText(me.ipAddressBox, "");
364         me.ipAddressBox.cursorPos = 0;
365 }
366 void ServerList_ShowFull_Click(entity box, entity me)
367 {
368         box.setChecked(box, me.filterShowFull = !me.filterShowFull);
369         me.refreshServerList(me, 0);
370
371         me.ipAddressBox.setText(me.ipAddressBox, "");
372         me.ipAddressBox.cursorPos = 0;
373 }
374 void setSortOrderNexuizServerList(entity me, float field, float direction)
375 {
376         if(me.currentSortField == field)
377                 direction = -me.currentSortOrder;
378         me.currentSortOrder = direction;
379         me.currentSortField = field;
380         me.sortButton1.forcePressed = (field == SLIST_FIELD_PING);
381         me.sortButton2.forcePressed = (field == SLIST_FIELD_NAME);
382         me.sortButton3.forcePressed = (field == SLIST_FIELD_MAP);
383         me.sortButton4.forcePressed = 0;
384         me.sortButton5.forcePressed = (field == SLIST_FIELD_NUMHUMANS);
385         me.selectedItem = 0;
386         if(me.selectedServer)
387                 strunzone(me.selectedServer);
388         me.selectedServer = string_null;
389         me.refreshServerList(me, 0);
390 }
391 void positionSortButtonNexuizServerList(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
392 {
393         vector originInLBSpace, sizeInLBSpace;
394         originInLBSpace = eY * (-me.itemHeight);
395         sizeInLBSpace = eY * me.itemHeight + eX * (1 - me.controlWidth);
396
397         vector originInDialogSpace, sizeInDialogSpace;
398         originInDialogSpace = boxToGlobal(originInLBSpace, me.Container_origin, me.Container_size);
399         sizeInDialogSpace = boxToGlobalSize(sizeInLBSpace, me.Container_size);
400
401         btn.Container_origin_x = originInDialogSpace_x + sizeInDialogSpace_x * theOrigin;
402         btn.Container_size_x   =                         sizeInDialogSpace_x * theSize;
403         btn.setText(btn, theTitle);
404         btn.onClick = theFunc;
405         btn.onClickEntity = me;
406         btn.resized = 1;
407 }
408 void resizeNotifyNexuizServerList(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
409 {
410         resizeNotifyNexuizListBox(me, relOrigin, relSize, absOrigin, absSize);
411
412         me.realFontSize_y = me.fontSize / (absSize_y * me.itemHeight);
413         me.realFontSize_x = me.fontSize / (absSize_x * (1 - me.controlWidth));
414         me.realUpperMargin = 0.5 * (1 - me.realFontSize_y);
415
416         me.columnPingOrigin = 0;
417         me.columnPingSize = me.realFontSize_x * 4;
418         me.columnMapSize = me.realFontSize_x * 12;
419         me.columnTypeSize = me.realFontSize_x * 4;
420         me.columnPlayersSize = me.realFontSize_x * 6;
421         me.columnNameSize = 1 - me.columnPlayersSize - me.columnMapSize - me.columnPingSize - me.columnTypeSize - 4 * me.realFontSize_x;
422         me.columnNameOrigin = me.columnPingOrigin + me.columnPingSize + me.realFontSize_x;
423         me.columnMapOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize_x;
424         me.columnTypeOrigin = me.columnMapOrigin + me.columnMapSize + me.realFontSize_x;
425         me.columnPlayersOrigin = me.columnTypeOrigin + me.columnTypeSize + me.realFontSize_x;
426
427         me.positionSortButton(me, me.sortButton1, me.columnPingOrigin, me.columnPingSize, "Ping", ServerList_PingSort_Click);
428         me.positionSortButton(me, me.sortButton2, me.columnNameOrigin, me.columnNameSize, "Host name", ServerList_NameSort_Click);
429         me.positionSortButton(me, me.sortButton3, me.columnMapOrigin, me.columnMapSize, "Map", ServerList_MapSort_Click);
430         me.positionSortButton(me, me.sortButton4, me.columnTypeOrigin, me.columnTypeSize, "Type", ServerList_TypeSort_Click);
431         me.positionSortButton(me, me.sortButton5, me.columnPlayersOrigin, me.columnPlayersSize, "Players", ServerList_PlayerSort_Click);
432
433         float f;
434         f = me.currentSortField;
435         if(f >= 0)
436         {
437                 me.currentSortField = -1;
438                 me.setSortOrder(me, f, me.currentSortOrder); // force resetting the sort order
439         }
440 }
441 void ServerList_Connect_Click(entity btn, entity me)
442 {
443         localcmd("connect ", me.ipAddressBox.text, "\n");
444 }
445 void ServerList_Favorite_Click(entity btn, entity me)
446 {
447         string ipstr;
448         ipstr = netaddress_resolve(me.ipAddressBox.text, 26000);
449         if(ipstr != "")
450         {
451                 me.ipAddressBox.setText(me.ipAddressBox, ipstr);
452                 me.ipAddressBox.cursorPos = strlen(ipstr);
453                 ToggleFavorite(ipstr);
454         }
455 }
456 void clickListBoxItemNexuizServerList(entity me, float i, vector where)
457 {
458         if(i == me.lastClickedServer)
459                 if(time < me.lastClickedTime + 0.3)
460                 {
461                         // DOUBLE CLICK!
462                         ServerList_Connect_Click(NULL, me);
463                 }
464         me.lastClickedServer = i;
465         me.lastClickedTime = time;
466 }
467 void drawListBoxItemNexuizServerList(entity me, float i, vector absSize, float isSelected)
468 {
469         // layout: Ping, Server name, Map name, NP, TP, MP
470         string s;
471         float p;
472         vector theColor;
473         float theAlpha;
474
475         if(isSelected)
476                 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
477
478         if(gethostcachenumber(SLIST_FIELD_NUMPLAYERS, i) >= gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i))
479                 theAlpha = SKINALPHA_SERVERLIST_FULL;
480         else if not(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i))
481                 theAlpha = SKINALPHA_SERVERLIST_EMPTY;
482         else
483                 theAlpha = 1;
484         
485         p = gethostcachenumber(SLIST_FIELD_PING, i);
486 #define PING_LOW 75
487 #define PING_MED 200
488 #define PING_HIGH 500
489         if(p < PING_LOW)
490                 theColor = SKINCOLOR_SERVERLIST_LOWPING + (SKINCOLOR_SERVERLIST_MEDPING - SKINCOLOR_SERVERLIST_LOWPING) * (p / PING_LOW);
491         else if(p < PING_MED)
492                 theColor = SKINCOLOR_SERVERLIST_MEDPING + (SKINCOLOR_SERVERLIST_HIGHPING - SKINCOLOR_SERVERLIST_MEDPING) * ((p - PING_LOW) / (PING_MED - PING_LOW));
493         else if(p < PING_HIGH)
494         {
495                 theColor = SKINCOLOR_SERVERLIST_HIGHPING;
496                 theAlpha *= 1 + (SKINALPHA_SERVERLIST_HIGHPING - 1) * ((p - PING_MED) / (PING_HIGH - PING_MED));
497         }
498         else
499         {
500                 theColor = eX;
501                 theAlpha *= SKINALPHA_SERVERLIST_HIGHPING;
502         }
503
504         if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, i))
505         {
506                 theColor = theColor * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINCOLOR_SERVERLIST_FAVORITE * SKINALPHA_SERVERLIST_FAVORITE;
507                 theAlpha = theAlpha * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINALPHA_SERVERLIST_FAVORITE;
508         }
509
510         s = ftos(p);
511         draw_Text(me.realUpperMargin * eY + (me.columnPingSize - draw_TextWidth(s, 0) * me.realFontSize_x) * eX, s, me.realFontSize, theColor, theAlpha, 0);
512         s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, i), me.columnNameSize / me.realFontSize_x, 0);
513         draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
514         s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, i), me.columnMapSize / me.realFontSize_x, 0);
515         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);
516         s = gethostcachestring(SLIST_FIELD_QCSTATUS, i);
517         p = strstrofs(s, ":", 0);
518         if(p >= 0)
519                 s = substring(s, 0, p);
520         else
521                 s = "";
522         s = draw_TextShortenToWidth(s, me.columnMapSize / me.realFontSize_x, 0);
523         draw_Text(me.realUpperMargin * eY + (me.columnTypeOrigin + (me.columnTypeSize - draw_TextWidth(s, 0) * me.realFontSize_x) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
524         s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i)));
525         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);
526 }
527
528 float keyDownNexuizServerList(entity me, float scan, float ascii, float shift)
529 {
530         float i;
531
532         if(scan == K_ENTER)
533         {
534                 ServerList_Connect_Click(NULL, me);
535                 return 1;
536         }
537         else if(scan == K_INS)
538         {
539                 i = me.selectedItem;
540                 if(i < me.nItems)
541                         ToggleFavorite(me.selectedServer);
542         }
543         else if(keyDownListBox(me, scan, ascii, shift))
544                 return 1;
545         else if(!me.controlledTextbox)
546                 return 0;
547         else
548                 return me.controlledTextbox.keyDown(me.controlledTextbox, scan, ascii, shift);
549 }
550 #endif