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