1// Copyright 2014 The gocui Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package main
6
7import (
8	"fmt"
9	"log"
10	"strings"
11
12	"github.com/miguelmota/gocui"
13)
14
15const delta = 1
16
17var (
18	views   = []string{}
19	curView = -1
20	idxView = 0
21)
22
23func main() {
24	g, err := gocui.NewGui(gocui.OutputNormal)
25	if err != nil {
26		log.Panicln(err)
27	}
28	defer g.Close()
29
30	g.Highlight = true
31	g.SelFgColor = gocui.ColorRed
32
33	g.SetManagerFunc(layout)
34
35	if err := initKeybindings(g); err != nil {
36		log.Panicln(err)
37	}
38	if err := newView(g); err != nil {
39		log.Panicln(err)
40	}
41
42	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
43		log.Panicln(err)
44	}
45}
46
47func layout(g *gocui.Gui) error {
48	maxX, _ := g.Size()
49	v, err := g.SetView("help", maxX-25, 0, maxX-1, 9)
50	if err != nil {
51		if err != gocui.ErrUnknownView {
52			return err
53		}
54		fmt.Fprintln(v, "KEYBINDINGS")
55		fmt.Fprintln(v, "Space: New View")
56		fmt.Fprintln(v, "Tab: Next View")
57		fmt.Fprintln(v, "← ↑ → ↓: Move View")
58		fmt.Fprintln(v, "Backspace: Delete View")
59		fmt.Fprintln(v, "t: Set view on top")
60		fmt.Fprintln(v, "b: Set view on bottom")
61		fmt.Fprintln(v, "^C: Exit")
62	}
63	return nil
64}
65
66func initKeybindings(g *gocui.Gui) error {
67	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone,
68		func(g *gocui.Gui, v *gocui.View) error {
69			return gocui.ErrQuit
70		}); err != nil {
71		return err
72	}
73	if err := g.SetKeybinding("", gocui.KeySpace, gocui.ModNone,
74		func(g *gocui.Gui, v *gocui.View) error {
75			return newView(g)
76		}); err != nil {
77		return err
78	}
79	if err := g.SetKeybinding("", gocui.KeyBackspace2, gocui.ModNone,
80		func(g *gocui.Gui, v *gocui.View) error {
81			return delView(g)
82		}); err != nil {
83		return err
84	}
85	if err := g.SetKeybinding("", gocui.KeyTab, gocui.ModNone,
86		func(g *gocui.Gui, v *gocui.View) error {
87			return nextView(g, true)
88		}); err != nil {
89		return err
90	}
91	if err := g.SetKeybinding("", gocui.KeyArrowLeft, gocui.ModNone,
92		func(g *gocui.Gui, v *gocui.View) error {
93			return moveView(g, v, -delta, 0)
94		}); err != nil {
95		return err
96	}
97	if err := g.SetKeybinding("", gocui.KeyArrowRight, gocui.ModNone,
98		func(g *gocui.Gui, v *gocui.View) error {
99			return moveView(g, v, delta, 0)
100		}); err != nil {
101		return err
102	}
103	if err := g.SetKeybinding("", gocui.KeyArrowDown, gocui.ModNone,
104		func(g *gocui.Gui, v *gocui.View) error {
105			return moveView(g, v, 0, delta)
106		}); err != nil {
107		return err
108	}
109	if err := g.SetKeybinding("", gocui.KeyArrowUp, gocui.ModNone,
110		func(g *gocui.Gui, v *gocui.View) error {
111			return moveView(g, v, 0, -delta)
112		}); err != nil {
113		return err
114	}
115	if err := g.SetKeybinding("", 't', gocui.ModNone,
116		func(g *gocui.Gui, v *gocui.View) error {
117			_, err := g.SetViewOnTop(views[curView])
118			return err
119		}); err != nil {
120		return err
121	}
122	if err := g.SetKeybinding("", 'b', gocui.ModNone,
123		func(g *gocui.Gui, v *gocui.View) error {
124			_, err := g.SetViewOnBottom(views[curView])
125			return err
126		}); err != nil {
127		return err
128	}
129	return nil
130}
131
132func newView(g *gocui.Gui) error {
133	maxX, maxY := g.Size()
134	name := fmt.Sprintf("v%v", idxView)
135	v, err := g.SetView(name, maxX/2-5, maxY/2-5, maxX/2+5, maxY/2+5)
136	if err != nil {
137		if err != gocui.ErrUnknownView {
138			return err
139		}
140		v.Wrap = true
141		fmt.Fprintln(v, strings.Repeat(name+" ", 30))
142	}
143	if _, err := g.SetCurrentView(name); err != nil {
144		return err
145	}
146
147	views = append(views, name)
148	curView = len(views) - 1
149	idxView += 1
150	return nil
151}
152
153func delView(g *gocui.Gui) error {
154	if len(views) <= 1 {
155		return nil
156	}
157
158	if err := g.DeleteView(views[curView]); err != nil {
159		return err
160	}
161	views = append(views[:curView], views[curView+1:]...)
162
163	return nextView(g, false)
164}
165
166func nextView(g *gocui.Gui, disableCurrent bool) error {
167	next := curView + 1
168	if next > len(views)-1 {
169		next = 0
170	}
171
172	if _, err := g.SetCurrentView(views[next]); err != nil {
173		return err
174	}
175
176	curView = next
177	return nil
178}
179
180func moveView(g *gocui.Gui, v *gocui.View, dx, dy int) error {
181	name := v.Name()
182	x0, y0, x1, y1, err := g.ViewPosition(name)
183	if err != nil {
184		return err
185	}
186	if _, err := g.SetView(name, x0+dx, y0+dy, x1+dx, y1+dy); err != nil {
187		return err
188	}
189	return nil
190}
191