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...)
|
o.fileFilters = append(o.fileFilters, f...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// macOS filters by case insensitive literal extension.
|
// Windows' patterns are case insensitive, don't support character classes or escaping.
|
||||||
// Extract all literal extensions from all patterns.
|
// First we remove character classes, then escaping. Patterns with literal wildcards are invalid (match nothing).
|
||||||
// If those contain wildcards, or classes with more than one character, accept anything.
|
// The semicolon is a separator, so we replace it with the single character wildcard.
|
||||||
func (f FileFilters) darwin() []string {
|
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
|
var res []string
|
||||||
for _, filter := range f {
|
for _, filter := range f {
|
||||||
for _, pattern := range filter.Patterns {
|
for _, pattern := range filter.Patterns {
|
||||||
|
@ -114,6 +146,9 @@ func (f FileFilters) darwin() []string {
|
||||||
return res
|
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 {
|
func removeClasses(pattern string) string {
|
||||||
var res strings.Builder
|
var res strings.Builder
|
||||||
for {
|
for {
|
||||||
|
|
|
@ -14,7 +14,7 @@ func selectFile(opts options) (string, error) {
|
||||||
data.Operation = "chooseFolder"
|
data.Operation = "chooseFolder"
|
||||||
} else {
|
} else {
|
||||||
data.Operation = "chooseFile"
|
data.Operation = "chooseFile"
|
||||||
data.Options.Type = opts.fileFilters.darwin()
|
data.Options.Type = opts.fileFilters.types()
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := zenutil.Run(opts.ctx, "file", data)
|
out, err := zenutil.Run(opts.ctx, "file", data)
|
||||||
|
@ -34,7 +34,7 @@ func selectFileMutiple(opts options) ([]string, error) {
|
||||||
data.Operation = "chooseFolder"
|
data.Operation = "chooseFolder"
|
||||||
} else {
|
} else {
|
||||||
data.Operation = "chooseFile"
|
data.Operation = "chooseFile"
|
||||||
data.Options.Type = opts.fileFilters.darwin()
|
data.Options.Type = opts.fileFilters.types()
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := zenutil.Run(opts.ctx, "file", data)
|
out, err := zenutil.Run(opts.ctx, "file", data)
|
||||||
|
|
|
@ -5,27 +5,55 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFileFilters_darwin(t *testing.T) {
|
func TestFileFilters_simplify(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
data FileFilters
|
data FileFilters
|
||||||
want []string
|
want []string
|
||||||
}{
|
}{
|
||||||
{FileFilters{{"", []string{`*.png`}}}, []string{`png`}},
|
{FileFilters{{"", []string{`*.png`}}}, []string{"*.png"}},
|
||||||
{FileFilters{{"", []string{`*.pn?`}}}, nil},
|
{FileFilters{{"", []string{`*.pn?`}}}, []string{"*.pn?"}},
|
||||||
{FileFilters{{"", []string{`*.pn\?`}}}, []string{`pn?`}},
|
{FileFilters{{"", []string{`*.pn;`}}}, []string{"*.pn?"}},
|
||||||
{FileFilters{{"", []string{`*.[PpNnGg]`}}}, nil},
|
{FileFilters{{"", []string{`*.pn\?`}}}, []string{""}},
|
||||||
{FileFilters{{"", []string{`*.[Pp][Nn][Gg]`}}}, []string{`PNG`}},
|
{FileFilters{{"", []string{`*.[PpNnGg]`}}}, []string{"*.?"}},
|
||||||
{FileFilters{{"", []string{`*.[Pp][\Nn][G\g]`}}}, []string{`PNG`}},
|
{FileFilters{{"", []string{`*.[Pp][Nn][Gg]`}}}, []string{"*.PNG"}},
|
||||||
{FileFilters{{"", []string{`*.[PNG`}}}, []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{`*.[]]PNG`}}}, []string{"*.]PNG"}},
|
||||||
{FileFilters{{"", []string{`*.[\]]PNG`}}}, []string{`]PNG`}},
|
{FileFilters{{"", []string{`*.[\[]PNG`}}}, []string{"*.[PNG"}},
|
||||||
|
{FileFilters{{"", []string{`*.[\]]PNG`}}}, []string{"*.]PNG"}},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for i, tt := range tests {
|
||||||
if got := tt.data.darwin(); !reflect.DeepEqual(got, tt.want) {
|
tt.data.simplify()
|
||||||
t.Fatalf("FileFilters.darwin(%+v) = %v, want %v", tt.data, got, tt.want)
|
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"
|
"github.com/ncruces/zenity"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultPath = ""
|
const defaultPath = ``
|
||||||
const defaultName = ""
|
const defaultName = ``
|
||||||
|
|
||||||
func ExampleSelectFile() {
|
func ExampleSelectFile() {
|
||||||
zenity.SelectFile(
|
zenity.SelectFile(
|
||||||
|
|
|
@ -361,7 +361,8 @@ func initDirNameExt(filename string, name []uint16) (dir *uint16, ext *uint16) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func initFilters(filters []FileFilter) []uint16 {
|
func initFilters(filters FileFilters) []uint16 {
|
||||||
|
filters.simplify()
|
||||||
var res []uint16
|
var res []uint16
|
||||||
for _, f := range filters {
|
for _, f := range filters {
|
||||||
res = append(res, utf16.Encode([]rune(f.Name))...)
|
res = append(res, utf16.Encode([]rune(f.Name))...)
|
||||||
|
|
Loading…
Reference in a new issue