mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-05-14 16:22:46 -04:00
app picker: extend App Picker to integrate with mime overrides
- Adds "DMS Opener" as an option (dms-open.desktop) - Add mime type GO utils - Add rememberance to App Picker modal
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
package desktop
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/utils"
|
||||
)
|
||||
|
||||
var mimeappsWriteMu sync.Mutex
|
||||
|
||||
const (
|
||||
groupDefaults = "Default Applications"
|
||||
groupAdded = "Added Associations"
|
||||
groupRemoved = "Removed Associations"
|
||||
)
|
||||
|
||||
type MimeAssociations struct {
|
||||
Defaults map[string]string
|
||||
Added map[string][]string
|
||||
Removed map[string][]string
|
||||
}
|
||||
|
||||
func newAssociations() *MimeAssociations {
|
||||
return &MimeAssociations{
|
||||
Defaults: make(map[string]string),
|
||||
Added: make(map[string][]string),
|
||||
Removed: make(map[string][]string),
|
||||
}
|
||||
}
|
||||
|
||||
func mimeappsSearchPaths() []string {
|
||||
var paths []string
|
||||
seen := make(map[string]bool)
|
||||
|
||||
add := func(p string) {
|
||||
if p == "" || seen[p] {
|
||||
return
|
||||
}
|
||||
seen[p] = true
|
||||
paths = append(paths, p)
|
||||
}
|
||||
|
||||
add(filepath.Join(utils.XDGConfigHome(), "mimeapps.list"))
|
||||
|
||||
if env := os.Getenv("XDG_CONFIG_DIRS"); env != "" {
|
||||
for d := range strings.SplitSeq(env, ":") {
|
||||
add(filepath.Join(strings.TrimSpace(d), "mimeapps.list"))
|
||||
}
|
||||
} else {
|
||||
add("/etc/xdg/mimeapps.list")
|
||||
}
|
||||
|
||||
add(filepath.Join(utils.XDGDataHome(), "applications", "mimeapps.list"))
|
||||
|
||||
if env := os.Getenv("XDG_DATA_DIRS"); env != "" {
|
||||
for d := range strings.SplitSeq(env, ":") {
|
||||
add(filepath.Join(strings.TrimSpace(d), "applications", "mimeapps.list"))
|
||||
}
|
||||
} else {
|
||||
add("/usr/local/share/applications/mimeapps.list")
|
||||
add("/usr/share/applications/mimeapps.list")
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
func mimeappsWritePath() string {
|
||||
return filepath.Join(utils.XDGConfigHome(), "mimeapps.list")
|
||||
}
|
||||
|
||||
func readAssociations(path string) (*MimeAssociations, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
groups := parseGroups(data)
|
||||
assoc := newAssociations()
|
||||
|
||||
if g := groups[groupDefaults]; g != nil {
|
||||
for mime, val := range g.keys {
|
||||
parts := splitList(val)
|
||||
if len(parts) > 0 {
|
||||
assoc.Defaults[mime] = parts[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if g := groups[groupAdded]; g != nil {
|
||||
for mime, val := range g.keys {
|
||||
assoc.Added[mime] = splitList(val)
|
||||
}
|
||||
}
|
||||
|
||||
if g := groups[groupRemoved]; g != nil {
|
||||
for mime, val := range g.keys {
|
||||
assoc.Removed[mime] = splitList(val)
|
||||
}
|
||||
}
|
||||
|
||||
return assoc, nil
|
||||
}
|
||||
|
||||
func mergedAssociations() *MimeAssociations {
|
||||
merged := newAssociations()
|
||||
|
||||
for _, path := range mimeappsSearchPaths() {
|
||||
assoc, err := readAssociations(path)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for mime, app := range assoc.Defaults {
|
||||
if _, ok := merged.Defaults[mime]; !ok {
|
||||
merged.Defaults[mime] = app
|
||||
}
|
||||
}
|
||||
for mime, apps := range assoc.Added {
|
||||
merged.Added[mime] = append(merged.Added[mime], apps...)
|
||||
}
|
||||
for mime, apps := range assoc.Removed {
|
||||
merged.Removed[mime] = append(merged.Removed[mime], apps...)
|
||||
}
|
||||
}
|
||||
|
||||
return merged
|
||||
}
|
||||
|
||||
func writeUserMimeapps(update func(*MimeAssociations)) error {
|
||||
mimeappsWriteMu.Lock()
|
||||
defer mimeappsWriteMu.Unlock()
|
||||
|
||||
path := mimeappsWritePath()
|
||||
|
||||
assoc, err := readAssociations(path)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
assoc = newAssociations()
|
||||
}
|
||||
|
||||
update(assoc)
|
||||
|
||||
var buf bytes.Buffer
|
||||
w := bufio.NewWriter(&buf)
|
||||
|
||||
writeSection := func(name string, entries map[string]string) {
|
||||
fmt.Fprintf(w, "[%s]\n", name)
|
||||
keys := make([]string, 0, len(entries))
|
||||
for k := range entries {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
fmt.Fprintf(w, "%s=%s\n", k, entries[k])
|
||||
}
|
||||
fmt.Fprintln(w)
|
||||
}
|
||||
|
||||
flatten := func(m map[string][]string) map[string]string {
|
||||
out := make(map[string]string, len(m))
|
||||
for k, list := range m {
|
||||
out[k] = strings.Join(list, ";") + ";"
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
writeSection(groupDefaults, assoc.Defaults)
|
||||
writeSection(groupAdded, flatten(assoc.Added))
|
||||
writeSection(groupRemoved, flatten(assoc.Removed))
|
||||
|
||||
if err := w.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path, buf.Bytes(), 0o644)
|
||||
}
|
||||
|
||||
func setDefaultAssociation(mimeType, desktopID string) error {
|
||||
return setDefaultAssociations([]string{mimeType}, desktopID)
|
||||
}
|
||||
|
||||
func setDefaultAssociations(mimeTypes []string, desktopID string) error {
|
||||
if !strings.HasSuffix(desktopID, ".desktop") {
|
||||
desktopID += ".desktop"
|
||||
}
|
||||
return writeUserMimeapps(func(assoc *MimeAssociations) {
|
||||
for _, mimeType := range mimeTypes {
|
||||
if mimeType == "" {
|
||||
continue
|
||||
}
|
||||
assoc.Defaults[mimeType] = desktopID
|
||||
existing := assoc.Added[mimeType]
|
||||
if !slices.Contains(existing, desktopID) {
|
||||
assoc.Added[mimeType] = append(existing, desktopID)
|
||||
}
|
||||
removed, ok := assoc.Removed[mimeType]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
filtered := removed[:0]
|
||||
for _, id := range removed {
|
||||
if id != desktopID {
|
||||
filtered = append(filtered, id)
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case len(filtered) == 0:
|
||||
delete(assoc.Removed, mimeType)
|
||||
default:
|
||||
assoc.Removed[mimeType] = filtered
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user