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