]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/menu/nexuiz/serverlist.c
remove favoriting by right mouse button (Mac users hate that). We have a button for...
[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         me.connectButton.disabled = (me.nItems == 0);
245
246         found = 0;
247         if(me.selectedServer)
248         {
249                 for(i = 0; i < me.nItems; ++i)
250                         if(gethostcachestring(SLIST_FIELD_CNAME, i) == me.selectedServer)
251                         {
252                                 if(i != me.selectedItem)
253                                 {
254                                         me.lastClickedServer = -1;
255                                         me.selectedItem = i;
256                                 }
257                                 found = 1;
258                                 break;
259                         }
260         }
261         if(!found)
262                 if(me.nItems > 0)
263                 {
264                         if(me.selectedItem >= me.nItems)
265                                 me.selectedItem = me.nItems - 1;
266                         if(me.selectedServer)
267                                 strunzone(me.selectedServer);
268                         me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
269                 }
270
271         if(owned)
272         {
273                 me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
274                 me.ipAddressBox.cursorPos = strlen(me.selectedServer);
275         }
276
277         if(IsFavorite(me.ipAddressBox.text))
278                 me.favoriteButton.setText(me.favoriteButton, "Remove");
279         else
280                 me.favoriteButton.setText(me.favoriteButton, "Bookmark");
281
282         drawListBox(me);
283 }
284 void ServerList_PingSort_Click(entity btn, entity me)
285 {
286         me.setSortOrder(me, SLIST_FIELD_PING, +1);
287 }
288 void ServerList_NameSort_Click(entity btn, entity me)
289 {
290         me.setSortOrder(me, SLIST_FIELD_NAME, -1); // why?
291 }
292 void ServerList_MapSort_Click(entity btn, entity me)
293 {
294         me.setSortOrder(me, SLIST_FIELD_MAP, -1); // why?
295 }
296 void ServerList_PlayerSort_Click(entity btn, entity me)
297 {
298         me.setSortOrder(me, SLIST_FIELD_NUMHUMANS, -1);
299 }
300 void ServerList_TypeSort_Click(entity btn, entity me)
301 {
302         string s, t;
303         float i, m;
304         s = me.filterString;
305         m = strstrofs(s, ":", 0);
306         if(m >= 0)
307         {
308                 s = substring(s, 0, m);
309                 while(substring(s, m+1, 1) == " ") // skip spaces
310                         ++m;
311         }
312         else
313                 s = "";
314
315         for(i = 1; ; ++i) // 20 modes ought to be enough for anyone
316         {
317                 t = GametypeNameFromType(i);
318                 if(i > 1)
319                         if(t == GametypeNameFromType(0)) // it repeats (default case)
320                         {
321                                 // no type was found
322                                 // choose the first one
323                                 s = t;
324                                 break;
325                         }
326                 if(s == GametypeNameFromType(i))
327                 {
328                         // the type was found
329                         // choose the next one
330                         s = GametypeNameFromType(i + 1);
331                         if(s == GametypeNameFromType(0))
332                                 s = "";
333                         break;
334                 }
335         }
336
337         if(s != "")
338                 s = strcat(s, ":");
339         s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1));
340
341         me.controlledTextbox.setText(me.controlledTextbox, s);
342         me.controlledTextbox.keyDown(me.controlledTextbox, K_END, 0, 0);
343         me.controlledTextbox.keyUp(me.controlledTextbox, K_END, 0, 0);
344         //ServerList_Filter_Change(me.controlledTextbox, me);
345 }
346 void ServerList_Filter_Change(entity box, entity me)
347 {
348         if(me.filterString)
349                 strunzone(me.filterString);
350         if(box.text != "")
351                 me.filterString = strzone(box.text);
352         else
353                 me.filterString = string_null;
354         me.refreshServerList(me, 0);
355 }
356 void ServerList_ShowEmpty_Click(entity box, entity me)
357 {
358         box.setChecked(box, me.filterShowEmpty = !me.filterShowEmpty);
359         me.refreshServerList(me, 0);
360 }
361 void ServerList_ShowFull_Click(entity box, entity me)
362 {
363         box.setChecked(box, me.filterShowFull = !me.filterShowFull);
364         me.refreshServerList(me, 0);
365 }
366 void setSortOrderNexuizServerList(entity me, float field, float direction)
367 {
368         if(me.currentSortField == field)
369                 direction = -me.currentSortOrder;
370         me.currentSortOrder = direction;
371         me.currentSortField = field;
372         me.sortButton1.forcePressed = (field == SLIST_FIELD_PING);
373         me.sortButton2.forcePressed = (field == SLIST_FIELD_NAME);
374         me.sortButton3.forcePressed = (field == SLIST_FIELD_MAP);
375         me.sortButton4.forcePressed = 0;
376         me.sortButton5.forcePressed = (field == SLIST_FIELD_NUMHUMANS);
377         me.selectedItem = 0;
378         if(me.selectedServer)
379                 strunzone(me.selectedServer);
380         me.selectedServer = string_null;
381         me.refreshServerList(me, 0);
382 }
383 void positionSortButtonNexuizServerList(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
384 {
385         vector originInLBSpace, sizeInLBSpace;
386         originInLBSpace = eY * (-me.itemHeight);
387         sizeInLBSpace = eY * me.itemHeight + eX * (1 - me.controlWidth);
388
389         vector originInDialogSpace, sizeInDialogSpace;
390         originInDialogSpace = boxToGlobal(originInLBSpace, me.Container_origin, me.Container_size);
391         sizeInDialogSpace = boxToGlobalSize(sizeInLBSpace, me.Container_size);
392
393         btn.Container_origin_x = originInDialogSpace_x + sizeInDialogSpace_x * theOrigin;
394         btn.Container_size_x   =                         sizeInDialogSpace_x * theSize;
395         btn.setText(btn, theTitle);
396         btn.onClick = theFunc;
397         btn.onClickEntity = me;
398         btn.resized = 1;
399 }
400 void resizeNotifyNexuizServerList(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
401 {
402         resizeNotifyNexuizListBox(me, relOrigin, relSize, absOrigin, absSize);
403
404         me.realFontSize_y = me.fontSize / (absSize_y * me.itemHeight);
405         me.realFontSize_x = me.fontSize / (absSize_x * (1 - me.controlWidth));
406         me.realUpperMargin = 0.5 * (1 - me.realFontSize_y);
407
408         me.columnPingOrigin = 0;
409         me.columnPingSize = me.realFontSize_x * 4;
410         me.columnMapSize = me.realFontSize_x * 12;
411         me.columnTypeSize = me.realFontSize_x * 4;
412         me.columnPlayersSize = me.realFontSize_x * 6;
413         me.columnNameSize = 1 - me.columnPlayersSize - me.columnMapSize - me.columnPingSize - me.columnTypeSize - 4 * me.realFontSize_x;
414         me.columnNameOrigin = me.columnPingOrigin + me.columnPingSize + me.realFontSize_x;
415         me.columnMapOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize_x;
416         me.columnTypeOrigin = me.columnMapOrigin + me.columnMapSize + me.realFontSize_x;
417         me.columnPlayersOrigin = me.columnTypeOrigin + me.columnTypeSize + me.realFontSize_x;
418
419         me.positionSortButton(me, me.sortButton1, me.columnPingOrigin, me.columnPingSize, "Ping", ServerList_PingSort_Click);
420         me.positionSortButton(me, me.sortButton2, me.columnNameOrigin, me.columnNameSize, "Host name", ServerList_NameSort_Click);
421         me.positionSortButton(me, me.sortButton3, me.columnMapOrigin, me.columnMapSize, "Map", ServerList_MapSort_Click);
422         me.positionSortButton(me, me.sortButton4, me.columnTypeOrigin, me.columnTypeSize, "Type", ServerList_TypeSort_Click);
423         me.positionSortButton(me, me.sortButton5, me.columnPlayersOrigin, me.columnPlayersSize, "Players", ServerList_PlayerSort_Click);
424
425         float f;
426         f = me.currentSortField;
427         if(f >= 0)
428         {
429                 me.currentSortField = -1;
430                 me.setSortOrder(me, f, me.currentSortOrder); // force resetting the sort order
431         }
432 }
433 void ServerList_Connect_Click(entity btn, entity me)
434 {
435         localcmd("connect ", me.ipAddressBox.text, "\n");
436 }
437 void ServerList_Favorite_Click(entity btn, entity me)
438 {
439         string ipstr;
440         ipstr = netaddress_resolve(me.ipAddressBox.text, 26000);
441         if(ipstr != "")
442         {
443                 me.ipAddressBox.setText(me.ipAddressBox, ipstr);
444                 me.ipAddressBox.cursorPos = strlen(ipstr);
445                 ToggleFavorite(ipstr);
446         }
447 }
448 void clickListBoxItemNexuizServerList(entity me, float i, vector where)
449 {
450         if(i == me.lastClickedServer)
451                 if(time < me.lastClickedTime + 0.3)
452                 {
453                         // DOUBLE CLICK!
454                         ServerList_Connect_Click(NULL, me);
455                 }
456         me.lastClickedServer = i;
457         me.lastClickedTime = time;
458 }
459 void drawListBoxItemNexuizServerList(entity me, float i, vector absSize, float isSelected)
460 {
461         // layout: Ping, Server name, Map name, NP, TP, MP
462         string s;
463         float p;
464         vector theColor;
465         float theAlpha;
466
467         if(isSelected)
468                 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
469
470         if(gethostcachenumber(SLIST_FIELD_NUMPLAYERS, i) >= gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i))
471                 theAlpha = SKINALPHA_SERVERLIST_FULL;
472         else if not(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i))
473                 theAlpha = SKINALPHA_SERVERLIST_EMPTY;
474         else
475                 theAlpha = 1;
476         
477         p = gethostcachenumber(SLIST_FIELD_PING, i);
478 #define PING_LOW 75
479 #define PING_MED 200
480 #define PING_HIGH 500
481         if(p < PING_LOW)
482                 theColor = SKINCOLOR_SERVERLIST_LOWPING + (SKINCOLOR_SERVERLIST_MEDPING - SKINCOLOR_SERVERLIST_LOWPING) * (p / PING_LOW);
483         else if(p < PING_MED)
484                 theColor = SKINCOLOR_SERVERLIST_MEDPING + (SKINCOLOR_SERVERLIST_HIGHPING - SKINCOLOR_SERVERLIST_MEDPING) * ((p - PING_LOW) / (PING_MED - PING_LOW));
485         else if(p < PING_HIGH)
486         {
487                 theColor = SKINCOLOR_SERVERLIST_HIGHPING;
488                 theAlpha *= 1 + (SKINALPHA_SERVERLIST_HIGHPING - 1) * ((p - PING_MED) / (PING_HIGH - PING_MED));
489         }
490         else
491         {
492                 theColor = eX;
493                 theAlpha *= SKINALPHA_SERVERLIST_HIGHPING;
494         }
495
496         if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, i))
497         {
498                 theColor = theColor * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINCOLOR_SERVERLIST_FAVORITE * SKINALPHA_SERVERLIST_FAVORITE;
499                 theAlpha = theAlpha * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINALPHA_SERVERLIST_FAVORITE;
500         }
501
502         s = ftos(p);
503         draw_Text(me.realUpperMargin * eY + (me.columnPingSize - draw_TextWidth(s, 0) * me.realFontSize_x) * eX, s, me.realFontSize, theColor, theAlpha, 0);
504         s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, i), me.columnNameSize / me.realFontSize_x, 0);
505         draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
506         s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, i), me.columnMapSize / me.realFontSize_x, 0);
507         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);
508         s = gethostcachestring(SLIST_FIELD_QCSTATUS, i);
509         p = strstrofs(s, ":", 0);
510         if(p >= 0)
511                 s = substring(s, 0, p);
512         else
513                 s = "";
514         s = draw_TextShortenToWidth(s, me.columnMapSize / me.realFontSize_x, 0);
515         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);
516         s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i)));
517         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);
518 }
519
520 float keyDownNexuizServerList(entity me, float scan, float ascii, float shift)
521 {
522         float i, o;
523         string s;
524
525         if(scan == K_ENTER)
526         {
527                 ServerList_Connect_Click(NULL, me);
528                 return 1;
529         }
530         else if(scan == K_INS)
531         {
532                 i = me.selectedItem;
533                 if(i < me.nItems)
534                         ToggleFavorite(me.selectedServer);
535         }
536         else if(keyDownListBox(me, scan, ascii, shift))
537                 return 1;
538         else if(!me.controlledTextbox)
539                 return 0;
540         else
541                 return me.controlledTextbox.keyDown(me.controlledTextbox, scan, ascii, shift);
542 }
543 #endif