Fixed screen refresh routine.
[crypto.git] / apps / silc / screen.c
1 /*
2
3   screen.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2000 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /*
21  * SILC client screen routines. These implement the user interface
22  * on ncurses routines. Most of these routines were taken from the
23  * old version of the SILC client dating back to 1997.
24  */
25 /* XXX: Input line handling is really buggy! */
26 /*
27  * $Id$
28  * $Log$
29  * Revision 1.3  2000/07/07 06:52:10  priikone
30  *      Fixed screen refresh routine.
31  *
32  * Revision 1.2  2000/07/05 06:12:05  priikone
33  *      Global cosmetic changes.
34  *
35  * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
36  *      Imported from internal CVS/Added Log headers.
37  *
38  *
39  */
40
41 #include "clientincludes.h"
42
43 SilcScreen silc_screen_init()
44 {
45   SilcScreen new;
46
47   new = silc_malloc(sizeof(*new));
48   new->output_win_count = 0;
49   new->input_pos = 0;
50   new->cursor_pos = 0;
51   new->virtual_window = 0;
52   new->insert = TRUE;
53
54   initscr();
55   cbreak();
56   nonl();
57   noecho();
58
59   silc_screen_create_output_window(new);
60   silc_screen_create_input_window(new);
61
62   return new;
63 }
64
65 /* Creates one (main) output window. Returns new created physical 
66    window. */
67
68 WINDOW *silc_screen_create_output_window(SilcScreen screen)
69 {
70   assert(screen != NULL);
71
72   screen->output_win = silc_malloc(sizeof(*screen->output_win) * 1);
73   screen->output_win_count = 1;
74   screen->output_win[0] = newwin(LINES - 3, COLS, 1, 0);
75   scrollok(screen->output_win[0], TRUE);
76   idlok(screen->output_win[0], TRUE);
77   wrefresh(screen->output_win[0]);
78
79   return screen->output_win[0];
80 }
81
82 /* Adds new output window. Return new created physical window. */
83
84 WINDOW *silc_screen_add_output_window(SilcScreen screen)
85 {
86   int i;
87
88   assert(screen != NULL);
89
90   screen->output_win = silc_realloc(screen->output_win, 
91                                     (screen->output_win_count + 1) *
92                                     sizeof(*screen->output_win));
93   i = screen->output_win_count;
94   screen->output_win[i] = newwin(LINES - 3, COLS, 1, 0);
95   scrollok(screen->output_win[i], TRUE);
96   idlok(screen->output_win[i], TRUE);
97   wrefresh(screen->output_win[i]);
98   screen->output_win_count++;
99
100   return screen->output_win[i];
101 }
102
103 void silc_screen_create_input_window(SilcScreen screen)
104 {
105   assert(screen != NULL);
106
107   screen->input_win = newwin(0, COLS, LINES - 1, 0);
108   scrollok(screen->input_win, TRUE);
109   keypad(screen->input_win, TRUE);
110   wrefresh(screen->input_win);
111 }
112
113 void silc_screen_init_upper_status_line(SilcScreen screen)
114 {
115   assert(screen != NULL);
116
117   /* Create upper status line */
118   screen->upper_stat_line = newwin(0, COLS, 0, 0);
119   scrollok(screen->upper_stat_line, FALSE);
120   wattrset(screen->upper_stat_line, A_REVERSE);
121
122   silc_screen_print_upper_stat_line(screen);
123 }
124
125 void silc_screen_print_upper_stat_line(SilcScreen screen)
126 {
127   int i;
128   int justify;
129   
130   /* Print empty line */
131   for (i = 0; i < COLS - 1; i++)
132     mvwprintw(screen->upper_stat_line, 0, i, " ");
133   
134   /* Print stuff with justify */
135   justify = COLS / 5;
136   mvwprintw(screen->upper_stat_line, 0, 1, "%s %s", 
137             screen->u_stat_line.program_name, 
138             screen->u_stat_line.program_version);
139
140   /* Prints clock on upper stat line */ 
141   silc_screen_print_clock(screen);
142   wrefresh(screen->upper_stat_line);
143 }
144
145 void silc_screen_init_output_status_line(SilcScreen screen)
146 {
147   int i;
148
149   assert(screen != NULL);
150
151   screen->output_stat_line = silc_calloc(1, sizeof(*screen->output_stat_line));
152   
153   screen->output_stat_line[0] = newwin(1, COLS, LINES - 2, 0);
154   scrollok(screen->output_stat_line[0], FALSE);
155   wattrset(screen->output_stat_line[0], A_REVERSE);
156   
157   /* print first just blank line */
158   for (i = 0; i < COLS - 1; i++)
159     mvwprintw(screen->output_stat_line[0], 0, i, " ");
160
161   /* Allocate bottom line */
162   screen->bottom_line = silc_calloc(1, sizeof(*screen->bottom_line));
163
164   wattrset(screen->output_stat_line[0], A_NORMAL);
165   wrefresh(screen->output_stat_line[0]);
166 }
167
168 void silc_screen_print_clock(SilcScreen screen)
169 {
170   time_t curtime;
171   struct tm *tp;
172
173   curtime = time(0);
174   tp = localtime(&curtime);
175
176   mvwprintw(screen->upper_stat_line, 0, COLS - 8, "[%02d:%02d] ", 
177             tp->tm_hour, tp->tm_min);
178   wrefresh(screen->upper_stat_line);
179 }
180
181 /* Prints current cursor coordinates on some output stat line */
182
183 void silc_screen_print_coordinates(SilcScreen screen, int win_index)
184 {
185   wattrset(screen->output_stat_line[win_index], A_REVERSE);
186   mvwprintw(screen->output_stat_line[win_index], 0, COLS - 10,
187             "[%4d,%3d]", screen->input_pos, LINES);
188   wrefresh(screen->output_stat_line[win_index]);
189   wattrset(screen->output_stat_line[win_index], A_NORMAL);
190 }
191
192 /* Prints bottom line (the status line) of the screen. */
193
194 void silc_screen_print_bottom_line(SilcScreen screen, int win_index)
195 {
196   char buf[512];
197   SilcScreenBottomLine line = screen->bottom_line;
198   int i, len;
199
200   memset(buf, 0, sizeof(buf));
201
202   if (line->mode) {
203     len = strlen(line->mode);
204     strncat(buf, line->mode, len);
205   }
206
207   if (line->nickname) {
208     len = strlen(line->nickname);
209     strncat(buf, line->nickname, len > SILC_SCREEN_MAX_NICK_LEN ? 
210             SILC_SCREEN_MAX_NICK_LEN : len);
211   }
212
213   if (line->connection) {
214     len = strlen(line->connection);
215     strncat(buf, " via ", 5);
216     strncat(buf, line->connection, len > SILC_SCREEN_MAX_CONN_LEN ? 
217             SILC_SCREEN_MAX_CONN_LEN : len);
218   }
219
220   if (line->channel) {
221     len = strlen(line->channel);
222     strncat(buf, " ", 1);
223     strncat(buf, line->channel, len > SILC_SCREEN_MAX_CHANNEL_LEN ?
224             SILC_SCREEN_MAX_CHANNEL_LEN : len);
225   }
226
227   wattrset(screen->output_stat_line[win_index], A_REVERSE);
228
229   for (i = 0; i < COLS - 10; i++)
230     mvwprintw(screen->output_stat_line[win_index], 0, i, " ");
231
232   mvwprintw(screen->output_stat_line[win_index], 0, 0, " %s", buf);
233   silc_screen_print_coordinates(screen, win_index);
234   wrefresh(screen->output_stat_line[win_index]);
235   wattrset(screen->output_stat_line[win_index], A_NORMAL);
236 }
237
238 /* Refresh all windows */
239
240 void silc_screen_refresh_all(SilcScreen screen)
241 {
242   int i;
243
244   assert(screen != NULL);
245
246   wclear(screen->upper_stat_line);
247   silc_screen_print_upper_stat_line(screen);
248
249   wclear(screen->output_stat_line[0]);
250   silc_screen_print_bottom_line(screen, 0);
251   silc_screen_print_coordinates(screen, 0);
252
253   for (i = 0; i < screen->output_win_count; i++) {
254     wclear(screen->output_win[i]);
255     wrefresh(screen->output_win[i]);
256   }
257
258   wclear(screen->input_win);
259   wrefresh(screen->input_win);
260 }
261
262 /* Refreshes a window */
263
264 void silc_screen_refresh_win(WINDOW *win)
265 {
266   assert(win != NULL);
267
268   redrawwin(win);
269   wrefresh(win);
270 }
271
272 /* Resets input window */
273
274 void silc_screen_input_reset(SilcScreen screen)
275 {
276   int i;
277
278   assert(screen != NULL);
279   for (i = 0; i < COLS - 1; i++)
280     mvwprintw(screen->input_win, 0, i, " ");
281   mvwprintw(screen->input_win, 0, 0, "");
282   wrefresh(screen->input_win);
283   screen->input_pos = 0;
284   screen->input_end = 0;
285   screen->cursor_pos = 0;
286   screen->virtual_window = 0;
287 }
288
289 /* Backspace. Removes one character from input windows. */
290
291 void silc_screen_input_backspace(SilcScreen screen)
292 {
293   WINDOW *win;
294   char *buffer;
295
296   assert(screen != NULL);
297   buffer = screen->input_buffer;
298   win = screen->input_win;
299
300   /* Return directly if at the start of input line */
301   if (screen->input_pos == 0)
302     return;
303
304   if (screen->virtual_window) {
305     if (screen->cursor_pos <= 10) {
306       int i;
307
308       /* Clear line */
309       for (i = 0; i < COLS; i++)
310         mvwprintw(win, 0, i, " ");
311       mvwprintw(win, 0, 0, "");
312
313       screen->virtual_window--;
314       
315       waddnstr(win, &buffer[screen->virtual_window * (COLS - 5)], COLS);
316       screen->input_pos = ((screen->virtual_window + 1) * (COLS - 5)) + 1;
317       screen->input_end = ((screen->virtual_window + 1) * (COLS - 5)) + 1;
318       screen->cursor_pos = (COLS - 5) + 1;
319       wrefresh(win);
320     }
321   }
322
323   screen->cursor_pos--;
324   screen->input_pos--;
325   screen->input_end--;
326   mvwdelch(win, 0, screen->cursor_pos);
327
328   if (screen->input_pos < screen->input_end)
329     /* Delete from inside the input line */
330     SILC_SCREEN_INPUT_DELETE(buffer, screen->input_pos, screen->input_end);
331   else
332     /* Delete from the end of the input line */
333     buffer[screen->input_pos] = 0;
334
335   wrefresh(win);
336 }
337
338 /* Switches insert on input window on/off */
339
340 void silc_screen_input_insert(SilcScreen screen)
341 {
342   assert(screen != NULL);
343
344   screen->insert = screen->insert == TRUE ? FALSE : TRUE;
345 }
346
347 /* Moves cursor one character length to rightward */
348
349 void silc_screen_input_cursor_right(SilcScreen screen)
350 {
351   WINDOW *win;
352   char *buffer;
353
354   assert(screen != NULL);
355   buffer = screen->input_buffer;
356   win = screen->input_win;
357
358   /* Return directly if we are at the end of input line */
359   if (screen->cursor_pos >= SILC_SCREEN_INPUT_WIN_SIZE)
360     return;
361
362   /* Make sure cursor doesn't advance over the end of the line */
363   if (screen->input_pos >= screen->input_end)
364     return;
365
366   /* When cursor advances enough we switch to new window and show
367      rest of the typed characters on the screen. */
368   if (screen->cursor_pos >= (COLS - 5)) {
369     int i;
370
371     /* Clear line */
372     for (i = 0; i < COLS; i++)
373       mvwprintw(win, 0, i, " ");
374     mvwprintw(win, 0, 0, "");
375
376     waddnstr(win, &buffer[screen->input_pos - 10], 
377              ((screen->input_pos - 10) - screen->input_end >= COLS) ?
378              COLS : (screen->input_pos - 10) - screen->input_end);
379     screen->cursor_pos = 10;
380     wrefresh(win);
381
382     screen->virtual_window++;
383   }
384
385   screen->cursor_pos++;
386   screen->input_pos++;
387   wmove(win, 0, screen->cursor_pos);
388   wrefresh(win);
389 }
390
391 /* Moves cursor one character length to leftward */
392
393 void silc_screen_input_cursor_left(SilcScreen screen)
394 {
395   WINDOW *win;
396   char *buffer;
397
398   assert(screen != NULL);
399   buffer = screen->input_buffer;
400   win = screen->input_win;
401
402   /* Return directly if at the start of input line */
403   if (screen->input_pos == 0)
404     return;
405
406   /* When cursor advances enough we switch to new window and show
407      rest of the typed characters on the screen. */
408   if (screen->virtual_window) {
409     if (screen->cursor_pos <= 10) {
410       int i;
411
412       /* Clear line */
413       for (i = 0; i < COLS; i++)
414         mvwprintw(win, 0, i, " ");
415       mvwprintw(win, 0, 0, "");
416
417       screen->virtual_window--;
418       
419       waddnstr(win, &buffer[screen->virtual_window * (COLS - 5)], COLS);
420       screen->input_pos = ((screen->virtual_window + 1) * (COLS - 5)) + 1;
421       screen->cursor_pos = (COLS - 5) + 1;
422       wrefresh(win);
423     }
424   }
425
426   screen->cursor_pos--;
427   screen->input_pos--;
428   wmove(win, 0, screen->cursor_pos);
429   wrefresh(win);
430 }
431
432 /* Moves cursor at the very start of the input line */
433
434 void silc_screen_input_cursor_home(SilcScreen screen)
435 {
436   WINDOW *win;
437   char *buffer;
438
439   assert(screen != NULL);
440   buffer = screen->input_buffer;
441   win = screen->input_win;
442
443   wclear(win);
444   waddnstr(win, &buffer[0], COLS);
445   wrefresh(win);
446
447   screen->input_pos = 0;
448   screen->cursor_pos = 0;
449   screen->virtual_window = 0;
450 }
451
452 /* Moves cursor at the very end of the input line */
453
454 void silc_screen_input_cursor_end(SilcScreen screen)
455 {
456   WINDOW *win;
457   char *buffer;
458
459   assert(screen != NULL);
460   buffer = screen->input_buffer;
461   win = screen->input_win;
462
463   wclear(win);
464   waddnstr(win, &buffer[screen->input_end - 10], 10);
465   wrefresh(win);
466
467   screen->input_pos = screen->input_end;
468   screen->cursor_pos = 10;
469   /* XXX */
470   screen->virtual_window = 0;
471 }
472
473 /* Prints typed character into the input window for user to see. Character 
474    attributes must be set separately outside this function. */
475
476 void silc_screen_input_print(SilcScreen screen, unsigned char c)
477 {
478   WINDOW *win;
479   char *buffer;
480
481   assert(screen != NULL);
482   buffer = screen->input_buffer;
483   win = screen->input_win;
484
485   /* Return directly if input window is full */
486   if (screen->input_pos >= SILC_SCREEN_INPUT_WIN_SIZE)
487     return;
488
489   /* The input window is COLS wide but one can type into it at most
490      SILC_SCREEN_INPUT_SIZE characters. When COLS - 5 characters is
491      typed the window is cleared and the cursor is moved at the tenth
492      character in the input window. Ten last typed character is then
493      showed at the start of the window. */
494   if (screen->cursor_pos >= (COLS - 5)) {
495     int i;
496
497     /* Clear line */
498     for (i = 0; i < COLS; i++)
499       mvwprintw(win, 0, i, " ");
500     mvwprintw(win, 0, 0, "");
501
502     /* Show ten last typed characters from the buffer on the screen */
503     waddnstr(win, &buffer[screen->input_pos - 10], 10);
504     screen->cursor_pos = 10;
505     wrefresh(win);
506
507     screen->virtual_window++;
508   }
509
510   if (screen->input_pos < screen->input_end) {
511     /* User moved cursor into the typed line. We are not adding 
512        character at the end of the line anymore */
513
514     if (screen->insert == FALSE) {
515       /* Add new character somewhere inside typed line. The input
516          line position is not advanced since a character was replaced
517          by the new character. */
518       waddch(win, c);
519       buffer[screen->input_pos] = c;
520       screen->cursor_pos++;
521       screen->input_pos++;
522       screen->input_end = screen->input_pos;
523     } else {
524       /* Insert new character somewhere inside typed line. Other
525          characters are moved forward. We must advance the input line
526          posititon. */
527       winsch(win, c);
528       wmove(win, 0, screen->cursor_pos + 1);
529       SILC_SCREEN_INPUT_INSERT(buffer, screen->input_pos, 
530                                c, screen->input_end);
531       screen->cursor_pos++;
532       screen->input_pos++;
533       screen->input_end++;
534     }
535   } else {
536     /* Add new character at the end of input line */
537     waddch(win, c);
538     buffer[screen->input_pos] = c;
539     screen->input_pos++;
540     screen->cursor_pos++;
541     screen->input_end = screen->input_pos;
542   }
543
544   /* Advance the cursor position. Cursor moves one to rightward always */
545   wrefresh(win);
546 }
547
548 /* Prints prompt to the input window. Cursors position aftern printing
549    is length of the prompt. */
550
551 void silc_screen_input_print_prompt(SilcScreen screen, char *prompt)
552 {
553   WINDOW *win;
554
555   assert(screen != NULL);
556   win = screen->input_win;
557
558   wclear(win);
559   waddnstr(win, prompt, strlen(prompt));
560   wrefresh(win);
561
562   screen->input_pos = strlen(prompt);
563   screen->cursor_pos = strlen(prompt);
564   screen->virtual_window = 0;
565 }