From 081d541e9e39cbdc5342711acc9b3f2503563317 Mon Sep 17 00:00:00 2001 From: Marco Hladik Date: Tue, 30 Jun 2020 17:14:06 +0200 Subject: [PATCH] Menu: IRC based Chat is now completely implemented using QuakeC. It already works quite well but will need a lot more testing! --- src/Makefile | 2 + src/client/entry.c | 4 + src/menu-fn/colors.cpp | 9 +- src/menu-fn/entry.cpp | 3 + src/menu-fn/m_chatrooms.cpp | 377 ++++++++++++++++++++++-------- src/menu-fn/m_chatroomslist.cpp | 67 +++++- src/menu-fn/m_creategame.cpp | 1 - src/menu-fn/m_creategame_inet.cpp | 1 + src/menu-fn/progs.src | 1 + src/menu-fn/w_listbox.cpp | 13 +- src/menu-fn/w_textbuffer.cpp | 85 +++++++ 11 files changed, 465 insertions(+), 98 deletions(-) create mode 100644 src/menu-fn/w_textbuffer.cpp diff --git a/src/Makefile b/src/Makefile index 976355d3..da62cfc5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -18,6 +18,8 @@ games: cd server/rewolf && $(MAKE) cd client/gearbox && $(MAKE) cd server/gearbox && $(MAKE) +# cd client/wastes && $(MAKE) + #cd server/wastes && $(MAKE) mods: cd client/cstrike && $(MAKE) diff --git a/src/client/entry.c b/src/client/entry.c index e97cfdad..93ac6fe0 100644 --- a/src/client/entry.c +++ b/src/client/entry.c @@ -55,6 +55,7 @@ CSQC_Init(float apilevel, string enginename, float engineversion) registercommand("+showscores"); registercommand("-showscores"); registercommand("buildcubemaps"); + registercommand("_fnchat_msg"); precache_model("sprites/640_pain.spr"); precache_model("sprites/crosshairs.spr"); @@ -656,6 +657,9 @@ CSQC_ConsoleCommand(string sCMD) case "slot10": HUD_SlotSelect(9); break; + case "_fnchat_msg": + CSQC_Parse_Print(argv(1), PRINT_CHAT); + break; default: return Game_ConsoleCommand(); } diff --git a/src/menu-fn/colors.cpp b/src/menu-fn/colors.cpp index 81cf7ada..2d0a6362 100644 --- a/src/menu-fn/colors.cpp +++ b/src/menu-fn/colors.cpp @@ -24,7 +24,8 @@ vector col_refresh_title; vector col_refresh_text; vector col_refresh_bg; -string Colors_RGB8_to_HEX(vector color) +string +Colors_RGB8_to_HEX(vector color) { string out = "^x"; @@ -59,6 +60,12 @@ string Colors_RGB8_to_HEX(vector color) return out; } +string +Colors_RGB255_to_HEX(vector color) +{ + return Colors_RGB8_to_HEX(color / 255); +} + void Colors_Init(void) { int c; diff --git a/src/menu-fn/entry.cpp b/src/menu-fn/entry.cpp index e8670f4c..341efc3c 100644 --- a/src/menu-fn/entry.cpp +++ b/src/menu-fn/entry.cpp @@ -163,6 +163,9 @@ m_draw(vector screensize) Menu_AutoScale(); } + /* to prevent TCP timeouts */ + menu_chatrooms_keepalive(); + if (!g_active) { return; } diff --git a/src/menu-fn/m_chatrooms.cpp b/src/menu-fn/m_chatrooms.cpp index 036db4e3..e8741904 100644 --- a/src/menu-fn/m_chatrooms.cpp +++ b/src/menu-fn/m_chatrooms.cpp @@ -22,35 +22,255 @@ CMainButton cr_btnListRooms; CMainButton cr_btnSearch; CMainButton cr_btnServers; CMainButton cr_btnDone; +CScrollbar cr_sbHistory; +CTextBuffer cr_lbHistory; CListBox cr_lbUsers; CDialog cr_dgConnect; +void crl_addroom(string); +void crl_clearrooms(void); +void crl_roomsdone(void); + +/* Ladies and gentlemen, here's the very first IRC client written purely + * in QuakeC. I have no regrets. */ + typedef struct { - string cur_tab; - string show_tab; - int channel_grabbed; - float user_refresh; - float timer; -} ircsession_t; -ircsession_t g_irc; + string m_strChannel; + string m_strTopic; + int m_iStatus; + int m_iReady; + string m_strNick; +} ircroom_t; +ircroom_t g_ircroom; +tcpinfo_t tcp_irc; -var string g_crIRCchannel; -var int g_iIRCActive; - -/* we've got a Internet servers button on here, so we require these */ +/* we've got a Internet servers button in this menu therefore */ void(void) cr_btnservers_start; +void +cr_print(string buffer) +{ + print(sprintf("%s\n", buffer)); + cr_lbHistory.Insert(buffer); +} + +/* tempstrings and fwrite == not a good idea. we need to manually copy all + * the chars. sigh. + * for static strings however, we can just use TCP_Send as is. */ +void +irc_send(string msg) +{ + string out; + + out = (string)memalloc(strlen(msg)); + + for (float i = 0; i < strlen(msg); i++) + out[i] = str2chr(msg, i); + + TCP_Send(&tcp_irc, out); +} + +void +irc_adduser(string user) +{ + int present = FALSE; + user = strreplace("@", "", user); + user = strreplace("+", "", user); + + for (int i = 0; i < cr_lbUsers.GetCount(); i++) { + if (cr_lbUsers.GetItem(i) == user) { + present = TRUE; + } + } + + if (present == FALSE) { + cr_lbUsers.AddEntry(user); + } +} + +void +irc_remuser(string user) +{ + user = strreplace("@", "", user); + user = strreplace("+", "", user); + + for (int i = 0; i < cr_lbUsers.GetCount(); i++) { + if (cr_lbUsers.GetItem(i) == user) { + int last = cr_lbUsers.GetCount()-1; + cr_lbUsers.SetItem(i, cr_lbUsers.GetItem(last)); + cr_lbUsers.DelEntry(last); + } + } +} + +void +irc_clear(void) +{ + cr_lbUsers.Clear(); + cr_lbHistory.Clear(); + g_ircroom.m_iReady = 0; +} + +void +irc_autoname(void) +{ + g_ircroom.m_strNick = sprintf("Guest%d", rint(random(0,9999))); + irc_send(sprintf("NICK %s\n", g_ircroom.m_strNick)); +} + +/* irc processing functions */ +void +irc_receive(string buffer) +{ + int l; + string src; + + if (!buffer) + return; + + l = tokenize(buffer); + + /* IRC tokenization goes like this: + [SOURCE] [CODE] [TARGET] [DATA] + example would be something like: + + A message 'test' from eukara to channel #debug: + :eukara!eukara@irc.won.net PRIVMSG #debug :test + + A message 'hello' from eukara to user Frank: + :eukara!eukara@irc.won.net PRIVMSG Frank :hello + */ + + print(buffer); + print("\n"); + + if (substring(buffer, 0, 4) == "PING") { + irc_send(sprintf("PONG :%s\n", substring(buffer, 6, -1))); + return; + } + + /* define the source */ + src = substring(argv(1), 0, strstrofs(argv(1), "!")); + + if (src == "") + src = "Server"; + + /* process the code */ + switch (argv(2)) { + case "PRIVMSG": /* a message */ + cr_print( + sprintf("%s<%s>%s %s", + Colors_RGB255_to_HEX([192,192,192]), + src, + Colors_RGB255_to_HEX([255,255,255]), + substring(buffer, strstrofs(buffer, ":", 1) + 1, -1) + ) + ); + localcmd(sprintf("_fnchat_msg \"[%s] %s: %s\"\n", g_ircroom.m_strChannel, src, argv(2))); + break; + case "321": + crl_clearrooms(); + break; + case "322": + crl_addroom(argv(4)); + break; + case "323": + crl_roomsdone(); + break; + case "332": /* topic upon joining channel */ + case "TOPIC": /* topic change */ + tokenizebyseparator(buffer, ":"); + tokenizebyseparator(argv(2), " - "); + g_ircroom.m_strTopic = argv(0); + break; + case "NOTICE": /* server notice */ + break; + case "JOIN": /* a player joined */ + if (src == g_ircroom.m_strNick) { + cr_print(sprintf("You are now speaking in %s", g_ircroom.m_strChannel)); + break; + } + + irc_adduser(src); + cr_print(sprintf("%s joined the chat.", src)); + break; + case "QUIT": /* a player quit */ + case "PART": /* a player left the channel */ + if (src == g_ircroom.m_strNick) + break; + + irc_remuser(src); + cr_print(sprintf("%s left the chat.", src)); + break; + case "353": /* channel user list */ + tokenizebyseparator(buffer, ":"); + int c = tokenize(argv(2)); + for (int i = 0; i < c; i++) + irc_adduser(argv(i)); + break; + case "366": /* end of user list */ + g_ircroom.m_iReady = 1; + break; + case "431": /* no nickname given */ + case "432": /* erroneus nickname */ + case "433": /* nick already in use */ + irc_autoname(); + break; + case "475": /* password protected/wrong */ + cr_print(sprintf("%s is a password protected channel. Access denied.", g_ircroom.m_strChannel)); + g_ircroom.m_iReady = 1; + break; + case "376": /* logging done */ + irc_send(sprintf("JOIN %s\n", g_ircroom.m_strChannel)); + break; + case "NICK": /* nick changed */ + tokenizebyseparator(buffer, ":"); + for (int i = 0; i < cr_lbUsers.GetCount(); i++) { + if (cr_lbUsers.GetItem(i) == src) { + cr_lbUsers.SetItem(i, argv(2)); + break; + } + } + cr_print(sprintf("%s is now known as %s", src, argv(2))); + } +} + +/* initial connect */ +void cr_makeconnection(void) +{ + int i; + i = TCP_Connect(&tcp_irc, "tcp://irc.frag-net.com:6667"); + + g_ircroom.m_strChannel = sprintf("#%s", cvar_string("game")); + TCP_Send(&tcp_irc, "USER guest fn irc.won.net :Player\n"); + + /* attempt to force our nickname to be the same as in-game. + * we'll probably have to keep track of event 433 though. */ + irc_send(sprintf("NICK %s\n", cvar_string("name"))); +} + +/* when ENTER is pressed on the message box */ +void cr_input_enter(string text) +{ + if (!text) + return; + + cr_print( + sprintf("%s<%s>%s %s", + Colors_RGB255_to_HEX([128,100,0]), + g_ircroom.m_strNick, + Colors_RGB255_to_HEX([180,128,55]), + text + ) + ); + irc_send(sprintf("PRIVMSG %s :%s\n", g_ircroom.m_strChannel, text)); + cr_tbInput.SetText(""); +} + void cr_closeconnection(void) { - if (!g_iIRCActive) { - return; - } - /* how is this meant to work? neither do! */ - con_printf(g_irc.show_tab, "/clear\n"); - con_getset(g_irc.show_tab, "clear"); - localcmd("irc /disconnect irc.frag-net.com:6667\n"); - g_iIRCActive = FALSE; + TCP_Disconnect(&tcp_irc); } void cr_btndone_start(void) @@ -73,6 +293,9 @@ void cr_btnlistrooms_start(void) static void cr_btnlistrooms_end(void) { g_menupage = PAGE_CHATROOMS_LIST; } + + irc_send("LIST\n"); + localsound("../media/launch_dnmenu1.wav"); header.SetStartEndPos(30,70,45,45); header.SetStartEndSize(156,26,460,80); @@ -85,27 +308,9 @@ void cr_btnlistrooms_start(void) /* 'Done' button */ void cr_btnservers_prepare(void) { - //cr_closeconnection(); cr_btnservers_start(); } -/* initial connect */ -void cr_makeconnection(void) -{ - localcmd("plug_load irc\n"); - localcmd(sprintf("seta irc_nick %.9s\n", cvar_string("name"))); - localcmd(sprintf("seta irc_altnick %.5s%.4d\n", cvar_string("name"), rint(random(1000,9999)))); - g_crIRCchannel = "#lobby"; - localcmd(sprintf("irc /open irc.frag-net.com:6667 %s\n", g_crIRCchannel)); -} - -/* when ENTER is pressed on the message box */ -void cr_input_enter(string text) -{ - localcmd(sprintf("irc /msg %s %s\n", g_crIRCchannel, text)); - cr_tbInput.SetText(""); -} - void menu_chatrooms_init(void) { fn_chatrooms = spawn(CWidget); @@ -150,6 +355,18 @@ void menu_chatrooms_init(void) cr_frUsers.SetSize(128,337); Widget_Add(fn_chatrooms, cr_frUsers); + cr_lbHistory = spawn(CTextBuffer); + cr_lbHistory.SetPos(33,104); + cr_lbHistory.SetSize(450,331); + //cr_lbHistory.SetChanged(inet_lb_clicked); + Widget_Add(fn_chatrooms, cr_lbHistory); + + /*cr_sbHistory = spawn(CScrollbar); + cr_sbHistory.SetPos(486,101); + cr_sbHistory.SetHeight(337); + cr_sbHistory.SetCallback(cr_sbhistory_changed); + Widget_Add(fn_chatrooms, cr_sbHistory);*/ + cr_lbUsers = spawn(CListBox); cr_lbUsers.SetPos(505,104); cr_lbUsers.SetSize(122,331); @@ -160,75 +377,50 @@ void menu_chatrooms_init(void) string menu_chatrooms_gettopic(void) { - string t = cvar_string("irc_currenttopic"); - if (t == "") { - return "No topic"; - } else { - tokenizebyseparator(t, " - "); /* strip long descriptions */ - return argv(0); - } + if (g_ircroom.m_strTopic) + return g_ircroom.m_strTopic; + else + return "No topic."; } void menu_chatrooms_draw(void) { - int tab_id; - string tmp; + static float timeout; Widget_Draw(fn_chatrooms); drawpic([g_menuofs[0]+550,g_menuofs[1]+10], "gfx/shell/fragnet",[80,80], [1,1,1], 1.0f, 0); - /* we need to figure out which console buffer belongs to our chat. */ - g_irc.show_tab = g_irc.cur_tab; - for (tmp = "", tab_id = 0; tmp; tmp = con_getset(tmp, "next")) { - if (substring(tmp, 0, 3) == "IRC") { - con_getset(tmp, "hidden", "1"); - string title = con_getset(tmp, "title"); - } - } - if not (g_irc.channel_grabbed) - for (tmp = ""; tmp; tmp = con_getset(tmp, "next")) { - if (substring(tmp, 0, 3) == "IRC" && substring(tmp, -strlen(g_crIRCchannel), -1) == g_crIRCchannel) { - g_irc.show_tab = tmp; - g_irc.channel_grabbed = TRUE; - break; - } - } - if (!g_irc.show_tab) { - for (tmp = ""; tmp; tmp = con_getset(tmp, "next")) { - if (substring(tmp, 0, 3) == "IRC" && substring(tmp, -1, -1) == ":") { - g_irc.show_tab = tmp; - break; - } - } - } - g_irc.cur_tab = g_irc.show_tab; - - /* establish the connection if nothing is found */ - if (!g_iIRCActive && !g_irc.show_tab) { + if (!g_ircroom.m_iStatus) { cr_makeconnection(); - g_iIRCActive = TRUE; - g_irc.timer = 10.0f; + timeout = 10.0f; + g_ircroom.m_iStatus = TRUE; } - /* draw the irc-log buffer when ready */ - if (g_irc.show_tab) { + if (TCP_GetState(&tcp_irc) == STATE_CONNECTED) { drawsetcliparea(g_menuofs[0] + 33, g_menuofs[1] + 104, 450,331); - con_draw(g_irc.show_tab, [g_menuofs[0] + 33, g_menuofs[1] + 104], [450,331], 12); drawresetcliparea(); - } else { + } else if (TCP_GetState(&tcp_irc) == STATE_CONNECTING) { /* connecting... dialog */ cr_dgConnect.Draw(); WField_Static(162, 180, m_reslbl[IDS_WON_LOGIN], 320, 260, col_prompt_text, 1.0f, 2, font_label_p); - WField_Static(162, 280, sprintf(m_reslbl[IDS_CHAT_JOIN], g_crIRCchannel), 320, 260, + WField_Static(162, 280, sprintf(m_reslbl[IDS_CHAT_JOIN], g_ircroom.m_strChannel), 320, 260, col_prompt_title, 1.0f, 2, font_label_p); - g_irc.timer -= frametime; - - if (g_irc.timer < 0.0) { + timeout -= frametime; + + if (timeout < 0.0) { cr_btndone_start(); } } + if (!g_ircroom.m_iReady) { + cr_dgConnect.Draw(); + WField_Static(162, 180, "Connected to Frag-Net", 320, 260, + col_prompt_text, 1.0f, 2, font_label_p); + WField_Static(162, 280, sprintf("Loading room info for %s", g_ircroom.m_strChannel), 320, 260, + col_prompt_title, 1.0f, 2, font_label_p); + } + /* draw the labels */ WLabel_Static(30, 38, m_reslbl[IDS_MULTI_CHATROOMCAPTION], 22, 22, [0.4,0.4,0.4], 1.0f, 0, font_label_p); @@ -236,17 +428,14 @@ void menu_chatrooms_draw(void) 1.0f, 0, font_label_p); WLabel_Static(30, 446, m_reslbl[IDS_CHAT_PROMPT], 12, 12, [1,1,1], 1.0f, 0, font_arial); +} - /* update the user list periodically */ - if (g_irc.user_refresh > time) { - return; - } - int c = tokenize(cvar_string("irc_currentusers")); - g_irc.user_refresh = time + 2.0f; - cr_lbUsers.Clear(); - for (int i = 0; i < c; i++) { - cr_lbUsers.AddEntry(argv(i)); - } +/* this function is run, even when the menu is visibly closed. we need to keep + * the TCP session with the IRC server alive afterall */ +void menu_chatrooms_keepalive(void) +{ + TCP_Frame(&tcp_irc); + irc_receive(TCP_Receive(&tcp_irc)); } void menu_chatrooms_input(float evtype, float scanx, float chary, float devid) diff --git a/src/menu-fn/m_chatroomslist.cpp b/src/menu-fn/m_chatroomslist.cpp index 5d8fbe0c..b0970b3b 100644 --- a/src/menu-fn/m_chatroomslist.cpp +++ b/src/menu-fn/m_chatroomslist.cpp @@ -19,11 +19,47 @@ CMainButton crl_btnJoin; CMainButton crl_btnCreateRoom; CMainButton crl_btnCancel; -var string g_crIRCchannel; +CFrame crl_frRooms; +CListBox crl_lbRooms; +CScrollbar crl_sbRooms; + +var int crl_iLoading; + +void +crl_addroom(string room) +{ + /* skip this thing. */ + if (room == "&SERVER") + return; + + crl_lbRooms.AddEntry(room); + crl_sbRooms.SetMax(crl_lbRooms.GetCount()); +} + +void +crl_clearrooms(void) +{ + crl_lbRooms.Clear(); + crl_sbRooms.SetMax(0); + crl_iLoading = TRUE; +} + +void +crl_roomsdone(void) +{ + crl_iLoading = FALSE; +} void crl_btnjoin_start(void) { static void cr_btncancel_end(void) { + /* part the current channel, clear textbuffer and user list */ + irc_send(sprintf("PART %s\n", g_ircroom.m_strChannel)); + irc_clear(); + + /* set the new current channel, attempt to join */ + g_ircroom.m_strChannel = crl_lbRooms.GetSelectedItem(); + irc_send(sprintf("JOIN %s\n", g_ircroom.m_strChannel)); g_menupage = PAGE_CHATROOMS; } localsound("../media/launch_dnmenu1.wav"); @@ -49,6 +85,11 @@ void crl_btncancel_start(void) header.SetExecute(crl_btncancel_end); } +void crl_rooms_changed(int val) +{ + crl_lbRooms.SetScroll(val); +} + void menu_chatroomslist_init(void) { fn_chatroomslist = spawn(CWidget); @@ -63,6 +104,22 @@ void menu_chatroomslist_init(void) //crl_btnCreateRoom.SetExecute(cr_btncancel_start); crl_btnCreateRoom.SetPos(50,172); Widget_Add(fn_chatroomslist, crl_btnCreateRoom); + + crl_frRooms = spawn(CFrame); + crl_frRooms.SetPos(382,172); + crl_frRooms.SetSize(208,288); + Widget_Add(fn_chatroomslist, crl_frRooms); + + crl_lbRooms = spawn(CListBox); + crl_lbRooms.SetPos(384,175); + crl_lbRooms.SetSize(202-16,282); + Widget_Add(fn_chatroomslist, crl_lbRooms); + + crl_sbRooms = spawn(CScrollbar); + crl_sbRooms.SetPos(571,175); + crl_sbRooms.SetHeight(282); + crl_sbRooms.SetCallback(crl_rooms_changed); + Widget_Add(fn_chatroomslist, crl_sbRooms); crl_btnCancel = spawn(CMainButton); crl_btnCancel.SetImage(BTN_CANCEL); @@ -76,6 +133,14 @@ void menu_chatroomslist_draw(void) Widget_Draw(fn_chatroomslist); drawpic([g_menuofs[0]+45,g_menuofs[1]+45], g_bmp[HEAD_ROOMS],[460,80], [1,1,1], 1.0f, 1); drawpic([g_menuofs[0]+550,g_menuofs[1]+10], "gfx/shell/fragnet",[80,80], [1,1,1], 1.0f, 0); + + if (crl_iLoading) { + cr_dgConnect.Draw(); + WField_Static(162, 180, "Connected to Frag-Net", 320, 260, + col_prompt_text, 1.0f, 2, font_label_p); + WField_Static(162, 280, "Retrieving Room List...", 320, 260, + col_prompt_title, 1.0f, 2, font_label_p); + } } void menu_chatroomslist_input(float evtype, float scanx, float chary, float devid) diff --git a/src/menu-fn/m_creategame.cpp b/src/menu-fn/m_creategame.cpp index 4c6bcc4f..c6a05265 100644 --- a/src/menu-fn/m_creategame.cpp +++ b/src/menu-fn/m_creategame.cpp @@ -42,7 +42,6 @@ void create_btnok_start(void) localcmd(sprintf("map %s\n", startmap)); g_menupage = PAGE_LANGAMES; } - /* Strip .bsp extension before submitting */ startmap = substring(create_lbMaps.GetSelectedItem(), 0, -5); diff --git a/src/menu-fn/m_creategame_inet.cpp b/src/menu-fn/m_creategame_inet.cpp index ec226f06..460dd364 100644 --- a/src/menu-fn/m_creategame_inet.cpp +++ b/src/menu-fn/m_creategame_inet.cpp @@ -29,6 +29,7 @@ void createinet_btnok_start(void) localcmd(sprintf("password %s\n", create_tbPassword.m_text)); localcmd(sprintf("map %s\n", startmap)); g_menupage = PAGE_INTERNETGAMES; + cr_input_enter(sprintf("ACTION is now hosting '%s' on the map %s\n", create_tbHostname.m_text, startmap)); } /* Strip .bsp extension before submitting */ diff --git a/src/menu-fn/progs.src b/src/menu-fn/progs.src index 76840a2e..56ff05d4 100644 --- a/src/menu-fn/progs.src +++ b/src/menu-fn/progs.src @@ -25,6 +25,7 @@ w_frame.cpp w_label.cpp w_combobox.cpp w_listbox.cpp +w_textbuffer.cpp w_servers.cpp w_mainbutton.cpp w_modetab.cpp diff --git a/src/menu-fn/w_listbox.cpp b/src/menu-fn/w_listbox.cpp index 87c563d2..b464dfbe 100644 --- a/src/menu-fn/w_listbox.cpp +++ b/src/menu-fn/w_listbox.cpp @@ -29,7 +29,8 @@ class CListBox:CWidget virtual void(void) Draw; virtual void(float type, float x, float y, float devid) Input; - virtual void(string m) AddEntry; + virtual void(string) AddEntry; + virtual void(int) DelEntry; virtual void(void) Clear; virtual void(int, int) SetSize; virtual void(void(int val) func) SetChanged; @@ -62,8 +63,10 @@ void CListBox::Draw(void) drawfill([g_menuofs[0] + pos[0] - 2, g_menuofs[1] + pos[1] - 2], [m_size[0], 15], [84/255,45/255,0], 1.0f); } + drawsetcliparea(g_menuofs[0] + m_x, g_menuofs[1] + m_y, m_size[0], m_size[1]); WLabel_Static(pos[0], pos[1], m_entries[i], 12, 12, [1,0.5,0.1], 1.0f, 0, font_label); + drawresetcliparea(); pos[1] += 15; } } @@ -101,6 +104,14 @@ void CListBox::AddEntry(string m) } } +void CListBox::DelEntry(int i) +{ + if (m_entries[i] != __NULL__) { + m_entries[i] = __NULL__; + m_count--; + } +} + void CListBox::Clear(void) { for (int i = 0; i < LB_MAX_ENTRIES; i++) { diff --git a/src/menu-fn/w_textbuffer.cpp b/src/menu-fn/w_textbuffer.cpp new file mode 100644 index 00000000..69cca1a8 --- /dev/null +++ b/src/menu-fn/w_textbuffer.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define TB_MAX_ENTRIES 128 + +class CTextBuffer:CWidget +{ + int m_size[2]; + string m_entries[TB_MAX_ENTRIES]; + virtual void(int val) m_execute = 0; + + void(void) CTextBuffer; + virtual void(void) Draw; + virtual void(float type, float x, float y, float devid) Input; + + virtual void(string m) Insert; + virtual void(void) Clear; + virtual void(int, int) SetSize; +}; + +void CTextBuffer::CTextBuffer(void) +{ +} + +void CTextBuffer::Draw(void) +{ + int visible; + int pos[2]; + drawfill([g_menuofs[0] + m_x, g_menuofs[1] + m_y], [m_size[0], m_size[1]], + [0,0,0], 1.0f); + + visible = floor(m_size[1] / 15); + pos[0] = m_x + 2; + pos[1] = m_y + 2 + ((visible - 1) * 15); + + for (int i = (TB_MAX_ENTRIES - 1); i > (TB_MAX_ENTRIES - visible) - 1; i--) { + drawsetcliparea(g_menuofs[0] + m_x, g_menuofs[1] + m_y, m_size[0], m_size[1]); + WLabel_Static(pos[0], pos[1], m_entries[i], 12, 12, [1,1,1], + 1.0f, 0, font_label); + drawresetcliparea(); + pos[1] -= 15; + } +} + +void CTextBuffer::Input(float type, float x, float y, float devid) +{ + +} + +void CTextBuffer::Insert(string m) +{ + for (int i = 0; i < TB_MAX_ENTRIES; i++) { + if (i == TB_MAX_ENTRIES - 1) { + m_entries[i] = m; + } else { + m_entries[i] = m_entries[i+1]; + } + } +} + +void CTextBuffer::Clear(void) +{ + for (int i = 0; i < TB_MAX_ENTRIES; i++) { + m_entries[i] = __NULL__; + } +} + +void CTextBuffer::SetSize(int w, int h) +{ + m_size[0] = w; + m_size[1] = h; +}