You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
fw-daemon/fw-settings/builder.go

209 lines
4.9 KiB

package main
import (
"bufio"
"encoding/xml"
"fmt"
"os"
"path/filepath"
"reflect"
"regexp"
"github.com/gotk3/gotk3/glib"
"github.com/gotk3/gotk3/gtk"
"github.com/subgraph/fw-daemon/fw-settings/definitions"
)
const (
defsFolder = "definitions"
xmlExtension = ".xml"
)
type GtkXMLInterface struct {
XMLName xml.Name `xml:"interface"`
Objects []*GtkXMLObject `xml:"object"`
Requires *GtkXMLRequires `xml:"requires"`
Comment string `xml:",comment"`
}
type GtkXMLRequires struct {
Lib string `xml:"lib,attr"`
Version string `xml:"version,attr"`
}
type GtkXMLObject struct {
XMLName xml.Name `xml:"object"`
Class string `xml:"class,attr"`
ID string `xml:"id,attr,omitempty"`
Properties []GtkXMLProperty `xml:"property"`
Children []GtkXMLChild `xml:"child,omitempty"`
Signals []GtkXMLSignal `xml:"signal,omitempty"`
}
type GtkXMLChild struct {
XMLName xml.Name `xml:"child"`
Objects []*GtkXMLObject `xml:"object"`
Placeholder *GtkXMLPlaceholder `xml:"placeholder,omitempty"`
InternalChild string `xml:"internal-child,attr,omitempty"`
}
type GtkXMLProperty struct {
XMLName xml.Name `xml:"property"`
Name string `xml:"name,attr"`
Translatable string `xml:"translatable,attr,omitempty"`
Value string `xml:",chardata"`
}
type GtkXMLSignal struct {
XMLName xml.Name `xml:"signal"`
Name string `xml:"name,attr"`
Handler string `xml:"handler,attr"`
}
type GtkXMLPlaceholder struct {
XMLName xml.Name `xml:"placeholder"`
}
func getDefinitionWithFileFallback(uiName string) string {
// this makes sure a missing definition wont break only when the app is released
uiDef := getDefinition(uiName)
fileName := filepath.Join(defsFolder, uiName+xmlExtension)
if fileNotFound(fileName) {
return uiDef.String()
}
return readFile(fileName)
}
// This must be called from the UI thread - otherwise bad things will happen sooner or later
func builderForString(template string) *gtk.Builder {
// assertInUIThread()
maj := gtk.GetMajorVersion()
min := gtk.GetMinorVersion()
if (maj == 3) && (min < 20) {
fmt.Fprintf(os.Stderr,
"Attempting runtime work-around for older versions of libgtk-3...\n")
dep_re := regexp.MustCompile(`<\s?property\s+name\s?=\s?"icon_size"\s?>.+<\s?/property\s?>`)
template = dep_re.ReplaceAllString(template, ``)
dep_re2 := regexp.MustCompile(`version\s?=\s?"3.20"`)
template = dep_re2.ReplaceAllString(template, `version="3.18"`)
}
builder, err := gtk.BuilderNew()
if err != nil {
//We cant recover from this
panic(err)
}
err = builder.AddFromString(template)
if err != nil {
//This is a programming error
panic(fmt.Sprintf("gui: failed load string template: %s\n", err.Error()))
}
return builder
}
// This must be called from the UI thread - otherwise bad things will happen sooner or later
func builderForDefinition(uiName string) *gtk.Builder {
// assertInUIThread()
template := getDefinitionWithFileFallback(uiName)
maj := gtk.GetMajorVersion()
min := gtk.GetMinorVersion()
if (maj == 3) && (min < 20) {
fmt.Fprintf(os.Stderr,
"Attempting runtime work-around for older versions of libgtk-3...\n")
dep_re := regexp.MustCompile(`<\s?property\s+name\s?=\s?"icon_size"\s?>.+<\s?/property\s?>`)
template = dep_re.ReplaceAllString(template, ``)
dep_re2 := regexp.MustCompile(`version\s?=\s?"3.20"`)
template = dep_re2.ReplaceAllString(template, `version="3.18"`)
}
builder, err := gtk.BuilderNew()
if err != nil {
//We cant recover from this
panic(err)
}
err = builder.AddFromString(template)
if err != nil {
//This is a programming error
panic(fmt.Sprintf("gui: failed load %s: %s\n", uiName, err.Error()))
}
return builder
}
func fileNotFound(fileName string) bool {
_, fnf := os.Stat(fileName)
return os.IsNotExist(fnf)
}
func readFile(fileName string) string {
file, _ := os.Open(fileName)
reader := bufio.NewScanner(file)
var content string
for reader.Scan() {
content = content + reader.Text()
}
file.Close()
return content
}
func getDefinition(uiName string) fmt.Stringer {
def, ok := definitions.Get(uiName)
if !ok {
panic(fmt.Sprintf("No definition found for %s", uiName))
}
return def
}
type builder struct {
*gtk.Builder
}
func newBuilder(uiName string) *builder {
return &builder{builderForDefinition(uiName)}
}
func newBuilderFromString(template string) *builder {
return &builder{builderForString(template)}
}
func (b *builder) getItem(name string, target interface{}) {
v := reflect.ValueOf(target)
if v.Kind() != reflect.Ptr {
panic("builder.getItem() target argument must be a pointer")
}
elem := v.Elem()
elem.Set(reflect.ValueOf(b.get(name)))
}
func (b *builder) getItems(args ...interface{}) {
for len(args) >= 2 {
name, ok := args[0].(string)
if !ok {
panic("string argument expected in builder.getItems()")
}
b.getItem(name, args[1])
args = args[2:]
}
}
func (b *builder) get(name string) glib.IObject {
obj, err := b.GetObject(name)
if err != nil {
panic("builder.GetObject() failed: " + err.Error())
}
return obj
}