246 NGWW_ADD, ///< 'Add server' button |
184 NGWW_ADD, ///< 'Add server' button |
247 NGWW_START, ///< 'Start server' button |
185 NGWW_START, ///< 'Start server' button |
248 NGWW_CANCEL, ///< 'Cancel' button |
186 NGWW_CANCEL, ///< 'Cancel' button |
249 }; |
187 }; |
250 |
188 |
251 /** |
189 struct NetworkGameWindow : public QueryStringBaseWindow { |
252 * Draw a single server line. |
190 byte field; ///< selected text-field |
253 * @param cur_item the server to draw. |
191 NetworkGameList *server; ///< selected server |
254 * @param y from where to draw? |
192 NetworkGameList **sort_list; ///< list of games (sorted) |
255 * @param highlight does the line need to be highlighted? |
193 list_d ld; ///< accompanying list-administration |
256 */ |
194 |
257 static void DrawServerLine(const Window *w, const NetworkGameList *cur_item, uint y, bool highlight) |
195 NetworkGameWindow(const WindowDesc *desc) : QueryStringBaseWindow(desc) |
258 { |
196 { |
259 /* show highlighted item with a different colour */ |
197 ttd_strlcpy(this->edit_str_buf, _network_player_name, lengthof(this->edit_str_buf)); |
260 if (highlight) GfxFillRect(w->widget[NGWW_NAME].left + 1, y - 2, w->widget[NGWW_INFO].right - 1, y + 9, 10); |
198 this->afilter = CS_ALPHANUMERAL; |
261 |
199 InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 120); |
262 SetDParamStr(0, cur_item->info.server_name); |
200 |
263 DrawStringTruncated(w->widget[NGWW_NAME].left + 5, y, STR_02BD, TC_BLACK, w->widget[NGWW_NAME].right - w->widget[NGWW_NAME].left - 5); |
201 UpdateNetworkGameWindow(true); |
264 |
202 |
265 SetDParam(0, cur_item->info.clients_on); |
203 this->vscroll.cap = 11; |
266 SetDParam(1, cur_item->info.clients_max); |
204 this->resize.step_height = NET_PRC__SIZE_OF_ROW; |
267 SetDParam(2, cur_item->info.companies_on); |
205 |
268 SetDParam(3, cur_item->info.companies_max); |
206 this->field = NGWW_PLAYER; |
269 DrawStringCentered(w->widget[NGWW_CLIENTS].left + 39, y, STR_NETWORK_GENERAL_ONLINE, TC_GOLD); |
207 this->server = NULL; |
270 |
208 |
271 /* only draw icons if the server is online */ |
209 this->sort_list = NULL; |
272 if (cur_item->online) { |
210 this->ld.flags = VL_REBUILD | (_ng_sorting.order ? VL_DESC : VL_NONE); |
273 /* draw a lock if the server is password protected */ |
211 this->ld.sort_type = _ng_sorting.criteria; |
274 if (cur_item->info.use_password) DrawSprite(SPR_LOCK, PAL_NONE, w->widget[NGWW_INFO].left + 5, y - 1); |
212 |
275 |
213 this->FindWindowPlacementAndResize(desc); |
276 /* draw red or green icon, depending on compatibility with server */ |
214 } |
277 DrawSprite(SPR_BLOT, (cur_item->info.compatible ? PALETTE_TO_GREEN : (cur_item->info.version_compatible ? PALETTE_TO_YELLOW : PALETTE_TO_RED)), w->widget[NGWW_INFO].left + 15, y); |
215 |
278 |
216 ~NetworkGameWindow() |
279 /* draw flag according to server language */ |
217 { |
280 DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, PAL_NONE, w->widget[NGWW_INFO].left + 25, y); |
218 free(this->sort_list); |
281 } |
219 } |
282 } |
220 |
283 |
221 /** |
284 /** |
222 * (Re)build the network game list as its amount has changed because |
285 * Handler of actions done in the NetworkStartServer window |
223 * an item has been added or deleted for example |
286 * |
224 */ |
287 * @param w pointer to the Window structure |
225 void BuildNetworkGameList() |
288 * @param e pointer to window event |
226 { |
289 * @note Uses network_ql_d (network_d, querystr_d and list_d) WP macro |
227 NetworkGameList *ngl_temp; |
290 * @see struct _network_game_window_widgets |
228 uint n = 0; |
291 * @see enum NetworkGameWindowWidgets |
229 |
292 */ |
230 if (!(this->ld.flags & VL_REBUILD)) return; |
293 |
231 |
294 static void NetworkGameWindowWndProc(Window *w, WindowEvent *e) |
232 /* Count the number of games in the list */ |
295 { |
233 for (ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) n++; |
296 network_d *nd = &WP(w, network_ql_d).n; |
234 if (n == 0) return; |
297 list_d *ld = &WP(w, network_ql_d).l; |
235 |
298 |
236 /* Create temporary array of games to use for listing */ |
299 switch (e->event) { |
237 this->sort_list = ReallocT(this->sort_list, n); |
300 case WE_CREATE: // Focus input box |
238 this->ld.list_length = n; |
301 w->vscroll.cap = 11; |
239 |
302 w->resize.step_height = NET_PRC__SIZE_OF_ROW; |
240 for (n = 0, ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) { |
303 |
241 this->sort_list[n++] = ngl_temp; |
304 nd->field = NGWW_PLAYER; |
242 } |
305 nd->server = NULL; |
243 |
306 |
244 /* Force resort */ |
307 WP(w, network_ql_d).sort_list = NULL; |
245 this->ld.flags &= ~VL_REBUILD; |
308 ld->flags = VL_REBUILD | (_ng_sorting.order ? VL_DESC : VL_NONE); |
246 this->ld.flags |= VL_RESORT; |
309 ld->sort_type = _ng_sorting.criteria; |
247 } |
310 break; |
248 |
311 |
249 void SortNetworkGameList() |
312 case WE_PAINT: { |
250 { |
313 const NetworkGameList *sel = nd->server; |
251 static NGameNameSortFunction * const ngame_sorter[] = { |
314 const SortButtonState arrow = (ld->flags & VL_DESC) ? SBS_DOWN : SBS_UP; |
252 &NGameNameSorter, |
315 |
253 &NGameClientSorter, |
316 if (ld->flags & VL_REBUILD) { |
254 &NGameAllowedSorter |
317 BuildNetworkGameList(&WP(w, network_ql_d)); |
255 }; |
318 SetVScrollCount(w, ld->list_length); |
256 |
|
257 NetworkGameList *item; |
|
258 uint i; |
|
259 |
|
260 if (!(this->ld.flags & VL_RESORT)) return; |
|
261 if (this->ld.list_length == 0) return; |
|
262 |
|
263 _internal_sort_order = !!(this->ld.flags & VL_DESC); |
|
264 qsort(this->sort_list, this->ld.list_length, sizeof(this->sort_list[0]), ngame_sorter[this->ld.sort_type]); |
|
265 |
|
266 /* After sorting ngl->sort_list contains the sorted items. Put these back |
|
267 * into the original list. Basically nothing has changed, we are only |
|
268 * shuffling the ->next pointers */ |
|
269 _network_game_list = this->sort_list[0]; |
|
270 for (item = _network_game_list, i = 1; i != this->ld.list_length; i++) { |
|
271 item->next = this->sort_list[i]; |
|
272 item = item->next; |
|
273 } |
|
274 item->next = NULL; |
|
275 |
|
276 this->ld.flags &= ~VL_RESORT; |
|
277 } |
|
278 |
|
279 /** |
|
280 * Draw a single server line. |
|
281 * @param cur_item the server to draw. |
|
282 * @param y from where to draw? |
|
283 * @param highlight does the line need to be highlighted? |
|
284 */ |
|
285 void DrawServerLine(const NetworkGameList *cur_item, uint y, bool highlight) |
|
286 { |
|
287 /* show highlighted item with a different colour */ |
|
288 if (highlight) GfxFillRect(this->widget[NGWW_NAME].left + 1, y - 2, this->widget[NGWW_INFO].right - 1, y + 9, 10); |
|
289 |
|
290 SetDParamStr(0, cur_item->info.server_name); |
|
291 DrawStringTruncated(this->widget[NGWW_NAME].left + 5, y, STR_02BD, TC_BLACK, this->widget[NGWW_NAME].right - this->widget[NGWW_NAME].left - 5); |
|
292 |
|
293 SetDParam(0, cur_item->info.clients_on); |
|
294 SetDParam(1, cur_item->info.clients_max); |
|
295 SetDParam(2, cur_item->info.companies_on); |
|
296 SetDParam(3, cur_item->info.companies_max); |
|
297 DrawStringCentered(this->widget[NGWW_CLIENTS].left + 39, y, STR_NETWORK_GENERAL_ONLINE, TC_GOLD); |
|
298 |
|
299 /* only draw icons if the server is online */ |
|
300 if (cur_item->online) { |
|
301 /* draw a lock if the server is password protected */ |
|
302 if (cur_item->info.use_password) DrawSprite(SPR_LOCK, PAL_NONE, this->widget[NGWW_INFO].left + 5, y - 1); |
|
303 |
|
304 /* draw red or green icon, depending on compatibility with server */ |
|
305 DrawSprite(SPR_BLOT, (cur_item->info.compatible ? PALETTE_TO_GREEN : (cur_item->info.version_compatible ? PALETTE_TO_YELLOW : PALETTE_TO_RED)), this->widget[NGWW_INFO].left + 15, y); |
|
306 |
|
307 /* draw flag according to server language */ |
|
308 DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, PAL_NONE, this->widget[NGWW_INFO].left + 25, y); |
|
309 } |
|
310 } |
|
311 |
|
312 virtual void OnPaint() |
|
313 { |
|
314 const NetworkGameList *sel = this->server; |
|
315 const SortButtonState arrow = (this->ld.flags & VL_DESC) ? SBS_DOWN : SBS_UP; |
|
316 |
|
317 if (this->ld.flags & VL_REBUILD) { |
|
318 this->BuildNetworkGameList(); |
|
319 SetVScrollCount(this, this->ld.list_length); |
|
320 } |
|
321 if (this->ld.flags & VL_RESORT) this->SortNetworkGameList(); |
|
322 |
|
323 /* 'Refresh' button invisible if no server selected */ |
|
324 this->SetWidgetDisabledState(NGWW_REFRESH, sel == NULL); |
|
325 /* 'Join' button disabling conditions */ |
|
326 this->SetWidgetDisabledState(NGWW_JOIN, sel == NULL || // no Selected Server |
|
327 !sel->online || // Server offline |
|
328 sel->info.clients_on >= sel->info.clients_max || // Server full |
|
329 !sel->info.compatible); // Revision mismatch |
|
330 |
|
331 /* 'NewGRF Settings' button invisible if no NewGRF is used */ |
|
332 this->SetWidgetHiddenState(NGWW_NEWGRF, sel == NULL || |
|
333 !sel->online || |
|
334 sel->info.grfconfig == NULL); |
|
335 |
|
336 SetDParam(0, 0x00); |
|
337 SetDParam(1, _lan_internet_types_dropdown[_network_lan_internet]); |
|
338 DrawWindowWidgets(this); |
|
339 |
|
340 /* Edit box to set player name */ |
|
341 this->DrawEditBox(NGWW_PLAYER); |
|
342 |
|
343 DrawString(this->widget[NGWW_PLAYER].left - 100, 23, STR_NETWORK_PLAYER_NAME, TC_GOLD); |
|
344 |
|
345 /* Sort based on widgets: name, clients, compatibility */ |
|
346 switch (this->ld.sort_type) { |
|
347 case NGWW_NAME - NGWW_NAME: DrawSortButtonState(this, NGWW_NAME, arrow); break; |
|
348 case NGWW_CLIENTS - NGWW_NAME: DrawSortButtonState(this, NGWW_CLIENTS, arrow); break; |
|
349 case NGWW_INFO - NGWW_NAME: DrawSortButtonState(this, NGWW_INFO, arrow); break; |
|
350 } |
|
351 |
|
352 uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3; |
|
353 int32 n = 0; |
|
354 int32 pos = this->vscroll.pos; |
|
355 const NetworkGameList *cur_item = _network_game_list; |
|
356 |
|
357 while (pos > 0 && cur_item != NULL) { |
|
358 pos--; |
|
359 cur_item = cur_item->next; |
|
360 } |
|
361 |
|
362 while (cur_item != NULL) { |
|
363 this->DrawServerLine(cur_item, y, cur_item == sel); |
|
364 |
|
365 cur_item = cur_item->next; |
|
366 y += NET_PRC__SIZE_OF_ROW; |
|
367 if (++n == this->vscroll.cap) break; // max number of games in the window |
|
368 } |
|
369 |
|
370 const NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port); |
|
371 /* Draw the last joined server, if any */ |
|
372 if (last_joined != NULL) this->DrawServerLine(last_joined, y = this->widget[NGWW_LASTJOINED].top + 3, last_joined == sel); |
|
373 |
|
374 /* Draw the right menu */ |
|
375 GfxFillRect(this->widget[NGWW_DETAILS].left + 1, 43, this->widget[NGWW_DETAILS].right - 1, 92, 157); |
|
376 if (sel == NULL) { |
|
377 DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 58, STR_NETWORK_GAME_INFO, TC_FROMSTRING); |
|
378 } else if (!sel->online) { |
|
379 SetDParamStr(0, sel->info.server_name); |
|
380 DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 68, STR_ORANGE, TC_FROMSTRING); // game name |
|
381 |
|
382 DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 132, STR_NETWORK_SERVER_OFFLINE, TC_FROMSTRING); // server offline |
|
383 } else { // show game info |
|
384 uint16 y = 100; |
|
385 const uint16 x = this->widget[NGWW_DETAILS].left + 5; |
|
386 |
|
387 DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 48, STR_NETWORK_GAME_INFO, TC_FROMSTRING); |
|
388 |
|
389 |
|
390 SetDParamStr(0, sel->info.server_name); |
|
391 DrawStringCenteredTruncated(this->widget[NGWW_DETAILS].left, this->widget[NGWW_DETAILS].right, 62, STR_ORANGE, TC_BLACK); // game name |
|
392 |
|
393 SetDParamStr(0, sel->info.map_name); |
|
394 DrawStringCenteredTruncated(this->widget[NGWW_DETAILS].left, this->widget[NGWW_DETAILS].right, 74, STR_02BD, TC_BLACK); // map name |
|
395 |
|
396 SetDParam(0, sel->info.clients_on); |
|
397 SetDParam(1, sel->info.clients_max); |
|
398 SetDParam(2, sel->info.companies_on); |
|
399 SetDParam(3, sel->info.companies_max); |
|
400 DrawString(x, y, STR_NETWORK_CLIENTS, TC_GOLD); |
|
401 y += 10; |
|
402 |
|
403 SetDParam(0, STR_NETWORK_LANG_ANY + sel->info.server_lang); |
|
404 DrawString(x, y, STR_NETWORK_LANGUAGE, TC_GOLD); // server language |
|
405 y += 10; |
|
406 |
|
407 SetDParam(0, STR_TEMPERATE_LANDSCAPE + sel->info.map_set); |
|
408 DrawString(x, y, STR_NETWORK_TILESET, TC_GOLD); // tileset |
|
409 y += 10; |
|
410 |
|
411 SetDParam(0, sel->info.map_width); |
|
412 SetDParam(1, sel->info.map_height); |
|
413 DrawString(x, y, STR_NETWORK_MAP_SIZE, TC_GOLD); // map size |
|
414 y += 10; |
|
415 |
|
416 SetDParamStr(0, sel->info.server_revision); |
|
417 DrawString(x, y, STR_NETWORK_SERVER_VERSION, TC_GOLD); // server version |
|
418 y += 10; |
|
419 |
|
420 SetDParamStr(0, sel->info.hostname); |
|
421 SetDParam(1, sel->port); |
|
422 DrawString(x, y, STR_NETWORK_SERVER_ADDRESS, TC_GOLD); // server address |
|
423 y += 10; |
|
424 |
|
425 SetDParam(0, sel->info.start_date); |
|
426 DrawString(x, y, STR_NETWORK_START_DATE, TC_GOLD); // start date |
|
427 y += 10; |
|
428 |
|
429 SetDParam(0, sel->info.game_date); |
|
430 DrawString(x, y, STR_NETWORK_CURRENT_DATE, TC_GOLD); // current date |
|
431 y += 10; |
|
432 |
|
433 y += 2; |
|
434 |
|
435 if (!sel->info.compatible) { |
|
436 DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, sel->info.version_compatible ? STR_NETWORK_GRF_MISMATCH : STR_NETWORK_VERSION_MISMATCH, TC_FROMSTRING); // server mismatch |
|
437 } else if (sel->info.clients_on == sel->info.clients_max) { |
|
438 /* Show: server full, when clients_on == clients_max */ |
|
439 DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_SERVER_FULL, TC_FROMSTRING); // server full |
|
440 } else if (sel->info.use_password) { |
|
441 DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_PASSWORD, TC_FROMSTRING); // password warning |
319 } |
442 } |
320 if (ld->flags & VL_RESORT) SortNetworkGameList(&WP(w, network_ql_d)); |
443 |
321 |
444 y += 10; |
322 /* 'Refresh' button invisible if no server selected */ |
445 } |
323 w->SetWidgetDisabledState(NGWW_REFRESH, sel == NULL); |
446 } |
324 /* 'Join' button disabling conditions */ |
447 |
325 w->SetWidgetDisabledState(NGWW_JOIN, sel == NULL || // no Selected Server |
448 virtual void OnClick(Point pt, int widget) |
326 !sel->online || // Server offline |
449 { |
327 sel->info.clients_on >= sel->info.clients_max || // Server full |
450 this->field = widget; |
328 !sel->info.compatible); // Revision mismatch |
451 switch (widget) { |
329 |
452 case NGWW_PLAYER: |
330 /* 'NewGRF Settings' button invisible if no NewGRF is used */ |
453 ShowOnScreenKeyboard(this, NGWW_PLAYER, 0, 0); |
331 w->SetWidgetHiddenState(NGWW_NEWGRF, sel == NULL || |
454 break; |
332 !sel->online || |
455 |
333 sel->info.grfconfig == NULL); |
456 case NGWW_CANCEL: // Cancel button |
334 |
457 DeleteWindowById(WC_NETWORK_WINDOW, 0); |
335 SetDParam(0, 0x00); |
458 break; |
336 SetDParam(1, _lan_internet_types_dropdown[_network_lan_internet]); |
459 |
337 DrawWindowWidgets(w); |
460 case NGWW_CONN_BTN: // 'Connection' droplist |
338 |
461 ShowDropDownMenu(this, _lan_internet_types_dropdown, _network_lan_internet, NGWW_CONN_BTN, 0, 0); // do it for widget NSSW_CONN_BTN |
339 /* Edit box to set player name */ |
462 break; |
340 DrawEditBox(w, &WP(w, network_ql_d).q, NGWW_PLAYER); |
463 |
341 |
464 case NGWW_NAME: // Sort by name |
342 DrawString(w->widget[NGWW_PLAYER].left - 100, 23, STR_NETWORK_PLAYER_NAME, TC_GOLD); |
465 case NGWW_CLIENTS: // Sort by connected clients |
343 |
466 case NGWW_INFO: // Connectivity (green dot) |
344 /* Sort based on widgets: name, clients, compatibility */ |
467 if (this->ld.sort_type == widget - NGWW_NAME) this->ld.flags ^= VL_DESC; |
345 switch (ld->sort_type) { |
468 this->ld.flags |= VL_RESORT; |
346 case NGWW_NAME - NGWW_NAME: DrawSortButtonState(w, NGWW_NAME, arrow); break; |
469 this->ld.sort_type = widget - NGWW_NAME; |
347 case NGWW_CLIENTS - NGWW_NAME: DrawSortButtonState(w, NGWW_CLIENTS, arrow); break; |
470 |
348 case NGWW_INFO - NGWW_NAME: DrawSortButtonState(w, NGWW_INFO, arrow); break; |
471 _ng_sorting.order = !!(this->ld.flags & VL_DESC); |
|
472 _ng_sorting.criteria = this->ld.sort_type; |
|
473 this->SetDirty(); |
|
474 break; |
|
475 |
|
476 case NGWW_MATRIX: { // Matrix to show networkgames |
|
477 NetworkGameList *cur_item; |
|
478 uint32 id_v = (pt.y - NET_PRC__OFFSET_TOP_WIDGET) / NET_PRC__SIZE_OF_ROW; |
|
479 |
|
480 if (id_v >= this->vscroll.cap) return; // click out of bounds |
|
481 id_v += this->vscroll.pos; |
|
482 |
|
483 cur_item = _network_game_list; |
|
484 for (; id_v > 0 && cur_item != NULL; id_v--) cur_item = cur_item->next; |
|
485 |
|
486 this->server = cur_item; |
|
487 this->SetDirty(); |
|
488 } break; |
|
489 |
|
490 case NGWW_LASTJOINED: { |
|
491 NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port); |
|
492 if (last_joined != NULL) { |
|
493 this->server = last_joined; |
|
494 this->SetDirty(); |
|
495 } |
|
496 } break; |
|
497 |
|
498 case NGWW_FIND: // Find server automatically |
|
499 switch (_network_lan_internet) { |
|
500 case 0: NetworkUDPSearchGame(); break; |
|
501 case 1: NetworkUDPQueryMasterServer(); break; |
|
502 } |
|
503 break; |
|
504 |
|
505 case NGWW_ADD: // Add a server |
|
506 ShowQueryString( |
|
507 BindCString(_network_default_ip), |
|
508 STR_NETWORK_ENTER_IP, |
|
509 31 | 0x1000, // maximum number of characters OR |
|
510 250, // characters up to this width pixels, whichever is satisfied first |
|
511 this, CS_ALPHANUMERAL); |
|
512 break; |
|
513 |
|
514 case NGWW_START: // Start server |
|
515 ShowNetworkStartServerWindow(); |
|
516 break; |
|
517 |
|
518 case NGWW_JOIN: // Join Game |
|
519 if (this->server != NULL) { |
|
520 snprintf(_network_last_host, sizeof(_network_last_host), "%s", inet_ntoa(*(struct in_addr *)&this->server->ip)); |
|
521 _network_last_port = this->server->port; |
|
522 ShowNetworkLobbyWindow(this->server); |
|
523 } |
|
524 break; |
|
525 |
|
526 case NGWW_REFRESH: // Refresh |
|
527 if (this->server != NULL) NetworkUDPQueryServer(this->server->info.hostname, this->server->port); |
|
528 break; |
|
529 |
|
530 case NGWW_NEWGRF: // NewGRF Settings |
|
531 if (this->server != NULL) ShowNewGRFSettings(false, false, false, &this->server->info.grfconfig); |
|
532 break; |
|
533 } |
|
534 } |
|
535 |
|
536 virtual void OnDropdownSelect(int widget, int index) |
|
537 { |
|
538 switch (widget) { |
|
539 case NGWW_CONN_BTN: |
|
540 _network_lan_internet = index; |
|
541 break; |
|
542 |
|
543 default: |
|
544 NOT_REACHED(); |
|
545 } |
|
546 |
|
547 this->SetDirty(); |
|
548 } |
|
549 |
|
550 virtual void OnMouseLoop() |
|
551 { |
|
552 if (this->field == NGWW_PLAYER) this->HandleEditBox(NGWW_PLAYER); |
|
553 } |
|
554 |
|
555 virtual void OnInvalidateData(int data) |
|
556 { |
|
557 if (data != 0) this->server = NULL; |
|
558 this->ld.flags |= VL_REBUILD; |
|
559 this->SetDirty(); |
|
560 } |
|
561 |
|
562 virtual bool OnKeyPress(uint16 key, uint16 keycode) |
|
563 { |
|
564 bool cont = true; |
|
565 if (this->field != NGWW_PLAYER) { |
|
566 if (this->server != NULL) { |
|
567 if (keycode == WKC_DELETE) { // Press 'delete' to remove servers |
|
568 NetworkGameListRemoveItem(this->server); |
|
569 NetworkRebuildHostList(); |
|
570 this->server = NULL; |
|
571 } |
349 } |
572 } |
350 |
573 return cont; |
351 uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3; |
574 } |
352 int32 n = 0; |
575 |
353 int32 pos = w->vscroll.pos; |
576 if (this->HandleEditBoxKey(NGWW_PLAYER, keycode, key, cont) == 1) return cont; // enter pressed |
354 const NetworkGameList *cur_item = _network_game_list; |
577 |
355 |
578 /* The name is only allowed when it starts with a letter! */ |
356 while (pos > 0 && cur_item != NULL) { |
579 if (StrEmpty(this->edit_str_buf) && this->edit_str_buf[0] != ' ') { |
357 pos--; |
580 ttd_strlcpy(_network_player_name, this->edit_str_buf, lengthof(_network_player_name)); |
358 cur_item = cur_item->next; |
581 } else { |
359 } |
582 ttd_strlcpy(_network_player_name, "Player", lengthof(_network_player_name)); |
360 |
583 } |
361 while (cur_item != NULL) { |
584 return cont; |
362 DrawServerLine(w, cur_item, y, cur_item == sel); |
585 } |
363 |
586 |
364 cur_item = cur_item->next; |
587 virtual void OnQueryTextFinished(char *str) |
365 y += NET_PRC__SIZE_OF_ROW; |
588 { |
366 if (++n == w->vscroll.cap) break; // max number of games in the window |
589 if (!StrEmpty(str)) { |
367 } |
590 NetworkAddServer(str); |
368 |
591 NetworkRebuildHostList(); |
369 const NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port); |
592 } |
370 /* Draw the last joined server, if any */ |
593 } |
371 if (last_joined != NULL) DrawServerLine(w, last_joined, y = w->widget[NGWW_LASTJOINED].top + 3, last_joined == sel); |
594 |
372 |
595 virtual void OnResize(Point new_size, Point delta) |
373 /* Draw the right menu */ |
596 { |
374 GfxFillRect(w->widget[NGWW_DETAILS].left + 1, 43, w->widget[NGWW_DETAILS].right - 1, 92, 157); |
597 this->vscroll.cap += delta.y / (int)this->resize.step_height; |
375 if (sel == NULL) { |
598 |
376 DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, 58, STR_NETWORK_GAME_INFO, TC_FROMSTRING); |
599 this->widget[NGWW_MATRIX].data = (this->vscroll.cap << 8) + 1; |
377 } else if (!sel->online) { |
600 |
378 SetDParamStr(0, sel->info.server_name); |
601 SetVScrollCount(this, this->ld.list_length); |
379 DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, 68, STR_ORANGE, TC_FROMSTRING); // game name |
602 |
380 |
603 int widget_width = this->widget[NGWW_FIND].right - this->widget[NGWW_FIND].left; |
381 DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, 132, STR_NETWORK_SERVER_OFFLINE, TC_FROMSTRING); // server offline |
604 int space = (this->width - 4 * widget_width - 25) / 3; |
382 } else { // show game info |
605 |
383 uint16 y = 100; |
606 int offset = 10; |
384 const uint16 x = w->widget[NGWW_DETAILS].left + 5; |
607 for (uint i = 0; i < 4; i++) { |
385 |
608 this->widget[NGWW_FIND + i].left = offset; |
386 DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, 48, STR_NETWORK_GAME_INFO, TC_FROMSTRING); |
609 offset += widget_width; |
387 |
610 this->widget[NGWW_FIND + i].right = offset; |
388 |
611 offset += space; |
389 SetDParamStr(0, sel->info.server_name); |
612 } |
390 DrawStringCenteredTruncated(w->widget[NGWW_DETAILS].left, w->widget[NGWW_DETAILS].right, 62, STR_ORANGE, TC_BLACK); // game name |
613 } |
391 |
614 }; |
392 SetDParamStr(0, sel->info.map_name); |
|
393 DrawStringCenteredTruncated(w->widget[NGWW_DETAILS].left, w->widget[NGWW_DETAILS].right, 74, STR_02BD, TC_BLACK); // map name |
|
394 |
|
395 SetDParam(0, sel->info.clients_on); |
|
396 SetDParam(1, sel->info.clients_max); |
|
397 SetDParam(2, sel->info.companies_on); |
|
398 SetDParam(3, sel->info.companies_max); |
|
399 DrawString(x, y, STR_NETWORK_CLIENTS, TC_GOLD); |
|
400 y += 10; |
|
401 |
|
402 SetDParam(0, STR_NETWORK_LANG_ANY + sel->info.server_lang); |
|
403 DrawString(x, y, STR_NETWORK_LANGUAGE, TC_GOLD); // server language |
|
404 y += 10; |
|
405 |
|
406 SetDParam(0, STR_TEMPERATE_LANDSCAPE + sel->info.map_set); |
|
407 DrawString(x, y, STR_NETWORK_TILESET, TC_GOLD); // tileset |
|
408 y += 10; |
|
409 |
|
410 SetDParam(0, sel->info.map_width); |
|
411 SetDParam(1, sel->info.map_height); |
|
412 DrawString(x, y, STR_NETWORK_MAP_SIZE, TC_GOLD); // map size |
|
413 y += 10; |
|
414 |
|
415 SetDParamStr(0, sel->info.server_revision); |
|
416 DrawString(x, y, STR_NETWORK_SERVER_VERSION, TC_GOLD); // server version |
|
417 y += 10; |
|
418 |
|
419 SetDParamStr(0, sel->info.hostname); |
|
420 SetDParam(1, sel->port); |
|
421 DrawString(x, y, STR_NETWORK_SERVER_ADDRESS, TC_GOLD); // server address |
|
422 y += 10; |
|
423 |
|
424 SetDParam(0, sel->info.start_date); |
|
425 DrawString(x, y, STR_NETWORK_START_DATE, TC_GOLD); // start date |
|
426 y += 10; |
|
427 |
|
428 SetDParam(0, sel->info.game_date); |
|
429 DrawString(x, y, STR_NETWORK_CURRENT_DATE, TC_GOLD); // current date |
|
430 y += 10; |
|
431 |
|
432 y += 2; |
|
433 |
|
434 if (!sel->info.compatible) { |
|
435 DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, y, sel->info.version_compatible ? STR_NETWORK_GRF_MISMATCH : STR_NETWORK_VERSION_MISMATCH, TC_FROMSTRING); // server mismatch |
|
436 } else if (sel->info.clients_on == sel->info.clients_max) { |
|
437 /* Show: server full, when clients_on == clients_max */ |
|
438 DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_SERVER_FULL, TC_FROMSTRING); // server full |
|
439 } else if (sel->info.use_password) { |
|
440 DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_PASSWORD, TC_FROMSTRING); // password warning |
|
441 } |
|
442 |
|
443 y += 10; |
|
444 } |
|
445 } break; |
|
446 |
|
447 case WE_CLICK: |
|
448 nd->field = e->we.click.widget; |
|
449 switch (e->we.click.widget) { |
|
450 case NGWW_PLAYER: |
|
451 ShowOnScreenKeyboard(w, &WP(w, network_ql_d).q, NGWW_PLAYER, 0, 0); |
|
452 break; |
|
453 |
|
454 case NGWW_CANCEL: // Cancel button |
|
455 DeleteWindowById(WC_NETWORK_WINDOW, 0); |
|
456 break; |
|
457 |
|
458 case NGWW_CONN_BTN: // 'Connection' droplist |
|
459 ShowDropDownMenu(w, _lan_internet_types_dropdown, _network_lan_internet, NGWW_CONN_BTN, 0, 0); // do it for widget NSSW_CONN_BTN |
|
460 break; |
|
461 |
|
462 case NGWW_NAME: // Sort by name |
|
463 case NGWW_CLIENTS: // Sort by connected clients |
|
464 case NGWW_INFO: // Connectivity (green dot) |
|
465 if (ld->sort_type == e->we.click.widget - NGWW_NAME) ld->flags ^= VL_DESC; |
|
466 ld->flags |= VL_RESORT; |
|
467 ld->sort_type = e->we.click.widget - NGWW_NAME; |
|
468 |
|
469 _ng_sorting.order = !!(ld->flags & VL_DESC); |
|
470 _ng_sorting.criteria = ld->sort_type; |
|
471 SetWindowDirty(w); |
|
472 break; |
|
473 |
|
474 case NGWW_MATRIX: { // Matrix to show networkgames |
|
475 NetworkGameList *cur_item; |
|
476 uint32 id_v = (e->we.click.pt.y - NET_PRC__OFFSET_TOP_WIDGET) / NET_PRC__SIZE_OF_ROW; |
|
477 |
|
478 if (id_v >= w->vscroll.cap) return; // click out of bounds |
|
479 id_v += w->vscroll.pos; |
|
480 |
|
481 cur_item = _network_game_list; |
|
482 for (; id_v > 0 && cur_item != NULL; id_v--) cur_item = cur_item->next; |
|
483 |
|
484 nd->server = cur_item; |
|
485 SetWindowDirty(w); |
|
486 } break; |
|
487 |
|
488 case NGWW_LASTJOINED: { |
|
489 NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port); |
|
490 if (last_joined != NULL) { |
|
491 nd->server = last_joined; |
|
492 SetWindowDirty(w); |
|
493 } |
|
494 } break; |
|
495 |
|
496 case NGWW_FIND: // Find server automatically |
|
497 switch (_network_lan_internet) { |
|
498 case 0: NetworkUDPSearchGame(); break; |
|
499 case 1: NetworkUDPQueryMasterServer(); break; |
|
500 } |
|
501 break; |
|
502 |
|
503 case NGWW_ADD: // Add a server |
|
504 ShowQueryString( |
|
505 BindCString(_network_default_ip), |
|
506 STR_NETWORK_ENTER_IP, |
|
507 31 | 0x1000, // maximum number of characters OR |
|
508 250, // characters up to this width pixels, whichever is satisfied first |
|
509 w, CS_ALPHANUMERAL); |
|
510 break; |
|
511 |
|
512 case NGWW_START: // Start server |
|
513 ShowNetworkStartServerWindow(); |
|
514 break; |
|
515 |
|
516 case NGWW_JOIN: // Join Game |
|
517 if (nd->server != NULL) { |
|
518 snprintf(_network_last_host, sizeof(_network_last_host), "%s", inet_ntoa(*(struct in_addr *)&nd->server->ip)); |
|
519 _network_last_port = nd->server->port; |
|
520 ShowNetworkLobbyWindow(nd->server); |
|
521 } |
|
522 break; |
|
523 |
|
524 case NGWW_REFRESH: // Refresh |
|
525 if (nd->server != NULL) NetworkUDPQueryServer(nd->server->info.hostname, nd->server->port); |
|
526 break; |
|
527 |
|
528 case NGWW_NEWGRF: // NewGRF Settings |
|
529 if (nd->server != NULL) ShowNewGRFSettings(false, false, false, &nd->server->info.grfconfig); |
|
530 break; |
|
531 } |
|
532 break; |
|
533 |
|
534 case WE_DROPDOWN_SELECT: // we have selected a dropdown item in the list |
|
535 switch (e->we.dropdown.button) { |
|
536 case NGWW_CONN_BTN: |
|
537 _network_lan_internet = e->we.dropdown.index; |
|
538 break; |
|
539 |
|
540 default: |
|
541 NOT_REACHED(); |
|
542 } |
|
543 |
|
544 SetWindowDirty(w); |
|
545 break; |
|
546 |
|
547 case WE_MOUSELOOP: |
|
548 if (nd->field == NGWW_PLAYER) HandleEditBox(w, &WP(w, network_ql_d).q, NGWW_PLAYER); |
|
549 break; |
|
550 |
|
551 case WE_INVALIDATE_DATA: |
|
552 if (e->we.invalidate.data != 0) nd->server = NULL; |
|
553 ld->flags |= VL_REBUILD; |
|
554 SetWindowDirty(w); |
|
555 break; |
|
556 |
|
557 case WE_KEYPRESS: |
|
558 if (nd->field != NGWW_PLAYER) { |
|
559 if (nd->server != NULL) { |
|
560 if (e->we.keypress.keycode == WKC_DELETE) { // Press 'delete' to remove servers |
|
561 NetworkGameListRemoveItem(nd->server); |
|
562 NetworkRebuildHostList(); |
|
563 nd->server = NULL; |
|
564 } |
|
565 } |
|
566 break; |
|
567 } |
|
568 |
|
569 if (HandleEditBoxKey(w, &WP(w, network_ql_d).q, NGWW_PLAYER, e) == 1) break; // enter pressed |
|
570 |
|
571 /* The name is only allowed when it starts with a letter! */ |
|
572 if (_edit_str_net_buf[0] != '\0' && _edit_str_net_buf[0] != ' ') { |
|
573 ttd_strlcpy(_network_player_name, _edit_str_net_buf, lengthof(_network_player_name)); |
|
574 } else { |
|
575 ttd_strlcpy(_network_player_name, "Player", lengthof(_network_player_name)); |
|
576 } |
|
577 |
|
578 break; |
|
579 |
|
580 case WE_ON_EDIT_TEXT: |
|
581 if (!StrEmpty(e->we.edittext.str)) { |
|
582 NetworkAddServer(e->we.edittext.str); |
|
583 NetworkRebuildHostList(); |
|
584 } |
|
585 break; |
|
586 |
|
587 case WE_RESIZE: { |
|
588 w->vscroll.cap += e->we.sizing.diff.y / (int)w->resize.step_height; |
|
589 |
|
590 w->widget[NGWW_MATRIX].data = (w->vscroll.cap << 8) + 1; |
|
591 |
|
592 SetVScrollCount(w, ld->list_length); |
|
593 |
|
594 int widget_width = w->widget[NGWW_FIND].right - w->widget[NGWW_FIND].left; |
|
595 int space = (w->width - 4 * widget_width - 25) / 3; |
|
596 |
|
597 int offset = 10; |
|
598 for (uint i = 0; i < 4; i++) { |
|
599 w->widget[NGWW_FIND + i].left = offset; |
|
600 offset += widget_width; |
|
601 w->widget[NGWW_FIND + i].right = offset; |
|
602 offset += space; |
|
603 } |
|
604 } break; |
|
605 |
|
606 case WE_DESTROY: // Nicely clean up the sort-list |
|
607 free(WP(w, network_ql_d).sort_list); |
|
608 break; |
|
609 } |
|
610 } |
|
611 |
615 |
612 static const Widget _network_game_window_widgets[] = { |
616 static const Widget _network_game_window_widgets[] = { |
613 /* TOP */ |
617 /* TOP */ |
614 { WWT_CLOSEBOX, RESIZE_NONE, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // NGWW_CLOSE |
618 { WWT_CLOSEBOX, RESIZE_NONE, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // NGWW_CLOSE |
615 { WWT_CAPTION, RESIZE_RIGHT, BGC, 11, 449, 0, 13, STR_NETWORK_MULTIPLAYER, STR_NULL}, // NGWW_CAPTION |
619 { WWT_CAPTION, RESIZE_RIGHT, BGC, 11, 449, 0, 13, STR_NETWORK_MULTIPLAYER, STR_NULL}, // NGWW_CAPTION |
713 NSSW_START = 25, ///< 'Start' button |
708 NSSW_START = 25, ///< 'Start' button |
714 NSSW_LOAD = 26, ///< 'Load' button |
709 NSSW_LOAD = 26, ///< 'Load' button |
715 NSSW_CANCEL = 27, ///< 'Cancel' button |
710 NSSW_CANCEL = 27, ///< 'Cancel' button |
716 }; |
711 }; |
717 |
712 |
718 /** |
713 struct NetworkStartServerWindow : public QueryStringBaseWindow { |
719 * Handler of actions done in the NetworkStartServer window |
714 byte field; ///< Selected text-field |
720 * |
715 FiosItem *map; ///< Selected map |
721 * @param w pointer to the Window structure |
716 byte widget_id; ///< The widget that has the pop-up input menu |
722 * @param e pointer to window event |
717 |
723 * @note Uses network_ql_d (network_d, querystr_d and list_d) WP macro |
718 NetworkStartServerWindow(const WindowDesc *desc) : QueryStringBaseWindow(desc) |
724 * @see struct _network_start_server_window_widgets |
719 { |
725 * @see enum NetworkStartServerWidgets |
720 ttd_strlcpy(this->edit_str_buf, _network_server_name, lengthof(this->edit_str_buf)); |
726 */ |
721 |
727 static void NetworkStartServerWindowWndProc(Window *w, WindowEvent *e) |
722 _saveload_mode = SLD_NEW_GAME; |
728 { |
723 BuildFileList(); |
729 network_d *nd = &WP(w, network_ql_d).n; |
724 this->vscroll.cap = 12; |
730 |
725 this->vscroll.count = _fios_num + 1; |
731 switch (e->event) { |
726 |
732 case WE_CREATE: // focus input box |
727 this->afilter = CS_ALPHANUMERAL; |
733 nd->field = NSSW_GAMENAME; |
728 InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 160); |
734 _network_game_info.use_password = (_network_server_password[0] != '\0'); |
729 |
735 break; |
730 this->field = NSSW_GAMENAME; |
736 |
731 _network_game_info.use_password = !StrEmpty(_network_server_password); |
737 case WE_PAINT: { |
732 |
738 int y = NSSWND_START, pos; |
733 this->FindWindowPlacementAndResize(desc); |
739 const FiosItem *item; |
734 } |
740 |
735 |
741 /* draw basic widgets */ |
736 virtual void OnPaint() |
742 SetDParam(1, _connection_types_dropdown[_network_advertise]); |
737 { |
743 SetDParam(2, _network_game_info.clients_max); |
738 int y = NSSWND_START, pos; |
744 SetDParam(3, _network_game_info.companies_max); |
739 const FiosItem *item; |
745 SetDParam(4, _network_game_info.spectators_max); |
740 |
746 SetDParam(5, STR_NETWORK_LANG_ANY + _network_game_info.server_lang); |
741 /* draw basic widgets */ |
747 DrawWindowWidgets(w); |
742 SetDParam(1, _connection_types_dropdown[_network_advertise]); |
748 |
743 SetDParam(2, _network_game_info.clients_max); |
749 /* editbox to set game name */ |
744 SetDParam(3, _network_game_info.companies_max); |
750 DrawEditBox(w, &WP(w, network_ql_d).q, NSSW_GAMENAME); |
745 SetDParam(4, _network_game_info.spectators_max); |
751 |
746 SetDParam(5, STR_NETWORK_LANG_ANY + _network_game_info.server_lang); |
752 /* if password is set, draw red '*' next to 'Set password' button */ |
747 DrawWindowWidgets(this); |
753 if (_network_game_info.use_password) DoDrawString("*", 408, 23, TC_RED); |
748 |
754 |
749 /* editbox to set game name */ |
755 /* draw list of maps */ |
750 this->DrawEditBox(NSSW_GAMENAME); |
756 GfxFillRect(11, 63, 258, 215, 0xD7); // black background of maps list |
751 |
757 |
752 /* if password is set, draw red '*' next to 'Set password' button */ |
758 pos = w->vscroll.pos; |
753 if (_network_game_info.use_password) DoDrawString("*", 408, 23, TC_RED); |
759 while (pos < _fios_num + 1) { |
754 |
760 item = _fios_list + pos - 1; |
755 /* draw list of maps */ |
761 if (item == nd->map || (pos == 0 && nd->map == NULL)) |
756 GfxFillRect(11, 63, 258, 215, 0xD7); // black background of maps list |
762 GfxFillRect(11, y - 1, 258, y + 10, 155); // show highlighted item with a different colour |
757 |
763 |
758 pos = this->vscroll.pos; |
764 if (pos == 0) { |
759 while (pos < _fios_num + 1) { |
765 DrawString(14, y, STR_4010_GENERATE_RANDOM_NEW_GAME, TC_DARK_GREEN); |
760 item = _fios_list + pos - 1; |
766 } else { |
761 if (item == this->map || (pos == 0 && this->map == NULL)) |
767 DoDrawString(item->title, 14, y, _fios_colors[item->type] ); |
762 GfxFillRect(11, y - 1, 258, y + 10, 155); // show highlighted item with a different colour |
|
763 |
|
764 if (pos == 0) { |
|
765 DrawString(14, y, STR_4010_GENERATE_RANDOM_NEW_GAME, TC_DARK_GREEN); |
|
766 } else { |
|
767 DoDrawString(item->title, 14, y, _fios_colors[item->type] ); |
|
768 } |
|
769 pos++; |
|
770 y += NSSWND_ROWSIZE; |
|
771 |
|
772 if (y >= this->vscroll.cap * NSSWND_ROWSIZE + NSSWND_START) break; |
|
773 } |
|
774 } |
|
775 |
|
776 virtual void OnClick(Point pt, int widget) |
|
777 { |
|
778 if (widget != NSSW_CONNTYPE_BTN && widget != NSSW_LANGUAGE_BTN) HideDropDownMenu(this); |
|
779 this->field = widget; |
|
780 switch (widget) { |
|
781 case NSSW_CLOSE: // Close 'X' |
|
782 case NSSW_CANCEL: // Cancel button |
|
783 ShowNetworkGameWindow(); |
|
784 break; |
|
785 |
|
786 case NSSW_GAMENAME: |
|
787 ShowOnScreenKeyboard(this, NSSW_GAMENAME, 0, 0); |
|
788 break; |
|
789 |
|
790 case NSSW_SETPWD: // Set password button |
|
791 this->widget_id = NSSW_SETPWD; |
|
792 ShowQueryString(BindCString(_network_server_password), STR_NETWORK_SET_PASSWORD, 20, 250, this, CS_ALPHANUMERAL); |
|
793 break; |
|
794 |
|
795 case NSSW_SELMAP: { // Select map |
|
796 int y = (pt.y - NSSWND_START) / NSSWND_ROWSIZE; |
|
797 |
|
798 y += this->vscroll.pos; |
|
799 if (y >= this->vscroll.count) return; |
|
800 |
|
801 this->map = (y == 0) ? NULL : _fios_list + y - 1; |
|
802 this->SetDirty(); |
|
803 } break; |
|
804 |
|
805 case NSSW_CONNTYPE_BTN: // Connection type |
|
806 ShowDropDownMenu(this, _connection_types_dropdown, _network_advertise, NSSW_CONNTYPE_BTN, 0, 0); // do it for widget NSSW_CONNTYPE_BTN |
|
807 break; |
|
808 |
|
809 case NSSW_CLIENTS_BTND: case NSSW_CLIENTS_BTNU: // Click on up/down button for number of clients |
|
810 case NSSW_COMPANIES_BTND: case NSSW_COMPANIES_BTNU: // Click on up/down button for number of companies |
|
811 case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU: // Click on up/down button for number of spectators |
|
812 /* Don't allow too fast scrolling */ |
|
813 if ((this->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) { |
|
814 this->HandleButtonClick(widget); |
|
815 this->SetDirty(); |
|
816 switch (widget) { |
|
817 default: NOT_REACHED(); |
|
818 case NSSW_CLIENTS_BTND: case NSSW_CLIENTS_BTNU: |
|
819 _network_game_info.clients_max = Clamp(_network_game_info.clients_max + widget - NSSW_CLIENTS_TXT, 2, MAX_CLIENTS); |
|
820 break; |
|
821 case NSSW_COMPANIES_BTND: case NSSW_COMPANIES_BTNU: |
|
822 _network_game_info.companies_max = Clamp(_network_game_info.companies_max + widget - NSSW_COMPANIES_TXT, 1, MAX_PLAYERS); |
|
823 break; |
|
824 case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU: |
|
825 _network_game_info.spectators_max = Clamp(_network_game_info.spectators_max + widget - NSSW_SPECTATORS_TXT, 0, MAX_CLIENTS); |
|
826 break; |
|
827 } |
768 } |
828 } |
769 pos++; |
829 _left_button_clicked = false; |
770 y += NSSWND_ROWSIZE; |
830 break; |
771 |
831 |
772 if (y >= w->vscroll.cap * NSSWND_ROWSIZE + NSSWND_START) break; |
832 case NSSW_CLIENTS_TXT: // Click on number of players |
|
833 this->widget_id = NSSW_CLIENTS_TXT; |
|
834 SetDParam(0, _network_game_info.clients_max); |
|
835 ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_CLIENTS, 3, 50, this, CS_NUMERAL); |
|
836 break; |
|
837 |
|
838 case NSSW_COMPANIES_TXT: // Click on number of companies |
|
839 this->widget_id = NSSW_COMPANIES_TXT; |
|
840 SetDParam(0, _network_game_info.companies_max); |
|
841 ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_COMPANIES, 3, 50, this, CS_NUMERAL); |
|
842 break; |
|
843 |
|
844 case NSSW_SPECTATORS_TXT: // Click on number of spectators |
|
845 this->widget_id = NSSW_SPECTATORS_TXT; |
|
846 SetDParam(0, _network_game_info.spectators_max); |
|
847 ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_SPECTATORS, 3, 50, this, CS_NUMERAL); |
|
848 break; |
|
849 |
|
850 case NSSW_LANGUAGE_BTN: { // Language |
|
851 uint sel = 0; |
|
852 for (uint i = 0; i < lengthof(_language_dropdown) - 1; i++) { |
|
853 if (_language_dropdown[i] == STR_NETWORK_LANG_ANY + _network_game_info.server_lang) { |
|
854 sel = i; |
|
855 break; |
|
856 } |
|
857 } |
|
858 ShowDropDownMenu(this, _language_dropdown, sel, NSSW_LANGUAGE_BTN, 0, 0); |
|
859 } break; |
|
860 |
|
861 case NSSW_START: // Start game |
|
862 _is_network_server = true; |
|
863 |
|
864 if (this->map == NULL) { // start random new game |
|
865 ShowGenerateLandscape(); |
|
866 } else { // load a scenario |
|
867 char *name = FiosBrowseTo(this->map); |
|
868 if (name != NULL) { |
|
869 SetFiosType(this->map->type); |
|
870 _file_to_saveload.filetype = FT_SCENARIO; |
|
871 ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name)); |
|
872 ttd_strlcpy(_file_to_saveload.title, this->map->title, sizeof(_file_to_saveload.title)); |
|
873 |
|
874 delete this; |
|
875 SwitchMode(SM_START_SCENARIO); |
|
876 } |
|
877 } |
|
878 break; |
|
879 |
|
880 case NSSW_LOAD: // Load game |
|
881 _is_network_server = true; |
|
882 /* XXX - WC_NETWORK_WINDOW (this window) should stay, but if it stays, it gets |
|
883 * copied all the elements of 'load game' and upon closing that, it segfaults */ |
|
884 delete this; |
|
885 ShowSaveLoadDialog(SLD_LOAD_GAME); |
|
886 break; |
|
887 } |
|
888 } |
|
889 |
|
890 virtual void OnDropdownSelect(int widget, int index) |
|
891 { |
|
892 switch (widget) { |
|
893 case NSSW_CONNTYPE_BTN: |
|
894 _network_advertise = (index != 0); |
|
895 break; |
|
896 case NSSW_LANGUAGE_BTN: |
|
897 _network_game_info.server_lang = _language_dropdown[index] - STR_NETWORK_LANG_ANY; |
|
898 break; |
|
899 default: |
|
900 NOT_REACHED(); |
|
901 } |
|
902 |
|
903 this->SetDirty(); |
|
904 } |
|
905 |
|
906 virtual void OnMouseLoop() |
|
907 { |
|
908 if (this->field == NSSW_GAMENAME) this->HandleEditBox(NSSW_GAMENAME); |
|
909 } |
|
910 |
|
911 virtual bool OnKeyPress(uint16 key, uint16 keycode) |
|
912 { |
|
913 bool cont = true; |
|
914 if (this->field == NSSW_GAMENAME) { |
|
915 if (this->HandleEditBoxKey(NSSW_GAMENAME, key, keycode, cont) == 1) return cont; // enter pressed |
|
916 |
|
917 ttd_strlcpy(_network_server_name, this->text.buf, sizeof(_network_server_name)); |
|
918 } |
|
919 |
|
920 return cont; |
|
921 } |
|
922 |
|
923 virtual void OnQueryTextFinished(char *str) |
|
924 { |
|
925 if (str == NULL) return; |
|
926 |
|
927 if (this->widget_id == NSSW_SETPWD) { |
|
928 ttd_strlcpy(_network_server_password, str, lengthof(_network_server_password)); |
|
929 _network_game_info.use_password = !StrEmpty(_network_server_password); |
|
930 } else { |
|
931 int32 value = atoi(str); |
|
932 this->InvalidateWidget(this->widget_id); |
|
933 switch (this->widget_id) { |
|
934 default: NOT_REACHED(); |
|
935 case NSSW_CLIENTS_TXT: _network_game_info.clients_max = Clamp(value, 2, MAX_CLIENTS); break; |
|
936 case NSSW_COMPANIES_TXT: _network_game_info.companies_max = Clamp(value, 1, MAX_PLAYERS); break; |
|
937 case NSSW_SPECTATORS_TXT: _network_game_info.spectators_max = Clamp(value, 0, MAX_CLIENTS); break; |
773 } |
938 } |
774 } break; |
939 } |
775 |
940 |
776 case WE_CLICK: |
941 this->SetDirty(); |
777 if (e->we.click.widget != NSSW_CONNTYPE_BTN && e->we.click.widget != NSSW_LANGUAGE_BTN) HideDropDownMenu(w); |
942 } |
778 nd->field = e->we.click.widget; |
943 }; |
779 switch (e->we.click.widget) { |
|
780 case NSSW_CLOSE: // Close 'X' |
|
781 case NSSW_CANCEL: // Cancel button |
|
782 ShowNetworkGameWindow(); |
|
783 break; |
|
784 |
|
785 case NSSW_GAMENAME: |
|
786 ShowOnScreenKeyboard(w, &WP(w, network_ql_d).q, NSSW_GAMENAME, 0, 0); |
|
787 break; |
|
788 |
|
789 case NSSW_SETPWD: // Set password button |
|
790 nd->widget_id = NSSW_SETPWD; |
|
791 ShowQueryString(BindCString(_network_server_password), STR_NETWORK_SET_PASSWORD, 20, 250, w, CS_ALPHANUMERAL); |
|
792 break; |
|
793 |
|
794 case NSSW_SELMAP: { // Select map |
|
795 int y = (e->we.click.pt.y - NSSWND_START) / NSSWND_ROWSIZE; |
|
796 |
|
797 y += w->vscroll.pos; |
|
798 if (y >= w->vscroll.count) return; |
|
799 |
|
800 nd->map = (y == 0) ? NULL : _fios_list + y - 1; |
|
801 SetWindowDirty(w); |
|
802 } break; |
|
803 |
|
804 case NSSW_CONNTYPE_BTN: // Connection type |
|
805 ShowDropDownMenu(w, _connection_types_dropdown, _network_advertise, NSSW_CONNTYPE_BTN, 0, 0); // do it for widget NSSW_CONNTYPE_BTN |
|
806 break; |
|
807 |
|
808 case NSSW_CLIENTS_BTND: case NSSW_CLIENTS_BTNU: // Click on up/down button for number of clients |
|
809 case NSSW_COMPANIES_BTND: case NSSW_COMPANIES_BTNU: // Click on up/down button for number of companies |
|
810 case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU: // Click on up/down button for number of spectators |
|
811 /* Don't allow too fast scrolling */ |
|
812 if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) { |
|
813 w->HandleButtonClick(e->we.click.widget); |
|
814 SetWindowDirty(w); |
|
815 switch (e->we.click.widget) { |
|
816 default: NOT_REACHED(); |
|
817 case NSSW_CLIENTS_BTND: case NSSW_CLIENTS_BTNU: |
|
818 _network_game_info.clients_max = Clamp(_network_game_info.clients_max + e->we.click.widget - NSSW_CLIENTS_TXT, 2, MAX_CLIENTS); |
|
819 break; |
|
820 case NSSW_COMPANIES_BTND: case NSSW_COMPANIES_BTNU: |
|
821 _network_game_info.companies_max = Clamp(_network_game_info.companies_max + e->we.click.widget - NSSW_COMPANIES_TXT, 1, MAX_PLAYERS); |
|
822 break; |
|
823 case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU: |
|
824 _network_game_info.spectators_max = Clamp(_network_game_info.spectators_max + e->we.click.widget - NSSW_SPECTATORS_TXT, 0, MAX_CLIENTS); |
|
825 break; |
|
826 } |
|
827 } |
|
828 _left_button_clicked = false; |
|
829 break; |
|
830 |
|
831 case NSSW_CLIENTS_TXT: // Click on number of players |
|
832 nd->widget_id = NSSW_CLIENTS_TXT; |
|
833 SetDParam(0, _network_game_info.clients_max); |
|
834 ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_CLIENTS, 3, 50, w, CS_NUMERAL); |
|
835 break; |
|
836 |
|
837 case NSSW_COMPANIES_TXT: // Click on number of companies |
|
838 nd->widget_id = NSSW_COMPANIES_TXT; |
|
839 SetDParam(0, _network_game_info.companies_max); |
|
840 ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_COMPANIES, 3, 50, w, CS_NUMERAL); |
|
841 break; |
|
842 |
|
843 case NSSW_SPECTATORS_TXT: // Click on number of spectators |
|
844 nd->widget_id = NSSW_SPECTATORS_TXT; |
|
845 SetDParam(0, _network_game_info.spectators_max); |
|
846 ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_SPECTATORS, 3, 50, w, CS_NUMERAL); |
|
847 break; |
|
848 |
|
849 case NSSW_LANGUAGE_BTN: { // Language |
|
850 uint sel = 0; |
|
851 for (uint i = 0; i < lengthof(_language_dropdown) - 1; i++) { |
|
852 if (_language_dropdown[i] == STR_NETWORK_LANG_ANY + _network_game_info.server_lang) { |
|
853 sel = i; |
|
854 break; |
|
855 } |
|
856 } |
|
857 ShowDropDownMenu(w, _language_dropdown, sel, NSSW_LANGUAGE_BTN, 0, 0); |
|
858 } break; |
|
859 |
|
860 case NSSW_START: // Start game |
|
861 _is_network_server = true; |
|
862 |
|
863 if (nd->map == NULL) { // start random new game |
|
864 ShowGenerateLandscape(); |
|
865 } else { // load a scenario |
|
866 char *name = FiosBrowseTo(nd->map); |
|
867 if (name != NULL) { |
|
868 SetFiosType(nd->map->type); |
|
869 _file_to_saveload.filetype = FT_SCENARIO; |
|
870 ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name)); |
|
871 ttd_strlcpy(_file_to_saveload.title, nd->map->title, sizeof(_file_to_saveload.title)); |
|
872 |
|
873 delete w; |
|
874 SwitchMode(SM_START_SCENARIO); |
|
875 } |
|
876 } |
|
877 break; |
|
878 |
|
879 case NSSW_LOAD: // Load game |
|
880 _is_network_server = true; |
|
881 /* XXX - WC_NETWORK_WINDOW (this window) should stay, but if it stays, it gets |
|
882 * copied all the elements of 'load game' and upon closing that, it segfaults */ |
|
883 delete w; |
|
884 ShowSaveLoadDialog(SLD_LOAD_GAME); |
|
885 break; |
|
886 } |
|
887 break; |
|
888 |
|
889 case WE_DROPDOWN_SELECT: // we have selected a dropdown item in the list |
|
890 switch (e->we.dropdown.button) { |
|
891 case NSSW_CONNTYPE_BTN: |
|
892 _network_advertise = (e->we.dropdown.index != 0); |
|
893 break; |
|
894 case NSSW_LANGUAGE_BTN: |
|
895 _network_game_info.server_lang = _language_dropdown[e->we.dropdown.index] - STR_NETWORK_LANG_ANY; |
|
896 break; |
|
897 default: |
|
898 NOT_REACHED(); |
|
899 } |
|
900 |
|
901 SetWindowDirty(w); |
|
902 break; |
|
903 |
|
904 case WE_MOUSELOOP: |
|
905 if (nd->field == NSSW_GAMENAME) HandleEditBox(w, &WP(w, network_ql_d).q, NSSW_GAMENAME); |
|
906 break; |
|
907 |
|
908 case WE_KEYPRESS: |
|
909 if (nd->field == NSSW_GAMENAME) { |
|
910 if (HandleEditBoxKey(w, &WP(w, network_ql_d).q, NSSW_GAMENAME, e) == 1) break; // enter pressed |
|
911 |
|
912 ttd_strlcpy(_network_server_name, WP(w, network_ql_d).q.text.buf, sizeof(_network_server_name)); |
|
913 } |
|
914 break; |
|
915 |
|
916 case WE_ON_EDIT_TEXT: |
|
917 if (e->we.edittext.str == NULL) break; |
|
918 |
|
919 if (nd->widget_id == NSSW_SETPWD) { |
|
920 ttd_strlcpy(_network_server_password, e->we.edittext.str, lengthof(_network_server_password)); |
|
921 _network_game_info.use_password = !StrEmpty(_network_server_password); |
|
922 } else { |
|
923 int32 value = atoi(e->we.edittext.str); |
|
924 w->InvalidateWidget(nd->widget_id); |
|
925 switch (nd->widget_id) { |
|
926 default: NOT_REACHED(); |
|
927 case NSSW_CLIENTS_TXT: _network_game_info.clients_max = Clamp(value, 2, MAX_CLIENTS); break; |
|
928 case NSSW_COMPANIES_TXT: _network_game_info.companies_max = Clamp(value, 1, MAX_PLAYERS); break; |
|
929 case NSSW_SPECTATORS_TXT: _network_game_info.spectators_max = Clamp(value, 0, MAX_CLIENTS); break; |
|
930 } |
|
931 } |
|
932 |
|
933 SetWindowDirty(w); |
|
934 break; |
|
935 } |
|
936 } |
|
937 |
944 |
938 static const Widget _network_start_server_window_widgets[] = { |
945 static const Widget _network_start_server_window_widgets[] = { |
939 /* Window decoration and background panel */ |
946 /* Window decoration and background panel */ |
940 { WWT_CLOSEBOX, RESIZE_NONE, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, // NSSW_CLOSE |
947 { WWT_CLOSEBOX, RESIZE_NONE, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, // NSSW_CLOSE |
941 { WWT_CAPTION, RESIZE_NONE, BGC, 11, 419, 0, 13, STR_NETWORK_START_GAME_WINDOW, STR_NULL}, |
948 { WWT_CAPTION, RESIZE_NONE, BGC, 11, 419, 0, 13, STR_NETWORK_START_GAME_WINDOW, STR_NULL}, |
1704 } else { |
1701 } else { |
1705 NetworkServer_HandleChat((NetworkAction)(NETWORK_ACTION_CHAT + type), type, dest, buf, NETWORK_SERVER_INDEX); |
1702 NetworkServer_HandleChat((NetworkAction)(NETWORK_ACTION_CHAT + type), type, dest, buf, NETWORK_SERVER_INDEX); |
1706 } |
1703 } |
1707 } |
1704 } |
1708 |
1705 |
1709 /** |
1706 |
1710 * Find the next item of the list of things that can be auto-completed. |
1707 struct NetworkChatWindow : public QueryStringBaseWindow { |
1711 * @param item The current indexed item to return. This function can, and most |
1708 DestType dtype; |
1712 * likely will, alter item, to skip empty items in the arrays. |
1709 int dest; |
1713 * @return Returns the char that matched to the index. |
1710 |
1714 */ |
1711 NetworkChatWindow (const WindowDesc *desc, DestType type, int dest) : QueryStringBaseWindow(desc) |
1715 static const char *ChatTabCompletionNextItem(uint *item) |
1712 { |
1716 { |
1713 this->LowerWidget(2); |
1717 static char chat_tab_temp_buffer[64]; |
1714 this->dtype = type; |
1718 |
1715 this->dest = dest; |
1719 /* First, try clients */ |
1716 this->afilter = CS_ALPHANUMERAL; |
1720 if (*item < MAX_CLIENT_INFO) { |
1717 InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 0); |
1721 /* Skip inactive clients */ |
1718 |
1722 while (_network_client_info[*item].client_index == NETWORK_EMPTY_INDEX && *item < MAX_CLIENT_INFO) (*item)++; |
1719 InvalidateWindowData(WC_NEWS_WINDOW, 0, this->height); |
1723 if (*item < MAX_CLIENT_INFO) return _network_client_info[*item].client_name; |
1720 SetBit(_no_scroll, SCROLL_CHAT); // do not scroll the game with the arrow-keys |
1724 } |
1721 |
1725 |
1722 _chat_tab_completion_active = false; |
1726 /* Then, try townnames */ |
1723 |
1727 /* Not that the following assumes all town indices are adjacent, ie no |
1724 this->FindWindowPlacementAndResize(desc); |
1728 * towns have been deleted. */ |
1725 } |
1729 if (*item <= (uint)MAX_CLIENT_INFO + GetMaxTownIndex()) { |
1726 |
1730 const Town *t; |
1727 ~NetworkChatWindow () |
1731 |
1728 { |
1732 FOR_ALL_TOWNS_FROM(t, *item - MAX_CLIENT_INFO) { |
1729 InvalidateWindowData(WC_NEWS_WINDOW, 0, 0); |
1733 /* Get the town-name via the string-system */ |
1730 ClrBit(_no_scroll, SCROLL_CHAT); |
1734 SetDParam(0, t->index); |
1731 } |
1735 GetString(chat_tab_temp_buffer, STR_TOWN, lastof(chat_tab_temp_buffer)); |
1732 |
1736 return &chat_tab_temp_buffer[0]; |
1733 /** |
1737 } |
1734 * Find the next item of the list of things that can be auto-completed. |
1738 } |
1735 * @param item The current indexed item to return. This function can, and most |
1739 |
1736 * likely will, alter item, to skip empty items in the arrays. |
1740 return NULL; |
1737 * @return Returns the char that matched to the index. |
1741 } |
1738 */ |
1742 |
1739 const char *ChatTabCompletionNextItem(uint *item) |
1743 /** |
1740 { |
1744 * Find what text to complete. It scans for a space from the left and marks |
1741 static char chat_tab_temp_buffer[64]; |
1745 * the word right from that as to complete. It also writes a \0 at the |
1742 |
1746 * position of the space (if any). If nothing found, buf is returned. |
1743 /* First, try clients */ |
1747 */ |
1744 if (*item < MAX_CLIENT_INFO) { |
1748 static char *ChatTabCompletionFindText(char *buf) |
1745 /* Skip inactive clients */ |
1749 { |
1746 while (_network_client_info[*item].client_index == NETWORK_EMPTY_INDEX && *item < MAX_CLIENT_INFO) (*item)++; |
1750 char *p = strrchr(buf, ' '); |
1747 if (*item < MAX_CLIENT_INFO) return _network_client_info[*item].client_name; |
1751 if (p == NULL) return buf; |
1748 } |
1752 |
1749 |
1753 *p = '\0'; |
1750 /* Then, try townnames */ |
1754 return p + 1; |
1751 /* Not that the following assumes all town indices are adjacent, ie no |
1755 } |
1752 * towns have been deleted. */ |
1756 |
1753 if (*item <= (uint)MAX_CLIENT_INFO + GetMaxTownIndex()) { |
1757 /** |
1754 const Town *t; |
1758 * See if we can auto-complete the current text of the user. |
1755 |
1759 */ |
1756 FOR_ALL_TOWNS_FROM(t, *item - MAX_CLIENT_INFO) { |
1760 static void ChatTabCompletion(Window *w) |
1757 /* Get the town-name via the string-system */ |
1761 { |
1758 SetDParam(0, t->index); |
1762 static char _chat_tab_completion_buf[lengthof(_edit_str_net_buf)]; |
1759 GetString(chat_tab_temp_buffer, STR_TOWN, lastof(chat_tab_temp_buffer)); |
1763 Textbuf *tb = &WP(w, chatquerystr_d).text; |
1760 return &chat_tab_temp_buffer[0]; |
1764 size_t len, tb_len; |
1761 } |
1765 uint item; |
1762 } |
1766 char *tb_buf, *pre_buf; |
1763 |
1767 const char *cur_name; |
1764 return NULL; |
1768 bool second_scan = false; |
1765 } |
1769 |
1766 |
1770 item = 0; |
1767 /** |
1771 |
1768 * Find what text to complete. It scans for a space from the left and marks |
1772 /* Copy the buffer so we can modify it without damaging the real data */ |
1769 * the word right from that as to complete. It also writes a \0 at the |
1773 pre_buf = (_chat_tab_completion_active) ? strdup(_chat_tab_completion_buf) : strdup(tb->buf); |
1770 * position of the space (if any). If nothing found, buf is returned. |
1774 |
1771 */ |
1775 tb_buf = ChatTabCompletionFindText(pre_buf); |
1772 static char *ChatTabCompletionFindText(char *buf) |
1776 tb_len = strlen(tb_buf); |
1773 { |
1777 |
1774 char *p = strrchr(buf, ' '); |
1778 while ((cur_name = ChatTabCompletionNextItem(&item)) != NULL) { |
1775 if (p == NULL) return buf; |
1779 item++; |
1776 |
1780 |
1777 *p = '\0'; |
1781 if (_chat_tab_completion_active) { |
1778 return p + 1; |
1782 /* We are pressing TAB again on the same name, is there an other name |
1779 } |
1783 * that starts with this? */ |
1780 |
1784 if (!second_scan) { |
1781 /** |
1785 size_t offset; |
1782 * See if we can auto-complete the current text of the user. |
1786 size_t length; |
1783 */ |
1787 |
1784 void ChatTabCompletion() |
1788 /* If we are completing at the begin of the line, skip the ': ' we added */ |
1785 { |
1789 if (tb_buf == pre_buf) { |
1786 static char _chat_tab_completion_buf[lengthof(this->edit_str_buf)]; |
1790 offset = 0; |
1787 Textbuf *tb = &this->text; |
1791 length = tb->length - 2; |
1788 size_t len, tb_len; |
|
1789 uint item; |
|
1790 char *tb_buf, *pre_buf; |
|
1791 const char *cur_name; |
|
1792 bool second_scan = false; |
|
1793 |
|
1794 item = 0; |
|
1795 |
|
1796 /* Copy the buffer so we can modify it without damaging the real data */ |
|
1797 pre_buf = (_chat_tab_completion_active) ? strdup(_chat_tab_completion_buf) : strdup(tb->buf); |
|
1798 |
|
1799 tb_buf = ChatTabCompletionFindText(pre_buf); |
|
1800 tb_len = strlen(tb_buf); |
|
1801 |
|
1802 while ((cur_name = ChatTabCompletionNextItem(&item)) != NULL) { |
|
1803 item++; |
|
1804 |
|
1805 if (_chat_tab_completion_active) { |
|
1806 /* We are pressing TAB again on the same name, is there an other name |
|
1807 * that starts with this? */ |
|
1808 if (!second_scan) { |
|
1809 size_t offset; |
|
1810 size_t length; |
|
1811 |
|
1812 /* If we are completing at the begin of the line, skip the ': ' we added */ |
|
1813 if (tb_buf == pre_buf) { |
|
1814 offset = 0; |
|
1815 length = tb->length - 2; |
|
1816 } else { |
|
1817 /* Else, find the place we are completing at */ |
|
1818 offset = strlen(pre_buf) + 1; |
|
1819 length = tb->length - offset; |
|
1820 } |
|
1821 |
|
1822 /* Compare if we have a match */ |
|
1823 if (strlen(cur_name) == length && strncmp(cur_name, tb->buf + offset, length) == 0) second_scan = true; |
|
1824 |
|
1825 continue; |
|
1826 } |
|
1827 |
|
1828 /* Now any match we make on _chat_tab_completion_buf after this, is perfect */ |
|
1829 } |
|
1830 |
|
1831 len = strlen(cur_name); |
|
1832 if (tb_len < len && strncasecmp(cur_name, tb_buf, tb_len) == 0) { |
|
1833 /* Save the data it was before completion */ |
|
1834 if (!second_scan) snprintf(_chat_tab_completion_buf, lengthof(_chat_tab_completion_buf), "%s", tb->buf); |
|
1835 _chat_tab_completion_active = true; |
|
1836 |
|
1837 /* Change to the found name. Add ': ' if we are at the start of the line (pretty) */ |
|
1838 if (pre_buf == tb_buf) { |
|
1839 snprintf(tb->buf, lengthof(this->edit_str_buf), "%s: ", cur_name); |
1792 } else { |
1840 } else { |
1793 /* Else, find the place we are completing at */ |
1841 snprintf(tb->buf, lengthof(this->edit_str_buf), "%s %s", pre_buf, cur_name); |
1794 offset = strlen(pre_buf) + 1; |
|
1795 length = tb->length - offset; |
|
1796 } |
1842 } |
1797 |
1843 |
1798 /* Compare if we have a match */ |
1844 /* Update the textbuffer */ |
1799 if (strlen(cur_name) == length && strncmp(cur_name, tb->buf + offset, length) == 0) second_scan = true; |
1845 UpdateTextBufferSize(&this->text); |
1800 |
1846 |
1801 continue; |
1847 this->SetDirty(); |
|
1848 free(pre_buf); |
|
1849 return; |
1802 } |
1850 } |
1803 |
1851 } |
1804 /* Now any match we make on _chat_tab_completion_buf after this, is perfect */ |
1852 |
1805 } |
1853 if (second_scan) { |
1806 |
1854 /* We walked all posibilities, and the user presses tab again.. revert to original text */ |
1807 len = strlen(cur_name); |
1855 strcpy(tb->buf, _chat_tab_completion_buf); |
1808 if (tb_len < len && strncasecmp(cur_name, tb_buf, tb_len) == 0) { |
1856 _chat_tab_completion_active = false; |
1809 /* Save the data it was before completion */ |
1857 |
1810 if (!second_scan) snprintf(_chat_tab_completion_buf, lengthof(_chat_tab_completion_buf), "%s", tb->buf); |
1858 /* Update the textbuffer */ |
1811 _chat_tab_completion_active = true; |
1859 UpdateTextBufferSize(&this->text); |
1812 |
1860 |
1813 /* Change to the found name. Add ': ' if we are at the start of the line (pretty) */ |
1861 this->SetDirty(); |
1814 if (pre_buf == tb_buf) { |
1862 } |
1815 snprintf(tb->buf, lengthof(_edit_str_net_buf), "%s: ", cur_name); |
1863 free(pre_buf); |
1816 } else { |
1864 } |
1817 snprintf(tb->buf, lengthof(_edit_str_net_buf), "%s %s", pre_buf, cur_name); |
1865 |
|
1866 virtual void OnPaint() |
|
1867 { |
|
1868 static const StringID chat_captions[] = { |
|
1869 STR_NETWORK_CHAT_ALL_CAPTION, |
|
1870 STR_NETWORK_CHAT_COMPANY_CAPTION, |
|
1871 STR_NETWORK_CHAT_CLIENT_CAPTION |
|
1872 }; |
|
1873 |
|
1874 DrawWindowWidgets(this); |
|
1875 |
|
1876 assert((uint)this->dtype < lengthof(chat_captions)); |
|
1877 DrawStringRightAligned(this->widget[2].left - 2, this->widget[2].top + 1, chat_captions[this->dtype], TC_BLACK); |
|
1878 this->DrawEditBox(2); |
|
1879 } |
|
1880 |
|
1881 virtual void OnClick(Point pt, int widget) |
|
1882 { |
|
1883 switch (widget) { |
|
1884 case 2: |
|
1885 ShowOnScreenKeyboard(this, 2, 0, 3); |
|
1886 break; |
|
1887 |
|
1888 case 3: /* Send */ |
|
1889 SendChat(this->text.buf, this->dtype, this->dest); |
|
1890 /* FALLTHROUGH */ |
|
1891 case 0: /* Cancel */ delete this; break; |
|
1892 } |
|
1893 } |
|
1894 |
|
1895 virtual void OnMouseLoop() |
|
1896 { |
|
1897 this->HandleEditBox(2); |
|
1898 } |
|
1899 |
|
1900 virtual bool OnKeyPress(uint16 key, uint16 keycode) |
|
1901 { |
|
1902 bool cont = true; |
|
1903 if (keycode == WKC_TAB) { |
|
1904 ChatTabCompletion(); |
|
1905 } else { |
|
1906 _chat_tab_completion_active = false; |
|
1907 switch (this->HandleEditBoxKey(2, key, keycode, cont)) { |
|
1908 case 1: /* Return */ |
|
1909 SendChat(this->text.buf, this->dtype, this->dest); |
|
1910 /* FALLTHROUGH */ |
|
1911 case 2: /* Escape */ delete this; break; |
1818 } |
1912 } |
1819 |
1913 } |
1820 /* Update the textbuffer */ |
1914 return cont; |
1821 UpdateTextBufferSize(&WP(w, chatquerystr_d).text); |
1915 } |
1822 |
1916 }; |
1823 SetWindowDirty(w); |
|
1824 free(pre_buf); |
|
1825 return; |
|
1826 } |
|
1827 } |
|
1828 |
|
1829 if (second_scan) { |
|
1830 /* We walked all posibilities, and the user presses tab again.. revert to original text */ |
|
1831 strcpy(tb->buf, _chat_tab_completion_buf); |
|
1832 _chat_tab_completion_active = false; |
|
1833 |
|
1834 /* Update the textbuffer */ |
|
1835 UpdateTextBufferSize(&WP(w, chatquerystr_d).text); |
|
1836 |
|
1837 SetWindowDirty(w); |
|
1838 } |
|
1839 free(pre_buf); |
|
1840 } |
|
1841 |
|
1842 /* |
|
1843 * uses chatquerystr_d WP macro |
|
1844 * uses chatquerystr_d->dtype to store type of chat message (Private/Team/All) |
|
1845 */ |
|
1846 static void ChatWindowWndProc(Window *w, WindowEvent *e) |
|
1847 { |
|
1848 switch (e->event) { |
|
1849 case WE_CREATE: |
|
1850 InvalidateWindowData(WC_NEWS_WINDOW, 0, w->height); |
|
1851 SetBit(_no_scroll, SCROLL_CHAT); // do not scroll the game with the arrow-keys |
|
1852 break; |
|
1853 |
|
1854 case WE_PAINT: { |
|
1855 static const StringID chat_captions[] = { |
|
1856 STR_NETWORK_CHAT_ALL_CAPTION, |
|
1857 STR_NETWORK_CHAT_COMPANY_CAPTION, |
|
1858 STR_NETWORK_CHAT_CLIENT_CAPTION |
|
1859 }; |
|
1860 |
|
1861 DrawWindowWidgets(w); |
|
1862 |
|
1863 assert((uint)WP(w, chatquerystr_d).dtype < lengthof(chat_captions)); |
|
1864 DrawStringRightAligned(w->widget[2].left - 2, w->widget[2].top + 1, chat_captions[WP(w, chatquerystr_d).dtype], TC_BLACK); |
|
1865 DrawEditBox(w, &WP(w, chatquerystr_d), 2); |
|
1866 } break; |
|
1867 |
|
1868 case WE_CLICK: |
|
1869 switch (e->we.click.widget) { |
|
1870 case 2: |
|
1871 ShowOnScreenKeyboard(w, &WP(w, chatquerystr_d), 2, 0, 3); |
|
1872 break; |
|
1873 |
|
1874 case 3: /* Send */ |
|
1875 SendChat(WP(w, chatquerystr_d).text.buf, WP(w, chatquerystr_d).dtype, WP(w, chatquerystr_d).dest); |
|
1876 /* FALLTHROUGH */ |
|
1877 case 0: /* Cancel */ delete w; break; |
|
1878 } |
|
1879 break; |
|
1880 |
|
1881 case WE_MOUSELOOP: |
|
1882 HandleEditBox(w, &WP(w, chatquerystr_d), 2); |
|
1883 break; |
|
1884 |
|
1885 case WE_KEYPRESS: |
|
1886 if (e->we.keypress.keycode == WKC_TAB) { |
|
1887 ChatTabCompletion(w); |
|
1888 } else { |
|
1889 _chat_tab_completion_active = false; |
|
1890 switch (HandleEditBoxKey(w, &WP(w, chatquerystr_d), 2, e)) { |
|
1891 case 1: /* Return */ |
|
1892 SendChat(WP(w, chatquerystr_d).text.buf, WP(w, chatquerystr_d).dtype, WP(w, chatquerystr_d).dest); |
|
1893 /* FALLTHROUGH */ |
|
1894 case 2: /* Escape */ delete w; break; |
|
1895 } |
|
1896 } |
|
1897 break; |
|
1898 |
|
1899 case WE_DESTROY: |
|
1900 InvalidateWindowData(WC_NEWS_WINDOW, 0, 0); |
|
1901 ClrBit(_no_scroll, SCROLL_CHAT); |
|
1902 break; |
|
1903 } |
|
1904 } |
|
1905 |
1917 |
1906 static const Widget _chat_window_widgets[] = { |
1918 static const Widget _chat_window_widgets[] = { |
1907 { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, |
1919 { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, |
1908 { WWT_PANEL, RESIZE_RIGHT, 14, 11, 319, 0, 13, 0x0, STR_NULL}, // background |
1920 { WWT_PANEL, RESIZE_RIGHT, 14, 11, 319, 0, 13, 0x0, STR_NULL}, // background |
1909 { WWT_EDITBOX, RESIZE_RIGHT, 14, 75, 257, 1, 12, STR_NETWORK_CHAT_OSKTITLE, STR_NULL}, // text box |
1921 { WWT_EDITBOX, RESIZE_RIGHT, 14, 75, 257, 1, 12, STR_NETWORK_CHAT_OSKTITLE, STR_NULL}, // text box |