github.com/jmigpin/editor@v1.6.0/core/dragndrop.go (about) 1 package core 2 3 import ( 4 "fmt" 5 "image" 6 "net/url" 7 "strings" 8 9 "github.com/jmigpin/editor/ui" 10 "github.com/jmigpin/editor/util/uiutil/event" 11 ) 12 13 type DndHandler struct { 14 ed *Editor 15 } 16 17 func NewDndHandler(ed *Editor) *DndHandler { 18 return &DndHandler{ed} 19 } 20 21 //---------- 22 23 func (h *DndHandler) OnPosition(ev *event.DndPosition) { 24 // dnd position must receive a reply 25 ev.Reply(h.onPosition2(ev)) 26 } 27 func (h *DndHandler) onPosition2(ev *event.DndPosition) event.DndAction { 28 // must drop on a column 29 _, ok := h.columnAtPoint(&ev.Point) 30 if !ok { 31 return event.DndADeny 32 } 33 // supported types 34 for _, t := range ev.Types { 35 if t == event.TextURLListDndT { 36 return event.DndAPrivate 37 } 38 } 39 return event.DndADeny 40 } 41 42 //---------- 43 44 func (h *DndHandler) OnDrop(ev *event.DndDrop) { 45 // The drop event might need to request data (send and then receive an event). To receive that event, the main eventloop can't be blocking with this procedure 46 go func() { 47 v := h.onDrop2(ev) 48 ev.ReplyAccept(v) 49 if v { 50 // ensure paint if needed 51 h.ed.UI.EnqueueNoOpEvent() 52 } 53 }() 54 } 55 func (h *DndHandler) onDrop2(ev *event.DndDrop) bool { 56 // find column that matches 57 col, ok := h.columnAtPoint(&ev.Point) 58 if !ok { 59 return false 60 } 61 // get data in required format 62 data, err := ev.RequestData(event.TextURLListDndT) 63 if err != nil { 64 h.ed.Error(err) 65 return false 66 } 67 // parse data 68 urls, err := parseAsTextURLList(data) 69 if err != nil { 70 h.ed.Error(err) 71 return false 72 } 73 74 h.handleDroppedURLs(col, &ev.Point, urls) 75 return true 76 } 77 78 //---------- 79 80 func (h *DndHandler) columnAtPoint(p *image.Point) (*ui.Column, bool) { 81 for _, col := range h.ed.UI.Root.Cols.Columns() { 82 if p.In(col.Bounds) { 83 return col, true 84 } 85 } 86 return nil, false 87 } 88 89 func (h *DndHandler) handleDroppedURLs(col *ui.Column, p *image.Point, urls []*url.URL) { 90 // ensure running on a goroutine since the drop2 was running on a goroutine to unblock the mainloop 91 h.ed.UI.RunOnUIGoRoutine(func() { 92 for _, u := range urls { 93 h.handleDroppedURL(col, p, u) 94 } 95 }) 96 } 97 func (h *DndHandler) handleDroppedURL(col *ui.Column, p *image.Point, u *url.URL) { 98 next, ok := col.PointNextRow(p) 99 if !ok { 100 next = nil 101 } 102 rowPos := ui.NewRowPos(col, next) 103 104 name := fmt.Sprintf("%s", u) 105 if u.Scheme == "file" { 106 name = u.Path 107 } 108 109 info := h.ed.ReadERowInfo(name) 110 _, err := NewLoadedERow(info, rowPos) 111 if err != nil { 112 h.ed.Error(err) 113 } 114 } 115 116 //---------- 117 118 func parseAsTextURLList(data []byte) ([]*url.URL, error) { 119 s := string(data) 120 entries := strings.Split(s, "\n") 121 var urls []*url.URL 122 for _, e := range entries { 123 e = strings.TrimSpace(e) 124 if e == "" { 125 continue 126 } 127 u, err := url.Parse(e) 128 if err != nil { 129 return nil, err 130 } 131 urls = append(urls, u) 132 } 133 return urls, nil 134 }