9fans.net/go@v0.0.7/cmd/acme/internal/wind/cols.go (about)

     1  package wind
     2  
     3  import (
     4  	"sort"
     5  
     6  	"9fans.net/go/cmd/acme/internal/adraw"
     7  	"9fans.net/go/cmd/acme/internal/runes"
     8  	"9fans.net/go/cmd/acme/internal/util"
     9  	"9fans.net/go/draw"
    10  	"9fans.net/go/draw/frame"
    11  )
    12  
    13  type Column struct {
    14  	R    draw.Rectangle
    15  	Tag  Text
    16  	Row  *Row
    17  	W    []*Window
    18  	Safe bool
    19  }
    20  
    21  var Activecol *Column
    22  
    23  func colinit(c *Column, r draw.Rectangle) {
    24  	adraw.Display.ScreenImage.Draw(r, adraw.Display.White, nil, draw.ZP)
    25  	c.R = r
    26  	c.W = nil
    27  	t := &c.Tag
    28  	t.W = nil
    29  	t.Col = c
    30  	r1 := r
    31  	r1.Max.Y = r1.Min.Y + adraw.Font.Height
    32  	textinit(t, fileaddtext(nil, t), r1, &adraw.RefFont1, adraw.TagCols[:])
    33  	t.What = Columntag
    34  	r1.Min.Y = r1.Max.Y
    35  	r1.Max.Y += adraw.Border()
    36  	adraw.Display.ScreenImage.Draw(r1, adraw.Display.Black, nil, draw.ZP)
    37  	Textinsert(t, 0, []rune("New Cut Paste Snarf Sort Zerox Delcol "), true)
    38  	Textsetselect(t, t.Len(), t.Len())
    39  	adraw.Display.ScreenImage.Draw(t.ScrollR, adraw.ColButton, nil, adraw.ColButton.R.Min)
    40  	c.Safe = true
    41  }
    42  
    43  func Coladd(c *Column, w *Window, clone *Window, y int) *Window {
    44  	var v *Window
    45  	r := c.R
    46  	r.Min.Y = c.Tag.Fr.R.Max.Y + adraw.Border()
    47  	if y < r.Min.Y && len(c.W) > 0 { // steal half of last window by default
    48  		v = c.W[len(c.W)-1]
    49  		y = v.Body.Fr.R.Min.Y + v.Body.Fr.R.Dy()/2
    50  	}
    51  	var i int
    52  	// look for window we'll land on
    53  	for i = 0; i < len(c.W); i++ {
    54  		v = c.W[i]
    55  		if y < v.R.Max.Y {
    56  			break
    57  		}
    58  	}
    59  	buggered := 0
    60  	if len(c.W) > 0 {
    61  		if i < len(c.W) {
    62  			i++ // new window will go after v
    63  		}
    64  		/*
    65  		 * if landing window (v) is too small, grow it first.
    66  		 */
    67  		minht := v.Tag.Fr.Font.Height + adraw.Border() + 1
    68  		j := 0
    69  		for !c.Safe || v.Body.Fr.MaxLines <= 3 || v.Body.All.Dy() <= minht {
    70  			j++
    71  			if j > 10 {
    72  				buggered = 1 // too many windows in column
    73  				break
    74  			}
    75  			Colgrow(c, v, 1)
    76  		}
    77  		var ymax int
    78  
    79  		/*
    80  		 * figure out where to split v to make room for w
    81  		 */
    82  
    83  		// new window stops where next window begins
    84  		if i < len(c.W) {
    85  			ymax = c.W[i].R.Min.Y - adraw.Border()
    86  		} else {
    87  			ymax = c.R.Max.Y
    88  		}
    89  
    90  		// new window must start after v's tag ends
    91  		y = util.Max(y, v.tagtop.Max.Y+adraw.Border())
    92  
    93  		// new window must start early enough to end before ymax
    94  		y = util.Min(y, ymax-minht)
    95  
    96  		// if y is too small, too many windows in column
    97  		if y < v.tagtop.Max.Y+adraw.Border() {
    98  			buggered = 1
    99  		}
   100  
   101  		/*
   102  		 * resize & redraw v
   103  		 */
   104  		r = v.R
   105  		r.Max.Y = ymax
   106  		adraw.Display.ScreenImage.Draw(r, adraw.TextCols[frame.BACK], nil, draw.ZP)
   107  		r1 := r
   108  		y = util.Min(y, ymax-(v.Tag.Fr.Font.Height*v.Taglines+v.Body.Fr.Font.Height+adraw.Border()+1))
   109  		r1.Max.Y = util.Min(y, v.Body.Fr.R.Min.Y+v.Body.Fr.NumLines*v.Body.Fr.Font.Height)
   110  		r1.Min.Y = Winresize(v, r1, false, false)
   111  		r1.Max.Y = r1.Min.Y + adraw.Border()
   112  		adraw.Display.ScreenImage.Draw(r1, adraw.Display.Black, nil, draw.ZP)
   113  
   114  		/*
   115  		 * leave r with w's coordinates
   116  		 */
   117  		r.Min.Y = r1.Max.Y
   118  	}
   119  	if w == nil {
   120  		w = new(Window)
   121  		w.Col = c
   122  		adraw.Display.ScreenImage.Draw(r, adraw.TextCols[frame.BACK], nil, draw.ZP)
   123  		Init(w, clone, r)
   124  	} else {
   125  		w.Col = c
   126  		Winresize(w, r, false, true)
   127  	}
   128  	w.Tag.Col = c
   129  	w.Tag.Row = c.Row
   130  	w.Body.Col = c
   131  	w.Body.Row = c.Row
   132  	c.W = append(c.W, nil)
   133  	copy(c.W[i+1:], c.W[i:])
   134  	c.W[i] = w
   135  	c.Safe = true
   136  
   137  	// if there were too many windows, redraw the whole column
   138  	if buggered != 0 {
   139  		Colresize(c, c.R)
   140  	}
   141  
   142  	return w
   143  }
   144  
   145  func Colclose(c *Column, w *Window, dofree bool) *Window {
   146  	// w is locked
   147  	if !c.Safe {
   148  		Colgrow(c, w, 1)
   149  	}
   150  	var i int
   151  	for i = 0; i < len(c.W); i++ {
   152  		if c.W[i] == w {
   153  			goto Found
   154  		}
   155  	}
   156  	util.Fatal("can't find window")
   157  Found:
   158  	r := w.R
   159  	w.Tag.Col = nil
   160  	w.Body.Col = nil
   161  	w.Col = nil
   162  	if dofree {
   163  		windelete(w)
   164  		Winclose(w)
   165  	}
   166  	copy(c.W[i:], c.W[i+1:])
   167  	c.W = c.W[:len(c.W)-1]
   168  	if len(c.W) == 0 {
   169  		adraw.Display.ScreenImage.Draw(r, adraw.Display.White, nil, draw.ZP)
   170  		return nil
   171  	}
   172  	if i == len(c.W) { // extend last window down
   173  		w = c.W[i-1]
   174  		r.Min.Y = w.R.Min.Y
   175  		r.Max.Y = c.R.Max.Y
   176  	} else { // extend next window up
   177  		w = c.W[i]
   178  		r.Max.Y = w.R.Max.Y
   179  	}
   180  	adraw.Display.ScreenImage.Draw(r, adraw.TextCols[frame.BACK], nil, draw.ZP)
   181  	if c.Safe {
   182  		Winresize(w, r, false, true)
   183  	}
   184  	return w
   185  }
   186  
   187  func colcloseall(c *Column) {
   188  	if c == Activecol {
   189  		Activecol = nil
   190  	}
   191  	textclose(&c.Tag)
   192  	for i := 0; i < len(c.W); i++ {
   193  		w := c.W[i]
   194  		Winclose(w)
   195  	}
   196  }
   197  
   198  func Colresize(c *Column, r draw.Rectangle) {
   199  	r1 := r
   200  	r1.Max.Y = r1.Min.Y + c.Tag.Fr.Font.Height
   201  	Textresize(&c.Tag, r1, true)
   202  	adraw.Display.ScreenImage.Draw(c.Tag.ScrollR, adraw.ColButton, nil, adraw.ColButton.R.Min)
   203  	r1.Min.Y = r1.Max.Y
   204  	r1.Max.Y += adraw.Border()
   205  	adraw.Display.ScreenImage.Draw(r1, adraw.Display.Black, nil, draw.ZP)
   206  	r1.Max.Y = r.Max.Y
   207  	new_ := r.Dy() - len(c.W)*(adraw.Border()+adraw.Font.Height)
   208  	old := c.R.Dy() - len(c.W)*(adraw.Border()+adraw.Font.Height)
   209  	for i := 0; i < len(c.W); i++ {
   210  		w := c.W[i]
   211  		w.Maxlines = 0
   212  		if i == len(c.W)-1 {
   213  			r1.Max.Y = r.Max.Y
   214  		} else {
   215  			r1.Max.Y = r1.Min.Y
   216  			if new_ > 0 && old > 0 && w.R.Dy() > adraw.Border()+adraw.Font.Height {
   217  				r1.Max.Y += (w.R.Dy()-adraw.Border()-adraw.Font.Height)*new_/old + adraw.Border() + adraw.Font.Height
   218  			}
   219  		}
   220  		r1.Max.Y = util.Max(r1.Max.Y, r1.Min.Y+adraw.Border()+adraw.Font.Height)
   221  		r2 := r1
   222  		r2.Max.Y = r2.Min.Y + adraw.Border()
   223  		adraw.Display.ScreenImage.Draw(r2, adraw.Display.Black, nil, draw.ZP)
   224  		r1.Min.Y = r2.Max.Y
   225  		r1.Min.Y = Winresize(w, r1, false, i == len(c.W)-1)
   226  	}
   227  	c.R = r
   228  }
   229  
   230  func Colclean(c *Column) bool {
   231  	clean := true
   232  	for i := 0; i < len(c.W); i++ {
   233  		clean = Winclean(c.W[i], true) && clean
   234  	}
   235  	return clean
   236  }
   237  
   238  func Colsort(c *Column) {
   239  	if len(c.W) == 0 {
   240  		return
   241  	}
   242  	rp := make([]draw.Rectangle, len(c.W))
   243  	wp := make([]*Window, len(c.W))
   244  	copy(wp, c.W)
   245  	sort.Slice(wp, func(i, j int) bool {
   246  		return runes.Compare(wp[i].Body.File.Name(), wp[j].Body.File.Name()) < 0
   247  	})
   248  
   249  	for i := 0; i < len(c.W); i++ {
   250  		rp[i] = wp[i].R
   251  	}
   252  	r := c.R
   253  	r.Min.Y = c.Tag.Fr.R.Max.Y
   254  	adraw.Display.ScreenImage.Draw(r, adraw.TextCols[frame.BACK], nil, draw.ZP)
   255  	y := r.Min.Y
   256  	for i := 0; i < len(c.W); i++ {
   257  		w := wp[i]
   258  		r.Min.Y = y
   259  		if i == len(c.W)-1 {
   260  			r.Max.Y = c.R.Max.Y
   261  		} else {
   262  			r.Max.Y = r.Min.Y + w.R.Dy() + adraw.Border()
   263  		}
   264  		r1 := r
   265  		r1.Max.Y = r1.Min.Y + adraw.Border()
   266  		adraw.Display.ScreenImage.Draw(r1, adraw.Display.Black, nil, draw.ZP)
   267  		r.Min.Y = r1.Max.Y
   268  		y = Winresize(w, r, false, i == len(c.W)-1)
   269  	}
   270  	c.W = wp
   271  }
   272  
   273  func Colgrow(c *Column, w *Window, but int) {
   274  	var i int
   275  	for i = 0; i < len(c.W); i++ {
   276  		if c.W[i] == w {
   277  			goto Found
   278  		}
   279  	}
   280  	util.Fatal("can't find window")
   281  
   282  Found:
   283  	cr := c.R
   284  	var r draw.Rectangle
   285  	if but < 0 { // make sure window fills its own space properly
   286  		r = w.R
   287  		if i == len(c.W)-1 || !c.Safe {
   288  			r.Max.Y = cr.Max.Y
   289  		} else {
   290  			r.Max.Y = c.W[i+1].R.Min.Y - adraw.Border()
   291  		}
   292  		Winresize(w, r, false, true)
   293  		return
   294  	}
   295  	cr.Min.Y = c.W[0].R.Min.Y
   296  	var v *Window
   297  	if but == 3 { // full size
   298  		if i != 0 {
   299  			v = c.W[0]
   300  			c.W[0] = w
   301  			c.W[i] = v
   302  		}
   303  		adraw.Display.ScreenImage.Draw(cr, adraw.TextCols[frame.BACK], nil, draw.ZP)
   304  		Winresize(w, cr, false, true)
   305  		for i = 1; i < len(c.W); i++ {
   306  			c.W[i].Body.Fr.MaxLines = 0
   307  		}
   308  		c.Safe = false
   309  		return
   310  	}
   311  	// store old #lines for each window
   312  	onl := w.Body.Fr.MaxLines
   313  	nl := make([]int, len(c.W))
   314  	ny := make([]int, len(c.W))
   315  	tot := 0
   316  	var j int
   317  	var l int
   318  	for j = 0; j < len(c.W); j++ {
   319  		l = c.W[j].Taglines - 1 + c.W[j].Body.Fr.MaxLines
   320  		nl[j] = l
   321  		tot += l
   322  	}
   323  	// approximate new #lines for this window
   324  	if but == 2 { // as big as can be
   325  		for i := range nl {
   326  			nl[i] = 0
   327  		}
   328  	} else {
   329  		nnl := util.Min(onl+util.Max(util.Min(5, w.Taglines-1+w.Maxlines), onl/2), tot)
   330  		if nnl < w.Taglines-1+w.Maxlines {
   331  			nnl = (w.Taglines - 1 + w.Maxlines + nnl) / 2
   332  		}
   333  		if nnl == 0 {
   334  			nnl = 2
   335  		}
   336  		dnl := nnl - onl
   337  		// compute new #lines for each window
   338  		for k := 1; k < len(c.W); k++ {
   339  			// prune from later window
   340  			j = i + k
   341  			if j < len(c.W) && nl[j] != 0 {
   342  				l = util.Min(dnl, util.Max(1, nl[j]/2))
   343  				nl[j] -= l
   344  				nl[i] += l
   345  				dnl -= l
   346  			}
   347  			// prune from earlier window
   348  			j = i - k
   349  			if j >= 0 && nl[j] != 0 {
   350  				l = util.Min(dnl, util.Max(1, nl[j]/2))
   351  				nl[j] -= l
   352  				nl[i] += l
   353  				dnl -= l
   354  			}
   355  		}
   356  	}
   357  	// pack everyone above
   358  	y1 := cr.Min.Y
   359  	for j = 0; j < i; j++ {
   360  		v = c.W[j]
   361  		r = v.R
   362  		r.Min.Y = y1
   363  		r.Max.Y = y1 + v.tagtop.Dy()
   364  		if nl[j] != 0 {
   365  			r.Max.Y += 1 + nl[j]*v.Body.Fr.Font.Height
   366  		}
   367  		r.Min.Y = Winresize(v, r, c.Safe, false)
   368  		r.Max.Y += adraw.Border()
   369  		adraw.Display.ScreenImage.Draw(r, adraw.Display.Black, nil, draw.ZP)
   370  		y1 = r.Max.Y
   371  	}
   372  	// scan to see new size of everyone below
   373  	y2 := c.R.Max.Y
   374  	for j = len(c.W) - 1; j > i; j-- {
   375  		v = c.W[j]
   376  		r = v.R
   377  		r.Min.Y = y2 - v.tagtop.Dy()
   378  		if nl[j] != 0 {
   379  			r.Min.Y -= 1 + nl[j]*v.Body.Fr.Font.Height
   380  		}
   381  		r.Min.Y -= adraw.Border()
   382  		ny[j] = r.Min.Y
   383  		y2 = r.Min.Y
   384  	}
   385  	// compute new size of window
   386  	r = w.R
   387  	r.Min.Y = y1
   388  	r.Max.Y = y2
   389  	h := w.Body.Fr.Font.Height
   390  	if r.Dy() < w.tagtop.Dy()+1+h+adraw.Border() {
   391  		r.Max.Y = r.Min.Y + w.tagtop.Dy() + 1 + h + adraw.Border()
   392  	}
   393  	// draw window
   394  	r.Max.Y = Winresize(w, r, c.Safe, true)
   395  	if i < len(c.W)-1 {
   396  		r.Min.Y = r.Max.Y
   397  		r.Max.Y += adraw.Border()
   398  		adraw.Display.ScreenImage.Draw(r, adraw.Display.Black, nil, draw.ZP)
   399  		for j = i + 1; j < len(c.W); j++ {
   400  			ny[j] -= (y2 - r.Max.Y)
   401  		}
   402  	}
   403  	// pack everyone below
   404  	y1 = r.Max.Y
   405  	for j = i + 1; j < len(c.W); j++ {
   406  		v = c.W[j]
   407  		r = v.R
   408  		r.Min.Y = y1
   409  		r.Max.Y = y1 + v.tagtop.Dy()
   410  		if nl[j] != 0 {
   411  			r.Max.Y += 1 + nl[j]*v.Body.Fr.Font.Height
   412  		}
   413  		y1 = Winresize(v, r, c.Safe, j == len(c.W)-1)
   414  		if j < len(c.W)-1 { // no border on last window
   415  			r.Min.Y = y1
   416  			r.Max.Y += adraw.Border()
   417  			adraw.Display.ScreenImage.Draw(r, adraw.Display.Black, nil, draw.ZP)
   418  			y1 = r.Max.Y
   419  		}
   420  	}
   421  	c.Safe = true
   422  }
   423  
   424  func Coldragwin1(c *Column, w *Window, but int, op, p draw.Point) {
   425  	var i int
   426  
   427  	for i = 0; i < len(c.W); i++ {
   428  		if c.W[i] == w {
   429  			goto Found
   430  		}
   431  	}
   432  	util.Fatal("can't find window")
   433  
   434  Found:
   435  	if w.Tagexpand { // force recomputation of window tag size
   436  		w.Taglines = 1
   437  	}
   438  	if util.Abs(p.X-op.X) < 5 && util.Abs(p.Y-op.Y) < 5 {
   439  		Colgrow(c, w, but)
   440  		return
   441  	}
   442  	// is it a flick to the right?
   443  	if util.Abs(p.Y-op.Y) < 10 && p.X > op.X+30 && rowwhichcol(c.Row, p) == c {
   444  		p.X = op.X + w.R.Dx() // yes: toss to next column
   445  	}
   446  	nc := rowwhichcol(c.Row, p)
   447  	if nc != nil && nc != c {
   448  		Colclose(c, w, false)
   449  		Coladd(nc, w, nil, p.Y)
   450  		return
   451  	}
   452  	if i == 0 && len(c.W) == 1 {
   453  		return // can't do it
   454  	}
   455  	if (i > 0 && p.Y < c.W[i-1].R.Min.Y) || (i < len(c.W)-1 && p.Y > w.R.Max.Y) || (i == 0 && p.Y > w.R.Max.Y) {
   456  		// shuffle
   457  		Colclose(c, w, false)
   458  		Coladd(c, w, nil, p.Y)
   459  		return
   460  	}
   461  	if i == 0 {
   462  		return
   463  	}
   464  	v := c.W[i-1]
   465  	if p.Y < v.tagtop.Max.Y {
   466  		p.Y = v.tagtop.Max.Y
   467  	}
   468  	if p.Y > w.R.Max.Y-w.tagtop.Dy()-adraw.Border() {
   469  		p.Y = w.R.Max.Y - w.tagtop.Dy() - adraw.Border()
   470  	}
   471  	r := v.R
   472  	r.Max.Y = p.Y
   473  	if r.Max.Y > v.Body.Fr.R.Min.Y {
   474  		r.Max.Y -= (r.Max.Y - v.Body.Fr.R.Min.Y) % v.Body.Fr.Font.Height
   475  		if v.Body.Fr.R.Min.Y == v.Body.Fr.R.Max.Y {
   476  			r.Max.Y++
   477  		}
   478  	}
   479  	r.Min.Y = Winresize(v, r, c.Safe, false)
   480  	r.Max.Y = r.Min.Y + adraw.Border()
   481  	adraw.Display.ScreenImage.Draw(r, adraw.Display.Black, nil, draw.ZP)
   482  	r.Min.Y = r.Max.Y
   483  	if i == len(c.W)-1 {
   484  		r.Max.Y = c.R.Max.Y
   485  	} else {
   486  		r.Max.Y = c.W[i+1].R.Min.Y - adraw.Border()
   487  	}
   488  	Winresize(w, r, c.Safe, true)
   489  	c.Safe = true
   490  }
   491  
   492  func colwhich(c *Column, p draw.Point) *Text {
   493  	if !p.In(c.R) {
   494  		return nil
   495  	}
   496  	if p.In(c.Tag.All) {
   497  		return &c.Tag
   498  	}
   499  	for i := 0; i < len(c.W); i++ {
   500  		w := c.W[i]
   501  		if p.In(w.R) {
   502  			if p.In(w.tagtop) || p.In(w.Tag.All) {
   503  				return &w.Tag
   504  			}
   505  			// exclude partial line at bottom
   506  			if p.X >= w.Body.ScrollR.Max.X && p.Y >= w.Body.Fr.R.Max.Y {
   507  				return nil
   508  			}
   509  			return &w.Body
   510  		}
   511  	}
   512  	return nil
   513  }