From 468ba2c6d11b775de9d50a9eeecf1dfde92c7759 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Mon, 20 May 2024 18:37:30 +0200 Subject: [PATCH] move keyer macros into a separate dialog to make room for future changes in the main window --- core/cfg/cfg.go | 2 + core/core.go | 4 + core/keyer/keyer.go | 181 +++++- core/keyer/keyer_test.go | 1 + core/mocked/mocked.go | 4 + core/pb/convert.go | 4 + core/pb/log.pb.go | 147 ++--- core/pb/log.proto | 2 + ui/app.go | 25 +- ui/glade/contest.glade | 744 ++++++++++++++++++++---- ui/{keyerView.go => keyerButtonView.go} | 84 +-- ui/keyerSettingsDialog.go | 81 +++ ui/keyerSettingsView.go | 182 ++++++ 13 files changed, 1177 insertions(+), 284 deletions(-) rename ui/{keyerView.go => keyerButtonView.go} (58%) create mode 100644 ui/keyerSettingsDialog.go create mode 100644 ui/keyerSettingsView.go diff --git a/core/cfg/cfg.go b/core/cfg/cfg.go index ad9f7db..aaae64d 100644 --- a/core/cfg/cfg.go +++ b/core/cfg/cfg.go @@ -64,12 +64,14 @@ var Default = Data{ "tu gl", "nr {{.MyNumber}} {{.MyXchange}} {{.MyNumber}} {{.MyXchange}}", }, + SPLabels: []string{"MyCall", "R Exch", "TU GL", "Exch AGN"}, RunMacros: []string{ "cq {{.MyCall}} test", "{{.TheirCall}} {{.MyReport}} {{.MyNumber}} {{.MyXchange}}", "tu {{.MyCall}} test", "nr {{.MyNumber}} {{.MyXchange}} {{.MyNumber}} {{.MyXchange}}", }, + RunLabels: []string{"CQ", "Exch", "TU QRZ?", "Exch AGN"}, }, }, SpotLifetime: "10m", diff --git a/core/core.go b/core/core.go index eb6e086..f5659b7 100644 --- a/core/core.go +++ b/core/core.go @@ -459,12 +459,16 @@ type KeyerSettings struct { Preset string `json:"preset"` SPMacros []string `json:"sp_macros"` RunMacros []string `json:"run_macros"` + SPLabels []string `json:"sp_labels"` + RunLabels []string `json:"run_labels"` } type KeyerPreset struct { Name string `json:"name"` SPMacros []string `json:"sp_macros"` RunMacros []string `json:"run_macros"` + SPLabels []string `json:"sp_labels"` + RunLabels []string `json:"run_labels"` } type Score struct { diff --git a/core/keyer/keyer.go b/core/keyer/keyer.go index 0d92f57..bc9c6cc 100644 --- a/core/keyer/keyer.go +++ b/core/keyer/keyer.go @@ -15,11 +15,21 @@ import ( const PatternCount = 4 -// View represents the visual parts of the keyer. -type View interface { +// ButtonView represents the visual parts of trigger the transmission of the keyer macros. +type ButtonView interface { ShowMessage(...interface{}) + SetLabel(int, string) SetPattern(int, string) SetSpeed(int) +} + +// SettingsView represents the visual parts to enter keyer macros. +type SettingsView interface { + Show() + ShowMessage(...any) + ClearMessage() + SetLabel(core.Workmode, int, string) + SetMacro(core.Workmode, int, string) SetPresetNames([]string) SetPreset(string) } @@ -44,8 +54,10 @@ func New(settings core.Settings, client CWClient, keyerSettings core.KeyerSettin writer: new(nullWriter), stationCallsign: settings.Station().Callsign, workmode: workmode, + spLabels: make(map[int]string), spPatterns: make(map[int]string), spTemplates: make(map[int]*template.Template), + runLabels: make(map[int]string), runPatterns: make(map[int]string), runTemplates: make(map[int]*template.Template), presets: presets, @@ -71,7 +83,8 @@ func presetNames(presets []core.KeyerPreset) []string { type Keyer struct { writer Writer - view View + buttonView ButtonView + settingsView SettingsView client CWClient presets []core.KeyerPreset presetNames []string @@ -84,10 +97,13 @@ type Keyer struct { stationCallsign callsign.Callsign workmode core.Workmode wpm int + spLabels map[int]string spPatterns map[int]string spTemplates map[int]*template.Template + runLabels map[int]string runPatterns map[int]string runTemplates map[int]*template.Template + labels *map[int]string patterns *map[int]string templates *map[int]*template.Template } @@ -96,9 +112,11 @@ func (k *Keyer) setWorkmode(workmode core.Workmode) { k.workmode = workmode switch workmode { case core.SearchPounce: + k.labels = &k.spLabels k.patterns = &k.spPatterns k.templates = &k.spTemplates case core.Run: + k.labels = &k.runLabels k.patterns = &k.runPatterns k.templates = &k.runTemplates } @@ -115,29 +133,40 @@ func (k *Keyer) SetWriter(writer Writer) { func (k *Keyer) SetSettings(settings core.KeyerSettings) { k.savedSettings = settings + spLabels := settings.SPLabels spMacros := settings.SPMacros + runLabels := settings.RunLabels runMacros := settings.RunMacros preset, ok := k.presetByName(settings.Preset) if ok { + spLabels = applyPreset(settings.SPLabels, preset.SPLabels) spMacros = applyPreset(settings.SPMacros, preset.SPMacros) + runLabels = applyPreset(settings.RunLabels, preset.RunLabels) runMacros = applyPreset(settings.RunMacros, preset.RunMacros) } k.wpm = settings.WPM + for i, label := range spLabels { + k.spLabels[i] = label + } for i, pattern := range spMacros { k.spPatterns[i] = pattern k.spTemplates[i], _ = template.New("").Parse(pattern) } + for i, label := range runLabels { + k.runLabels[i] = label + } for i, pattern := range runMacros { k.runPatterns[i] = pattern k.runTemplates[i], _ = template.New("").Parse(pattern) } k.showPatterns() - if k.view != nil { - k.view.SetSpeed(k.wpm) + if k.buttonView != nil { + k.buttonView.SetSpeed(k.wpm) } + k.showKeyerSettings() } func (k *Keyer) presetByName(name string) (core.KeyerPreset, bool) { @@ -172,19 +201,65 @@ func applyPreset(settingsPatterns []string, presetPatterns []string) []string { return result } -func (k *Keyer) SetView(view View) { - k.view = view +func (k *Keyer) SetView(view ButtonView) { + k.buttonView = view k.showPatterns() - k.view.SetPresetNames(k.presetNames) - k.view.SetSpeed(k.wpm) + k.buttonView.SetSpeed(k.wpm) } func (k *Keyer) showPatterns() { - if k.view == nil { + if k.buttonView == nil { return } + for i, label := range *k.labels { + k.buttonView.SetLabel(i, label) + } for i, pattern := range *k.patterns { - k.view.SetPattern(i, pattern) + k.buttonView.SetPattern(i, pattern) + } +} + +func (k *Keyer) SetSettingsView(view SettingsView) { + k.settingsView = view + k.showKeyerSettings() + k.settingsView.SetPresetNames(k.presetNames) + if k.selectedPreset != nil { + k.settingsView.SetPreset(k.selectedPreset.Name) + } else { + k.settingsView.SetPreset("") + } +} + +func (k *Keyer) OpenKeyerSettings() { + if k.settingsView == nil { + return + } + + k.settingsView.Show() + k.settingsView.SetPresetNames(k.presetNames) + if k.selectedPreset != nil { + k.settingsView.SetPreset(k.selectedPreset.Name) + } else { + k.settingsView.SetPreset("") + } + k.showKeyerSettings() +} + +func (k *Keyer) showKeyerSettings() { + if k.settingsView == nil { + return + } + for i, label := range k.spLabels { + k.settingsView.SetLabel(core.SearchPounce, i, label) + } + for i, pattern := range k.spPatterns { + k.settingsView.SetMacro(core.SearchPounce, i, pattern) + } + for i, label := range k.runLabels { + k.settingsView.SetLabel(core.Run, i, label) + } + for i, pattern := range k.runPatterns { + k.settingsView.SetMacro(core.Run, i, pattern) } } @@ -218,6 +293,14 @@ func (k *Keyer) KeyerSettings() core.KeyerSettings { func (k *Keyer) getKeyerSettings() (core.KeyerSettings, bool) { var keyer core.KeyerSettings keyer.WPM = k.wpm + keyer.SPLabels = make([]string, len(k.spLabels)) + for i := range keyer.SPLabels { + label, ok := k.spLabels[i] + if !ok { + continue + } + keyer.SPLabels[i] = label + } keyer.SPMacros = make([]string, len(k.spPatterns)) for i := range keyer.SPMacros { pattern, ok := k.spPatterns[i] @@ -226,6 +309,14 @@ func (k *Keyer) getKeyerSettings() (core.KeyerSettings, bool) { } keyer.SPMacros[i] = pattern } + keyer.RunLabels = make([]string, len(k.runLabels)) + for i := range keyer.RunLabels { + label, ok := k.runLabels[i] + if !ok { + continue + } + keyer.RunLabels[i] = label + } keyer.RunMacros = make([]string, len(k.runPatterns)) for i := range keyer.RunMacros { pattern, ok := k.runPatterns[i] @@ -248,19 +339,23 @@ func (k *Keyer) SelectPreset(name string) { } } if k.selectedPreset == nil { - k.view.SetPreset("") + k.settingsView.SetPreset("") return } preset := *k.selectedPreset - k.view.SetPreset(preset.Name) + k.settingsView.SetPreset(preset.Name) settings := core.KeyerSettings{ WPM: k.savedSettings.WPM, Preset: name, + SPLabels: make([]string, len(preset.SPLabels)), SPMacros: make([]string, len(preset.SPMacros)), + RunLabels: make([]string, len(preset.RunLabels)), RunMacros: make([]string, len(preset.RunMacros)), } + copy(settings.SPLabels, preset.SPLabels) copy(settings.SPMacros, preset.SPMacros) + copy(settings.RunLabels, preset.RunLabels) copy(settings.RunMacros, preset.RunMacros) k.SetSettings(settings) k.Save() @@ -272,14 +367,66 @@ func (k *Keyer) EnterSpeed(speed int) { k.client.Speed(k.wpm) } +func (k *Keyer) EnterLabel(workmode core.Workmode, index int, text string) { + switch workmode { + case core.SearchPounce: + k.spLabels[index] = text + case core.Run: + k.runLabels[index] = text + } + + if workmode == k.workmode { + k.buttonView.SetLabel(index, text) + } +} + +func (k *Keyer) EnterMacro(workmode core.Workmode, index int, pattern string) { + t, err := template.New("").Parse(pattern) + if err != nil { + k.settingsView.ShowMessage(err) + } else { + k.settingsView.ClearMessage() + } + + switch workmode { + case core.SearchPounce: + k.spPatterns[index] = pattern + k.spTemplates[index] = t + case core.Run: + k.runPatterns[index] = pattern + k.runTemplates[index] = t + } + + if workmode == k.workmode { + k.buttonView.SetPattern(index, pattern) + } + + if k.selectedPreset == nil { + return + } + + presetPattern := "" + switch workmode { + case core.SearchPounce: + presetPattern = k.selectedPreset.SPMacros[index] + case core.Run: + presetPattern = k.selectedPreset.RunMacros[index] + } + + if presetPattern != pattern { + k.selectedPreset = nil + k.settingsView.SetPreset("") + } +} + func (k *Keyer) EnterPattern(index int, pattern string) { (*k.patterns)[index] = pattern var err error (*k.templates)[index], err = template.New("").Parse(pattern) if err != nil { - k.view.ShowMessage(err) + k.buttonView.ShowMessage(err) } else { - k.view.ShowMessage() + k.buttonView.ShowMessage() } if k.selectedPreset == nil { @@ -296,7 +443,7 @@ func (k *Keyer) EnterPattern(index int, pattern string) { if presetPattern != pattern { k.selectedPreset = nil - k.view.SetPreset("") + k.settingsView.SetPreset("") } } @@ -342,7 +489,7 @@ func (k *Keyer) fillins() map[string]string { func (k *Keyer) Send(index int) { message, err := k.GetText(index) if err != nil { - k.view.ShowMessage(err) + k.buttonView.ShowMessage(err) return } k.send(message) diff --git a/core/keyer/keyer_test.go b/core/keyer/keyer_test.go index 0888cfa..843cfba 100644 --- a/core/keyer/keyer_test.go +++ b/core/keyer/keyer_test.go @@ -28,6 +28,7 @@ func TestSend(t *testing.T) { view.On("SetKeyerController", mock.Anything) view.On("ShowMessage", mock.Anything) view.On("SetSpeed", mock.Anything) + view.On("SetLabel", mock.Anything, mock.Anything) view.On("SetPattern", mock.Anything, mock.Anything) view.On("SetPresetNames", mock.Anything) cwClient := new(mocked.CWClient) diff --git a/core/mocked/mocked.go b/core/mocked/mocked.go index 2277ab8..3009f87 100644 --- a/core/mocked/mocked.go +++ b/core/mocked/mocked.go @@ -358,6 +358,10 @@ func (m *KeyerView) Pattern(index int) string { return args.String(0) } +func (m *KeyerView) SetLabel(index int, pattern string) { + m.Called(index, pattern) +} + func (m *KeyerView) SetPattern(index int, pattern string) { m.Called(index, pattern) } diff --git a/core/pb/convert.go b/core/pb/convert.go index 821e65f..61eb2d6 100644 --- a/core/pb/convert.go +++ b/core/pb/convert.go @@ -160,7 +160,9 @@ func ContestToPB(contest core.Contest) Contest { func ToKeyerSettings(pbSettings Keyer) (core.KeyerSettings, error) { var result core.KeyerSettings result.WPM = int(pbSettings.Wpm) + result.SPLabels = pbSettings.SpLabels result.SPMacros = pbSettings.SpMacros + result.RunLabels = pbSettings.RunLabels result.RunMacros = pbSettings.RunMacros return result, nil } @@ -168,7 +170,9 @@ func ToKeyerSettings(pbSettings Keyer) (core.KeyerSettings, error) { func KeyerSettingsToPB(settings core.KeyerSettings) Keyer { return Keyer{ Wpm: int32(settings.WPM), + SpLabels: settings.SPLabels, SpMacros: settings.SPMacros, + RunLabels: settings.RunLabels, RunMacros: settings.RunMacros, } } diff --git a/core/pb/log.pb.go b/core/pb/log.pb.go index a0c1900..f6851ef 100644 --- a/core/pb/log.pb.go +++ b/core/pb/log.pb.go @@ -667,6 +667,8 @@ type Keyer struct { Wpm int32 `protobuf:"varint,1,opt,name=wpm,proto3" json:"wpm,omitempty"` SpMacros []string `protobuf:"bytes,2,rep,name=sp_macros,json=spMacros,proto3" json:"sp_macros,omitempty"` RunMacros []string `protobuf:"bytes,3,rep,name=run_macros,json=runMacros,proto3" json:"run_macros,omitempty"` + SpLabels []string `protobuf:"bytes,4,rep,name=sp_labels,json=spLabels,proto3" json:"sp_labels,omitempty"` + RunLabels []string `protobuf:"bytes,5,rep,name=run_labels,json=runLabels,proto3" json:"run_labels,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -718,6 +720,20 @@ func (m *Keyer) GetRunMacros() []string { return nil } +func (m *Keyer) GetSpLabels() []string { + if m != nil { + return m.SpLabels + } + return nil +} + +func (m *Keyer) GetRunLabels() []string { + if m != nil { + return m.RunLabels + } + return nil +} + func init() { proto.RegisterType((*FileInfo)(nil), "pb.FileInfo") proto.RegisterType((*Entry)(nil), "pb.Entry") @@ -733,70 +749,71 @@ func init() { } var fileDescriptor_a153da538f858886 = []byte{ - // 1035 bytes of a gzipped FileDescriptorProto + // 1056 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x55, 0xdd, 0x6e, 0xdb, 0x36, - 0x14, 0x6e, 0xe2, 0x38, 0x96, 0x8e, 0x13, 0xc7, 0xa1, 0x97, 0x46, 0x4d, 0x3a, 0x34, 0xf5, 0x36, - 0x34, 0x03, 0x06, 0x17, 0xcb, 0x80, 0xfd, 0x5d, 0xb6, 0x68, 0x97, 0x75, 0x48, 0x93, 0x28, 0x59, - 0xb1, 0x61, 0x17, 0x84, 0x2c, 0xd3, 0x8e, 0x30, 0x4a, 0x54, 0x48, 0xba, 0x8d, 0x1e, 0x66, 0x37, - 0x7b, 0x8e, 0x3d, 0xdc, 0x70, 0x0e, 0x49, 0xbb, 0xf1, 0x80, 0xdd, 0x91, 0xdf, 0xf7, 0x1d, 0x92, - 0xe7, 0x97, 0x10, 0x4b, 0x35, 0x1b, 0xd5, 0x5a, 0x59, 0xc5, 0xd6, 0xeb, 0xf1, 0xc1, 0x93, 0x99, - 0x52, 0x33, 0x29, 0x9e, 0x13, 0x32, 0x9e, 0x4f, 0x9f, 0xdb, 0xa2, 0x14, 0xc6, 0x66, 0x65, 0xed, - 0x44, 0xc3, 0xaf, 0x21, 0x7a, 0x5d, 0x48, 0xf1, 0x73, 0x35, 0x55, 0xec, 0x0b, 0xe8, 0x4d, 0x95, - 0x2e, 0x33, 0xcb, 0xdf, 0x0b, 0x6d, 0x0a, 0x55, 0x25, 0x6b, 0x47, 0x6b, 0xc7, 0xed, 0x74, 0xdb, - 0xa1, 0xef, 0x1c, 0x38, 0xfc, 0x7b, 0x0d, 0xda, 0xaf, 0x2a, 0xab, 0x1b, 0x76, 0x08, 0xad, 0x5b, - 0xa3, 0x48, 0xd5, 0x3d, 0xe9, 0x8c, 0xea, 0xf1, 0xe8, 0xf2, 0xea, 0xfc, 0xf4, 0x41, 0x8a, 0x28, - 0x7b, 0x06, 0x1d, 0x63, 0x33, 0x8b, 0xc7, 0xac, 0x93, 0xa0, 0x8b, 0x82, 0x2b, 0x07, 0x9d, 0x3e, - 0x48, 0x03, 0x8b, 0xc2, 0x5c, 0x55, 0x56, 0x18, 0x9b, 0xb4, 0x96, 0xc2, 0x97, 0x0e, 0x42, 0xa1, - 0x67, 0xd9, 0x53, 0x68, 0xff, 0x29, 0x1a, 0xa1, 0x93, 0x0d, 0x92, 0xc5, 0x28, 0xfb, 0x05, 0x81, - 0xd3, 0x07, 0xa9, 0x63, 0x5e, 0x74, 0xa0, 0x2d, 0xf0, 0x69, 0xc3, 0x7f, 0x5a, 0xd0, 0xba, 0xbc, - 0x3a, 0x67, 0x07, 0x10, 0xe5, 0x99, 0x94, 0xa6, 0x98, 0x39, 0x6f, 0xe2, 0x74, 0xb1, 0x67, 0x8f, - 0x21, 0x5e, 0x84, 0x83, 0xde, 0xd8, 0x4a, 0x97, 0x00, 0x63, 0xb0, 0x31, 0xce, 0xaa, 0x09, 0xbd, - 0x29, 0x4e, 0x69, 0x8d, 0x58, 0xa9, 0x26, 0x82, 0x1e, 0x10, 0xa7, 0xb4, 0x66, 0x87, 0x10, 0x97, - 0x0d, 0xd7, 0xa2, 0x56, 0xda, 0x26, 0x6d, 0x77, 0x45, 0xd9, 0xa4, 0xb4, 0xf7, 0x64, 0x35, 0x2f, - 0xc7, 0x42, 0x27, 0x9b, 0x14, 0xcd, 0xa8, 0x6c, 0xde, 0xd2, 0x9e, 0x3d, 0x85, 0x2d, 0x7b, 0x23, - 0x0a, 0x1d, 0x8c, 0x3b, 0x64, 0xdc, 0x25, 0xcc, 0xdb, 0x2f, 0x24, 0xfe, 0x88, 0x88, 0x8e, 0x70, - 0x12, 0x7f, 0xca, 0x67, 0xb0, 0x2d, 0xd5, 0x8c, 0x2f, 0x3d, 0x89, 0xc9, 0x93, 0x2d, 0xa9, 0x66, - 0xd7, 0x0b, 0x67, 0x3e, 0x05, 0x28, 0x1b, 0x7e, 0x97, 0xdf, 0x64, 0xd5, 0x4c, 0x24, 0x40, 0x17, - 0xc5, 0x65, 0xf3, 0x9b, 0x03, 0xf0, 0x0c, 0x77, 0x4d, 0x50, 0x74, 0x49, 0xe1, 0xee, 0x0e, 0xa2, - 0xc7, 0x10, 0x4f, 0xb5, 0xb8, 0x9d, 0x8b, 0x2a, 0x6f, 0x92, 0xad, 0xa3, 0xb5, 0xe3, 0xb5, 0x74, - 0x09, 0xb0, 0x27, 0xd0, 0x2d, 0x1b, 0x2e, 0xc2, 0x01, 0xbd, 0xa3, 0xd6, 0x71, 0x9c, 0x42, 0xd9, - 0xbc, 0xf2, 0x08, 0x56, 0x97, 0xbb, 0x63, 0xa1, 0xd9, 0x21, 0x8d, 0xbb, 0x39, 0xc8, 0xde, 0x6c, - 0x44, 0xdb, 0xfd, 0xde, 0xf0, 0x0f, 0xe8, 0xf8, 0x4a, 0xf9, 0xdf, 0x0c, 0x1e, 0x40, 0xa4, 0x6a, - 0xa1, 0x33, 0xab, 0x34, 0x25, 0x30, 0x4e, 0x17, 0x7b, 0x96, 0x40, 0x47, 0xaa, 0x9c, 0x28, 0x97, - 0xc2, 0xb0, 0x1d, 0xfe, 0x15, 0x43, 0xc7, 0x97, 0x17, 0x66, 0xb4, 0xca, 0x4a, 0xe1, 0x4f, 0xa6, - 0x35, 0xfb, 0x0a, 0x98, 0xa8, 0xac, 0xd0, 0xfc, 0x5e, 0xe8, 0xf1, 0xfc, 0x28, 0xed, 0x13, 0x73, - 0xfd, 0x51, 0xfc, 0x47, 0x30, 0xf8, 0x58, 0x1d, 0x9c, 0x6b, 0x91, 0x7c, 0x77, 0x29, 0x0f, 0x61, - 0x3c, 0x81, 0x3d, 0x0c, 0x5a, 0xa1, 0xc5, 0x8a, 0xc5, 0x06, 0x59, 0x0c, 0x3c, 0x79, 0xcf, 0xe6, - 0x18, 0xfa, 0x99, 0x94, 0xea, 0x03, 0x2f, 0xe7, 0xd2, 0x16, 0x9c, 0xea, 0xb2, 0x4d, 0xf2, 0x1e, - 0xe1, 0x67, 0x08, 0xbf, 0xc0, 0x0a, 0x5d, 0x51, 0x52, 0xb5, 0x6e, 0xae, 0x2a, 0xcf, 0xb0, 0x6e, - 0x47, 0x30, 0x30, 0x59, 0x29, 0x78, 0xae, 0xe6, 0xd8, 0x31, 0xbc, 0x56, 0x45, 0x65, 0x0d, 0x15, - 0x61, 0x3b, 0xdd, 0x45, 0xea, 0xa5, 0x63, 0x2e, 0x88, 0xc0, 0x77, 0x7b, 0x7d, 0x65, 0x8b, 0x4a, - 0x54, 0x36, 0x58, 0xb8, 0x9a, 0x1c, 0x38, 0x0b, 0xcf, 0x79, 0x9b, 0x6f, 0x61, 0xdf, 0xd4, 0x22, - 0x2f, 0xa6, 0x45, 0xbe, 0x7a, 0x4f, 0x4c, 0x56, 0x7b, 0x81, 0xbe, 0x7f, 0xd7, 0x8f, 0xf0, 0xe8, - 0xbf, 0x76, 0x5a, 0x4c, 0x8b, 0x3b, 0x61, 0x12, 0xa0, 0xb2, 0xd9, 0x5f, 0xb5, 0xf4, 0x34, 0xb6, - 0x8c, 0xb2, 0x37, 0x42, 0x87, 0x8b, 0xba, 0xae, 0x65, 0x08, 0xf3, 0xc7, 0x0f, 0x61, 0x93, 0xc2, - 0x63, 0xa8, 0x8c, 0xbb, 0x27, 0x80, 0x93, 0x84, 0x22, 0x63, 0x52, 0xcf, 0xa0, 0xbb, 0x3e, 0x31, - 0x3e, 0x94, 0x75, 0x66, 0xad, 0xd0, 0x55, 0xb2, 0x4d, 0x95, 0x32, 0xf0, 0x24, 0x59, 0x5d, 0x38, - 0x8a, 0x7d, 0x0e, 0x3d, 0x7a, 0x2d, 0xaf, 0x85, 0x76, 0x49, 0xea, 0x51, 0xe8, 0xb7, 0x08, 0xbd, - 0x10, 0x9a, 0x52, 0x74, 0x02, 0x7b, 0x79, 0x36, 0xd6, 0x85, 0x94, 0x8a, 0xdf, 0x1a, 0xc5, 0xad, - 0x28, 0x6b, 0x99, 0x59, 0xec, 0x07, 0x3a, 0x39, 0x90, 0x97, 0x46, 0x5d, 0x7b, 0xca, 0xd9, 0x48, - 0xc9, 0x6f, 0x0a, 0x63, 0x95, 0x6e, 0xf8, 0xb4, 0x90, 0x82, 0xea, 0xb6, 0x1f, 0x6c, 0xa4, 0x3c, - 0x75, 0xdc, 0x6b, 0x4f, 0xb1, 0x67, 0xb0, 0x33, 0x11, 0xd3, 0xa2, 0x2a, 0xb0, 0x8d, 0x78, 0x93, - 0x95, 0x32, 0x61, 0xa4, 0xee, 0x2d, 0xe1, 0xdf, 0xb3, 0x52, 0xa2, 0x30, 0xf4, 0x24, 0x7f, 0x9f, - 0xc9, 0xb9, 0x30, 0xc9, 0x80, 0x62, 0xdc, 0x0b, 0xf0, 0x3b, 0x42, 0xd9, 0xf7, 0x90, 0xcc, 0x44, - 0x85, 0xfd, 0x25, 0xb8, 0x11, 0xba, 0xc8, 0xe4, 0xb2, 0x99, 0x3f, 0x21, 0x4f, 0x1f, 0x06, 0xfe, - 0x8a, 0xe8, 0x45, 0xf3, 0x7f, 0x07, 0xc9, 0xca, 0xfb, 0x85, 0x9c, 0x70, 0x7c, 0xa6, 0x49, 0xf6, - 0xe8, 0xae, 0xbd, 0x7b, 0x2e, 0x08, 0x39, 0x79, 0x8b, 0x24, 0x0e, 0xd0, 0x5b, 0xa3, 0x0c, 0x9f, - 0xa9, 0x4c, 0x26, 0x0f, 0xdd, 0x00, 0x45, 0xe0, 0x27, 0x95, 0x49, 0x9c, 0x39, 0x2e, 0xc9, 0x8e, - 0xde, 0x27, 0x1a, 0x1c, 0x14, 0x04, 0x2e, 0x9d, 0x4e, 0x90, 0x38, 0x81, 0x83, 0x48, 0xf0, 0x25, - 0xf4, 0x4d, 0xad, 0x8b, 0xca, 0x72, 0x37, 0x37, 0xf0, 0xb7, 0x7a, 0x44, 0x9e, 0xec, 0x38, 0xfc, - 0x3c, 0xc0, 0xec, 0x07, 0x00, 0x63, 0x33, 0x6d, 0x69, 0xd2, 0x26, 0x07, 0x54, 0x38, 0x07, 0x23, - 0xf7, 0xbf, 0x8e, 0xc2, 0xff, 0x3a, 0x5a, 0x8c, 0xdc, 0x34, 0x26, 0x35, 0xee, 0x31, 0xc0, 0x8b, - 0xb8, 0xf9, 0x59, 0x7f, 0xe8, 0x7a, 0x32, 0xc0, 0x6e, 0xdc, 0xbf, 0xd9, 0x88, 0x76, 0xfb, 0x6c, - 0x78, 0x0a, 0x9b, 0xae, 0x18, 0x71, 0x3a, 0x4d, 0xee, 0xf2, 0x9c, 0xa6, 0x53, 0x94, 0xd2, 0x9a, - 0xf5, 0xa1, 0xf5, 0xa1, 0xbe, 0xf3, 0xe3, 0x08, 0x97, 0x38, 0xe9, 0xee, 0x4f, 0x9d, 0xb0, 0x1d, - 0xfe, 0x0a, 0x6d, 0xfa, 0x20, 0x9d, 0x51, 0xe9, 0xff, 0x73, 0x5c, 0x62, 0x60, 0x4d, 0xcd, 0xcb, - 0x2c, 0xd7, 0xca, 0x24, 0xeb, 0x94, 0x82, 0xc8, 0xd4, 0x67, 0xb4, 0xc7, 0xef, 0x42, 0xcf, 0xab, - 0xc0, 0xb6, 0x88, 0x8d, 0xf5, 0xbc, 0x72, 0xf4, 0x78, 0x93, 0xdc, 0xfd, 0xe6, 0xdf, 0x00, 0x00, - 0x00, 0xff, 0xff, 0x34, 0xd1, 0x7d, 0xc1, 0x6d, 0x08, 0x00, 0x00, + 0x14, 0x8e, 0x63, 0x3b, 0xb6, 0x8e, 0x13, 0xc7, 0xa1, 0x97, 0x46, 0x4d, 0x3a, 0x34, 0xf5, 0x36, + 0x34, 0x03, 0x06, 0x17, 0xcb, 0x80, 0xfd, 0x5d, 0xb6, 0x68, 0x97, 0x75, 0x4b, 0x93, 0x28, 0x41, + 0xb1, 0x61, 0x17, 0x04, 0x2d, 0xd3, 0x8e, 0x30, 0x4a, 0x54, 0x48, 0xba, 0x8d, 0x1e, 0x63, 0x0f, + 0xb0, 0x9b, 0x3d, 0xc7, 0x1e, 0x6e, 0xe0, 0x21, 0x29, 0xd7, 0x1e, 0xd0, 0x3b, 0xf2, 0xfb, 0xe1, + 0xcf, 0xe1, 0x39, 0x87, 0x10, 0x09, 0x39, 0x1f, 0x97, 0x4a, 0x1a, 0x49, 0x36, 0xcb, 0xc9, 0xe1, + 0xe3, 0xb9, 0x94, 0x73, 0xc1, 0x9f, 0x21, 0x32, 0x59, 0xcc, 0x9e, 0x99, 0x2c, 0xe7, 0xda, 0xb0, + 0xbc, 0x74, 0xa2, 0xd1, 0xd7, 0xd0, 0x7d, 0x95, 0x09, 0xfe, 0x73, 0x31, 0x93, 0xe4, 0x0b, 0xe8, + 0xcf, 0xa4, 0xca, 0x99, 0xa1, 0xef, 0xb8, 0xd2, 0x99, 0x2c, 0xe2, 0xc6, 0x71, 0xe3, 0xa4, 0x9d, + 0xec, 0x38, 0xf4, 0xad, 0x03, 0x47, 0xff, 0x34, 0xa0, 0xfd, 0xb2, 0x30, 0xaa, 0x22, 0x47, 0xd0, + 0xbc, 0xd3, 0x12, 0x55, 0xbd, 0xd3, 0xce, 0xb8, 0x9c, 0x8c, 0xaf, 0xae, 0x2f, 0xce, 0x36, 0x12, + 0x8b, 0x92, 0xa7, 0xd0, 0xd1, 0x86, 0x19, 0xbb, 0xcc, 0x26, 0x0a, 0x7a, 0x56, 0x70, 0xed, 0xa0, + 0xb3, 0x8d, 0x24, 0xb0, 0x56, 0x98, 0xca, 0xc2, 0x70, 0x6d, 0xe2, 0xe6, 0x52, 0xf8, 0xc2, 0x41, + 0x56, 0xe8, 0x59, 0xf2, 0x04, 0xda, 0x7f, 0xf2, 0x8a, 0xab, 0xb8, 0x85, 0xb2, 0xc8, 0xca, 0x7e, + 0xb1, 0xc0, 0xd9, 0x46, 0xe2, 0x98, 0xe7, 0x1d, 0x68, 0x73, 0x7b, 0xb4, 0xd1, 0xbf, 0x4d, 0x68, + 0x5e, 0x5d, 0x5f, 0x90, 0x43, 0xe8, 0xa6, 0x4c, 0x08, 0x9d, 0xcd, 0xdd, 0x6d, 0xa2, 0xa4, 0x9e, + 0x93, 0x47, 0x10, 0xd5, 0xe1, 0xc0, 0x33, 0x36, 0x93, 0x25, 0x40, 0x08, 0xb4, 0x26, 0xac, 0x98, + 0xe2, 0x99, 0xa2, 0x04, 0xc7, 0x16, 0xcb, 0xe5, 0x94, 0xe3, 0x01, 0xa2, 0x04, 0xc7, 0xe4, 0x08, + 0xa2, 0xbc, 0xa2, 0x8a, 0x97, 0x52, 0x99, 0xb8, 0xed, 0xb6, 0xc8, 0xab, 0x04, 0xe7, 0x9e, 0x2c, + 0x16, 0xf9, 0x84, 0xab, 0x78, 0x0b, 0xa3, 0xd9, 0xcd, 0xab, 0x37, 0x38, 0x27, 0x4f, 0x60, 0xdb, + 0xdc, 0xf2, 0x4c, 0x05, 0x73, 0x07, 0xcd, 0x3d, 0xc4, 0xbc, 0xbf, 0x96, 0xf8, 0x25, 0xba, 0xb8, + 0x84, 0x93, 0xf8, 0x55, 0x3e, 0x83, 0x1d, 0x21, 0xe7, 0x74, 0x79, 0x93, 0x08, 0x6f, 0xb2, 0x2d, + 0xe4, 0xfc, 0xa6, 0xbe, 0xcc, 0xa7, 0x00, 0x79, 0x45, 0xef, 0xd3, 0x5b, 0x56, 0xcc, 0x79, 0x0c, + 0xb8, 0x51, 0x94, 0x57, 0xbf, 0x39, 0xc0, 0xae, 0xe1, 0xb6, 0x09, 0x8a, 0x1e, 0x2a, 0xdc, 0xde, + 0x41, 0xf4, 0x08, 0xa2, 0x99, 0xe2, 0x77, 0x0b, 0x5e, 0xa4, 0x55, 0xbc, 0x7d, 0xdc, 0x38, 0x69, + 0x24, 0x4b, 0x80, 0x3c, 0x86, 0x5e, 0x5e, 0x51, 0x1e, 0x16, 0xe8, 0x1f, 0x37, 0x4f, 0xa2, 0x04, + 0xf2, 0xea, 0xa5, 0x47, 0x6c, 0x76, 0xb9, 0x3d, 0x6a, 0xcd, 0x2e, 0x6a, 0xdc, 0xce, 0x41, 0xf6, + 0xba, 0xd5, 0xdd, 0x19, 0xf4, 0x47, 0x7f, 0x40, 0xc7, 0x67, 0xca, 0x47, 0x5f, 0xf0, 0x10, 0xba, + 0xb2, 0xe4, 0x8a, 0x19, 0xa9, 0xf0, 0x01, 0xa3, 0xa4, 0x9e, 0x93, 0x18, 0x3a, 0x42, 0xa6, 0x48, + 0xb9, 0x27, 0x0c, 0xd3, 0xd1, 0xdf, 0x11, 0x74, 0x7c, 0x7a, 0xd9, 0x17, 0x2d, 0x58, 0xce, 0xfd, + 0xca, 0x38, 0x26, 0x5f, 0x01, 0xe1, 0x85, 0xe1, 0x8a, 0xae, 0x84, 0xde, 0xae, 0xdf, 0x4d, 0x06, + 0xc8, 0xdc, 0x7c, 0x10, 0xff, 0x31, 0x0c, 0x3f, 0x54, 0x87, 0xcb, 0x35, 0x51, 0xbe, 0xb7, 0x94, + 0x87, 0x30, 0x9e, 0xc2, 0xbe, 0x0d, 0x5a, 0xa6, 0xf8, 0x9a, 0xa3, 0x85, 0x8e, 0xa1, 0x27, 0x57, + 0x3c, 0x27, 0x30, 0x60, 0x42, 0xc8, 0xf7, 0x34, 0x5f, 0x08, 0x93, 0x51, 0xcc, 0xcb, 0x36, 0xca, + 0xfb, 0x88, 0x9f, 0x5b, 0xf8, 0xb9, 0xcd, 0xd0, 0x35, 0x25, 0x66, 0xeb, 0xd6, 0xba, 0xf2, 0xdc, + 0xe6, 0xed, 0x18, 0x86, 0x9a, 0xe5, 0x9c, 0xa6, 0x72, 0x61, 0x2b, 0x86, 0x96, 0x32, 0x2b, 0x8c, + 0xc6, 0x24, 0x6c, 0x27, 0x7b, 0x96, 0x7a, 0xe1, 0x98, 0x4b, 0x24, 0xec, 0xb9, 0xbd, 0xbe, 0x30, + 0x59, 0xc1, 0x0b, 0x13, 0x1c, 0x2e, 0x27, 0x87, 0xce, 0xe1, 0x39, 0xef, 0xf9, 0x16, 0x0e, 0x74, + 0xc9, 0xd3, 0x6c, 0x96, 0xa5, 0xeb, 0xfb, 0x44, 0xe8, 0xda, 0x0f, 0xf4, 0xea, 0x5e, 0x3f, 0xc2, + 0xc3, 0xff, 0xfb, 0x14, 0x9f, 0x65, 0xf7, 0x5c, 0xc7, 0x80, 0x69, 0x73, 0xb0, 0xee, 0xf4, 0xb4, + 0x2d, 0x19, 0x69, 0x6e, 0xb9, 0x0a, 0x1b, 0xf5, 0x5c, 0xc9, 0x20, 0xe6, 0x97, 0x1f, 0xc1, 0x16, + 0x86, 0x47, 0x63, 0x1a, 0xf7, 0x4e, 0xc1, 0x76, 0x12, 0x8c, 0x8c, 0x4e, 0x3c, 0x63, 0xaf, 0xeb, + 0x1f, 0xc6, 0x87, 0xb2, 0x64, 0xc6, 0x70, 0x55, 0xc4, 0x3b, 0x98, 0x29, 0x43, 0x4f, 0xa2, 0xeb, + 0xd2, 0x51, 0xe4, 0x73, 0xe8, 0xe3, 0x69, 0x69, 0xc9, 0x95, 0x7b, 0xa4, 0x3e, 0x86, 0x7e, 0x1b, + 0xd1, 0x4b, 0xae, 0xf0, 0x89, 0x4e, 0x61, 0x3f, 0x65, 0x13, 0x95, 0x09, 0x21, 0xe9, 0x9d, 0x96, + 0xd4, 0xf0, 0xbc, 0x14, 0xcc, 0xd8, 0x7a, 0xc0, 0x95, 0x03, 0x79, 0xa5, 0xe5, 0x8d, 0xa7, 0x9c, + 0x47, 0x08, 0x7a, 0x9b, 0x69, 0x23, 0x55, 0x45, 0x67, 0x99, 0xe0, 0x98, 0xb7, 0x83, 0xe0, 0x11, + 0xe2, 0xcc, 0x71, 0xaf, 0x3c, 0x45, 0x9e, 0xc2, 0xee, 0x94, 0xcf, 0xb2, 0x22, 0xb3, 0x65, 0x44, + 0x2b, 0x96, 0x8b, 0x98, 0xa0, 0xba, 0xbf, 0x84, 0x7f, 0x67, 0xb9, 0xb0, 0xc2, 0x50, 0x93, 0xf4, + 0x1d, 0x13, 0x0b, 0xae, 0xe3, 0x21, 0xc6, 0xb8, 0x1f, 0xe0, 0xb7, 0x88, 0x92, 0xef, 0x21, 0x9e, + 0xf3, 0xc2, 0xd6, 0x17, 0xa7, 0x9a, 0xab, 0x8c, 0x89, 0x65, 0x31, 0x7f, 0x82, 0x37, 0x7d, 0x10, + 0xf8, 0x6b, 0xa4, 0xeb, 0xe2, 0xff, 0x0e, 0xe2, 0xb5, 0xf3, 0x73, 0x31, 0xa5, 0xf6, 0x98, 0x3a, + 0xde, 0xc7, 0xbd, 0xf6, 0x57, 0xae, 0xc0, 0xc5, 0xf4, 0x8d, 0x25, 0x6d, 0x03, 0xbd, 0xd3, 0x52, + 0xd3, 0xb9, 0x64, 0x22, 0x7e, 0xe0, 0x1a, 0xa8, 0x05, 0x7e, 0x92, 0x4c, 0xd8, 0x9e, 0xe3, 0x1e, + 0xd9, 0xd1, 0x07, 0x48, 0x83, 0x83, 0x82, 0xc0, 0x3d, 0xa7, 0x13, 0xc4, 0x4e, 0xe0, 0x20, 0x14, + 0x7c, 0x09, 0x03, 0x5d, 0xaa, 0xac, 0x30, 0xd4, 0xf5, 0x0d, 0xfb, 0x5b, 0x3d, 0xc4, 0x9b, 0xec, + 0x3a, 0xfc, 0x22, 0xc0, 0xe4, 0x07, 0x00, 0x6d, 0x98, 0x32, 0xd8, 0x69, 0xe3, 0x43, 0x4c, 0x9c, + 0xc3, 0xb1, 0xfb, 0x5f, 0xc7, 0xe1, 0x7f, 0x1d, 0xd7, 0x2d, 0x37, 0x89, 0x50, 0x6d, 0xe7, 0x36, + 0xc0, 0x75, 0xdc, 0x7c, 0xaf, 0x3f, 0x72, 0x35, 0x19, 0x60, 0xd7, 0xee, 0x5f, 0xb7, 0xba, 0x7b, + 0x03, 0x32, 0x3a, 0x83, 0x2d, 0x97, 0x8c, 0xb6, 0x3b, 0x4d, 0xef, 0xd3, 0x14, 0xbb, 0x53, 0x37, + 0xc1, 0x31, 0x19, 0x40, 0xf3, 0x7d, 0x79, 0xef, 0xdb, 0x91, 0x1d, 0xda, 0x4e, 0xb7, 0xda, 0x75, + 0xc2, 0x74, 0xf4, 0x57, 0x03, 0xda, 0xf8, 0x43, 0x3a, 0x57, 0xee, 0x3f, 0x74, 0x3b, 0xb4, 0x91, + 0xd5, 0x25, 0xcd, 0x59, 0xaa, 0xa4, 0x8e, 0x37, 0xf1, 0x0d, 0xba, 0xba, 0x3c, 0xc7, 0xb9, 0xfd, + 0x2f, 0xd4, 0xa2, 0x08, 0x6c, 0x13, 0xd9, 0x48, 0x2d, 0x0a, 0x4f, 0x3b, 0xaf, 0x60, 0x13, 0x2e, + 0x74, 0xdc, 0x0a, 0xde, 0x5f, 0x71, 0x1e, 0xbc, 0x9e, 0x6d, 0xd7, 0x5e, 0x47, 0x4f, 0xb6, 0x30, + 0x56, 0xdf, 0xfc, 0x17, 0x00, 0x00, 0xff, 0xff, 0x20, 0x33, 0x08, 0xe4, 0xaa, 0x08, 0x00, 0x00, } diff --git a/core/pb/log.proto b/core/pb/log.proto index 62c15cc..cd8a46f 100644 --- a/core/pb/log.proto +++ b/core/pb/log.proto @@ -81,4 +81,6 @@ message Keyer { int32 wpm = 1; repeated string sp_macros = 2; repeated string run_macros = 3; + repeated string sp_labels = 4; + repeated string run_labels = 5; } diff --git a/ui/app.go b/ui/app.go index 0b1a0c0..1d8f55e 100644 --- a/ui/app.go +++ b/ui/app.go @@ -42,17 +42,18 @@ type application struct { version string sponsors string - app *gtk.Application - builder *gtk.Builder - style *style.Style - windowGeometry *gmtry.Geometry - mainWindow *mainWindow - callinfoWindow *callinfoWindow - scoreWindow *scoreWindow - rateWindow *rateWindow - spotsWindow *spotsWindow - newContestDialog *newContestDialog - settingsDialog *settingsDialog + app *gtk.Application + builder *gtk.Builder + style *style.Style + windowGeometry *gmtry.Geometry + mainWindow *mainWindow + callinfoWindow *callinfoWindow + scoreWindow *scoreWindow + rateWindow *rateWindow + spotsWindow *spotsWindow + newContestDialog *newContestDialog + settingsDialog *settingsDialog + keyerSettingsDialog *keyerSettingsDialog controller *app.Controller } @@ -95,6 +96,7 @@ func (a *application) activate() { a.rateWindow = setupRateWindow(a.windowGeometry, a.style) a.spotsWindow = setupSpotsWindow(a.windowGeometry, a.style, a.controller.Bandmap) a.settingsDialog = setupSettingsDialog(a.mainWindow.window, a.controller.Settings) + a.keyerSettingsDialog = setupKeyerSettingsDialog(a.mainWindow.window, a.controller.Keyer) a.newContestDialog = setupNewContestDialog(a.mainWindow.window, a.controller.NewContest) a.mainWindow.SetMainMenuController(a.controller) @@ -113,6 +115,7 @@ func (a *application) activate() { a.controller.Workmode.Notify(a.mainWindow) a.controller.Radio.SetView(a.mainWindow) a.controller.Keyer.SetView(a.mainWindow) + a.controller.Keyer.SetSettingsView(a.keyerSettingsDialog) a.controller.ServiceStatus.Notify(a.mainWindow) a.controller.Callinfo.SetView(a.callinfoWindow) a.controller.Score.SetView(a.scoreWindow) diff --git a/ui/glade/contest.glade b/ui/glade/contest.glade index 7a5cf27..71d8e85 100644 --- a/ui/glade/contest.glade +++ b/ui/glade/contest.glade @@ -163,6 +163,548 @@ THE SOFTWARE. + + False + Keyer Macros + dialog + + + False + vertical + 2 + + + False + end + + + + + + _Close + True + True + True + True + + + True + True + 1 + + + + + False + False + 0 + + + + + + True + False + + + True + False + start + center + 5 + 5 + False + False + S & P + + + 0 + 4 + 3 + + + + + True + False + center + center + False + False + Label + + + 1 + 3 + + + + + True + False + Macro + + + 2 + 3 + + + + + True + False + center + center + 5 + 5 + False + False + F1 + + + 0 + 5 + + + + + True + False + center + center + 5 + 5 + False + False + F2 + + + 0 + 6 + + + + + True + False + center + center + 5 + 5 + False + False + F3 + + + 0 + 7 + + + + + True + False + center + center + 5 + 5 + False + False + F4 + + + 0 + 8 + + + + + True + False + start + center + 5 + 5 + False + False + Run + + + 0 + 9 + 3 + + + + + True + False + center + center + 5 + 5 + False + False + F1 + + + 0 + 10 + + + + + True + False + center + center + 5 + 5 + False + False + F2 + + + 0 + 11 + + + + + True + False + center + center + 5 + 5 + False + False + F3 + + + 0 + 12 + + + + + True + False + center + center + 5 + 5 + False + False + F4 + + + 0 + 13 + + + + + spF1Label + True + True + start + False + False + 10 + + + 1 + 5 + + + + + True + True + start + False + False + 10 + + + 1 + 6 + + + + + spF1Label + True + True + start + False + False + 10 + + + 1 + 7 + + + + + spF1Label + True + True + start + False + False + 10 + + + 1 + 8 + + + + + spF1Label + True + True + start + False + False + 10 + + + 1 + 10 + + + + + spF1Label + True + True + start + False + False + 10 + + + 1 + 11 + + + + + spF1Label + True + True + start + False + False + 10 + + + 1 + 12 + + + + + spF1Label + True + True + start + False + False + 10 + + + 1 + 13 + + + + + True + True + True + True + + + 2 + 5 + + + + + True + True + True + True + + + 2 + 6 + + + + + True + True + True + True + + + 2 + 7 + + + + + True + True + True + True + + + 2 + 8 + + + + + True + True + True + True + + + 2 + 10 + + + + + True + True + True + True + + + 2 + 11 + + + + + True + True + True + True + + + 2 + 12 + + + + + True + True + True + True + + + 2 + 13 + + + + + True + False + 5 + + + 0 + 3 + + + + + True + False + start + center + False + False + + + 0 + 1 + 3 + + + + + True + False + True + False + <b>Templates:</b> + +{{.MyCall}} the station callsign +{{.MyReport}} my report +{{.MyNumber}} my QSO number (t = 0, n = 9) +{{.MyXchange}} all exchange fields concatenated, except report and number +{{.MyExchange}} all exchange fields concatenated, including report and number +{{.TheirCall}} their callsign + +For more details see <a href="https://github.com/ftl/hellocontest/wiki/Main-Window#cw-macros">the wiki</a>. + True + True + True + + + 0 + 0 + 3 + + + + + True + False + 5 + 5 + Preset: + + + 0 + 2 + + + + + True + False + True + + + 1 + 2 + 2 + + + + + False + True + 1 + + + + + False New Contest @@ -1745,7 +2287,7 @@ THE SOFTWARE. - + True False @@ -1753,6 +2295,7 @@ THE SOFTWARE. 4 5 5 + True 2 5 True @@ -1763,11 +2306,12 @@ THE SOFTWARE. False True Send Marcro (F1) + True - 0 - 1 + 1 + 0 @@ -1777,11 +2321,12 @@ THE SOFTWARE. False True Send Marcro (F2) + True - 0 - 2 + 2 + 0 @@ -1791,11 +2336,12 @@ THE SOFTWARE. False True Send Marcro (F3) + True - 0 - 3 + 3 + 0 @@ -1805,64 +2351,17 @@ THE SOFTWARE. False True Send Marcro (F5) - - - - 0 - 4 - - - - - True - True - True - - - 1 - 1 - 4 - - - - - True - True - True - - - 1 - 2 - 4 - - - - - True - True - True - - - 1 - 3 - 4 - - - - - True - True True + - 1 - 4 - 4 + 4 + 0 - ESC + ESC: Stop TX stopButton True True @@ -1871,93 +2370,80 @@ THE SOFTWARE. 0 - 5 - - - - - True - False - start - False - wpm - - - 4 - 5 - - - - - True - False - end - True - Speed: - right - - - 2 - 5 + 0 - + + Macros... True True - CW Speed (Ctrl-s) - center - False - 2 - 2 - 2 - digits - speedAdjustment - True - - - - 3 - 5 - - - - - True - False - start - 2 - True - Stop TX - 0 - - - 1 - 5 - - - - - True - False - start - False - Preset: + True + True 0 - 0 + 1 - + True False - True + + + True + False + end + True + Speed: + right + + + False + True + 0 + + + + + True + True + CW Speed (Ctrl-s) + center + False + 2 + 2 + 2 + digits + speedAdjustment + True + + + + False + True + 1 + + + + + True + False + start + False + wpm + + + False + True + 2 + + 1 - 0 + 1 4 diff --git a/ui/keyerView.go b/ui/keyerButtonView.go similarity index 58% rename from ui/keyerView.go rename to ui/keyerButtonView.go index e1cb958..e0d9bcb 100644 --- a/ui/keyerView.go +++ b/ui/keyerButtonView.go @@ -12,20 +12,18 @@ import ( type KeyerController interface { Send(int) Stop() - EnterPattern(int, string) EnterSpeed(int) Save() - SelectPreset(string) + OpenKeyerSettings() } type keyerView struct { controller KeyerController - buttons []*gtk.Button - entries []*gtk.Entry - stopButton *gtk.Button - speedEntry *gtk.SpinButton - presetCombo *gtk.ComboBoxText + buttons []*gtk.Button + stopButton *gtk.Button + openKeyerSettingsButton *gtk.Button + speedEntry *gtk.SpinButton ignoreChangedEvent bool } @@ -34,25 +32,21 @@ func setupKeyerView(builder *gtk.Builder) *keyerView { result := new(keyerView) result.buttons = make([]*gtk.Button, 4) - result.entries = make([]*gtk.Entry, 4) for i := 0; i < len(result.buttons); i++ { result.buttons[i] = getUI(builder, fmt.Sprintf("f%dButton", i+1)).(*gtk.Button) - result.entries[i] = getUI(builder, fmt.Sprintf("f%dEntry", i+1)).(*gtk.Entry) result.buttons[i].Connect("clicked", result.onButton(i)) - result.entries[i].Connect("changed", result.onEntryChanged(i)) - result.entries[i].Connect("focus_out_event", result.onEntryFocusOut) } result.stopButton = getUI(builder, "stopButton").(*gtk.Button) result.stopButton.Connect("clicked", result.onStop) + result.openKeyerSettingsButton = getUI(builder, "openKeyerSettingsButton").(*gtk.Button) + result.openKeyerSettingsButton.Connect("clicked", result.onOpenKeyerSettings) + result.speedEntry = getUI(builder, "speedEntry").(*gtk.SpinButton) result.speedEntry.Connect("value-changed", result.onSpeedChanged) result.speedEntry.Connect("focus_out_event", result.onEntryFocusOut) - result.presetCombo = getUI(builder, "keyerPresetComboBoxText").(*gtk.ComboBoxText) - result.presetCombo.Connect("changed", result.onPresetChanged) - return result } @@ -84,23 +78,13 @@ func (k *keyerView) onButton(index int) func(button *gtk.Button) bool { } } -func (k *keyerView) onEntryChanged(index int) func(entry *gtk.Entry) bool { - return func(entry *gtk.Entry) bool { - if k.ignoreChangedEvent { - return false - } - if k.controller == nil { - log.Println("onEntryChanged: no keyer controller") - return false - } - text, err := entry.GetText() - if err != nil { - log.Println(err) - return false - } - k.controller.EnterPattern(index, text) +func (k *keyerView) onOpenKeyerSettings(button *gtk.Button) bool { + if k.controller == nil { + log.Println("onOpenKeyerSettings: no keyer controller") return false } + k.controller.OpenKeyerSettings() + return true } func (k *keyerView) onStop(button *gtk.Button) bool { @@ -125,30 +109,22 @@ func (k *keyerView) onSpeedChanged(button *gtk.SpinButton) bool { return true } -func (k *keyerView) onPresetChanged(combo *gtk.ComboBoxText) bool { - if k.ignoreChangedEvent { - return false - } - if k.controller == nil { - log.Println("onPresetChanged: no keyer controller") - return false - } - - k.controller.SelectPreset(combo.GetActiveText()) - return true -} - func (k *keyerView) SetKeyerController(controller KeyerController) { k.controller = controller } -func (k *keyerView) Pattern(index int) string { - text, _ := k.entries[index].GetText() - return text +func (k *keyerView) SetLabel(index int, text string) { + var label string + if text == "" { + label = fmt.Sprintf("F%d", index+1) + } else { + label = fmt.Sprintf("F%d: %s", index+1, text) + } + k.buttons[index].SetLabel(label) } func (k *keyerView) SetPattern(index int, text string) { - k.entries[index].SetText(text) + k.buttons[index].SetTooltipText(fmt.Sprintf("F%d: %s", index+1, text)) } func (k *keyerView) Speed() int { @@ -158,19 +134,3 @@ func (k *keyerView) Speed() int { func (k *keyerView) SetSpeed(speed int) { k.speedEntry.SetValue(float64(speed)) } - -func (k *keyerView) SetPresetNames(names []string) { - k.doIgnoreChanges(func() { - k.presetCombo.RemoveAll() - k.presetCombo.Append("", "") - for _, name := range names { - k.presetCombo.Append(name, name) - } - }) -} - -func (k *keyerView) SetPreset(name string) { - k.doIgnoreChanges(func() { - k.presetCombo.SetActiveID(name) - }) -} diff --git a/ui/keyerSettingsDialog.go b/ui/keyerSettingsDialog.go new file mode 100644 index 0000000..e2cc5a2 --- /dev/null +++ b/ui/keyerSettingsDialog.go @@ -0,0 +1,81 @@ +package ui + +import ( + "github.com/ftl/hellocontest/core" + "github.com/gotk3/gotk3/gtk" +) + +type keyerSettingsDialog struct { + dialog *gtk.Dialog + parent gtk.IWidget + + controller KeyerSettingsController + *keyerSettingsView +} + +func setupKeyerSettingsDialog(parent gtk.IWidget, controller KeyerSettingsController) *keyerSettingsDialog { + result := &keyerSettingsDialog{ + parent: parent, + controller: controller, + } + return result +} + +func (d *keyerSettingsDialog) onDestroy() { + d.dialog = nil + d.keyerSettingsView = nil +} + +func (d *keyerSettingsDialog) Show() { + if d.dialog == nil { + builder := setupBuilder() + d.dialog = getUI(builder, "keyerSettingsDialog").(*gtk.Dialog) + d.dialog.SetPosition(gtk.WIN_POS_CENTER) + d.dialog.Connect("destroy", d.onDestroy) + d.keyerSettingsView = setupKeyerSettingsView(builder, d.dialog, d.controller) + } + d.dialog.ShowAll() + d.dialog.Present() +} + +func (d *keyerSettingsDialog) ShowMessage(message ...any) { + if d.keyerSettingsView == nil { + return + } + d.keyerSettingsView.ShowMessage(message...) +} + +func (d *keyerSettingsDialog) ClearMessage() { + if d.keyerSettingsView == nil { + return + } + d.keyerSettingsView.ClearMessage() +} + +func (d *keyerSettingsDialog) SetLabel(workmode core.Workmode, index int, text string) { + if d.keyerSettingsView == nil { + return + } + d.keyerSettingsView.SetLabel(workmode, index, text) +} + +func (d *keyerSettingsDialog) SetMacro(workmode core.Workmode, index int, text string) { + if d.keyerSettingsView == nil { + return + } + d.keyerSettingsView.SetMacro(workmode, index, text) +} + +func (d *keyerSettingsDialog) SetPresetNames(names []string) { + if d.keyerSettingsView == nil { + return + } + d.keyerSettingsView.SetPresetNames(names) +} + +func (d *keyerSettingsDialog) SetPreset(name string) { + if d.keyerSettingsView == nil { + return + } + d.keyerSettingsView.SetPreset(name) +} diff --git a/ui/keyerSettingsView.go b/ui/keyerSettingsView.go new file mode 100644 index 0000000..b7ef876 --- /dev/null +++ b/ui/keyerSettingsView.go @@ -0,0 +1,182 @@ +package ui + +import ( + "fmt" + "log" + + "github.com/gotk3/gotk3/gdk" + "github.com/gotk3/gotk3/gtk" + + "github.com/ftl/hellocontest/core" +) + +type KeyerSettingsController interface { + EnterLabel(core.Workmode, int, string) + EnterMacro(core.Workmode, int, string) + SelectPreset(string) + Save() +} + +type keyerSettingsView struct { + parent *gtk.Dialog + controller KeyerSettingsController + + spLabels []*gtk.Entry + spMacros []*gtk.Entry + runLabels []*gtk.Entry + runMacros []*gtk.Entry + labels [][]*gtk.Entry + macros [][]*gtk.Entry + presetCombo *gtk.ComboBoxText + messageLabel *gtk.Label + close *gtk.Button + + ignoreChangedEvent bool +} + +func setupKeyerSettingsView(builder *gtk.Builder, parent *gtk.Dialog, controller KeyerSettingsController) *keyerSettingsView { + result := new(keyerSettingsView) + result.parent = parent + result.controller = controller + + const macroCount = 4 + result.spLabels = make([]*gtk.Entry, macroCount) + result.spMacros = make([]*gtk.Entry, macroCount) + result.runLabels = make([]*gtk.Entry, macroCount) + result.runMacros = make([]*gtk.Entry, macroCount) + for i := 0; i < macroCount; i++ { + result.spLabels[i] = result.setupEntry(builder, "spF%dLabel", core.SearchPounce, i, result.onLabelChanged) + result.spMacros[i] = result.setupEntry(builder, "spF%dMacro", core.SearchPounce, i, result.onMacroChanged) + result.runLabels[i] = result.setupEntry(builder, "runF%dLabel", core.Run, i, result.onLabelChanged) + result.runMacros[i] = result.setupEntry(builder, "runF%dMacro", core.Run, i, result.onMacroChanged) + } + + result.labels = [][]*gtk.Entry{result.spLabels, result.runLabels} + result.macros = [][]*gtk.Entry{result.spMacros, result.runMacros} + + result.presetCombo = getUI(builder, "keyerPresetCombo").(*gtk.ComboBoxText) + result.presetCombo.Connect("changed", result.onPresetChanged) + + result.messageLabel = getUI(builder, "keyerSettingsMessageLabel").(*gtk.Label) + + result.close = getUI(builder, "keyerSettingsCloseButton").(*gtk.Button) + result.close.Connect("clicked", result.onClosePressed) + result.close.SetCanDefault(true) + + return result +} + +func (v *keyerSettingsView) setupEntry(builder *gtk.Builder, idPattern string, workmode core.Workmode, i int, listenerFactory func(core.Workmode, int) func(*gtk.Entry) bool) *gtk.Entry { + result := getUI(builder, fmt.Sprintf(idPattern, i+1)).(*gtk.Entry) + result.Connect("changed", listenerFactory(workmode, i)) + result.Connect("focus_out_event", v.onEntryFocusOut) + return result +} + +func (v *keyerSettingsView) doIgnoreChanges(f func()) { + if v == nil { + return + } + + v.ignoreChangedEvent = true + defer func() { + v.ignoreChangedEvent = false + }() + f() +} + +func (v *keyerSettingsView) onClosePressed(_ *gtk.Button) { + v.parent.Close() +} + +func (v *keyerSettingsView) onEntryFocusOut(_ any, _ *gdk.Event) bool { + v.controller.Save() + return false +} + +func (v *keyerSettingsView) onLabelChanged(workmode core.Workmode, index int) func(entry *gtk.Entry) bool { + return func(entry *gtk.Entry) bool { + if v.ignoreChangedEvent { + return false + } + if v.controller == nil { + log.Println("onLabelChanged: no keyer controller") + return false + } + text, err := entry.GetText() + if err != nil { + log.Println(err) + return false + } + v.controller.EnterLabel(workmode, index, text) + return false + } +} + +func (v *keyerSettingsView) onMacroChanged(workmode core.Workmode, index int) func(entry *gtk.Entry) bool { + return func(entry *gtk.Entry) bool { + if v.ignoreChangedEvent { + return false + } + if v.controller == nil { + log.Println("onMacroChanged: no keyer controller") + return false + } + text, err := entry.GetText() + if err != nil { + log.Println(err) + return false + } + v.controller.EnterMacro(workmode, index, text) + return false + } +} + +func (v *keyerSettingsView) onPresetChanged(combo *gtk.ComboBoxText) bool { + if v.ignoreChangedEvent { + return false + } + if v.controller == nil { + log.Println("onPresetChanged: no keyer controller") + return false + } + + v.controller.SelectPreset(combo.GetActiveText()) + return true +} + +func (v *keyerSettingsView) SetKeyerController(controller KeyerSettingsController) { + v.controller = controller +} + +func (v *keyerSettingsView) SetLabel(workmode core.Workmode, index int, text string) { + v.labels[int(workmode)][index].SetText(text) +} + +func (v *keyerSettingsView) SetMacro(workmode core.Workmode, index int, text string) { + v.macros[int(workmode)][index].SetText(text) +} + +func (v *keyerSettingsView) SetPresetNames(names []string) { + v.doIgnoreChanges(func() { + v.presetCombo.RemoveAll() + v.presetCombo.Append("", "") + for _, name := range names { + v.presetCombo.Append(name, name) + } + }) +} + +func (v *keyerSettingsView) SetPreset(name string) { + v.doIgnoreChanges(func() { + v.presetCombo.SetActiveID(name) + }) +} + +func (v *keyerSettingsView) ShowMessage(args ...interface{}) { + v.messageLabel.SetText(fmt.Sprint(args...)) +} + +func (v *keyerSettingsView) ClearMessage() { + v.messageLabel.SetText("") +}