- Lowell George, Fred Martin
#=============================================================================================== # Example Script: MessageChat # Demonstrates the use of Aztec remote messaging to create a network chat program. #=============================================================================================== # The "Main" class is derived from 'Thread'. System automatically creates it and invokes Run().
public class Main from<Thread>
{
# Constructor for Main class. Even with no initialization, still need so Thread() is called for base class.
public method Main()
{
}
#--------------------------------------------------------------------------
# This is the main Run method for the Script. Aztec automatically invokes
# this method in order to start execution of the Script. It builds and
# displays the main UI for the script. We also set up the Script Message
# listening port and also connect to the message server for 'client' mode.
# We then go into a perpetual event waiting mode, never to return.
#--------------------------------------------------------------------------
public virtual method Run()
{
data<bool> Success = true
data<int> RemotePort
data<string> Argument
data<string> DialogMessage
# Determine whether we are the "Server" or the "Client", based on command line argument.
Argument = Script().GetArg(1)
if ( Argument.Lwr() == "server")
{
IsServer = true
IncomingPort = 1081
OutgoingPort = 1082
}
else
{
IsServer = false
IncomingPort = 1082
OutgoingPort = 1081
# Set the server name from the second argument.
RemoteName = Script().GetArg(2)
}
# Setup text message event handler for client and server.
Script().AddTextMessageHandler(MainTextMessageHandler)
# Setup the message server to receive messages from the other side (done for client and server).
Success = Script().StartMessageServer(IncomingPort)
if ( Success )
{
#-------------------------------------------------------------------------------
# For client only, create the message client and try to connect to the server.
# Then display the chat dialog which can talk or display connection error.
#-------------------------------------------------------------------------------
if ( !IsServer )
{
# Reset success flag to false for client processing within this block.
Success = false
if ( RemoteName.Len() > 0 )
{
RemoteMessageClient = new<MessageClient>
Success = RemoteMessageClient.Connect(RemoteName,OutgoingPort)
if ( Success )
{
#--------------------------------------------------------------------------------
# Send message with start command to remote message server to establish "chat".
# The message Id is Start, and the text doesn't matter.
#--------------------------------------------------------------------------------
RemoteMessageClient.SendTextMessage("",StartCommand)
DialogMessage = "Client successfully connected to " + RemoteName + " server."
}
else
{
DialogMessage = "Error connecting to " + RemoteName
}
}
else
{
DialogMessage = "Server name is empty"
}
}
else
{
DialogMessage = "Waiting for connection request from client..."
}
}
else
{
DialogMessage = "Error starting message server on Port " + IncomingPort.Str()
}
# Create Chat Dialog object (client and server) and then go into event mode (success or failure).
LocalChatDialog = new<ChatDialog(self,IsServer,Success,DialogMessage)>
EventMode()
}
#---------------------------------------------------------------------------
# This method receives incoming Text messages from remote machine. We also
# support a couple "commmands" in addition to the text bound for the UI.
# The MessageId() of MessageEvent class is used to send several commands
# here - "start" and "stop". The remote name that is connected is in the
# text for the start command.
#---------------------------------------------------------------------------
method unique MainTextMessageHandler(TextMessageEvent Event,Base ExtraRef)
{
data<bool> Success
data<string> MessageString
data<string[]> ClientNames
data<int> shared TotalClientCount = 0
#------------------------------------------------------------------------
# Look at the message ID for special commands, start and stop. The Start
# will only come from the 'client', but Stop can come from either side.
#------------------------------------------------------------------------
MessageString = Event.Text()
if ( Event.MessageId() == StopCommand )
{
if ( IsServer )
LocalChatDialog.DisableMessageInput("Disabled chat - client has shut down.")
else
LocalChatDialog.DisableMessageInput("Disabled chat - server has shut down.")
}
else if ( Event.MessageId() == StartCommand )
{
# Only happens on Server. The client name can be gotten from the Message Client Name list.
ClientNames = Script().GetMessageClients()
if ( ClientNames != null )
{
# Simple logic assumes no conflict with multiple clients connecting simultaneously and conflicting.
TotalClientCount.Inc()
RemoteName = ClientNames[TotalClientCount]
# Connect to the message server on the 'client' machine.
RemoteMessageClient = new<MessageClient>
Success = RemoteMessageClient.Connect(RemoteName,OutgoingPort)
if ( Success )
LocalChatDialog.EnableMessageInput("Established connection with " + RemoteName + " client.")
else
LocalChatDialog.DisableMessageInput("Error connecting to " + RemoteName)
}
else
{
LocalChatDialog.DisableMessageInput("Unable to determine name of message client.")
}
}
else
{
# This is a standard text message and process accordingly.
ProcessMessage(MessageString,false)
}
}
#------------------------------------------------------------------------
# Message to be called from the ChatDialog object when the Send button
# is pressed. It handles it for client and server. We massage the text
# and then send it back to the Chat Dialog. We then also send it to the
# remote connection using Script.SendTextMessage() method.
#------------------------------------------------------------------------
method ProcessMessage(string Message,bool IsLocal)
{
data<string> LocalSource = "Local: "
data<string> RemoteSource = "Remote: "
data<string> MassagedMessage
# Create the message for the local ChatDialog object and send it to conversation window.
if ( IsLocal )
MassagedMessage = LocalSource + Message
else
MassagedMessage = RemoteSource + Message
LocalChatDialog.UpdateConversation(MassagedMessage)
#---------------------------------------------------------------------------
# Send the text to the remote server (client or server), if the local flag
# is on. If remote, put up message saying we received the message.
#---------------------------------------------------------------------------
if ( IsLocal )
RemoteMessageClient.SendTextMessage(Message)
else
LocalChatDialog.SetDialogMessage("Received new message.")
}
# Function to be called from within the script to shut down the program.
method ShutdownScript(string Message)
{
# If we're connected to remote message server, send message to server that we're shutting down.
if ( RemoteMessageClient != null )
{
RemoteMessageClient.SendTextMessage("",StopCommand)
}
Script().WriteLog(Message)
exit
}
# Data items for the 'Main' Class - 'private' by default.
data<int> IncomingPort
data<int> OutgoingPort
data<bool> IsServer
data<string> RemoteName
data<int> const StopCommand = 2
data<int> const StartCommand = 1
data<ChatDialog> LocalChatDialog
data<MessageClient> RemoteMessageClient
}
#-------------------------------------------------------------------------------------------------
# The "ChatDialog" class is responsible for creating a chat window, and it works for the "client"
# side of the link and for the "server" side of the link. The script creates one instance of
# this object. The client creates it right away. The server holds off until it gets a message
# successfully sent to it.
#-------------------------------------------------------------------------------------------------
public class ChatDialog
{
# Constructor for the Main class. Receives main thread object and several other startup flags.
public method ChatDialog(Main MainThread, bool IsServer, bool RemoteConnectionSuccessful = true,
string StartupMessage = "")
{
MainScriptThread = MainThread
IsServerDialog = IsServer
# Build and display the chat dialog.
BuildChatDialog(RemoteConnectionSuccessful,StartupMessage)
}
# This method is used to create the Chat Dialog for local and remote windows.
method BuildChatDialog(bool RemoteConnectionSuccessful, string StartupMessage)
{
data<bool> ReadOnly = true
data<string> FrameTitle
if ( IsServerDialog )
FrameTitle = "Message Server Chat Dialog"
else
FrameTitle = "Message Client Chat Dialog"
# Create main window and the controls for the run-time options. Add Close and Resize handlers.
MainFrame = new<Frame(TargetDisplay,0,0,FrameWidth,FrameHeight,FrameTitle,true)>
MainFrame.AddWindowCloseHandler(MainFrameCloseHandler)
MainFrame.AddWindowResizeHandler(MainFrameResizeHandler)
# Create the "Conversation" box and associated text box (read-only). Use arbitrary size.
ConversationBox = new<GroupBox(MainFrame,TopBoxPosX,TopBoxPosY,10,10," Conversation ")>
ConversationEditor = new<Editor(ConversationBox,1,1,10,10,ReadOnly)>
# Create the "Message" box and associated text box (arbitrary size) and "Send" button (disable at startup).
MessageBox = new<GroupBox(MainFrame,TopBoxPosX,TopBoxPosY,10,10," Outgoing Message ")>
MessageEditor = new<Editor(MessageBox,1,1,10,10)>
MessageEditor.AddTextChangedHandler(MessageUpdateHandler)
SendButton = new<PushButton(MessageBox,1,1,ButtonWidth,ButtonHeight,"Send",null)>
SendButton.AddButtonClickHandler(SendHandler)
SendButton.Disable()
# Finally create the "Alert" text and the "Exit" button and attach the appropriate event handler.
AlertText = new<Text(MainFrame,1,1,10,10,"")>
ExitButton = new<PushButton(MainFrame,1,1,ButtonWidth,ButtonHeight,"Close",null)>
ExitButton.AddButtonClickHandler(ExitHandler)
#-----------------------------------------------------------------------------
# If error connecting to remote display, display error message on local
# window and gray out messaging controls. Otherwise, display startup message.
#-----------------------------------------------------------------------------
if ( !RemoteConnectionSuccessful )
DisableMessageInput(StartupMessage)
else
AlertText.SetWindowText(StartupMessage)
# For server, disable input into the message box until we have an active connection.
if ( IsServerDialog )
{
DisableMessageInput()
}
#--------------------------------------------------------------------
# Display the dialog and and all controls below it. Works the same
# way for the local and the remote UI. The UI framework handles all
# of the details.
#--------------------------------------------------------------------
SetUISizesAndPositions()
MainFrame.Show()
}
# Method to disable messaging controls in Local when error in Remote, or at start of program.
method DisableMessageInput(string ErrorMessage = "")
{
if ( ErrorMessage.Len() > 0 )
{
AlertText.SetWindowText(ErrorMessage)
}
MessageBox.Disable()
ConversationBox.Disable()
}
# Method to turn on the message boxes once we get contacted from client.
method EnableMessageInput(string ErrorMessage = "")
{
if ( ErrorMessage.Len() > 0 )
{
AlertText.SetWindowText(ErrorMessage)
}
MessageBox.Enable()
ConversationBox.Enable()
}
method SetDialogMessage(string Message)
{
AlertText.SetWindowText(Message)
}
#--------------------------------------------------------------------------------------
# Takes a massaged message from the Main object and adds it to the Conversation
# editor control. This will be slightly different based on the source of the message
# and which ChatDialog is receiving it.
#--------------------------------------------------------------------------------------
method UpdateConversation(string Message)
{
# Simply add the string to the conversation editor control.
ConversationEditor.AddLine(Message)
}
#--------------------------------------------------------------------------------------
# Sends a message to the "other" chat dialog. We send it to the Main Script object and
# let it handle the details. It massages it based on who sent it, and then it gets
# sent back out to both ChatDialog objects to be embedded in the Conversation editor.
# Finally, we clear out the Message editor control within this dialog.
#--------------------------------------------------------------------------------------
method unique SendHandler(ButtonClickEvent ButtonEvent1,Base ExtraRef1)
{
data<bool> IsLocal = true
data<string> Message
# Extract the message and send it to the main object for processing.
Message = MessageEditor.WindowText()
MainScriptThread.ProcessMessage(Message,IsLocal)
# Clear the text in the Message editor control and disable Send button.
MessageEditor.SetWindowText("")
SendButton.Disable()
}
#-----------------------------------------------------------------------
# Event handler to get control when the Message field has been updated.
# We simply turn on the Send button. We will also clear out Alert text.
#-----------------------------------------------------------------------
method unique MessageUpdateHandler(TextChangedEvent ChangedEvent1,Base ExtraRef1)
{
SendButton.Enable()
UpdateAlertText("")
}
# Updates the "Alert" text box near the bottom of the window.
method UpdateAlertText(string AlertMessage)
{
AlertText.SetWindowText(AlertMessage)
}
# Event handler to get control when the Chat Dialog is closed.
method unique MainFrameCloseHandler(WindowCloseEvent CloseEvent1,Base ExtraRef1)
{
ShutdownChatDialog()
}
# Event handler to get control when the Chat Dialog is resized.
method unique MainFrameResizeHandler(WindowResizeEvent ResizeEvent1,Base ExtraRef1)
{
# Resize and/or reposition all of the controls in the window.
SetUISizesAndPositions()
}
# Event handler to get control when the Chat Dialog is closed via the 'Close' button.
method unique ExitHandler(ButtonClickEvent ButtonEvent1,Base ExtraRef1)
{
ShutdownChatDialog()
}
#---------------------------------------------------------------------------------
# This method calls the associated shut down method from the Main Script Thread.
# if we're the Local dialog. If we're the remote dialog, then we'll hide ourself
# and then tell the Local dialog to disable messaging controls.
#---------------------------------------------------------------------------------
method ShutdownChatDialog()
{
data<string> Message
if ( IsServerDialog )
{
Message = "Shutting down chat script - cancelled from server side."
MainScriptThread.ShutdownScript(Message)
}
else
{
Message = "Shutting down chat script - cancelled from client side."
MainScriptThread.ShutdownScript(Message)
}
}
#-----------------------------------------------------------------------
# Dynamically sets the position and size of the controls in the frame.
# Invoked when dialog is first displayed and also as a resize handler.
#-----------------------------------------------------------------------
method SetUISizesAndPositions()
{
data<int> ButtonX
data<int> ButtonY
data<int> BoxWidth
data<int> BoxHeight
data<int> FrameWidth
data<int> FrameHeight
data<int> TestFrameWidth
data<int> TestFrameHeight
data<int> const InnerGapX = 5
data<int> const InnerGapY = 5
data<int> const OuterGapX = 10
data<int> const OuterGapY = 10
data<int> const ButtonGapX = 10
data<int> const ButtonGapY = 7
data<int> const MinFrameWidth = 250
data<int> const MinFrameHeight = 250
data<int> const TextToButtonAdjustY = 3
#------------------------------------------------------------------------------
# First, determine width and height of frame to use for sizing other controls.
# We will only let the entire thing get so small, and will fix the dimension
# at the min value, regardless of actual size of the window.
#------------------------------------------------------------------------------
TestFrameWidth = MainFrame.Width()
TestFrameHeight = MainFrame.Height()
# Determine if we use the actual width or the min width for our "virtual frame".
if ( TestFrameWidth > MinFrameWidth )
FrameWidth = TestFrameWidth
else
FrameWidth = MinFrameWidth
# Determine if we use the actual height or the min height for our "virtual frame".
if ( TestFrameHeight > MinFrameHeight )
FrameHeight = TestFrameHeight
else
FrameHeight = MinFrameHeight
#----------------------------------------------------------------------------------------------
# Now we know the size that we're going to work with, so lay out the controls appropriately.
# Some will be moved (most controls and all buttons) and some will be resized. The group boxes
# and the Editor controls will grow in both directions as necessary.
#----------------------------------------------------------------------------------------------
# First, determine the width and height of the two group boxes. They will have same height.
BoxWidth = FrameWidth - (2 * OuterGapX)
BoxHeight = (FrameHeight - ((2 * ButtonGapY) + ButtonHeight + OuterGapY + TopBoxPosY)) / 2
# Do the top box first and its internal editor control.
ConversationBox.SetPos(TopBoxPosX,TopBoxPosY)
ConversationBox.SetSize(BoxWidth,BoxHeight)
ConversationEditor.SetSize(ConversationBox.Width(),ConversationBox.Height())
# Do the bottom box next, its internal editor control and the Send button.
MessageBox.SetPos(TopBoxPosX,TopBoxPosY + BoxHeight + OuterGapY)
MessageBox.SetSize(BoxWidth,BoxHeight)
MessageEditor.SetSize(MessageBox.Width(),MessageBox.Height() - (ButtonHeight + (ButtonGapY *2)))
SendButton.SetPos(1,MessageEditor.Height() + ButtonGapY + 1)
# Finally, modify the position of the Alert text (and size) and the Exit button.
ButtonX = FrameWidth - OuterGapX - ButtonWidth
ButtonY = TopBoxPosY + (BoxHeight * 2) + OuterGapY + ButtonGapY
AlertText.SetPos(OuterGapX,ButtonY + TextToButtonAdjustY)
AlertText.SetSize(ButtonX - (OuterGapX + ButtonGapX),ControlHeight)
ExitButton.SetPos(ButtonX,ButtonY)
}
# Data items for the 'Main' Class - 'private' by default.
data<Main> MainScriptThread
data<Display> TargetDisplay
data<Frame> MainFrame
data<GroupBox> ConversationBox
data<GroupBox> MessageBox
data<Editor> ConversationEditor
data<Editor> MessageEditor
data<Text> AlertText
data<Button> SendButton
data<Button> ExitButton
data<bool> IsClosed
data<bool> IsServerDialog
data<int> const FrameWidth = 600
data<int> const FrameHeight = 500
data<int> const TopBoxPosX = 10
data<int> const TopBoxPosY = 15
data<int> const ButtonWidth = 75
data<int> const ButtonHeight = 25
data<int> const ControlHeight = 25
}
#=====================================================================================================
# Example Script: MessageSerializationChat
# Demonstrates the use of Aztec message server technology to create a network chat program. This code
# also uses serialization to transfer contents of an Aztec object across the network.
#=====================================================================================================
# Uses the serialization services which are in a separate Aztec source file.
CompilerLoadModule('SerializeManager')
# The "Main" class is derived from 'Thread'. System automatically creates it and invokes Run().
public class Main from<Thread>
{
# Constructor for Main class. Even with no initialization, still need so Thread() is called for base class.
public method Main()
{
}
#--------------------------------------------------------------------------
# This is the main Run method for the Script. Aztec automatically invokes
# this method in order to start execution of the Script. It builds and
# displays the main UI for the script. We also set up the Script Message
# listening port and also connect to the message server for 'client' mode.
# We then go into a perpetual event waiting mode, never to return.
#--------------------------------------------------------------------------
public virtual method Run()
{
data<bool> Success = true
data<int> RemotePort
data<string> Argument
data<string> DialogMessage
# Determine whether we are the "Server" or the "Client", based on command line argument.
Argument = Script().GetArg(1)
if ( Argument.Lwr() == "server")
{
IsServer = true
IncomingPort = 1081
OutgoingPort = 1082
}
else
{
IsServer = false
IncomingPort = 1082
OutgoingPort = 1081
# Set the server name from the second argument.
RemoteName = Script().GetArg(2)
}
# Setup binary message event handler for client and server.
Script().AddBinaryMessageHandler(MainBinaryMessageHandler)
# Setup the message server to receive messages from the other side (done for client and server).
Success = Script().StartMessageServer(IncomingPort)
if ( Success )
{
#-------------------------------------------------------------------------------
# For client only, create the message client and try to connect to the server.
# Then display the chat dialog which can talk or display connection error.
#-------------------------------------------------------------------------------
if ( !IsServer )
{
# Reset success flag to false for client processing within this block.
Success = false
if ( RemoteName.Len() > 0 )
{
RemoteMessageClient = new<MessageClient>
Success = RemoteMessageClient.Connect(RemoteName,OutgoingPort)
if ( Success )
{
# Create a memory buffer to hold just the remote name.
data<MemoryStream> NameStream = new<MemoryStream>
#--------------------------------------------------------------------------------
# Send message with start command to remote message server to establish "chat".
# The message Id is Start, and send memory buffer message with it.
#--------------------------------------------------------------------------------
RemoteMessageClient.SendBinaryMessage(NameStream,0,StartCommand)
DialogMessage = "Client successfully connected to " + RemoteName + " server."
}
else
{
DialogMessage = "Error connecting to " + RemoteName
}
}
else
{
DialogMessage = "Server name is empty"
}
}
else
{
DialogMessage = "Waiting for connection request from client..."
}
}
else
{
DialogMessage = "Error starting message server on Port " + IncomingPort.Str()
}
# Create Chat Dialog object (client and server) and then go into event mode (success or failure).
LocalChatDialog = new<ChatDialog(self,IsServer,Success,DialogMessage)>
EventMode()
}
#----------------------------------------------------------------------------
# This method receives incoming binary messages from remote machine. We also
# support a couple "commmands" in addition to the object message bound for
# the UI. The MessageId() of MessageEvent class is used to send several
# commands here - "start" and "stop". Otherwise, it is used to indicate a
# a real binary message (which is an "AztecRemoteMessage" object).
#----------------------------------------------------------------------------
method unique MainBinaryMessageHandler(BinaryMessageEvent Event,Base ExtraRef)
{
data<bool> Success
data<string[]> ClientNames
data<int> shared TotalClientCount = 0
data<AztecRemoteMessage> RemoteMessage
#------------------------------------------------------------------------
# Look at the message ID for special commands, start and stop. For the
# start command, the remote name is contained in the text. For the stop
# command, there is no text. The Start will only come from the 'client',
# but the Stop can come from either side.
#------------------------------------------------------------------------
if ( Event.MessageId() == StopCommand )
{
if ( IsServer )
LocalChatDialog.DisableMessageInput("Disabled chat - client has shut down.")
else
LocalChatDialog.DisableMessageInput("Disabled chat - server has shut down.")
}
else if ( Event.MessageId() == StartCommand )
{
# Only happens on Server. The client name can be gotten from the Message Client Name list.
ClientNames = Script().GetMessageClients()
if ( ClientNames != null )
{
# Simple logic assumes no conflict with multiple clients connecting simultaneously and conflicting.
TotalClientCount.Inc()
RemoteName = ClientNames[TotalClientCount]
# Connect to the message server on the 'client' machine.
RemoteMessageClient = new<MessageClient>
Success = RemoteMessageClient.Connect(RemoteName,OutgoingPort)
if ( Success )
LocalChatDialog.EnableMessageInput("Established connection with " + RemoteName + " client.")
else
LocalChatDialog.DisableMessageInput("Error connecting to " + RemoteName)
}
else
{
LocalChatDialog.DisableMessageInput("Unable to determine name of message client.")
}
}
else
{
# This is a standard binary message. Create message object and serialize binary message into it.
RemoteMessage = new<AztecRemoteMessage>
RemoteMessage.SerializeIn(Event.Message())
# Process the text portion of the message and send it to the UI.
ProcessMessage(RemoteMessage.MessageText(),false)
}
}
#------------------------------------------------------------------------
# Message to be called from the ChatDialog object when the Send button
# is pressed. It handles it for client and server. We massage the text
# and then send it back to the Chat Dialog. We then also send it to the
# remote connection using Script.SendTextMessage() method.
#------------------------------------------------------------------------
method ProcessMessage(string Message,bool IsLocal)
{
data<string> LocalSource = "Local: "
data<string> RemoteSource = "Remote: "
data<string> MassagedMessage
data<AztecRemoteMessage> RemoteMessage
# Create the message for the local ChatDialog object and send it to conversation window.
if ( IsLocal )
MassagedMessage = LocalSource + Message
else
MassagedMessage = RemoteSource + Message
LocalChatDialog.UpdateConversation(MassagedMessage)
#---------------------------------------------------------------------------
# Send the text to the remote server (client or server), if the local flag
# is on. If remote, put up message saying we received the message.
#---------------------------------------------------------------------------
if ( IsLocal )
{
# Create a message object and serialize it to a memory buffer and then reset position to 1.
RemoteMessage = new<AztecRemoteMessage(Message)>
RemoteMessage.SerializeOut()
RemoteMessage.OutgoingMessage().SetPos(1)
# Get the memory stream from the message and send it to the server.
RemoteMessageClient.SendBinaryMessage(RemoteMessage.OutgoingMessage())
}
else
{
LocalChatDialog.SetDialogMessage("Received new message.")
}
}
# Function to be called from within the script to shut down the program.
method ShutdownScript(string Message)
{
# If we're connected to remote message server, send message to server that we're shutting down.
if ( RemoteMessageClient != null )
{
RemoteMessageClient.SendBinaryMessage(null,0,StopCommand)
}
Script().WriteLog(Message)
exit
}
# Data items for the 'Main' Class - 'private' by default.
data<int> IncomingPort
data<int> OutgoingPort
data<bool> IsServer
data<string> RemoteName
data<int> const StopCommand = 2
data<int> const StartCommand = 1
data<ChatDialog> LocalChatDialog
data<MessageClient> RemoteMessageClient
}
#-------------------------------------------------------------------------------------------
# This class contains the "message" to be sent to the message server. We will create an
# instance, serialize it to a memory stream (buffer) and send it to the server. The server
# then automatically creates the message object from the binary buffer. The serialization
# methods for input and output to a memory buffer are included in the class.
#-------------------------------------------------------------------------------------------
class AztecRemoteMessage
{
# Constructor used when creating the message on client to send to server.
method AztecRemoteMessage(string Text)
{
# Save text message and record time this message is created.
MessageText = Text
CreationTime = new<Time>
}
# Constructor used when creating empty message on server to serialize in message from client.
method AztecRemoteMessage()
{
}
method<Time> CreationTime()
{
return(CreationTime)
}
method<string> MessageText()
{
return(MessageText)
}
# Balance the I/O in this output method with the I/O in the input method below.
method SerializeOut()
{
data<SerializeManager> IOManager
# Create a serialization object with memory and then write contents of the class.
IOManager = new<SerializeManager>
# Write out all of the items in this object. Write time as seconds and milliseconds.
IOManager.Out(MessageText)
IOManager.Out(CreationTime.TotalSeconds())
IOManager.Out(CreationTime.MilliSecond())
# Set the outgoing buffer with the memory stream inside the serialize manager.
OutgoingMessage = IOManager.SerializeStream() as type<MemoryStream>
}
# Balance the I/O in this input method with the I/O in the output method above.
method SerializeIn(MemoryStream Buffer)
{
data<int> Seconds
data<int> MilliSeconds
data<SerializeManager> IOManager
# Create a serialization object with memory and then read contents of this class from the buffer.
IOManager = new<SerializeManager(Buffer)>
# Read in all of the items in this object. Read time as seconds and milliseconds.
IOManager.In(@MessageText)
IOManager.In(@Seconds)
IOManager.In(@MilliSeconds)
# Create the original time using the seconds/milliseconds from the message.
CreationTime = new<Time(Seconds,MilliSeconds)>
# Let's add in time to incoming message automatically.
MessageText.Add(" (" + CreationTime.TimeStr(TimeFormat.HHMMSSms) + ")")
}
method<MemoryStream> OutgoingMessage()
{
return(OutgoingMessage)
}
# Internal data items visible only in the class.
data<Time> CreationTime
data<string> MessageText
data<MemoryStream> OutgoingMessage
}
#-------------------------------------------------------------------------------------------------
# The "ChatDialog" class is responsible for creating a chat window, and it works for the "client"
# side of the link and for the "server" side of the link. The script creates one instance of
# this object. The client creates it right away. The server holds off until it gets a message
# successfully sent to it.
#-------------------------------------------------------------------------------------------------
public class ChatDialog
{
# Constructor for the Main class. Receives main thread object and several other startup flags.
public method ChatDialog(Main MainThread, bool IsServer, bool RemoteConnectionSuccessful = true,
string StartupMessage = "")
{
MainScriptThread = MainThread
IsServerDialog = IsServer
# Build and display the chat dialog.
BuildChatDialog(RemoteConnectionSuccessful,StartupMessage)
}
# This method is used to create the Chat Dialog for local and remote windows.
method BuildChatDialog(bool RemoteConnectionSuccessful, string StartupMessage)
{
data<bool> ReadOnly = true
data<string> FrameTitle
if ( IsServerDialog )
FrameTitle = "Message Server Chat Dialog"
else
FrameTitle = "Message Client Chat Dialog"
# Create main window and the controls for the run-time options. Add Close and Resize handlers.
MainFrame = new<Frame(TargetDisplay,0,0,FrameWidth,FrameHeight,FrameTitle,true)>
MainFrame.AddWindowCloseHandler(MainFrameCloseHandler)
MainFrame.AddWindowResizeHandler(MainFrameResizeHandler)
# Create the "Conversation" box and associated text box (read-only). Use arbitrary size.
ConversationBox = new<GroupBox(MainFrame,TopBoxPosX,TopBoxPosY,10,10," Conversation ")>
ConversationEditor = new<Editor(ConversationBox,1,1,10,10,ReadOnly)>
# Create the "Message" box and associated text box (arbitrary size) and "Send" button (disable at startup).
MessageBox = new<GroupBox(MainFrame,TopBoxPosX,TopBoxPosY,10,10," Outgoing Message ")>
MessageEditor = new<Editor(MessageBox,1,1,10,10)>
MessageEditor.AddTextChangedHandler(MessageUpdateHandler)
SendButton = new<PushButton(MessageBox,1,1,ButtonWidth,ButtonHeight,"Send",null)>
SendButton.AddButtonClickHandler(SendHandler)
SendButton.Disable()
# Finally create the "Alert" text and the "Exit" button and attach the appropriate event handler.
AlertText = new<Text(MainFrame,1,1,10,10,"")>
ExitButton = new<PushButton(MainFrame,1,1,ButtonWidth,ButtonHeight,"Close",null)>
ExitButton.AddButtonClickHandler(ExitHandler)
#-----------------------------------------------------------------------------
# If error connecting to remote display, display error message on local
# window and gray out messaging controls. Otherwise, display startup message.
#-----------------------------------------------------------------------------
if ( !RemoteConnectionSuccessful )
DisableMessageInput(StartupMessage)
else
AlertText.SetWindowText(StartupMessage)
# For server, disable input into the message box until we have an active connection.
if ( IsServerDialog )
{
DisableMessageInput()
}
#--------------------------------------------------------------------
# Display the dialog and and all controls below it. Works the same
# way for the local and the remote UI. The UI framework handles all
# of the details.
#--------------------------------------------------------------------
SetUISizesAndPositions()
MainFrame.Show()
}
# Method to disable messaging controls in Local when error in Remote, or at start of program.
method DisableMessageInput(string ErrorMessage = "")
{
if ( ErrorMessage.Len() > 0 )
{
AlertText.SetWindowText(ErrorMessage)
}
MessageBox.Disable()
ConversationBox.Disable()
}
# Method to turn on the message boxes once we get contacted from client.
method EnableMessageInput(string ErrorMessage = "")
{
if ( ErrorMessage.Len() > 0 )
{
AlertText.SetWindowText(ErrorMessage)
}
MessageBox.Enable()
ConversationBox.Enable()
}
method SetDialogMessage(string Message)
{
AlertText.SetWindowText(Message)
}
#--------------------------------------------------------------------------------------
# Takes a massaged message from the Main object and adds it to the Conversation
# editor control. This will be slightly different based on the source of the message
# and which ChatDialog is receiving it.
#--------------------------------------------------------------------------------------
method UpdateConversation(string Message)
{
# Simply add the string to the conversation editor control.
ConversationEditor.AddLine(Message)
}
#--------------------------------------------------------------------------------------
# Sends a message to the "other" chat dialog. We send it to the Main Script object and
# let it handle the details. It massages it based on who sent it, and then it gets
# sent back out to both ChatDialog objects to be embedded in the Conversation editor.
# Finally, we clear out the Message editor control within this dialog.
#--------------------------------------------------------------------------------------
method unique SendHandler(ButtonClickEvent ButtonEvent1,Base ExtraRef1)
{
data<bool> IsLocal = true
data<string> Message
# Extract the message and send it to the main object for processing.
Message = MessageEditor.WindowText()
MainScriptThread.ProcessMessage(Message,IsLocal)
# Clear the text in the Message editor control and disable Send button.
MessageEditor.SetWindowText("")
SendButton.Disable()
}
#-----------------------------------------------------------------------
# Event handler to get control when the Message field has been updated.
# We simply turn on the Send button. We will also clear out Alert text.
#-----------------------------------------------------------------------
method unique MessageUpdateHandler(TextChangedEvent ChangedEvent1,Base ExtraRef1)
{
SendButton.Enable()
UpdateAlertText("")
}
# Updates the "Alert" text box near the bottom of the window.
method UpdateAlertText(string AlertMessage)
{
AlertText.SetWindowText(AlertMessage)
}
# Event handler to get control when the Chat Dialog is closed.
method unique MainFrameCloseHandler(WindowCloseEvent CloseEvent1,Base ExtraRef1)
{
ShutdownChatDialog()
}
# Event handler to get control when the Chat Dialog is resized.
method unique MainFrameResizeHandler(WindowResizeEvent ResizeEvent1,Base ExtraRef1)
{
# Resize and/or reposition all of the controls in the window.
SetUISizesAndPositions()
}
# Event handler to get control when the Chat Dialog is closed via the 'Close' button.
method unique ExitHandler(ButtonClickEvent ButtonEvent1,Base ExtraRef1)
{
ShutdownChatDialog()
}
#---------------------------------------------------------------------------------
# This method calls the associated shut down method from the Main Script Thread.
# if we're the Local dialog. If we're the remote dialog, then we'll hide ourself
# and then tell the Local dialog to disable messaging controls.
#---------------------------------------------------------------------------------
method ShutdownChatDialog()
{
data<string> Message
if ( IsServerDialog )
{
Message = "Shutting down chat script - cancelled from server side."
MainScriptThread.ShutdownScript(Message)
}
else
{
Message = "Shutting down chat script - cancelled from client side."
MainScriptThread.ShutdownScript(Message)
}
}
#-----------------------------------------------------------------------
# Dynamically sets the position and size of the controls in the frame.
# Invoked when dialog is first displayed and also as a resize handler.
#-----------------------------------------------------------------------
method SetUISizesAndPositions()
{
data<int> ButtonX
data<int> ButtonY
data<int> BoxWidth
data<int> BoxHeight
data<int> FrameWidth
data<int> FrameHeight
data<int> TestFrameWidth
data<int> TestFrameHeight
data<int> const InnerGapX = 5
data<int> const InnerGapY = 5
data<int> const OuterGapX = 10
data<int> const OuterGapY = 10
data<int> const ButtonGapX = 10
data<int> const ButtonGapY = 7
data<int> const MinFrameWidth = 250
data<int> const MinFrameHeight = 250
data<int> const TextToButtonAdjustY = 3
#------------------------------------------------------------------------------
# First, determine width and height of frame to use for sizing other controls.
# We will only let the entire thing get so small, and will fix the dimension
# at the min value, regardless of actual size of the window.
#------------------------------------------------------------------------------
TestFrameWidth = MainFrame.Width()
TestFrameHeight = MainFrame.Height()
# Determine if we use the actual width or the min width for our "virtual frame".
if ( TestFrameWidth > MinFrameWidth )
FrameWidth = TestFrameWidth
else
FrameWidth = MinFrameWidth
# Determine if we use the actual height or the min height for our "virtual frame".
if ( TestFrameHeight > MinFrameHeight )
FrameHeight = TestFrameHeight
else
FrameHeight = MinFrameHeight
#----------------------------------------------------------------------------------------------
# Now we know the size that we're going to work with, so lay out the controls appropriately.
# Some will be moved (most controls and all buttons) and some will be resized. The group boxes
# and the Editor controls will grow in both directions as necessary.
#----------------------------------------------------------------------------------------------
# First, determine the width and height of the two group boxes. They will have same height.
BoxWidth = FrameWidth - (2 * OuterGapX)
BoxHeight = (FrameHeight - ((2 * ButtonGapY) + ButtonHeight + OuterGapY + TopBoxPosY)) / 2
# Do the top box first and its internal editor control.
ConversationBox.SetPos(TopBoxPosX,TopBoxPosY)
ConversationBox.SetSize(BoxWidth,BoxHeight)
ConversationEditor.SetSize(ConversationBox.Width(),ConversationBox.Height())
# Do the bottom box next, its internal editor control and the Send button.
MessageBox.SetPos(TopBoxPosX,TopBoxPosY + BoxHeight + OuterGapY)
MessageBox.SetSize(BoxWidth,BoxHeight)
MessageEditor.SetSize(MessageBox.Width(),MessageBox.Height() - (ButtonHeight + (ButtonGapY *2)))
SendButton.SetPos(1,MessageEditor.Height() + ButtonGapY + 1)
# Finally, modify the position of the Alert text (and size) and the Exit button.
ButtonX = FrameWidth - OuterGapX - ButtonWidth
ButtonY = TopBoxPosY + (BoxHeight * 2) + OuterGapY + ButtonGapY
AlertText.SetPos(OuterGapX,ButtonY + TextToButtonAdjustY)
AlertText.SetSize(ButtonX - (OuterGapX + ButtonGapX),ControlHeight)
ExitButton.SetPos(ButtonX,ButtonY)
}
# Data items for the 'Main' Class - 'private' by default.
data<Main> MainScriptThread
data<Display> TargetDisplay
data<Frame> MainFrame
data<GroupBox> ConversationBox
data<GroupBox> MessageBox
data<Editor> ConversationEditor
data<Editor> MessageEditor
data<Text> AlertText
data<Button> SendButton
data<Button> ExitButton
data<bool> IsClosed
data<bool> IsServerDialog
data<int> const FrameWidth = 600
data<int> const FrameHeight = 500
data<int> const TopBoxPosX = 10
data<int> const TopBoxPosY = 15
data<int> const ButtonWidth = 75
data<int> const ButtonHeight = 25
data<int> const ControlHeight = 25
}