Compare commits
No commits in common. "main" and "feature/ui" have entirely different histories.
main
...
feature/ui
16
README.md
16
README.md
@ -28,13 +28,9 @@ Vlastnosti:
|
||||
|
||||
---
|
||||
|
||||
## Rychlý start (GUI)
|
||||
## Rychlý start (gui)
|
||||
|
||||
```bash
|
||||
# 1) po buildnutí (go build .) stačí spustit binárku – otevře se GUI
|
||||
./fckeuspy-go
|
||||
|
||||
# Alternativně přes go run
|
||||
go run . gui
|
||||
```
|
||||
|
||||
@ -137,15 +133,11 @@ Formát payloadu:
|
||||
|
||||
---
|
||||
|
||||
## Spuštění (GUI / Web)
|
||||
## Fyne GUI režim
|
||||
|
||||
Spuštění desktopu (GUI):
|
||||
Spuštění desktopu:
|
||||
|
||||
```bash
|
||||
# po buildnutí (go build .)
|
||||
./fckeuspy-go
|
||||
|
||||
# nebo přes go run
|
||||
go run . gui
|
||||
```
|
||||
|
||||
@ -161,8 +153,6 @@ HTTP režim zůstává:
|
||||
|
||||
```bash
|
||||
go run . web
|
||||
# nebo po buildnutí (go build .)
|
||||
./fckeuspy-go web
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
10
cmd.go
10
cmd.go
@ -7,13 +7,9 @@ import (
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "fckeuspy",
|
||||
Short: "Hybrid RSA+AES šifrovací nástroj (GUI / HTTP)",
|
||||
Long: "Fckeuspy: nástroj pro šifrování a dešifrování pomocí hybridního RSA-OAEP + AES-GCM, s Fyne GUI a HTTP serverem.",
|
||||
// Default: run GUI when executed without subcommand
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
runFyne()
|
||||
},
|
||||
Use: "fckeuspy",
|
||||
Short: "Hybrid RSA+AES šifrovací nástroj (GUI / HTTP)",
|
||||
Long: "Fckeuspy: nástroj pro šifrování a dešifrování pomocí hybridního RSA-OAEP + AES-GCM, s Fyne GUI a HTTP serverem.",
|
||||
}
|
||||
|
||||
var guiCmd = &cobra.Command{
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"image/png"
|
||||
"log"
|
||||
"math"
|
||||
"os/exec"
|
||||
|
||||
@ -31,12 +32,17 @@ func GenerateQRPNG(text string, size int) ([]byte, error) {
|
||||
|
||||
// DecodeQR decodes first QR code text from an image.
|
||||
func DecodeQR(img image.Image) (string, error) {
|
||||
log.Printf("[qr] start decode: %dx%d %T", img.Bounds().Dx(), img.Bounds().Dy(), img)
|
||||
// Try basic decode first
|
||||
codes, err := goqr.Recognize(img)
|
||||
if err == nil && len(codes) > 0 {
|
||||
log.Printf("[qr] basic success length=%d", len(codes))
|
||||
return string(codes[0].Payload), nil
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("[qr] basic error: %v", err)
|
||||
} else {
|
||||
log.Printf("[qr] basic no codes")
|
||||
}
|
||||
|
||||
// Convert palette/indexed images to RGBA first to avoid issues with goqr
|
||||
@ -49,8 +55,14 @@ func DecodeQR(img image.Image) (string, error) {
|
||||
// Try again after conversion
|
||||
codes, err := goqr.Recognize(img)
|
||||
if err == nil && len(codes) > 0 {
|
||||
log.Printf("[qr] palette-convert success")
|
||||
return string(codes[0].Payload), nil
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("[qr] palette-convert err: %v", err)
|
||||
} else {
|
||||
log.Printf("[qr] palette-convert no codes")
|
||||
}
|
||||
}
|
||||
|
||||
// Try multiple preprocessing strategies to improve recognition from clipboard screenshots
|
||||
@ -201,17 +213,20 @@ func DecodeQR(img image.Image) (string, error) {
|
||||
}
|
||||
|
||||
var firstErr error
|
||||
for _, attempt := range attempts {
|
||||
for idx, attempt := range attempts {
|
||||
codes, err := goqr.Recognize(attempt)
|
||||
if err != nil {
|
||||
if firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
log.Printf("[qr] attempt %d error: %v", idx, err)
|
||||
continue
|
||||
}
|
||||
if len(codes) > 0 {
|
||||
log.Printf("[qr] attempt %d success (%d codes)", idx, len(codes))
|
||||
return string(codes[0].Payload), nil
|
||||
}
|
||||
log.Printf("[qr] attempt %d no codes", idx)
|
||||
}
|
||||
|
||||
// ZXing helper used across multiple branches
|
||||
@ -232,19 +247,25 @@ func DecodeQR(img image.Image) (string, error) {
|
||||
}
|
||||
}
|
||||
// Also try ZXing on all preprocessed attempts
|
||||
for _, attempt := range attempts {
|
||||
for idx, attempt := range attempts {
|
||||
if txt, err := decodeZX(attempt, map[gozxing.DecodeHintType]interface{}{
|
||||
gozxing.DecodeHintType_TRY_HARDER: true,
|
||||
gozxing.DecodeHintType_PURE_BARCODE: true,
|
||||
}); err == nil && txt != "" {
|
||||
log.Printf("[qr] zxing attempt %d success (pure)", idx)
|
||||
return txt, nil
|
||||
}
|
||||
if txt, err := decodeZX(attempt, map[gozxing.DecodeHintType]interface{}{
|
||||
gozxing.DecodeHintType_TRY_HARDER: true,
|
||||
}); err == nil && txt != "" {
|
||||
log.Printf("[qr] zxing attempt %d success (try-harder)", idx)
|
||||
return txt, nil
|
||||
}
|
||||
}
|
||||
if firstErr != nil {
|
||||
log.Printf("[qr] preprocess firstErr: %v", firstErr)
|
||||
}
|
||||
|
||||
// --- Heuristic fallback: auto-invert + quiet-zone pad + gozxing ---
|
||||
addQuietZone := func(src image.Image) image.Image {
|
||||
b := src.Bounds()
|
||||
@ -294,6 +315,7 @@ func DecodeQR(img image.Image) (string, error) {
|
||||
modified = append(modified, addQuietZone(invert(base)))
|
||||
for _, m := range modified {
|
||||
if codes, err := goqr.Recognize(m); err == nil && len(codes) > 0 {
|
||||
log.Printf("[qr] quiet-zone/invert success")
|
||||
return string(codes[0].Payload), nil
|
||||
}
|
||||
// Also try ZXing on these variants before moving on
|
||||
@ -301,11 +323,13 @@ func DecodeQR(img image.Image) (string, error) {
|
||||
gozxing.DecodeHintType_TRY_HARDER: true,
|
||||
gozxing.DecodeHintType_PURE_BARCODE: true,
|
||||
}); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing success (quiet-zone+pure)")
|
||||
return txt, nil
|
||||
}
|
||||
if txt, err := decodeZX(m, map[gozxing.DecodeHintType]interface{}{
|
||||
gozxing.DecodeHintType_TRY_HARDER: true,
|
||||
}); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing success (quiet-zone+try-harder)")
|
||||
return txt, nil
|
||||
}
|
||||
}
|
||||
@ -316,19 +340,27 @@ func DecodeQR(img image.Image) (string, error) {
|
||||
gozxing.DecodeHintType_PURE_BARCODE: true,
|
||||
}
|
||||
if txt, err := decodeZX(base, hints); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing success (hints)")
|
||||
return txt, nil
|
||||
} else if err != nil {
|
||||
log.Printf("[qr] gozxing(hints) err: %v", err)
|
||||
}
|
||||
// Try again without PURE_BARCODE in case quiet zone is cropped
|
||||
hints2 := map[gozxing.DecodeHintType]interface{}{gozxing.DecodeHintType_TRY_HARDER: true}
|
||||
if txt, err := decodeZX(base, hints2); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing success (try-harder)")
|
||||
return txt, nil
|
||||
} else if err != nil {
|
||||
log.Printf("[qr] gozxing(try-harder) err: %v", err)
|
||||
}
|
||||
// Try ZXing on rotations as well
|
||||
for _, r := range rotate(base) {
|
||||
if txt, err := decodeZX(r, hints2); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing success (rotated)")
|
||||
return txt, nil
|
||||
}
|
||||
if txt, err := decodeZX(r, hints); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing success (rotated pure)")
|
||||
return txt, nil
|
||||
}
|
||||
}
|
||||
@ -360,6 +392,7 @@ func DecodeQR(img image.Image) (string, error) {
|
||||
tryPure := func(src image.Image) (string, bool) {
|
||||
for _, px := range []int{1, 2, 3} {
|
||||
if txt, err := decodeZX(inset(src, px), hints); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing pure+inset %d success", px)
|
||||
return txt, true
|
||||
}
|
||||
}
|
||||
@ -392,9 +425,11 @@ func DecodeQR(img image.Image) (string, error) {
|
||||
continue
|
||||
}
|
||||
if txt, err := decodeZX(cand, hints); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing pure+asym inset %d,%d,%d,%d success", l, t, r, btm)
|
||||
return txt, nil
|
||||
}
|
||||
if txt, err := decodeZX(cand, hints2); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing asym inset %d,%d,%d,%d success", l, t, r, btm)
|
||||
return txt, nil
|
||||
}
|
||||
}
|
||||
@ -430,6 +465,7 @@ func DecodeQR(img image.Image) (string, error) {
|
||||
if minX < maxX && minY < maxY {
|
||||
crop := image.NewRGBA(image.Rect(0, 0, maxX-minX+1, maxY-minY+1))
|
||||
draw.Draw(crop, crop.Bounds(), base, image.Point{X: minX, Y: minY}, draw.Src)
|
||||
log.Printf("[qr] crop bbox size=%dx%d", crop.Bounds().Dx(), crop.Bounds().Dy())
|
||||
for _, up := range []int{2, 3, 4} {
|
||||
cw := crop.Bounds().Dx() * up
|
||||
ch := crop.Bounds().Dy() * up
|
||||
@ -445,7 +481,10 @@ func DecodeQR(img image.Image) (string, error) {
|
||||
}
|
||||
}
|
||||
if txt, err := decodeZX(scaled, hints2); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing crop+scale success up=%d", up)
|
||||
return txt, nil
|
||||
} else if err != nil {
|
||||
log.Printf("[qr] crop up=%d fail: %v", up, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -460,8 +499,12 @@ func DecodeQR(img image.Image) (string, error) {
|
||||
c.Stdin = bytes.NewReader(buf.Bytes())
|
||||
out, err := c.Output()
|
||||
if err == nil && len(out) > 0 {
|
||||
log.Printf("[qr] zbarimg success")
|
||||
return string(out), nil
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("[qr] zbarimg error: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", errors.New("no qr code found")
|
||||
|
||||
190
ui.go
190
ui.go
@ -10,6 +10,7 @@ import (
|
||||
"image/color"
|
||||
"image/png"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@ -54,7 +55,6 @@ func buildEntries() *uiParts {
|
||||
p.pubQR.SetMinSize(fyne.NewSize(200, 200))
|
||||
p.peerQR.SetMinSize(fyne.NewSize(200, 200))
|
||||
p.payloadQR.SetMinSize(fyne.NewSize(220, 220))
|
||||
p.pubQR.FillMode = canvas.ImageFillContain
|
||||
p.toastLabel.Hide()
|
||||
return p
|
||||
}
|
||||
@ -74,15 +74,10 @@ func (p *uiParts) showToast(s string) {
|
||||
type simpleTheme struct{}
|
||||
|
||||
func (simpleTheme) Color(n fyne.ThemeColorName, v fyne.ThemeVariant) color.Color {
|
||||
switch n {
|
||||
case theme.ColorNameBackground:
|
||||
if n == theme.ColorNameBackground {
|
||||
return color.NRGBA{24, 27, 31, 255}
|
||||
case theme.ColorNameDisabled:
|
||||
// Make disabled text brighter for readability on dark background
|
||||
return color.NRGBA{230, 233, 238, 255}
|
||||
default:
|
||||
return theme.DefaultTheme().Color(n, v)
|
||||
}
|
||||
return theme.DefaultTheme().Color(n, v)
|
||||
}
|
||||
func (simpleTheme) Font(st fyne.TextStyle) fyne.Resource { return theme.DefaultTheme().Font(st) }
|
||||
func (simpleTheme) Icon(n fyne.ThemeIconName) fyne.Resource { return theme.DefaultTheme().Icon(n) }
|
||||
@ -177,6 +172,7 @@ func readImageClipboard() (image.Image, error) {
|
||||
|
||||
if os.Getenv("WAYLAND_DISPLAY") != "" && has("wl-paste") {
|
||||
if types, err := exec.Command("wl-paste", "--list-types").Output(); err == nil {
|
||||
log.Printf("[clip] wl types: %s", strings.TrimSpace(string(types)))
|
||||
pref := []string{"image/png", "image/jpeg", "image/jpg", "image/webp"}
|
||||
seen := map[string]bool{}
|
||||
for _, p := range pref {
|
||||
@ -194,13 +190,23 @@ func readImageClipboard() (image.Image, error) {
|
||||
}
|
||||
for _, t := range order {
|
||||
if data, err := exec.Command("wl-paste", "--type", t).Output(); err == nil {
|
||||
log.Printf("[clip] got type %s size=%d", t, len(data))
|
||||
if img, ok := tryDecode(data); ok {
|
||||
// save debug
|
||||
_ = os.MkdirAll("qr_debug", 0o755)
|
||||
fn := filepath.Join("qr_debug", fmt.Sprintf("clip_raw_%d.png", time.Now().UnixNano()))
|
||||
if f, e := os.Create(fn); e == nil {
|
||||
_ = png.Encode(f, img)
|
||||
f.Close()
|
||||
log.Printf("[clip] saved %s", fn)
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if data, err := exec.Command("wl-paste").Output(); err == nil {
|
||||
log.Printf("[clip] generic wl-paste size=%d", len(data))
|
||||
if img, ok := tryDecode(data); ok {
|
||||
return img, nil
|
||||
}
|
||||
@ -209,12 +215,14 @@ func readImageClipboard() (image.Image, error) {
|
||||
if has("xclip") {
|
||||
for _, m := range []string{"image/png", "image/jpeg", "image/jpg", "image/bmp"} {
|
||||
if data, err := exec.Command("xclip", "-selection", "clipboard", "-t", m, "-o").Output(); err == nil {
|
||||
log.Printf("[clip] xclip type %s size=%d", m, len(data))
|
||||
if img, ok := tryDecode(data); ok {
|
||||
return img, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if data, err := exec.Command("xclip", "-selection", "clipboard", "-o").Output(); err == nil {
|
||||
log.Printf("[clip] xclip raw size=%d", len(data))
|
||||
if img, ok := tryDecode(data); ok {
|
||||
return img, nil
|
||||
}
|
||||
@ -226,19 +234,18 @@ func readImageClipboard() (image.Image, error) {
|
||||
// Identity tab
|
||||
func buildIdentityTab(parts *uiParts, svc ServiceFacade, vaultPath string) fyne.CanvasObject {
|
||||
// Toolbar: choose what to encode into the QR to keep density manageable
|
||||
mode := "cert" // cert, pub
|
||||
options := []string{"Certifikát", "Veřejný klíč"}
|
||||
chooser := widget.NewSelect(options, func(string) {})
|
||||
chooser.Selected = options[0]
|
||||
mode := "combined" // combined, cert, pub
|
||||
chooser := widget.NewSelect([]string{"Veřejný+Cert", "Jen Cert", "Jen Veřejný"}, func(string) {})
|
||||
chooser.Selected = "Veřejný+Cert"
|
||||
var update func()
|
||||
chooser.OnChanged = func(v string) {
|
||||
switch v {
|
||||
case "Certifikát":
|
||||
case "Jen Cert":
|
||||
mode = "cert"
|
||||
case "Veřejný klíč":
|
||||
case "Jen Veřejný":
|
||||
mode = "pub"
|
||||
default:
|
||||
mode = "cert"
|
||||
mode = "combined"
|
||||
}
|
||||
update()
|
||||
}
|
||||
@ -260,8 +267,6 @@ func buildIdentityTab(parts *uiParts, svc ServiceFacade, vaultPath string) fyne.
|
||||
d.Resize(fyne.NewSize(420, 200))
|
||||
d.Show()
|
||||
})
|
||||
// Keep button minimal; align right
|
||||
deleteRow := container.NewHBox(layout.NewSpacer(), deleteBtn)
|
||||
makeQR := func(data string, target *canvas.Image) {
|
||||
if data == "" {
|
||||
target.Image = nil
|
||||
@ -279,51 +284,38 @@ func buildIdentityTab(parts *uiParts, svc ServiceFacade, vaultPath string) fyne.
|
||||
if parts.showQR {
|
||||
var text string
|
||||
switch mode {
|
||||
case "cert":
|
||||
text = strings.TrimSpace(svc.PublicCert())
|
||||
case "pub":
|
||||
text = strings.TrimSpace(svc.PublicPEM())
|
||||
default: // cert
|
||||
text = strings.TrimSpace(svc.PublicCert())
|
||||
default:
|
||||
text = strings.TrimSpace(svc.PublicPEM() + "\n" + svc.PublicCert())
|
||||
}
|
||||
makeQR(text, parts.pubQR)
|
||||
// debug save for comparison
|
||||
if parts.pubQR.Image != nil {
|
||||
_ = os.MkdirAll("qr_debug", 0o755)
|
||||
if f, e := os.Create(filepath.Join("qr_debug", "identity_current.png")); e == nil {
|
||||
png.Encode(f, parts.pubQR.Image)
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parts.pubQR.Image = nil
|
||||
parts.pubQR.Refresh()
|
||||
}
|
||||
}
|
||||
update()
|
||||
saveQR := widget.NewButtonWithIcon("Uložit QR", theme.DocumentSaveIcon(), func() {
|
||||
if parts.pubQR.Image == nil {
|
||||
return
|
||||
}
|
||||
win := fyne.CurrentApp().Driver().AllWindows()[0]
|
||||
img := parts.pubQR.Image
|
||||
fd := dialog.NewFileSave(func(wc fyne.URIWriteCloser, err error) {
|
||||
if err != nil || wc == nil {
|
||||
return
|
||||
}
|
||||
defer wc.Close()
|
||||
_ = png.Encode(wc, img)
|
||||
parts.showToast("QR uložen")
|
||||
}, win)
|
||||
fd.SetFileName("identity_qr.png")
|
||||
fd.Show()
|
||||
})
|
||||
qrRow := container.NewHBox(
|
||||
widget.NewLabel("Obsah QR"),
|
||||
chooser,
|
||||
layout.NewSpacer(),
|
||||
widget.NewButtonWithIcon("Kopírovat jako obrázek", theme.ContentPasteIcon(), func() { copyImageToClipboard(parts.pubQR.Image, parts) }),
|
||||
saveQR,
|
||||
)
|
||||
box := container.NewVBox(qrRow, container.NewCenter(parts.pubQR))
|
||||
return container.NewVScroll(container.NewVBox(container.NewHBox(widget.NewLabelWithStyle("Moje identita", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}), layout.NewSpacer()), box, deleteRow))
|
||||
box := container.NewVBox(container.NewHBox(widget.NewLabel("QR obsah:"), chooser, layout.NewSpacer(), widget.NewButtonWithIcon("Kopírovat jako obrázek", theme.ContentPasteIcon(), func() { copyImageToClipboard(parts.pubQR.Image, parts) })), parts.pubQR)
|
||||
return container.NewVScroll(container.NewVBox(container.NewHBox(widget.NewLabelWithStyle("Moje identita", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}), layout.NewSpacer()), box, deleteBtn))
|
||||
}
|
||||
|
||||
// Decrypt tab
|
||||
func buildDecryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
parts.plainOut.Disable()
|
||||
parts.plainOut.Wrapping = fyne.TextWrapWord
|
||||
parts.plainOut.SetMinRowsVisible(12)
|
||||
parts.payload.Disable()
|
||||
parts.payload.SetPlaceHolder("Cyphertext se načte po vložení QR kódu…")
|
||||
parts.payload.OnChanged = nil
|
||||
parts.payloadQR.FillMode = canvas.ImageFillContain
|
||||
parts.payloadQR.SetMinSize(fyne.NewSize(260, 260))
|
||||
|
||||
@ -347,6 +339,7 @@ func buildDecryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
}
|
||||
|
||||
setPayload := func(cipher string, img image.Image) {
|
||||
parts.payload.SetText(cipher)
|
||||
parts.payloadQR.Image = img
|
||||
parts.payloadQR.Refresh()
|
||||
decrypt(cipher)
|
||||
@ -411,26 +404,30 @@ func buildDecryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
fd.Show()
|
||||
})
|
||||
|
||||
copyPayloadBtn := widget.NewButtonWithIcon("Kopírovat payload", theme.ContentCopyIcon(), func() {
|
||||
if parts.payload.Text == "" {
|
||||
return
|
||||
}
|
||||
copyClip(parts.payload.Text, parts)
|
||||
})
|
||||
|
||||
clearBtn := widget.NewButtonWithIcon("Vymazat", theme.ContentClearIcon(), func() {
|
||||
parts.payload.SetText("")
|
||||
parts.payloadQR.Image = nil
|
||||
parts.payloadQR.Refresh()
|
||||
parts.plainOut.SetText("")
|
||||
parts.showToast("Vymazáno")
|
||||
})
|
||||
|
||||
// Align buttons to the right by placing spacer first
|
||||
toolbar := container.NewHBox(layout.NewSpacer(), pasteQRBtn, openQRBtn, clearBtn)
|
||||
toolbar := container.NewHBox(pasteQRBtn, openQRBtn, copyPayloadBtn, clearBtn, layout.NewSpacer())
|
||||
|
||||
copyDecBtn := widget.NewButtonWithIcon("Kopírovat zprávu", theme.ContentCopyIcon(), func() {
|
||||
if strings.TrimSpace(parts.plainOut.Text) != "" {
|
||||
copyClip(parts.plainOut.Text, parts)
|
||||
}
|
||||
})
|
||||
return container.NewVBox(
|
||||
widget.NewLabelWithStyle("Dešifrování", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||
toolbar,
|
||||
container.NewHBox(parts.payloadQR, layout.NewSpacer()),
|
||||
container.NewHBox(widget.NewLabel("Výsledek"), layout.NewSpacer(), copyDecBtn),
|
||||
widget.NewLabel("Payload"),
|
||||
parts.payload,
|
||||
widget.NewLabel("Výsledek"),
|
||||
parts.plainOut,
|
||||
)
|
||||
}
|
||||
@ -458,11 +455,13 @@ func openEncryptPopup(parts *uiParts, svc ServiceFacade, ct Contact) {
|
||||
}
|
||||
}
|
||||
win := fyne.CurrentApp().Driver().AllWindows()[0]
|
||||
lastCipher := ""
|
||||
doEncrypt := func() {
|
||||
m := strings.TrimSpace(msgEntry.Text)
|
||||
fyne.Do(func() {
|
||||
if m == "" {
|
||||
status.SetText("Zpráva je prázdná")
|
||||
lastCipher = ""
|
||||
updateQR("")
|
||||
return
|
||||
}
|
||||
@ -477,7 +476,7 @@ func openEncryptPopup(parts *uiParts, svc ServiceFacade, ct Contact) {
|
||||
fyne.Do(func() { status.SetText("Chyba: " + err.Error()) })
|
||||
return
|
||||
}
|
||||
fyne.Do(func() { updateQR(res); status.SetText("Hotovo") })
|
||||
fyne.Do(func() { lastCipher = res; updateQR(res); status.SetText("Hotovo") })
|
||||
}(m)
|
||||
}
|
||||
var tmr *time.Timer
|
||||
@ -487,6 +486,12 @@ func openEncryptPopup(parts *uiParts, svc ServiceFacade, ct Contact) {
|
||||
}
|
||||
tmr = time.AfterFunc(300*time.Millisecond, doEncrypt)
|
||||
}
|
||||
copyPayloadBtn := widget.NewButton("Kopírovat payload", func() {
|
||||
if lastCipher != "" {
|
||||
copyClip(lastCipher, parts)
|
||||
status.SetText("Zkopírováno")
|
||||
}
|
||||
})
|
||||
copyQRBtn := widget.NewButton("Kopírovat QR", func() { copyImageToClipboard(qrImg.Image, parts) })
|
||||
saveQRBtn := widget.NewButton("Uložit QR", func() {
|
||||
if qrImg.Image == nil {
|
||||
@ -504,7 +509,7 @@ func openEncryptPopup(parts *uiParts, svc ServiceFacade, ct Contact) {
|
||||
fd.SetFileName("message_qr.png")
|
||||
fd.Show()
|
||||
})
|
||||
content := container.NewVBox(widget.NewLabel("Zpráva"), msgEntry, widget.NewSeparator(), container.NewHBox(widget.NewLabel("QR kód"), layout.NewSpacer(), copyQRBtn, saveQRBtn), qrImg, status)
|
||||
content := container.NewVBox(widget.NewLabel("Zpráva"), msgEntry, widget.NewSeparator(), container.NewHBox(widget.NewLabel("QR kód"), layout.NewSpacer(), copyPayloadBtn, copyQRBtn, saveQRBtn), qrImg, status)
|
||||
title := ct.Name
|
||||
if title == "" {
|
||||
title = "(bez názvu)"
|
||||
@ -568,6 +573,13 @@ func buildContactsTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
if existing != nil {
|
||||
certValue = existing.Cert
|
||||
}
|
||||
manualEntry := widget.NewMultiLineEntry()
|
||||
manualEntry.SetMinRowsVisible(4)
|
||||
manualEntry.Wrapping = fyne.TextWrapWord
|
||||
manualEntry.SetPlaceHolder("Sem lze vložit text PEM pokud QR selže…")
|
||||
if certValue != "" {
|
||||
manualEntry.SetText(certValue)
|
||||
}
|
||||
qrImg := canvas.NewImageFromImage(nil)
|
||||
qrImg.FillMode = canvas.ImageFillContain
|
||||
qrImg.SetMinSize(fyne.NewSize(300, 300))
|
||||
@ -585,7 +597,23 @@ func buildContactsTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
}
|
||||
}
|
||||
updateQR()
|
||||
pasteQR := widget.NewToolbarAction(theme.ContentPasteIcon(), func() {
|
||||
// now that updateQR exists, bind manualEntry changes
|
||||
manualEntry.OnChanged = func(s string) {
|
||||
certValue = s
|
||||
updateQR()
|
||||
}
|
||||
pasteText := widget.NewToolbarAction(theme.ContentPasteIcon(), func() {
|
||||
clip := fyne.CurrentApp().Clipboard().Content()
|
||||
if strings.TrimSpace(clip) == "" {
|
||||
parts.showToast("Schránka prázdná")
|
||||
return
|
||||
}
|
||||
certValue = clip
|
||||
manualEntry.SetText(certValue)
|
||||
updateQR()
|
||||
parts.showToast("Vloženo")
|
||||
})
|
||||
pasteQR := widget.NewToolbarAction(theme.ComputerIcon(), func() {
|
||||
img, err := readImageClipboard()
|
||||
if err != nil {
|
||||
parts.showToast("Chyba čtení schránky: " + err.Error())
|
||||
@ -608,6 +636,7 @@ func buildContactsTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
}
|
||||
if txt2, err2 := DecodeQR(inv); err2 == nil {
|
||||
certValue = txt2
|
||||
manualEntry.SetText(certValue)
|
||||
updateQR()
|
||||
parts.showToast("Načteno z invert QR")
|
||||
return
|
||||
@ -625,6 +654,7 @@ func buildContactsTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
return
|
||||
}
|
||||
certValue = txt
|
||||
manualEntry.SetText(certValue)
|
||||
updateQR()
|
||||
parts.showToast("Načteno z QR")
|
||||
})
|
||||
@ -648,14 +678,37 @@ func buildContactsTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
return
|
||||
}
|
||||
certValue = txt
|
||||
manualEntry.SetText(certValue)
|
||||
updateQR()
|
||||
parts.showToast("Načteno z QR")
|
||||
}, win)
|
||||
fd.SetFilter(storage.NewExtensionFileFilter([]string{".png", ".jpg", ".jpeg"}))
|
||||
fd.Show()
|
||||
})
|
||||
clearAct := widget.NewToolbarAction(theme.ContentClearIcon(), func() { certValue = ""; updateQR() })
|
||||
toolbar := widget.NewToolbar(pasteQR, openImg, clearAct)
|
||||
clearAct := widget.NewToolbarAction(theme.ContentClearIcon(), func() { certValue = ""; manualEntry.SetText(""); updateQR() })
|
||||
copyAct := widget.NewToolbarAction(theme.ContentCopyIcon(), func() {
|
||||
if certValue != "" {
|
||||
copyClip(certValue, parts)
|
||||
}
|
||||
})
|
||||
saveAct := widget.NewToolbarAction(theme.DocumentSaveIcon(), func() {
|
||||
if qrImg.Image == nil {
|
||||
return
|
||||
}
|
||||
win := fyne.CurrentApp().Driver().AllWindows()[0]
|
||||
img := qrImg.Image
|
||||
fd := dialog.NewFileSave(func(wc fyne.URIWriteCloser, err error) {
|
||||
if err != nil || wc == nil {
|
||||
return
|
||||
}
|
||||
defer wc.Close()
|
||||
_ = png.Encode(wc, img)
|
||||
parts.showToast("QR uložen")
|
||||
}, win)
|
||||
fd.SetFileName("contact_qr.png")
|
||||
fd.Show()
|
||||
})
|
||||
toolbar := widget.NewToolbar(pasteText, pasteQR, openImg, widget.NewToolbarSeparator(), copyAct, saveAct, clearAct)
|
||||
win := fyne.CurrentApp().Driver().AllWindows()[0]
|
||||
var popup dialog.Dialog
|
||||
save := func(useEncrypt bool) {
|
||||
@ -691,19 +744,12 @@ func buildContactsTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
}
|
||||
}
|
||||
if ask {
|
||||
entry := widget.NewEntry()
|
||||
entry.SetText(name)
|
||||
content := container.NewVBox(
|
||||
widget.NewLabel(fmt.Sprintf("Common Name nalezen v certifikátu: %s", cn)),
|
||||
widget.NewLabel("Chcete použít CN jako název, nebo jej upravit?"),
|
||||
entry,
|
||||
)
|
||||
dialog.NewCustomConfirm("Název kontaktu", "Použít CN", "Uložit", content, func(ok bool) {
|
||||
dialog.NewCustomConfirm("Common Name", "Použít", "Ponechat", widget.NewLabel(fmt.Sprintf("Common Name: %s\nPoužít jako název?", cn)), func(ok bool) {
|
||||
if ok {
|
||||
proceed(cn)
|
||||
return
|
||||
} else {
|
||||
proceed(name)
|
||||
}
|
||||
proceed(strings.TrimSpace(entry.Text))
|
||||
}, win).Show()
|
||||
return
|
||||
}
|
||||
@ -726,8 +772,9 @@ func buildContactsTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
parts.showToast("Smazáno")
|
||||
}, win).Show()
|
||||
})
|
||||
useBtn := widget.NewButton("Použít ve Šifrování", func() { save(true) })
|
||||
saveBtn := widget.NewButton("Uložit", func() { save(false) })
|
||||
row := container.NewHBox(layout.NewSpacer(), saveBtn, delBtn, layout.NewSpacer())
|
||||
row := container.NewHBox(saveBtn, useBtn, layout.NewSpacer(), delBtn)
|
||||
title := "Nový kontakt"
|
||||
if existing != nil {
|
||||
title = "Upravit kontakt"
|
||||
@ -736,6 +783,7 @@ func buildContactsTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
popup = dialog.NewCustom(title, "Zavřít", container.NewVBox(
|
||||
widget.NewLabel("Název"), nameEntry,
|
||||
widget.NewLabel("Certifikát / Public key (QR)"), toolbar, qrImg,
|
||||
widget.NewLabel("Text PEM"), manualEntry,
|
||||
widget.NewSeparator(), row), win)
|
||||
popup.Resize(fyne.NewSize(640, 520))
|
||||
popup.Show()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user