ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/Net-IRC3/samples/CursesChatMainwindow.pm
Revision: 1.4
Committed: Fri Jan 5 09:51:54 2007 UTC (19 years, 9 months ago) by elmex
Branch: MAIN
Changes since 1.3: +11 -3 lines
Log Message:
added another status line to the top of the window

File Contents

# Content
1 package CursesChatMainwindow;
2 use strict;
3 use Curses;
4 use Term::ReadKey;
5 use Exporter;
6 our @ISA = qw/Exporter/;
7 our @EXPORT = qw/printline select_buffer current_buffer list_buffers/;
8
9 our $wins;
10
11 our $HEIGHT;
12 our $WIDTH;
13 our $MAINWIN;
14 our $stdiowatcher;
15 our $winchwatcher;
16
17 our $current_buffer_scroll = 0;
18 our $current_buffer = "status";
19 our $winbuffers = {};
20
21 our $complete_cb = sub {};
22 our $input_cb = sub {};
23
24 our %completions;
25 our $first_compl;
26 our @history;
27 our $inputbuffer;
28 our $inputoffset = 0;
29 our $cursor = 0;
30 our $statusline;
31 our $msgline;
32
33 sub handle_resize {
34 my ($wc, $hc, $wpx, $hpx) = GetTerminalSize ();
35 resizeterm ($hc, $wc);
36 $MAINWIN->resize ($hc, $wc);
37 $HEIGHT = $hc;
38 $WIDTH = $wc;
39 $MAINWIN->erase;
40 refresh ();
41 }
42
43 sub complete {
44 my ($line) = @_;
45 my @words;
46 while ($line ne '') {
47 $line =~ s/^(\S+)//
48 or $line =~ s/^(\s+)//;
49 push @words, $1;
50 }
51 my $widx = @words - 1;
52 unless (@words) { $words[0] = ''; $widx = 0; }
53 # while ($widx >= 0 && $words[$widx] =~ /^\s*$/) {
54 # $widx--;
55 # }
56 # return $line unless $widx >= 0;
57
58 $first_compl = defined $first_compl ? $first_compl : $words[$widx];
59 my $word = $complete_cb->($words[$widx], $first_compl, $widx, @words);
60 $words[$widx] = defined $word ? $word : $words[$widx];
61
62 return join '', @words;
63 }
64
65 sub color2attr {
66 my $c = $_[0];
67 my $attr;
68 if ($c <= 63) {
69 $attr = COLOR_PAIR($c);
70 } elsif ($c <= 127) {
71 $c -= 64;
72 $attr = COLOR_PAIR($c) | A_BOLD;
73 } elsif ($c <= 191) {
74 $c -= 128;
75 $attr = COLOR_PAIR($c) | A_UNDERLINE;
76 } else {
77 $c -= 192;
78 $attr = COLOR_PAIR($c) | A_UNDERLINE | A_BOLD;
79 }
80 $attr
81 }
82
83 sub register_input_cb {
84 my ($cb) = @_;
85 $input_cb = $cb;
86 }
87
88 sub register_complete_cb {
89 my ($cb) = @_;
90 $complete_cb = $cb;
91 }
92
93 sub _draw_attr_line_at {
94 my ($line, $attrline) = @_;
95
96 my @lcont = @$attrline;
97 my $li = 0;
98
99 while (@lcont > 0) {
100 my ($color, $str) = (shift @lcont, shift @lcont);
101
102 $MAINWIN->attron (color2attr ($color));
103
104 my $sl = length $str;
105 if (($li + $sl) > $WIDTH) {
106 $sl = $WIDTH - $li;
107 last if $sl < 0;
108 }
109
110 $MAINWIN->addnstr ($line, $li, (substr $str, 0, $sl), $sl);
111
112 $li += $sl;
113
114 $MAINWIN->attroff (color2attr ($color));
115 }
116 }
117
118 sub clog {
119 open LOG, ">>/tmp/debuglog";
120 print LOG @_;
121 close LOG;
122 }
123
124 sub wrap_attr_lines {
125 my ($width, @lines) = @_;
126
127 my @outlines;
128
129 for my $line (@lines) {
130 my $wrap_padding = 0;
131 my @cline = @$line;
132 my @oline;
133
134 my $outc = 0;
135
136 while (@cline) {
137 my ($color, $str) = (shift @cline, shift @cline);
138 if ($color =~ m/^p\s*(\d+)\s*$/) {
139 $wrap_padding = $1;
140 ($color, $str) = ($str, shift @cline);
141 }
142
143 my $strlen = length $str;
144
145 if (($outc + $strlen) > $width) {
146 my $thisl = $width - $outc;
147 push @oline, ($color, substr $str, 0, $thisl);
148 push @outlines, [@oline];
149 @oline = ();
150 unshift @cline, ($color, substr $str, $thisl);
151 if ($wrap_padding && $wrap_padding < $width) {
152 unshift @cline, (0, ' ' x $wrap_padding);
153 }
154 $outc = 0;
155 } else {
156 push @oline, ($color, $str);
157 $outc += $strlen;
158 }
159 }
160
161 push @outlines, [@oline];
162 }
163
164 # my $i = 0;
165 # for my $line (@outlines) {
166 # clog ("LINE$i: ".join ('|',@$line)."\n");
167 # $i++;
168 # }
169
170 @outlines;
171 }
172
173 sub refresh_lines {
174 $MAINWIN->erase;
175
176 # TODO: cache the wrapped lines until something in the buffer
177 # or the window size changes
178 # NOTE: cache doesn't have to be invalidated for adding new lines!
179 my @revwin = @{$winbuffers->{$current_buffer} || []};
180 my (@out_lines) = reverse wrap_attr_lines ($WIDTH, @revwin);
181
182 if ($current_buffer_scroll > 0) {
183 if ($current_buffer_scroll > scalar (@out_lines)) {
184 $current_buffer_scroll = scalar (@out_lines);
185 }
186 @out_lines = splice @out_lines, $current_buffer_scroll;
187 clog ("SPLICE$current_buffer_scroll [".(scalar @out_lines)."]\n");
188 }
189
190 _draw_attr_line_at (0, $msgline || [0, 'nothing']);
191 _draw_attr_line_at ($HEIGHT - 2, $statusline || [0, 'nothing']);
192
193 my $line = $HEIGHT - 3;
194 while ($line >= 1) {
195 my $l = shift @out_lines;
196 last unless defined $l;
197 _draw_attr_line_at ($line--, $l);
198 }
199 }
200
201 sub refresh {
202 my ($onlyinput) = @_;
203
204 refresh_lines unless $onlyinput;
205
206 $MAINWIN->move ($HEIGHT - 1, 0);
207 $MAINWIN->clrtoeol ();
208
209 my $padding = $WIDTH - int ($WIDTH / 1.2);
210
211 if ($cursor >= ($WIDTH - $padding)) {
212 my $iobuf = $inputbuffer;
213 my $il = length $iobuf;
214 my $ncursor = $cursor;
215 my $lcursor = $cursor - int ($WIDTH / 1.2);
216 $ncursor -= $lcursor;
217 substr $iobuf, 0, $lcursor, '';
218 $MAINWIN->addstr ($HEIGHT - 1, 0, (substr $iobuf, 0, $WIDTH));
219 $MAINWIN->move ($HEIGHT - 1, $ncursor);
220 } else {
221 $MAINWIN->addstr ($HEIGHT - 1, 0, (substr $inputbuffer, 0, $WIDTH));
222 $MAINWIN->move ($HEIGHT - 1, $cursor);
223 }
224 $MAINWIN->refresh;
225 }
226
227 sub select_buffer {
228 $current_buffer = $_[0];
229 $current_buffer_scroll = 0;
230 refresh ();
231 }
232
233 sub current_buffer {
234 $current_buffer
235 }
236
237 sub list_buffers {
238 keys %$winbuffers;
239 }
240
241 sub printline {
242 if ($_[0] eq 'statusline') {
243 $statusline = $_[1];
244 } elsif ($_[0] eq 'msgline') {
245 $msgline = $_[1];
246 } else {
247 push @{$winbuffers->{(defined $_[0] ? $_[0] : $current_buffer)}}, $_[1];
248 }
249 refresh ();
250 }
251
252 sub clear_completion_state {
253 # erase completion state
254 $first_compl = undef;
255 }
256
257 sub input {
258 my $c = $MAINWIN->getch;
259
260 if ($c == KEY_BACKSPACE) {
261 if ($cursor > 0) {
262 substr $inputbuffer, --$cursor, 1, '';
263 }
264 clear_completion_state;
265 } elsif ($c == KEY_DC) {
266 substr $inputbuffer, $cursor, 1, '';
267 clear_completion_state;
268 } elsif ($c == KEY_LEFT) {
269 $cursor-- if $cursor > 0;
270 clear_completion_state;
271 } elsif ($c == KEY_RIGHT) {
272 $cursor++ if (($cursor + 1) <= (length $inputbuffer));
273 clear_completion_state;
274 } elsif ($c == KEY_PPAGE) {
275 $current_buffer_scroll += int ($HEIGHT / 2);
276 } elsif ($c == KEY_NPAGE) {
277 $current_buffer_scroll -= int ($HEIGHT / 2);
278 $current_buffer_scroll = 0 if $current_buffer_scroll < 0;
279 } elsif ($c eq "\t") {
280 my $compl_line = substr $inputbuffer, 0, $cursor;
281 $compl_line = complete ($compl_line);
282 substr $inputbuffer, 0, $cursor, $compl_line;
283 $cursor = length $compl_line;
284 } elsif ($c eq "\n") {
285 push @history, $inputbuffer;
286 $input_cb->($inputbuffer);
287 $inputbuffer = "";
288 $cursor = 0;
289 clear_completion_state;
290 } else {
291 if ($c > 255) {
292 printline (status => [0, "SPECIAL[$c]"]);
293 } else {
294 if (ord ($c) == 27) {
295 my $nxt = $MAINWIN->getch;
296 $input_cb->(undef, $nxt);
297 return;
298 } else {
299 clear_completion_state;
300 substr $inputbuffer, $cursor++, 0, $c;
301 }
302 }
303 }
304 refresh ();
305 }
306
307 sub init {
308 my $win = $MAINWIN = Curses->new;
309 cbreak;
310 noecho;
311 clear;
312 $win->keypad (1);
313 ($HEIGHT, $WIDTH) = ($LINES, $COLS);
314 if (has_colors ()) {
315 start_color ();
316 use_default_colors ();
317 my $colors = "";
318 my (@ansi_tab) = qw/0 4 2 6 1 5 3 7/;
319 for (my $i = 1; $i < $COLOR_PAIRS; $i++) {
320 init_pair ($i, $ansi_tab[$i & 7], $i <= 7 ? -1 : $ansi_tab[$i >> 3]);
321 }
322 }
323 $win->attron (A_NORMAL);
324 $winchwatcher = AnyEvent->signal (signal => 'WINCH', cb => sub { handle_resize });
325 $stdiowatcher = AnyEvent->io (fh => \*STDIN, poll => "r", cb => sub {
326 input;
327 });
328 }
329
330 sub end {
331 endwin;
332 }
333
334 sub print_colors {
335 my $l = [];
336 for (my $i = 0; $i <= 256; $i++) {
337 push @$l, $i, (sprintf "[%3d]", $i);
338 if (($i + 1) % 8 == 0) {
339 printline ($current_buffer => $l);
340 $l = [];
341 }
342 }
343 printline ($current_buffer => $l);
344 }
345 #printline ("TESTE!!!");
346
347 1;