| 35 #include "network/ipaddress.h" |
35 #include "network/ipaddress.h" |
| 36 #include "coloredline.h" |
36 #include "coloredline.h" |
| 37 |
37 |
| 38 static const int g_pageSize = 10; |
38 static const int g_pageSize = 10; |
| 39 |
39 |
| 40 enum InputState |
40 // ------------------------------------------------------------------------------------------------- |
| 41 { |
41 // |
| 42 INPUTSTATE_NORMAL, |
42 int Interface::color_pair (Color fg, Color bg) |
| 43 INPUTSTATE_ADDRESS, |
|
| 44 INPUTSTATE_PASSWORD, |
|
| 45 INPUTSTATE_CONFIRM_DISCONNECTION, |
|
| 46 }; |
|
| 47 |
|
| 48 static StringList InputHistory; |
|
| 49 static int InputCursor = 0; |
|
| 50 static int CursorPosition = 0; |
|
| 51 static int InputPanning = 0; |
|
| 52 static bool NeedRefresh = false; |
|
| 53 static bool NeedStatusBarRender = false; |
|
| 54 static bool NeedInputRender = false; |
|
| 55 static bool NeedOutputRender = false; |
|
| 56 static bool NeedNicklistRender = false; |
|
| 57 static struct { char ch; int x; } CursorCharacter; |
|
| 58 static Vector<ColoredLine> OutputLines; |
|
| 59 static int OutputScroll = 0; |
|
| 60 static String Title; |
|
| 61 static InputState CurrentInputState = INPUTSTATE_NORMAL; |
|
| 62 static Function<void (void)> DisconnectConfirmFunction = nullptr; |
|
| 63 static IPAddress CurrentAddress; |
|
| 64 static String StatusBarText; |
|
| 65 static StringList PlayerNames; |
|
| 66 static String PasteBuffer; |
|
| 67 |
|
| 68 // ------------------------------------------------------------------------------------------------- |
|
| 69 // |
|
| 70 static FUNCTION |
|
| 71 interface_color_pair (Color fg, Color bg) -> int |
|
| 72 { |
43 { |
| 73 return COLOR_PAIR ((int (fg) * NUM_COLORS) + int (bg)); |
44 return COLOR_PAIR ((int (fg) * NUM_COLORS) + int (bg)); |
| 74 } |
45 } |
| 75 |
46 |
| 76 // ------------------------------------------------------------------------------------------------- |
47 // ------------------------------------------------------------------------------------------------- |
| 77 // |
48 // |
| 78 static FUNCTION |
49 const String& Interface::current_input() |
| 79 current_input() -> const String& |
|
| 80 { |
50 { |
| 81 return InputHistory[InputCursor]; |
51 return InputHistory[InputCursor]; |
| 82 } |
52 } |
| 83 |
53 |
| 84 // ------------------------------------------------------------------------------------------------- |
54 // ------------------------------------------------------------------------------------------------- |
| 85 // |
55 // |
| 86 // Makes current_input() the lastmost input (so that we won't modify history) |
56 // Makes current_input() the lastmost input (so that we won't modify history) |
| 87 // |
57 // |
| 88 static FUNCTION |
58 void Interface::detach_input() |
| 89 detach_input() -> void |
|
| 90 { |
59 { |
| 91 if (InputCursor > 0) |
60 if (InputCursor > 0) |
| 92 { |
61 { |
| 93 InputHistory[0] = current_input(); |
62 InputHistory[0] = current_input(); |
| 94 InputCursor = 0; |
63 InputCursor = 0; |
| 96 } |
65 } |
| 97 |
66 |
| 98 // ------------------------------------------------------------------------------------------------- |
67 // ------------------------------------------------------------------------------------------------- |
| 99 // A version of current_input() that allows changing the contents of it. |
68 // A version of current_input() that allows changing the contents of it. |
| 100 // |
69 // |
| 101 static FUNCTION |
70 String& Interface::mutable_current_input() |
| 102 mutable_current_input() -> String& |
|
| 103 { |
71 { |
| 104 detach_input(); |
72 detach_input(); |
| 105 return InputHistory[InputCursor]; |
73 return InputHistory[InputCursor]; |
| 106 } |
74 } |
| 107 |
75 |
| 108 // ------------------------------------------------------------------------------------------------- |
76 // ------------------------------------------------------------------------------------------------- |
| 109 // |
77 // |
| 110 static FUNCTION |
78 void Interface::move_input_cursor (int delta) |
| 111 move_input_cursor (int delta) -> void |
|
| 112 { |
79 { |
| 113 // No input history when inputting addresses or passwords |
80 // No input history when inputting addresses or passwords |
| 114 if (CurrentInputState != INPUTSTATE_NORMAL) |
81 if (CurrentInputState != INPUTSTATE_NORMAL) |
| 115 { |
82 { |
| 116 InputCursor = 0; |
83 InputCursor = 0; |
| 145 return prompt; |
111 return prompt; |
| 146 } |
112 } |
| 147 |
113 |
| 148 // ------------------------------------------------------------------------------------------------- |
114 // ------------------------------------------------------------------------------------------------- |
| 149 // |
115 // |
| 150 static FUNCTION |
116 void Interface::set_input_state (InputState newstate) |
| 151 set_input_state (InputState newstate) -> void |
|
| 152 { |
117 { |
| 153 // Clear the input row (unless going to or from confirm state) |
118 // Clear the input row (unless going to or from confirm state) |
| 154 if (newstate != INPUTSTATE_CONFIRM_DISCONNECTION |
119 if (newstate != INPUTSTATE_CONFIRM_DISCONNECTION |
| 155 and CurrentInputState != INPUTSTATE_CONFIRM_DISCONNECTION) |
120 and CurrentInputState != INPUTSTATE_CONFIRM_DISCONNECTION) |
| 156 { |
121 { |
| 205 NeedRefresh = false; |
170 NeedRefresh = false; |
| 206 } |
171 } |
| 207 |
172 |
| 208 // ------------------------------------------------------------------------------------------------- |
173 // ------------------------------------------------------------------------------------------------- |
| 209 // |
174 // |
| 210 static FUNCTION |
175 void Interface::render_titlebar() |
| 211 interface_render_titlebar() -> void |
|
| 212 { |
176 { |
| 213 if (Title.length() <= COLS) |
177 if (Title.length() <= COLS) |
| 214 { |
178 { |
| 215 int pair = interface_color_pair (WHITE, BLUE); |
179 int pair = color_pair (WHITE, BLUE); |
| 216 int startx = (COLS - Title.length()) / 2; |
180 int startx = (COLS - Title.length()) / 2; |
| 217 int endx = startx + Title.length(); |
181 int endx = startx + Title.length(); |
| 218 attron (pair); |
182 attron (pair); |
| 219 mvprintw (0, startx, "%s", Title.chars()); |
183 mvprintw (0, startx, "%s", Title.chars()); |
| 220 mvhline (0, 0, ' ', startx); |
184 mvhline (0, 0, ' ', startx); |
| 225 NeedRefresh = true; |
189 NeedRefresh = true; |
| 226 } |
190 } |
| 227 |
191 |
| 228 // ------------------------------------------------------------------------------------------------- |
192 // ------------------------------------------------------------------------------------------------- |
| 229 // |
193 // |
| 230 FUNCTION |
194 void Interface::set_title (const String& title) |
| 231 Interface::set_title (const String& title) -> void |
|
| 232 { |
195 { |
| 233 Title = title; |
196 Title = title; |
| 234 interface_render_titlebar(); |
197 render_titlebar(); |
| 235 } |
198 } |
| 236 |
199 |
| 237 // ------------------------------------------------------------------------------------------------- |
200 // ------------------------------------------------------------------------------------------------- |
| 238 // |
201 // |
| 239 static FUNCTION |
202 void Interface::safe_disconnect (Function<void()> afterwards) |
| 240 safe_disconnect (Function<void()> afterwards) -> void |
203 { |
| 241 { |
204 if (Session.is_active()) |
| 242 if (RCONSession::get_session()->is_active()) |
|
| 243 { |
205 { |
| 244 DisconnectConfirmFunction = afterwards; |
206 DisconnectConfirmFunction = afterwards; |
| 245 set_input_state (INPUTSTATE_CONFIRM_DISCONNECTION); |
207 set_input_state (INPUTSTATE_CONFIRM_DISCONNECTION); |
| 246 } |
208 } |
| 247 else |
209 else |
| 248 afterwards(); |
210 afterwards(); |
| 249 } |
211 } |
| 250 |
212 |
| 251 // ------------------------------------------------------------------------------------------------- |
213 // ------------------------------------------------------------------------------------------------- |
| 252 // |
214 // |
| 253 static FUNCTION |
215 int Interface::nicklist_width() |
| 254 interface_nicklist_width() -> int |
|
| 255 { |
216 { |
| 256 // Allocate at least 12 characters, at most 24 characters, for the nicklist. If we cannot |
217 // Allocate at least 12 characters, at most 24 characters, for the nicklist. If we cannot |
| 257 // afford that (o_O) then we probably shouldn't draw the nicklist at all I think. |
218 // afford that (o_O) then we probably shouldn't draw the nicklist at all I think. |
| 258 int nicklistWidth = COLS / 4; |
219 int nicklistWidth = COLS / 4; |
| 259 |
220 |
| 265 |
226 |
| 266 // ------------------------------------------------------------------------------------------------- |
227 // ------------------------------------------------------------------------------------------------- |
| 267 // Renders the given colored line onto the screen. Will wrap if allowWrap is true. Returns the |
228 // Renders the given colored line onto the screen. Will wrap if allowWrap is true. Returns the |
| 268 // 'y' value for the next line. |
229 // 'y' value for the next line. |
| 269 // |
230 // |
| 270 static FUNCTION |
231 int Interface::render_colorline (int y, int x0, int width, const ColoredLine& line, bool allowWrap) |
| 271 interface_render_colorline (int y, int x0, int width, |
|
| 272 const ColoredLine& line, bool allowWrap) -> int |
|
| 273 { |
232 { |
| 274 int x = x0; |
233 int x = x0; |
| 275 |
234 |
| 276 for (int byte : line.data()) |
235 for (int byte : line.data()) |
| 277 { |
236 { |
| 297 case RLINE_ON_YELLOW: |
256 case RLINE_ON_YELLOW: |
| 298 case RLINE_ON_BLUE: |
257 case RLINE_ON_BLUE: |
| 299 case RLINE_ON_MAGENTA: |
258 case RLINE_ON_MAGENTA: |
| 300 case RLINE_ON_CYAN: |
259 case RLINE_ON_CYAN: |
| 301 case RLINE_ON_WHITE: |
260 case RLINE_ON_WHITE: |
| 302 attron (interface_color_pair (Color (byte - RLINE_ON_BLACK), DEFAULT)); |
261 attron (color_pair (Color (byte - RLINE_ON_BLACK), DEFAULT)); |
| 303 break; |
262 break; |
| 304 |
263 |
| 305 case RLINE_OFF_BLACK: |
264 case RLINE_OFF_BLACK: |
| 306 case RLINE_OFF_RED: |
265 case RLINE_OFF_RED: |
| 307 case RLINE_OFF_GREEN: |
266 case RLINE_OFF_GREEN: |
| 308 case RLINE_OFF_YELLOW: |
267 case RLINE_OFF_YELLOW: |
| 309 case RLINE_OFF_BLUE: |
268 case RLINE_OFF_BLUE: |
| 310 case RLINE_OFF_MAGENTA: |
269 case RLINE_OFF_MAGENTA: |
| 311 case RLINE_OFF_CYAN: |
270 case RLINE_OFF_CYAN: |
| 312 case RLINE_OFF_WHITE: |
271 case RLINE_OFF_WHITE: |
| 313 attroff (interface_color_pair (Color (byte - RLINE_OFF_BLACK), DEFAULT)); |
272 attroff (color_pair (Color (byte - RLINE_OFF_BLACK), DEFAULT)); |
| 314 break; |
273 break; |
| 315 |
274 |
| 316 case RLINE_ON_BOLD: |
275 case RLINE_ON_BOLD: |
| 317 attron (A_BOLD); |
276 attron (A_BOLD); |
| 318 break; |
277 break; |
| 326 return y + 1; |
285 return y + 1; |
| 327 } |
286 } |
| 328 |
287 |
| 329 // ------------------------------------------------------------------------------------------------- |
288 // ------------------------------------------------------------------------------------------------- |
| 330 // |
289 // |
| 331 static FUNCTION |
290 void Interface::render_output() |
| 332 interface_render_output() -> void |
|
| 333 { |
291 { |
| 334 if (OutputLines.size() == 1) |
292 if (OutputLines.size() == 1) |
| 335 return; |
293 return; |
| 336 |
294 |
| 337 OutputScroll = clamp (OutputScroll, 0, OutputLines.size() - 1); |
295 OutputScroll = clamp (OutputScroll, 0, OutputLines.size() - 1); |
| 338 |
296 |
| 339 int height = LINES - 3; |
297 int height = LINES - 3; |
| 340 int width = COLS - interface_nicklist_width(); |
298 int width = COLS - nicklist_width(); |
| 341 int printOffset = 0; |
299 int printOffset = 0; |
| 342 int end = OutputLines.size() - 1 - OutputScroll; |
300 int end = OutputLines.size() - 1 - OutputScroll; |
| 343 int start = end; |
301 int start = end; |
| 344 int usedHeight = 0; |
302 int usedHeight = 0; |
| 345 int y = 1; |
303 int y = 1; |
| 395 |
353 |
| 396 // Print the lines |
354 // Print the lines |
| 397 y += printOffset; |
355 y += printOffset; |
| 398 |
356 |
| 399 for (int i = start; i < end; ++i) |
357 for (int i = start; i < end; ++i) |
| 400 y = interface_render_colorline (y, 0, width, OutputLines[i], true); |
358 y = render_colorline (y, 0, width, OutputLines[i], true); |
| 401 |
359 |
| 402 NeedOutputRender = false; |
360 NeedOutputRender = false; |
| 403 NeedRefresh = true; |
361 NeedRefresh = true; |
| 404 } |
362 } |
| 405 |
363 |
| 406 // ------------------------------------------------------------------------------------------------- |
364 // ------------------------------------------------------------------------------------------------- |
| 407 // |
365 // |
| 408 static FUNCTION |
366 void Interface::render_nicklist() |
| 409 interface_render_nicklist() -> void |
367 { |
| 410 { |
368 int width = nicklist_width(); |
| 411 int width = interface_nicklist_width(); |
|
| 412 int height = LINES- 3; |
369 int height = LINES- 3; |
| 413 int y = 1; |
370 int y = 1; |
| 414 int x = COLS - width; |
371 int x = COLS - width; |
| 415 |
372 |
| 416 if (width == 0) |
373 if (width == 0) |
| 440 NeedRefresh = true; |
397 NeedRefresh = true; |
| 441 } |
398 } |
| 442 |
399 |
| 443 // ------------------------------------------------------------------------------------------------- |
400 // ------------------------------------------------------------------------------------------------- |
| 444 // |
401 // |
| 445 static FUNCTION |
402 void Interface::render_input() |
| 446 interface_render_input() -> void |
403 { |
| 447 { |
404 int promptColor = color_pair (WHITE, BLUE); |
| 448 int promptColor = interface_color_pair (WHITE, BLUE); |
|
| 449 |
405 |
| 450 // If we're asking the user if they want to disconnect, we don't render any input strings, |
406 // If we're asking the user if they want to disconnect, we don't render any input strings, |
| 451 // just the confirmation message. |
407 // just the confirmation message. |
| 452 if (CurrentInputState == INPUTSTATE_CONFIRM_DISCONNECTION) |
408 if (CurrentInputState == INPUTSTATE_CONFIRM_DISCONNECTION) |
| 453 { |
409 { |
| 457 attroff (promptColor); |
413 attroff (promptColor); |
| 458 NeedRefresh = true; |
414 NeedRefresh = true; |
| 459 return; |
415 return; |
| 460 } |
416 } |
| 461 |
417 |
| 462 String prompt = interface_prompt_string(); |
418 String prompt = prompt_string(); |
| 463 int displayLength = COLS - prompt.length() - 2; |
419 int displayLength = COLS - prompt.length() - 2; |
| 464 String displayString = current_input(); |
420 String displayString = current_input(); |
| 465 int y = LINES - 2; |
421 int y = LINES - 2; |
| 466 |
422 |
| 467 // If we're inputting a password, replace it with asterisks |
423 // If we're inputting a password, replace it with asterisks |
| 502 NeedInputRender = false; |
458 NeedInputRender = false; |
| 503 } |
459 } |
| 504 |
460 |
| 505 // ------------------------------------------------------------------------------------------------- |
461 // ------------------------------------------------------------------------------------------------- |
| 506 // |
462 // |
| 507 static FUNCTION |
463 void Interface::render_statusbar() |
| 508 interface_render_statusbar() -> void |
464 { |
| 509 { |
465 int color = color_pair (WHITE, BLUE); |
| 510 int color = interface_color_pair (WHITE, BLUE); |
|
| 511 int y = LINES - 1; |
466 int y = LINES - 1; |
| 512 attron (color); |
467 attron (color); |
| 513 mvhline (y, 0, ' ', COLS); |
468 mvhline (y, 0, ' ', COLS); |
| 514 mvprintw (y, 0, "%s", StatusBarText.chars()); |
469 mvprintw (y, 0, "%s", StatusBarText.chars()); |
| 515 attroff (color); |
470 attroff (color); |
| 517 NeedStatusBarRender = false; |
472 NeedStatusBarRender = false; |
| 518 } |
473 } |
| 519 |
474 |
| 520 // ------------------------------------------------------------------------------------------------- |
475 // ------------------------------------------------------------------------------------------------- |
| 521 // |
476 // |
| 522 FUNCTION |
477 void Interface::update_statusbar() |
| 523 Interface::update_statusbar() -> void |
|
| 524 { |
478 { |
| 525 String text; |
479 String text; |
| 526 RCONSession* session = RCONSession::get_session(); |
480 |
| 527 |
481 switch (Session.state()) |
| 528 switch (session->state()) |
|
| 529 { |
482 { |
| 530 case RCON_DISCONNECTED: |
483 case RCON_DISCONNECTED: |
| 531 text = "Disconnected."; |
484 text = "Disconnected."; |
| 532 break; |
485 break; |
| 533 |
486 |
| 534 case RCON_CONNECTING: |
487 case RCON_CONNECTING: |
| 535 case RCON_AUTHENTICATING: |
488 case RCON_AUTHENTICATING: |
| 536 text = "Connecting to " + session->address().to_string (IP_WITH_PORT) + "..."; |
489 text = "Connecting to " + Session.address().to_string (IP_WITH_PORT) + "..."; |
| 537 break; |
490 break; |
| 538 |
491 |
| 539 case RCON_CONNECTED: |
492 case RCON_CONNECTED: |
| 540 { |
493 { |
| 541 String adminText = (session->num_admins() == 0) ? "No other admins" |
494 String adminText; |
| 542 : format ("%1 other admin%s1", session->num_admins()); |
495 |
| 543 text = format ("%1 | %2 | %3", session->address().to_string (IP_WITH_PORT), |
496 if (Session.num_admins() == 0) |
| 544 session->level(), adminText); |
497 adminText = "No other admins"; |
| |
498 else |
| |
499 adminText = format ("%1 other admin%s1", Session.num_admins()); |
| |
500 |
| |
501 text = format ("%1 | %2 | %3", Session.address().to_string (IP_WITH_PORT), |
| |
502 Session.level(), adminText); |
| 545 } |
503 } |
| 546 break; |
504 break; |
| 547 } |
505 } |
| 548 |
506 |
| 549 if (not text.is_empty()) |
507 if (not text.is_empty()) |
| 558 } |
516 } |
| 559 } |
517 } |
| 560 |
518 |
| 561 // ------------------------------------------------------------------------------------------------- |
519 // ------------------------------------------------------------------------------------------------- |
| 562 // |
520 // |
| 563 FUNCTION |
521 void Interface::render_full() |
| 564 Interface::render_full() -> void |
|
| 565 { |
522 { |
| 566 update_statusbar(); |
523 update_statusbar(); |
| 567 interface_render_titlebar(); |
524 render_titlebar(); |
| 568 interface_render_output(); |
525 render_output(); |
| 569 interface_render_statusbar(); |
526 render_statusbar(); |
| 570 interface_render_input(); |
527 render_input(); |
| 571 interface_render_nicklist(); |
528 render_nicklist(); |
| 572 } |
529 } |
| 573 |
530 |
| 574 // ------------------------------------------------------------------------------------------------- |
531 // ------------------------------------------------------------------------------------------------- |
| 575 // |
532 // |
| 576 static FUNCTION |
533 void Interface::position_cursor() |
| 577 interface_position_cursor() -> void |
|
| 578 { |
534 { |
| 579 // This is only relevant if the input string is being drawn |
535 // This is only relevant if the input string is being drawn |
| 580 if (CurrentInputState == INPUTSTATE_CONFIRM_DISCONNECTION) |
536 if (CurrentInputState == INPUTSTATE_CONFIRM_DISCONNECTION) |
| 581 return; |
537 return; |
| 582 |
538 |
| 583 int y = LINES - 2; |
539 int y = LINES - 2; |
| 584 |
540 |
| 585 if (CursorCharacter.ch != '\0') |
541 if (CursorCharacter.ch != '\0') |
| 586 mvprintw (y, CursorCharacter.x, "%c", CursorCharacter.ch); |
542 mvprintw (y, CursorCharacter.x, "%c", CursorCharacter.ch); |
| 587 else |
543 else |
| 588 mvprintw (y, interface_prompt_string().length(), " "); |
544 mvprintw (y, prompt_string().length(), " "); |
| 589 } |
545 } |
| 590 |
546 |
| 591 // ------------------------------------------------------------------------------------------------- |
547 // ------------------------------------------------------------------------------------------------- |
| 592 // |
548 // |
| 593 static FUNCTION |
549 int Interface::find_previous_word() |
| 594 interface_find_previous_word() -> int |
|
| 595 { |
550 { |
| 596 const String& input = current_input(); |
551 const String& input = current_input(); |
| 597 int pos = CursorPosition; |
552 int pos = CursorPosition; |
| 598 |
553 |
| 599 // Move past whitespace |
554 // Move past whitespace |
| 607 return pos; |
562 return pos; |
| 608 } |
563 } |
| 609 |
564 |
| 610 // ------------------------------------------------------------------------------------------------- |
565 // ------------------------------------------------------------------------------------------------- |
| 611 // |
566 // |
| 612 static FUNCTION |
567 int Interface::find_next_word() |
| 613 interface_find_next_word() -> int |
|
| 614 { |
568 { |
| 615 const String& input = current_input(); |
569 const String& input = current_input(); |
| 616 int pos = CursorPosition; |
570 int pos = CursorPosition; |
| 617 |
571 |
| 618 // Move past current whitespace |
572 // Move past current whitespace |
| 659 |
611 |
| 660 if (CurrentInputState == INPUTSTATE_CONFIRM_DISCONNECTION) |
612 if (CurrentInputState == INPUTSTATE_CONFIRM_DISCONNECTION) |
| 661 { |
613 { |
| 662 if (ch == 'y' or ch == 'Y') |
614 if (ch == 'y' or ch == 'Y') |
| 663 { |
615 { |
| 664 RCONSession::get_session()->disconnect(); |
616 Session.disconnect(); |
| 665 DisconnectConfirmFunction(); |
617 DisconnectConfirmFunction(); |
| 666 } |
618 } |
| 667 else if (ch == 'n' or ch == 'N') |
619 else if (ch == 'n' or ch == 'N') |
| 668 set_input_state (INPUTSTATE_NORMAL); |
620 set_input_state (INPUTSTATE_NORMAL); |
| 669 |
621 |
| 682 { |
634 { |
| 683 case INPUTSTATE_CONFIRM_DISCONNECTION: |
635 case INPUTSTATE_CONFIRM_DISCONNECTION: |
| 684 break; |
636 break; |
| 685 |
637 |
| 686 case INPUTSTATE_NORMAL: |
638 case INPUTSTATE_NORMAL: |
| 687 safe_disconnect ([]() |
639 safe_disconnect ([&]() |
| 688 { |
640 { |
| 689 RCONSession* session = RCONSession::get_session(); |
641 if (Session.is_active()) |
| 690 |
|
| 691 if (session->is_active()) |
|
| 692 { |
642 { |
| 693 session->disconnect(); |
643 Session.disconnect(); |
| 694 set_input_state (INPUTSTATE_NORMAL); |
644 set_input_state (INPUTSTATE_NORMAL); |
| 695 } |
645 } |
| 696 else |
646 else |
| 697 { |
647 { |
| 698 endwin(); |
648 endwin(); |
| 789 case 'K' - 'A' + 1: // readline ^K - delete from cursor to end |
739 case 'K' - 'A' + 1: // readline ^K - delete from cursor to end |
| 790 yank (CursorPosition, mutable_current_input().length()); |
740 yank (CursorPosition, mutable_current_input().length()); |
| 791 break; |
741 break; |
| 792 |
742 |
| 793 case 'W' - 'A' + 1: // readline ^W - delete from previous word bounary to current |
743 case 'W' - 'A' + 1: // readline ^W - delete from previous word bounary to current |
| 794 yank (interface_find_previous_word(), CursorPosition); |
744 yank (find_previous_word(), CursorPosition); |
| 795 break; |
745 break; |
| 796 |
746 |
| 797 case 'Y' - 'A' + 1: // readline ^Y - paste previously deleted text |
747 case 'Y' - 'A' + 1: // readline ^Y - paste previously deleted text |
| 798 if (not PasteBuffer.is_empty()) |
748 if (not PasteBuffer.is_empty()) |
| 799 { |
749 { |
| 806 case '\t': |
756 case '\t': |
| 807 { |
757 { |
| 808 int space = current_input().find (" "); |
758 int space = current_input().find (" "); |
| 809 |
759 |
| 810 if (CurrentInputState == INPUTSTATE_NORMAL |
760 if (CurrentInputState == INPUTSTATE_NORMAL |
| 811 and InputCursor > 0 |
761 and CursorPosition > 0 |
| 812 and (space == -1 or space >= InputCursor)) |
762 and (space == -1 or space >= CursorPosition)) |
| 813 { |
763 { |
| 814 String start = current_input().mid (0, InputCursor); |
764 String start = current_input().mid (0, CursorPosition); |
| 815 RCONSession::get_session()->request_tab_complete (start); |
765 Session.request_tab_complete (start); |
| 816 } |
766 } |
| 817 } |
767 } |
| 818 break; |
768 break; |
| 819 |
769 |
| 820 case '\n': |
770 case '\n': |
| 842 break; |
792 break; |
| 843 |
793 |
| 844 case INPUTSTATE_PASSWORD: |
794 case INPUTSTATE_PASSWORD: |
| 845 if (CurrentInputState == INPUTSTATE_PASSWORD and not current_input().is_empty()) |
795 if (CurrentInputState == INPUTSTATE_PASSWORD and not current_input().is_empty()) |
| 846 { |
796 { |
| 847 RCONSession* session = RCONSession::get_session(); |
797 Session.disconnect(); |
| 848 session->disconnect(); |
798 Session.set_password (current_input()); |
| 849 session->set_password (current_input()); |
799 Session.connect (CurrentAddress); |
| 850 session->connect (CurrentAddress); |
|
| 851 set_input_state (INPUTSTATE_NORMAL); |
800 set_input_state (INPUTSTATE_NORMAL); |
| 852 } |
801 } |
| 853 break; |
802 break; |
| 854 |
803 |
| 855 case INPUTSTATE_NORMAL: |
804 case INPUTSTATE_NORMAL: |
| 856 if (RCONSession::get_session()->send_command (current_input())) |
805 if (Session.send_command (current_input())) |
| 857 { |
806 { |
| 858 InputHistory.insert (0, ""); |
807 InputHistory.insert (0, ""); |
| 859 NeedInputRender = true; |
808 NeedInputRender = true; |
| 860 } |
809 } |
| 861 break; |
810 break; |
| 862 } |
811 } |
| 863 break; |
812 break; |
| 864 |
813 |
| 865 case 'N' - 'A' + 1: // ^N |
814 case 'N' - 'A' + 1: // ^N |
| 866 if (CurrentInputState == INPUTSTATE_NORMAL) |
815 if (CurrentInputState == INPUTSTATE_NORMAL) |
| 867 safe_disconnect ([]() {set_input_state (INPUTSTATE_ADDRESS);}); |
816 safe_disconnect ([&]() {set_input_state (INPUTSTATE_ADDRESS);}); |
| 868 break; |
817 break; |
| 869 |
818 |
| 870 case '\e': // Escape |
819 case '\e': // Escape |
| 871 // We may have an alt key coming |
820 // We may have an alt key coming |
| 872 ch = ::getch(); |
821 ch = ::getch(); |
| 876 switch (ch) |
825 switch (ch) |
| 877 { |
826 { |
| 878 case 'b': |
827 case 'b': |
| 879 case 'B': |
828 case 'B': |
| 880 // readline alt-b - move one word to the left |
829 // readline alt-b - move one word to the left |
| 881 CursorPosition = interface_find_previous_word(); |
830 CursorPosition = find_previous_word(); |
| 882 NeedInputRender = true; |
831 NeedInputRender = true; |
| 883 break; |
832 break; |
| 884 |
833 |
| 885 case 'f': |
834 case 'f': |
| 886 case 'F': |
835 case 'F': |
| 887 // readline alt-f - move one word to the right |
836 // readline alt-f - move one word to the right |
| 888 CursorPosition = interface_find_next_word(); |
837 CursorPosition = find_next_word(); |
| 889 NeedInputRender = true; |
838 NeedInputRender = true; |
| 890 break; |
839 break; |
| 891 |
840 |
| 892 case 'd': |
841 case 'd': |
| 893 case 'D': |
842 case 'D': |
| 894 // readline alt-d - delete from here till next word boundary |
843 // readline alt-d - delete from here till next word boundary |
| 895 yank (CursorPosition, interface_find_next_word()); |
844 yank (CursorPosition, find_next_word()); |
| 896 break; |
845 break; |
| 897 } |
846 } |
| 898 } |
847 } |
| 899 else |
848 else |
| 900 { |
849 { |
| 910 render(); |
859 render(); |
| 911 } |
860 } |
| 912 |
861 |
| 913 // ------------------------------------------------------------------------------------------------- |
862 // ------------------------------------------------------------------------------------------------- |
| 914 // |
863 // |
| 915 FUNCTION |
864 void Interface::render() |
| 916 Interface::render() -> void |
865 { |
| 917 { |
866 if (NeedStatusBarRender) render_statusbar(); |
| 918 if (NeedStatusBarRender) interface_render_statusbar(); |
867 if (NeedInputRender) render_input(); |
| 919 if (NeedInputRender) interface_render_input(); |
868 if (NeedOutputRender) render_output(); |
| 920 if (NeedOutputRender) interface_render_output(); |
869 if (NeedNicklistRender) render_nicklist(); |
| 921 if (NeedNicklistRender) interface_render_nicklist(); |
|
| 922 |
870 |
| 923 if (NeedRefresh) |
871 if (NeedRefresh) |
| 924 { |
872 { |
| 925 interface_position_cursor(); |
873 position_cursor(); |
| 926 refresh(); |
874 refresh(); |
| 927 NeedRefresh = false; |
875 NeedRefresh = false; |
| 928 } |
876 } |
| 929 } |
877 } |
| 930 |
878 |
| 931 // ------------------------------------------------------------------------------------------------- |
879 // ------------------------------------------------------------------------------------------------- |
| 932 // |
880 // |
| 933 FUNCTION print_to_console (String a) -> void |
881 void Interface::print_to_console (String a) |
| 934 { |
882 { |
| 935 // Zandronum is retarded and SOMETIMES sends color codes as "\\c" and sometimes as "\x1C". |
883 // Zandronum sometimes sends color codes as "\\c" and sometimes as "\x1C". |
| 936 // Let's correct that on our end and HOPE this won't cause conflicts. |
884 // Let's correct that on our end and hope this won't cause conflicts. |
| 937 a.replace ("\\c", "\x1C"); |
885 a.replace ("\\c", "\x1C"); |
| 938 |
886 |
| 939 for (char ch : a) |
887 for (char ch : a) |
| 940 { |
888 { |
| 941 if (ch == '\n') |
889 if (ch == '\n') |
| 978 } |
925 } |
| 979 |
926 |
| 980 if (CurrentAddress.port == 0) |
927 if (CurrentAddress.port == 0) |
| 981 CurrentAddress.port = 10666; |
928 CurrentAddress.port = 10666; |
| 982 |
929 |
| 983 RCONSession* session = RCONSession::get_session(); |
930 Session.disconnect(); |
| 984 session->disconnect(); |
931 Session.set_password (password); |
| 985 session->set_password (password); |
932 Session.connect (CurrentAddress); |
| 986 session->connect (CurrentAddress); |
933 } |
| 987 } |
934 |
| 988 |
935 // ------------------------------------------------------------------------------------------------- |
| 989 // ------------------------------------------------------------------------------------------------- |
936 // |
| 990 // |
937 void Interface::set_player_names (const StringList& names) |
| 991 FUNCTION |
|
| 992 Interface::set_player_names (const StringList& names) -> void |
|
| 993 { |
938 { |
| 994 PlayerNames = names; |
939 PlayerNames = names; |
| 995 NeedNicklistRender = true; |
940 NeedNicklistRender = true; |
| 996 } |
941 } |
| 997 |
942 |
| 998 // ------------------------------------------------------------------------------------------------- |
943 // ------------------------------------------------------------------------------------------------- |
| 999 // |
944 // |
| 1000 FUNCTION |
945 void Interface::tab_complete (const String& part, String complete) |
| 1001 Interface::tab_complete (const String& part, String complete) -> void |
|
| 1002 { |
946 { |
| 1003 String& input = mutable_current_input(); |
947 String& input = mutable_current_input(); |
| 1004 |
948 |
| 1005 if (input.starts_with (part)) |
949 if (input.starts_with (part)) |
| 1006 { |
950 { |
| 1007 if (input[part.length()] != ' ') |
951 if (input[part.length()] != ' ') |
| 1008 complete += ' '; |
952 complete += ' '; |
| 1009 |
953 |
| 1010 input.replace (0, part.length(), complete); |
954 input.replace (0, part.length(), complete); |
| 1011 InputCursor = complete.length(); |
955 CursorPosition = complete.length(); |
| 1012 NeedInputRender = true; |
956 NeedInputRender = true; |
| 1013 } |
957 } |
| 1014 } |
958 } |