diff --git a/core/app/app.go b/core/app/app.go index 4b0fd23..aca1e46 100644 --- a/core/app/app.go +++ b/core/app/app.go @@ -70,23 +70,23 @@ type Controller struct { callHistoryFinder *callhistory.Finder hamDXMap *hamdxmap.HamDXMap - VFO *vfo.VFO - Logbook *logbook.Logbook - QSOList *logbook.QSOList - Entry *entry.Controller - Workmode *workmode.Controller - Radio *radio.Controller - Keyer *keyer.Keyer - Callinfo *callinfo.Callinfo - Score *score.Counter - Rate *rate.Counter - ServiceStatus *ServiceStatus - NewContestDialog *newcontest.Controller - ExportCabrilloDialog *cabrillo.Controller - Settings *settings.Settings - Bandmap *bandmap.Bandmap - Clusters *cluster.Clusters - Parrot *parrot.Parrot + VFO *vfo.VFO + Logbook *logbook.Logbook + QSOList *logbook.QSOList + Entry *entry.Controller + Workmode *workmode.Controller + Radio *radio.Controller + Keyer *keyer.Keyer + Callinfo *callinfo.Callinfo + Score *score.Counter + Rate *rate.Counter + ServiceStatus *ServiceStatus + NewContestController *newcontest.Controller + ExportCabrilloController *cabrillo.Controller + Settings *settings.Settings + Bandmap *bandmap.Bandmap + Clusters *cluster.Clusters + Parrot *parrot.Parrot } // View defines the visual functionality of the main application window. @@ -147,8 +147,8 @@ func (c *Controller) Startup() { c.configuration.Contest(), ) c.callHistoryFinder.Notify(c.Settings) - c.NewContestDialog = newcontest.NewController(c.Settings, c.configuration.LogDirectory()) - c.ExportCabrilloDialog = cabrillo.NewController() + c.NewContestController = newcontest.NewController(c.Settings, c.configuration.LogDirectory()) + c.ExportCabrilloController = cabrillo.NewController() c.bandplan = bandplan.IARURegion1 // TODO: make the bandplan configurable c.dxccFinder = dxcc.New() @@ -398,7 +398,7 @@ func proposeFilename(contestName, callsign string) string { func (c *Controller) New() { var err error - newContest, ok := c.NewContestDialog.Run() + newContest, ok := c.NewContestController.Run() if !ok { return } @@ -541,7 +541,7 @@ func (c *Controller) SaveAs() { func (c *Controller) ExportCabrillo() { var err error - ok := c.ExportCabrilloDialog.Run() + export, openCabrilloFile, ok := c.ExportCabrilloController.Run(c.Settings, c.Score.Result(), c.QSOList.All()) if !ok { return } @@ -563,16 +563,15 @@ func (c *Controller) ExportCabrillo() { } defer file.Close() - err = cabrillo.Export( - file, - c.Settings, - c.Score.Result(), - c.QSOList.All()...) + err = cabrillo.Export(file, export) if err != nil { c.view.ShowErrorDialog("Cannot export Cabrillo to %s: %v", filename, err) return } - c.openWithExternalApplication(filename) + + if openCabrilloFile { + c.openWithExternalApplication(filename) + } } func (c *Controller) ExportADIF() { diff --git a/core/export/cabrillo/cabrillo.go b/core/export/cabrillo/cabrillo.go index 6d5bb08..36109c6 100644 --- a/core/export/cabrillo/cabrillo.go +++ b/core/export/cabrillo/cabrillo.go @@ -14,14 +14,20 @@ import ( type View interface { Show() bool + + SetOpenAfterExport(bool) } type Controller struct { view View + + openAfterExport bool } func NewController() *Controller { - result := &Controller{} + result := &Controller{ + openAfterExport: false, + } return result } @@ -37,22 +43,27 @@ func (c *Controller) SetView(view View) { c.view = view } -func (c *Controller) Run() bool { +func (c *Controller) Run(settings core.Settings, claimedScore int, qsos []core.QSO) (*cabrillo.Log, bool, bool) { + c.view.SetOpenAfterExport(c.openAfterExport) accepted := c.view.Show() if !accepted { - return false + return nil, false, false } - return accepted + export := createCabrilloLog(settings, claimedScore, qsos) + + return export, c.openAfterExport, true } -// Export writes the given QSOs to the given writer in the Cabrillo format. -// The header is very limited and needs to be completed manually after the log was written. -func Export(w io.Writer, settings core.Settings, claimedScore int, qsos ...core.QSO) error { +func (c *Controller) SetOpenAfterExport(open bool) { + c.openAfterExport = open +} + +func createCabrilloLog(settings core.Settings, claimedScore int, qsos []core.QSO) *cabrillo.Log { export := cabrillo.NewLog() export.Callsign = settings.Station().Callsign export.CreatedBy = "Hello Contest" - export.Contest = cabrillo.ContestIdentifier(settings.Contest().Name) + export.Contest = cabrillo.ContestIdentifier(settings.Contest().Definition.Identifier) export.Operators = []callsign.Callsign{settings.Station().Operator} export.GridLocator = settings.Station().Locator export.ClaimedScore = claimedScore @@ -70,9 +81,15 @@ func Export(w io.Writer, settings core.Settings, claimedScore int, qsos ...core. export.QSOData = qsoData export.IgnoredQSOs = ignoredQSOs + return export +} + +// Export writes the given QSOs to the given writer in the Cabrillo format. +// The header is very limited and needs to be completed manually after the log was written. +func Export(w io.Writer, export *cabrillo.Log) error { return cabrillo.WriteWithTags(w, export, false, false, cabrillo.CreatedByTag, cabrillo.ContestTag, cabrillo.CallsignTag, cabrillo.OperatorsTag, cabrillo.GridLocatorTag, cabrillo.ClaimedScoreTag, - cabrillo.Tag("SPECIFIC"), cabrillo.CategoryAssistedTag, cabrillo.CategoryBandTag, cabrillo.CategoryModeTag, + cabrillo.CategoryAssistedTag, cabrillo.CategoryBandTag, cabrillo.CategoryModeTag, cabrillo.CategoryOperatorTag, cabrillo.CategoryPowerTag, cabrillo.ClubTag, cabrillo.NameTag, cabrillo.EmailTag) } diff --git a/core/export/cabrillo/cabrillo_test.go b/core/export/cabrillo/cabrillo_test.go index 4431772..3333957 100644 --- a/core/export/cabrillo/cabrillo_test.go +++ b/core/export/cabrillo/cabrillo_test.go @@ -6,6 +6,7 @@ import ( "text/template" "time" + "github.com/ftl/conval" "github.com/ftl/hamradio/callsign" "github.com/ftl/hamradio/locator" "github.com/stretchr/testify/assert" @@ -68,9 +69,10 @@ func TestQsoLine(t *testing.T) { func TestExport(t *testing.T) { buffer := bytes.NewBuffer([]byte{}) settings := &testSettings{ - stationCallsign: "AA1ZZZ", - stationOperator: "AA2ZZZ", - stationLocator: "AA00AA", + stationCallsign: "AA1ZZZ", + stationOperator: "AA2ZZZ", + stationLocator: "AA00AA", + contestIdentifier: "HELLO-CONTEST-CABRILLO-TEST", } theirCall, _ := callsign.Parse("S50A") qso := core.QSO{ @@ -88,33 +90,34 @@ func TestExport(t *testing.T) { expected := `START-OF-LOG: 3.0 CREATED-BY: Hello Contest -CONTEST: +CONTEST: HELLO-CONTEST-CABRILLO-TEST CALLSIGN: AA1ZZZ OPERATORS: AA2ZZZ GRID-LOCATOR: AA00aa CLAIMED-SCORE: 123 -SPECIFIC: -CATEGORY-ASSISTED: -CATEGORY-BAND: -CATEGORY-MODE: -CATEGORY-OPERATOR: -CATEGORY-POWER: -CLUB: -NAME: -EMAIL: +CATEGORY-ASSISTED: +CATEGORY-BAND: +CATEGORY-MODE: +CATEGORY-OPERATOR: +CATEGORY-POWER: +CLUB: +NAME: +EMAIL: QSO: 7000 CW 2009-05-30 0002 AA1ZZZ 599 001 ABC S50A 589 004 DEF -END-OF-LOG: +END-OF-LOG: ` - Export(buffer, settings, 123, qso) + export := createCabrilloLog(settings, 123, []core.QSO{qso}) + Export(buffer, export) assert.Equal(t, expected, buffer.String()) } type testSettings struct { - stationCallsign string - stationOperator string - stationLocator string + stationCallsign string + stationOperator string + stationLocator string + contestIdentifier string } func (s *testSettings) Station() core.Station { @@ -127,5 +130,9 @@ func (s *testSettings) Station() core.Station { } func (s *testSettings) Contest() core.Contest { - return core.Contest{} + return core.Contest{ + Definition: &conval.Definition{ + Identifier: conval.ContestIdentifier(s.contestIdentifier), + }, + } } diff --git a/go.mod b/go.mod index 5f90ba9..ec7631a 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ go 1.22.3 // replace github.com/gotk3/gotk3 => ../gotk3 require ( - github.com/ftl/cabrillo v0.2.2 + github.com/ftl/cabrillo v0.2.3 github.com/ftl/clusterix v0.1.0 github.com/ftl/conval v0.8.0 github.com/ftl/gmtry v0.0.0-20201120192810-fa4a1b99fc04 diff --git a/go.sum b/go.sum index 1c7bcc4..374a034 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/ftl/cabrillo v0.2.2 h1:c54U9f14eo/aEKp1z9cfGcPfAn2feJTNbPr230hOXlQ= -github.com/ftl/cabrillo v0.2.2/go.mod h1:KYCrmgZXhXuta03bS8fnD27bJoRSmP/mvkzCV7lMU1w= +github.com/ftl/cabrillo v0.2.3 h1:x2Adf24QX6zQ0/OX12LJ6+2yp9sZBUpNV4Xsxb2w0H0= +github.com/ftl/cabrillo v0.2.3/go.mod h1:yrVvhb4datmP7OTEfX6gpEsTP8QNpXH0LAoatwvsjCI= github.com/ftl/clusterix v0.1.0 h1:+fbTybTvkVorx3B4zZ3DBiVbmLV0hhFpfDGW50Ss9cg= github.com/ftl/clusterix v0.1.0/go.mod h1:N89RHOExeAbnWqoXs/MD76uackK2/9Dcu+FooVq1tKg= github.com/ftl/conval v0.8.0 h1:ZD1b+YpNne8ED53jmDR6l3Amv+wtOveLwHQSPSGgTMc= diff --git a/ui/app.go b/ui/app.go index 36ce772..636a21f 100644 --- a/ui/app.go +++ b/ui/app.go @@ -95,8 +95,8 @@ func (a *application) activate() { 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.NewContestDialog) - a.exportCabrilloDialog = setupExportCabrilloDialog(a.mainWindow.window, a.controller.ExportCabrillo) + a.newContestDialog = setupNewContestDialog(a.mainWindow.window, a.controller.NewContestController) + a.exportCabrilloDialog = setupExportCabrilloDialog(a.mainWindow.window, a.controller.ExportCabrilloController) a.mainWindow.SetMainMenuController(a.controller) a.mainWindow.SetRadioMenuController(a.controller) @@ -125,8 +125,8 @@ func (a *application) activate() { a.controller.Bandmap.SetView(a.spotsWindow) a.controller.Settings.SetView(a.settingsDialog) a.controller.Settings.Notify(a.mainWindow) - a.controller.NewContestDialog.SetView(a.newContestDialog) - a.controller.ExportCabrilloDialog.SetView(a.exportCabrilloDialog) + a.controller.NewContestController.SetView(a.newContestDialog) + a.controller.ExportCabrilloController.SetView(a.exportCabrilloDialog) a.controller.Clusters.SetView(a.mainWindow) a.controller.Parrot.SetView(a.mainWindow) diff --git a/ui/exportCabrilloDialog.go b/ui/exportCabrilloDialog.go index 71ec162..be4b4cf 100644 --- a/ui/exportCabrilloDialog.go +++ b/ui/exportCabrilloDialog.go @@ -7,25 +7,39 @@ import ( type exportCabrilloDialog struct { dialog *gtk.Dialog parent gtk.IWidget + + controller ExportCabrilloController + view *exportCabrilloView + + openAfterExport bool } func setupExportCabrilloDialog(parent gtk.IWidget, controller ExportCabrilloController) *exportCabrilloDialog { result := &exportCabrilloDialog{ - parent: parent, + parent: parent, + controller: controller, } return result } func (d *exportCabrilloDialog) onDestroy() { d.dialog = nil + d.view = nil } func (d *exportCabrilloDialog) Show() bool { - label, _ := gtk.LabelNew("Export the log as Cabrillo format.") - + d.view = &exportCabrilloView{} grid, _ := gtk.GridNew() grid.SetOrientation(gtk.ORIENTATION_VERTICAL) - grid.Add(label) + + label, _ := gtk.LabelNew("Export the log as Cabrillo format.") + grid.Attach(label, 0, 0, 2, 1) + + d.view.openAfterExportCheckButton, _ = gtk.CheckButtonNewWithLabel("Open the file after export") + d.view.openAfterExportCheckButton.SetActive(d.openAfterExport) + grid.Attach(d.view.openAfterExportCheckButton, 0, 1, 2, 1) + + d.view.setup(d.controller) dialog, _ := gtk.DialogNew() d.dialog = dialog @@ -46,6 +60,11 @@ func (d *exportCabrilloDialog) Show() bool { d.dialog.Close() d.dialog.Destroy() d.dialog = nil + d.view = nil return result } + +func (d *exportCabrilloDialog) SetOpenAfterExport(open bool) { + d.openAfterExport = open +} diff --git a/ui/exportCabrilloView.go b/ui/exportCabrilloView.go index 4c17faf..b6eb3ca 100644 --- a/ui/exportCabrilloView.go +++ b/ui/exportCabrilloView.go @@ -1,4 +1,22 @@ package ui +import "github.com/gotk3/gotk3/gtk" + type ExportCabrilloController interface { + SetOpenAfterExport(bool) +} + +type exportCabrilloView struct { + controller ExportCabrilloController + + openAfterExportCheckButton *gtk.CheckButton +} + +func (v *exportCabrilloView) setup(controller ExportCabrilloController) { + v.controller = controller + v.openAfterExportCheckButton.Connect("toggled", v.onOpenAfterExportToggled) +} + +func (v *exportCabrilloView) onOpenAfterExportToggled() { + v.controller.SetOpenAfterExport(v.openAfterExportCheckButton.GetActive()) }