Files
clr-installer/gui/pages/bundle.go
T
Mark D Horn bd38c8b9d7 Fixes for changes to gotk3 v0.6.0
Several API changes from gotk3 v0.4.0 to v0.6.0
which was a required bump to support building under golang 1.16.x.

Signed-off-by: Mark D Horn <mark.d.horn@intel.com>
2021-07-12 17:05:10 -07:00

333 lines
8.5 KiB
Go

// Copyright © 2020 Intel Corporation
//
// SPDX-License-Identifier: GPL-3.0-only
package pages
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/gotk3/gotk3/gdk"
"github.com/gotk3/gotk3/gtk"
"github.com/clearlinux/clr-installer/controller"
"github.com/clearlinux/clr-installer/gui/common"
"github.com/clearlinux/clr-installer/gui/network"
"github.com/clearlinux/clr-installer/model"
"github.com/clearlinux/clr-installer/swupd"
"github.com/clearlinux/clr-installer/utils"
)
const (
// IconDirectory is where we can find bundle icons
IconDirectory = "/usr/share/clear/bundle-icons"
)
var (
// IconSuffixes is the supported set of suffixes for the
// current Clear Bundles
IconSuffixes = []string{
".svg",
".png",
}
)
// Bundle is a simple page to help with Bundle settings
type Bundle struct {
model *model.SystemInstall
windowController Controller
bundles []*swupd.Bundle // Known bundles
box *gtk.Box // Main layout
checks *gtk.FlowBox // Where to store checks
scroll *gtk.ScrolledWindow // Scroll the checks
selections []*gtk.CheckButton
clearPage bool
}
type decisionDialog struct {
box *gtk.Box
label *gtk.Label
dialog *gtk.Dialog
confirmButton *gtk.Widget
cancelButton *gtk.Widget
}
// LookupBundleIcon attempts to find the icon for the given bundle.
// If it is found, we'll return true and the icon path, otherwise
// we'll return false with an empty string.
func LookupBundleIcon(bundle *swupd.Bundle) (string, bool) {
for _, suffix := range IconSuffixes {
path := filepath.Join(IconDirectory, fmt.Sprintf("%s%s", bundle.Name, suffix))
if _, err := os.Stat(path); err == nil {
return path, true
}
}
return "", false
}
// createBundleWidget creates new displayable widget for the given bundle
func createBundleWidget(bundle *swupd.Bundle) (*gtk.CheckButton, error) {
// Create the root layout
root, err := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
if err != nil {
return nil, err
}
// Create display check
check, err := gtk.CheckButtonNew()
if err != nil {
return nil, err
}
check.SetMarginTop(6)
check.SetMarginStart(12)
// Create display image
img, err := gtk.ImageNew()
img.SetMarginStart(12)
img.SetMarginEnd(6)
if err != nil {
return nil, err
}
icon, set := LookupBundleIcon(bundle)
if set {
pbuf, err := gdk.PixbufNewFromFileAtSize(icon, 48, 48)
if err != nil {
set = false
} else {
img.SetFromPixbuf(pbuf)
}
}
// Still not set? Fallback.
if !set {
img.SetFromIconName("package-x-generic", gtk.ICON_SIZE_INVALID)
}
img.SetPixelSize(48)
img.SetSizeRequest(48, 48)
root.PackStart(img, false, false, 0)
txt := fmt.Sprintf("<b>%s</b>\n%s", bundle.Name, utils.Locale.Get(bundle.Desc))
label, err := gtk.LabelNew(txt)
if err != nil {
return nil, err
}
label.SetMarginStart(6)
label.SetMarginEnd(12)
label.SetXAlign(0.0)
root.PackStart(label, false, false, 0)
label.SetUseMarkup(true)
check.Add(root)
return check, nil
}
func createDecisionBox(model *model.SystemInstall, bundle *Bundle) (*decisionDialog, error) {
var err error
decisionMaker := &decisionDialog{}
decisionMaker.box, err = gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
if err != nil {
return nil, err
}
decisionMaker.box.SetHAlign(gtk.ALIGN_FILL)
decisionMaker.box.SetMarginBottom(common.TopBottomMargin)
text := utils.Locale.Get("This requires a working network connection.\nProceed with a network test?")
decisionMaker.label, err = common.SetLabel(text, "label-warning", 0.0)
if err != nil {
return nil, err
}
decisionMaker.label.SetUseMarkup(true)
decisionMaker.label.SetHAlign(gtk.ALIGN_START)
decisionMaker.box.PackStart(decisionMaker.label, false, true, 0)
decisionMaker.dialog, err = common.CreateDialogOkCancel(decisionMaker.box,
utils.Locale.Get("Network Required"), utils.Locale.Get("CONFIRM"), utils.Locale.Get("CANCEL"))
if err != nil {
return nil, err
}
decisionMaker.dialog.SetDeletable(false)
// Configure confirm button
var buttonIWidget gtk.IWidget
buttonIWidget, err = decisionMaker.dialog.GetWidgetForResponse(gtk.RESPONSE_OK)
if err != nil {
return nil, err
}
decisionMaker.confirmButton = buttonIWidget.ToWidget()
// Configure cancel button
buttonIWidget, err = decisionMaker.dialog.GetWidgetForResponse(gtk.RESPONSE_CANCEL)
if err != nil {
return nil, err
}
decisionMaker.cancelButton = buttonIWidget.ToWidget()
_ = decisionMaker.confirmButton.Connect("clicked", func() {
if ret, _ := network.RunNetworkTest(model); ret != network.NetTestSuccess {
bundle.clearPage = true
bundle.ResetChanges()
decisionMaker.dialog.Destroy()
return
}
bundle.clearPage = false
bundle.windowController.SetButtonState(ButtonConfirm, controller.NetworkPassing)
decisionMaker.dialog.Destroy()
})
_ = decisionMaker.cancelButton.Connect("clicked", func() {
bundle.clearPage = true
decisionMaker.dialog.Destroy()
bundle.ResetChanges()
})
decisionMaker.confirmButton.SetSensitive(true)
decisionMaker.cancelButton.SetSensitive(true)
decisionMaker.dialog.ShowAll()
return decisionMaker, nil
}
// NewBundlePage returns a new BundlePage
func NewBundlePage(windowController Controller, model *model.SystemInstall) (Page, error) {
var err error
bundle := &Bundle{
windowController: windowController,
model: model,
}
// Load our bundles
bundle.bundles, err = swupd.LoadBundleList(model)
if err != nil {
return nil, err
}
// main layout
bundle.box, err = gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
if err != nil {
return nil, err
}
bundle.box.SetBorderWidth(8)
// check list
bundle.checks, err = gtk.FlowBoxNew()
if err != nil {
return nil, err
}
bundle.checks.SetSelectionMode(gtk.SELECTION_MULTIPLE)
bundle.scroll, err = gtk.ScrolledWindowNew(nil, nil)
if err != nil {
return nil, err
}
// no horizontal scrolling
bundle.scroll.SetPolicy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
bundle.scroll.Add(bundle.checks)
bundle.box.PackStart(bundle.scroll, true, true, 0)
// Match the bundle set to our ticks
for _, b := range bundle.bundles {
wid, err := createBundleWidget(b)
if err != nil {
return nil, err
}
bundle.checks.Add(wid)
bundle.selections = append(bundle.selections, wid)
}
for i := range bundle.selections {
if !controller.NetworkPassing {
_ = bundle.selections[i].Connect("toggled", func() {
if !controller.NetworkPassing && !bundle.clearPage {
// we dont want to fire any checkbox signals
// when we want to clear the page. if encounter
// clearPage set to true, we set to
// false so that it fire nexttime
// onwards
_, err := createDecisionBox(model, bundle)
if err != nil {
return
}
}
if !controller.NetworkPassing && bundle.clearPage {
bundle.clearPage = false
}
})
}
}
return bundle, nil
}
// IsDone checks if all the steps are completed
func (bundle *Bundle) IsDone() bool {
return true
}
// IsRequired will return false as we have default values
func (bundle *Bundle) IsRequired() bool {
return false
}
// GetID returns the ID for this page
func (bundle *Bundle) GetID() int {
return PageIDBundle
}
// GetIcon returns the icon for this page
func (bundle *Bundle) GetIcon() string {
return "applications-system"
}
// GetRootWidget returns the root embeddable widget for this page
func (bundle *Bundle) GetRootWidget() gtk.IWidget {
return bundle.box
}
// GetSummary will return the summary for this page
func (bundle *Bundle) GetSummary() string {
return utils.Locale.Get("Select Additional Bundles")
}
// GetTitle will return the title for this page
func (bundle *Bundle) GetTitle() string {
return bundle.GetSummary()
}
// StoreChanges will store this pages changes into the model
func (bundle *Bundle) StoreChanges() {
// Match model selection to our selections
for n, b := range bundle.bundles {
set := bundle.selections[n].GetActive()
if set {
bundle.model.AddUserBundle(b.Name)
} else {
bundle.model.RemoveUserBundle(b.Name)
}
}
}
// ResetChanges will reset this page to match the model
func (bundle *Bundle) ResetChanges() {
// Match selection to what's in the model
for n, b := range bundle.bundles {
bundle.selections[n].SetActive(bundle.model.ContainsUserBundle(b.Name))
}
bundle.windowController.SetButtonState(ButtonConfirm, controller.NetworkPassing)
}
// GetConfiguredValue returns our current config
func (bundle *Bundle) GetConfiguredValue() string {
if len(bundle.model.UserBundles) == 0 {
return utils.Locale.Get("No additional bundles selected")
}
return " • " + strings.Join(bundle.model.UserBundles, "\n • ")
}