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