# todo: # done gotta handle bsp chars... # resize is still a bit messy - but it works # user can adjust font size # login accepts server:port syntax # more sophisticated parse??? # done \say echoes to self??? # done attrib commands done before server commands - don't need net # recall previous commands # done generate msg when move between rooms - required # done msg when login/logout # afk, emote, tell, ignore, quit!, who # GUI version # logout works - does not close chat window - can repeat login/logout # need upper right close of chat window not to kill the graphics stuff # link graphics # bizarre - activate link and doors do not render!!!??? link "commands" link "session" link "lists" class Chat_win( world, Cmds, margin, xpos, width, height, fheight, fwidth, input_line, usercmd, userargs, session) # # create draws a client window with bottom half for client input # and top half for server input # method chat_create() if \(world.c_win) then { # &window := world.c_win world.set_window( world.c_win ) WAttrib( "canvas=normal" ) Raise( world.c_win ) } else { world.c_win := open("Command Window", "g", "size="|| width || "," || height , "bg=cyan", "font=mono,bold,16" , "resize=on", "cursor=on") | stop("can't open chat window") # world.c_win.connect(self, "dispose", ACTION_EVENT ) put( world.event_source_list, world.c_win ) world.set_window( world.c_win ) # &window is not so global apparantly??? # Cmds := Commands( self ) Cmds := Commands() session := Session() # &window := world.c_win fheight := WAttrib( "fheight" ) fwidth := WAttrib( "fwidth" ) DrawLine(0, height/2, width, height/2) WAttrib( "x=0" , "y=" || ( height - margin ) ) } end # method chat_create() # trying to intercept c_win close and prevent it from closing the app # method close() # # world.set_window(world.c_win) # write( "c_win hidden rather than closed " ) # WAttrib( "canvas=hidden" ) # # end # method c_win_close method resize(x, y) tmp := WAttrib( "fg") WAttrib("fg="||WAttrib("bg")) DrawLine(0, height/2, width, height/2) WAttrib("fg="||tmp) # &window := world.c_win width := x height := y DrawLine(0, height/2, width, height/2) WAttrib( "x=0" , "y=" || ( height - margin ) ) Refresh() end # method resize() # # write_to_chat_win(w,c) takes in characters from the user and # processes them in the correct half of the client window # w = 1 writes in top part, w = 2 writes in bottom c is char string # method write_to_chat_win(w, S[]) local y_offset, c, pnum pnum := 0 world.set_window( world.c_win ) # &window := world.c_win while c := pop(S) do { if c == "\^d" then fail y_offset := (w - 1) * height/2 if c == ("\r"|"\n") | xpos[w] > width then { scroll_up(y_offset+1, height/2-1) xpos[w] := margin } if c == ("\r"|"\n") then next if c == "\b" then { if xpos[w] >= margin + fwidth then { EraseArea(xpos[w] - fwidth, y_offset+1+height/2-1-fheight, fwidth, fheight) xpos[w] -:= fwidth return } } DrawString(xpos[w], height/2 + y_offset - margin, c) xpos[w] +:= fwidth WAttrib( "x=" || xpos[2] , "y=" || ( height - margin ) ) } return end # method write_to_chat_win() # # ScrollUp(vpos,h) adjusts the client window to accept new text # method scroll_up(vpos, h) CopyArea( 0,vpos+fheight,width, h-fheight,0,vpos) EraseArea(0,vpos+h-fheight, width, fheight) end # todo: # 1. need to handle backspace/delete chars better #. 2. method handle_chat_window() # need better name here local e, message , parse, tmp world.set_window(world.c_win) e := Event( world.c_win ) if e === &resize then resize(&x, &y ) if e == "\b" then # char( 8 ) then # handle backspace { # write(" gotta backspace :::" || input_line || "::: len:",*input_line) if *input_line = 0 then { return } else { tmp := (*input_line) # for some bizzarro reason, this deletes the # last character *******???????? input_line := input_line[1:*input_line] write_to_chat_win(2, e) # write(" done backspace :::" || input_line || "::: len:",*input_line) return } } # skip control keys, arrow keys, etc. if type(e) == "integer" then fail #if type(e) == "integer" & (&lpress >= e >= &rdrag) then fail if not string(e) then { write("nonstring ", image(e)) reset_input_bufs() return } input_line := input_line || e write_to_chat_win(2,e) | fail # echo char to chat window if ( ( e ~== "\n" ) & ( e ~== "\r" ) ) then return # no end of line so loop on input if *input_line = 1 then # blank line { write_to_chat_win(2, input_line) reset_input_bufs() return } if not Cmds.IsCommand( input_line ) then { write_to_chat_win(1, input_line || " is not a correct command line." || " Message not sent to server." , "\n") reset_input_bufs() return } #check for correct protocol parse := Cmds.GetCommand( input_line ) # get cmd from cmd line # validate command is available usercmd := parse[1] userargs := parse[2] # write ( " usercmd 1 : ", usercmd) if not Cmds.ValidCommand(usercmd) then { message := "\\" || usercmd || " is not a command. " || " Message not sent to server." write_to_chat_win(1, message, "\n") reset_input_bufs() return } # local commands which affect only client can go here if usercmd === "attrib" then { WAttrib( trim(userargs,"\"",0) ) Refresh() reset_input_bufs() return } if usercmd === "close" then { if \(world.net) then { write_to_chat_win(1, "You must logout before closing chat window. ", "\n" ) } else { close( world.c_win ) world.c_win := &null } reset_input_bufs() return } if usercmd === "server" then { if userargs === "" then { write_to_chat_win(1, "Server: " || world.server || ":" || world.port , "\n" ) } else { if \(world.net) then { write_to_chat_win(1, "You must logout before changing server. ", "\n" ) } else { tmp := find(':', userargs) world.server := userargs[1:tmp] world.port := userargs[(tmp+1):0] write_to_chat_win(1, "Server: " || world.server || ":" || world.port , "\n" ) } } reset_input_bufs() return } if usercmd === "quit!" then { close(net) # fast, but not clean on server side close(c_win) world.close() } # end local commands # write ( " usercmd 2 : ", usercmd) #open up connection to server if user requests \login if session.NotLogged(usercmd) then { # write ( " usercmd 3 : ", usercmd) connect_server() reset_input_bufs() return } #check for net connection and send message to server if /world.net then { write_to_chat_win(1, "Not connected to server", "\n") reset_input_bufs() return } #deny login if already connected to server if usercmd === "login" then { message:="Already logged into server as: " || session.uid write_to_chat_win(1, message, "\n") reset_input_bufs() return } if usercmd === "logout" then { write(world.net, input_line) WSync() # see user_logout() - these must wait for return server msg # lremvals(world.event_source_list, world.net) # world.net := &null # session.logout() reset_input_bufs() return } # FINALLY - send input_line to server # write("sending:::" || input_line || ":::" ) write(world.net, input_line) #### should check return codes here... reset_input_bufs() end # method handle_chat_window() method reset_input_bufs() input_line := "" usercmd := "" userargs := "" WSync() end # method reset_input_bufs method connect_server() # make sure user name is given if not session.CheckUid( input_line , *usercmd+2) then { write_to_chat_win(1, "Cannot connect to server without user name. ", "\n") session.uid := "" return } write(" trying : ", world.server, || ":" || world.port ) # can we test that the user *has* an internet connection???? # especially for dial-up if world.net := open( (world.server || ":" || world.port), "n") then { write(" type(world.net) is: ", type(world.net) ) write(world.net, input_line ) put(world.event_source_list, world.net) write_to_chat_win(1, "Logging into server as: "|| session.uid, "\n") } else { session.uid := "" write_to_chat_win(1, "Net open failed", "\n") write( "Open failed: ") write( " ", sys_errstr(&errno)) } return end # method connect_server() method user_logout() # write(" chat_window.user_logout() " ) lremvals(world.event_source_list, world.net) world.net := &null session.logout() end # method user_logout() # these came from commands.icn # prolly belong in client chat_window.icn ###### CLIENT COMMANDS #write out available input for user to read - only used by ClientCommand method ClientMessage(buffer,n) write_to_chat_win(1,buffer[n:*buffer+1], "\n") end #validate command and service it method ClientCommand(buffer) local UserCmd if Cmds.IsCommand(buffer) then { UserCmd := (Cmds.GetCommand(buffer))[1] # write(" commands ClientCommand UserCmd:"||UserCmd) if Cmds.ValidCommand(UserCmd) then { case UserCmd of { "say": ClientMessage(buffer,*UserCmd+3) "logout": { ClientMessage(buffer, *UserCmd+3) user_logout() } default: ClientMessage("message processed, no command available",0) } } else { fail } } else { fail } end ###### END CLIENT COMMANDS # should these move to class world???? # global x, y, initially( worl ) world := worl Cmds := &null session := &null margin := 3 xpos := [margin, margin] # used in method write_to_chat_win() width := 600 # chat window initial height/width height := 250 fheight := &null # font height/width fwidth := &null input_line := "" usercmd := "" userargs := "" end # class Chat_win