Compare commits

18 Commits
Author SHA1 Message Date
Felitendo c08d69c4d0 docs: show new ui as screenshots in the readme
check / syntax and shellcheck (push) Successful in 11s
2026-09-28 15:08:18 +02:00
Felitendo 919418ec3e chore: show feature requests before bug reports 2026-09-26 21:19:33 +02:00
Felitendo 171b6a2e7d chore: commit different topics separately 2026-09-25 09:30:38 +02:00
Felitendo da021305d9 chore: update readme
Removed unnecessary dependencies from the README.
2026-09-25 08:02:24 +02:00
Felitendo 3fdd3ce868 chore: switch to ko-fi 2026-09-24 13:21:14 +02:00
Felitendo 4049c021a5 chore: add issue templates 2026-09-24 12:39:45 +02:00
Felitendo 409ad657cc docs: commit and push only after a local check 2026-09-24 08:42:01 +02:00
Felitendo 74c817473c chore: 0.1.1 2026-09-24 02:01:10 +02:00
Felitendo 0916fc3cf6 docs: cut the readme down, fold the details 2026-09-24 02:00:39 +02:00
Felitendo ce3fd6955b docs: show the menu as real screenshots 2026-09-24 02:00:39 +02:00
Felitendo eb99682dbd fix: keep the file types row on one line 2026-09-24 02:00:39 +02:00
Felitendo 6861be823f docs: add a changelog and the release notes flow 2026-09-24 02:00:39 +02:00
Felitendo b8fbc2028f docs: coffee button and bug report link 2026-09-24 01:13:11 +02:00
Felitendo bf0647eef2 chore: switch funding to buy me a coffee 2026-09-24 01:13:11 +02:00
Felitendo 9a79d024b1 docs: link how to use instead of features 2026-09-21 14:02:32 +02:00
Felitendo c80fff41d0 docs: avoid dashes and simplify wording 2026-09-21 14:00:02 +02:00
Felitendo 29b3024dc1 docs: add icon and centered readme header 2026-09-21 13:50:12 +02:00
Felitendo 757d883424 chore: move to LoonixTools 2026-09-21 13:07:18 +02:00
24 changed files with 567 additions and 184 deletions

No files matched your search

-14
View File
@@ -1,15 +1 @@
# These are supported funding model platforms
github: Felitendo
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: felitendo ko_fi: felitendo
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
thanks_dev: # Replace with a single thanks.dev username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
@@ -0,0 +1,35 @@
name: 💡 Feature request
description: An idea to make bottles-opener better.
labels:
- enhancement
body:
- type: checkboxes
id: checks
attributes:
label: Before you start
options:
- label: I searched the issues and this is not requested yet.
required: true
- type: textarea
id: problem
attributes:
label: What problem would this solve?
description: What are you trying to do, and what gets in the way?
validations:
required: true
- type: textarea
id: idea
attributes:
label: What would you like?
description: How it could work. A rough idea is fine.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Other ways you thought of
- type: textarea
id: more
attributes:
label: Anything else?
description: Mockups, screenshots, or other tools that do it well.
+81
View File
@@ -0,0 +1,81 @@
name: 🐛 Bug report
description: Something does not work as it should.
labels:
- bug
body:
- type: markdown
attributes:
value: Thanks for taking the time! The more you fill in, the faster it can be fixed.
- type: checkboxes
id: checks
attributes:
label: Before you start
options:
- label: I searched the issues and this is not reported yet.
required: true
- label: I use the latest version.
required: true
- type: textarea
id: what
attributes:
label: What happened?
description: What did you do, and what went wrong?
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to reproduce
placeholder: |
1.
2.
3.
- type: textarea
id: expected
attributes:
label: What did you expect?
validations:
required: true
- type: input
id: file
attributes:
label: File type and program
description: The file type, the program that should open it, and its bottle.
placeholder: .psd, Photoshop in the bottle "Adobe"
- type: input
id: version
attributes:
label: Version
description: The output of `bottles-opener --version`.
placeholder: bottles-opener 0.1.1
validations:
required: true
- type: dropdown
id: install
attributes:
label: How did you install it?
options:
- AUR
- Built from source
- Other
validations:
required: true
- type: input
id: system
attributes:
label: System
description: Distribution, desktop, and your Bottles version and how it is installed.
placeholder: CachyOS, KDE Plasma 6.4, Bottles 51 (Flatpak)
validations:
required: true
- type: textarea
id: logs
attributes:
label: Status and logs
description: The output of `bottles-opener status` and `bottles-opener list`.
render: shell
- type: textarea
id: more
attributes:
label: Anything else?
description: Screenshots, videos or anything else that could help.
+1
View File
@@ -0,0 +1 @@
blank_issues_enabled: false
+136
View File
@@ -0,0 +1,136 @@
#!/usr/bin/env bash
#
# Builds the GitHub release notes for a tag, the way big projects such as
# Immich lay them out: the hand-written entry from CHANGELOG.md (a welcome,
# the highlights), a support section, then every commit since the last
# release, sorted by kind, and a link to the full changelog.
#
# .github/release-notes.sh v1.2.0 the notes
# .github/release-notes.sh --title v1.2.0 the title (checks the entry exists)
#
# Commit authors come from the GitHub API through gh. Without it the list
# goes without them.
set -euo pipefail
cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.."
title=0
if [[ ${1:-} == --title ]]; then
title=1
shift
fi
tag="${1:?usage: release-notes.sh [--title] vX.Y.Z}"
if ! grep -qx "## $tag" CHANGELOG.md; then
echo "CHANGELOG.md has no entry for $tag. Add \"## $tag\" first." >&2
exit 1
fi
if (( title )); then
printf '%s\n' "$tag"
exit 0
fi
repo="${GITHUB_REPOSITORY:-$(git remote get-url origin | sed -E 's#^.*github\.com[:/]##; s#\.git$##')}"
name="${repo#*/}"
# The entry: up to the next one, without the date line (GitHub shows the
# date), one heading level up, outside code blocks.
entry="$(awk -v h="## $tag" '
$0 == h { on = 1; next }
on && /^## / { exit }
!on { next }
/^```/ { code = !code }
!code && /^_[0-9]{4}-[0-9]{2}-[0-9]{2}_$/ { next }
!code && /^###/ { sub(/^#/, "") }
{ print }
' CHANGELOG.md | sed -e '/./,$!d')"
# The commits: from the last release, or from the start. A tag that does not
# exist yet is a preview of what HEAD would become.
if git rev-parse -q --verify "refs/tags/$tag" > /dev/null; then
target="$tag"
prev="$(git describe --tags --abbrev=0 --match 'v[0-9]*' "$tag^" 2>/dev/null || true)"
else
target="$(git rev-parse HEAD)"
prev="$(git describe --tags --abbrev=0 --match 'v[0-9]*' HEAD 2>/dev/null || true)"
fi
declare -A login=()
if command -v gh > /dev/null; then
if [[ -n $prev ]]; then
api=(api "repos/$repo/compare/$prev...$target" --jq '.commits[] | [.sha, (.author.login // "")] | @tsv')
else
api=(api --paginate "repos/$repo/commits?sha=$target&per_page=100" --jq '.[] | [.sha, (.author.login // "")] | @tsv')
fi
while IFS=$'\t' read -r sha who; do
[[ -n $who ]] && login[$sha]="$who"
done < <(gh "${api[@]}" 2>/dev/null || true)
fi
declare -A list=()
count_maint=0
while IFS=$'\t' read -r sha subject body; do
# Version bumps say nothing a reader needs.
[[ $subject =~ ^(chore:\ )?($name\ )?v?[0-9]+\.[0-9]+\.[0-9]+$ ]] && continue
case "$subject" in
*!:*) kind=breaking ;;
feat:* | feat\(*) kind=feat ;;
fix:* | fix\(*) kind=fix ;;
perf:* | perf\(*) kind=enh ;;
docs:* | docs\(*) kind=docs ;;
chore* | ci:* | ci\(* | build* | refactor* | test* | style*) kind=maint ;;
# Older commits without a prefix, sorted by what they say.
*README* | *readme* | *Readme*) kind=docs ;;
Add\ * | Put\ * | Introduce\ *) kind=feat ;;
Fix\ * | Repair\ * | Recover\ * | Stop\ *) kind=fix ;;
*\ *) kind=enh ;;
*) kind=maint ;;
esac
[[ $body == *"BREAKING CHANGE"* ]] && kind=breaking
[[ $subject == "Initial commit" ]] && kind=maint
line="* $subject"
[[ -n ${login[$sha]:-} ]] && line+=" by @${login[$sha]}"
line+=" in https://github.com/$repo/commit/$sha"
list[$kind]+="$line"$'\n'
[[ $kind == maint ]] && count_maint=$((count_maint + 1))
done < <(git log --reverse --no-merges --format='%H%x09%s%x09%b%x1e' "${prev:+$prev..}$target" | tr '\n\036' ' \n' | sed 's/^ //')
changes=''
for pair in "breaking:🚨 Breaking Changes" "feat:🚀 Features" "enh:🌟 Enhancements" "fix:🐛 Bug fixes" "docs:📚 Documentation"; do
kind="${pair%%:*}"
[[ -n ${list[$kind]:-} ]] || continue
changes+="### ${pair#*:}"$'\n'"${list[$kind]}"
done
if [[ -n ${list[maint]:-} ]]; then
changes+=$'\n'"<details><summary>🧰 Maintenance ($count_maint)</summary>"$'\n\n'"${list[maint]}"$'\n'"</details>"$'\n'
fi
if [[ -n $prev ]]; then
full="**Full Changelog**: https://github.com/$repo/compare/$prev...$tag"
else
full="**Full Changelog**: https://github.com/$repo/commits/$tag"
fi
# A release with highlights is a big one: it gets a heading and the support
# section, as in Immich. A patch is a sentence or two and the list.
if grep -q '^## Highlights$' <<< "$entry"; then
cat <<EOF
# 🚀 $name $tag
$entry
## ☕ Support $name
If $name is useful to you, you can buy me a coffee. It keeps these tools going. Found a bug or have an idea? Tell me in the [issues](https://github.com/$repo/issues).
<a href="https://ko-fi.com/felitendo"><img src="https://storage.ko-fi.com/cdn/kofi5.png?v=6" alt="Buy me a coffee on Ko-fi" height="48"></a>
----
EOF
else
printf '%s\n\n' "$entry"
fi
printf "## What's Changed\n%s\n\n%s\n" "$changes" "$full"
+25
View File
@@ -0,0 +1,25 @@
# Changelog
What each release brings, newest first. The [GitHub releases](https://github.com/LoonixTools/bottles-opener/releases) add every commit that went into it.
## v0.1.1
_2026-09-24_
A small patch. The file types in the menu now stay on one line, however wide your terminal is.
## v0.1.0
_2026-09-21_
Welcome to the very first release of bottles-opener! You install FL Studio in Bottles, double-click your `.flp`, and it just opens. Like on Windows.
<p align="center">
<img width="620" alt="The bottles-opener menu in Konsole" src="https://raw.githubusercontent.com/LoonixTools/bottles-opener/91463a2a5062a779bb69ea5fc708967255f41c82/res/screenshots/menu.png">
</p>
### Highlights
- Your files open in their Windows program
- The right icons
- Add your own file types
+67 -1
View File
@@ -2,7 +2,10 @@
## Style ## Style
Keep everything short — replies, explanations, comments, docs. - Keep everything short: replies, explanations, comments, docs.
- Use simple English: short sentences, common words.
- Avoid em dashes (—). Do not just swap them for "-" either. Rewrite the sentence instead, for
example with a comma, a colon, brackets or two sentences.
## Commits ## Commits
@@ -11,6 +14,69 @@ Keep everything short — replies, explanations, comments, docs.
- Subject as short as possible. Imperative, lowercase, no trailing period. - Subject as short as possible. Imperative, lowercase, no trailing period.
- Body only when something genuinely cannot be inferred from the diff. - Body only when something genuinely cannot be inferred from the diff.
- Never add `Co-Authored-By`, "Generated with" or any other AI attribution to commits or PR descriptions. - Never add `Co-Authored-By`, "Generated with" or any other AI attribution to commits or PR descriptions.
- Do not commit or push before I have checked the changes locally and said they are fine. Then
commit and push. Several attempts at the same thing make one commit, and attempts that did not
work make none. Different things done in one session get a commit each. This also goes for
releases and tags.
## Releases
Releases look like the ones of big projects such as Immich. `.github/release-notes.sh <tag>` builds
the notes: the entry from `CHANGELOG.md` (welcome and highlights), a support section, every commit
since the last release sorted by its prefix (`feat`, `fix`, `docs`, ...) with author and link, and
the full changelog link. So commit subjects end up in public: keep them clear.
1. Read `git log <last tag>..HEAD` and pick the version: only fixes → patch, something new → minor,
something that breaks or needs the user to act → major.
2. Bump the version and add the entry at the top of `CHANGELOG.md`, in one commit (`chore: 1.4.0`).
3. Push, tag and create the release:
```bash
git tag v1.4.0 && git push origin main v1.4.0
gh release create v1.4.0 --title v1.4.0 --notes "$(.github/release-notes.sh v1.4.0)"
```
4. PKGBUILDS picks up the new release for the AUR on its own.
A minor or major release (has `### Highlights`, gets a heading and the support section):
```markdown
## v1.4.0
_2026-09-24_
Welcome to bottles-opener `v1.4.0`! One or two sentences on what this release is about.
<p align="center">
<img width="480" alt="What the picture shows" src="https://raw.githubusercontent.com/LoonixTools/bottles-opener/v1.4.0/<path>">
</p>
### 🚨 Breaking changes
- Only if there are any: what changed, and what the user has to do.
### Highlights
- First highlight, a few words
- Second highlight
```
A picture under the welcome is optional. To show the menu or another screen of the program, use a
real screenshot of it running in Konsole, in English. Never a text copy of the screen. The same goes
for the README. The highlights stay a plain list: no heading or text per highlight, the list of
commits explains the rest.
A patch release is just a sentence or two, for example: "A small patch. The menu no longer closes
when you press Enter." The list of commits follows on its own.
- Write for users: what they notice, not how the code does it. Friendly and simple.
- Commands and settings they type go in backticks, buttons and labels in bold.
- To change an old release: edit its entry, commit, then
`gh release edit <tag> --title <tag> --notes "$(.github/release-notes.sh <tag>)"`.
## README
- New UI (a window, a screen, a menu, a setting, a notification) gets a screenshot in the README,
next to the text about it. Like for releases: a real screenshot of it running, in English.
- When a screen changes, take its screenshot again. The README never shows an old one.
## Testing ## Testing
+6 -6
View File
@@ -1,4 +1,4 @@
# bottles-opener - build and install # bottles-opener: build and install
# #
# Everything here is plain shell and one small Python script; "building" only # Everything here is plain shell and one small Python script; "building" only
# means compiling the gettext catalogs and rendering the man page. Both targets # means compiling the gettext catalogs and rendering the man page. Both targets
@@ -8,7 +8,7 @@
# Overridable so a packager can pass the version it is actually building # Overridable so a packager can pass the version it is actually building
# (`make VERSION=$pkgver`). The literal below is the fallback for builds # (`make VERSION=$pkgver`). The literal below is the fallback for builds
# straight from a checkout, and is what a release tag has to carry. # straight from a checkout, and is what a release tag has to carry.
VERSION ?= 0.1.0 VERSION ?= 0.1.1
PREFIX ?= /usr PREFIX ?= /usr
DESTDIR ?= DESTDIR ?=
@@ -56,14 +56,14 @@ po/%.mo: po/%.po
ifdef MSGFMT ifdef MSGFMT
$(MSGFMT) --check --output-file=$@ $< $(MSGFMT) --check --output-file=$@ $<
else else
@echo "msgfmt not found - skipping $@" @echo "msgfmt not found, skipping $@"
endif endif
$(MANPAGE): doc/bottles-opener.1.scd $(MANPAGE): doc/bottles-opener.1.scd
ifdef SCDOC ifdef SCDOC
$(SCDOC) < $< > $@ $(SCDOC) < $< > $@
else else
@echo "scdoc not found - skipping $@" @echo "scdoc not found, skipping $@"
endif endif
# Regenerates the template from the sources. The setting labels live in an # Regenerates the template from the sources. The setting labels live in an
@@ -72,7 +72,7 @@ endif
pot: pot:
xgettext --language=Shell --from-code=UTF-8 -k --keyword=bo_msg --keyword=bo_msg_into \ xgettext --language=Shell --from-code=UTF-8 -k --keyword=bo_msg --keyword=bo_msg_into \
--package-name=bottles-opener --package-version=$(VERSION) \ --package-name=bottles-opener --package-version=$(VERSION) \
--msgid-bugs-address=https://github.com/Felitendo/bottles-opener/issues \ --msgid-bugs-address=https://github.com/LoonixTools/bottles-opener/issues \
--add-comments=TRANSLATORS --no-location --sort-output \ --add-comments=TRANSLATORS --no-location --sort-output \
--output=po/bottles-opener.pot.tmp src/bottles-opener $(LIBS) --output=po/bottles-opener.pot.tmp src/bottles-opener $(LIBS)
msgcat --use-first --sort-output --output-file=po/bottles-opener.pot \ msgcat --use-first --sort-output --output-file=po/bottles-opener.pot \
@@ -92,7 +92,7 @@ check:
shellcheck -x -e SC1090,SC1091 src/bottles-opener $(LIBS); \ shellcheck -x -e SC1090,SC1091 src/bottles-opener $(LIBS); \
echo "ok shellcheck"; \ echo "ok shellcheck"; \
else \ else \
echo "shellcheck not found - skipped"; \ echo "shellcheck not found, skipped"; \
fi fi
@if command -v msgfmt >/dev/null 2>&1; then \ @if command -v msgfmt >/dev/null 2>&1; then \
for l in $(LINGUAS); do msgfmt --check --output-file=/dev/null po/$$l.po && echo "ok po/$$l.po"; done; \ for l in $(LINGUAS); do msgfmt --check --output-file=/dev/null po/$$l.po && echo "ok po/$$l.po"; done; \
+87 -88
View File
@@ -1,58 +1,97 @@
# bottles-opener <p align="center">
<img width="200" src="res/bottles-opener.svg" alt="bottles-opener">
</p>
bottles-opener is a CLI for linux that lets you double-click a file and have it open in its Windows program, inside its bottle. <h1 align="center">bottles-opener</h1>
Works with FL Studio, Microsoft Office, Photoshop, Ableton and anything else you run through [Bottles](https://usebottles.com).
You install FL Studio in Bottles, double-click your `.flp` and... your file manager asks what an `.flp` is. This tool reads which files your Windows programs registered in their bottle - the same list Windows uses - teaches your system those types, gives them the program's icons and hands the file over the way Explorer would. One command and your files _✨just open✨_ (like on windows). <h3 align="center">Double-click a file and it opens in its Windows program.</h3>
<p align="center">
FL Studio, Office, Photoshop, Ableton and anything else you run in <a href="https://usebottles.com">Bottles</a>.
</p>
<h5 align="center">
<a href="#install">Install</a> |
<a href="#how-to-use">How to use</a> |
<a href="https://github.com/LoonixTools/bottles-opener/issues">Report a bug</a>
</h5>
<p align="center">
<a href="https://ko-fi.com/felitendo"><img src="https://storage.ko-fi.com/cdn/kofi5.png?v=6" alt="Buy me a coffee on Ko-fi" height="48"></a>
</p>
## Install
```bash
yay -S bottles-opener
```
## How to use ## How to use
Just run `bottles-opener`:
```
Bottles Opener
File opening ON
Bottles package
Programs found 5 in 2 bottles
File types .doc .docx .flp .fsc .fst .pptx .xlsx +31
New programs ON
Last applied 2 minutes ago
Double-click one of these files and it opens in its program.
[1] Turn file opening on or off
[2] Re-apply everything
[3] File types
[4] Programs
[5] Settings
[q] Quit
>
```
Press `[1]` and you're done. `[3]` lists every file type and lets you pick what opens it:
```
File types
▸ .docx Word · Office double-click
.flp FL Studio · FL Studio double-click
.psd Photoshop · Adobe Open With
```
`double-click` opens it. `Open With` only lists it in the right-click menu - that's what types get that already have an app on your system (`.docx` with LibreOffice), so nothing you picked gets taken away. Space switches.
`[4]` lists your programs; Enter edits their file types, so you can add any extension you like. Or without the menu:
```bash ```bash
bottles-opener add flp,fst "FL Studio" bottles-opener
``` ```
## How to install <p align="center">
<img src="res/screenshots/menu.png" alt="The bottles-opener menu in Konsole: file opening on, 5 programs in 3 bottles" width="620">
</p>
**From source (for now)** Press **1**. Your files _✨just open✨_ in their program, like on Windows.
<details>
<summary>File types</summary>
**3** lists every file type. Space switches between:
| | |
|---|---|
| double-click | Opens it in the program |
| Open With | Only listed in the right-click menu (when another app already opens it) |
<p align="center">
<img src="res/screenshots/file-types.png" alt="The file types list in Konsole: .flp opens in FL Studio on a double-click, Office types are under Open With" width="620">
</p>
Your own file types: `bottles-opener add flp,fst "FL Studio"`
</details>
## More
<details>
<summary>How it works</summary>
| | |
|---|---|
| File types | Read from the bottle's registry, like Windows does. Known programs without entries come from a built-in list. |
| Icons | The ones Windows shows, taken from the program. |
| Opening | With the Windows path and the program's own switches, through `bottles-cli`. |
| Undo | `bottles-opener disable` puts back exactly what was there. |
Bottles 67 has file associations too, but it refuses extensions your system does not know (like
`.flp`), passes Linux paths and leaves the icons alone.
</details>
<details>
<summary>Commands</summary>
| | |
|---|---|
| `bottles-opener` | The menu |
| `… enable` | Turn on |
| `… disable` | Undo everything |
| `… apply` | Pick up new programs |
| `… status` | What is set up |
| `… list` | All file types and what opens them |
| `… programs` | All programs and their file types |
| `… add <ext,...> <program> [bottle]` | Open file types with a program |
| `… remove <ext,...> [program]` | Stop opening file types |
</details>
<details>
<summary>Build from source</summary>
```bash ```bash
make make
@@ -60,48 +99,8 @@ sudo make install
bottles-opener enable bottles-opener enable
``` ```
No root? `make install PREFIX=~/.local` works too. `make install PREFIX=~/.local` works without root.
Needs `bash`, `python3` with PyYAML and icoextract (Bottles needs both itself) and `update-mime-database`. </details>
## How it works
- **Which files:** read from the bottle's registry, like Windows does (`.flp` → `FL64.flp.26` → `FL64.exe "%1"`). Programs that registered nothing are matched against a built-in list: all of Office, FL Studio, Ableton, Cubase, Photoshop, Illustrator, Affinity, AutoCAD, SketchUp and more.
- **Unknown extensions** (`.flp`, `.msg`, `.als`, ...) get a MIME type in `~/.local/share/mime/packages/bottles-opener.xml`, named like in Windows ("FL Studio project file"). Where Linux means something else by an extension (`.mpp` is Musepack, `.fst` a tracker module) the file's signature tells them apart.
- **Icons:** the file gets the icon Windows shows, pulled out of the program. A type your system already knows keeps its icon until the program is its default.
- **Opening:** the path becomes the Windows path (`Z:\home\you\song.flp`), with the switches from the registry (Outlook `/f`, PowerPoint shows `/s`), started through `bottles-cli` with all your bottle's settings.
- **Flatpak Bottles:** the file goes through the document portal first, like `flatpak run --file-forwarding`.
Everything written is recorded, so `bottles-opener disable` puts back exactly what was there. New programs are picked up by a systemd path unit; `bottles-opener apply` does the same by hand.
## Why not just Bottles?
Bottles 67 has "File Associations" per program, but it rejects extensions your system doesn't know (so `.flp`, `.msg`, `.als` are "invalid"), passes Linux paths, doesn't know which program opens what and doesn't touch icons.
## Commands
| Command | |
|---|---|
| `bottles-opener` | Interactive menu |
| `… enable` | Turn on, apply, start watching |
| `… disable` | Undo everything |
| `… apply` | Pick up new programs |
| `… apply --rebuild` | Redo from scratch |
| `… status` | What's set up |
| `… list` | All file types and what opens them |
| `… programs` | All programs and their file types |
| `… add <ext,...> <program> [bottle]` | Open file types with a program |
| `… remove <ext,...> [program]` | Stop opening file types |
## Building from source
```bash
make
sudo make install
```
Optionally needs `msgfmt` (gettext) and `scdoc`. Supports `PREFIX` and `DESTDIR`. `make check` runs syntax checks and shellcheck.
## License
GPL-3.0-or-later. GPL-3.0-or-later.
+1 -1
View File
@@ -125,4 +125,4 @@ dependencies), *update-mime-database*. Optional: *notify-send*.
# AUTHORS # AUTHORS
Felitendo. https://github.com/Felitendo/bottles-opener Felitendo. https://github.com/LoonixTools/bottles-opener
+16 -17
View File
@@ -7,8 +7,8 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: bottles-opener 0.1.0\n" "Project-Id-Version: bottles-opener 0.1.0\n"
"Report-Msgid-Bugs-To: https://github.com/Felitendo/bottles-opener/issues\n" "Report-Msgid-Bugs-To: https://github.com/LoonixTools/bottles-opener/issues\n"
"POT-Creation-Date: 2026-09-21 11:30+0200\n" "POT-Creation-Date: 2026-09-21 13:54+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -55,12 +55,12 @@ msgid "Already up to date for %s."
msgstr "" msgstr ""
msgid "" msgid ""
"Bottles could not be read - is python3 with PyYAML installed? Nothing was " "Bottles could not be read. Is python3 with PyYAML installed? Nothing was "
"changed." "changed."
msgstr "" msgstr ""
msgid "" msgid ""
"Bottles is not installed - neither the package nor the Flatpak was found." "Bottles is not installed. Neither the package nor the Flatpak was found."
msgstr "" msgstr ""
msgid "Commands:" msgid "Commands:"
@@ -82,10 +82,10 @@ msgstr ""
msgid "File opening" msgid "File opening"
msgstr "" msgstr ""
msgid "File opening is off - this is what it would do." msgid "File opening is off and everything that was changed has been put back."
msgstr "" msgstr ""
msgid "File opening is off and everything that was changed has been put back." msgid "File opening is off. This is what it would do."
msgstr "" msgstr ""
msgid "File opening is off. Turn it on first." msgid "File opening is off. Turn it on first."
@@ -121,7 +121,7 @@ msgid "Looking at the bottles..."
msgstr "" msgstr ""
#, sh-printf-format #, sh-printf-format
msgid "More than one program is called \"%s\" - name the bottle as well." msgid "More than one program is called \"%s\". Name the bottle as well."
msgstr "" msgstr ""
msgid "New programs" msgid "New programs"
@@ -152,12 +152,12 @@ msgid "No programs found in any bottle."
msgstr "" msgstr ""
msgid "" msgid ""
"No systemd user session - newly installed programs will not be picked up on " "No systemd user session, so newly installed programs will not be picked up "
"their own." "on their own."
msgstr "" msgstr ""
#, sh-printf-format #, sh-printf-format
msgid "Not overwriting %s - it was not written by this program." msgid "Not overwriting %s: this program did not write it."
msgstr "" msgstr ""
#, sh-printf-format #, sh-printf-format
@@ -246,17 +246,16 @@ msgstr ""
msgid "Unknown command: %s" msgid "Unknown command: %s"
msgstr "" msgstr ""
msgid "Up/Down select - Enter edits its file types - q goes back" msgid "Up/Down: select, Enter: edit file types, q: back"
msgstr "" msgstr ""
msgid "Up/Down select - Enter picks - q cancels" msgid "Up/Down: select, Enter: pick, q: cancel"
msgstr "" msgstr ""
msgid "Up/Down select - Space changes - q goes back" msgid "Up/Down: select, Space: change, q: back"
msgstr "" msgstr ""
msgid "" msgid "Up/Down: select, Space: switch, a: add, Del: remove, q: back"
"Up/Down select - Space switches - a adds one - Del removes - q goes back"
msgstr "" msgstr ""
msgid "Usage: bottles-opener [command]" msgid "Usage: bottles-opener [command]"
@@ -286,7 +285,7 @@ msgstr ""
msgid "added" msgid "added"
msgstr "" msgstr ""
msgid "could not be read - is python3 with PyYAML installed?" msgid "could not be read (is python3 with PyYAML installed?)"
msgstr "" msgstr ""
msgid "double-click" msgid "double-click"
@@ -298,7 +297,7 @@ msgstr ""
msgid "never" msgid "never"
msgstr "" msgstr ""
msgid "none yet - add one under File types" msgid "none yet, add one under File types"
msgstr "" msgstr ""
msgid "not found" msgid "not found"
+30 -31
View File
@@ -5,8 +5,8 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: bottles-opener 0.1.0\n" "Project-Id-Version: bottles-opener 0.1.0\n"
"Report-Msgid-Bugs-To: https://github.com/Felitendo/bottles-opener/issues\n" "Report-Msgid-Bugs-To: https://github.com/LoonixTools/bottles-opener/issues\n"
"POT-Creation-Date: 2026-09-21 11:30+0200\n" "POT-Creation-Date: 2026-09-21 13:54+0200\n"
"PO-Revision-Date: 2026-09-21 00:00+0200\n" "PO-Revision-Date: 2026-09-21 00:00+0200\n"
"Last-Translator: Felitendo\n" "Last-Translator: Felitendo\n"
"Language-Team: German\n" "Language-Team: German\n"
@@ -55,16 +55,16 @@ msgid "Already up to date for %s."
msgstr "Für %s ist schon alles eingerichtet." msgstr "Für %s ist schon alles eingerichtet."
msgid "" msgid ""
"Bottles could not be read - is python3 with PyYAML installed? Nothing was " "Bottles could not be read. Is python3 with PyYAML installed? Nothing was "
"changed." "changed."
msgstr "" msgstr ""
"Bottles konnte nicht gelesen werden - ist python3 mit PyYAML installiert? Es " "Bottles konnte nicht gelesen werden. Ist python3 mit PyYAML installiert? Es "
"wurde nichts geändert." "wurde nichts geändert."
msgid "" msgid ""
"Bottles is not installed - neither the package nor the Flatpak was found." "Bottles is not installed. Neither the package nor the Flatpak was found."
msgstr "" msgstr ""
"Bottles ist nicht installiert - weder das Paket noch das Flatpak wurde " "Bottles ist nicht installiert. Weder das Paket noch das Flatpak wurde "
"gefunden." "gefunden."
msgid "Commands:" msgid "Commands:"
@@ -87,13 +87,13 @@ msgstr "Ein Doppelklick auf eine dieser Dateien öffnet sie in ihrem Programm."
msgid "File opening" msgid "File opening"
msgstr "Dateien öffnen" msgstr "Dateien öffnen"
msgid "File opening is off - this is what it would do."
msgstr "Dateien öffnen ist aus - so würde es aussehen."
msgid "File opening is off and everything that was changed has been put back." msgid "File opening is off and everything that was changed has been put back."
msgstr "" msgstr ""
"Dateien öffnen ist aus und alles, was geändert wurde, ist wiederhergestellt." "Dateien öffnen ist aus und alles, was geändert wurde, ist wiederhergestellt."
msgid "File opening is off. This is what it would do."
msgstr "Dateien öffnen ist aus. So würde es aussehen."
msgid "File opening is off. Turn it on first." msgid "File opening is off. Turn it on first."
msgstr "Dateien öffnen ist aus. Schalte es zuerst ein." msgstr "Dateien öffnen ist aus. Schalte es zuerst ein."
@@ -127,8 +127,8 @@ msgid "Looking at the bottles..."
msgstr "Bottles werden durchsucht..." msgstr "Bottles werden durchsucht..."
#, sh-printf-format #, sh-printf-format
msgid "More than one program is called \"%s\" - name the bottle as well." msgid "More than one program is called \"%s\". Name the bottle as well."
msgstr "Mehrere Programme heißen „%s“ - gib auch die Bottle an." msgstr "Mehrere Programme heißen „%s“. Gib auch die Bottle an."
msgid "New programs" msgid "New programs"
msgstr "Neue Programme" msgstr "Neue Programme"
@@ -162,16 +162,16 @@ msgid "No programs found in any bottle."
msgstr "In keiner Bottle wurden Programme gefunden." msgstr "In keiner Bottle wurden Programme gefunden."
msgid "" msgid ""
"No systemd user session - newly installed programs will not be picked up on " "No systemd user session, so newly installed programs will not be picked up "
"their own." "on their own."
msgstr "" msgstr ""
"Keine systemd-Benutzersitzung - neu installierte Programme werden nicht von " "Keine systemd-Benutzersitzung, daher werden neu installierte Programme nicht "
"selbst erkannt." "von selbst erkannt."
#, sh-printf-format #, sh-printf-format
msgid "Not overwriting %s - it was not written by this program." msgid "Not overwriting %s: this program did not write it."
msgstr "" msgstr ""
"%s wird nicht überschrieben - die Datei stammt nicht von diesem Programm." "%s wird nicht überschrieben, weil die Datei nicht von diesem Programm stammt."
#, sh-printf-format #, sh-printf-format
msgid "Nothing opens .%s." msgid "Nothing opens .%s."
@@ -261,20 +261,19 @@ msgstr "Ausschalten und alles wiederherstellen"
msgid "Unknown command: %s" msgid "Unknown command: %s"
msgstr "Unbekannter Befehl: %s" msgstr "Unbekannter Befehl: %s"
msgid "Up/Down select - Enter edits its file types - q goes back" msgid "Up/Down: select, Enter: edit file types, q: back"
msgstr "Hoch/Runter wählt - Enter bearbeitet die Dateitypen - q geht zurück" msgstr "Hoch/Runter: wählen, Enter: Dateitypen bearbeiten, q: zurück"
msgid "Up/Down select - Enter picks - q cancels" msgid "Up/Down: select, Enter: pick, q: cancel"
msgstr "Hoch/Runter wählt - Enter übernimmt - q bricht ab" msgstr "Hoch/Runter: wählen, Enter: übernehmen, q: abbrechen"
msgid "Up/Down select - Space changes - q goes back" msgid "Up/Down: select, Space: change, q: back"
msgstr "Hoch/Runter wählt - Leertaste ändert - q geht zurück" msgstr "Hoch/Runter: wählen, Leertaste: ändern, q: zurück"
msgid "" msgid "Up/Down: select, Space: switch, a: add, Del: remove, q: back"
"Up/Down select - Space switches - a adds one - Del removes - q goes back"
msgstr "" msgstr ""
"Hoch/Runter wählt - Leertaste schaltet um - a fügt hinzu - Entf entfernt - q " "Hoch/Runter: wählen, Leertaste: umschalten, a: hinzufügen, Entf: entfernen, "
"geht zurück" "q: zurück"
msgid "Usage: bottles-opener [command]" msgid "Usage: bottles-opener [command]"
msgstr "Aufruf: bottles-opener [Befehl]" msgstr "Aufruf: bottles-opener [Befehl]"
@@ -305,8 +304,8 @@ msgstr "Ohne Befehl wird ein interaktives Menü angezeigt."
msgid "added" msgid "added"
msgstr "hinzugefügt" msgstr "hinzugefügt"
msgid "could not be read - is python3 with PyYAML installed?" msgid "could not be read (is python3 with PyYAML installed?)"
msgstr "konnte nicht gelesen werden - ist python3 mit PyYAML installiert?" msgstr "konnte nicht gelesen werden (ist python3 mit PyYAML installiert?)"
msgid "double-click" msgid "double-click"
msgstr "Doppelklick" msgstr "Doppelklick"
@@ -317,8 +316,8 @@ msgstr "gerade eben"
msgid "never" msgid "never"
msgstr "nie" msgstr "nie"
msgid "none yet - add one under File types" msgid "none yet, add one under File types"
msgstr "noch keine - unter Dateitypen hinzufügen" msgstr "noch keine, unter Dateitypen hinzufügen"
msgid "not found" msgid "not found"
msgstr "nicht gefunden" msgstr "nicht gefunden"
+32
View File
@@ -0,0 +1,32 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-39.6 -28.1 596.2 596.2" width="512" height="512">
<defs>
<linearGradient id="glass" x1="0" y1="0" x2="1" y2="0"><stop offset="0" stop-color="#5fd0ff"/><stop offset=".45" stop-color="#2aa7e8"/><stop offset="1" stop-color="#0c62a0"/></linearGradient>
<linearGradient id="cork" x1="0" y1="0" x2="1" y2="0"><stop offset="0" stop-color="#e2ad74"/><stop offset="1" stop-color="#a86f3c"/></linearGradient>
<linearGradient id="paper" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#ffffff"/><stop offset="1" stop-color="#e6edf5"/></linearGradient>
<filter id="soft" x="-20%" y="-20%" width="140%" height="150%"><feDropShadow dx="0" dy="10" stdDeviation="12" flood-color="#04213a" flood-opacity=".35"/></filter>
</defs>
<g filter="url(#soft)">
<!-- bottle, tilted -->
<g transform="translate(40 50) rotate(-24 256 300)">
<!-- the file, popping out of the neck: in the bottle's own frame, narrower than the lip -->
<path d="M221 12 L269 12 L291 34 L291 128 L221 128 Z" fill="url(#paper)" stroke="#c9d5e3" stroke-width="2.5" stroke-linejoin="round"/>
<path d="M269 12 L269 28 C269 31.5 271.5 34 275 34 L291 34 Z" fill="#c9d5e3"/>
<rect x="232" y="48" width="38" height="7.5" rx="3.75" fill="#1793d1"/>
<rect x="232" y="64" width="48" height="6.5" rx="3.25" fill="#9fb0c4"/>
<rect x="232" y="78" width="48" height="6.5" rx="3.25" fill="#9fb0c4"/>
<rect x="232" y="92" width="30" height="6.5" rx="3.25" fill="#9fb0c4"/>
<path d="M226 128 L226 206 C226 238 166 250 166 300 L166 412 C166 432 182 448 202 448 L310 448 C330 448 346 432 346 412 L346 300 C346 250 286 238 286 206 L286 128 Z" fill="url(#glass)"/>
<path d="M196 304 C196 276 212 262 232 254" fill="none" stroke="#fff" stroke-opacity=".55" stroke-width="14" stroke-linecap="round"/>
<path d="M196 332 L196 410" stroke="#fff" stroke-opacity=".35" stroke-width="14" stroke-linecap="round"/>
<path d="M244 140 L244 196" stroke="#fff" stroke-opacity=".4" stroke-width="8" stroke-linecap="round"/>
<rect x="216" y="108" width="80" height="26" rx="9" fill="#0b5a92"/>
</g>
</g>
<!-- pop -->
<g stroke="#f5a524" stroke-width="12" stroke-linecap="round">
<line x1="210" y1="24" x2="222" y2="0"/>
<line x1="264" y1="60" x2="292" y2="48"/>
<line x1="104" y1="92" x2="78" y2="80"/>
<line x1="98" y1="146" x2="70" y2="150"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

+1 -1
View File
@@ -6,7 +6,7 @@ Documentation=man:bottles-opener(1)
# A bottle that is created or deleted. Where the bottles are depends on how # A bottle that is created or deleted. Where the bottles are depends on how
# Bottles was installed: a distribution package keeps them in the user's data # Bottles was installed: a distribution package keeps them in the user's data
# directory, the Flatpak in its own. A directory that does not exist on this # directory, the Flatpak in its own. A directory that does not exist on this
# system costs nothing - systemd watches the nearest parent that does and picks # system costs nothing: systemd watches the nearest parent that does and picks
# the path up when it appears. # the path up when it appears.
PathModified=%h/.local/share/bottles/bottles PathModified=%h/.local/share/bottles/bottles
PathModified=%h/.var/app/com.usebottles.bottles/data/bottles/bottles PathModified=%h/.var/app/com.usebottles.bottles/data/bottles/bottles
+4 -4
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# #
# bottles-opener - double-click a file and it opens in its Windows program, # bottles-opener: double-click a file and it opens in its Windows program,
# inside its bottle # inside its bottle
# #
# Copyright (C) 2026 Felitendo # Copyright (C) 2026 Felitendo
@@ -35,7 +35,7 @@ fi
_bo_need_bottles() { _bo_need_bottles() {
bo_installs_detect bo_installs_detect
(( ${#BO_INSTALLS[@]} )) && return 0 (( ${#BO_INSTALLS[@]} )) && return 0
bo_bad "$(bo_msg "Bottles is not installed - neither the package nor the Flatpak was found.")" bo_bad "$(bo_msg "Bottles is not installed. Neither the package nor the Flatpak was found.")"
return 1 return 1
} }
@@ -63,7 +63,7 @@ bo_do_enable() {
if bo_watch_available; then if bo_watch_available; then
bo_watch_enable || bo_note "$(bo_msg "Could not start the watcher for newly installed programs.")" bo_watch_enable || bo_note "$(bo_msg "Could not start the watcher for newly installed programs.")"
else else
bo_note "$(bo_msg "No systemd user session - newly installed programs will not be picked up on their own.")" bo_note "$(bo_msg "No systemd user session, so newly installed programs will not be picked up on their own.")"
fi fi
fi fi
} }
@@ -220,7 +220,7 @@ _bo_find_program() {
if (( ${#hits[@]} == 0 )); then if (( ${#hits[@]} == 0 )); then
bo_bad "$(bo_msg "No program called \"%s\" was found. \"bottles-opener programs\" lists them all." "$want")" bo_bad "$(bo_msg "No program called \"%s\" was found. \"bottles-opener programs\" lists them all." "$want")"
else else
bo_bad "$(bo_msg "More than one program is called \"%s\" - name the bottle as well." "$want")" bo_bad "$(bo_msg "More than one program is called \"%s\". Name the bottle as well." "$want")"
fi fi
return 1 return 1
} }
+2 -2
View File
@@ -89,7 +89,7 @@ _bo_file_put() {
local path="$1" content="$2" rc local path="$1" content="$2" rc
if ! _bo_owned "$path"; then if ! _bo_owned "$path"; then
bo_bad "$(bo_msg "Not overwriting %s - it was not written by this program." "$path")" bo_bad "$(bo_msg "Not overwriting %s: this program did not write it." "$path")"
return 1 return 1
fi fi
@@ -193,7 +193,7 @@ bo_apply() {
# Unreadable looks like empty, and converging on empty removes everything. # Unreadable looks like empty, and converging on empty removes everything.
if [[ -n $BO_SCAN_ERROR ]]; then if [[ -n $BO_SCAN_ERROR ]]; then
bo_bad "$(bo_msg "Bottles could not be read - is python3 with PyYAML installed? Nothing was changed.")" bo_bad "$(bo_msg "Bottles could not be read. Is python3 with PyYAML installed? Nothing was changed.")"
return 1 return 1
fi fi
+2 -2
View File
@@ -112,8 +112,8 @@ bo_assoc_compute() {
done done
# A detected type is the default when this program teaches the system the # A detected type is the default when this program teaches the system the
# type - nothing else could open it. A type the system knows already # type, because nothing else could open it. A type the system knows already
# belongs to some application somebody chose, and only gets "Open With". # belongs to an app somebody chose, so it only gets "Open With".
for k in "${!BO_A_EXT[@]}"; do for k in "${!BO_A_EXT[@]}"; do
[[ ${BO_A_STATE[k]} == auto ]] || continue [[ ${BO_A_STATE[k]} == auto ]] || continue
ext="${BO_A_EXT[k]}" ext="${BO_A_EXT[k]}"
+1 -1
View File
@@ -145,7 +145,7 @@ bo_types_set() {
mkdir -p "$(dirname "$BO_TYPES")" || return 1 mkdir -p "$(dirname "$BO_TYPES")" || return 1
if [[ ! -e $BO_TYPES ]]; then if [[ ! -e $BO_TYPES ]]; then
{ {
printf '# %s - file types changed by hand\n' "$BO_PRETTY" printf '# %s: file types changed by hand\n' "$BO_PRETTY"
printf '#\n' printf '#\n'
printf '# Written by `%s`. Nothing here needs editing by hand.\n' "$BO_NAME" printf '# Written by `%s`. Nothing here needs editing by hand.\n' "$BO_NAME"
} > "$BO_TYPES" || return 1 } > "$BO_TYPES" || return 1
+2 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# #
# Writes one icon of a Windows program as PNG to stdout - the largest image of # Writes one icon of a Windows program as PNG to stdout. It picks the largest
# it, the way Explorer would pick it for a big view. # image, like Explorer does for a big view.
# #
# Usage: icon.py <file> [index] # Usage: icon.py <file> [index]
# <file> an .exe or .dll, an .ico, or a .png # <file> an .exe or .dll, an .ico, or a .png
+4 -4
View File
@@ -4,7 +4,7 @@
# #
# The bottle's registry is asked first (scan.py): whatever an installer # The bottle's registry is asked first (scan.py): whatever an installer
# registered there is what Windows would open. This table is for programs # registered there is what Windows would open. This table is for programs
# that registered nothing - portable installs, copied folders. # that registered nothing, like portable installs or copied folders.
# Format: <pattern>|<ext>[=<switch>] ... # Format: <pattern>|<ext>[=<switch>] ...
# #
@@ -80,9 +80,9 @@ BO_KNOWN_PROGRAMS=(
# Format: <ext>|<MIME type>|<description>|<magic>|<generic icon>|<flags> # Format: <ext>|<MIME type>|<description>|<magic>|<generic icon>|<flags>
# #
# <magic>: string:<text> or big32:<hex>, matched at offset 0. # <magic>: string:<text> or big32:<hex>, matched at offset 0.
# <flags>: "own" defines the type even where the system has the extension # <flags>: "own" defines the type even where the system already uses the
# already, as something else - .mpp is Musepack audio there, not a Project # extension for something else. For example, .mpp is Musepack audio there, not
# file. The magic tells the two apart. # a Project file. The magic tells the two apart.
BO_KNOWN_TYPES=( BO_KNOWN_TYPES=(
# Office # Office
"ppa|application/x-ms-powerpoint-addin|PowerPoint add-in||x-office-presentation|" "ppa|application/x-ms-powerpoint-addin|PowerPoint add-in||x-office-presentation|"
+33 -9
View File
@@ -121,6 +121,26 @@ _bo_row() {
printf ' %s%*s %s\n' "$label" "$pad" '' "$value" printf ' %s%*s %s\n' "$label" "$pad" '' "$value"
} }
# _bo_fit <width> <words>
# As many of the words as fit in width, the rest counted: ".doc .flp +12".
_bo_fit() {
local width="$1" out='' w n=0 total rest room
local -a words
read -r -a words <<< "$2"
total=${#words[@]}
for w in "${words[@]}"; do
# Leave room for the count of what does not fit.
rest=$(( total - n - 1 ))
room=$(( width - ${#out} - (n > 0 ? 1 : 0) - ${#w} ))
(( rest > 0 )) && room=$(( room - ${#rest} - 2 ))
(( room < 0 )) && break
out+="${out:+ }$w"
n=$(( n + 1 ))
done
(( n < total )) && out+="${out:+ }+$(( total - n ))"
printf '%s\n' "$out"
}
_bo_onoff() { _bo_onoff() {
if [[ $1 == yes ]]; then if [[ $1 == yes ]]; then
printf '%s%s%s' "$BO_C_GREEN" "$(bo_msg "ON")" "$BO_C_RESET" printf '%s%s%s' "$BO_C_GREEN" "$(bo_msg "ON")" "$BO_C_RESET"
@@ -165,7 +185,7 @@ _bo_scroll() {
# bo_ui_status # bo_ui_status
# For `status` and the menu header, from a fresh scan. # For `status` and the menu header, from a fresh scan.
bo_ui_status() { bo_ui_status() {
local last exts nb=0 np install where='' local last exts nb=0 np install where='' cols
bo_assoc_compute bo_assoc_compute
bo_assoc_counts bo_assoc_counts
@@ -184,7 +204,7 @@ bo_ui_status() {
_bo_row "Bottles" "${BO_C_YELLOW}$(bo_msg "not found")${BO_C_RESET}" _bo_row "Bottles" "${BO_C_YELLOW}$(bo_msg "not found")${BO_C_RESET}"
elif [[ -n $BO_SCAN_ERROR ]]; then elif [[ -n $BO_SCAN_ERROR ]]; then
_bo_row "Bottles" \ _bo_row "Bottles" \
"${BO_C_RED}$(bo_msg "could not be read - is python3 with PyYAML installed?")${BO_C_RESET}" "${BO_C_RED}$(bo_msg "could not be read (is python3 with PyYAML installed?)")${BO_C_RESET}"
else else
_bo_row "Bottles" "$where" _bo_row "Bottles" "$where"
fi fi
@@ -197,11 +217,15 @@ bo_ui_status() {
_bo_row "$(bo_msg "Programs found")" "$(bo_msg "%d in %d bottles" "$np" "$nb")" _bo_row "$(bo_msg "Programs found")" "$(bo_msg "%d in %d bottles" "$np" "$nb")"
fi fi
exts="$(bo_assoc_active_exts 10)" # On one line: the value starts in column 34 of at most 80.
cols="$(tput cols 2>/dev/null)" || cols=80
[[ $cols =~ ^[0-9]+$ ]] || cols=80
(( cols > 80 )) && cols=80
exts="$(_bo_fit $(( cols - 35 )) "$(bo_assoc_active_exts)")"
if (( BO_N_ACTIVE )); then if (( BO_N_ACTIVE )); then
_bo_row "$(bo_msg "File types")" "$exts" _bo_row "$(bo_msg "File types")" "$exts"
else else
_bo_row "$(bo_msg "File types")" "${BO_C_DIM}$(bo_msg "none yet - add one under File types")${BO_C_RESET}" _bo_row "$(bo_msg "File types")" "${BO_C_DIM}$(bo_msg "none yet, add one under File types")${BO_C_RESET}"
fi fi
if bo_watch_available; then if bo_watch_available; then
@@ -250,7 +274,7 @@ bo_ui_settings() {
done done
bo_msg_into "Settings"; title="$BO_MSG_RESULT" bo_msg_into "Settings"; title="$BO_MSG_RESULT"
bo_msg_into "Up/Down select - Space changes - q goes back" bo_msg_into "Up/Down: select, Space: change, q: back"
hint="$BO_MSG_RESULT" hint="$BO_MSG_RESULT"
_bo_clearseq _bo_clearseq
@@ -327,7 +351,7 @@ bo_ui_pick_program() {
labels+=("$BO_LABEL") labels+=("$BO_LABEL")
done done
bo_msg_into "Up/Down select - Enter picks - q cancels" bo_msg_into "Up/Down: select, Enter: pick, q: cancel"
hint="$BO_MSG_RESULT" hint="$BO_MSG_RESULT"
_bo_clearseq _bo_clearseq
_bo_term_rows 7 _bo_term_rows 7
@@ -416,7 +440,7 @@ bo_ui_programs() {
local -a labels=() pexts=() local -a labels=() pexts=()
bo_msg_into "Programs"; title="$BO_MSG_RESULT" bo_msg_into "Programs"; title="$BO_MSG_RESULT"
bo_msg_into "Up/Down select - Enter edits its file types - q goes back" bo_msg_into "Up/Down: select, Enter: edit file types, q: back"
hint="$BO_MSG_RESULT" hint="$BO_MSG_RESULT"
bo_msg_into "No programs found in any bottle."; empty="$BO_MSG_RESULT" bo_msg_into "No programs found in any bottle."; empty="$BO_MSG_RESULT"
@@ -501,11 +525,11 @@ bo_ui_types() {
local l_default l_openwith l_off l_missing l_added local l_default l_openwith l_off l_missing l_added
bo_msg_into "File types"; title="$BO_MSG_RESULT" bo_msg_into "File types"; title="$BO_MSG_RESULT"
bo_msg_into "Up/Down select - Space switches - a adds one - Del removes - q goes back" bo_msg_into "Up/Down: select, Space: switch, a: add, Del: remove, q: back"
hint="$BO_MSG_RESULT" hint="$BO_MSG_RESULT"
bo_msg_into "\"double-click\" opens it; \"Open With\" only lists the program there." bo_msg_into "\"double-click\" opens it; \"Open With\" only lists the program there."
legend="$BO_MSG_RESULT" legend="$BO_MSG_RESULT"
bo_msg_into "File opening is off - this is what it would do." bo_msg_into "File opening is off. This is what it would do."
warn="$BO_MSG_RESULT" warn="$BO_MSG_RESULT"
bo_msg_into "No file types yet. Press a to add one." bo_msg_into "No file types yet. Press a to add one."
empty="$BO_MSG_RESULT" empty="$BO_MSG_RESULT"
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# #
# Lists the bottles and programs of one Bottles installation, and the file # Lists the bottles and programs of one Bottles installation, and the file
# types each program registered in its bottle's registry - the way Windows # types each program registered in its bottle's registry. That is how Windows
# knows them: extension -> ProgID -> open command, icon and description. # knows them: extension -> ProgID -> open command, icon and description.
# #
# Runs on the host, or inside the Bottles Flatpak fed through stdin, so it only # Runs on the host, or inside the Bottles Flatpak fed through stdin, so it only