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