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