File filters (windows).
This commit is contained in:
parent
6a81d17a04
commit
2bb3f4747e
5 changed files with 89 additions and 25 deletions
43
file.go
43
file.go
|
@ -85,10 +85,42 @@ func (f FileFilters) apply(o *options) {
|
|||
o.fileFilters = append(o.fileFilters, f...)
|
||||
}
|
||||
|
||||
// macOS filters by case insensitive literal extension.
|
||||
// Extract all literal extensions from all patterns.
|
||||
// If those contain wildcards, or classes with more than one character, accept anything.
|
||||
func (f FileFilters) darwin() []string {
|
||||
// Windows' patterns are case insensitive, don't support character classes or escaping.
|
||||
// First we remove character classes, then escaping. Patterns with literal wildcards are invalid (match nothing).
|
||||
// The semicolon is a separator, so we replace it with the single character wildcard.
|
||||
func (f FileFilters) simplify() {
|
||||
for _, filter := range f {
|
||||
for i, pattern := range filter.Patterns {
|
||||
var escape, invalid bool
|
||||
var buf strings.Builder
|
||||
for _, r := range removeClasses(pattern) {
|
||||
if !escape && r == '\\' {
|
||||
escape = true
|
||||
continue
|
||||
}
|
||||
if escape && (r == '*' || r == '?') {
|
||||
invalid = true
|
||||
break
|
||||
}
|
||||
if r == ';' {
|
||||
r = '?'
|
||||
}
|
||||
buf.WriteRune(r)
|
||||
escape = false
|
||||
}
|
||||
if invalid {
|
||||
filter.Patterns[i] = ""
|
||||
} else {
|
||||
filter.Patterns[i] = buf.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// macOS filters by "type"; the case insensitive literal extension is a good proxy.
|
||||
// So we extract the extension from each pattern, remove character classes, then escaping.
|
||||
// If an extension contains a wildcard, any type is accepted.
|
||||
func (f FileFilters) types() []string {
|
||||
var res []string
|
||||
for _, filter := range f {
|
||||
for _, pattern := range filter.Patterns {
|
||||
|
@ -114,6 +146,9 @@ func (f FileFilters) darwin() []string {
|
|||
return res
|
||||
}
|
||||
|
||||
// Remove character classes from pattern, assuming case insensitivity.
|
||||
// Classes of one character (case insensitive) are replaced by the character.
|
||||
// Others are replaced by the single character wildcard.
|
||||
func removeClasses(pattern string) string {
|
||||
var res strings.Builder
|
||||
for {
|
||||
|
|
|
@ -14,7 +14,7 @@ func selectFile(opts options) (string, error) {
|
|||
data.Operation = "chooseFolder"
|
||||
} else {
|
||||
data.Operation = "chooseFile"
|
||||
data.Options.Type = opts.fileFilters.darwin()
|
||||
data.Options.Type = opts.fileFilters.types()
|
||||
}
|
||||
|
||||
out, err := zenutil.Run(opts.ctx, "file", data)
|
||||
|
@ -34,7 +34,7 @@ func selectFileMutiple(opts options) ([]string, error) {
|
|||
data.Operation = "chooseFolder"
|
||||
} else {
|
||||
data.Operation = "chooseFile"
|
||||
data.Options.Type = opts.fileFilters.darwin()
|
||||
data.Options.Type = opts.fileFilters.types()
|
||||
}
|
||||
|
||||
out, err := zenutil.Run(opts.ctx, "file", data)
|
||||
|
|
|
@ -5,27 +5,55 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
func TestFileFilters_darwin(t *testing.T) {
|
||||
func TestFileFilters_simplify(t *testing.T) {
|
||||
tests := []struct {
|
||||
data FileFilters
|
||||
want []string
|
||||
}{
|
||||
{FileFilters{{"", []string{`*.png`}}}, []string{`png`}},
|
||||
{FileFilters{{"", []string{`*.pn?`}}}, nil},
|
||||
{FileFilters{{"", []string{`*.pn\?`}}}, []string{`pn?`}},
|
||||
{FileFilters{{"", []string{`*.[PpNnGg]`}}}, nil},
|
||||
{FileFilters{{"", []string{`*.[Pp][Nn][Gg]`}}}, []string{`PNG`}},
|
||||
{FileFilters{{"", []string{`*.[Pp][\Nn][G\g]`}}}, []string{`PNG`}},
|
||||
{FileFilters{{"", []string{`*.[PNG`}}}, []string{`[PNG`}},
|
||||
{FileFilters{{"", []string{`*.]PNG`}}}, []string{`]PNG`}},
|
||||
{FileFilters{{"", []string{`*.[[]PNG`}}}, []string{`[PNG`}},
|
||||
{FileFilters{{"", []string{`*.[]]PNG`}}}, []string{`]PNG`}},
|
||||
{FileFilters{{"", []string{`*.[\[]PNG`}}}, []string{`[PNG`}},
|
||||
{FileFilters{{"", []string{`*.[\]]PNG`}}}, []string{`]PNG`}},
|
||||
{FileFilters{{"", []string{`*.png`}}}, []string{"*.png"}},
|
||||
{FileFilters{{"", []string{`*.pn?`}}}, []string{"*.pn?"}},
|
||||
{FileFilters{{"", []string{`*.pn;`}}}, []string{"*.pn?"}},
|
||||
{FileFilters{{"", []string{`*.pn\?`}}}, []string{""}},
|
||||
{FileFilters{{"", []string{`*.[PpNnGg]`}}}, []string{"*.?"}},
|
||||
{FileFilters{{"", []string{`*.[Pp][Nn][Gg]`}}}, []string{"*.PNG"}},
|
||||
{FileFilters{{"", []string{`*.[Pp][\Nn][G\g]`}}}, []string{"*.PNG"}},
|
||||
{FileFilters{{"", []string{`*.[PNG`}}}, []string{"*.[PNG"}},
|
||||
{FileFilters{{"", []string{`*.]PNG`}}}, []string{"*.]PNG"}},
|
||||
{FileFilters{{"", []string{`*.[[]PNG`}}}, []string{"*.[PNG"}},
|
||||
{FileFilters{{"", []string{`*.[]]PNG`}}}, []string{"*.]PNG"}},
|
||||
{FileFilters{{"", []string{`*.[\[]PNG`}}}, []string{"*.[PNG"}},
|
||||
{FileFilters{{"", []string{`*.[\]]PNG`}}}, []string{"*.]PNG"}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
if got := tt.data.darwin(); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Fatalf("FileFilters.darwin(%+v) = %v, want %v", tt.data, got, tt.want)
|
||||
for i, tt := range tests {
|
||||
tt.data.simplify()
|
||||
if got := tt.data[0].Patterns; !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("FileFilters.simplify[%d] = %q, want %q", i, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileFilters_types(t *testing.T) {
|
||||
tests := []struct {
|
||||
data FileFilters
|
||||
want []string
|
||||
}{
|
||||
{FileFilters{{"", []string{`*.png`}}}, []string{"png"}},
|
||||
{FileFilters{{"", []string{`*.pn?`}}}, nil},
|
||||
{FileFilters{{"", []string{`*.pn;`}}}, []string{"pn;"}},
|
||||
{FileFilters{{"", []string{`*.pn\?`}}}, []string{"pn?"}},
|
||||
{FileFilters{{"", []string{`*.[PpNnGg]`}}}, nil},
|
||||
{FileFilters{{"", []string{`*.[Pp][Nn][Gg]`}}}, []string{"PNG"}},
|
||||
{FileFilters{{"", []string{`*.[Pp][\Nn][G\g]`}}}, []string{"PNG"}},
|
||||
{FileFilters{{"", []string{`*.[PNG`}}}, []string{"[PNG"}},
|
||||
{FileFilters{{"", []string{`*.]PNG`}}}, []string{"]PNG"}},
|
||||
{FileFilters{{"", []string{`*.[[]PNG`}}}, []string{"[PNG"}},
|
||||
{FileFilters{{"", []string{`*.[]]PNG`}}}, []string{"]PNG"}},
|
||||
{FileFilters{{"", []string{`*.[\[]PNG`}}}, []string{"[PNG"}},
|
||||
{FileFilters{{"", []string{`*.[\]]PNG`}}}, []string{"]PNG"}},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
if got := tt.data.types(); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Fatalf("FileFilters.types[%d] = %v, want %v", i, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
"github.com/ncruces/zenity"
|
||||
)
|
||||
|
||||
const defaultPath = ""
|
||||
const defaultName = ""
|
||||
const defaultPath = ``
|
||||
const defaultName = ``
|
||||
|
||||
func ExampleSelectFile() {
|
||||
zenity.SelectFile(
|
||||
|
|
|
@ -361,7 +361,8 @@ func initDirNameExt(filename string, name []uint16) (dir *uint16, ext *uint16) {
|
|||
return
|
||||
}
|
||||
|
||||
func initFilters(filters []FileFilter) []uint16 {
|
||||
func initFilters(filters FileFilters) []uint16 {
|
||||
filters.simplify()
|
||||
var res []uint16
|
||||
for _, f := range filters {
|
||||
res = append(res, utf16.Encode([]rune(f.Name))...)
|
||||
|
|
Loading…
Reference in a new issue