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