aboutsummaryrefslogtreecommitdiff
path: root/go-src/windowsHelpers.go
blob: f59f34fbcaf2129ed59fd9db899e330823d62058 (plain) (blame)
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...)
}