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  }