- Mick Jagger and Keith Richards
#===================================================================================================
# Example Script: RemoteUIChat
# Demonstrates the use of Aztec remote UI technology to create a network chat program. This code
# runs only on the "client" and it displays a dialog on the client and on the server.
#===================================================================================================
# The "Main" class is derived from 'Thread'. System automatically creates it and invokes Run().
public class Main from<Thread>
{
public method Main()
{
}
#----------------------------------------------------------------------------
# This is the main Run method for the Script. The Aztec system automatically
# invokes this method in order to start execution of the Script. It builds
# and displays the main UI for the script and then immediately goes into an
# event waiting mode, never to return.
#----------------------------------------------------------------------------
public virtual method Run()
{
data<bool> Success = false
data<int> RemotePort
data<string> ErrorMessage
data<string> RemoteAddress
# Get the remote address and port from the command line (1st and 2nd "arg").
RemoteAddress = Script().GetArg(1)
RemotePort = Script().GetArg(2).Int()
# Create the Display object and try to connect to it.
if ( RemoteAddress.Len() > 0 )
{
RemoteDisplay = new<Display>
Success = RemoteDisplay.Connect(RemoteAddress,RemotePort)
}
# Create two separate chat objects - one for local and one for remote - and go into Event Mode.
if ( Success )
{
# Successfully connected to remote display, so create both chat windows.
LocalChatDialog = new<ChatDialog(self,GetDisplay(),true)>
RemoteChatDialog = new<ChatDialog(self,RemoteDisplay,false)>
}
else
{
# Not able to connect to remote display. Create local chat window, disable most controls, and display message.
if ( RemoteAddress.Len() > 0 )
ErrorMessage = "Unable to connect to remote display and port (" + RemoteAddress + "/" + RemotePort.Str() + ")."
else
ErrorMessage = "Remote display and port must be specified on command line (-arg)."
LocalChatDialog = new<ChatDialog(self,GetDisplay(),true,false,ErrorMessage)>
}
# Whether successful or error, at least one window gets displayed, so go into VM event mode.
EventMode()
}
#------------------------------------------------------------------------
# Method to be called from either of the ChatDialog objects when the
# Send button is pressed. We massage the message text and send it out to
# both of the ChatDialog objects to be embedded within the conversation.
# Depending on who sends it and which dialog we're sending it to, we
# prepend the message with text to indicate the source of the message.
#------------------------------------------------------------------------
method ProcessMessage(string Message,bool SourceIsLocal)
{
data<string> LocalSource = "Local: "
data<string> RemoteSource = "Remote: "
data<string> MassagedMessage
# Create the message for the local ChatDialog object and send it to it.
if ( SourceIsLocal )
MassagedMessage = LocalSource + Message
else
MassagedMessage = RemoteSource + Message
LocalChatDialog.UpdateConversation(MassagedMessage)
# Create the message for the remote ChatDialog object and send it to it.
if ( SourceIsLocal )
MassagedMessage = RemoteSource + Message
else
MassagedMessage = LocalSource + Message
if ( RemoteChatDialog != null )
{
RemoteChatDialog.UpdateConversation(MassagedMessage)
if ( SourceIsLocal )
{
RemoteChatDialog.UpdateAlertText("New message received")
}
}
# Update the Alert text box to tell the recipient there's a new message.
if ( !SourceIsLocal )
{
LocalChatDialog.UpdateAlertText("New message received")
}
}
#----------------------------------------------------------------------
# Method to be called from within the script to shut down the program.
# Close both frames (local and remote), though not necessary.
#----------------------------------------------------------------------
method ShutdownScript(string Message)
{
LocalChatDialog.CloseFrame()
if ( RemoteChatDialog != null )
{
RemoteChatDialog.CloseFrame()
RemoteChatDialog = null
}
Script().WriteLog(Message)
exit
}
#--------------------------------------------------------------------------
# Function to be called when remote dialog was closed. We close the remote
# dialog and then disable some of the local dialog.
#--------------------------------------------------------------------------
method ShutdownRemote(string Message)
{
# Disable messaging controls in local dialog, close remote dialog and close remote connection.
LocalChatDialog.DisableMessageInput(Message)
if ( RemoteChatDialog != null )
{
RemoteChatDialog.CloseFrame()
RemoteChatDialog = null
if ( RemoteDisplay != null )
{
RemoteDisplay.Close()
}
}
}
# Data items for the 'Main' Class - 'private' by default.
data<Display> RemoteDisplay
data<ChatDialog> LocalChatDialog
data<ChatDialog> RemoteChatDialog
}
#-----------------------------------------------------------------------------------------------
# The "ChatDialog" class is responsible for creating a chat window, displaying it (either
# locally or on a remote computer), and then processing events. The program will create two
# separate instances of this class - one for the local client and one for the remote UI.
#-----------------------------------------------------------------------------------------------
public class ChatDialog
{
#--------------------------------------------------------------------------------------
# Constructor for the Main class. Receives the target Display object. The caller is
# responsible for creating the Display object, and in the case of the remote UI,
# connecting to the remote Display Server.
#--------------------------------------------------------------------------------------
public method ChatDialog(Main MainThread, Display Target, bool IsLocal, bool RemoteConnectionSuccessful = true,
string ErrorMessage = "")
{
MainScriptThread = MainThread
TargetDisplay = Target
IsClosed = false
IsLocalDialog = IsLocal
# Build and display the chat dialog.
BuildChatDialog(RemoteConnectionSuccessful,ErrorMessage)
}
# This method is used to create the Chat Dialog for local and remote windows.
method BuildChatDialog(bool RemoteConnectionSuccessful, string ErrorMessage)
{
data<bool> ReadOnly = true
data<string> FrameTitle
if ( IsLocalDialog )
FrameTitle = "Local UI Chat Dialog"
else
FrameTitle = "Remote UI 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,null)
MainFrame.AddWindowResizeHandler(MainFrameResizeHandler,null)
# 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,null)
SendButton = new<PushButton(MessageBox,1,1,ButtonWidth,ButtonHeight,"Send",null)>
SendButton.AddButtonClickHandler(SendHandler,null)
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,null)
#----------------------------------------------------------------------------
# If there was an error connecting to remote display, display error message
# on local window and gray out the messaging controls.
#----------------------------------------------------------------------------
if ( !RemoteConnectionSuccessful )
{
DisableMessageInput(ErrorMessage)
}
#--------------------------------------------------------------------
# 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.
method DisableMessageInput(string ErrorMessage)
{
AlertText.SetWindowText(ErrorMessage)
MessageBox.Disable()
ConversationBox.Disable()
}
#--------------------------------------------------------------------------------------
# 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<string> Message
# Extract the message and send it to the main object for processing.
Message = MessageEditor.WindowText()
MainScriptThread.ProcessMessage(Message,IsLocalDialog)
# 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 CloseEvent1,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 ( IsLocalDialog )
{
Message = "Shutting down chat script - cancelled from local chat dialog."
MainScriptThread.ShutdownScript(Message)
}
else
{
Message = "Disabled chat communication - cancelled from remote chat dialog."
MainScriptThread.ShutdownRemote(Message)
}
}
method CloseFrame()
{
if ( !IsClosed )
{
MainFrame.Close()
IsClosed = true
}
}
#-----------------------------------------------------------------------
# Dynamically sets the position and size of the controls in the frame.
# Invoked when first shown and 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 'ChatDialog' 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> IsLocalDialog
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
}