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