233 lines
7.1 KiB
Go
233 lines
7.1 KiB
Go
// This file is part of https://github.com/racingmars/go3270/
|
|
// Copyright 2025 by Matthew R. Wilson, licensed under the MIT license. See
|
|
// LICENSE in the project root for license information.
|
|
|
|
// This example demonstrates using the RunTransactions() approach to
|
|
// structuring a go3270 application.
|
|
|
|
// This file contains the main menu and the example placeholder features
|
|
// of the application.
|
|
|
|
package main
|
|
|
|
import (
|
|
"net"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/racingmars/go3270"
|
|
)
|
|
|
|
const mainmenuOption = "option"
|
|
const mainmenuUsername = "username"
|
|
const mainmenuTime = "time"
|
|
const mainmenuUsers = "users"
|
|
const mainmenuError = "errormsg"
|
|
|
|
var mainmenuScreen = go3270.Screen{
|
|
{Row: 0, Col: 31, Intense: true, Content: "Main Menu"},
|
|
|
|
// Option
|
|
{Row: 1, Col: 0, Content: "Option ===>", Color: go3270.Green},
|
|
{Row: 1, Col: 12, Name: mainmenuOption, Write: true,
|
|
Highlighting: go3270.Underscore, Color: go3270.Turquoise},
|
|
{Row: 1, Col: 79, Autoskip: true}, // field "stop" character
|
|
|
|
// Info
|
|
{Row: 3, Col: 57, Content: "User ID . :", Color: go3270.Green},
|
|
{Row: 3, Col: 69, Name: mainmenuUsername, Color: go3270.Turquoise},
|
|
{Row: 4, Col: 57, Content: "Time. . . :", Color: go3270.Green},
|
|
{Row: 4, Col: 69, Name: mainmenuTime, Color: go3270.Turquoise},
|
|
{Row: 5, Col: 57, Content: "Users . . :", Color: go3270.Green},
|
|
{Row: 5, Col: 69, Name: mainmenuUsers, Color: go3270.Turquoise},
|
|
|
|
// Options
|
|
{Row: 3, Col: 0, Content: "1", Color: go3270.White},
|
|
{Row: 3, Col: 3, Content: "Feature 1",
|
|
Color: go3270.Turquoise, Intense: true},
|
|
{Row: 3, Col: 17, Content: "A very cool feature", Color: go3270.Green},
|
|
|
|
{Row: 4, Col: 0, Content: "2", Color: go3270.White},
|
|
{Row: 4, Col: 3, Content: "Feature 2",
|
|
Color: go3270.Turquoise, Intense: true},
|
|
{Row: 4, Col: 17, Content: "Another neat feature", Color: go3270.Green},
|
|
|
|
{Row: 5, Col: 0, Content: "3", Color: go3270.White},
|
|
{Row: 5, Col: 3, Content: "Feature 3",
|
|
Color: go3270.Turquoise, Intense: true},
|
|
{Row: 5, Col: 17, Content: "This one's a boring feature",
|
|
Color: go3270.Green},
|
|
|
|
{Row: 7, Col: 5, Content: "Enter", Color: go3270.Green},
|
|
{Row: 7, Col: 11, Content: "X", Color: go3270.Turquoise, Intense: true},
|
|
{Row: 7, Col: 13, Content: "to log off and exit.", Color: go3270.Green},
|
|
|
|
// Error message
|
|
{Row: 21, Col: 0, Name: mainmenuError, Color: go3270.Red, Intense: true},
|
|
|
|
// Key legend
|
|
{Row: 23, Col: 1, Content: "F1=Help"},
|
|
{Row: 23, Col: 14, Content: "F3=Exit"},
|
|
{Row: 23, Col: 27, Content: "F5=Refresh"},
|
|
}
|
|
|
|
type mainmenuData struct {
|
|
option string // pre-populated option field value
|
|
errmsg string // error message to display
|
|
}
|
|
|
|
// mainmenu transaction accepts a mainmenuData struct as the data if the
|
|
// option field or error message should be populated.
|
|
func (sess *session) mainmenu(conn net.Conn, data any) (go3270.Tx, any, error) {
|
|
|
|
fieldValues := make(map[string]string)
|
|
|
|
if data != nil {
|
|
if d, ok := data.(mainmenuData); ok {
|
|
fieldValues[mainmenuOption] = d.option
|
|
fieldValues[mainmenuError] = d.errmsg
|
|
}
|
|
}
|
|
|
|
fieldValues[mainmenuUsername] = strings.ToUpper(sess.user.Username)
|
|
fieldValues[mainmenuTime] = time.Now().UTC().Format("15:04:05")
|
|
fieldValues[mainmenuUsers] = strconv.Itoa(sess.g.usercount)
|
|
|
|
resp, err := go3270.HandleScreen(
|
|
mainmenuScreen, // the screen to display
|
|
nil, // (no) rules to enforce
|
|
fieldValues, // pre-populated values in fields
|
|
[]go3270.AID{go3270.AIDEnter}, // keys we accept -- validating
|
|
[]go3270.AID{ // keys we accept -- non-validating
|
|
go3270.AIDPF1,
|
|
go3270.AIDPF3,
|
|
go3270.AIDPF5,
|
|
},
|
|
mainmenuError, // name of field to put error messages in
|
|
1, 13, // cursor coordinates
|
|
conn)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
switch resp.AID {
|
|
case go3270.AIDPF1:
|
|
// Display help, then return to this transaction
|
|
return help(sess.mainmenu), nil, nil
|
|
case go3270.AIDPF3:
|
|
// Exit
|
|
return nil, nil, nil
|
|
case go3270.AIDPF5:
|
|
// Refresh
|
|
return sess.mainmenu, nil, nil
|
|
}
|
|
|
|
// Handle the available options:
|
|
switch resp.Values[mainmenuOption] {
|
|
case "1":
|
|
return sess.exampleFeature,
|
|
"This is feature 1, a very cool feature.", nil
|
|
case "2":
|
|
return sess.exampleFeature,
|
|
"This is feature 2, another neat feature.", nil
|
|
case "3":
|
|
return sess.exampleFeature,
|
|
"This is feature 3, which is a boring one.", nil
|
|
case "x", "X":
|
|
// As if user hit PF3; exit
|
|
return nil, nil, nil
|
|
case "":
|
|
// As if user hit PF5, refresh
|
|
return sess.mainmenu, nil, nil
|
|
default:
|
|
// Run the transaction again with an error message
|
|
return sess.mainmenu,
|
|
mainmenuData{
|
|
option: resp.Values[mainmenuOption],
|
|
errmsg: "Unknown option",
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
const featureMessage = "message"
|
|
|
|
var exampleScreen = go3270.Screen{
|
|
{Row: 0, Col: 30, Intense: true, Content: "Application Feature"},
|
|
|
|
// Info -- replicate the data shown on the main menu
|
|
{Row: 3, Col: 57, Content: "User ID . :", Color: go3270.Green},
|
|
{Row: 3, Col: 69, Name: mainmenuUsername, Color: go3270.Turquoise},
|
|
{Row: 4, Col: 57, Content: "Time. . . :", Color: go3270.Green},
|
|
{Row: 4, Col: 69, Name: mainmenuTime, Color: go3270.Turquoise},
|
|
{Row: 5, Col: 57, Content: "Users . . :", Color: go3270.Green},
|
|
{Row: 5, Col: 69, Name: mainmenuUsers, Color: go3270.Turquoise},
|
|
|
|
// Feature-specific message
|
|
{Row: 12, Col: 0, Name: featureMessage},
|
|
|
|
{Row: 14, Col: 0, Content: "Press"},
|
|
{Row: 14, Col: 6, Content: "PF3", Intense: true, Color: go3270.White},
|
|
{Row: 14, Col: 10, Content: "to return to the main menu."},
|
|
|
|
// Error message
|
|
{Row: 21, Col: 0, Name: mainmenuError, Color: go3270.Red, Intense: true},
|
|
|
|
// Key legend
|
|
{Row: 23, Col: 1, Content: "F1=Help"},
|
|
{Row: 23, Col: 14, Content: "F3=Exit"},
|
|
{Row: 23, Col: 27, Content: "F5=Refresh"},
|
|
}
|
|
|
|
// exampleFeature is a transaction that will act as a placeholder for real
|
|
// application functionality. It accepts a string in the data which will
|
|
// be displayed on the panel.
|
|
func (sess *session) exampleFeature(conn net.Conn, data any) (go3270.Tx,
|
|
any, error) {
|
|
|
|
fieldValues := make(map[string]string)
|
|
|
|
if data != nil {
|
|
if message, ok := data.(string); ok {
|
|
fieldValues[featureMessage] = message
|
|
}
|
|
}
|
|
|
|
fieldValues[mainmenuUsername] = strings.ToUpper(sess.user.Username)
|
|
fieldValues[mainmenuTime] = time.Now().UTC().Format("15:04:05")
|
|
fieldValues[mainmenuUsers] = strconv.Itoa(sess.g.usercount)
|
|
|
|
resp, err := go3270.HandleScreen(
|
|
exampleScreen, // the screen to display
|
|
nil, // (no) rules to enforce
|
|
fieldValues, // pre-populated values in fields
|
|
nil, // keys we accept -- validating
|
|
[]go3270.AID{ // keys we accept -- non-validating
|
|
go3270.AIDPF1,
|
|
go3270.AIDPF3,
|
|
go3270.AIDPF5,
|
|
},
|
|
mainmenuError, // name of field to put error messages in
|
|
23, 79, // cursor coordinates
|
|
conn)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
switch resp.AID {
|
|
case go3270.AIDPF1:
|
|
// Display help, then return to this transaction
|
|
return help(sess.exampleFeature), data, nil
|
|
case go3270.AIDPF3:
|
|
// Exit
|
|
return sess.mainmenu, nil, nil
|
|
case go3270.AIDPF5:
|
|
// Refresh
|
|
return sess.exampleFeature, data, nil
|
|
}
|
|
|
|
// ...there shouldn't be any actions not handled above. Just in case,
|
|
// we'll just re-run this transaction as if refresh was hit.
|
|
return sess.exampleFeature, data, nil
|
|
}
|