Console.cpp 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306
  1. #include "Console.h"
  2. #include <iostream>
  3. #ifndef WIN32
  4. # include <termios.h>
  5. # include <unistd.h>
  6. #endif
  7. using namespace Framework;
  8. ConsoleCommand::ConsoleCommand(Text name)
  9. : ReferenceCounter(),
  10. name(name)
  11. {}
  12. ConsoleCommand::~ConsoleCommand() {}
  13. Text& Framework::ConsoleCommand::getName()
  14. {
  15. return name;
  16. }
  17. Framework::StickyConsoleContent::StickyConsoleContent()
  18. : ReferenceCounter(),
  19. length(0),
  20. content(0),
  21. color(0),
  22. backgroundColor(0),
  23. zConsoleHandler(0)
  24. {}
  25. Framework::StickyConsoleContent::~StickyConsoleContent()
  26. {
  27. delete[] content;
  28. delete[] color;
  29. delete[] backgroundColor;
  30. }
  31. int Framework::StickyConsoleContent::getLength() const
  32. {
  33. return length;
  34. }
  35. void Framework::StickyConsoleContent::setContent(
  36. int length, const char* content)
  37. {
  38. delete[] this->content;
  39. this->content = new char[length];
  40. memcpy(this->content, content, length);
  41. this->length = length;
  42. delete[] color;
  43. delete[] backgroundColor;
  44. color = 0;
  45. backgroundColor = 0;
  46. }
  47. void Framework::StickyConsoleContent::setContent(
  48. int length, const char* content, Color color)
  49. {
  50. setContent(length, content);
  51. this->color = new Color[length];
  52. for (int i = 0; i < length; i++)
  53. {
  54. this->color[i] = color;
  55. }
  56. }
  57. void Framework::StickyConsoleContent::setContent(
  58. int length, const char* content, Color color, Color backgroundColor)
  59. {
  60. setContent(length, content, color);
  61. this->backgroundColor = new Color[length];
  62. for (int i = 0; i < length; i++)
  63. {
  64. this->backgroundColor[i] = backgroundColor;
  65. }
  66. }
  67. void Framework::StickyConsoleContent::setContent(
  68. int length, const char* content, Color* color)
  69. {
  70. setContent(length, content);
  71. this->color = new Color[length];
  72. for (int i = 0; i < length; i++)
  73. {
  74. this->color[i] = color[i];
  75. }
  76. }
  77. void Framework::StickyConsoleContent::setContent(
  78. int length, const char* content, Color* color, Color* backgroundColor)
  79. {
  80. setContent(length, content, color);
  81. this->backgroundColor = new Color[length];
  82. for (int i = 0; i < length; i++)
  83. {
  84. this->backgroundColor[i] = backgroundColor[i];
  85. }
  86. }
  87. void Framework::StickyConsoleContent::repaceContent(
  88. int start, int length, int newLength, const char* newContent)
  89. {
  90. int resultLength = this->length + newLength - length;
  91. char* resultContent = new char[resultLength];
  92. memcpy(resultContent, content, start);
  93. memcpy(resultContent + start, newContent, newLength);
  94. memcpy(resultContent + start + newLength,
  95. content + start + length,
  96. this->length - start - length);
  97. delete[] content;
  98. content = resultContent;
  99. if (color)
  100. {
  101. Color* resultColor = new Color[resultLength];
  102. memcpy(resultColor, color, start);
  103. for (int i = 0; i < newLength; i++)
  104. {
  105. resultColor[start + i] = color[start];
  106. }
  107. memcpy(resultColor + start + newLength,
  108. color + start + length,
  109. sizeof(Color) * (this->length - start - length));
  110. delete[] color;
  111. color = resultColor;
  112. }
  113. if (backgroundColor)
  114. {
  115. Color* resultBgColor = new Color[resultLength];
  116. memcpy(resultBgColor, backgroundColor, start);
  117. for (int i = 0; i < newLength; i++)
  118. {
  119. resultBgColor[start + i] = backgroundColor[start];
  120. }
  121. memcpy(resultBgColor + start + newLength,
  122. backgroundColor + start + length,
  123. sizeof(Color) * (this->length - start - length));
  124. delete[] backgroundColor;
  125. backgroundColor = resultBgColor;
  126. }
  127. this->length = resultLength;
  128. }
  129. void Framework::StickyConsoleContent::repaceContent(
  130. int start, int length, int newLength, const char* newContent, Color color)
  131. {
  132. repaceContent(start, length, newLength, newContent);
  133. if (!this->color)
  134. {
  135. this->color = new Color[this->length];
  136. for (int i = 0; i < this->length; i++)
  137. {
  138. this->color[i] = (Color)-1;
  139. }
  140. }
  141. for (int i = start; i < start + newLength; i++)
  142. {
  143. this->color[i] = color;
  144. }
  145. }
  146. void Framework::StickyConsoleContent::repaceContent(int start,
  147. int length,
  148. int newLength,
  149. const char* newContent,
  150. Color color,
  151. Color backgroundColor)
  152. {
  153. repaceContent(start, length, newLength, newContent, color);
  154. if (!this->backgroundColor)
  155. {
  156. this->backgroundColor = new Color[this->length];
  157. for (int i = 0; i < this->length; i++)
  158. {
  159. this->backgroundColor[i] = (Color)-1;
  160. }
  161. }
  162. for (int i = start; i < start + newLength; i++)
  163. {
  164. this->backgroundColor[i] = backgroundColor;
  165. }
  166. }
  167. void Framework::StickyConsoleContent::repaceContent(
  168. int start, int length, int newLength, const char* newContent, Color* color)
  169. {
  170. repaceContent(start, length, newLength, newContent);
  171. if (!this->color)
  172. {
  173. this->color = new Color[this->length];
  174. for (int i = 0; i < this->length; i++)
  175. {
  176. this->color[i] = (Color)-1;
  177. }
  178. }
  179. memcpy(this->color + start, color, sizeof(Color) * newLength);
  180. }
  181. void Framework::StickyConsoleContent::repaceContent(int start,
  182. int length,
  183. int newLength,
  184. const char* newContent,
  185. Color* color,
  186. Color* backgroundColor)
  187. {
  188. repaceContent(start, length, newLength, newContent, color);
  189. if (!this->backgroundColor)
  190. {
  191. this->backgroundColor = new Color[this->length];
  192. for (int i = 0; i < this->length; i++)
  193. {
  194. this->backgroundColor[i] = (Color)-1;
  195. }
  196. }
  197. memcpy(this->backgroundColor + start,
  198. backgroundColor,
  199. sizeof(Color) * newLength);
  200. }
  201. void Framework::StickyConsoleContent::triggerUpdate()
  202. {
  203. if (zConsoleHandler != 0)
  204. {
  205. zConsoleHandler->print();
  206. }
  207. }
  208. bool Framework::StickyConsoleContent::isInput()
  209. {
  210. return false;
  211. }
  212. void Framework::StickyConsoleContent::setConsoleHandlerZ(
  213. ConsoleHandler* zConsoleHandler)
  214. {
  215. this->zConsoleHandler = zConsoleHandler;
  216. }
  217. void Framework::StickyConsoleContent::setCursorToBeginning() {}
  218. int Framework::StickyConsoleContent::print() const
  219. {
  220. int lineCount = 0;
  221. int maxWidth = zConsoleHandler->getWidth();
  222. std::cout << "\r\33[0K"; // erase current line
  223. int lineLength = 0;
  224. int lastColor = -1;
  225. int lastBgColor = -1;
  226. for (int i = 0; i < length; i++)
  227. {
  228. if (color && (int)color[i] != lastColor)
  229. {
  230. if ((int)color[i] == -1)
  231. {
  232. std::cout << "\033[0m";
  233. if (backgroundColor && (int)backgroundColor[i] != -1)
  234. {
  235. if (backgroundColor[i] < Color::LIGHT_BLACK)
  236. {
  237. std::cout << Text("\033[1;")
  238. + Text(40 + (int)backgroundColor[i])
  239. + "m";
  240. }
  241. else
  242. {
  243. std::cout << Text("\033[1;")
  244. + Text(100 + (int)backgroundColor[i])
  245. + "m";
  246. }
  247. }
  248. }
  249. else if (color[i] < Color::LIGHT_BLACK)
  250. {
  251. std::cout << Text("\033[1;") + Text(30 + (int)color[i]) + "m";
  252. }
  253. else
  254. {
  255. std::cout << Text("\033[1;") + Text(90 + (int)color[i]) + "m";
  256. }
  257. }
  258. if (backgroundColor && (int)backgroundColor[i] != lastBgColor)
  259. {
  260. if ((int)backgroundColor[i] == -1)
  261. {
  262. std::cout << "\033[0m";
  263. if (color && (int)color[i] != -1)
  264. {
  265. if (color[i] < Color::LIGHT_BLACK)
  266. {
  267. std::cout
  268. << Text("\033[1;") + Text(30 + (int)color[i]) + "m";
  269. }
  270. else
  271. {
  272. std::cout
  273. << Text("\033[1;") + Text(90 + (int)color[i]) + "m";
  274. }
  275. }
  276. }
  277. else if (backgroundColor[i] < Color::LIGHT_BLACK)
  278. {
  279. std::cout << Text("\033[1;")
  280. + Text(40 + (int)backgroundColor[i]) + "m";
  281. }
  282. else
  283. {
  284. std::cout << Text("\033[1;")
  285. + Text(100 + (int)backgroundColor[i]) + "m";
  286. }
  287. }
  288. std::cout << content[i];
  289. lastColor = color ? (int)color[i] : -1;
  290. lastBgColor = backgroundColor ? (int)backgroundColor[i] : -1;
  291. if ((content[i] == '\n' || lineLength == maxWidth) && i < length - 1)
  292. {
  293. if (lineLength == maxWidth && content[i] != '\n')
  294. {
  295. std::cout << "\n";
  296. }
  297. lineLength = 0;
  298. lineCount++;
  299. }
  300. else
  301. {
  302. lineLength++;
  303. }
  304. }
  305. if (lastColor != -1 || lastBgColor != -1) std::cout << "\033[0m";
  306. if (lineCount == 0 && lineLength > 0)
  307. {
  308. lineCount++;
  309. }
  310. return lineCount;
  311. }
  312. void Framework::StickyConsoleContent::restoreCursorPos() {}
  313. ConsoleHandler* Framework::StickyConsoleContent::zConsoleHandlerRef() const
  314. {
  315. return zConsoleHandler;
  316. }
  317. Framework::ConsoleProgressBar::ConsoleProgressBar()
  318. : StickyConsoleContent(),
  319. progress(0),
  320. maxProgress(1),
  321. maxWidth(-1)
  322. {}
  323. void Framework::ConsoleProgressBar::setMaxWidth(int maxWidth)
  324. {
  325. this->maxWidth = maxWidth;
  326. }
  327. void Framework::ConsoleProgressBar::setProgress(int progress)
  328. {
  329. if (progress < 0) progress = 0;
  330. this->progress = progress;
  331. }
  332. void Framework::ConsoleProgressBar::setMaxProgress(int maxProgress)
  333. {
  334. if (maxProgress < 0) maxProgress = 0;
  335. this->maxProgress = maxProgress;
  336. }
  337. int Framework::ConsoleProgressBar::getProgress() const
  338. {
  339. return progress;
  340. }
  341. int Framework::ConsoleProgressBar::print() const
  342. {
  343. int maxWidth = zConsoleHandlerRef()->getWidth();
  344. int width = this->maxWidth == -1 ? maxWidth : this->maxWidth;
  345. if (width > maxWidth)
  346. {
  347. width = maxWidth;
  348. }
  349. if (width < 7)
  350. {
  351. return 0;
  352. }
  353. std::cout << "\r\33[0K"; // erase current line
  354. int progress = this->progress > maxProgress ? maxProgress : this->progress;
  355. int progressChars = width - 7;
  356. std::cout << "[\033[1;47m";
  357. int progressWidth = (int)(((double)progress / maxProgress) * progressChars);
  358. if (progressWidth > progressChars)
  359. {
  360. progressWidth = progressChars;
  361. }
  362. for (int i = 0; i < progressWidth; i++)
  363. {
  364. std::cout << " ";
  365. }
  366. std::cout << "\033[0m";
  367. for (int i = 0; i < progressChars - progressWidth; i++)
  368. {
  369. std::cout << " ";
  370. }
  371. std::cout << "] ";
  372. Text str((int)(((double)progress / maxProgress) * 100));
  373. for (int i = 0; i < 3 - str.getLength(); i++)
  374. {
  375. std::cout << " ";
  376. }
  377. std::cout << str.getText() << "%";
  378. return 1;
  379. }
  380. Framework::InputLine::InputLine()
  381. : StickyConsoleContent(),
  382. Thread(),
  383. input(""),
  384. cursorPos(0),
  385. suggestions(0)
  386. {}
  387. Framework::InputLine::~InputLine()
  388. {
  389. suggestions->release();
  390. }
  391. void Framework::InputLine::addPossibleCommand(ConsoleCommand* command)
  392. {
  393. commands.add(command);
  394. }
  395. bool Framework::InputLine::isInput()
  396. {
  397. return true;
  398. }
  399. void Framework::InputLine::setCursorToBeginning()
  400. {
  401. cs.lock();
  402. std::cout << "\r";
  403. }
  404. int Framework::InputLine::print() const
  405. {
  406. std::cout << "\33[0K" // clear current line
  407. << input.getText();
  408. if (suggestions)
  409. {
  410. std::cout << "\n";
  411. return 1 + dynamic_cast<StickyConsoleContent*>(suggestions)->print();
  412. }
  413. return 1;
  414. }
  415. void Framework::InputLine::restoreCursorPos()
  416. {
  417. if (cursorPos > 0)
  418. {
  419. std::cout << Text("\33[") + Text(cursorPos) + "C";
  420. }
  421. cs.unlock();
  422. }
  423. void Framework::InputLine::thread()
  424. {
  425. #ifdef WIN32
  426. INPUT_RECORD inputRecord;
  427. DWORD eventsRead;
  428. HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
  429. while (true)
  430. {
  431. if (ReadConsoleInput(handle, &inputRecord, 1, &eventsRead))
  432. {
  433. cs.lock();
  434. if (eventsRead > 0 && inputRecord.EventType == KEY_EVENT
  435. && inputRecord.Event.KeyEvent.bKeyDown)
  436. {
  437. if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_LEFT)
  438. {
  439. cursorPos--;
  440. if (cursorPos < 0)
  441. {
  442. cursorPos = 0;
  443. }
  444. else
  445. {
  446. std::cout << "\33[1D" << std::flush;
  447. }
  448. }
  449. else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_HOME)
  450. { // Pos1 key moves cursor to beginning of line
  451. std::cout << "\r" << std::flush;
  452. cursorPos = 0;
  453. }
  454. else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_RIGHT)
  455. {
  456. cursorPos++;
  457. if (cursorPos > input.getLength())
  458. {
  459. cursorPos = input.getLength();
  460. }
  461. else
  462. {
  463. std::cout << "\33[1C" << std::flush;
  464. }
  465. }
  466. else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_END)
  467. { // Pos1 key moves cursor to beginning of line
  468. cursorPos = input.getLength();
  469. std::cout << Text("\r\33[") + Text(cursorPos) + "C"
  470. << std::flush;
  471. }
  472. else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_TAB)
  473. {
  474. applyAutocompletion();
  475. }
  476. else if (inputRecord.Event.KeyEvent.wVirtualKeyCode
  477. == VK_RETURN)
  478. {
  479. std::cout << "\r\33[0K" << std::flush; // eraze line
  480. Text cmd = input;
  481. cs.unlock();
  482. if (executeCommand(cmd))
  483. {
  484. cs.lock();
  485. input = "";
  486. cursorPos = 0;
  487. cs.unlock();
  488. }
  489. continue; // skip the unlock
  490. }
  491. else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_BACK)
  492. {
  493. if (cursorPos > 0)
  494. {
  495. input.remove(cursorPos - 1, cursorPos);
  496. cursorPos--;
  497. std::cout << "\r\33[0K" // eraze line
  498. << input.getText() // output current input
  499. // move cursor to current position
  500. << Text("\r\33[") + Text(cursorPos) + "C"
  501. << std::flush;
  502. }
  503. }
  504. else if (inputRecord.Event.KeyEvent.uChar.AsciiChar)
  505. {
  506. input.insert(
  507. cursorPos, inputRecord.Event.KeyEvent.uChar.AsciiChar);
  508. cursorPos++;
  509. std::cout << "\r\33[0K" // eraze line
  510. << input.getText() // output current input
  511. // move cursor to current position
  512. << Text("\r\33[") + Text(cursorPos) + "C"
  513. << std::flush;
  514. }
  515. }
  516. cs.unlock();
  517. }
  518. }
  519. #else
  520. char c;
  521. while (read(STDIN_FILENO, &c, 1) == 1)
  522. {
  523. if (c == 27)
  524. { // Check for the escape key (27 is the ASCII code for escape)
  525. char seq[2];
  526. if (read(STDIN_FILENO, &seq, 2) == 2)
  527. {
  528. cs.lock();
  529. if (seq[0] == '[')
  530. {
  531. if (seq[1] == 'D')
  532. { // left arrow key
  533. cursorPos--;
  534. if (cursorPos < 0)
  535. {
  536. cursorPos = 0;
  537. }
  538. else
  539. {
  540. std::cout << "\33[1D" << std::flush;
  541. }
  542. }
  543. else if (seq[1] == 'H')
  544. { // Pos1 key moves cursor to beginning of line
  545. std::cout << "\r" << std::flush;
  546. cursorPos = 0;
  547. }
  548. else if (seq[1] == 'C')
  549. { // right arrow key
  550. cursorPos++;
  551. if (cursorPos > input.getLength())
  552. {
  553. cursorPos = input.getLength();
  554. }
  555. else
  556. {
  557. std::cout << "\33[1C" << std::flush;
  558. }
  559. }
  560. else if (seq[1] == 'F')
  561. { // End key moves cursor to end of line
  562. cursorPos = input.getLength();
  563. std::cout << Text("\r\33[") + Text(cursorPos) + "C"
  564. << std::flush;
  565. }
  566. }
  567. cs.unlock();
  568. }
  569. }
  570. else if (c == 8)
  571. { // backspace
  572. if (cursorPos > 0)
  573. {
  574. cs.lock();
  575. input.remove(cursorPos - 1, cursorPos);
  576. cursorPos--;
  577. std::cout << "\r\33[0K" // eraze line
  578. << input.getText() // output current input
  579. // move cursor to current position
  580. << Text("\r\33[") + Text(cursorPos) + "C"
  581. << std::flush;
  582. cs.unlock();
  583. }
  584. }
  585. else if (c == 9)
  586. { // tab
  587. cs.lock();
  588. applyAutocompletion();
  589. cs.unlock();
  590. }
  591. else if (c == 13)
  592. { // enter
  593. cs.lock();
  594. std::cout << "\r\33[0K" << std::flush; // eraze line
  595. Text cmd = input;
  596. cs.unlock();
  597. if (executeCommand(cmd))
  598. {
  599. cs.lock();
  600. input = "";
  601. cursorPos = 0;
  602. cs.unlock();
  603. }
  604. }
  605. else
  606. {
  607. cs.lock();
  608. input.insert(cursorPos, c);
  609. cursorPos++;
  610. std::cout << "\r\33[0K" // eraze line
  611. << input.getText() // output current input
  612. // move cursor to current position
  613. << Text("\r\33[") + Text(cursorPos) + "C" << std::flush;
  614. cs.unlock();
  615. }
  616. }
  617. #endif
  618. }
  619. void Framework::InputLine::applyAutocompletion()
  620. {
  621. RCArray<Text> params;
  622. bool lastArgFinished = 0;
  623. Text* cmd = input.getTeilText(0, cursorPos);
  624. parseCommand(*cmd, params, lastArgFinished);
  625. cmd->release();
  626. bool paramsStarted = params.getEintragAnzahl() > 1 || lastArgFinished;
  627. Text* name;
  628. if (params.getEintragAnzahl() > 0)
  629. {
  630. name = params.get(0);
  631. params.remove(0);
  632. }
  633. else
  634. {
  635. name = new Text("");
  636. }
  637. RCArray<Text> suggestions;
  638. for (ConsoleCommand* command : commands)
  639. {
  640. if ((!paramsStarted && command->getName().hatAt(0, *name))
  641. || (paramsStarted && command->getName().istGleich(*name)))
  642. {
  643. if (paramsStarted)
  644. {
  645. command->addAutocompletePossibilities(
  646. params, !lastArgFinished, suggestions);
  647. break;
  648. }
  649. else
  650. {
  651. suggestions.add(new Text(command->getName()));
  652. }
  653. }
  654. }
  655. if (this->suggestions)
  656. {
  657. this->suggestions->clear();
  658. }
  659. else
  660. {
  661. this->suggestions = new ConsoleListView();
  662. this->suggestions->setConsoleHandlerZ(zConsoleHandlerRef());
  663. }
  664. for (Text* suggestion : suggestions)
  665. {
  666. this->suggestions->addItem(*suggestion);
  667. }
  668. if (this->suggestions->getItems().getEintragAnzahl() > 0)
  669. {
  670. bool commonChar = true;
  671. for (int i
  672. = lastArgFinished
  673. ? 0
  674. : (params.getEintragAnzahl() > 0
  675. ? params.z(params.getEintragAnzahl() - 1)->getLength()
  676. : name->getLength());
  677. true;
  678. i++)
  679. {
  680. if (i >= this->suggestions->getItems().z(0)->getLength())
  681. {
  682. commonChar = false;
  683. break;
  684. }
  685. for (int j = 1;
  686. j < this->suggestions->getItems().getEintragAnzahl();
  687. j++)
  688. {
  689. if (i >= this->suggestions->getItems().z(j)->getLength())
  690. {
  691. commonChar = false;
  692. break;
  693. }
  694. if (this->suggestions->getItems().z(j)->getText()[i]
  695. != this->suggestions->getItems().z(0)->getText()[i])
  696. {
  697. commonChar = false;
  698. break;
  699. }
  700. }
  701. if (!commonChar)
  702. {
  703. break;
  704. }
  705. input.insert(
  706. cursorPos, this->suggestions->getItems().z(0)->getText()[i]);
  707. cursorPos++;
  708. }
  709. }
  710. name->release();
  711. if (this->suggestions->getItems().getEintragAnzahl() <= 1)
  712. {
  713. this->suggestions->release();
  714. this->suggestions = 0;
  715. }
  716. triggerUpdate();
  717. }
  718. bool Framework::InputLine::executeCommand(Text command)
  719. {
  720. RCArray<Text> params;
  721. bool lastArgFinished = 0;
  722. Text* cmd = input.getTeilText(0, cursorPos);
  723. if (!parseCommand(*cmd, params, lastArgFinished))
  724. {
  725. cmd->release();
  726. return false;
  727. }
  728. cmd->release();
  729. Text* name;
  730. if (params.getEintragAnzahl() > 0)
  731. {
  732. name = params.get(0);
  733. params.remove(0);
  734. }
  735. else
  736. {
  737. name = new Text("");
  738. }
  739. RCArray<Text> suggestions;
  740. for (ConsoleCommand* command : commands)
  741. {
  742. if (command->getName().istGleich(*name))
  743. {
  744. name->release();
  745. return command->execute(params);
  746. }
  747. }
  748. name->release();
  749. return false;
  750. }
  751. bool Framework::InputLine::parseCommand(
  752. Text command, RCArray<Text>& split, bool& lastFinished)
  753. {
  754. const char* cmd = command.getText();
  755. Text str;
  756. bool insideString = false;
  757. bool insideString2 = false;
  758. bool lastEscape = false;
  759. lastFinished = false;
  760. while (*cmd)
  761. {
  762. if (*cmd == ' ' && !insideString && !insideString2 && !lastEscape)
  763. {
  764. if (str.getLength() > 0)
  765. {
  766. split.add(new Text(str));
  767. str = "";
  768. lastFinished = true;
  769. }
  770. }
  771. else if (*cmd == '"' && !insideString2 && !lastEscape)
  772. {
  773. insideString = !insideString;
  774. lastFinished = !insideString;
  775. }
  776. else if (*cmd == '\'' && !insideString && !lastEscape)
  777. {
  778. insideString2 = !insideString2;
  779. lastFinished = !insideString2;
  780. }
  781. else if (*cmd == '\\' && !lastEscape)
  782. {
  783. lastEscape = true;
  784. lastFinished = false;
  785. }
  786. else
  787. {
  788. lastEscape = false;
  789. str.append(*cmd);
  790. lastFinished = false;
  791. }
  792. cmd++;
  793. }
  794. if (str.getLength() > 0)
  795. {
  796. split.add(new Text(str));
  797. }
  798. return !insideString && !insideString2 && !lastEscape;
  799. }
  800. Framework::ConsoleListView::ConsoleListView()
  801. : StickyConsoleContent(),
  802. maxColumns(-1),
  803. maxVisibleLines(5),
  804. lineOffset(0)
  805. {}
  806. int Framework::ConsoleListView::getUsedColumns() const
  807. {
  808. if (!zConsoleHandlerRef()) return 0;
  809. int maxWidth = zConsoleHandlerRef()->getWidth();
  810. int columnSize = 0;
  811. for (Text* item : items)
  812. {
  813. if (item->getLength() > columnSize)
  814. {
  815. columnSize = item->getLength();
  816. }
  817. }
  818. columnSize += 4;
  819. if (maxWidth < columnSize)
  820. {
  821. return 0;
  822. }
  823. int columns = maxWidth / columnSize;
  824. if (maxColumns > 0 && columns > maxColumns)
  825. {
  826. columns = maxColumns;
  827. }
  828. if (columns > items.getEintragAnzahl())
  829. {
  830. columns = items.getEintragAnzahl();
  831. }
  832. return columns;
  833. }
  834. int Framework::ConsoleListView::getNeededLines() const
  835. {
  836. int columns = getUsedColumns();
  837. if (!columns)
  838. {
  839. return 0;
  840. }
  841. if (maxColumns > 0 && columns > maxColumns)
  842. {
  843. columns = maxColumns;
  844. }
  845. int lines = items.getEintragAnzahl() / columns;
  846. if (items.getEintragAnzahl() % columns != 0)
  847. {
  848. lines++;
  849. }
  850. return lines;
  851. }
  852. void Framework::ConsoleListView::setMaxVisibleLines(int maxVisibleLines)
  853. {
  854. this->maxVisibleLines = maxVisibleLines;
  855. }
  856. void Framework::ConsoleListView::setLineOffset(int lineOffset)
  857. {
  858. this->lineOffset = lineOffset;
  859. int maxLines = getNeededLines();
  860. if (this->lineOffset > maxLines - maxVisibleLines)
  861. {
  862. this->lineOffset = maxLines - maxVisibleLines;
  863. }
  864. if (this->lineOffset < 0)
  865. {
  866. this->lineOffset = 0;
  867. }
  868. }
  869. void Framework::ConsoleListView::setMaxColumns(int maxColumns)
  870. {
  871. this->maxColumns = maxColumns;
  872. }
  873. void Framework::ConsoleListView::addItem(Text item)
  874. {
  875. item.ersetzen("\n", " ");
  876. int index = 0;
  877. for (Text* curreent : items)
  878. {
  879. int i = 0;
  880. while (i < curreent->getLength() && i < item.getLength())
  881. {
  882. if (curreent->getText()[i] != item.getText()[i])
  883. {
  884. break;
  885. }
  886. i++;
  887. }
  888. if (i == curreent->getLength() && i == item.getLength())
  889. {
  890. return; // item already exists
  891. }
  892. if (i < curreent->getLength() && i == item.getLength())
  893. {
  894. items.add(new Text(item), index);
  895. return;
  896. }
  897. if (i < curreent->getLength() && i < item.getLength()
  898. && curreent->getText()[i] > item.getText()[i])
  899. {
  900. items.add(new Text(item), index);
  901. return;
  902. }
  903. index++;
  904. }
  905. items.add(new Text(item));
  906. }
  907. void Framework::ConsoleListView::clear()
  908. {
  909. items.leeren();
  910. }
  911. const RCArray<Text>& Framework::ConsoleListView::getItems() const
  912. {
  913. return items;
  914. }
  915. int Framework::ConsoleListView::print() const
  916. {
  917. int lines = lineOffset;
  918. int maxLines = getNeededLines();
  919. if (lines > maxLines - maxVisibleLines)
  920. {
  921. lines = maxLines - maxVisibleLines;
  922. }
  923. if (lines < 0)
  924. {
  925. lines = 0;
  926. }
  927. int columns = getUsedColumns();
  928. if (!columns)
  929. {
  930. return 0;
  931. }
  932. int columnSize = zConsoleHandlerRef()->getWidth() / columns;
  933. int printetLines = 0;
  934. int currentColumn = 0;
  935. std::cout << "\33[0K"; // clear current line
  936. for (int i = columns * lines; i < items.getEintragAnzahl(); i++)
  937. {
  938. if (i >= columns * (lines + maxVisibleLines))
  939. {
  940. break;
  941. }
  942. if (currentColumn >= columns)
  943. {
  944. std::cout << "\n\r\33[0K";
  945. printetLines++;
  946. currentColumn++;
  947. }
  948. if (!printetLines)
  949. {
  950. printetLines++;
  951. }
  952. std::cout << items.z(i)->getText();
  953. for (int j = items.z(i)->getLength(); j < columnSize; j++)
  954. {
  955. std::cout << " ";
  956. }
  957. currentColumn++;
  958. }
  959. return printetLines;
  960. }
  961. Framework::ConsoleHandler::ConsoleHandler()
  962. : ReferenceCounter(),
  963. lines(0),
  964. lineCounts(0),
  965. contentCount(0)
  966. {
  967. #ifdef WIN32
  968. hConsole = GetStdHandle(STD_INPUT_HANDLE);
  969. SetConsoleMode(hConsole, ENABLE_PROCESSED_INPUT); // set raw mode
  970. hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  971. #else
  972. struct termios t;
  973. tcgetattr(STDIN_FILENO, &t);
  974. t.c_lflag &= ~(ICANON | ECHO); // set raw mode
  975. tcsetattr(STDIN_FILENO, TCSANOW, &t);
  976. #endif
  977. }
  978. Framework::ConsoleHandler::~ConsoleHandler()
  979. {
  980. for (int i = 0; i < contentCount; i++)
  981. {
  982. lines[i]->release();
  983. }
  984. delete[] lines;
  985. delete[] lineCounts;
  986. }
  987. void Framework::ConsoleHandler::addContent(
  988. StickyConsoleContent* content, ConsoleContentPosition pos)
  989. {
  990. cs.lock();
  991. if (content->isInput())
  992. {
  993. for (int i = 0; i < contentCount; i++)
  994. {
  995. if (lines[i]->isInput())
  996. {
  997. content->release();
  998. cs.unlock();
  999. throw "There can only be one input line";
  1000. }
  1001. }
  1002. }
  1003. if (content->zConsoleHandler)
  1004. {
  1005. cs.unlock();
  1006. throw "A console content can only be added to one console handler "
  1007. "at "
  1008. "the same time.";
  1009. }
  1010. content->zConsoleHandler = this;
  1011. contentCount++;
  1012. int* lineCounts = new int[contentCount];
  1013. StickyConsoleContent** lines = new StickyConsoleContent*[contentCount];
  1014. if (pos == ConsoleContentPosition::Top)
  1015. {
  1016. lines[0] = content;
  1017. lineCounts[0] = 0;
  1018. for (int i = 1; i < contentCount; i++)
  1019. {
  1020. lines[i] = this->lines[i - 1];
  1021. lineCounts[i] = this->lineCounts[i - 1];
  1022. }
  1023. }
  1024. else
  1025. {
  1026. lines[contentCount - 1] = content;
  1027. lineCounts[contentCount - 1] = content->print();
  1028. for (int i = 0; i < contentCount - 1; i++)
  1029. {
  1030. lines[i] = this->lines[i];
  1031. lineCounts[i] = this->lineCounts[i];
  1032. }
  1033. }
  1034. delete[] this->lines;
  1035. delete[] this->lineCounts;
  1036. this->lines = lines;
  1037. this->lineCounts = lineCounts;
  1038. InputLine* input = dynamic_cast<InputLine*>(content);
  1039. if (input)
  1040. {
  1041. input->start();
  1042. }
  1043. cs.unlock();
  1044. }
  1045. void Framework::ConsoleHandler::removeContent(StickyConsoleContent* zContent)
  1046. {
  1047. cs.lock();
  1048. int index = -1;
  1049. for (int i = 0; i < contentCount; i++)
  1050. {
  1051. if (lines[i] == zContent)
  1052. {
  1053. index = i;
  1054. break;
  1055. }
  1056. }
  1057. if (index == -1)
  1058. {
  1059. cs.unlock();
  1060. return;
  1061. }
  1062. zContent->zConsoleHandler = 0;
  1063. contentCount--;
  1064. int* lineCounts = new int[contentCount];
  1065. StickyConsoleContent** lines = new StickyConsoleContent*[contentCount];
  1066. for (int i = 0; i < index; i++)
  1067. {
  1068. lines[i] = this->lines[i];
  1069. lineCounts[i] = this->lineCounts[i];
  1070. }
  1071. for (int i = index; i < contentCount; i++)
  1072. {
  1073. lines[i] = this->lines[i + 1];
  1074. lineCounts[i] = this->lineCounts[i + 1];
  1075. }
  1076. delete[] this->lines;
  1077. delete[] this->lineCounts;
  1078. this->lines = lines;
  1079. this->lineCounts = lineCounts;
  1080. if (zContent->isInput())
  1081. {
  1082. InputLine* input = dynamic_cast<InputLine*>(zContent);
  1083. if (input)
  1084. {
  1085. input->start();
  1086. }
  1087. }
  1088. zContent->release();
  1089. cs.unlock();
  1090. }
  1091. int Framework::ConsoleHandler::getWidth() const
  1092. {
  1093. #ifdef WIN32
  1094. CONSOLE_SCREEN_BUFFER_INFO csbi;
  1095. GetConsoleScreenBufferInfo(hConsole, &csbi);
  1096. return csbi.dwSize.X;
  1097. #else
  1098. struct winsize ws;
  1099. if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1)
  1100. {
  1101. return 0;
  1102. }
  1103. return ws.ws_col;
  1104. #endif
  1105. }
  1106. int Framework::ConsoleHandler::getHeight() const
  1107. {
  1108. #ifdef WIN32
  1109. CONSOLE_SCREEN_BUFFER_INFO csbi;
  1110. GetConsoleScreenBufferInfo(hConsole, &csbi);
  1111. return csbi.dwSize.Y;
  1112. #else
  1113. struct winsize ws;
  1114. if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1)
  1115. {
  1116. return 0;
  1117. }
  1118. return ws.ws_row;
  1119. #endif
  1120. }
  1121. void Framework::ConsoleHandler::clear()
  1122. {
  1123. cs.lock();
  1124. for (int i = 0; i < contentCount; i++)
  1125. {
  1126. lines[i]->release();
  1127. }
  1128. delete[] lines;
  1129. delete[] lineCounts;
  1130. lines = 0;
  1131. lineCounts = 0;
  1132. contentCount = 0;
  1133. cs.unlock();
  1134. }
  1135. void Framework::ConsoleHandler::print()
  1136. {
  1137. cs.lock();
  1138. int totalLines = 0;
  1139. bool adittionalLine = 0;
  1140. for (int i = 0; i < contentCount; i++)
  1141. {
  1142. if (lines[i]->isInput())
  1143. {
  1144. lines[i]->setCursorToBeginning();
  1145. break;
  1146. }
  1147. totalLines += lineCounts[i];
  1148. }
  1149. if (totalLines > 0)
  1150. {
  1151. std::cout << "\33[" << totalLines << "A";
  1152. }
  1153. totalLines = 0;
  1154. for (int i = 0; i < contentCount; i++)
  1155. {
  1156. lineCounts[i] = lines[i]->print();
  1157. if (lineCounts[i] > 0)
  1158. {
  1159. adittionalLine = 0;
  1160. if (i < contentCount - 1)
  1161. {
  1162. std::cout << "\n";
  1163. adittionalLine = 1;
  1164. }
  1165. }
  1166. totalLines += lineCounts[i];
  1167. }
  1168. if (adittionalLine)
  1169. {
  1170. totalLines++;
  1171. }
  1172. std::cout << "\33[" << (totalLines - 1) << "A\r";
  1173. for (int i = 0; i < contentCount; i++)
  1174. {
  1175. if (lines[i]->isInput())
  1176. {
  1177. lines[i]->restoreCursorPos();
  1178. break;
  1179. }
  1180. if (i < contentCount - 1 && lineCounts[i])
  1181. {
  1182. std::cout << "\33[" << lineCounts[i] << "B";
  1183. }
  1184. }
  1185. std::cout << std::flush;
  1186. cs.unlock();
  1187. }
  1188. void Framework::ConsoleHandler::print(Text str)
  1189. {
  1190. std::cout << "\n";
  1191. cs.lock();
  1192. int totalLines = 0;
  1193. bool adittionalLine = 0;
  1194. for (int i = 0; i < contentCount; i++)
  1195. {
  1196. if (lines[i]->isInput())
  1197. {
  1198. lines[i]->setCursorToBeginning();
  1199. break;
  1200. }
  1201. totalLines += lineCounts[i];
  1202. }
  1203. std::cout << "\33[" << totalLines + 1 << "A";
  1204. std::cout << "\33[0K" // clear current line
  1205. << str.getText();
  1206. if (str.getText()[str.getLength() - 1] != '\n')
  1207. {
  1208. std::cout << "\n";
  1209. }
  1210. totalLines = 0;
  1211. for (int i = 0; i < contentCount; i++)
  1212. {
  1213. lineCounts[i] = lines[i]->print();
  1214. if (lineCounts[i] > 0)
  1215. {
  1216. adittionalLine = 0;
  1217. if (i < contentCount - 1)
  1218. {
  1219. std::cout << "\n";
  1220. adittionalLine = 1;
  1221. }
  1222. }
  1223. totalLines += lineCounts[i];
  1224. }
  1225. if (adittionalLine)
  1226. {
  1227. totalLines++;
  1228. }
  1229. std::cout << "\33[" << (totalLines - 1) << "A\r";
  1230. for (int i = 0; i < contentCount; i++)
  1231. {
  1232. if (lines[i]->isInput())
  1233. {
  1234. lines[i]->restoreCursorPos();
  1235. break;
  1236. }
  1237. if (i < contentCount - 1 && lineCounts[i])
  1238. {
  1239. std::cout << "\33[" << lineCounts[i] << "B";
  1240. }
  1241. }
  1242. std::cout << std::flush;
  1243. cs.unlock();
  1244. }