1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
package ctfc
import (
"strconv"
"strings"
"time"
"gitlab.com/Syndamia/ctfc/go-src/csi"
"gitlab.com/Syndamia/ctfc/go-src/ui"
"gitlab.com/Syndamia/ctfc/go-src/utils"
)
/* Pagination */
const pageSize = 15
func totalPages(messageAmount int) int {
return utils.CeilDivInt(messageAmount, pageSize)
}
func paginate(page int, messages ...string) []string {
return messages[utils.MaxInt(len(messages)-pageSize*page, 0) : len(messages)-pageSize*(page-1)]
}
// Must be run in a routine ( go routinePaginatedSubwindow(...) )
// There must be empty lines for the subwindow
func routinePaginatedSubwindow(messages *[]string, updateMessages func(*[]string), lastLine *int, page int, customLinesBelow int, numbered bool) {
for *lastLine > -2 {
// Update messages, if we've already shown the last message
if *lastLine == len(*messages)-1 {
updateMessages(messages)
continue
}
*lastLine = len(*messages) - 1
csi.SaveCursorPosition()
csi.MoveCursorUpN(pageSize + 1 + customLinesBelow)
csi.MoveCursorToBeginningOfLine()
pageMessages := paginate(page, *messages...)
// Leaves empty lines at the top, if there aren't enough messages to fill a full page
// Works on the assumption that there are ui.EmptyLine(), where messages would be printed
for i := 0; i < pageSize-len(pageMessages); i++ {
csi.MoveCursorDown()
}
if numbered {
ui.NumberedFields(pageMessages...)
} else {
ui.TextFields(pageMessages...)
}
ui.PageField(page, totalPages(len(*messages)))
csi.RestoreCursorPosition()
time.Sleep(500 * time.Millisecond)
}
}
func initPaginatedValues(defaultLength int, values *[]string) {
if len(*values) == defaultLength {
*values = append(append(*values, "."), "")
}
}
/* Input actions */
type inputAction struct {
value string
execute window
args []string
}
func handleInputActions(input string, handleNav bool, ia ...inputAction) func() {
if handleNav {
ia = append(ia,
inputAction{"C", chatsWindow, nil},
inputAction{"D", directMessagesWindow, nil},
inputAction{"A", accountWindow, nil},
inputAction{"L", logoutWindow, nil},
)
}
for _, v := range ia {
if strings.ToLower(input) == strings.ToLower(v.value) {
return func() { v.execute(v.args...) }
}
}
return nil
}
func handleComplexInputActions(input string, allValues []string, nameSep string, pageArgument *string, currentWindow window, singleValueWindow window, existsCheck func(string) bool) {
// If user input is number, navigate to chat of given number, else show error
if chatI, err := strconv.Atoi(input); chatI >= 0 && chatI <= len(allValues) && err == nil {
defer singleValueWindow(strings.Split(allValues[utils.MaxInt(len(allValues)-pageSize*len(*pageArgument), 0)+chatI-1], " : ")[0])
} else if input == ">" && pageArgument != nil {
// If possible, increment to the next page (adds a dot to the end of the string)
if len(*pageArgument) < totalPages(len(allValues)) {
*pageArgument += "."
}
defer currentWindow(allValues...)
} else if input == "<" {
// If possible, decrement to the previous page (removes a dot from the string)
if len(*pageArgument) > 1 {
utils.StrShortenRight(*&pageArgument, 1)
}
defer currentWindow(allValues...)
} else if existsCheck(input) {
defer singleValueWindow(input)
} else {
defer showError(invalidCommand, currentWindow)
}
}
/* Form */
type formInput struct {
name string
specification string
validationFunc func(string) bool
}
func formWindow(boxTitle string, backWindow window, formInputs []formInput, currentValues ...string) (userInputs []string) {
userInputs = currentValues
// Go through all inputs
for i, v := range formInputs[len(currentValues):] {
csi.ClearScreen()
ui.NormalBox(true, boxTitle)
// Show filled input fields
for j, va := range userInputs {
ui.InputFieldFilled(formInputs[j].name, va)
}
// Wait for input on current input field
input := ui.InputField(v.name + v.specification)
// Go to the "backWindow", if we're on the first input, we are given a back window, and the letter B is given
if i == 0 && backWindow != nil && strings.ToLower(input) == "b" {
defer backWindow()
return
}
// If we're validating input data, try to validate
if v.validationFunc != nil {
// If input data isn't valid, show an error and the same screen
if !v.validationFunc(input) {
formCallback := func(...string) {
defer formWindow(boxTitle, backWindow, formInputs, userInputs...)
}
defer showError(invalidValueFor(v.name), formCallback)
return
}
}
// Add current input as a filled input
userInputs = append(userInputs, input)
}
return
}
type multiFormProp struct {
propInd int
editTitle string
formInp formInput
updateF func([]string) bool
postSucUpdateF func([]string)
}
func validatedMultiForm(input string, returnWindow window, props ...multiFormProp) {
propInd, _ := strconv.Atoi(input)
inputs, triedUpdate, updateSuccessful := []formInput{{currentPasswordInName, inputBackSpec, nil}}, false, false
for _, v := range props {
if v.propInd == propInd {
triedUpdate = true
values := formWindow(v.editTitle, returnWindow, append(inputs, v.formInp))
updateSuccessful = v.updateF(values)
if updateSuccessful && v.postSucUpdateF != nil {
v.postSucUpdateF(values)
}
break
}
}
if !triedUpdate {
defer showError(invalidCommand, returnWindow)
}
if !updateSuccessful {
defer showError(invalidArgument, returnWindow)
} else {
defer returnWindow()
}
}
/* Error */
func showError(message string, callback window, callbackData ...string) {
csi.ClearScreen()
ui.ErrorBox(message)
csi.BlockClearScreenNextTime()
defer callback(callbackData...)
}
|