aboutsummaryrefslogtreecommitdiff
path: root/gui/internal/ui
diff options
context:
space:
mode:
Diffstat (limited to 'gui/internal/ui')
-rw-r--r--gui/internal/ui/plan.go58
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)