diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-17 10:15:45 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-17 10:15:45 +0200 |
| commit | a2cb20851499e9c00bd3bf642680a9ce3148cae8 (patch) | |
| tree | 09ef80b9662308b758a88ed2f43078c92e525f29 /gui/internal/ui/plan.go | |
| parent | 47b6776c2cb9b017ad95acb5aae8e34b8776fc7c (diff) | |
| download | krino-a2cb20851499e9c00bd3bf642680a9ce3148cae8.tar.gz krino-a2cb20851499e9c00bd3bf642680a9ce3148cae8.zip | |
gui: name the other copy of a duplicate, and offer to keep this one instead
Diffstat (limited to 'gui/internal/ui/plan.go')
| -rw-r--r-- | gui/internal/ui/plan.go | 58 |
1 files changed, 53 insertions, 5 deletions
diff --git a/gui/internal/ui/plan.go b/gui/internal/ui/plan.go index 8b6a394..0a9b718 100644 --- a/gui/internal/ui/plan.go +++ b/gui/internal/ui/plan.go @@ -64,6 +64,7 @@ type planView struct { sortFollowsPrefs bool startSelected bool menu *gtk.Popover + keep *gtk.Button menuRow int tab *model.PlanTab @@ -317,21 +318,61 @@ func (p *planView) confirmDeleteChecked() { d.Show() } +// confirmKeepThisCopy asks before one copy replaces another, naming both. +func (p *planView) confirmKeepThisCopy() { + if p.tab == nil || p.menuRow < 0 || p.menuRow >= len(p.tab.Rows) { + return + } + r := p.tab.Rows[p.menuRow] + if r.DuplicateOf == "" { + return + } + d := gtk.NewMessageDialog(&p.w.win.Window, gtk.DialogModal|gtk.DialogDestroyWithParent, + gtk.MessageQuestion, gtk.ButtonsNone) + d.SetObjectProperty("text", "Keep "+escape(r.Rel)+" and replace the other copy?") + d.SetObjectProperty("secondary-text", "This file takes the place of\n"+ + escape(xdg.Abbrev(r.DuplicateOf))+ + "\n\nThat copy goes to the Trash, and krino undo can bring it back. Nothing happens until you press Apply.") + d.AddButton("Cancel", int(gtk.ResponseCancel)) + d.AddButton("Keep this copy", int(gtk.ResponseAccept)) + d.ConnectResponse(func(response int) { + d.Destroy() + if response != int(gtk.ResponseAccept) { + return + } + if err := p.tab.KeepThisCopy(p.menuRow); err != nil { + p.w.setStatus("%v", err) + return + } + p.fillList() + p.showDetails(p.menuRow) + p.w.setStatus("%s will replace %s when you press Apply; nothing has moved yet", + escape(r.Rel), escape(xdg.Abbrev(r.DuplicateOf))) + }) + d.Show() +} + // newMenu builds the row menu: what to do with a file instead of what the // rules decided. func (p *planView) newMenu() *gtk.Popover { box := gtk.NewBox(gtk.OrientationVertical, 0) + pop := gtk.NewPopover() + pop.SetChild(box) + pop.SetParent(p.list) + pop.SetHasArrow(false) trash := gtk.NewButtonWithLabel("Trash instead") perm := gtk.NewButtonWithLabel("Delete permanently instead...") - for _, b := range []*gtk.Button{trash, perm} { + p.keep = gtk.NewButtonWithLabel("Keep this copy, replace the other...") + p.keep.SetTooltipText("put this file where the copy it duplicates is, and send that one to the Trash") + for _, b := range []*gtk.Button{trash, perm, p.keep} { b.SetHasFrame(false) b.SetHAlign(gtk.AlignFill) box.Append(b) } - pop := gtk.NewPopover() - pop.SetChild(box) - pop.SetParent(p.list) - pop.SetHasArrow(false) + p.keep.ConnectClicked(func() { + pop.Popdown() + p.confirmKeepThisCopy() + }) trash.ConnectClicked(func() { pop.Popdown() p.replace(plan.Trash) @@ -367,6 +408,7 @@ func (p *planView) onRightClick(x, y float64) { if p.menuRow < 0 { return } + p.keep.SetVisible(p.tab.Rows[p.menuRow].DuplicateOf != "") at := gdk.NewRectangle(int(x), int(y), 1, 1) p.menu.SetPointingTo(&at) p.menu.Popup() @@ -869,6 +911,12 @@ func (p *planView) showDetails(i int) { if what := describeFile(r); what != "" { p.details.Append(detailLine(what, "dim-label")) } + // Where the other copy is, in full: the reason names it relative to the + // directory when it is inside it, which reads as no place at all (his + // report, 2026-09-17). + if r.DuplicateOf != "" { + p.details.Append(detailLine("the same bytes as "+escape(xdg.Abbrev(r.DuplicateOf)), "dim-label")) + } for _, s := range r.Steps { row := gtk.NewBox(gtk.OrientationHorizontal, 8) |
