Mercurial > hg
annotate mcabber/src/screen.c @ 312:f0b7ff2df7e8
Ctrl-C does not terminate mcabber
1st Ctrl-C abort current completion
2 Ctrl-C in less than 2 seconds leave mcabber
author | Mikael Berthe <mikael@lilotux.net> |
---|---|
date | Thu, 14 Jul 2005 23:17:21 +0100 |
parents | 566818afee1c |
children | 1ceb68eb2fc1 |
rev | line source |
---|---|
307 | 1 /* |
2 * screen.c -- UI stuff | |
3 * | |
4 * Copyright (C) 2005 Mikael Berthe <bmikael@lists.lilotux.net> | |
5 * Parts of this file come from the Cabber project <cabber@ajmacias.com> | |
6 * | |
7 * This program is free software; you can redistribute it and/or modify | |
8 * it under the terms of the GNU General Public License as published by | |
9 * the Free Software Foundation; either version 2 of the License, or (at | |
10 * your option) any later version. | |
11 * | |
12 * This program is distributed in the hope that it will be useful, but | |
13 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU General Public License | |
18 * along with this program; if not, write to the Free Software | |
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |
20 * USA | |
21 */ | |
22 | |
24 | 23 #include <stdio.h> |
24 #include <stdlib.h> | |
25 #include <string.h> | |
26 #include <ncurses.h> | |
27 #include <panel.h> | |
28 #include <time.h> | |
29 #include <ctype.h> | |
30 #include <locale.h> | |
232 | 31 #include <langinfo.h> |
24 | 32 |
33 #include "screen.h" | |
81 | 34 #include "hbuf.h" |
47 | 35 #include "commands.h" |
95 | 36 #include "compl.h" |
81 | 37 #include "roster.h" |
180 | 38 #include "histolog.h" |
279
f5dd437c057b
Rewrite the settings system
Mikael Berthe <mikael@lilotux.net>
parents:
276
diff
changeset
|
39 #include "settings.h" |
81 | 40 #include "utils.h" |
24 | 41 #include "list.h" |
42 | |
43 #define window_entry(n) list_entry(n, window_entry_t, list) | |
44 | |
151 | 45 inline void check_offset(int); |
46 | |
24 | 47 LIST_HEAD(window_list); |
48 | |
49 typedef struct _window_entry_t { | |
50 WINDOW *win; | |
108 | 51 PANEL *panel; |
52 char *name; | |
53 GList *hbuf; | |
181 | 54 GList *top; // If top is NULL, we'll display the last lines |
55 char cleared; // For ex, user has issued a /clear command... | |
24 | 56 struct list_head list; |
57 } window_entry_t; | |
58 | |
59 | |
60 static WINDOW *rosterWnd, *chatWnd, *inputWnd; | |
61 static WINDOW *logWnd, *logWnd_border; | |
62 static PANEL *rosterPanel, *chatPanel, *inputPanel; | |
63 static PANEL *logPanel, *logPanel_border; | |
64 static int maxY, maxX; | |
65 static window_entry_t *currentWindow; | |
66 | |
67 static int chatmode; | |
238 | 68 static int multimode; |
69 static char *multiline; | |
30 | 70 int update_roster; |
232 | 71 int utf8_mode = 0; |
24 | 72 |
174 | 73 static char inputLine[INPUTLINE_LENGTH+1]; |
74 static char *ptr_inputline; | |
75 static short int inputline_offset; | |
76 static int completion_started; | |
173 | 77 static GList *cmdhisto; |
78 static GList *cmdhisto_cur; | |
174 | 79 static char cmdhisto_backup[INPUTLINE_LENGTH+1]; |
24 | 80 |
81 | |
99 | 82 /* Functions */ |
24 | 83 |
74 | 84 int scr_WindowWidth(WINDOW * win) |
24 | 85 { |
86 int x, y; | |
87 getmaxyx(win, y, x); | |
88 return x; | |
89 } | |
90 | |
74 | 91 void scr_clear_box(WINDOW *win, int y, int x, int height, int width, int Color) |
92 { | |
93 int i, j; | |
94 | |
95 wattrset(win, COLOR_PAIR(Color)); | |
96 for (i = 0; i < height; i++) { | |
97 wmove(win, y + i, x); | |
98 for (j = 0; j < width; j++) | |
99 wprintw(win, " "); | |
100 } | |
101 } | |
102 | |
24 | 103 void scr_draw_box(WINDOW * win, int y, int x, int height, int width, |
104 int Color, chtype box, chtype border) | |
105 { | |
106 int i, j; | |
107 | |
108 wattrset(win, COLOR_PAIR(Color)); | |
109 for (i = 0; i < height; i++) { | |
110 wmove(win, y + i, x); | |
111 for (j = 0; j < width; j++) | |
112 if (!i && !j) | |
113 waddch(win, border | ACS_ULCORNER); | |
114 else if (i == height - 1 && !j) | |
115 waddch(win, border | ACS_LLCORNER); | |
116 else if (!i && j == width - 1) | |
117 waddch(win, box | ACS_URCORNER); | |
118 else if (i == height - 1 && j == width - 1) | |
119 waddch(win, box | ACS_LRCORNER); | |
120 else if (!i) | |
121 waddch(win, border | ACS_HLINE); | |
122 else if (i == height - 1) | |
123 waddch(win, box | ACS_HLINE); | |
124 else if (!j) | |
125 waddch(win, border | ACS_VLINE); | |
126 else if (j == width - 1) | |
127 waddch(win, box | ACS_VLINE); | |
128 else | |
129 waddch(win, box | ' '); | |
130 } | |
131 } | |
132 | |
281
f562b9af2de7
Add "const" specifier in prototypes
Mikael Berthe <mikael@lilotux.net>
parents:
279
diff
changeset
|
133 int FindColor(const char *name) |
24 | 134 { |
135 if (!strcmp(name, "default")) | |
136 return -1; | |
137 if (!strcmp(name, "black")) | |
138 return COLOR_BLACK; | |
139 if (!strcmp(name, "red")) | |
140 return COLOR_RED; | |
141 if (!strcmp(name, "green")) | |
142 return COLOR_GREEN; | |
143 if (!strcmp(name, "yellow")) | |
144 return COLOR_YELLOW; | |
145 if (!strcmp(name, "blue")) | |
146 return COLOR_BLUE; | |
147 if (!strcmp(name, "magenta")) | |
148 return COLOR_MAGENTA; | |
149 if (!strcmp(name, "cyan")) | |
150 return COLOR_CYAN; | |
151 if (!strcmp(name, "white")) | |
152 return COLOR_WHITE; | |
153 | |
154 return -1; | |
155 } | |
156 | |
157 void ParseColors(void) | |
158 { | |
281
f562b9af2de7
Add "const" specifier in prototypes
Mikael Berthe <mikael@lilotux.net>
parents:
279
diff
changeset
|
159 const char *colors[8] = { |
24 | 160 "", "", |
267 | 161 "general", |
139 | 162 "newmsg", |
267 | 163 "rosterselect", |
164 "rosternormal", | |
24 | 165 NULL |
166 }; | |
167 | |
168 char *tmp = malloc(1024); | |
281
f562b9af2de7
Add "const" specifier in prototypes
Mikael Berthe <mikael@lilotux.net>
parents:
279
diff
changeset
|
169 const char *color; |
f562b9af2de7
Add "const" specifier in prototypes
Mikael Berthe <mikael@lilotux.net>
parents:
279
diff
changeset
|
170 const char *background = settings_opt_get("color_background"); |
f562b9af2de7
Add "const" specifier in prototypes
Mikael Berthe <mikael@lilotux.net>
parents:
279
diff
changeset
|
171 const char *backselected = settings_opt_get("color_backselected"); |
24 | 172 int i = 0; |
173 | |
267 | 174 // Default values |
175 if (!background) background = "blue"; | |
176 if (!backselected) backselected = "cyan"; | |
177 | |
24 | 178 while (colors[i]) { |
179 sprintf(tmp, "color_%s", colors[i]); | |
279
f5dd437c057b
Rewrite the settings system
Mikael Berthe <mikael@lilotux.net>
parents:
276
diff
changeset
|
180 color = settings_opt_get(tmp); |
24 | 181 |
182 switch (i + 1) { | |
183 case 1: | |
184 init_pair(1, COLOR_BLACK, COLOR_WHITE); | |
185 break; | |
186 case 2: | |
187 init_pair(2, COLOR_WHITE, COLOR_BLACK); | |
188 break; | |
189 case 3: | |
267 | 190 init_pair(3, ((color) ? FindColor(color) : COLOR_WHITE), |
191 FindColor(background)); | |
24 | 192 break; |
193 case 4: | |
267 | 194 init_pair(4, ((color) ? FindColor(color) : COLOR_RED), |
195 FindColor(background)); | |
24 | 196 break; |
197 case 5: | |
267 | 198 init_pair(5, ((color) ? FindColor(color) : COLOR_BLACK), |
199 FindColor(backselected)); | |
24 | 200 break; |
201 case 6: | |
267 | 202 init_pair(6, ((color) ? FindColor(color) : COLOR_MAGENTA), |
203 FindColor(background)); | |
24 | 204 break; |
205 } | |
206 i++; | |
207 } | |
208 } | |
209 | |
210 | |
151 | 211 window_entry_t *scr_CreateBuddyPanel(const char *title, int dont_show) |
24 | 212 { |
151 | 213 int x; |
214 int y; | |
215 int lines; | |
216 int cols; | |
153 | 217 window_entry_t *tmp; |
218 | |
219 do { | |
220 tmp = calloc(1, sizeof(window_entry_t)); | |
221 } while (!tmp); | |
24 | 222 |
151 | 223 // Dimensions |
224 x = ROSTER_WIDTH; | |
225 y = 0; | |
226 lines = CHAT_WIN_HEIGHT; | |
227 cols = maxX - ROSTER_WIDTH; | |
228 | |
24 | 229 tmp->win = newwin(lines, cols, y, x); |
154 | 230 while (!tmp->win) { |
231 usleep(250); | |
232 tmp->win = newwin(lines, cols, y, x); | |
233 } | |
168 | 234 wbkgd(tmp->win, COLOR_PAIR(COLOR_GENERAL)); |
24 | 235 tmp->panel = new_panel(tmp->win); |
153 | 236 tmp->name = (char *) calloc(1, 96); |
237 strncpy(tmp->name, title, 96); | |
24 | 238 |
143 | 239 if (!dont_show) { |
24 | 240 currentWindow = tmp; |
241 } else { | |
242 if (currentWindow) | |
243 top_panel(currentWindow->panel); | |
244 else | |
245 top_panel(chatPanel); | |
246 } | |
143 | 247 update_panels(); |
24 | 248 |
181 | 249 // Load buddy history from file (if enabled) |
185 | 250 hlog_read_history(title, &tmp->hbuf, maxX - ROSTER_WIDTH - PREFIX_WIDTH); |
181 | 251 |
24 | 252 list_add_tail(&tmp->list, &window_list); |
253 | |
254 return tmp; | |
255 } | |
256 | |
50 | 257 window_entry_t *scr_SearchWindow(const char *winId) |
24 | 258 { |
259 struct list_head *pos, *n; | |
260 window_entry_t *search_entry = NULL; | |
261 | |
262 list_for_each_safe(pos, n, &window_list) { | |
263 search_entry = window_entry(pos); | |
264 if (search_entry->name) { | |
265 if (!strcasecmp(search_entry->name, winId)) { | |
266 return search_entry; | |
267 } | |
268 } | |
269 } | |
270 return NULL; | |
271 } | |
272 | |
143 | 273 // scr_UpdateWindow() |
274 // (Re-)Display the given chat window. | |
74 | 275 void scr_UpdateWindow(window_entry_t *win_entry) |
276 { | |
277 int n; | |
278 int width; | |
184 | 279 hbb_line **lines, *line; |
74 | 280 GList *hbuf_head; |
184 | 281 char date[32]; |
74 | 282 |
108 | 283 width = scr_WindowWidth(win_entry->win); |
284 | |
285 // Should the window be empty? | |
286 if (win_entry->cleared) { | |
168 | 287 werase(win_entry->win); |
108 | 288 return; |
289 } | |
290 | |
105 | 291 // win_entry->top is the top message of the screen. If it set to NULL, we |
292 // are displaying the last messages. | |
293 | |
74 | 294 // We will show the last CHAT_WIN_HEIGHT lines. |
295 // Let's find out where it begins. | |
105 | 296 if (!win_entry->top || |
297 (g_list_position(g_list_first(win_entry->hbuf), win_entry->top) == -1)) { | |
298 // Move up CHAT_WIN_HEIGHT lines | |
299 win_entry->hbuf = g_list_last(win_entry->hbuf); | |
300 hbuf_head = win_entry->hbuf; | |
301 win_entry->top = NULL; // (Just to make sure) | |
302 n = 0; | |
303 while (hbuf_head && (n < CHAT_WIN_HEIGHT-1) && g_list_previous(hbuf_head)) { | |
304 hbuf_head = g_list_previous(hbuf_head); | |
305 n++; | |
306 } | |
307 } else | |
308 hbuf_head = win_entry->top; | |
74 | 309 |
310 // Get the last CHAT_WIN_HEIGHT lines. | |
311 lines = hbuf_get_lines(hbuf_head, CHAT_WIN_HEIGHT); | |
312 | |
313 // Display these lines | |
314 for (n = 0; n < CHAT_WIN_HEIGHT; n++) { | |
168 | 315 wmove(win_entry->win, n, 0); |
184 | 316 line = *(lines+n); |
185 | 317 // NOTE: update PREFIX_WIDTH if you change the date format!! |
318 // You need to set it to the whole prefix length + 1 | |
184 | 319 if (line) { |
320 if (line->timestamp) { | |
185 | 321 strftime(date, 35, "%m-%d %H:%M", localtime(&line->timestamp)); |
184 | 322 } else |
185 | 323 strcpy(date, " "); |
197 | 324 if (line->flags & HBB_PREFIX_INFO) { |
325 char dir = '*'; | |
326 if (line->flags & HBB_PREFIX_IN) | |
327 dir = '<'; | |
328 else if (line->flags & HBB_PREFIX_OUT) | |
329 dir = '>'; | |
330 wprintw(win_entry->win, "%.11s *%c* ", date, dir); | |
331 } else if (line->flags & HBB_PREFIX_IN) | |
185 | 332 wprintw(win_entry->win, "%.11s <== ", date); |
184 | 333 else if (line->flags & HBB_PREFIX_OUT) |
185 | 334 wprintw(win_entry->win, "%.11s --> ", date); |
75 | 335 else { |
185 | 336 wprintw(win_entry->win, "%.11s ", date); |
75 | 337 } |
184 | 338 wprintw(win_entry->win, "%s", line->text); // line |
168 | 339 wclrtoeol(win_entry->win); |
184 | 340 g_free(line->text); |
168 | 341 } else { |
342 wclrtobot(win_entry->win); | |
343 break; | |
75 | 344 } |
74 | 345 } |
346 g_free(lines); | |
347 } | |
348 | |
143 | 349 // scr_ShowWindow() |
350 // Display the chat window with the given identifier. | |
50 | 351 void scr_ShowWindow(const char *winId) |
24 | 352 { |
74 | 353 window_entry_t *win_entry = scr_SearchWindow(winId); |
354 | |
181 | 355 if (!win_entry) |
180 | 356 win_entry = scr_CreateBuddyPanel(winId, FALSE); |
74 | 357 |
180 | 358 top_panel(win_entry->panel); |
359 currentWindow = win_entry; | |
360 chatmode = TRUE; | |
361 roster_msg_setflag(winId, FALSE); | |
362 roster_setflags(winId, ROSTER_FLAG_LOCK, TRUE); | |
363 update_roster = TRUE; | |
74 | 364 |
180 | 365 // Refresh the window |
366 scr_UpdateWindow(win_entry); | |
367 | |
368 // Finished :) | |
369 update_panels(); | |
142 | 370 |
371 top_panel(inputPanel); | |
24 | 372 } |
373 | |
143 | 374 // scr_ShowBuddyWindow() |
375 // Display the chat window buffer for the current buddy. | |
24 | 376 void scr_ShowBuddyWindow(void) |
377 { | |
105 | 378 const gchar *jid; |
140 | 379 |
105 | 380 if (!current_buddy) |
140 | 381 jid = NULL; |
382 else | |
383 jid = CURRENT_JID; | |
384 | |
385 if (!jid) { | |
386 top_panel(chatPanel); | |
143 | 387 top_panel(inputPanel); |
140 | 388 currentWindow = NULL; |
105 | 389 return; |
140 | 390 } |
391 | |
105 | 392 scr_ShowWindow(jid); |
24 | 393 } |
394 | |
395 | |
143 | 396 // scr_WriteInWindow() |
397 // Write some text in the winId window (this usually is a jid). | |
398 // Lines are splitted when they are too long to fit in the chat window. | |
399 // If this window doesn't exist, it is created. | |
184 | 400 void scr_WriteInWindow(const char *winId, const char *text, time_t timestamp, |
401 unsigned int prefix_flags, int force_show) | |
24 | 402 { |
74 | 403 window_entry_t *win_entry; |
24 | 404 int dont_show = FALSE; |
405 | |
74 | 406 // Look for the window entry. |
407 win_entry = scr_SearchWindow(winId); | |
408 | |
409 // Do we have to really show the window? | |
24 | 410 if (!chatmode) |
411 dont_show = TRUE; | |
74 | 412 else if ((!force_show) && ((!currentWindow || (currentWindow != win_entry)))) |
24 | 413 dont_show = TRUE; |
414 | |
74 | 415 // If the window entry doesn't exist yet, let's create it. |
416 if (win_entry == NULL) { | |
151 | 417 win_entry = scr_CreateBuddyPanel(winId, dont_show); |
24 | 418 } |
419 | |
220 | 420 // The message must be displayed -> update top pointer |
421 if (win_entry->cleared) | |
422 win_entry->top = g_list_last(win_entry->hbuf); | |
423 | |
184 | 424 hbuf_add_line(&win_entry->hbuf, text, timestamp, prefix_flags, |
185 | 425 maxX - ROSTER_WIDTH - PREFIX_WIDTH); |
74 | 426 |
108 | 427 if (win_entry->cleared) { |
220 | 428 win_entry->cleared = FALSE; |
429 if (g_list_next(win_entry->top)) | |
430 win_entry->top = g_list_next(win_entry->top); | |
431 } | |
432 | |
433 // Make sure the last line appears in the window; update top if necessary | |
434 if (win_entry->top) { | |
435 int dist; | |
436 GList *first = g_list_first(win_entry->hbuf); | |
437 dist = g_list_position(first, g_list_last(win_entry->hbuf)) - | |
438 g_list_position(first, win_entry->top); | |
439 if (dist >= CHAT_WIN_HEIGHT) | |
440 win_entry->top = NULL; | |
108 | 441 } |
442 | |
24 | 443 if (!dont_show) { |
74 | 444 // Show and refresh the window |
445 top_panel(win_entry->panel); | |
446 scr_UpdateWindow(win_entry); | |
142 | 447 top_panel(inputPanel); |
24 | 448 update_panels(); |
449 doupdate(); | |
450 } else { | |
148 | 451 roster_msg_setflag(winId, TRUE); |
30 | 452 update_roster = TRUE; |
24 | 453 } |
454 } | |
455 | |
456 void scr_InitCurses(void) | |
457 { | |
458 initscr(); | |
459 noecho(); | |
460 raw(); | |
35 | 461 halfdelay(5); |
24 | 462 start_color(); |
463 use_default_colors(); | |
464 | |
465 ParseColors(); | |
466 | |
467 getmaxyx(stdscr, maxY, maxX); | |
167 | 468 if (maxY < LOG_WIN_HEIGHT+2) |
469 maxY = LOG_WIN_HEIGHT+2; | |
24 | 470 inputLine[0] = 0; |
471 ptr_inputline = inputLine; | |
472 | |
35 | 473 setlocale(LC_CTYPE, ""); |
232 | 474 utf8_mode = (strcmp(nl_langinfo(CODESET), "UTF-8") == 0); |
24 | 475 |
476 return; | |
477 } | |
478 | |
81 | 479 void scr_TerminateCurses(void) |
480 { | |
481 clear(); | |
482 refresh(); | |
483 endwin(); | |
484 return; | |
485 } | |
486 | |
151 | 487 // scr_DrawMainWindow() |
157 | 488 // Set fullinit to TRUE to also create panels. Set it to FALSE for a resize. |
151 | 489 // |
490 // I think it could be improved a _lot_ but I'm really not an ncurses | |
491 // expert... :-\ Mikael. | |
492 // | |
493 void scr_DrawMainWindow(unsigned int fullinit) | |
24 | 494 { |
157 | 495 if (fullinit) { |
496 /* Create windows */ | |
497 rosterWnd = newwin(CHAT_WIN_HEIGHT, ROSTER_WIDTH, 0, 0); | |
498 chatWnd = newwin(CHAT_WIN_HEIGHT, maxX - ROSTER_WIDTH, 0, ROSTER_WIDTH); | |
499 logWnd_border = newwin(LOG_WIN_HEIGHT, maxX, CHAT_WIN_HEIGHT, 0); | |
500 logWnd = newwin(LOG_WIN_HEIGHT-2, maxX-2, CHAT_WIN_HEIGHT+1, 1); | |
501 inputWnd = newwin(1, maxX, maxY-1, 0); | |
168 | 502 wbkgd(rosterWnd, COLOR_PAIR(COLOR_GENERAL)); |
503 wbkgd(chatWnd, COLOR_PAIR(COLOR_GENERAL)); | |
504 wbkgd(logWnd_border, COLOR_PAIR(COLOR_GENERAL)); | |
505 wbkgd(logWnd, COLOR_PAIR(COLOR_GENERAL)); | |
157 | 506 } else { |
507 /* Resize windows */ | |
508 wresize(rosterWnd, CHAT_WIN_HEIGHT, ROSTER_WIDTH); | |
509 wresize(chatWnd, CHAT_WIN_HEIGHT, maxX - ROSTER_WIDTH); | |
510 | |
511 wresize(logWnd_border, LOG_WIN_HEIGHT, maxX); | |
512 wresize(logWnd, LOG_WIN_HEIGHT-2, maxX-2); | |
513 mvwin(logWnd_border, CHAT_WIN_HEIGHT, 0); | |
514 mvwin(logWnd, CHAT_WIN_HEIGHT+1, 1); | |
515 | |
516 wresize(inputWnd, 1, maxX); | |
517 mvwin(inputWnd, maxY-1, 0); | |
168 | 518 |
519 werase(chatWnd); | |
157 | 520 } |
151 | 521 |
522 /* Draw/init windows */ | |
523 | |
74 | 524 mvwprintw(chatWnd, 0, 0, "This is the status window"); |
24 | 525 |
151 | 526 // - Draw/clear the log window |
24 | 527 scr_draw_box(logWnd_border, 0, 0, LOG_WIN_HEIGHT, maxX, COLOR_GENERAL, 0, 0); |
157 | 528 // Auto-scrolling in log window |
74 | 529 scrollok(logWnd, TRUE); |
24 | 530 |
531 | |
151 | 532 if (fullinit) { |
157 | 533 // Enable keypad (+ special keys) |
534 keypad(inputWnd, TRUE); | |
535 | |
151 | 536 // Create panels |
537 rosterPanel = new_panel(rosterWnd); | |
538 chatPanel = new_panel(chatWnd); | |
539 logPanel_border = new_panel(logWnd_border); | |
540 logPanel = new_panel(logWnd); | |
541 inputPanel = new_panel(inputWnd); | |
232 | 542 |
543 if (utf8_mode) | |
544 scr_LogPrint("WARNING: UTF-8 not yet supported!"); | |
157 | 545 } else { |
546 // Update panels | |
547 replace_panel(rosterPanel, rosterWnd); | |
548 replace_panel(chatPanel, chatWnd); | |
549 replace_panel(logPanel, logWnd); | |
550 replace_panel(logPanel_border, logWnd_border); | |
551 replace_panel(inputPanel, inputWnd); | |
151 | 552 } |
553 | |
554 // We'll need to redraw the roster | |
149 | 555 update_roster = TRUE; |
24 | 556 return; |
557 } | |
558 | |
151 | 559 // scr_Resize() |
560 // Function called when the window is resized. | |
157 | 561 // - Resize windows |
151 | 562 // - Rewrap lines in each buddy buffer |
563 void scr_Resize() | |
564 { | |
565 struct list_head *pos, *n; | |
566 window_entry_t *search_entry; | |
567 int x, y, lines, cols; | |
568 | |
569 // First, update the global variables | |
570 getmaxyx(stdscr, maxY, maxX); | |
167 | 571 if (maxY < LOG_WIN_HEIGHT+2) |
572 maxY = LOG_WIN_HEIGHT+2; | |
151 | 573 // Make sure the cursor stays inside the window |
574 check_offset(0); | |
575 | |
157 | 576 // Resize windows and update panels |
151 | 577 scr_DrawMainWindow(FALSE); |
578 | |
579 // Resize all buddy windows | |
580 x = ROSTER_WIDTH; | |
581 y = 0; | |
582 lines = CHAT_WIN_HEIGHT; | |
583 cols = maxX - ROSTER_WIDTH; | |
584 | |
585 list_for_each_safe(pos, n, &window_list) { | |
586 search_entry = window_entry(pos); | |
587 if (search_entry->win) { | |
189 | 588 GList *rescue_top; |
157 | 589 // Resize buddy window (no need to move it) |
590 wresize(search_entry->win, lines, cols); | |
168 | 591 werase(search_entry->win); |
151 | 592 // If a panel exists, replace the old window with the new |
593 if (search_entry->panel) { | |
594 replace_panel(search_entry->panel, search_entry->win); | |
595 } | |
596 // Redo line wrapping | |
189 | 597 rescue_top = hbuf_previous_persistent(search_entry->top); |
151 | 598 hbuf_rebuild(&search_entry->hbuf, |
185 | 599 maxX - ROSTER_WIDTH - PREFIX_WIDTH); |
189 | 600 if (g_list_position(g_list_first(search_entry->hbuf), search_entry->top) == -1) |
601 search_entry->top = rescue_top; | |
151 | 602 } |
603 } | |
604 | |
605 // Refresh current buddy window | |
157 | 606 if (chatmode) |
151 | 607 scr_ShowBuddyWindow(); |
608 } | |
609 | |
143 | 610 // scr_DrawRoster() |
611 // Actually, display the buddylist on the screen. | |
81 | 612 void scr_DrawRoster(void) |
24 | 613 { |
81 | 614 static guint offset = 0; |
615 char name[ROSTER_WIDTH]; | |
616 int maxx, maxy; | |
617 GList *buddy; | |
618 int i, n; | |
619 int rOffset; | |
164 | 620 enum imstatus currentstatus = jb_getstatus(); |
81 | 621 |
123 | 622 // We can reset update_roster |
623 update_roster = FALSE; | |
624 | |
81 | 625 getmaxyx(rosterWnd, maxy, maxx); |
626 maxx --; // last char is for vertical border | |
627 name[ROSTER_WIDTH-7] = 0; | |
628 | |
629 // cleanup of roster window | |
168 | 630 werase(rosterWnd); |
631 // Redraw the vertical line (not very good...) | |
81 | 632 wattrset(rosterWnd, COLOR_PAIR(COLOR_GENERAL)); |
168 | 633 for (i=0 ; i < CHAT_WIN_HEIGHT ; i++) |
634 mvwaddch(rosterWnd, i, ROSTER_WIDTH-1, ACS_VLINE); | |
81 | 635 |
636 // Leave now if buddylist is empty | |
637 if (!buddylist) { | |
638 offset = 0; | |
123 | 639 update_panels(); |
640 doupdate(); | |
81 | 641 return; |
642 } | |
643 | |
84 | 644 // Update offset if necessary |
645 i = g_list_position(buddylist, current_buddy); | |
646 if (i == -1) { // This is bad | |
647 scr_LogPrint("Doh! Can't find current selected buddy!!"); | |
648 return; | |
649 } else if (i < offset) { | |
650 offset = i; | |
651 } else if (i+1 > offset + maxy) { | |
652 offset = i + 1 - maxy; | |
653 } | |
81 | 654 |
655 buddy = buddylist; | |
656 rOffset = offset; | |
657 | |
84 | 658 for (i=0; i<maxy && buddy; buddy = g_list_next(buddy)) { |
81 | 659 |
660 char status = '?'; | |
661 char pending = ' '; | |
662 enum imstatus budstate; | |
149 | 663 unsigned short ismsg = buddy_getflags(BUDDATA(buddy)) & ROSTER_FLAG_MSG; |
664 unsigned short isgrp = buddy_gettype(BUDDATA(buddy)) & ROSTER_TYPE_GROUP; | |
665 unsigned short ishid = buddy_getflags(BUDDATA(buddy)) & ROSTER_FLAG_HIDE; | |
81 | 666 |
667 if (rOffset > 0) { | |
668 rOffset--; | |
669 continue; | |
670 } | |
671 | |
149 | 672 // Display message notice if there is a message flag, but not |
673 // for unfolded groups. | |
674 if (ismsg && (!isgrp || ishid)) { | |
81 | 675 pending = '#'; |
676 } | |
677 | |
678 budstate = buddy_getstatus(BUDDATA(buddy)); | |
164 | 679 if (budstate >= 0 && budstate < imstatus_size && currentstatus != offline) |
81 | 680 status = imstatus2char[budstate]; |
681 if (buddy == current_buddy) { | |
682 wattrset(rosterWnd, COLOR_PAIR(COLOR_BD_DESSEL)); | |
683 // The 3 following lines aim to color the whole line | |
684 wmove(rosterWnd, i, 0); | |
685 for (n = 0; n < maxx; n++) | |
686 waddch(rosterWnd, ' '); | |
687 } else { | |
149 | 688 if (pending == '#') |
139 | 689 wattrset(rosterWnd, COLOR_PAIR(COLOR_NMSG)); |
690 else | |
691 wattrset(rosterWnd, COLOR_PAIR(COLOR_BD_DES)); | |
81 | 692 } |
693 | |
694 strncpy(name, buddy_getname(BUDDATA(buddy)), ROSTER_WIDTH-7); | |
149 | 695 if (isgrp) { |
133 | 696 char *sep; |
149 | 697 if (ishid) |
133 | 698 sep = "+++"; |
699 else | |
700 sep = "---"; | |
701 mvwprintw(rosterWnd, i, 0, " %c%s %s", pending, sep, name); | |
702 } | |
126 | 703 else |
704 mvwprintw(rosterWnd, i, 0, " %c[%c] %s", pending, status, name); | |
84 | 705 |
706 i++; | |
81 | 707 } |
708 | |
142 | 709 top_panel(inputPanel); |
81 | 710 update_panels(); |
711 doupdate(); | |
24 | 712 } |
713 | |
184 | 714 void scr_WriteMessage(const char *jid, const char *text, time_t timestamp, |
715 guint prefix_flags) | |
24 | 716 { |
184 | 717 if (!timestamp) timestamp = time(NULL); |
718 | |
719 scr_WriteInWindow(jid, text, timestamp, prefix_flags, FALSE); | |
47 | 720 } |
721 | |
190 | 722 // If prefix is NULL, HBB_PREFIX_IN is supposed. |
184 | 723 void scr_WriteIncomingMessage(const char *jidfrom, const char *text, |
190 | 724 time_t timestamp, guint prefix) |
47 | 725 { |
190 | 726 if (!prefix) prefix = HBB_PREFIX_IN; |
75 | 727 // FIXME expand tabs / filter out special chars... |
190 | 728 scr_WriteMessage(jidfrom, text, timestamp, prefix); |
24 | 729 update_panels(); |
730 doupdate(); | |
731 } | |
732 | |
50 | 733 void scr_WriteOutgoingMessage(const char *jidto, const char *text) |
47 | 734 { |
184 | 735 scr_WriteMessage(jidto, text, 0, HBB_PREFIX_OUT); |
47 | 736 scr_ShowWindow(jidto); |
737 } | |
738 | |
24 | 739 int scr_Getch(void) |
740 { | |
741 int ch; | |
742 ch = wgetch(inputWnd); | |
743 return ch; | |
744 } | |
745 | |
746 WINDOW *scr_GetRosterWindow(void) | |
747 { | |
748 return rosterWnd; | |
749 } | |
750 | |
751 WINDOW *scr_GetStatusWindow(void) | |
752 { | |
753 return chatWnd; | |
754 } | |
755 | |
756 WINDOW *scr_GetInputWindow(void) | |
757 { | |
758 return inputWnd; | |
759 } | |
760 | |
143 | 761 // scr_RosterTop() |
762 // Go to the first buddy in the buddylist | |
105 | 763 void scr_RosterTop(void) |
104 | 764 { |
143 | 765 enum imstatus prev_st = imstatus_size; // undef |
119 | 766 |
767 if (current_buddy) { | |
768 prev_st = buddy_getstatus(BUDDATA(current_buddy)); | |
769 if (chatmode) | |
770 buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE); | |
771 } | |
104 | 772 current_buddy = buddylist; |
119 | 773 if (chatmode && current_buddy) |
774 buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, TRUE); | |
775 | |
776 // We should rebuild the buddylist but not everytime | |
143 | 777 // Here we check if we were locking a buddy who is actually offline, |
778 // and hide_offline_buddies is TRUE. In which case we need to rebuild. | |
120 | 779 if (current_buddy && prev_st == offline && |
780 buddylist_get_hide_offline_buddies()) | |
119 | 781 buddylist_build(); |
104 | 782 if (chatmode) |
783 scr_ShowBuddyWindow(); | |
143 | 784 update_roster = TRUE; |
104 | 785 } |
786 | |
143 | 787 // scr_RosterBottom() |
788 // Go to the last buddy in the buddylist | |
105 | 789 void scr_RosterBottom(void) |
104 | 790 { |
143 | 791 enum imstatus prev_st = imstatus_size; // undef |
119 | 792 |
793 if (current_buddy) { | |
794 prev_st = buddy_getstatus(BUDDATA(current_buddy)); | |
795 if (chatmode) | |
796 buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE); | |
797 } | |
104 | 798 current_buddy = g_list_last(buddylist); |
143 | 799 // Lock the buddy in the buddylist if we're in chat mode |
119 | 800 if (chatmode && current_buddy) |
801 buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, TRUE); | |
802 | |
803 // We should rebuild the buddylist but not everytime | |
143 | 804 // Here we check if we were locking a buddy who is actually offline, |
805 // and hide_offline_buddies is TRUE. In which case we need to rebuild. | |
120 | 806 if (current_buddy && prev_st == offline && |
807 buddylist_get_hide_offline_buddies()) | |
119 | 808 buddylist_build(); |
143 | 809 |
104 | 810 if (chatmode) |
811 scr_ShowBuddyWindow(); | |
143 | 812 update_roster = TRUE; |
104 | 813 } |
814 | |
143 | 815 // scr_RosterUp() |
816 // Go to the previous buddy in the buddylist | |
105 | 817 void scr_RosterUp(void) |
81 | 818 { |
143 | 819 enum imstatus prev_st = imstatus_size; // undef |
119 | 820 |
81 | 821 if (current_buddy) { |
822 if (g_list_previous(current_buddy)) { | |
120 | 823 prev_st = buddy_getstatus(BUDDATA(current_buddy)); |
119 | 824 buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE); |
81 | 825 current_buddy = g_list_previous(current_buddy); |
143 | 826 // Lock the buddy in the buddylist if we're in chat mode |
119 | 827 if (chatmode) |
828 buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, TRUE); | |
829 // We should rebuild the buddylist but not everytime | |
143 | 830 // Here we check if we were locking a buddy who is actually offline, |
831 // and hide_offline_buddies is TRUE. In which case we need to rebuild. | |
120 | 832 if (prev_st == offline && buddylist_get_hide_offline_buddies()) |
119 | 833 buddylist_build(); |
143 | 834 update_roster = TRUE; |
81 | 835 } |
836 } | |
104 | 837 |
838 if (chatmode) | |
839 scr_ShowBuddyWindow(); | |
81 | 840 } |
841 | |
143 | 842 // scr_RosterDown() |
843 // Go to the next buddy in the buddylist | |
105 | 844 void scr_RosterDown(void) |
81 | 845 { |
143 | 846 enum imstatus prev_st = imstatus_size; // undef |
119 | 847 |
81 | 848 if (current_buddy) { |
849 if (g_list_next(current_buddy)) { | |
120 | 850 prev_st = buddy_getstatus(BUDDATA(current_buddy)); |
119 | 851 buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE); |
81 | 852 current_buddy = g_list_next(current_buddy); |
119 | 853 if (chatmode) |
854 buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, TRUE); | |
855 // We should rebuild the buddylist but not everytime | |
143 | 856 // Here we check if we were locking a buddy who is actually offline, |
857 // and hide_offline_buddies is TRUE. In which case we need to rebuild. | |
120 | 858 if (prev_st == offline && buddylist_get_hide_offline_buddies()) |
119 | 859 buddylist_build(); |
143 | 860 update_roster = TRUE; |
81 | 861 } |
862 } | |
104 | 863 |
864 if (chatmode) | |
865 scr_ShowBuddyWindow(); | |
81 | 866 } |
867 | |
265 | 868 // scr_RosterSearch(str) |
869 // Look forward for a buddy with jid/name containing str. | |
870 void scr_RosterSearch(char *str) | |
871 { | |
872 GList *matching_buddy; | |
873 enum imstatus prev_st = imstatus_size; // undef | |
874 | |
875 if (current_buddy) { | |
876 matching_buddy = buddy_search(str); | |
877 if (matching_buddy) { | |
878 prev_st = buddy_getstatus(BUDDATA(current_buddy)); | |
879 buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE); | |
880 current_buddy = matching_buddy; | |
881 if (chatmode) | |
882 buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, TRUE); | |
883 // We should rebuild the buddylist but not everytime | |
884 // Here we check if we were locking a buddy who is actually offline, | |
885 // and hide_offline_buddies is TRUE. In which case we need to rebuild. | |
886 if (prev_st == offline && buddylist_get_hide_offline_buddies()) | |
887 buddylist_build(); | |
888 update_roster = TRUE; | |
889 } | |
890 } | |
891 | |
892 if (chatmode) | |
893 scr_ShowBuddyWindow(); | |
894 } | |
895 | |
236 | 896 // scr_RosterUnreadMessage(next) |
897 // Go to a new message. If next is not null, try to go to the next new | |
898 // message. If it is not possible or if next is NULL, go to the first new | |
899 // message from unread_list. | |
900 void scr_RosterUnreadMessage(int next) | |
901 { | |
902 enum imstatus prev_st = imstatus_size; // undef | |
903 | |
904 if (current_buddy) { | |
905 gpointer unread_ptr; | |
906 gpointer refbuddata; | |
907 gpointer ngroup; | |
908 GList *nbuddy; | |
909 | |
910 if (next) refbuddata = BUDDATA(current_buddy); | |
911 else refbuddata = NULL; | |
912 | |
913 unread_ptr = unread_msg(refbuddata); | |
914 if (!unread_ptr) return; | |
915 | |
916 // If buddy is in a folded group, we need to expand it | |
917 ngroup = buddy_getgroup(unread_ptr); | |
918 if (buddy_getflags(ngroup) & ROSTER_FLAG_HIDE) { | |
919 buddy_setflags(ngroup, ROSTER_FLAG_HIDE, FALSE); | |
920 buddylist_build(); | |
921 } | |
922 | |
923 nbuddy = g_list_find(buddylist, unread_ptr); | |
924 if (nbuddy) { | |
925 prev_st = buddy_getstatus(BUDDATA(current_buddy)); | |
926 buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE); | |
927 current_buddy = nbuddy; | |
928 if (chatmode) | |
929 buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, TRUE); | |
930 // We should rebuild the buddylist but not everytime | |
931 // Here we check if we were locking a buddy who is actually offline, | |
932 // and hide_offline_buddies is TRUE. In which case we need to rebuild. | |
933 if (prev_st == offline && buddylist_get_hide_offline_buddies()) | |
934 buddylist_build(); | |
935 update_roster = TRUE; | |
936 | |
937 if (chatmode) scr_ShowBuddyWindow(); | |
938 } else scr_LogPrint("Error: nbuddy == NULL"); | |
939 } | |
940 } | |
941 | |
143 | 942 // scr_ScrollUp() |
943 // Scroll up the current buddy window, half a screen. | |
105 | 944 void scr_ScrollUp(void) |
945 { | |
946 const gchar *jid; | |
947 window_entry_t *win_entry; | |
948 int n, nblines; | |
949 GList *hbuf_top; | |
950 | |
951 // Get win_entry | |
952 if (!current_buddy) | |
953 return; | |
954 jid = CURRENT_JID; | |
955 if (!jid) | |
956 return; | |
957 win_entry = scr_SearchWindow(jid); | |
958 if (!win_entry) | |
959 return; | |
960 | |
961 // Scroll up half a screen (or less) | |
962 nblines = CHAT_WIN_HEIGHT/2-1; | |
963 hbuf_top = win_entry->top; | |
964 if (!hbuf_top) { | |
965 hbuf_top = g_list_last(win_entry->hbuf); | |
109 | 966 if (!win_entry->cleared) |
967 nblines *= 3; | |
968 else | |
969 win_entry->cleared = FALSE; | |
105 | 970 } |
971 | |
972 n = 0; | |
973 while (hbuf_top && n < nblines && g_list_previous(hbuf_top)) { | |
974 hbuf_top = g_list_previous(hbuf_top); | |
975 n++; | |
976 } | |
977 win_entry->top = hbuf_top; | |
978 | |
979 // Refresh the window | |
980 scr_UpdateWindow(win_entry); | |
981 | |
982 // Finished :) | |
983 update_panels(); | |
984 doupdate(); | |
985 } | |
986 | |
143 | 987 // scr_ScrollDown() |
988 // Scroll down the current buddy window, half a screen. | |
105 | 989 void scr_ScrollDown(void) |
990 { | |
991 const gchar *jid; | |
992 window_entry_t *win_entry; | |
993 int n, nblines; | |
994 GList *hbuf_top; | |
995 | |
996 // Get win_entry | |
997 if (!current_buddy) | |
998 return; | |
999 jid = CURRENT_JID; | |
1000 if (!jid) | |
1001 return; | |
1002 win_entry = scr_SearchWindow(jid); | |
1003 if (!win_entry) | |
1004 return; | |
1005 | |
1006 // Scroll down half a screen (or less) | |
1007 nblines = CHAT_WIN_HEIGHT/2-1; | |
1008 hbuf_top = win_entry->top; | |
1009 | |
1010 for (n=0 ; hbuf_top && n < nblines ; n++) | |
1011 hbuf_top = g_list_next(hbuf_top); | |
1012 win_entry->top = hbuf_top; | |
1013 // Check if we are at the bottom | |
1014 for (n=0 ; hbuf_top && n < CHAT_WIN_HEIGHT-1 ; n++) | |
1015 hbuf_top = g_list_next(hbuf_top); | |
1016 if (!hbuf_top) | |
1017 win_entry->top = NULL; // End reached | |
1018 | |
1019 // Refresh the window | |
1020 scr_UpdateWindow(win_entry); | |
1021 | |
1022 // Finished :) | |
1023 update_panels(); | |
1024 doupdate(); | |
1025 } | |
1026 | |
143 | 1027 // scr_Clear() |
1028 // Clear the current buddy window (used for the /clear command) | |
108 | 1029 void scr_Clear(void) |
1030 { | |
1031 const gchar *jid; | |
1032 window_entry_t *win_entry; | |
1033 | |
1034 // Get win_entry | |
1035 if (!current_buddy) | |
1036 return; | |
1037 jid = CURRENT_JID; | |
1038 if (!jid) | |
1039 return; | |
1040 win_entry = scr_SearchWindow(jid); | |
1041 if (!win_entry) | |
1042 return; | |
1043 | |
1044 win_entry->cleared = TRUE; | |
109 | 1045 win_entry->top = NULL; |
108 | 1046 |
1047 // Refresh the window | |
1048 scr_UpdateWindow(win_entry); | |
1049 | |
1050 // Finished :) | |
1051 update_panels(); | |
1052 doupdate(); | |
1053 } | |
1054 | |
187 | 1055 // scr_BufferTop() |
1056 // Jump to the head of the current buddy window | |
1057 void scr_BufferTop(void) | |
1058 { | |
1059 const gchar *jid; | |
1060 window_entry_t *win_entry; | |
1061 | |
1062 // Get win_entry | |
1063 if (!current_buddy) return; | |
1064 jid = CURRENT_JID; | |
1065 if (!jid) return; | |
1066 win_entry = scr_SearchWindow(jid); | |
1067 | |
1068 if (!win_entry) return; | |
1069 | |
1070 win_entry->cleared = FALSE; | |
1071 win_entry->top = g_list_first(win_entry->hbuf); | |
1072 | |
1073 // Refresh the window | |
1074 scr_UpdateWindow(win_entry); | |
1075 | |
1076 // Finished :) | |
1077 update_panels(); | |
1078 doupdate(); | |
1079 } | |
1080 | |
1081 // scr_BufferBottom() | |
1082 // Jump to the end of the current buddy window | |
1083 void scr_BufferBottom(void) | |
1084 { | |
1085 const gchar *jid; | |
1086 window_entry_t *win_entry; | |
1087 | |
1088 // Get win_entry | |
1089 if (!current_buddy) return; | |
1090 jid = CURRENT_JID; | |
1091 if (!jid) return; | |
1092 win_entry = scr_SearchWindow(jid); | |
1093 | |
1094 if (!win_entry) return; | |
1095 | |
1096 win_entry->cleared = FALSE; | |
1097 win_entry->top = NULL; | |
1098 | |
1099 // Refresh the window | |
1100 scr_UpdateWindow(win_entry); | |
1101 | |
1102 // Finished :) | |
1103 update_panels(); | |
1104 doupdate(); | |
1105 } | |
1106 | |
44 | 1107 // scr_LogPrint(...) |
1108 // Display a message in the log window. | |
24 | 1109 void scr_LogPrint(const char *fmt, ...) |
1110 { | |
1111 time_t timestamp; | |
1112 char *buffer; | |
1113 va_list ap; | |
1114 | |
153 | 1115 do { |
157 | 1116 buffer = (char *) calloc(1, 1024); |
153 | 1117 } while (!buffer); |
24 | 1118 |
1119 timestamp = time(NULL); | |
1120 strftime(buffer, 64, "[%H:%M:%S] ", localtime(×tamp)); | |
1121 wprintw(logWnd, "\n%s", buffer); | |
1122 | |
1123 va_start(ap, fmt); | |
153 | 1124 vsnprintf(buffer, 1024, fmt, ap); |
24 | 1125 va_end(ap); |
1126 | |
1127 wprintw(logWnd, "%s", buffer); | |
1128 free(buffer); | |
1129 | |
1130 update_panels(); | |
1131 doupdate(); | |
1132 } | |
1133 | |
143 | 1134 // scr_set_chatmode() |
261 | 1135 // Public function to (un)set chatmode... |
129 | 1136 inline void scr_set_chatmode(int enable) |
1137 { | |
1138 chatmode = enable; | |
1139 } | |
1140 | |
238 | 1141 // scr_get_multimode() |
261 | 1142 // Public function to get multimode status... |
238 | 1143 inline int scr_get_multimode() |
1144 { | |
1145 return multimode; | |
1146 } | |
1147 | |
1148 // scr_set_multimode() | |
261 | 1149 // Public function to (un)set multimode... |
260
33e1a05864a6
Add "verbatim multi-line" mode, with commands disabled
mikael@frmp8452
parents:
252
diff
changeset
|
1150 // Convention: |
33e1a05864a6
Add "verbatim multi-line" mode, with commands disabled
mikael@frmp8452
parents:
252
diff
changeset
|
1151 // 0 = disabled / 1 = multimode / 2 = multimode verbatim (commands disabled) |
238 | 1152 inline void scr_set_multimode(int enable) |
1153 { | |
1154 if (multiline) { | |
1155 g_free(multiline); | |
1156 multiline = NULL; | |
1157 } | |
260
33e1a05864a6
Add "verbatim multi-line" mode, with commands disabled
mikael@frmp8452
parents:
252
diff
changeset
|
1158 multimode = enable; |
238 | 1159 } |
1160 | |
1161 // scr_get_multiline() | |
261 | 1162 // Public function to get the current multi-line. |
238 | 1163 inline const char *scr_get_multiline() |
1164 { | |
1165 if (multimode && multiline) | |
1166 return multiline; | |
1167 else | |
1168 return ""; | |
1169 } | |
1170 | |
1171 // scr_append_multiline(line) | |
1172 // Public function to append a line to the current multi-line message. | |
1173 // Skip empty leading lines. | |
1174 void scr_append_multiline(const char *line) | |
1175 { | |
1176 static int num; | |
1177 | |
1178 if (!multimode) { | |
1179 scr_LogPrint("Error: Not in multi-line message mode!"); | |
1180 return; | |
1181 } | |
1182 if (multiline) { | |
1183 int len = strlen(multiline)+strlen(line)+2; | |
252 | 1184 if (len >= HBB_BLOCKSIZE - 1) { |
238 | 1185 // We don't handle single messages with size > HBB_BLOCKSIZE |
1186 // (see hbuf) | |
1187 scr_LogPrint("Your multi-line message is too big, this line has " | |
1188 "not been added."); | |
1189 scr_LogPrint("Please send this part now..."); | |
1190 return; | |
1191 } | |
276
627925d885de
Limit the number of lines in multi-line messages
Mikael Berthe <mikael@lilotux.net>
parents:
271
diff
changeset
|
1192 if (num >= MULTILINE_MAX_LINE_NUMBER) { |
627925d885de
Limit the number of lines in multi-line messages
Mikael Berthe <mikael@lilotux.net>
parents:
271
diff
changeset
|
1193 // We don't allow too many lines; however the maximum is arbitrary |
627925d885de
Limit the number of lines in multi-line messages
Mikael Berthe <mikael@lilotux.net>
parents:
271
diff
changeset
|
1194 // (It should be < 1000 yet) |
627925d885de
Limit the number of lines in multi-line messages
Mikael Berthe <mikael@lilotux.net>
parents:
271
diff
changeset
|
1195 scr_LogPrint("Your message has too many lines, this one has " |
627925d885de
Limit the number of lines in multi-line messages
Mikael Berthe <mikael@lilotux.net>
parents:
271
diff
changeset
|
1196 "not been added."); |
627925d885de
Limit the number of lines in multi-line messages
Mikael Berthe <mikael@lilotux.net>
parents:
271
diff
changeset
|
1197 scr_LogPrint("Please send this part now..."); |
627925d885de
Limit the number of lines in multi-line messages
Mikael Berthe <mikael@lilotux.net>
parents:
271
diff
changeset
|
1198 return; |
627925d885de
Limit the number of lines in multi-line messages
Mikael Berthe <mikael@lilotux.net>
parents:
271
diff
changeset
|
1199 } |
238 | 1200 multiline = g_renew(char, multiline, len); |
1201 strcat(multiline, "\n"); | |
1202 strcat(multiline, line); | |
1203 num++; | |
1204 } else { | |
1205 // First message line (we skip leading empty lines) | |
1206 num = 0; | |
1207 if (line[0]) { | |
1208 multiline = g_new(char, strlen(line)+1); | |
1209 strcpy(multiline, line); | |
1210 num++; | |
1211 } else | |
1212 return; | |
1213 } | |
1214 scr_LogPrint("Multi-line mode: line #%d added [%.25s...", num, line); | |
1215 } | |
1216 | |
173 | 1217 // scr_cmdhisto_addline() |
1218 // Add a line to the inputLine history | |
1219 inline void scr_cmdhisto_addline(char *line) | |
1220 { | |
1221 if (!line || !*line) return; | |
1222 | |
1223 cmdhisto = g_list_append(cmdhisto, g_strdup(line)); | |
1224 } | |
1225 | |
1226 // scr_cmdhisto_prev() | |
1227 // Look for previous line beginning w/ the given mask in the inputLine history | |
175 | 1228 // Returns NULL if none found |
173 | 1229 const char *scr_cmdhisto_prev(char *mask, guint len) |
1230 { | |
1231 GList *hl; | |
1232 if (!cmdhisto_cur) { | |
1233 hl = g_list_last(cmdhisto); | |
174 | 1234 if (hl) { // backup current line |
1235 strncpy(cmdhisto_backup, mask, INPUTLINE_LENGTH); | |
1236 } | |
173 | 1237 } else { |
1238 hl = g_list_previous(cmdhisto_cur); | |
1239 } | |
1240 while (hl) { | |
1241 if (!strncmp((char*)hl->data, mask, len)) { | |
1242 // Found a match | |
1243 cmdhisto_cur = hl; | |
1244 return (const char*)hl->data; | |
1245 } | |
1246 hl = g_list_previous(hl); | |
1247 } | |
1248 return NULL; | |
1249 } | |
1250 | |
1251 // scr_cmdhisto_next() | |
1252 // Look for next line beginning w/ the given mask in the inputLine history | |
175 | 1253 // Returns NULL if none found |
173 | 1254 const char *scr_cmdhisto_next(char *mask, guint len) |
1255 { | |
1256 GList *hl; | |
1257 if (!cmdhisto_cur) return NULL; | |
1258 hl = cmdhisto_cur; | |
1259 while ((hl = g_list_next(hl)) != NULL) | |
1260 if (!strncmp((char*)hl->data, mask, len)) { | |
1261 // Found a match | |
1262 cmdhisto_cur = hl; | |
1263 return (const char*)hl->data; | |
1264 } | |
175 | 1265 // If the "backuped" line matches, we'll use it |
1266 if (strncmp(cmdhisto_backup, mask, len)) return NULL; // No match | |
174 | 1267 cmdhisto_cur = NULL; |
1268 return cmdhisto_backup; | |
173 | 1269 } |
1270 | |
195 | 1271 // readline_transpose_chars() |
1272 // Drag the character before point forward over the character at | |
1273 // point, moving point forward as well. If point is at the end of | |
1274 // the line, then this transposes the two characters before point. | |
1275 void readline_transpose_chars() | |
1276 { | |
1277 char swp; | |
1278 | |
1279 if (ptr_inputline == inputLine) return; | |
1280 | |
1281 if (!*ptr_inputline) { // We're at EOL | |
1282 // If line is only 1 char long, nothing to do... | |
1283 if (ptr_inputline == inputLine+1) return; | |
1284 // Transpose the two previous characters | |
1285 swp = *(ptr_inputline-2); | |
1286 *(ptr_inputline-2) = *(ptr_inputline-1); | |
1287 *(ptr_inputline-1) = swp; | |
1288 } else { | |
196 | 1289 // Swap the two characters before the cursor and move right. |
195 | 1290 swp = *(ptr_inputline-1); |
1291 *(ptr_inputline-1) = *ptr_inputline; | |
1292 *ptr_inputline++ = swp; | |
1293 check_offset(1); | |
1294 } | |
1295 } | |
1296 | |
1297 // readline_backward_kill_word() | |
194 | 1298 // Kill the word before the cursor, in input line |
195 | 1299 void readline_backward_kill_word() |
194 | 1300 { |
1301 char *c, *old = ptr_inputline; | |
1302 int spaceallowed = 1; | |
1303 | |
1304 if (ptr_inputline == inputLine) return; | |
1305 | |
1306 for (c = ptr_inputline-1 ; c > inputLine ; c--) | |
1307 if (!isalnum(*c)) { | |
1308 if (*c == ' ') | |
1309 if (!spaceallowed) break; | |
1310 } else spaceallowed = 0; | |
1311 | |
1312 if (c != inputLine || *c != ' ') | |
1313 if ((c < ptr_inputline-1) && (!isalnum(*c))) | |
1314 c++; | |
1315 | |
1316 // Modify the line | |
1317 ptr_inputline = c; | |
1318 for (;;) { | |
1319 *c = *old++; | |
1320 if (!*c++) break; | |
1321 } | |
195 | 1322 check_offset(-1); |
194 | 1323 } |
1324 | |
98 | 1325 // which_row() |
1326 // Tells which row our cursor is in, in the command line. | |
1327 // -1 -> normal text | |
1328 // 0 -> command | |
1329 // 1 -> parameter 1 (etc.) | |
102 | 1330 // If > 0, then *p_row is set to the beginning of the row |
1331 int which_row(char **p_row) | |
98 | 1332 { |
1333 int row = -1; | |
1334 char *p; | |
1335 int quote = FALSE; | |
1336 | |
1337 // Not a command? | |
1338 if ((ptr_inputline == inputLine) || (inputLine[0] != '/')) | |
1339 return -1; | |
1340 | |
1341 // This is a command | |
1342 row = 0; | |
1343 for (p = inputLine ; p < ptr_inputline ; p++) { | |
1344 if (quote) { | |
1345 if (*p == '"' && *(p-1) != '\\') | |
1346 quote = FALSE; | |
1347 continue; | |
1348 } | |
1349 if (*p == '"' && *(p-1) != '\\') { | |
1350 quote = TRUE; | |
121 | 1351 } else if (*p == ' ') { |
1352 if (*(p-1) != ' ') | |
1353 row++; | |
102 | 1354 *p_row = p+1; |
1355 } | |
98 | 1356 } |
1357 return row; | |
1358 } | |
1359 | |
143 | 1360 // scr_insert_text() |
1361 // Insert the given text at the current cursor position. | |
1362 // The cursor is moved. We don't check if the cursor still is in the screen | |
1363 // after, the caller should do that. | |
98 | 1364 void scr_insert_text(const char *text) |
1365 { | |
1366 char tmpLine[INPUTLINE_LENGTH+1]; | |
1367 int len = strlen(text); | |
1368 // Check the line isn't too long | |
1369 if (strlen(inputLine) + len >= INPUTLINE_LENGTH) { | |
1370 scr_LogPrint("Cannot insert text, line too long."); | |
1371 return; | |
1372 } | |
1373 | |
1374 strcpy(tmpLine, ptr_inputline); | |
1375 strcpy(ptr_inputline, text); ptr_inputline += len; | |
1376 strcpy(ptr_inputline, tmpLine); | |
1377 } | |
1378 | |
143 | 1379 // scr_handle_tab() |
1380 // Function called when tab is pressed. | |
1381 // Initiate or continue a completion... | |
98 | 1382 void scr_handle_tab(void) |
1383 { | |
102 | 1384 int nrow; |
1385 char *row; | |
1386 const char *cchar; | |
103 | 1387 guint compl_categ; |
98 | 1388 |
102 | 1389 nrow = which_row(&row); |
98 | 1390 |
103 | 1391 // a) No completion if no leading slash ('cause not a command) |
1392 // b) We can't have more than 2 parameters (we use 2 flags) | |
1393 if (nrow < 0 || nrow > 2) return; | |
102 | 1394 |
103 | 1395 if (nrow == 0) { // Command completion |
1396 row = &inputLine[1]; | |
1397 compl_categ = COMPL_CMD; | |
1398 } else { // Other completion, depending on the command | |
285 | 1399 int alias = FALSE; |
1400 cmd *com; | |
1401 char *xpline = expandalias(inputLine); | |
1402 com = cmd_get(xpline); | |
1403 if (xpline != inputLine) { | |
1404 // This is an alias, so we can't complete rows > 0 | |
1405 alias = TRUE; | |
1406 g_free(xpline); | |
1407 } | |
1408 if ((!com && (!alias || !completion_started)) || !row) { | |
103 | 1409 scr_LogPrint("I cannot complete that..."); |
1410 return; | |
1411 } | |
285 | 1412 if (!alias) |
1413 compl_categ = com->completion_flags[nrow-1]; | |
1414 else | |
1415 compl_categ = 0; | |
103 | 1416 } |
1417 | |
1418 if (!completion_started) { | |
1419 GSList *list = compl_get_category_list(compl_categ); | |
1420 if (list) { | |
1421 char *prefix = g_strndup(row, ptr_inputline-row); | |
1422 // Init completion | |
1423 new_completion(prefix, list); | |
1424 g_free(prefix); | |
1425 // Now complete | |
98 | 1426 cchar = complete(); |
1427 if (cchar) | |
1428 scr_insert_text(cchar); | |
103 | 1429 completion_started = TRUE; |
98 | 1430 } |
103 | 1431 } else { // Completion already initialized |
1432 char *c; | |
1433 guint back = cancel_completion(); | |
1434 // Remove $back chars | |
1435 ptr_inputline -= back; | |
1436 c = ptr_inputline; | |
1437 for ( ; *c ; c++) | |
1438 *c = *(c+back); | |
1439 // Now complete again | |
1440 cchar = complete(); | |
1441 if (cchar) | |
1442 scr_insert_text(cchar); | |
102 | 1443 } |
98 | 1444 } |
1445 | |
1446 void scr_cancel_current_completion(void) | |
1447 { | |
1448 char *c; | |
1449 guint back = cancel_completion(); | |
1450 // Remove $back chars | |
1451 ptr_inputline -= back; | |
1452 c = ptr_inputline; | |
1453 for ( ; *c ; c++) | |
1454 *c = *(c+back); | |
1455 } | |
1456 | |
1457 void scr_end_current_completion(void) | |
1458 { | |
1459 done_completion(); | |
1460 completion_started = FALSE; | |
1461 } | |
1462 | |
24 | 1463 // check_offset(int direction) |
1464 // Check inputline_offset value, and make sure the cursor is inside the | |
1465 // screen. | |
1466 inline void check_offset(int direction) | |
1467 { | |
1468 // Left side | |
1469 if (inputline_offset && direction <= 0) { | |
1470 while (ptr_inputline <= (char*)&inputLine + inputline_offset) { | |
1471 if (inputline_offset) { | |
1472 inputline_offset -= 5; | |
1473 if (inputline_offset < 0) | |
1474 inputline_offset = 0; | |
1475 } | |
1476 } | |
1477 } | |
1478 // Right side | |
1479 if (direction >= 0) { | |
1480 while (ptr_inputline >= inputline_offset + (char*)&inputLine + maxX) | |
1481 inputline_offset += 5; | |
1482 } | |
1483 } | |
1484 | |
312
f0b7ff2df7e8
Ctrl-C does not terminate mcabber
Mikael Berthe <mikael@lilotux.net>
parents:
307
diff
changeset
|
1485 inline void refresh_inputline(void) |
f0b7ff2df7e8
Ctrl-C does not terminate mcabber
Mikael Berthe <mikael@lilotux.net>
parents:
307
diff
changeset
|
1486 { |
f0b7ff2df7e8
Ctrl-C does not terminate mcabber
Mikael Berthe <mikael@lilotux.net>
parents:
307
diff
changeset
|
1487 mvwprintw(inputWnd, 0,0, "%s", inputLine + inputline_offset); |
f0b7ff2df7e8
Ctrl-C does not terminate mcabber
Mikael Berthe <mikael@lilotux.net>
parents:
307
diff
changeset
|
1488 wclrtoeol(inputWnd); |
f0b7ff2df7e8
Ctrl-C does not terminate mcabber
Mikael Berthe <mikael@lilotux.net>
parents:
307
diff
changeset
|
1489 if (*ptr_inputline) |
f0b7ff2df7e8
Ctrl-C does not terminate mcabber
Mikael Berthe <mikael@lilotux.net>
parents:
307
diff
changeset
|
1490 wmove(inputWnd, 0, ptr_inputline - (char*)&inputLine - inputline_offset); |
f0b7ff2df7e8
Ctrl-C does not terminate mcabber
Mikael Berthe <mikael@lilotux.net>
parents:
307
diff
changeset
|
1491 } |
f0b7ff2df7e8
Ctrl-C does not terminate mcabber
Mikael Berthe <mikael@lilotux.net>
parents:
307
diff
changeset
|
1492 |
f0b7ff2df7e8
Ctrl-C does not terminate mcabber
Mikael Berthe <mikael@lilotux.net>
parents:
307
diff
changeset
|
1493 void scr_handle_sigint(void) |
f0b7ff2df7e8
Ctrl-C does not terminate mcabber
Mikael Berthe <mikael@lilotux.net>
parents:
307
diff
changeset
|
1494 { |
f0b7ff2df7e8
Ctrl-C does not terminate mcabber
Mikael Berthe <mikael@lilotux.net>
parents:
307
diff
changeset
|
1495 scr_LogPrint("In screen. completion_started=%d", completion_started); |
f0b7ff2df7e8
Ctrl-C does not terminate mcabber
Mikael Berthe <mikael@lilotux.net>
parents:
307
diff
changeset
|
1496 // Same as Ctrl-g, now |
f0b7ff2df7e8
Ctrl-C does not terminate mcabber
Mikael Berthe <mikael@lilotux.net>
parents:
307
diff
changeset
|
1497 scr_cancel_current_completion(); |
f0b7ff2df7e8
Ctrl-C does not terminate mcabber
Mikael Berthe <mikael@lilotux.net>
parents:
307
diff
changeset
|
1498 scr_end_current_completion(); |
f0b7ff2df7e8
Ctrl-C does not terminate mcabber
Mikael Berthe <mikael@lilotux.net>
parents:
307
diff
changeset
|
1499 check_offset(-1); |
f0b7ff2df7e8
Ctrl-C does not terminate mcabber
Mikael Berthe <mikael@lilotux.net>
parents:
307
diff
changeset
|
1500 refresh_inputline(); |
f0b7ff2df7e8
Ctrl-C does not terminate mcabber
Mikael Berthe <mikael@lilotux.net>
parents:
307
diff
changeset
|
1501 } |
f0b7ff2df7e8
Ctrl-C does not terminate mcabber
Mikael Berthe <mikael@lilotux.net>
parents:
307
diff
changeset
|
1502 |
44 | 1503 // process_key(key) |
1504 // Handle the pressed key, in the command line (bottom). | |
29 | 1505 int process_key(int key) |
24 | 1506 { |
1507 if (isprint(key)) { | |
1508 char tmpLine[INPUTLINE_LENGTH+1]; | |
1509 | |
1510 // Check the line isn't too long | |
1511 if (strlen(inputLine) >= INPUTLINE_LENGTH) | |
1512 return 0; | |
1513 | |
1514 // Insert char | |
1515 strcpy(tmpLine, ptr_inputline); | |
1516 *ptr_inputline++ = key; | |
1517 strcpy(ptr_inputline, tmpLine); | |
1518 check_offset(1); | |
1519 } else { | |
1520 switch(key) { | |
232 | 1521 case 8: // Ctrl-h |
1522 case 127: // Backspace too | |
24 | 1523 case KEY_BACKSPACE: |
1524 if (ptr_inputline != (char*)&inputLine) { | |
42 | 1525 char *c = --ptr_inputline; |
1526 for ( ; *c ; c++) | |
1527 *c = *(c+1); | |
24 | 1528 check_offset(-1); |
1529 } | |
1530 break; | |
238 | 1531 case KEY_DC:// Del |
24 | 1532 if (*ptr_inputline) |
1533 strcpy(ptr_inputline, ptr_inputline+1); | |
1534 break; | |
1535 case KEY_LEFT: | |
1536 if (ptr_inputline != (char*)&inputLine) { | |
1537 ptr_inputline--; | |
1538 check_offset(-1); | |
1539 } | |
1540 break; | |
1541 case KEY_RIGHT: | |
1542 if (*ptr_inputline) | |
1543 ptr_inputline++; | |
1544 check_offset(1); | |
1545 break; | |
98 | 1546 case 7: // Ctrl-g |
1547 scr_cancel_current_completion(); | |
1548 scr_end_current_completion(); | |
1549 check_offset(-1); | |
1550 break; | |
24 | 1551 case 9: // Tab |
98 | 1552 scr_handle_tab(); |
1553 check_offset(0); | |
24 | 1554 break; |
1555 case '\n': // Enter | |
263 | 1556 case 15: // Ctrl-o ("accept-line-and-down-history") |
29 | 1557 if (process_line(inputLine)) |
24 | 1558 return 255; |
173 | 1559 // Add line to history |
1560 scr_cmdhisto_addline(inputLine); | |
1561 // Reset the line | |
24 | 1562 ptr_inputline = inputLine; |
1563 *ptr_inputline = 0; | |
1564 inputline_offset = 0; | |
263 | 1565 |
1566 if (key == '\n') // Enter | |
1567 { | |
1568 // Reset history line pointer | |
1569 cmdhisto_cur = NULL; | |
1570 } else { // down-history | |
1571 // Use next history line instead of a blank line | |
1572 const char *l = scr_cmdhisto_next("", 0); | |
1573 if (l) | |
1574 strcpy(inputLine, l); | |
1575 // Reset backup history line | |
1576 cmdhisto_backup[0] = 0; | |
1577 } | |
24 | 1578 break; |
1579 case KEY_UP: | |
175 | 1580 { |
1581 const char *l = scr_cmdhisto_prev(inputLine, | |
1582 ptr_inputline-inputLine); | |
1583 if (l) { | |
1584 strcpy(inputLine, l); | |
1585 } | |
1586 } | |
24 | 1587 break; |
1588 case KEY_DOWN: | |
175 | 1589 { |
1590 const char *l = scr_cmdhisto_next(inputLine, | |
1591 ptr_inputline-inputLine); | |
1592 if (l) { | |
1593 strcpy(inputLine, l); | |
1594 } | |
1595 } | |
24 | 1596 break; |
1597 case KEY_PPAGE: | |
175 | 1598 scr_RosterUp(); |
24 | 1599 break; |
1600 case KEY_NPAGE: | |
175 | 1601 scr_RosterDown(); |
24 | 1602 break; |
1603 case KEY_HOME: | |
1604 case 1: | |
1605 ptr_inputline = inputLine; | |
1606 inputline_offset = 0; | |
1607 break; | |
1608 case KEY_END: | |
1609 case 5: | |
1610 for (; *ptr_inputline; ptr_inputline++) ; | |
1611 check_offset(1); | |
1612 break; | |
1613 case 21: // Ctrl-u | |
1614 strcpy(inputLine, ptr_inputline); | |
1615 ptr_inputline = inputLine; | |
1616 inputline_offset = 0; | |
1617 break; | |
1618 case KEY_EOL: | |
1619 case 11: // Ctrl-k | |
1620 *ptr_inputline = 0; | |
1621 break; | |
1622 case 16: // Ctrl-p | |
175 | 1623 scr_ScrollUp(); |
24 | 1624 break; |
1625 case 14: // Ctrl-n | |
175 | 1626 scr_ScrollDown(); |
24 | 1627 break; |
196 | 1628 case 17: // Ctrl-q |
236 | 1629 scr_RosterUnreadMessage(1); // next unread message |
196 | 1630 break; |
195 | 1631 case 20: // Ctrl-t |
1632 readline_transpose_chars(); | |
1633 break; | |
194 | 1634 case 23: // Ctrl-w |
195 | 1635 readline_backward_kill_word(); |
194 | 1636 break; |
24 | 1637 case 27: // ESC |
1638 currentWindow = NULL; | |
1639 chatmode = FALSE; | |
119 | 1640 if (current_buddy) |
1641 buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE); | |
24 | 1642 top_panel(chatPanel); |
1643 top_panel(inputPanel); | |
157 | 1644 update_panels(); |
24 | 1645 break; |
151 | 1646 case 12: // Ctrl-l |
1647 case KEY_RESIZE: | |
1648 scr_Resize(); | |
1649 break; | |
24 | 1650 default: |
288 | 1651 { |
1652 const gchar *boundcmd = isbound(key); | |
1653 if (boundcmd) { | |
1654 gchar *cmd = g_strdup_printf("/%s", boundcmd); | |
1655 if (process_command(cmd)) | |
1656 return 255; | |
1657 g_free(cmd); | |
1658 } else { | |
1659 scr_LogPrint("Unknown key=%d", key); | |
1660 if (utf8_mode) | |
1661 scr_LogPrint("WARNING: UTF-8 not yet supported!"); | |
1662 } | |
1663 } | |
24 | 1664 } |
1665 } | |
157 | 1666 if (completion_started && key != 9 && key != KEY_RESIZE) |
98 | 1667 scr_end_current_completion(); |
312
f0b7ff2df7e8
Ctrl-C does not terminate mcabber
Mikael Berthe <mikael@lilotux.net>
parents:
307
diff
changeset
|
1668 refresh_inputline(); |
f0b7ff2df7e8
Ctrl-C does not terminate mcabber
Mikael Berthe <mikael@lilotux.net>
parents:
307
diff
changeset
|
1669 if (!update_roster) |
157 | 1670 doupdate(); |
24 | 1671 return 0; |
1672 } |