1 /*
2  *  STFL - The Structured Terminal Forms Language/Library
3  *  Copyright (C) 2006, 2007  Clifford Wolf <clifford@clifford.at>
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 3 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18  *  MA 02110-1301 USA
19  *
20  *  wt_input.c: Widget type 'input'
21  */
22 
23 #include "stfl_internals.h"
24 
25 #include <string.h>
26 #include <stdlib.h>
27 #include <wctype.h>
28 
wt_input_init(struct stfl_widget * w)29 static void wt_input_init(struct stfl_widget *w)
30 {
31 	w->allow_focus = 1;
32 }
33 
fix_offset_pos(struct stfl_widget * w)34 static void fix_offset_pos(struct stfl_widget *w)
35 {
36 	int pos = stfl_widget_getkv_int(w, L"pos", 0);
37 	int offset = stfl_widget_getkv_int(w, L"offset", 0);
38 	const wchar_t* text = stfl_widget_getkv_str(w, L"text", L"");
39 	int text_len = wcslen(text);
40 	int changed = 0;
41 	int width;
42 
43 	if (pos > text_len) {
44 		pos = text_len;
45 		changed = 1;
46 	}
47 
48 	if (offset > pos) {
49 		offset = pos;
50 		changed = 1;
51 	}
52 
53 	width = wcswidth(text + offset, pos - offset);
54 	while (width >= w->w && pos > offset) {
55 		width -= wcwidth(text[offset++]);
56 		changed = 1;
57 	}
58 
59 	if (changed) {
60 		stfl_widget_setkv_int(w, L"pos", pos);
61 		stfl_widget_setkv_int(w, L"offset", offset);
62 	}
63 }
64 
wt_input_prepare(struct stfl_widget * w,struct stfl_form * f)65 static void wt_input_prepare(struct stfl_widget *w, struct stfl_form *f)
66 {
67 	int size = stfl_widget_getkv_int(w, L"size", 5);
68 
69 	w->min_w = size;
70 	w->min_h = 1;
71 
72 	fix_offset_pos(w);
73 }
74 
wt_input_draw(struct stfl_widget * w,struct stfl_form * f,WINDOW * win)75 static void wt_input_draw(struct stfl_widget *w, struct stfl_form *f, WINDOW *win)
76 {
77 	int pos = stfl_widget_getkv_int(w, L"pos", 0);
78 	int blind = stfl_widget_getkv_int(w, L"blind", 0);
79 	int offset = stfl_widget_getkv_int(w, L"offset", 0);
80 	const wchar_t * const text_off = stfl_widget_getkv_str(w, L"text", L"") + offset;
81 	int i;
82 
83 	stfl_widget_style(w, f, win);
84 
85 	for (i=0; i<w->w; i++)
86 		mvwaddwstr(win, w->y, w->x+i, L" ");
87 
88 	if (!blind) {
89 		const int off_len = wcslen(text_off);
90 		int width, len;
91 
92 		width = wcswidth(text_off, w->w);
93 		if (w->w > off_len)
94 			len = off_len;
95 		else
96 			len = w->w;
97 		while (width > w->w)
98 			width -= wcwidth(text_off[--len]);
99 		mvwaddnwstr(win, w->y, w->x, text_off, len);
100 	}
101 
102 	if (f->current_focus_id == w->id) {
103 		f->root->cur_x = f->cursor_x = w->x + wcswidth(text_off, pos - offset);
104 		f->root->cur_y = f->cursor_y = w->y;
105 	}
106 }
107 
wt_input_process(struct stfl_widget * w,struct stfl_widget * fw,struct stfl_form * f,wchar_t ch,int isfunckey)108 static int wt_input_process(struct stfl_widget *w, struct stfl_widget *fw, struct stfl_form *f, wchar_t ch, int isfunckey)
109 {
110 	int pos = stfl_widget_getkv_int(w, L"pos", 0);
111 	const wchar_t *text = stfl_widget_getkv_str(w, L"text", L"");
112 	int text_len = wcslen(text);
113 
114 	if (pos > 0 && stfl_matchbind(w, ch, isfunckey, L"left", L"LEFT")) {
115 		stfl_widget_setkv_int(w, L"pos", pos-1);
116 		fix_offset_pos(w);
117 		return 1;
118 	}
119 
120 	if (pos < text_len && stfl_matchbind(w, ch, isfunckey, L"right", L"RIGHT")) {
121 		stfl_widget_setkv_int(w, L"pos", pos+1);
122 		fix_offset_pos(w);
123 		return 1;
124 	}
125 
126 	// pos1 / home / Ctrl-A
127 	if (stfl_matchbind(w, ch, isfunckey, L"home", L"HOME ^A")) {
128 		stfl_widget_setkv_int(w, L"pos", 0);
129 		fix_offset_pos(w);
130 		return 1;
131 	}
132 
133 	// end / Ctrl-E
134 	if (stfl_matchbind(w, ch, isfunckey, L"end", L"END ^E")) {
135 		stfl_widget_setkv_int(w, L"pos", text_len);
136 		fix_offset_pos(w);
137 		return 1;
138 	}
139 
140 	// delete
141 	if (stfl_matchbind(w, ch, isfunckey, L"delete", L"DC")) {
142 		if (pos == text_len)
143 			return 0;
144 		wchar_t newtext[text_len];
145 		wmemcpy(newtext, text, pos);
146 		wcscpy(newtext + pos, text + pos + 1);
147 		stfl_widget_setkv_str(w, L"text", newtext);
148 		fix_offset_pos(w);
149 		return 1;
150 	}
151 
152 	// backspace
153 	if (stfl_matchbind(w, ch, isfunckey, L"backspace", L"BACKSPACE")) {
154 		if (pos == 0)
155 			return 0;
156 		wchar_t newtext[text_len];
157 		wmemcpy(newtext, text, pos-1);
158 		wcscpy(newtext + pos - 1, text + pos);
159 		stfl_widget_setkv_str(w, L"text", newtext);
160 		stfl_widget_setkv_int(w, L"pos", pos-1);
161 		fix_offset_pos(w);
162 		return 1;
163 	}
164 
165 	// 'normal' characters
166 	if (!isfunckey && iswprint(ch)) {
167 		wchar_t newtext[text_len + 2];
168 		wmemcpy(newtext, text, pos);
169 		newtext[pos] = ch;
170 		wcscpy(newtext + pos + 1, text + pos);
171 		stfl_widget_setkv_str(w, L"text", newtext);
172 		stfl_widget_setkv_int(w, L"pos", pos+1);
173 		fix_offset_pos(w);
174 		return 1;
175 	}
176 
177 	return 0;
178 }
179 
180 struct stfl_widget_type stfl_widget_type_input = {
181 	L"input",
182 	wt_input_init,
183 	0, // f_done
184 	0, // f_enter
185 	0, // f_leave
186 	wt_input_prepare,
187 	wt_input_draw,
188 	wt_input_process
189 };
190 
191