sources/interface.cpp

changeset 194
0c7e44e1078a
parent 193
fb1709f27108
child 195
be953e1621d9
equal deleted inserted replaced
193:fb1709f27108 194:0c7e44e1078a
51 51
52 // ------------------------------------------------------------------------------------------------- 52 // -------------------------------------------------------------------------------------------------
53 // 53 //
54 const std::string& Interface::getCurrentInput() 54 const std::string& Interface::getCurrentInput()
55 { 55 {
56 return m_inputHistory[m_inputCursor]; 56 return this->m_inputHistory[this->m_inputCursor];
57 } 57 }
58 58
59 // ------------------------------------------------------------------------------------------------- 59 // -------------------------------------------------------------------------------------------------
60 // 60 //
61 // Makes current_input() the lastmost input(so that we won't modify history) 61 // Makes current_input() the lastmost input(so that we won't modify history)
62 // 62 //
63 void Interface::detachInput() 63 void Interface::detachInput()
64 { 64 {
65 if (m_inputCursor > 0) 65 if (this->m_inputCursor > 0)
66 { 66 {
67 m_inputHistory[0] = getCurrentInput(); 67 this->m_inputHistory[0] = getCurrentInput();
68 m_inputCursor = 0; 68 this->m_inputCursor = 0;
69 } 69 }
70 } 70 }
71 71
72 // ------------------------------------------------------------------------------------------------- 72 // -------------------------------------------------------------------------------------------------
73 // A version of current_input() that allows changing the contents of it. 73 // A version of current_input() that allows changing the contents of it.
74 // 74 //
75 std::string& Interface::getEditableInput() 75 std::string& Interface::getEditableInput()
76 { 76 {
77 detachInput(); 77 detachInput();
78 return m_inputHistory[m_inputCursor]; 78 return this->m_inputHistory[this->m_inputCursor];
79 } 79 }
80 80
81 // ------------------------------------------------------------------------------------------------- 81 // -------------------------------------------------------------------------------------------------
82 // 82 //
83 void Interface::moveInputCursor(int delta) 83 void Interface::moveInputCursor(int delta)
84 { 84 {
85 // No input history when inputting addresses or passwords 85 // No input history when inputting addresses or passwords
86 if (m_inputState != INPUTSTATE_NORMAL) 86 if (this->m_inputState != INPUTSTATE_NORMAL)
87 { 87 {
88 m_inputCursor = 0; 88 this->m_inputCursor = 0;
89 return; 89 return;
90 } 90 }
91 91
92 int oldcursor = m_inputCursor; 92 int oldcursor = this->m_inputCursor;
93 m_inputCursor = clamp(m_inputCursor + delta, 0, static_cast<int>(m_inputHistory.size() - 1)); 93 this->m_inputCursor = clamp(this->m_inputCursor + delta, 0, static_cast<int>(this->m_inputHistory.size() - 1));
94 94
95 if (m_inputCursor != oldcursor) 95 if (this->m_inputCursor != oldcursor)
96 { 96 {
97 m_cursorPosition = getCurrentInput().length(); 97 this->m_cursorPosition = getCurrentInput().length();
98 m_needInputRender = true; 98 this->m_needInputRender = true;
99 } 99 }
100 } 100 }
101 101
102 // ------------------------------------------------------------------------------------------------- 102 // -------------------------------------------------------------------------------------------------
103 // 103 //
104 std::string Interface::getPromptString() 104 std::string Interface::getPromptString()
105 { 105 {
106 std::string prompt; 106 std::string prompt;
107 107
108 switch (m_inputState) 108 switch (this->m_inputState)
109 { 109 {
110 case INPUTSTATE_NORMAL: prompt = ">"; break; 110 case INPUTSTATE_NORMAL: prompt = ">"; break;
111 case INPUTSTATE_ADDRESS: prompt = "address:"; break; 111 case INPUTSTATE_ADDRESS: prompt = "address:"; break;
112 case INPUTSTATE_PASSWORD: prompt = "password:"; break; 112 case INPUTSTATE_PASSWORD: prompt = "password:"; break;
113 case INPUTSTATE_CONFIRM_DISCONNECTION: break; 113 case INPUTSTATE_CONFIRM_DISCONNECTION: break;
120 // 120 //
121 void Interface::setInputState(InputState newstate) 121 void Interface::setInputState(InputState newstate)
122 { 122 {
123 // Clear the input row(unless going to or from confirm state) 123 // Clear the input row(unless going to or from confirm state)
124 if (newstate != INPUTSTATE_CONFIRM_DISCONNECTION 124 if (newstate != INPUTSTATE_CONFIRM_DISCONNECTION
125 and m_inputState != INPUTSTATE_CONFIRM_DISCONNECTION) 125 and this->m_inputState != INPUTSTATE_CONFIRM_DISCONNECTION)
126 { 126 {
127 m_inputCursor = 0; 127 this->m_inputCursor = 0;
128 getEditableInput().clear(); 128 getEditableInput().clear();
129 } 129 }
130 130
131 switch (newstate) 131 switch (newstate)
132 { 132 {
133 case INPUTSTATE_ADDRESS: 133 case INPUTSTATE_ADDRESS:
134 if (m_remoteAddress.host != 0) 134 if (this->m_remoteAddress.host != 0)
135 getEditableInput() = net::ip_address_to_string(m_remoteAddress); 135 getEditableInput() = net::ip_address_to_string(this->m_remoteAddress);
136 break; 136 break;
137 137
138 default: 138 default:
139 break; 139 break;
140 } 140 }
141 141
142 m_inputState = newstate; 142 this->m_inputState = newstate;
143 m_needInputRender = true; 143 this->m_needInputRender = true;
144 } 144 }
145 145
146 // ------------------------------------------------------------------------------------------------- 146 // -------------------------------------------------------------------------------------------------
147 // 147 //
148 Interface::Interface() : 148 Interface::Interface() :
162 ::raw(); 162 ::raw();
163 ::keypad(stdscr, true); 163 ::keypad(stdscr, true);
164 ::noecho(); 164 ::noecho();
165 ::refresh(); 165 ::refresh();
166 ::timeout(0); 166 ::timeout(0);
167 m_inputHistory.clear(); 167 this->m_inputHistory.clear();
168 m_inputHistory.push_back(""); 168 this->m_inputHistory.push_back("");
169 m_outputLines.clear(); 169 this->m_outputLines.clear();
170 m_outputLines.push_back(ColoredLine()); 170 this->m_outputLines.push_back(ColoredLine());
171 m_session.setInterface(this); 171 this->m_session.setInterface(this);
172 resetTitle(); 172 resetTitle();
173 173
174 if (::has_colors()) 174 if (::has_colors())
175 { 175 {
176 ::start_color(); 176 ::start_color();
194 } 194 }
195 } 195 }
196 196
197 renderFull(); 197 renderFull();
198 refresh(); 198 refresh();
199 m_needRefresh = false; 199 this->m_needRefresh = false;
200 } 200 }
201 201
202 Interface::~Interface() 202 Interface::~Interface()
203 { 203 {
204 ::endwin(); 204 ::endwin();
206 206
207 // ------------------------------------------------------------------------------------------------- 207 // -------------------------------------------------------------------------------------------------
208 // 208 //
209 void Interface::renderTitlebar() 209 void Interface::renderTitlebar()
210 { 210 {
211 if (static_cast<signed>(m_title.length()) <= COLS) 211 if (static_cast<signed>(this->m_title.length()) <= COLS)
212 { 212 {
213 chtype pair = getColorPair(WHITE, BLUE); 213 chtype pair = getColorPair(WHITE, BLUE);
214 int startx =(COLS - m_title.length()) / 2; 214 int startx =(COLS - this->m_title.length()) / 2;
215 int endx = startx + m_title.length(); 215 int endx = startx + this->m_title.length();
216 attron(pair); 216 attron(pair);
217 mvprintw(0, startx, "%s", m_title.data()); 217 mvprintw(0, startx, "%s", this->m_title.data());
218 mvhline(0, 0, ' ', startx); 218 mvhline(0, 0, ' ', startx);
219 mvhline(0, endx, ' ', COLS - endx); 219 mvhline(0, endx, ' ', COLS - endx);
220 attroff(pair); 220 attroff(pair);
221 } 221 }
222 222
223 m_needRefresh = true; 223 this->m_needRefresh = true;
224 } 224 }
225 225
226 // ------------------------------------------------------------------------------------------------- 226 // -------------------------------------------------------------------------------------------------
227 // 227 //
228 void Interface::setTitle(const std::string& title) 228 void Interface::setTitle(const std::string& title)
229 { 229 {
230 m_title = title; 230 this->m_title = title;
231 renderTitlebar(); 231 renderTitlebar();
232 } 232 }
233 233
234 // ------------------------------------------------------------------------------------------------- 234 // -------------------------------------------------------------------------------------------------
235 // 235 //
236 void Interface::safeDisconnect(std::function<void(bool)> afterwards) 236 void Interface::safeDisconnect(std::function<void(bool)> afterwards)
237 { 237 {
238 if (m_session.isActive()) 238 if (this->m_session.isActive())
239 { 239 {
240 m_disconnectCallback = afterwards; 240 this->m_disconnectCallback = afterwards;
241 setInputState(INPUTSTATE_CONFIRM_DISCONNECTION); 241 setInputState(INPUTSTATE_CONFIRM_DISCONNECTION);
242 } 242 }
243 else 243 else
244 afterwards(false); 244 afterwards(false);
245 } 245 }
304 304
305 // ------------------------------------------------------------------------------------------------- 305 // -------------------------------------------------------------------------------------------------
306 // 306 //
307 void Interface::renderOutput() 307 void Interface::renderOutput()
308 { 308 {
309 if (m_outputLines.size() == 1) 309 if (this->m_outputLines.size() == 1)
310 return; 310 return;
311 311
312 m_outputScroll = clamp(m_outputScroll, 0, static_cast<signed>(m_outputLines.size() - 1)); 312 this->m_outputScroll = clamp(this->m_outputScroll, 0, static_cast<signed>(this->m_outputLines.size() - 1));
313 313
314 int height = LINES - 3; 314 int height = LINES - 3;
315 int width = COLS - nicklistWidth(); 315 int width = COLS - nicklistWidth();
316 int printOffset = 0; 316 int printOffset = 0;
317 int end = m_outputLines.size() - 1 - m_outputScroll; 317 int end = this->m_outputLines.size() - 1 - this->m_outputScroll;
318 int start = end; 318 int start = end;
319 int usedHeight = 0; 319 int usedHeight = 0;
320 int y = 1; 320 int y = 1;
321 bool tightFit = false; 321 bool tightFit = false;
322 322
323 // Where to start? 323 // Where to start?
324 while (start > 0) 324 while (start > 0)
325 { 325 {
326 int rows = m_outputLines[start - 1].rows(width); 326 int rows = this->m_outputLines[start - 1].rows(width);
327 327
328 if (usedHeight + rows > height) 328 if (usedHeight + rows > height)
329 { 329 {
330 // This line won't fit anymore. 330 // This line won't fit anymore.
331 tightFit = true; 331 tightFit = true;
337 } 337 }
338 338
339 // See if there's any more rows to use(end may be too small) 339 // See if there's any more rows to use(end may be too small)
340 if (not tightFit) 340 if (not tightFit)
341 { 341 {
342 while (end < static_cast<int>(m_outputLines.size())) 342 while (end < static_cast<int>(this->m_outputLines.size()))
343 { 343 {
344 int rows = m_outputLines[end].rows(width); 344 int rows = this->m_outputLines[end].rows(width);
345 345
346 if (usedHeight + rows > height) 346 if (usedHeight + rows > height)
347 { 347 {
348 tightFit = true; 348 tightFit = true;
349 break; 349 break;
355 } 355 }
356 356
357 if (start > 0) 357 if (start > 0)
358 printOffset = height - usedHeight; 358 printOffset = height - usedHeight;
359 359
360 m_outputScroll = m_outputLines.size() - 1 - end; 360 this->m_outputScroll = this->m_outputLines.size() - 1 - end;
361 361
362 if (start < 0 or start == end or printOffset >= height) 362 if (start < 0 or start == end or printOffset >= height)
363 return; 363 return;
364 364
365 assert(start <= end and start - end <= height); 365 assert(start <= end and start - end <= height);
370 370
371 // Print the lines 371 // Print the lines
372 y += printOffset; 372 y += printOffset;
373 373
374 for (int i = start; i < end; i += 1) 374 for (int i = start; i < end; i += 1)
375 y = renderColorline(y, 0, width, m_outputLines[i], true); 375 y = renderColorline(y, 0, width, this->m_outputLines[i], true);
376 376
377 m_needOutputRender = false; 377 this->m_needOutputRender = false;
378 m_needRefresh = true; 378 this->m_needRefresh = true;
379 } 379 }
380 380
381 // ------------------------------------------------------------------------------------------------- 381 // -------------------------------------------------------------------------------------------------
382 // 382 //
383 void Interface::renderNicklist() 383 void Interface::renderNicklist()
392 392
393 for (int i = 0; i < height; i += 1) 393 for (int i = 0; i < height; i += 1)
394 { 394 {
395 mvhline(y, x, ' ', width); 395 mvhline(y, x, ' ', width);
396 396
397 if (i < static_cast<signed>(m_playerNames.size())) 397 if (i < static_cast<signed>(this->m_playerNames.size()))
398 renderColorline(y, x, width, m_playerNames[i], false); 398 renderColorline(y, x, width, this->m_playerNames[i], false);
399 399
400 y++; 400 y++;
401 } 401 }
402 402
403 m_needNicklistRender = false; 403 this->m_needNicklistRender = false;
404 m_needRefresh = true; 404 this->m_needRefresh = true;
405 } 405 }
406 406
407 // ------------------------------------------------------------------------------------------------- 407 // -------------------------------------------------------------------------------------------------
408 // 408 //
409 void Interface::renderInput() 409 void Interface::renderInput()
410 { 410 {
411 chtype promptColor = getColorPair(WHITE, BLUE); 411 chtype promptColor = getColorPair(WHITE, BLUE);
412 412
413 // If we're asking the user if they want to disconnect, we don't render any input strings, 413 // If we're asking the user if they want to disconnect, we don't render any input strings,
414 // just the confirmation message. 414 // just the confirmation message.
415 if (m_inputState == INPUTSTATE_CONFIRM_DISCONNECTION) 415 if (this->m_inputState == INPUTSTATE_CONFIRM_DISCONNECTION)
416 { 416 {
417 attron(promptColor); 417 attron(promptColor);
418 mvhline(LINES - 2, 0, ' ', COLS); 418 mvhline(LINES - 2, 0, ' ', COLS);
419 mvprintw(LINES - 2, 0, "Are you sure you want to disconnect? y/n"); 419 mvprintw(LINES - 2, 0, "Are you sure you want to disconnect? y/n");
420 attroff(promptColor); 420 attroff(promptColor);
421 m_needRefresh = true; 421 this->m_needRefresh = true;
422 return; 422 return;
423 } 423 }
424 424
425 std::string prompt = getPromptString(); 425 std::string prompt = getPromptString();
426 int displayLength = COLS - prompt.length() - 2; 426 int displayLength = COLS - prompt.length() - 2;
427 std::string displayString = getCurrentInput(); 427 std::string displayString = getCurrentInput();
428 int y = LINES - 2; 428 int y = LINES - 2;
429 429
430 // If we're inputting a password, replace it with asterisks 430 // If we're inputting a password, replace it with asterisks
431 if (m_inputState == INPUTSTATE_PASSWORD) 431 if (this->m_inputState == INPUTSTATE_PASSWORD)
432 { 432 {
433 for (char &ch : displayString) 433 for (char &ch : displayString)
434 ch = '*'; 434 ch = '*';
435 } 435 }
436 436
437 // Ensure the cursor is within bounds 437 // Ensure the cursor is within bounds
438 m_cursorPosition = clamp(m_cursorPosition, 0, static_cast<signed>(displayString.length())); 438 this->m_cursorPosition = clamp(this->m_cursorPosition, 0, static_cast<signed>(displayString.length()));
439 439
440 // Ensure that the cursor is always in view, adjust panning if this is not the case 440 // Ensure that the cursor is always in view, adjust panning if this is not the case
441 if (m_cursorPosition > m_inputPanning + displayLength) 441 if (this->m_cursorPosition > this->m_inputPanning + displayLength)
442 m_inputPanning = m_cursorPosition - displayLength; // cursor went too far right 442 this->m_inputPanning = this->m_cursorPosition - displayLength; // cursor went too far right
443 else if (m_cursorPosition < m_inputPanning) 443 else if (this->m_cursorPosition < this->m_inputPanning)
444 m_inputPanning = m_cursorPosition; // cursor went past the pan value to the left 444 this->m_inputPanning = this->m_cursorPosition; // cursor went past the pan value to the left
445 445
446 // What part of the string to draw? 446 // What part of the string to draw?
447 int start = m_inputPanning; 447 int start = this->m_inputPanning;
448 int end = min<int>(displayString.length(), start + displayLength); 448 int end = min<int>(displayString.length(), start + displayLength);
449 assert(m_cursorPosition >= start and m_cursorPosition <= end); 449 assert(this->m_cursorPosition >= start and this->m_cursorPosition <= end);
450 450
451 // Render the input string 451 // Render the input string
452 mvhline(LINES - 2, 0, ' ', COLS); 452 mvhline(LINES - 2, 0, ' ', COLS);
453 mvprintw(y, prompt.length() + 1, "%s", mid(displayString, start, end).data()); 453 mvprintw(y, prompt.length() + 1, "%s", mid(displayString, start, end).data());
454 454
457 mvprintw(y, 0, "%s", prompt.data()); 457 mvprintw(y, 0, "%s", prompt.data());
458 attroff(promptColor); 458 attroff(promptColor);
459 459
460 // Store in memory where the cursor is now(so that we can re-draw it to position the terminal 460 // Store in memory where the cursor is now(so that we can re-draw it to position the terminal
461 // cursor). 461 // cursor).
462 m_cursorCharacter.ch = m_cursorPosition != 0 ? displayString[m_cursorPosition - 1] : '\0'; 462 this->m_cursorCharacter.ch = this->m_cursorPosition != 0 ? displayString[this->m_cursorPosition - 1] : '\0';
463 m_cursorCharacter.x = prompt.length() + (m_cursorPosition - m_inputPanning); 463 this->m_cursorCharacter.x = prompt.length() + (this->m_cursorPosition - this->m_inputPanning);
464 m_needRefresh = true; 464 this->m_needRefresh = true;
465 m_needInputRender = false; 465 this->m_needInputRender = false;
466 } 466 }
467 467
468 // ------------------------------------------------------------------------------------------------- 468 // -------------------------------------------------------------------------------------------------
469 // 469 //
470 void Interface::renderStatusBar() 470 void Interface::renderStatusBar()
471 { 471 {
472 chtype color = getColorPair(WHITE, BLUE); 472 chtype color = getColorPair(WHITE, BLUE);
473 int y = LINES - 1; 473 int y = LINES - 1;
474 attron(color); 474 attron(color);
475 mvhline(y, 0, ' ', COLS); 475 mvhline(y, 0, ' ', COLS);
476 mvprintw(y, 0, "%s", m_statusBarText.data()); 476 mvprintw(y, 0, "%s", this->m_statusBarText.data());
477 attroff(color); 477 attroff(color);
478 m_needRefresh = true; 478 this->m_needRefresh = true;
479 m_needStatusBarRender = false; 479 this->m_needStatusBarRender = false;
480 } 480 }
481 481
482 // ------------------------------------------------------------------------------------------------- 482 // -------------------------------------------------------------------------------------------------
483 // 483 //
484 void Interface::updateStatusBar() 484 void Interface::updateStatusBar()
485 { 485 {
486 std::string text; 486 std::string text;
487 487
488 switch (m_session.getState()) 488 switch (this->m_session.getState())
489 { 489 {
490 case RCON_DISCONNECTED: 490 case RCON_DISCONNECTED:
491 text = "Disconnected."; 491 text = "Disconnected.";
492 break; 492 break;
493 493
494 case RCON_CONNECTING: 494 case RCON_CONNECTING:
495 case RCON_AUTHENTICATING: 495 case RCON_AUTHENTICATING:
496 text = "Connecting to " + net::ip_address_to_string(m_session.address()) + "..."; 496 text = "Connecting to " + net::ip_address_to_string(this->m_session.address()) + "...";
497 break; 497 break;
498 498
499 case RCON_CONNECTED: 499 case RCON_CONNECTED:
500 { 500 {
501 std::string adminText; 501 std::string adminText;
502 502
503 if (m_session.getAdminCount() == 0) 503 if (this->m_session.getAdminCount() == 0)
504 { 504 {
505 adminText = "No other admins"; 505 adminText = "No other admins";
506 } 506 }
507 else 507 else
508 { 508 {
509 adminText = zfc::sprintf("%d other admin%s", m_session.getAdminCount(), 509 adminText = zfc::sprintf("%d other admin%s", this->m_session.getAdminCount(),
510 m_session.getAdminCount() != 1 ? "s" : ""); 510 this->m_session.getAdminCount() != 1 ? "s" : "");
511 } 511 }
512 512
513 text = zfc::sprintf("%s | %s | %s", 513 text = zfc::sprintf("%s | %s | %s",
514 net::ip_address_to_string(m_session.address()).data(), 514 net::ip_address_to_string(this->m_session.address()).data(),
515 m_session.getLevel().data(), 515 this->m_session.getLevel().data(),
516 adminText.data()); 516 adminText.data());
517 } 517 }
518 break; 518 break;
519 } 519 }
520 520
521 if (not text.empty()) 521 if (not text.empty())
522 text += " | "; 522 text += " | ";
523 523
524 text += "Ctrl+N to connect, Ctrl+Q to "; 524 text += "Ctrl+N to connect, Ctrl+Q to ";
525 text +=(m_session.getState() == RCON_DISCONNECTED) ? "quit" : "disconnect"; 525 text +=(this->m_session.getState() == RCON_DISCONNECTED) ? "quit" : "disconnect";
526 526
527 if (text != m_statusBarText) 527 if (text != this->m_statusBarText)
528 { 528 {
529 m_statusBarText = text; 529 this->m_statusBarText = text;
530 m_needStatusBarRender = true; 530 this->m_needStatusBarRender = true;
531 } 531 }
532 } 532 }
533 533
534 // ------------------------------------------------------------------------------------------------- 534 // -------------------------------------------------------------------------------------------------
535 // 535 //
546 // ------------------------------------------------------------------------------------------------- 546 // -------------------------------------------------------------------------------------------------
547 // 547 //
548 void Interface::positionCursor() 548 void Interface::positionCursor()
549 { 549 {
550 // This is only relevant if the input string is being drawn 550 // This is only relevant if the input string is being drawn
551 if (m_inputState == INPUTSTATE_CONFIRM_DISCONNECTION) 551 if (this->m_inputState == INPUTSTATE_CONFIRM_DISCONNECTION)
552 return; 552 return;
553 553
554 int y = LINES - 2; 554 int y = LINES - 2;
555 555
556 if (m_cursorCharacter.ch != '\0') 556 if (this->m_cursorCharacter.ch != '\0')
557 mvprintw(y, m_cursorCharacter.x, "%c", m_cursorCharacter.ch); 557 mvprintw(y, this->m_cursorCharacter.x, "%c", this->m_cursorCharacter.ch);
558 else 558 else
559 mvprintw(y, getPromptString().length(), " "); 559 mvprintw(y, getPromptString().length(), " ");
560 } 560 }
561 561
562 // ------------------------------------------------------------------------------------------------- 562 // -------------------------------------------------------------------------------------------------
563 // 563 //
564 int Interface::findPreviousWord() 564 int Interface::findPreviousWord()
565 { 565 {
566 const std::string& input = getCurrentInput(); 566 const std::string& input = getCurrentInput();
567 int pos = m_cursorPosition; 567 int pos = this->m_cursorPosition;
568 568
569 // Move past whitespace 569 // Move past whitespace
570 while (pos > 0 and isspace(input[pos - 1])) 570 while (pos > 0 and isspace(input[pos - 1]))
571 pos--; 571 pos--;
572 572
580 // ------------------------------------------------------------------------------------------------- 580 // -------------------------------------------------------------------------------------------------
581 // 581 //
582 int Interface::findNextWord() 582 int Interface::findNextWord()
583 { 583 {
584 const std::string& input = getCurrentInput(); 584 const std::string& input = getCurrentInput();
585 int pos = m_cursorPosition; 585 int pos = this->m_cursorPosition;
586 586
587 // Move past current whitespace 587 // Move past current whitespace
588 while (pos < static_cast<signed>(input.length()) and isspace(input[pos])) 588 while (pos < static_cast<signed>(input.length()) and isspace(input[pos]))
589 pos++; 589 pos++;
590 590
600 void Interface::yank(int a, int b) 600 void Interface::yank(int a, int b)
601 { 601 {
602 if (a >= b) 602 if (a >= b)
603 return; 603 return;
604 604
605 if (m_cursorPosition > a and m_cursorPosition <= b) 605 if (this->m_cursorPosition > a and this->m_cursorPosition <= b)
606 m_cursorPosition = a; 606 this->m_cursorPosition = a;
607 607
608 std::string& input = getEditableInput(); 608 std::string& input = getEditableInput();
609 m_pasteBuffer = mid(input, a, b); 609 this->m_pasteBuffer = mid(input, a, b);
610 input = remove_range(input, a, b - a); 610 input = remove_range(input, a, b - a);
611 m_needInputRender = true; 611 this->m_needInputRender = true;
612 } 612 }
613 613
614 bool Interface::tryResolveAddress(const std::string &address_string, net::ip_address* target) 614 bool Interface::tryResolveAddress(const std::string &address_string, net::ip_address* target)
615 { 615 {
616 std::stringstream errors; 616 std::stringstream errors;
645 ::clear(); 645 ::clear();
646 renderFull(); 646 renderFull();
647 return; 647 return;
648 } 648 }
649 649
650 if (m_inputState == INPUTSTATE_CONFIRM_DISCONNECTION) 650 if (this->m_inputState == INPUTSTATE_CONFIRM_DISCONNECTION)
651 { 651 {
652 if (ch == 'y' or ch == 'Y') 652 if (ch == 'y' or ch == 'Y')
653 { 653 {
654 m_session.disconnect(); 654 this->m_session.disconnect();
655 m_disconnectCallback(true); 655 this->m_disconnectCallback(true);
656 this->updateStatusBar(); 656 this->updateStatusBar();
657 } 657 }
658 else if (ch == 'n' or ch == 'N') 658 else if (ch == 'n' or ch == 'N')
659 setInputState(INPUTSTATE_NORMAL); 659 setInputState(INPUTSTATE_NORMAL);
660 660
662 } 662 }
663 663
664 if (ch >= 0x20 and ch <= 0x7E) 664 if (ch >= 0x20 and ch <= 0x7E)
665 { 665 {
666 std::string& input = getEditableInput(); 666 std::string& input = getEditableInput();
667 input.insert(input.begin() + m_cursorPosition, char(ch)); 667 input.insert(input.begin() + this->m_cursorPosition, char(ch));
668 m_cursorPosition += 1; 668 this->m_cursorPosition += 1;
669 m_needInputRender = true; 669 this->m_needInputRender = true;
670 } 670 }
671 else switch (ch) 671 else switch (ch)
672 { 672 {
673 case 'Q' - 'A' + 1: // ^Q 673 case 'Q' - 'A' + 1: // ^Q
674 switch (m_inputState) 674 switch (this->m_inputState)
675 { 675 {
676 case INPUTSTATE_CONFIRM_DISCONNECTION: 676 case INPUTSTATE_CONFIRM_DISCONNECTION:
677 break; 677 break;
678 678
679 case INPUTSTATE_NORMAL: 679 case INPUTSTATE_NORMAL:
699 } 699 }
700 break; 700 break;
701 701
702 case KEY_LEFT: 702 case KEY_LEFT:
703 case 'B' - 'A' + 1: // readline ^B 703 case 'B' - 'A' + 1: // readline ^B
704 if (m_cursorPosition > 0) 704 if (this->m_cursorPosition > 0)
705 { 705 {
706 m_cursorPosition--; 706 this->m_cursorPosition--;
707 m_needInputRender = true; 707 this->m_needInputRender = true;
708 } 708 }
709 break; 709 break;
710 710
711 case KEY_RIGHT: 711 case KEY_RIGHT:
712 case 'F' - 'A' + 1: // readline ^F 712 case 'F' - 'A' + 1: // readline ^F
713 if (m_cursorPosition < static_cast<int>(getCurrentInput().length())) 713 if (this->m_cursorPosition < static_cast<int>(getCurrentInput().length()))
714 { 714 {
715 m_cursorPosition++; 715 this->m_cursorPosition++;
716 m_needInputRender = true; 716 this->m_needInputRender = true;
717 } 717 }
718 break; 718 break;
719 719
720 case KEY_DOWN: 720 case KEY_DOWN:
721 case KEY_UP: 721 case KEY_UP:
722 moveInputCursor(ch == KEY_DOWN ? -1 : 1); 722 moveInputCursor(ch == KEY_DOWN ? -1 : 1);
723 break; 723 break;
724 724
725 case KEY_HOME: 725 case KEY_HOME:
726 case 'A' - 'A' + 1: // readline ^A 726 case 'A' - 'A' + 1: // readline ^A
727 if (m_cursorPosition != 0) 727 if (this->m_cursorPosition != 0)
728 { 728 {
729 m_cursorPosition = 0; 729 this->m_cursorPosition = 0;
730 m_needInputRender = true; 730 this->m_needInputRender = true;
731 } 731 }
732 break; 732 break;
733 733
734 case KEY_END: 734 case KEY_END:
735 case 'E' - 'A' + 1: // readline ^E 735 case 'E' - 'A' + 1: // readline ^E
736 if (m_cursorPosition != static_cast<signed>(getCurrentInput().length())) 736 if (this->m_cursorPosition != static_cast<signed>(getCurrentInput().length()))
737 { 737 {
738 m_cursorPosition = getCurrentInput().length(); 738 this->m_cursorPosition = getCurrentInput().length();
739 m_needInputRender = true; 739 this->m_needInputRender = true;
740 } 740 }
741 break; 741 break;
742 742
743 case KEY_BACKSPACE: 743 case KEY_BACKSPACE:
744 case '\b': 744 case '\b':
745 if (m_cursorPosition > 0) 745 if (this->m_cursorPosition > 0)
746 { 746 {
747 std::string& input = getEditableInput(); 747 std::string& input = getEditableInput();
748 input.erase(input.begin() + m_cursorPosition - 1); 748 input.erase(input.begin() + this->m_cursorPosition - 1);
749 m_cursorPosition -= 1; 749 this->m_cursorPosition -= 1;
750 m_needInputRender = true; 750 this->m_needInputRender = true;
751 } 751 }
752 break; 752 break;
753 753
754 case KEY_DC: 754 case KEY_DC:
755 case 'D' - 'A' + 1: // readline ^D 755 case 'D' - 'A' + 1: // readline ^D
756 if (m_cursorPosition < static_cast<signed>(getCurrentInput().length())) 756 if (this->m_cursorPosition < static_cast<signed>(getCurrentInput().length()))
757 { 757 {
758 std::string& input = getEditableInput(); 758 std::string& input = getEditableInput();
759 input.erase(input.begin() + m_cursorPosition); 759 input.erase(input.begin() + this->m_cursorPosition);
760 m_needInputRender = true; 760 this->m_needInputRender = true;
761 } 761 }
762 break; 762 break;
763 763
764 case KEY_PPAGE: 764 case KEY_PPAGE:
765 m_outputScroll += min(PAGE_SIZE, LINES / 2); 765 this->m_outputScroll += min(PAGE_SIZE, LINES / 2);
766 m_needOutputRender = true; 766 this->m_needOutputRender = true;
767 break; 767 break;
768 768
769 case KEY_NPAGE: 769 case KEY_NPAGE:
770 m_outputScroll -= min(PAGE_SIZE, LINES / 2); 770 this->m_outputScroll -= min(PAGE_SIZE, LINES / 2);
771 m_needOutputRender = true; 771 this->m_needOutputRender = true;
772 break; 772 break;
773 773
774 case 'U' - 'A' + 1: // readline ^U - delete from start to cursor 774 case 'U' - 'A' + 1: // readline ^U - delete from start to cursor
775 if (m_cursorPosition > 0) 775 if (this->m_cursorPosition > 0)
776 { 776 {
777 yank(0, m_cursorPosition); 777 yank(0, this->m_cursorPosition);
778 m_cursorPosition = 0; 778 this->m_cursorPosition = 0;
779 } 779 }
780 break; 780 break;
781 781
782 case 'K' - 'A' + 1: // readline ^K - delete from cursor to end 782 case 'K' - 'A' + 1: // readline ^K - delete from cursor to end
783 yank(m_cursorPosition, getEditableInput().length()); 783 yank(this->m_cursorPosition, getEditableInput().length());
784 break; 784 break;
785 785
786 case 'W' - 'A' + 1: // readline ^W - delete from previous word bounary to current 786 case 'W' - 'A' + 1: // readline ^W - delete from previous word bounary to current
787 yank(findPreviousWord(), m_cursorPosition); 787 yank(findPreviousWord(), this->m_cursorPosition);
788 break; 788 break;
789 789
790 case 'Y' - 'A' + 1: // readline ^Y - paste previously deleted text 790 case 'Y' - 'A' + 1: // readline ^Y - paste previously deleted text
791 if (not m_pasteBuffer.empty()) 791 if (not this->m_pasteBuffer.empty())
792 { 792 {
793 getEditableInput().insert(m_cursorPosition, m_pasteBuffer); 793 getEditableInput().insert(this->m_cursorPosition, this->m_pasteBuffer);
794 m_cursorPosition += m_pasteBuffer.length(); 794 this->m_cursorPosition += this->m_pasteBuffer.length();
795 m_needInputRender = true; 795 this->m_needInputRender = true;
796 } 796 }
797 break; 797 break;
798 798
799 case '\t': 799 case '\t':
800 { 800 {
801 int space = getCurrentInput().find(" "); 801 int space = getCurrentInput().find(" ");
802 802
803 if (m_inputState == INPUTSTATE_NORMAL 803 if (this->m_inputState == INPUTSTATE_NORMAL
804 and m_cursorPosition > 0 804 and this->m_cursorPosition > 0
805 and(space == -1 or space >= m_cursorPosition)) 805 and(space == -1 or space >= this->m_cursorPosition))
806 { 806 {
807 std::string start = mid(getCurrentInput(), 0, m_cursorPosition); 807 std::string start = mid(getCurrentInput(), 0, this->m_cursorPosition);
808 m_session.requestTabCompletion(start); 808 this->m_session.requestTabCompletion(start);
809 } 809 }
810 } 810 }
811 break; 811 break;
812 812
813 case '\n': 813 case '\n':
814 case '\r': 814 case '\r':
815 case KEY_ENTER: 815 case KEY_ENTER:
816 switch (m_inputState) 816 switch (this->m_inputState)
817 { 817 {
818 case INPUTSTATE_CONFIRM_DISCONNECTION: 818 case INPUTSTATE_CONFIRM_DISCONNECTION:
819 break; // handled above 819 break; // handled above
820 820
821 case INPUTSTATE_ADDRESS: 821 case INPUTSTATE_ADDRESS:
824 setInputState(INPUTSTATE_PASSWORD); 824 setInputState(INPUTSTATE_PASSWORD);
825 } 825 }
826 break; 826 break;
827 827
828 case INPUTSTATE_PASSWORD: 828 case INPUTSTATE_PASSWORD:
829 if (m_inputState == INPUTSTATE_PASSWORD and not getCurrentInput().empty()) 829 if (this->m_inputState == INPUTSTATE_PASSWORD and not getCurrentInput().empty())
830 { 830 {
831 m_session.disconnect(); 831 this->m_session.disconnect();
832 m_session.setPassword(getCurrentInput()); 832 this->m_session.setPassword(getCurrentInput());
833 m_session.connect(m_remoteAddress); 833 this->m_session.connect(this->m_remoteAddress);
834 setInputState(INPUTSTATE_NORMAL); 834 setInputState(INPUTSTATE_NORMAL);
835 } 835 }
836 break; 836 break;
837 837
838 case INPUTSTATE_NORMAL: 838 case INPUTSTATE_NORMAL:
839 if (getCurrentInput()[0] == '/') 839 if (getCurrentInput()[0] == '/')
840 { 840 {
841 handleCommand(getCurrentInput(), shouldquit); 841 handleCommand(getCurrentInput(), shouldquit);
842 flushInput(); 842 flushInput();
843 } 843 }
844 else if (m_session.sendCommand(getCurrentInput())) 844 else if (this->m_session.sendCommand(getCurrentInput()))
845 { 845 {
846 flushInput(); 846 flushInput();
847 } 847 }
848 break; 848 break;
849 } 849 }
850 break; 850 break;
851 851
852 case 'N' - 'A' + 1: // ^N 852 case 'N' - 'A' + 1: // ^N
853 if (m_inputState == INPUTSTATE_NORMAL) 853 if (this->m_inputState == INPUTSTATE_NORMAL)
854 safeDisconnect([&](bool){setInputState(INPUTSTATE_ADDRESS);}); 854 safeDisconnect([&](bool){setInputState(INPUTSTATE_ADDRESS);});
855 break; 855 break;
856 856
857 case '\x1b': // Escape 857 case '\x1b': // Escape
858 // We may have an alt key coming 858 // We may have an alt key coming
863 switch (ch) 863 switch (ch)
864 { 864 {
865 case 'b': 865 case 'b':
866 case 'B': 866 case 'B':
867 // readline alt-b - move one word to the left 867 // readline alt-b - move one word to the left
868 m_cursorPosition = findPreviousWord(); 868 this->m_cursorPosition = findPreviousWord();
869 m_needInputRender = true; 869 this->m_needInputRender = true;
870 break; 870 break;
871 871
872 case 'f': 872 case 'f':
873 case 'F': 873 case 'F':
874 // readline alt-f - move one word to the right 874 // readline alt-f - move one word to the right
875 m_cursorPosition = findNextWord(); 875 this->m_cursorPosition = findNextWord();
876 m_needInputRender = true; 876 this->m_needInputRender = true;
877 break; 877 break;
878 878
879 case 'd': 879 case 'd':
880 case 'D': 880 case 'D':
881 // readline alt-d - delete from here till next word boundary 881 // readline alt-d - delete from here till next word boundary
882 yank(m_cursorPosition, findNextWord()); 882 yank(this->m_cursorPosition, findNextWord());
883 break; 883 break;
884 884
885 case KEY_BACKSPACE: // alt+backspace, remove previous word 885 case KEY_BACKSPACE: // alt+backspace, remove previous word
886 case '\b': 886 case '\b':
887 yank(findPreviousWord(), m_cursorPosition); 887 yank(findPreviousWord(), this->m_cursorPosition);
888 break; 888 break;
889 } 889 }
890 } 890 }
891 else 891 else
892 { 892 {
893 // No alt-key, handle pure escape 893 // No alt-key, handle pure escape
894 if (m_inputState == INPUTSTATE_PASSWORD) 894 if (this->m_inputState == INPUTSTATE_PASSWORD)
895 setInputState(INPUTSTATE_ADDRESS); 895 setInputState(INPUTSTATE_ADDRESS);
896 else if (m_inputState == INPUTSTATE_ADDRESS) 896 else if (this->m_inputState == INPUTSTATE_ADDRESS)
897 setInputState(INPUTSTATE_NORMAL); 897 setInputState(INPUTSTATE_NORMAL);
898 } 898 }
899 break; 899 break;
900 } 900 }
901 901
904 904
905 // ------------------------------------------------------------------------------------------------- 905 // -------------------------------------------------------------------------------------------------
906 // 906 //
907 void Interface::render() 907 void Interface::render()
908 { 908 {
909 if (m_needStatusBarRender) renderStatusBar(); 909 if (this->m_needStatusBarRender) renderStatusBar();
910 if (m_needInputRender) renderInput(); 910 if (this->m_needInputRender) renderInput();
911 if (m_needOutputRender) renderOutput(); 911 if (this->m_needOutputRender) renderOutput();
912 if (m_needNicklistRender) renderNicklist(); 912 if (this->m_needNicklistRender) renderNicklist();
913 913
914 if (m_needRefresh) 914 if (this->m_needRefresh)
915 { 915 {
916 positionCursor(); 916 positionCursor();
917 refresh(); 917 refresh();
918 m_needRefresh = false; 918 this->m_needRefresh = false;
919 } 919 }
920 } 920 }
921 921
922 // ------------------------------------------------------------------------------------------------- 922 // -------------------------------------------------------------------------------------------------
923 // 923 //
981 981
982 for (char ch : message) 982 for (char ch : message)
983 { 983 {
984 if (ch == '\n') 984 if (ch == '\n')
985 { 985 {
986 zfc::last(m_outputLines).finalize(); 986 zfc::last(this->m_outputLines).finalize();
987 m_outputLines.push_back({}); 987 this->m_outputLines.push_back({});
988 continue; 988 continue;
989 } 989 }
990 990
991 if (zfc::last(m_outputLines).length() == 0) 991 if (zfc::last(this->m_outputLines).length() == 0)
992 { 992 {
993 time_t now; 993 time_t now;
994 time(&now); 994 time(&now);
995 char timestamp[32]; 995 char timestamp[32];
996 strftime(timestamp, sizeof timestamp, "[%H:%M:%S] ", localtime(&now)); 996 strftime(timestamp, sizeof timestamp, "[%H:%M:%S] ", localtime(&now));
997 997
998 for (char ch : std::string(timestamp)) 998 for (char ch : std::string(timestamp))
999 zfc::last(m_outputLines).addChar(ch); 999 zfc::last(this->m_outputLines).addChar(ch);
1000 } 1000 }
1001 1001
1002 // Remove some lines if there's too many of them. 20,000 should be enough, I hope. 1002 // Remove some lines if there's too many of them. 20,000 should be enough, I hope.
1003 while (m_outputLines.size() > 20000) 1003 while (this->m_outputLines.size() > 20000)
1004 m_outputLines.erase(m_outputLines.begin()); 1004 this->m_outputLines.erase(this->m_outputLines.begin());
1005 1005
1006 zfc::last(m_outputLines).addChar(ch); 1006 zfc::last(this->m_outputLines).addChar(ch);
1007 } 1007 }
1008 1008
1009 m_needOutputRender = true; 1009 this->m_needOutputRender = true;
1010 } 1010 }
1011 1011
1012 // ------------------------------------------------------------------------------------------------- 1012 // -------------------------------------------------------------------------------------------------
1013 // 1013 //
1014 void Interface::connect(std::string address_string, std::string password) 1014 void Interface::connect(std::string address_string, std::string password)
1015 { 1015 {
1016 if (this->tryResolveAddress(address_string, &this->m_remoteAddress)) 1016 if (this->tryResolveAddress(address_string, &this->m_remoteAddress))
1017 { 1017 {
1018 m_session.disconnect(); 1018 this->m_session.disconnect();
1019 m_session.setPassword(password); 1019 this->m_session.setPassword(password);
1020 m_session.connect(m_remoteAddress); 1020 this->m_session.connect(this->m_remoteAddress);
1021 } 1021 }
1022 } 1022 }
1023 1023
1024 // ------------------------------------------------------------------------------------------------- 1024 // -------------------------------------------------------------------------------------------------
1025 // 1025 //
1026 void Interface::setPlayerNames(const std::vector<std::string>& names) 1026 void Interface::setPlayerNames(const std::vector<std::string>& names)
1027 { 1027 {
1028 m_playerNames.clear(); 1028 this->m_playerNames.clear();
1029 1029
1030 for (const std::string& name : names) 1030 for (const std::string& name : names)
1031 { 1031 {
1032 ColoredLine coloredname; 1032 ColoredLine coloredname;
1033 coloredname.addString(name); 1033 coloredname.addString(name);
1034 coloredname.finalize(); 1034 coloredname.finalize();
1035 m_playerNames.push_back(coloredname); 1035 this->m_playerNames.push_back(coloredname);
1036 } 1036 }
1037 1037
1038 m_needNicklistRender = true; 1038 this->m_needNicklistRender = true;
1039 } 1039 }
1040 1040
1041 // ------------------------------------------------------------------------------------------------- 1041 // -------------------------------------------------------------------------------------------------
1042 // 1042 //
1043 void Interface::tabComplete(const std::string& part, std::string complete) 1043 void Interface::tabComplete(const std::string& part, std::string complete)
1048 { 1048 {
1049 if (input[part.length()] != ' ') 1049 if (input[part.length()] != ' ')
1050 complete += ' '; 1050 complete += ' ';
1051 1051
1052 input.replace(0, part.length(), complete); 1052 input.replace(0, part.length(), complete);
1053 m_cursorPosition = complete.length(); 1053 this->m_cursorPosition = complete.length();
1054 m_needInputRender = true; 1054 this->m_needInputRender = true;
1055 } 1055 }
1056 } 1056 }
1057 1057
1058 // ------------------------------------------------------------------------------------------------- 1058 // -------------------------------------------------------------------------------------------------
1059 // 1059 //
1077 this->connect(args[0], args[1]); 1077 this->connect(args[0], args[1]);
1078 } 1078 }
1079 } 1079 }
1080 else if (command == "disconnect") 1080 else if (command == "disconnect")
1081 { 1081 {
1082 m_session.disconnect(); 1082 this->m_session.disconnect();
1083 } 1083 }
1084 else if (command == "quit") 1084 else if (command == "quit")
1085 { 1085 {
1086 m_session.disconnect(); 1086 this->m_session.disconnect();
1087 *shouldquit = true; 1087 *shouldquit = true;
1088 } 1088 }
1089 else 1089 else
1090 printError("Unknown command: %s\n", command.data()); 1090 printError("Unknown command: %s\n", command.data());
1091 } 1091 }
1092 1092
1093 // ------------------------------------------------------------------------------------------------- 1093 // -------------------------------------------------------------------------------------------------
1094 // 1094 //
1095 void Interface::disconnected() 1095 void Interface::disconnected()
1096 { 1096 {
1097 print("Disconnected from %s\n", net::ip_address_to_string(m_session.address()).data()); 1097 print("Disconnected from %s\n", net::ip_address_to_string(this->m_session.address()).data());
1098 resetTitle(); 1098 resetTitle();
1099 renderFull(); 1099 renderFull();
1100 } 1100 }
1101 1101
1102 // ------------------------------------------------------------------------------------------------- 1102 // -------------------------------------------------------------------------------------------------
1103 // 1103 //
1104 void Interface::resetTitle() 1104 void Interface::resetTitle()
1105 { 1105 {
1106 m_title = sprintf("%s %s (%s)", application_name(), full_version_string(), changeset_date_string()); 1106 this->m_title = sprintf("%s %s (%s)", application_name(), full_version_string(), changeset_date_string());
1107 } 1107 }
1108 1108
1109 // ------------------------------------------------------------------------------------------------- 1109 // -------------------------------------------------------------------------------------------------
1110 // 1110 //
1111 void Interface::flushInput() 1111 void Interface::flushInput()
1112 { 1112 {
1113 m_inputHistory.insert(m_inputHistory.begin(), ""); 1113 this->m_inputHistory.insert(this->m_inputHistory.begin(), "");
1114 m_inputCursor = 0; 1114 this->m_inputCursor = 0;
1115 m_needInputRender = true; 1115 this->m_needInputRender = true;
1116 } 1116 }
1117 1117
1118 END_ZFC_NAMESPACE 1118 END_ZFC_NAMESPACE

mercurial