1 /*
2 * window.c
3 * vim: expandtab:ts=4:sts=4:sw=4
4 *
5 * Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
6 * Copyright (C) 2019 - 2021 Michael Vetter <jubalh@iodoru.org>
7 *
8 * This file is part of Profanity.
9 *
10 * Profanity is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * Profanity is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with Profanity. If not, see <https://www.gnu.org/licenses/>.
22 *
23 * In addition, as a special exception, the copyright holders give permission to
24 * link the code of portions of this program with the OpenSSL library under
25 * certain conditions as described in each individual source file, and
26 * distribute linked combinations including the two.
27 *
28 * You must obey the GNU General Public License in all respects for all of the
29 * code used other than OpenSSL. If you modify file(s) with this exception, you
30 * may extend this exception to your version of the file(s), but you are not
31 * obligated to do so. If you do not wish to do so, delete this exception
32 * statement from your version. If you delete this exception statement from all
33 * source files in the program, then also delete it here.
34 *
35 */
36
37 #include "config.h"
38
39 #include <stdlib.h>
40 #include <string.h>
41 #include <time.h>
42 #include <assert.h>
43 #include <wchar.h>
44
45 #include <glib.h>
46
47 #ifdef HAVE_NCURSESW_NCURSES_H
48 #include <ncursesw/ncurses.h>
49 #elif HAVE_NCURSES_H
50 #include <ncurses.h>
51 #elif HAVE_CURSES_H
52 #include <curses.h>
53 #endif
54
55 #include "log.h"
56 #include "config/theme.h"
57 #include "config/preferences.h"
58 #include "ui/ui.h"
59 #include "ui/window.h"
60 #include "ui/screen.h"
61 #include "xmpp/xmpp.h"
62 #include "xmpp/roster_list.h"
63
64 #define CONS_WIN_TITLE "Profanity. Type /help for help information."
65 #define XML_WIN_TITLE "XML Console"
66
67 #define CEILING(X) (X - (int)(X) > 0 ? (int)(X + 1) : (int)(X))
68
69 static void
70 _win_printf(ProfWin* window, const char* show_char, int pad_indent, GDateTime* timestamp, int flags, theme_item_t theme_item, const char* const display_from, const char* const from_jid, const char* const message_id, const char* const message, ...);
71 static void _win_print_internal(ProfWin* window, const char* show_char, int pad_indent, GDateTime* time,
72 int flags, theme_item_t theme_item, const char* const from, const char* const message, DeliveryReceipt* receipt);
73 static void _win_print_wrapped(WINDOW* win, const char* const message, size_t indent, int pad_indent);
74
75 int
win_roster_cols(void)76 win_roster_cols(void)
77 {
78 int roster_win_percent = prefs_get_roster_size();
79 int cols = getmaxx(stdscr);
80 return CEILING((((double)cols) / 100) * roster_win_percent);
81 }
82
83 int
win_occpuants_cols(void)84 win_occpuants_cols(void)
85 {
86 int occupants_win_percent = prefs_get_occupants_size();
87 int cols = getmaxx(stdscr);
88 return CEILING((((double)cols) / 100) * occupants_win_percent);
89 }
90
91 static ProfLayout*
_win_create_simple_layout(void)92 _win_create_simple_layout(void)
93 {
94 int cols = getmaxx(stdscr);
95
96 ProfLayoutSimple* layout = malloc(sizeof(ProfLayoutSimple));
97 layout->base.type = LAYOUT_SIMPLE;
98 layout->base.win = newpad(PAD_SIZE, cols);
99 wbkgd(layout->base.win, theme_attrs(THEME_TEXT));
100 layout->base.buffer = buffer_create();
101 layout->base.y_pos = 0;
102 layout->base.paged = 0;
103 scrollok(layout->base.win, TRUE);
104
105 return &layout->base;
106 }
107
108 static ProfLayout*
_win_create_split_layout(void)109 _win_create_split_layout(void)
110 {
111 int cols = getmaxx(stdscr);
112
113 ProfLayoutSplit* layout = malloc(sizeof(ProfLayoutSplit));
114 layout->base.type = LAYOUT_SPLIT;
115 layout->base.win = newpad(PAD_SIZE, cols);
116 wbkgd(layout->base.win, theme_attrs(THEME_TEXT));
117 layout->base.buffer = buffer_create();
118 layout->base.y_pos = 0;
119 layout->base.paged = 0;
120 scrollok(layout->base.win, TRUE);
121 layout->subwin = NULL;
122 layout->sub_y_pos = 0;
123 layout->memcheck = LAYOUT_SPLIT_MEMCHECK;
124
125 return &layout->base;
126 }
127
128 ProfWin*
win_create_console(void)129 win_create_console(void)
130 {
131 ProfConsoleWin* new_win = malloc(sizeof(ProfConsoleWin));
132 new_win->window.type = WIN_CONSOLE;
133 new_win->window.layout = _win_create_split_layout();
134
135 return &new_win->window;
136 }
137
138 ProfWin*
win_create_chat(const char * const barejid)139 win_create_chat(const char* const barejid)
140 {
141 ProfChatWin* new_win = malloc(sizeof(ProfChatWin));
142 new_win->window.type = WIN_CHAT;
143 new_win->window.layout = _win_create_simple_layout();
144
145 new_win->barejid = strdup(barejid);
146 new_win->resource_override = NULL;
147 new_win->is_otr = FALSE;
148 new_win->otr_is_trusted = FALSE;
149 new_win->pgp_recv = FALSE;
150 new_win->pgp_send = FALSE;
151 new_win->is_omemo = FALSE;
152 new_win->is_ox = FALSE;
153 new_win->history_shown = FALSE;
154 new_win->unread = 0;
155 new_win->state = chat_state_new();
156 new_win->enctext = NULL;
157 new_win->incoming_char = NULL;
158 new_win->outgoing_char = NULL;
159 new_win->last_message = NULL;
160 new_win->last_msg_id = NULL;
161 new_win->has_attention = FALSE;
162 new_win->memcheck = PROFCHATWIN_MEMCHECK;
163
164 return &new_win->window;
165 }
166
167 ProfWin*
win_create_muc(const char * const roomjid)168 win_create_muc(const char* const roomjid)
169 {
170 ProfMucWin* new_win = malloc(sizeof(ProfMucWin));
171 int cols = getmaxx(stdscr);
172
173 new_win->window.type = WIN_MUC;
174 ProfLayoutSplit* layout = malloc(sizeof(ProfLayoutSplit));
175 layout->base.type = LAYOUT_SPLIT;
176
177 if (prefs_get_boolean(PREF_OCCUPANTS)) {
178 int subwin_cols = win_occpuants_cols();
179 layout->base.win = newpad(PAD_SIZE, cols - subwin_cols);
180 wbkgd(layout->base.win, theme_attrs(THEME_TEXT));
181 layout->subwin = newpad(PAD_SIZE, subwin_cols);
182 wbkgd(layout->subwin, theme_attrs(THEME_TEXT));
183 } else {
184 layout->base.win = newpad(PAD_SIZE, (cols));
185 wbkgd(layout->base.win, theme_attrs(THEME_TEXT));
186 layout->subwin = NULL;
187 }
188 layout->sub_y_pos = 0;
189 layout->memcheck = LAYOUT_SPLIT_MEMCHECK;
190 layout->base.buffer = buffer_create();
191 layout->base.y_pos = 0;
192 layout->base.paged = 0;
193 scrollok(layout->base.win, TRUE);
194 new_win->window.layout = (ProfLayout*)layout;
195
196 new_win->roomjid = strdup(roomjid);
197 new_win->room_name = NULL;
198 new_win->unread = 0;
199 new_win->unread_mentions = FALSE;
200 new_win->unread_triggers = FALSE;
201 if (prefs_get_boolean(PREF_OCCUPANTS_JID)) {
202 new_win->showjid = TRUE;
203 } else {
204 new_win->showjid = FALSE;
205 }
206 if (prefs_get_boolean(PREF_OCCUPANTS_OFFLINE)) {
207 new_win->showoffline = TRUE;
208 } else {
209 new_win->showoffline = FALSE;
210 }
211 new_win->enctext = NULL;
212 new_win->message_char = NULL;
213 new_win->is_omemo = FALSE;
214 new_win->last_message = NULL;
215 new_win->last_msg_id = NULL;
216 new_win->has_attention = FALSE;
217
218 new_win->memcheck = PROFMUCWIN_MEMCHECK;
219
220 return &new_win->window;
221 }
222
223 ProfWin*
win_create_config(const char * const roomjid,DataForm * form,ProfConfWinCallback submit,ProfConfWinCallback cancel,const void * userdata)224 win_create_config(const char* const roomjid, DataForm* form, ProfConfWinCallback submit, ProfConfWinCallback cancel, const void* userdata)
225 {
226 ProfConfWin* new_win = malloc(sizeof(ProfConfWin));
227 new_win->window.type = WIN_CONFIG;
228 new_win->window.layout = _win_create_simple_layout();
229 new_win->roomjid = strdup(roomjid);
230 new_win->form = form;
231 new_win->submit = submit;
232 new_win->cancel = cancel;
233 new_win->userdata = userdata;
234
235 new_win->memcheck = PROFCONFWIN_MEMCHECK;
236
237 return &new_win->window;
238 }
239
240 ProfWin*
win_create_private(const char * const fulljid)241 win_create_private(const char* const fulljid)
242 {
243 ProfPrivateWin* new_win = malloc(sizeof(ProfPrivateWin));
244 new_win->window.type = WIN_PRIVATE;
245 new_win->window.layout = _win_create_simple_layout();
246 new_win->fulljid = strdup(fulljid);
247 new_win->unread = 0;
248 new_win->occupant_offline = FALSE;
249 new_win->room_left = FALSE;
250
251 new_win->memcheck = PROFPRIVATEWIN_MEMCHECK;
252
253 return &new_win->window;
254 }
255
256 ProfWin*
win_create_xmlconsole(void)257 win_create_xmlconsole(void)
258 {
259 ProfXMLWin* new_win = malloc(sizeof(ProfXMLWin));
260 new_win->window.type = WIN_XML;
261 new_win->window.layout = _win_create_simple_layout();
262
263 new_win->memcheck = PROFXMLWIN_MEMCHECK;
264
265 return &new_win->window;
266 }
267
268 ProfWin*
win_create_plugin(const char * const plugin_name,const char * const tag)269 win_create_plugin(const char* const plugin_name, const char* const tag)
270 {
271 ProfPluginWin* new_win = malloc(sizeof(ProfPluginWin));
272 new_win->window.type = WIN_PLUGIN;
273 new_win->window.layout = _win_create_simple_layout();
274
275 new_win->tag = strdup(tag);
276 new_win->plugin_name = strdup(plugin_name);
277
278 new_win->memcheck = PROFPLUGINWIN_MEMCHECK;
279
280 return &new_win->window;
281 }
282
283 char*
win_get_title(ProfWin * window)284 win_get_title(ProfWin* window)
285 {
286 if (window == NULL) {
287 return strdup(CONS_WIN_TITLE);
288 }
289 if (window->type == WIN_CONSOLE) {
290 return strdup(CONS_WIN_TITLE);
291 }
292 if (window->type == WIN_CHAT) {
293 ProfChatWin* chatwin = (ProfChatWin*)window;
294 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
295 jabber_conn_status_t conn_status = connection_get_status();
296 if (conn_status == JABBER_CONNECTED) {
297 PContact contact = roster_get_contact(chatwin->barejid);
298 if (contact) {
299 const char* name = p_contact_name_or_jid(contact);
300 return strdup(name);
301 } else {
302 return strdup(chatwin->barejid);
303 }
304 } else {
305 return strdup(chatwin->barejid);
306 }
307 }
308 if (window->type == WIN_MUC) {
309 ProfMucWin* mucwin = (ProfMucWin*)window;
310 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
311
312 gboolean show_titlebar_jid = prefs_get_boolean(PREF_TITLEBAR_MUC_TITLE_JID);
313 gboolean show_titlebar_name = prefs_get_boolean(PREF_TITLEBAR_MUC_TITLE_NAME);
314 GString* title = g_string_new("");
315
316 if (show_titlebar_name && mucwin->room_name) {
317 g_string_append(title, mucwin->room_name);
318 g_string_append(title, " ");
319 }
320 if (show_titlebar_jid) {
321 g_string_append(title, mucwin->roomjid);
322 }
323
324 char* title_str = title->str;
325 g_string_free(title, FALSE);
326 return title_str;
327 }
328 if (window->type == WIN_CONFIG) {
329 ProfConfWin* confwin = (ProfConfWin*)window;
330 assert(confwin->memcheck == PROFCONFWIN_MEMCHECK);
331 GString* title = g_string_new(confwin->roomjid);
332 g_string_append(title, " config");
333 if (confwin->form->modified) {
334 g_string_append(title, " *");
335 }
336 char* title_str = title->str;
337 g_string_free(title, FALSE);
338 return title_str;
339 }
340 if (window->type == WIN_PRIVATE) {
341 ProfPrivateWin* privatewin = (ProfPrivateWin*)window;
342 assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
343 return strdup(privatewin->fulljid);
344 }
345 if (window->type == WIN_XML) {
346 return strdup(XML_WIN_TITLE);
347 }
348 if (window->type == WIN_PLUGIN) {
349 ProfPluginWin* pluginwin = (ProfPluginWin*)window;
350 assert(pluginwin->memcheck == PROFPLUGINWIN_MEMCHECK);
351 return strdup(pluginwin->tag);
352 }
353
354 return NULL;
355 }
356
357 char*
win_get_tab_identifier(ProfWin * window)358 win_get_tab_identifier(ProfWin* window)
359 {
360 assert(window != NULL);
361
362 switch (window->type) {
363 case WIN_CONSOLE:
364 {
365 return strdup("console");
366 }
367 case WIN_CHAT:
368 {
369 ProfChatWin* chatwin = (ProfChatWin*)window;
370 return strdup(chatwin->barejid);
371 }
372 case WIN_MUC:
373 {
374 ProfMucWin* mucwin = (ProfMucWin*)window;
375 return strdup(mucwin->roomjid);
376 }
377 case WIN_CONFIG:
378 {
379 ProfConfWin* confwin = (ProfConfWin*)window;
380 return strdup(confwin->roomjid);
381 }
382 case WIN_PRIVATE:
383 {
384 ProfPrivateWin* privwin = (ProfPrivateWin*)window;
385 return strdup(privwin->fulljid);
386 }
387 case WIN_PLUGIN:
388 {
389 ProfPluginWin* pluginwin = (ProfPluginWin*)window;
390 return strdup(pluginwin->tag);
391 }
392 case WIN_XML:
393 {
394 return strdup("xmlconsole");
395 }
396 default:
397 return strdup("UNKNOWN");
398 }
399 }
400
401 char*
win_to_string(ProfWin * window)402 win_to_string(ProfWin* window)
403 {
404 assert(window != NULL);
405
406 switch (window->type) {
407 case WIN_CONSOLE:
408 {
409 ProfConsoleWin* conswin = (ProfConsoleWin*)window;
410 return cons_get_string(conswin);
411 }
412 case WIN_CHAT:
413 {
414 ProfChatWin* chatwin = (ProfChatWin*)window;
415 return chatwin_get_string(chatwin);
416 }
417 case WIN_MUC:
418 {
419 ProfMucWin* mucwin = (ProfMucWin*)window;
420 return mucwin_get_string(mucwin);
421 }
422 case WIN_CONFIG:
423 {
424 ProfConfWin* confwin = (ProfConfWin*)window;
425 return confwin_get_string(confwin);
426 }
427 case WIN_PRIVATE:
428 {
429 ProfPrivateWin* privwin = (ProfPrivateWin*)window;
430 return privwin_get_string(privwin);
431 }
432 case WIN_XML:
433 {
434 ProfXMLWin* xmlwin = (ProfXMLWin*)window;
435 return xmlwin_get_string(xmlwin);
436 }
437 case WIN_PLUGIN:
438 {
439 ProfPluginWin* pluginwin = (ProfPluginWin*)window;
440 GString* gstring = g_string_new("");
441 g_string_append_printf(gstring, "Plugin: %s", pluginwin->tag);
442 char* res = gstring->str;
443 g_string_free(gstring, FALSE);
444 return res;
445 }
446 default:
447 return NULL;
448 }
449 }
450
451 void
win_hide_subwin(ProfWin * window)452 win_hide_subwin(ProfWin* window)
453 {
454 if (window->layout->type == LAYOUT_SPLIT) {
455 ProfLayoutSplit* layout = (ProfLayoutSplit*)window->layout;
456 if (layout->subwin) {
457 delwin(layout->subwin);
458 }
459 layout->subwin = NULL;
460 layout->sub_y_pos = 0;
461 int cols = getmaxx(stdscr);
462 wresize(layout->base.win, PAD_SIZE, cols);
463 win_redraw(window);
464 } else {
465 int cols = getmaxx(stdscr);
466 wresize(window->layout->win, PAD_SIZE, cols);
467 win_redraw(window);
468 }
469 }
470
471 void
win_show_subwin(ProfWin * window)472 win_show_subwin(ProfWin* window)
473 {
474 int cols = getmaxx(stdscr);
475 int subwin_cols = 0;
476
477 if (window->layout->type != LAYOUT_SPLIT) {
478 return;
479 }
480
481 if (window->type == WIN_MUC) {
482 subwin_cols = win_occpuants_cols();
483 } else if (window->type == WIN_CONSOLE) {
484 subwin_cols = win_roster_cols();
485 }
486
487 ProfLayoutSplit* layout = (ProfLayoutSplit*)window->layout;
488 layout->subwin = newpad(PAD_SIZE, subwin_cols);
489 wbkgd(layout->subwin, theme_attrs(THEME_TEXT));
490 wresize(layout->base.win, PAD_SIZE, cols - subwin_cols);
491 win_redraw(window);
492 }
493
494 void
win_free(ProfWin * window)495 win_free(ProfWin* window)
496 {
497 if (window->layout->type == LAYOUT_SPLIT) {
498 ProfLayoutSplit* layout = (ProfLayoutSplit*)window->layout;
499 if (layout->subwin) {
500 delwin(layout->subwin);
501 }
502 buffer_free(layout->base.buffer);
503 delwin(layout->base.win);
504 } else {
505 buffer_free(window->layout->buffer);
506 delwin(window->layout->win);
507 }
508 free(window->layout);
509
510 switch (window->type) {
511 case WIN_CHAT:
512 {
513 ProfChatWin* chatwin = (ProfChatWin*)window;
514 free(chatwin->barejid);
515 free(chatwin->resource_override);
516 free(chatwin->enctext);
517 free(chatwin->incoming_char);
518 free(chatwin->outgoing_char);
519 free(chatwin->last_message);
520 free(chatwin->last_msg_id);
521 chat_state_free(chatwin->state);
522 break;
523 }
524 case WIN_MUC:
525 {
526 ProfMucWin* mucwin = (ProfMucWin*)window;
527 free(mucwin->roomjid);
528 free(mucwin->room_name);
529 free(mucwin->enctext);
530 free(mucwin->message_char);
531 free(mucwin->last_message);
532 free(mucwin->last_msg_id);
533 break;
534 }
535 case WIN_CONFIG:
536 {
537 ProfConfWin* conf = (ProfConfWin*)window;
538 free(conf->roomjid);
539 form_destroy(conf->form);
540 break;
541 }
542 case WIN_PRIVATE:
543 {
544 ProfPrivateWin* privatewin = (ProfPrivateWin*)window;
545 free(privatewin->fulljid);
546 break;
547 }
548 case WIN_PLUGIN:
549 {
550 ProfPluginWin* pluginwin = (ProfPluginWin*)window;
551 free(pluginwin->tag);
552 free(pluginwin->plugin_name);
553 break;
554 }
555 default:
556 break;
557 }
558
559 free(window);
560 }
561
562 void
win_page_up(ProfWin * window)563 win_page_up(ProfWin* window)
564 {
565 int rows = getmaxy(stdscr);
566 int y = getcury(window->layout->win);
567 int page_space = rows - 4;
568 int* page_start = &(window->layout->y_pos);
569
570 *page_start -= page_space;
571
572 // went past beginning, show first page
573 if (*page_start < 0)
574 *page_start = 0;
575
576 window->layout->paged = 1;
577 win_update_virtual(window);
578
579 // switch off page if last line and space line visible
580 if ((y) - *page_start == page_space) {
581 window->layout->paged = 0;
582 }
583 }
584
585 void
win_page_down(ProfWin * window)586 win_page_down(ProfWin* window)
587 {
588 int rows = getmaxy(stdscr);
589 int y = getcury(window->layout->win);
590 int page_space = rows - 4;
591 int* page_start = &(window->layout->y_pos);
592
593 *page_start += page_space;
594
595 // only got half a screen, show full screen
596 if ((y - (*page_start)) < page_space)
597 *page_start = y - page_space;
598
599 // went past end, show full screen
600 else if (*page_start >= y)
601 *page_start = y - page_space - 1;
602
603 window->layout->paged = 1;
604 win_update_virtual(window);
605
606 // switch off page if last line and space line visible
607 if ((y) - *page_start == page_space) {
608 window->layout->paged = 0;
609 }
610 }
611
612 void
win_sub_page_down(ProfWin * window)613 win_sub_page_down(ProfWin* window)
614 {
615 if (window->layout->type == LAYOUT_SPLIT) {
616 int rows = getmaxy(stdscr);
617 int page_space = rows - 4;
618 ProfLayoutSplit* split_layout = (ProfLayoutSplit*)window->layout;
619 int sub_y = getcury(split_layout->subwin);
620 int* sub_y_pos = &(split_layout->sub_y_pos);
621
622 *sub_y_pos += page_space;
623
624 // only got half a screen, show full screen
625 if ((sub_y - (*sub_y_pos)) < page_space)
626 *sub_y_pos = sub_y - page_space;
627
628 // went past end, show full screen
629 else if (*sub_y_pos >= sub_y)
630 *sub_y_pos = sub_y - page_space - 1;
631
632 win_update_virtual(window);
633 }
634 }
635
636 void
win_sub_page_up(ProfWin * window)637 win_sub_page_up(ProfWin* window)
638 {
639 if (window->layout->type == LAYOUT_SPLIT) {
640 int rows = getmaxy(stdscr);
641 int page_space = rows - 4;
642 ProfLayoutSplit* split_layout = (ProfLayoutSplit*)window->layout;
643 int* sub_y_pos = &(split_layout->sub_y_pos);
644
645 *sub_y_pos -= page_space;
646
647 // went past beginning, show first page
648 if (*sub_y_pos < 0)
649 *sub_y_pos = 0;
650
651 win_update_virtual(window);
652 }
653 }
654
655 void
win_clear(ProfWin * window)656 win_clear(ProfWin* window)
657 {
658 if (!prefs_get_boolean(PREF_CLEAR_PERSIST_HISTORY)) {
659 werase(window->layout->win);
660 buffer_free(window->layout->buffer);
661 window->layout->buffer = buffer_create();
662 return;
663 }
664
665 int y = getcury(window->layout->win);
666 int* page_start = &(window->layout->y_pos);
667 *page_start = y;
668 window->layout->paged = 1;
669 win_update_virtual(window);
670 }
671
672 void
win_resize(ProfWin * window)673 win_resize(ProfWin* window)
674 {
675 int cols = getmaxx(stdscr);
676
677 if (window->layout->type == LAYOUT_SPLIT) {
678 ProfLayoutSplit* layout = (ProfLayoutSplit*)window->layout;
679 if (layout->subwin) {
680 int subwin_cols = 0;
681 if (window->type == WIN_CONSOLE) {
682 subwin_cols = win_roster_cols();
683 } else if (window->type == WIN_MUC) {
684 subwin_cols = win_occpuants_cols();
685 }
686 wbkgd(layout->base.win, theme_attrs(THEME_TEXT));
687 wresize(layout->base.win, PAD_SIZE, cols - subwin_cols);
688 wbkgd(layout->subwin, theme_attrs(THEME_TEXT));
689 wresize(layout->subwin, PAD_SIZE, subwin_cols);
690 if (window->type == WIN_CONSOLE) {
691 rosterwin_roster();
692 } else if (window->type == WIN_MUC) {
693 ProfMucWin* mucwin = (ProfMucWin*)window;
694 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
695 occupantswin_occupants(mucwin->roomjid);
696 }
697 } else {
698 wbkgd(layout->base.win, theme_attrs(THEME_TEXT));
699 wresize(layout->base.win, PAD_SIZE, cols);
700 }
701 } else {
702 wbkgd(window->layout->win, theme_attrs(THEME_TEXT));
703 wresize(window->layout->win, PAD_SIZE, cols);
704 }
705
706 win_redraw(window);
707 }
708
709 void
win_update_virtual(ProfWin * window)710 win_update_virtual(ProfWin* window)
711 {
712 int cols = getmaxx(stdscr);
713
714 int row_start = screen_mainwin_row_start();
715 int row_end = screen_mainwin_row_end();
716 if (window->layout->type == LAYOUT_SPLIT) {
717 ProfLayoutSplit* layout = (ProfLayoutSplit*)window->layout;
718 if (layout->subwin) {
719 int subwin_cols = 0;
720 if (window->type == WIN_MUC) {
721 subwin_cols = win_occpuants_cols();
722 } else {
723 subwin_cols = win_roster_cols();
724 }
725 pnoutrefresh(layout->base.win, layout->base.y_pos, 0, row_start, 0, row_end, (cols - subwin_cols) - 1);
726 pnoutrefresh(layout->subwin, layout->sub_y_pos, 0, row_start, (cols - subwin_cols), row_end, cols - 1);
727 } else {
728 pnoutrefresh(layout->base.win, layout->base.y_pos, 0, row_start, 0, row_end, cols - 1);
729 }
730 } else {
731 pnoutrefresh(window->layout->win, window->layout->y_pos, 0, row_start, 0, row_end, cols - 1);
732 }
733 }
734
735 void
win_refresh_without_subwin(ProfWin * window)736 win_refresh_without_subwin(ProfWin* window)
737 {
738 int cols = getmaxx(stdscr);
739
740 if ((window->type == WIN_MUC) || (window->type == WIN_CONSOLE)) {
741 int row_start = screen_mainwin_row_start();
742 int row_end = screen_mainwin_row_end();
743 pnoutrefresh(window->layout->win, window->layout->y_pos, 0, row_start, 0, row_end, cols - 1);
744 }
745 }
746
747 void
win_refresh_with_subwin(ProfWin * window)748 win_refresh_with_subwin(ProfWin* window)
749 {
750 int subwin_cols = 0;
751 int cols = getmaxx(stdscr);
752 int row_start = screen_mainwin_row_start();
753 int row_end = screen_mainwin_row_end();
754 ProfLayoutSplit* layout = (ProfLayoutSplit*)window->layout;
755
756 if (window->type == WIN_MUC) {
757 subwin_cols = win_occpuants_cols();
758 } else if (window->type == WIN_CONSOLE) {
759 subwin_cols = win_roster_cols();
760 } else {
761 // Other window types don't support subwindows, we shouldn't be here
762 return;
763 }
764
765 pnoutrefresh(layout->base.win, layout->base.y_pos, 0, row_start, 0, row_end, (cols - subwin_cols) - 1);
766 pnoutrefresh(layout->subwin, layout->sub_y_pos, 0, row_start, (cols - subwin_cols), row_end, cols - 1);
767 }
768
769 void
win_move_to_end(ProfWin * window)770 win_move_to_end(ProfWin* window)
771 {
772 window->layout->paged = 0;
773
774 int rows = getmaxy(stdscr);
775 int y = getcury(window->layout->win);
776 int size = rows - 3;
777
778 window->layout->y_pos = y - (size - 1);
779 if (window->layout->y_pos < 0) {
780 window->layout->y_pos = 0;
781 }
782 }
783
784 void
win_show_occupant(ProfWin * window,Occupant * occupant)785 win_show_occupant(ProfWin* window, Occupant* occupant)
786 {
787 const char* presence_str = string_from_resource_presence(occupant->presence);
788
789 theme_item_t presence_colour = theme_main_presence_attrs(presence_str);
790
791 win_print(window, presence_colour, "-", "%s", occupant->nick);
792 win_append(window, presence_colour, " is %s", presence_str);
793
794 if (occupant->status) {
795 win_append(window, presence_colour, ", \"%s\"", occupant->status);
796 }
797
798 win_appendln(window, presence_colour, "");
799 }
800
801 void
win_show_contact(ProfWin * window,PContact contact)802 win_show_contact(ProfWin* window, PContact contact)
803 {
804 const char* barejid = p_contact_barejid(contact);
805 const char* name = p_contact_name(contact);
806 const char* presence = p_contact_presence(contact);
807 const char* status = p_contact_status(contact);
808 GDateTime* last_activity = p_contact_last_activity(contact);
809
810 theme_item_t presence_colour = theme_main_presence_attrs(presence);
811
812 if (name) {
813 win_print(window, presence_colour, "-", "%s", name);
814 } else {
815 win_print(window, presence_colour, "-", "%s", barejid);
816 }
817
818 win_append(window, presence_colour, " is %s", presence);
819
820 if (last_activity) {
821 GDateTime* now = g_date_time_new_now_local();
822 GTimeSpan span = g_date_time_difference(now, last_activity);
823 g_date_time_unref(now);
824
825 int hours = span / G_TIME_SPAN_HOUR;
826 span = span - hours * G_TIME_SPAN_HOUR;
827 int minutes = span / G_TIME_SPAN_MINUTE;
828 span = span - minutes * G_TIME_SPAN_MINUTE;
829 int seconds = span / G_TIME_SPAN_SECOND;
830
831 if (hours > 0) {
832 win_append(window, presence_colour, ", idle %dh%dm%ds", hours, minutes, seconds);
833 } else {
834 win_append(window, presence_colour, ", idle %dm%ds", minutes, seconds);
835 }
836 }
837
838 if (status) {
839 win_append(window, presence_colour, ", \"%s\"", p_contact_status(contact));
840 }
841
842 win_appendln(window, presence_colour, "");
843 }
844
845 void
win_show_occupant_info(ProfWin * window,const char * const room,Occupant * occupant)846 win_show_occupant_info(ProfWin* window, const char* const room, Occupant* occupant)
847 {
848 const char* presence_str = string_from_resource_presence(occupant->presence);
849 const char* occupant_affiliation = muc_occupant_affiliation_str(occupant);
850 const char* occupant_role = muc_occupant_role_str(occupant);
851
852 theme_item_t presence_colour = theme_main_presence_attrs(presence_str);
853
854 win_print(window, presence_colour, "!", "%s", occupant->nick);
855 win_append(window, presence_colour, " is %s", presence_str);
856
857 if (occupant->status) {
858 win_append(window, presence_colour, ", \"%s\"", occupant->status);
859 }
860
861 win_newline(window);
862
863 if (occupant->jid) {
864 win_println(window, THEME_DEFAULT, "!", " Jid: %s", occupant->jid);
865 }
866
867 win_println(window, THEME_DEFAULT, "!", " Affiliation: %s", occupant_affiliation);
868 win_println(window, THEME_DEFAULT, "!", " Role: %s", occupant_role);
869
870 Jid* jidp = jid_create_from_bare_and_resource(room, occupant->nick);
871 EntityCapabilities* caps = caps_lookup(jidp->fulljid);
872 jid_destroy(jidp);
873
874 if (caps) {
875 // show identity
876 if (caps->identity) {
877 DiscoIdentity* identity = caps->identity;
878 win_print(window, THEME_DEFAULT, "!", " Identity: ");
879 if (identity->name) {
880 win_append(window, THEME_DEFAULT, "%s", identity->name);
881 if (identity->category || identity->type) {
882 win_append(window, THEME_DEFAULT, " ");
883 }
884 }
885 if (identity->type) {
886 win_append(window, THEME_DEFAULT, "%s", identity->type);
887 if (identity->category) {
888 win_append(window, THEME_DEFAULT, " ");
889 }
890 }
891 if (identity->category) {
892 win_append(window, THEME_DEFAULT, "%s", identity->category);
893 }
894 win_newline(window);
895 }
896
897 if (caps->software_version) {
898 SoftwareVersion* software_version = caps->software_version;
899 if (software_version->software) {
900 win_print(window, THEME_DEFAULT, "!", " Software: %s", software_version->software);
901 }
902 if (software_version->software_version) {
903 win_append(window, THEME_DEFAULT, ", %s", software_version->software_version);
904 }
905 if (software_version->software || software_version->software_version) {
906 win_newline(window);
907 }
908 if (software_version->os) {
909 win_print(window, THEME_DEFAULT, "!", " OS: %s", software_version->os);
910 }
911 if (software_version->os_version) {
912 win_append(window, THEME_DEFAULT, ", %s", software_version->os_version);
913 }
914 if (software_version->os || software_version->os_version) {
915 win_newline(window);
916 }
917 }
918
919 caps_destroy(caps);
920 }
921
922 win_println(window, THEME_DEFAULT, "-", "");
923 }
924
925 void
win_show_info(ProfWin * window,PContact contact)926 win_show_info(ProfWin* window, PContact contact)
927 {
928 const char* barejid = p_contact_barejid(contact);
929 const char* name = p_contact_name(contact);
930 const char* presence = p_contact_presence(contact);
931 const char* sub = p_contact_subscription(contact);
932 GDateTime* last_activity = p_contact_last_activity(contact);
933
934 theme_item_t presence_colour = theme_main_presence_attrs(presence);
935
936 win_println(window, THEME_DEFAULT, "-", "");
937 win_print(window, presence_colour, "-", "%s", barejid);
938 if (name) {
939 win_append(window, presence_colour, " (%s)", name);
940 }
941 win_appendln(window, THEME_DEFAULT, ":");
942
943 if (sub) {
944 win_println(window, THEME_DEFAULT, "-", "Subscription: %s", sub);
945 }
946
947 if (last_activity) {
948 GDateTime* now = g_date_time_new_now_local();
949 GTimeSpan span = g_date_time_difference(now, last_activity);
950
951 int hours = span / G_TIME_SPAN_HOUR;
952 span = span - hours * G_TIME_SPAN_HOUR;
953 int minutes = span / G_TIME_SPAN_MINUTE;
954 span = span - minutes * G_TIME_SPAN_MINUTE;
955 int seconds = span / G_TIME_SPAN_SECOND;
956
957 if (hours > 0) {
958 win_println(window, THEME_DEFAULT, "-", "Last activity: %dh%dm%ds", hours, minutes, seconds);
959 } else {
960 win_println(window, THEME_DEFAULT, "-", "Last activity: %dm%ds", minutes, seconds);
961 }
962
963 g_date_time_unref(now);
964 }
965
966 GList* resources = p_contact_get_available_resources(contact);
967 GList* ordered_resources = NULL;
968 if (resources) {
969 win_println(window, THEME_DEFAULT, "-", "Resources:");
970
971 // sort in order of availability
972 GList* curr = resources;
973 while (curr) {
974 Resource* resource = curr->data;
975 ordered_resources = g_list_insert_sorted(ordered_resources,
976 resource, (GCompareFunc)resource_compare_availability);
977 curr = g_list_next(curr);
978 }
979 }
980 g_list_free(resources);
981
982 GList* curr = ordered_resources;
983 while (curr) {
984 Resource* resource = curr->data;
985 const char* resource_presence = string_from_resource_presence(resource->presence);
986 theme_item_t presence_colour = theme_main_presence_attrs(resource_presence);
987 win_print(window, presence_colour, "-", " %s (%d), %s", resource->name, resource->priority, resource_presence);
988 if (resource->status) {
989 win_append(window, presence_colour, ", \"%s\"", resource->status);
990 }
991 win_newline(window);
992
993 Jid* jidp = jid_create_from_bare_and_resource(barejid, resource->name);
994 EntityCapabilities* caps = caps_lookup(jidp->fulljid);
995 jid_destroy(jidp);
996
997 if (caps) {
998 // show identity
999 if (caps->identity) {
1000 DiscoIdentity* identity = caps->identity;
1001 win_print(window, THEME_DEFAULT, "-", " Identity: ");
1002 if (identity->name) {
1003 win_append(window, THEME_DEFAULT, "%s", identity->name);
1004 if (identity->category || identity->type) {
1005 win_append(window, THEME_DEFAULT, " ");
1006 }
1007 }
1008 if (identity->type) {
1009 win_append(window, THEME_DEFAULT, "%s", identity->type);
1010 if (identity->category) {
1011 win_append(window, THEME_DEFAULT, " ");
1012 }
1013 }
1014 if (identity->category) {
1015 win_append(window, THEME_DEFAULT, "%s", identity->category);
1016 }
1017 win_newline(window);
1018 }
1019
1020 if (caps->software_version) {
1021 SoftwareVersion* software_version = caps->software_version;
1022 if (software_version->software) {
1023 win_print(window, THEME_DEFAULT, "-", " Software: %s", software_version->software);
1024 }
1025 if (software_version->software_version) {
1026 win_append(window, THEME_DEFAULT, ", %s", software_version->software_version);
1027 }
1028 if (software_version->software || software_version->software_version) {
1029 win_newline(window);
1030 }
1031 if (software_version->os) {
1032 win_print(window, THEME_DEFAULT, "-", " OS: %s", software_version->os);
1033 }
1034 if (software_version->os_version) {
1035 win_append(window, THEME_DEFAULT, ", %s", software_version->os_version);
1036 }
1037 if (software_version->os || software_version->os_version) {
1038 win_newline(window);
1039 }
1040 }
1041
1042 caps_destroy(caps);
1043 }
1044
1045 curr = g_list_next(curr);
1046 }
1047 g_list_free(ordered_resources);
1048 }
1049
1050 void
win_show_status_string(ProfWin * window,const char * const from,const char * const show,const char * const status,GDateTime * last_activity,const char * const pre,const char * const default_show)1051 win_show_status_string(ProfWin* window, const char* const from,
1052 const char* const show, const char* const status,
1053 GDateTime* last_activity, const char* const pre,
1054 const char* const default_show)
1055 {
1056 theme_item_t presence_colour;
1057
1058 if (show) {
1059 presence_colour = theme_main_presence_attrs(show);
1060 } else if (strcmp(default_show, "online") == 0) {
1061 presence_colour = THEME_ONLINE;
1062 } else {
1063 presence_colour = THEME_OFFLINE;
1064 }
1065
1066 win_print(window, presence_colour, "-", "%s %s", pre, from);
1067
1068 if (show)
1069 win_append(window, presence_colour, " is %s", show);
1070 else
1071 win_append(window, presence_colour, " is %s", default_show);
1072
1073 if (last_activity) {
1074 gchar* date_fmt = NULL;
1075 char* time_pref = prefs_get_string(PREF_TIME_LASTACTIVITY);
1076 date_fmt = g_date_time_format(last_activity, time_pref);
1077 g_free(time_pref);
1078 assert(date_fmt != NULL);
1079
1080 win_append(window, presence_colour, ", last activity: %s", date_fmt);
1081
1082 g_free(date_fmt);
1083 }
1084
1085 if (status)
1086 win_append(window, presence_colour, ", \"%s\"", status);
1087
1088 win_appendln(window, presence_colour, "");
1089 }
1090
1091 static void
_win_correct(ProfWin * window,const char * const message,const char * const id,const char * const replace_id,const char * const from_jid)1092 _win_correct(ProfWin* window, const char* const message, const char* const id, const char* const replace_id, const char* const from_jid)
1093 {
1094 ProfBuffEntry* entry = buffer_get_entry_by_id(window->layout->buffer, replace_id);
1095 if (!entry) {
1096 log_debug("Replace ID %s could not be found in buffer. Message: %s", replace_id, message);
1097 return;
1098 }
1099
1100 if (g_strcmp0(entry->from_jid, from_jid) != 0) {
1101 log_debug("Illicit LMC attempt from %s for message from %s with: %s", from_jid, entry->from_jid, message);
1102 cons_show("Illicit LMC attempt from %s for message from %s", from_jid, entry->from_jid);
1103 return;
1104 }
1105
1106 /*TODO: set date?
1107 if (entry->date) {
1108 if (entry->date->timestamp) {
1109 g_date_time_unref(entry->date->timestamp);
1110 }
1111 free(entry->date);
1112 }
1113
1114 entry->date = buffer_date_new_now();
1115 */
1116
1117 free(entry->show_char);
1118 entry->show_char = prefs_get_correction_char();
1119
1120 if (entry->message) {
1121 free(entry->message);
1122 }
1123 entry->message = strdup(message);
1124
1125 if (entry->id) {
1126 free(entry->id);
1127 }
1128 entry->id = strdup(id);
1129
1130 win_redraw(window);
1131 }
1132
1133 void
win_print_incoming(ProfWin * window,const char * const display_name_from,ProfMessage * message)1134 win_print_incoming(ProfWin* window, const char* const display_name_from, ProfMessage* message)
1135 {
1136 int flags = NO_ME;
1137
1138 if (!message->trusted) {
1139 flags |= UNTRUSTED;
1140 }
1141
1142 switch (window->type) {
1143 case WIN_CHAT:
1144 {
1145 char* enc_char;
1146 ProfChatWin* chatwin = (ProfChatWin*)window;
1147
1148 if (chatwin->incoming_char) {
1149 enc_char = strdup(chatwin->incoming_char);
1150 } else if (message->enc == PROF_MSG_ENC_OTR) {
1151 enc_char = prefs_get_otr_char();
1152 } else if (message->enc == PROF_MSG_ENC_PGP) {
1153 enc_char = prefs_get_pgp_char();
1154 } else if (message->enc == PROF_MSG_ENC_OX) { // XEP-0373: OpenPGP for XMPP
1155 enc_char = prefs_get_ox_char();
1156 } else if (message->enc == PROF_MSG_ENC_OMEMO) {
1157 enc_char = prefs_get_omemo_char();
1158 } else {
1159 enc_char = strdup("-");
1160 }
1161
1162 if (prefs_get_boolean(PREF_CORRECTION_ALLOW) && message->replace_id) {
1163 _win_correct(window, message->plain, message->id, message->replace_id, message->from_jid->barejid);
1164 } else {
1165 _win_printf(window, enc_char, 0, message->timestamp, flags, THEME_TEXT_THEM, display_name_from, message->from_jid->barejid, message->id, "%s", message->plain);
1166 }
1167
1168 free(enc_char);
1169 break;
1170 }
1171 case WIN_PRIVATE:
1172 _win_printf(window, "-", 0, message->timestamp, flags, THEME_TEXT_THEM, display_name_from, message->from_jid->barejid, message->id, "%s", message->plain);
1173 break;
1174 default:
1175 assert(FALSE);
1176 break;
1177 }
1178 }
1179
1180 void
win_print_them(ProfWin * window,theme_item_t theme_item,const char * const show_char,int flags,const char * const them)1181 win_print_them(ProfWin* window, theme_item_t theme_item, const char* const show_char, int flags, const char* const them)
1182 {
1183 _win_printf(window, show_char, 0, NULL, flags | NO_ME | NO_EOL, theme_item, them, NULL, NULL, "");
1184 }
1185
1186 void
win_println_incoming_muc_msg(ProfWin * window,char * show_char,int flags,const ProfMessage * const message)1187 win_println_incoming_muc_msg(ProfWin* window, char* show_char, int flags, const ProfMessage* const message)
1188 {
1189 if (prefs_get_boolean(PREF_CORRECTION_ALLOW) && message->replace_id) {
1190 _win_correct(window, message->plain, message->id, message->replace_id, message->from_jid->fulljid);
1191 } else {
1192 _win_printf(window, show_char, 0, message->timestamp, flags | NO_ME, THEME_TEXT_THEM, message->from_jid->resourcepart, message->from_jid->fulljid, message->id, "%s", message->plain);
1193 }
1194
1195 inp_nonblocking(TRUE);
1196 }
1197
1198 void
win_print_outgoing_muc_msg(ProfWin * window,char * show_char,const char * const me,const char * const id,const char * const replace_id,const char * const message)1199 win_print_outgoing_muc_msg(ProfWin* window, char* show_char, const char* const me, const char* const id, const char* const replace_id, const char* const message)
1200 {
1201 GDateTime* timestamp = g_date_time_new_now_local();
1202
1203 if (prefs_get_boolean(PREF_CORRECTION_ALLOW) && replace_id) {
1204 _win_correct(window, message, id, replace_id, me);
1205 } else {
1206 _win_printf(window, show_char, 0, timestamp, 0, THEME_TEXT_ME, me, me, id, "%s", message);
1207 }
1208
1209 inp_nonblocking(TRUE);
1210 g_date_time_unref(timestamp);
1211 }
1212
1213 void
win_print_outgoing(ProfWin * window,const char * show_char,const char * const id,const char * const replace_id,const char * const message)1214 win_print_outgoing(ProfWin* window, const char* show_char, const char* const id, const char* const replace_id, const char* const message)
1215 {
1216 GDateTime* timestamp = g_date_time_new_now_local();
1217
1218 const char* myjid = connection_get_fulljid();
1219 if (replace_id) {
1220 _win_correct(window, message, id, replace_id, myjid);
1221 } else {
1222 _win_printf(window, show_char, 0, timestamp, 0, THEME_TEXT_ME, "me", myjid, id, "%s", message);
1223 }
1224
1225 inp_nonblocking(TRUE);
1226 g_date_time_unref(timestamp);
1227 }
1228
1229 void
win_print_history(ProfWin * window,const ProfMessage * const message)1230 win_print_history(ProfWin* window, const ProfMessage* const message)
1231 {
1232 g_date_time_ref(message->timestamp);
1233
1234 char* display_name;
1235 int flags = 0;
1236 const char* jid = connection_get_fulljid();
1237 Jid* jidp = jid_create(jid);
1238
1239 if (g_strcmp0(jidp->barejid, message->from_jid->barejid) == 0) {
1240 display_name = strdup("me");
1241 } else {
1242 display_name = roster_get_msg_display_name(message->from_jid->barejid, message->from_jid->resourcepart);
1243 }
1244
1245 jid_destroy(jidp);
1246
1247 buffer_append(window->layout->buffer, "-", 0, message->timestamp, flags, THEME_TEXT_HISTORY, display_name, NULL, message->plain, NULL, NULL);
1248 _win_print_internal(window, "-", 0, message->timestamp, flags, THEME_TEXT_HISTORY, display_name, message->plain, NULL);
1249
1250 free(display_name);
1251
1252 inp_nonblocking(TRUE);
1253 g_date_time_unref(message->timestamp);
1254 }
1255
1256 void
win_print(ProfWin * window,theme_item_t theme_item,const char * show_char,const char * const message,...)1257 win_print(ProfWin* window, theme_item_t theme_item, const char* show_char, const char* const message, ...)
1258 {
1259 GDateTime* timestamp = g_date_time_new_now_local();
1260
1261 va_list arg;
1262 va_start(arg, message);
1263 GString* fmt_msg = g_string_new(NULL);
1264 g_string_vprintf(fmt_msg, message, arg);
1265
1266 buffer_append(window->layout->buffer, show_char, 0, timestamp, NO_EOL, theme_item, "", NULL, fmt_msg->str, NULL, NULL);
1267 _win_print_internal(window, show_char, 0, timestamp, NO_EOL, theme_item, "", fmt_msg->str, NULL);
1268
1269 inp_nonblocking(TRUE);
1270 g_date_time_unref(timestamp);
1271
1272 g_string_free(fmt_msg, TRUE);
1273 va_end(arg);
1274 }
1275
1276 void
win_println(ProfWin * window,theme_item_t theme_item,const char * show_char,const char * const message,...)1277 win_println(ProfWin* window, theme_item_t theme_item, const char* show_char, const char* const message, ...)
1278 {
1279 GDateTime* timestamp = g_date_time_new_now_local();
1280
1281 va_list arg;
1282 va_start(arg, message);
1283 GString* fmt_msg = g_string_new(NULL);
1284 g_string_vprintf(fmt_msg, message, arg);
1285
1286 buffer_append(window->layout->buffer, show_char, 0, timestamp, 0, theme_item, "", NULL, fmt_msg->str, NULL, NULL);
1287 _win_print_internal(window, show_char, 0, timestamp, 0, theme_item, "", fmt_msg->str, NULL);
1288
1289 inp_nonblocking(TRUE);
1290 g_date_time_unref(timestamp);
1291
1292 g_string_free(fmt_msg, TRUE);
1293 va_end(arg);
1294 }
1295
1296 void
win_println_indent(ProfWin * window,int pad,const char * const message,...)1297 win_println_indent(ProfWin* window, int pad, const char* const message, ...)
1298 {
1299 GDateTime* timestamp = g_date_time_new_now_local();
1300
1301 va_list arg;
1302 va_start(arg, message);
1303 GString* fmt_msg = g_string_new(NULL);
1304 g_string_vprintf(fmt_msg, message, arg);
1305
1306 buffer_append(window->layout->buffer, "-", pad, timestamp, 0, THEME_DEFAULT, "", NULL, fmt_msg->str, NULL, NULL);
1307 _win_print_internal(window, "-", pad, timestamp, 0, THEME_DEFAULT, "", fmt_msg->str, NULL);
1308
1309 inp_nonblocking(TRUE);
1310 g_date_time_unref(timestamp);
1311
1312 g_string_free(fmt_msg, TRUE);
1313 va_end(arg);
1314 }
1315
1316 void
win_append(ProfWin * window,theme_item_t theme_item,const char * const message,...)1317 win_append(ProfWin* window, theme_item_t theme_item, const char* const message, ...)
1318 {
1319 GDateTime* timestamp = g_date_time_new_now_local();
1320
1321 va_list arg;
1322 va_start(arg, message);
1323 GString* fmt_msg = g_string_new(NULL);
1324 g_string_vprintf(fmt_msg, message, arg);
1325
1326 buffer_append(window->layout->buffer, "-", 0, timestamp, NO_DATE | NO_EOL, theme_item, "", NULL, fmt_msg->str, NULL, NULL);
1327 _win_print_internal(window, "-", 0, timestamp, NO_DATE | NO_EOL, theme_item, "", fmt_msg->str, NULL);
1328
1329 inp_nonblocking(TRUE);
1330 g_date_time_unref(timestamp);
1331
1332 g_string_free(fmt_msg, TRUE);
1333 va_end(arg);
1334 }
1335
1336 void
win_appendln(ProfWin * window,theme_item_t theme_item,const char * const message,...)1337 win_appendln(ProfWin* window, theme_item_t theme_item, const char* const message, ...)
1338 {
1339 GDateTime* timestamp = g_date_time_new_now_local();
1340
1341 va_list arg;
1342 va_start(arg, message);
1343 GString* fmt_msg = g_string_new(NULL);
1344 g_string_vprintf(fmt_msg, message, arg);
1345
1346 buffer_append(window->layout->buffer, "-", 0, timestamp, NO_DATE, theme_item, "", NULL, fmt_msg->str, NULL, NULL);
1347 _win_print_internal(window, "-", 0, timestamp, NO_DATE, theme_item, "", fmt_msg->str, NULL);
1348
1349 inp_nonblocking(TRUE);
1350 g_date_time_unref(timestamp);
1351
1352 g_string_free(fmt_msg, TRUE);
1353 va_end(arg);
1354 }
1355
1356 void
win_append_highlight(ProfWin * window,theme_item_t theme_item,const char * const message,...)1357 win_append_highlight(ProfWin* window, theme_item_t theme_item, const char* const message, ...)
1358 {
1359 GDateTime* timestamp = g_date_time_new_now_local();
1360
1361 va_list arg;
1362 va_start(arg, message);
1363 GString* fmt_msg = g_string_new(NULL);
1364 g_string_vprintf(fmt_msg, message, arg);
1365
1366 buffer_append(window->layout->buffer, "-", 0, timestamp, NO_DATE | NO_ME | NO_EOL, theme_item, "", NULL, fmt_msg->str, NULL, NULL);
1367 _win_print_internal(window, "-", 0, timestamp, NO_DATE | NO_ME | NO_EOL, theme_item, "", fmt_msg->str, NULL);
1368
1369 inp_nonblocking(TRUE);
1370 g_date_time_unref(timestamp);
1371
1372 g_string_free(fmt_msg, TRUE);
1373 va_end(arg);
1374 }
1375
1376 void
win_appendln_highlight(ProfWin * window,theme_item_t theme_item,const char * const message,...)1377 win_appendln_highlight(ProfWin* window, theme_item_t theme_item, const char* const message, ...)
1378 {
1379 GDateTime* timestamp = g_date_time_new_now_local();
1380
1381 va_list arg;
1382 va_start(arg, message);
1383 GString* fmt_msg = g_string_new(NULL);
1384 g_string_vprintf(fmt_msg, message, arg);
1385
1386 buffer_append(window->layout->buffer, "-", 0, timestamp, NO_DATE | NO_ME, theme_item, "", NULL, fmt_msg->str, NULL, NULL);
1387 _win_print_internal(window, "-", 0, timestamp, NO_DATE | NO_ME, theme_item, "", fmt_msg->str, NULL);
1388
1389 inp_nonblocking(TRUE);
1390 g_date_time_unref(timestamp);
1391
1392 g_string_free(fmt_msg, TRUE);
1393 va_end(arg);
1394 }
1395
1396 void
win_print_http_transfer(ProfWin * window,const char * const message,char * url)1397 win_print_http_transfer(ProfWin* window, const char* const message, char* url)
1398 {
1399 win_print_outgoing_with_receipt(window, "!", NULL, message, url, NULL);
1400 }
1401
1402 void
win_print_outgoing_with_receipt(ProfWin * window,const char * show_char,const char * const from,const char * const message,char * id,const char * const replace_id)1403 win_print_outgoing_with_receipt(ProfWin* window, const char* show_char, const char* const from, const char* const message, char* id, const char* const replace_id)
1404 {
1405 GDateTime* time = g_date_time_new_now_local();
1406
1407 DeliveryReceipt* receipt = malloc(sizeof(struct delivery_receipt_t));
1408 receipt->received = FALSE;
1409
1410 const char* myjid = connection_get_fulljid();
1411 if (replace_id) {
1412 _win_correct(window, message, id, replace_id, myjid);
1413 free(receipt); //TODO: probably we should use this in _win_correct()
1414 } else {
1415 buffer_append(window->layout->buffer, show_char, 0, time, 0, THEME_TEXT_ME, from, myjid, message, receipt, id);
1416 _win_print_internal(window, show_char, 0, time, 0, THEME_TEXT_ME, from, message, receipt);
1417 }
1418
1419 // TODO: cross-reference.. this should be replaced by a real event-based system
1420 inp_nonblocking(TRUE);
1421 g_date_time_unref(time);
1422 }
1423
1424 void
win_mark_received(ProfWin * window,const char * const id)1425 win_mark_received(ProfWin* window, const char* const id)
1426 {
1427 gboolean received = buffer_mark_received(window->layout->buffer, id);
1428 if (received) {
1429 win_redraw(window);
1430 }
1431 }
1432
1433 void
win_update_entry_message(ProfWin * window,const char * const id,const char * const message)1434 win_update_entry_message(ProfWin* window, const char* const id, const char* const message)
1435 {
1436 ProfBuffEntry* entry = buffer_get_entry_by_id(window->layout->buffer, id);
1437 if (entry) {
1438 free(entry->message);
1439 entry->message = strdup(message);
1440 win_redraw(window);
1441 }
1442 }
1443
1444 void
win_remove_entry_message(ProfWin * window,const char * const id)1445 win_remove_entry_message(ProfWin* window, const char* const id)
1446 {
1447 buffer_remove_entry_by_id(window->layout->buffer, id);
1448 win_redraw(window);
1449 }
1450
1451 void
win_newline(ProfWin * window)1452 win_newline(ProfWin* window)
1453 {
1454 win_appendln(window, THEME_DEFAULT, "");
1455 }
1456
1457 static void
_win_printf(ProfWin * window,const char * show_char,int pad_indent,GDateTime * timestamp,int flags,theme_item_t theme_item,const char * const display_from,const char * const from_jid,const char * const message_id,const char * const message,...)1458 _win_printf(ProfWin* window, const char* show_char, int pad_indent, GDateTime* timestamp, int flags, theme_item_t theme_item, const char* const display_from, const char* const from_jid, const char* const message_id, const char* const message, ...)
1459 {
1460 if (timestamp == NULL) {
1461 timestamp = g_date_time_new_now_local();
1462 } else {
1463 g_date_time_ref(timestamp);
1464 }
1465
1466 va_list arg;
1467 va_start(arg, message);
1468 GString* fmt_msg = g_string_new(NULL);
1469 g_string_vprintf(fmt_msg, message, arg);
1470
1471 buffer_append(window->layout->buffer, show_char, pad_indent, timestamp, flags, theme_item, display_from, from_jid, fmt_msg->str, NULL, message_id);
1472
1473 _win_print_internal(window, show_char, pad_indent, timestamp, flags, theme_item, display_from, fmt_msg->str, NULL);
1474
1475 inp_nonblocking(TRUE);
1476 g_date_time_unref(timestamp);
1477
1478 g_string_free(fmt_msg, TRUE);
1479 va_end(arg);
1480 }
1481
1482 static void
_win_print_internal(ProfWin * window,const char * show_char,int pad_indent,GDateTime * time,int flags,theme_item_t theme_item,const char * const from,const char * const message,DeliveryReceipt * receipt)1483 _win_print_internal(ProfWin* window, const char* show_char, int pad_indent, GDateTime* time,
1484 int flags, theme_item_t theme_item, const char* const from, const char* const message, DeliveryReceipt* receipt)
1485 {
1486 // flags : 1st bit = 0/1 - me/not me. define: NO_ME
1487 // 2nd bit = 0/1 - date/no date. define: NO_DATE
1488 // 3rd bit = 0/1 - eol/no eol. define: NO_EOL
1489 // 4th bit = 0/1 - color from/no color from. define: NO_COLOUR_FROM
1490 // 5th bit = 0/1 - color date/no date. define: NO_COLOUR_DATE
1491 // 6th bit = 0/1 - trusted/untrusted. define: UNTRUSTED
1492 gboolean me_message = FALSE;
1493 int offset = 0;
1494 int colour = theme_attrs(THEME_ME);
1495 size_t indent = 0;
1496
1497 char* time_pref = NULL;
1498 switch (window->type) {
1499 case WIN_CHAT:
1500 time_pref = prefs_get_string(PREF_TIME_CHAT);
1501 break;
1502 case WIN_MUC:
1503 time_pref = prefs_get_string(PREF_TIME_MUC);
1504 break;
1505 case WIN_CONFIG:
1506 time_pref = prefs_get_string(PREF_TIME_CONFIG);
1507 break;
1508 case WIN_PRIVATE:
1509 time_pref = prefs_get_string(PREF_TIME_PRIVATE);
1510 break;
1511 case WIN_XML:
1512 time_pref = prefs_get_string(PREF_TIME_XMLCONSOLE);
1513 break;
1514 default:
1515 time_pref = prefs_get_string(PREF_TIME_CONSOLE);
1516 break;
1517 }
1518
1519 gchar* date_fmt = NULL;
1520 if (g_strcmp0(time_pref, "off") == 0 || time == NULL) {
1521 date_fmt = g_strdup("");
1522 } else {
1523 date_fmt = g_date_time_format(time, time_pref);
1524 }
1525 g_free(time_pref);
1526 assert(date_fmt != NULL);
1527
1528 if (strlen(date_fmt) != 0) {
1529 indent = 3 + strlen(date_fmt);
1530 }
1531
1532 if ((flags & NO_DATE) == 0) {
1533 if (date_fmt && strlen(date_fmt)) {
1534 if ((flags & NO_COLOUR_DATE) == 0) {
1535 wbkgdset(window->layout->win, theme_attrs(THEME_TIME));
1536 wattron(window->layout->win, theme_attrs(THEME_TIME));
1537 }
1538 wprintw(window->layout->win, "%s %s ", date_fmt, show_char);
1539 if ((flags & NO_COLOUR_DATE) == 0) {
1540 wattroff(window->layout->win, theme_attrs(THEME_TIME));
1541 }
1542 }
1543 }
1544
1545 if (from && strlen(from) > 0) {
1546 if (flags & NO_ME) {
1547 colour = theme_attrs(THEME_THEM);
1548 }
1549
1550 char* color_pref = prefs_get_string(PREF_COLOR_NICK);
1551 if (color_pref != NULL && (strcmp(color_pref, "false") != 0)) {
1552 if (flags & NO_ME || (!(flags & NO_ME) && prefs_get_boolean(PREF_COLOR_NICK_OWN))) {
1553 colour = theme_hash_attrs(from);
1554 }
1555 }
1556 g_free(color_pref);
1557
1558 if (flags & NO_COLOUR_FROM) {
1559 colour = 0;
1560 }
1561
1562 if (receipt && !receipt->received) {
1563 colour = theme_attrs(THEME_RECEIPT_SENT);
1564 }
1565
1566 wbkgdset(window->layout->win, colour);
1567 wattron(window->layout->win, colour);
1568 if (strncmp(message, "/me ", 4) == 0) {
1569 wprintw(window->layout->win, "*%s ", from);
1570 offset = 4;
1571 me_message = TRUE;
1572 } else {
1573 wprintw(window->layout->win, "%s: ", from);
1574 wattroff(window->layout->win, colour);
1575 }
1576 }
1577
1578 if (!me_message) {
1579 if (receipt && !receipt->received) {
1580 wbkgdset(window->layout->win, theme_attrs(THEME_RECEIPT_SENT));
1581 wattron(window->layout->win, theme_attrs(THEME_RECEIPT_SENT));
1582 } else if (flags & UNTRUSTED) {
1583 wbkgdset(window->layout->win, theme_attrs(THEME_UNTRUSTED));
1584 wattron(window->layout->win, theme_attrs(THEME_UNTRUSTED));
1585 } else {
1586 wbkgdset(window->layout->win, theme_attrs(theme_item));
1587 wattron(window->layout->win, theme_attrs(theme_item));
1588 }
1589 }
1590
1591 if (prefs_get_boolean(PREF_WRAP)) {
1592 _win_print_wrapped(window->layout->win, message + offset, indent, pad_indent);
1593 } else {
1594 wprintw(window->layout->win, "%s", message + offset);
1595 }
1596
1597 if ((flags & NO_EOL) == 0) {
1598 int curx = getcurx(window->layout->win);
1599 if (curx != 0) {
1600 wprintw(window->layout->win, "\n");
1601 }
1602 }
1603
1604 if (me_message) {
1605 wattroff(window->layout->win, colour);
1606 } else {
1607 if (receipt && !receipt->received) {
1608 wattroff(window->layout->win, theme_attrs(THEME_RECEIPT_SENT));
1609 } else {
1610 wattroff(window->layout->win, theme_attrs(theme_item));
1611 }
1612 }
1613
1614 g_free(date_fmt);
1615 }
1616
1617 static void
_win_indent(WINDOW * win,int size)1618 _win_indent(WINDOW* win, int size)
1619 {
1620 for (int i = 0; i < size; i++) {
1621 waddch(win, ' ');
1622 }
1623 }
1624
1625 static void
_win_print_wrapped(WINDOW * win,const char * const message,size_t indent,int pad_indent)1626 _win_print_wrapped(WINDOW* win, const char* const message, size_t indent, int pad_indent)
1627 {
1628 int starty = getcury(win);
1629 int wordi = 0;
1630 char* word = malloc(strlen(message) + 1);
1631
1632 gchar* curr_ch = g_utf8_offset_to_pointer(message, 0);
1633
1634 while (*curr_ch != '\0') {
1635
1636 // handle space
1637 if (*curr_ch == ' ') {
1638 waddch(win, ' ');
1639 curr_ch = g_utf8_next_char(curr_ch);
1640
1641 // handle newline
1642 } else if (*curr_ch == '\n') {
1643 waddch(win, '\n');
1644 _win_indent(win, indent + pad_indent);
1645 curr_ch = g_utf8_next_char(curr_ch);
1646
1647 // handle word
1648 } else {
1649 wordi = 0;
1650 int wordlen = 0;
1651 while (*curr_ch != ' ' && *curr_ch != '\n' && *curr_ch != '\0') {
1652 size_t ch_len = mbrlen(curr_ch, MB_CUR_MAX, NULL);
1653 if ((ch_len == (size_t)-2) || (ch_len == (size_t)-1)) {
1654 curr_ch++;
1655 continue;
1656 }
1657 int offset = 0;
1658 while (offset < ch_len) {
1659 word[wordi++] = curr_ch[offset++];
1660 }
1661 curr_ch = g_utf8_next_char(curr_ch);
1662 }
1663 word[wordi] = '\0';
1664 wordlen = utf8_display_len(word);
1665
1666 int curx = getcurx(win);
1667 int cury;
1668 int maxx = getmaxx(win);
1669
1670 // wrap required
1671 if (curx + wordlen > maxx) {
1672 int linelen = maxx - (indent + pad_indent);
1673
1674 // word larger than line
1675 if (wordlen > linelen) {
1676 gchar* word_ch = g_utf8_offset_to_pointer(word, 0);
1677 while (*word_ch != '\0') {
1678 curx = getcurx(win);
1679 cury = getcury(win);
1680 gboolean firstline = cury == starty;
1681
1682 if (firstline && curx < indent) {
1683 _win_indent(win, indent);
1684 }
1685 if (!firstline && curx < (indent + pad_indent)) {
1686 _win_indent(win, indent + pad_indent);
1687 }
1688
1689 gchar copy[wordi + 1];
1690 g_utf8_strncpy(copy, word_ch, 1);
1691 waddstr(win, copy);
1692
1693 word_ch = g_utf8_next_char(word_ch);
1694 }
1695
1696 // newline and print word
1697 } else {
1698 waddch(win, '\n');
1699 curx = getcurx(win);
1700 cury = getcury(win);
1701 gboolean firstline = cury == starty;
1702
1703 if (firstline && curx < indent) {
1704 _win_indent(win, indent);
1705 }
1706 if (!firstline && curx < (indent + pad_indent)) {
1707 _win_indent(win, indent + pad_indent);
1708 }
1709 waddstr(win, word);
1710 }
1711
1712 // no wrap required
1713 } else {
1714 curx = getcurx(win);
1715 cury = getcury(win);
1716 gboolean firstline = cury == starty;
1717
1718 if (firstline && curx < indent) {
1719 _win_indent(win, indent);
1720 }
1721 if (!firstline && curx < (indent + pad_indent)) {
1722 _win_indent(win, indent + pad_indent);
1723 }
1724 waddstr(win, word);
1725 }
1726 }
1727
1728 // consume first space of next line
1729 int curx = getcurx(win);
1730 int cury = getcury(win);
1731 gboolean firstline = (cury == starty);
1732
1733 if (!firstline && curx == 0 && *curr_ch == ' ') {
1734 curr_ch = g_utf8_next_char(curr_ch);
1735 }
1736 }
1737
1738 free(word);
1739 }
1740
1741 void
win_print_trackbar(ProfWin * window)1742 win_print_trackbar(ProfWin* window)
1743 {
1744 int cols = getmaxx(window->layout->win);
1745
1746 wbkgdset(window->layout->win, theme_attrs(THEME_TRACKBAR));
1747 wattron(window->layout->win, theme_attrs(THEME_TRACKBAR));
1748
1749 for (int i = 1; i <= cols; i++) {
1750 wprintw(window->layout->win, "-");
1751 }
1752
1753 wattroff(window->layout->win, theme_attrs(THEME_TRACKBAR));
1754 }
1755
1756 void
win_redraw(ProfWin * window)1757 win_redraw(ProfWin* window)
1758 {
1759 int size;
1760 werase(window->layout->win);
1761 size = buffer_size(window->layout->buffer);
1762
1763 for (int i = 0; i < size; i++) {
1764 ProfBuffEntry* e = buffer_get_entry(window->layout->buffer, i);
1765
1766 if (e->display_from == NULL && e->message && e->message[0] == '-') {
1767 // just an indicator to print the trackbar/separator not the actual message
1768 win_print_trackbar(window);
1769 } else {
1770 // regular thing to print
1771 _win_print_internal(window, e->show_char, e->pad_indent, e->time, e->flags, e->theme_item, e->display_from, e->message, e->receipt);
1772 }
1773 }
1774 }
1775
1776 gboolean
win_has_active_subwin(ProfWin * window)1777 win_has_active_subwin(ProfWin* window)
1778 {
1779 if (window->layout->type == LAYOUT_SPLIT) {
1780 ProfLayoutSplit* layout = (ProfLayoutSplit*)window->layout;
1781 return (layout->subwin != NULL);
1782 } else {
1783 return FALSE;
1784 }
1785 }
1786
1787 gboolean
win_notify_remind(ProfWin * window)1788 win_notify_remind(ProfWin* window)
1789 {
1790 switch (window->type) {
1791 case WIN_CHAT:
1792 {
1793 ProfChatWin* chatwin = (ProfChatWin*)window;
1794 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
1795
1796 if (prefs_get_boolean(PREF_NOTIFY_CHAT) && chatwin->unread > 0) {
1797 return TRUE;
1798 } else {
1799 return FALSE;
1800 }
1801 }
1802 case WIN_MUC:
1803 {
1804 ProfMucWin* mucwin = (ProfMucWin*)window;
1805 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
1806
1807 return prefs_do_room_notify_mention(mucwin->roomjid, mucwin->unread, mucwin->unread_mentions, mucwin->unread_triggers);
1808 }
1809 case WIN_PRIVATE:
1810 {
1811 ProfPrivateWin* privatewin = (ProfPrivateWin*)window;
1812 assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
1813
1814 if (prefs_get_boolean(PREF_NOTIFY_CHAT) && privatewin->unread > 0) {
1815 return TRUE;
1816 } else {
1817 return FALSE;
1818 }
1819 }
1820 default:
1821 return FALSE;
1822 }
1823 }
1824
1825 int
win_unread(ProfWin * window)1826 win_unread(ProfWin* window)
1827 {
1828 if (window->type == WIN_CHAT) {
1829 ProfChatWin* chatwin = (ProfChatWin*)window;
1830 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
1831 return chatwin->unread;
1832 } else if (window->type == WIN_MUC) {
1833 ProfMucWin* mucwin = (ProfMucWin*)window;
1834 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
1835 return mucwin->unread;
1836 } else if (window->type == WIN_PRIVATE) {
1837 ProfPrivateWin* privatewin = (ProfPrivateWin*)window;
1838 assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
1839 return privatewin->unread;
1840 } else {
1841 return 0;
1842 }
1843 }
1844
1845 gboolean
win_has_attention(ProfWin * window)1846 win_has_attention(ProfWin* window)
1847 {
1848 if (window->type == WIN_CHAT) {
1849 ProfChatWin* chatwin = (ProfChatWin*)window;
1850 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
1851 return chatwin->has_attention;
1852 } else if (window->type == WIN_MUC) {
1853 ProfMucWin* mucwin = (ProfMucWin*)window;
1854 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
1855 return mucwin->has_attention;
1856 }
1857 return FALSE;
1858 }
1859
1860 gboolean
win_toggle_attention(ProfWin * window)1861 win_toggle_attention(ProfWin* window)
1862 {
1863 if (window->type == WIN_CHAT) {
1864 ProfChatWin* chatwin = (ProfChatWin*)window;
1865 assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
1866 chatwin->has_attention = !chatwin->has_attention;
1867 return chatwin->has_attention;
1868 } else if (window->type == WIN_MUC) {
1869 ProfMucWin* mucwin = (ProfMucWin*)window;
1870 assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
1871 mucwin->has_attention = !mucwin->has_attention;
1872 return mucwin->has_attention;
1873 }
1874 return FALSE;
1875 }
1876
1877
1878 void
win_sub_print(WINDOW * win,char * msg,gboolean newline,gboolean wrap,int indent)1879 win_sub_print(WINDOW* win, char* msg, gboolean newline, gboolean wrap, int indent)
1880 {
1881 int maxx = getmaxx(win);
1882 int curx = getcurx(win);
1883 int cury = getcury(win);
1884
1885 if (wrap) {
1886 _win_print_wrapped(win, msg, 1, indent);
1887 } else {
1888 waddnstr(win, msg, maxx - curx);
1889 }
1890
1891 if (newline) {
1892 wmove(win, cury + 1, 0);
1893 }
1894 }
1895
1896 void
win_sub_newline_lazy(WINDOW * win)1897 win_sub_newline_lazy(WINDOW* win)
1898 {
1899 int curx;
1900
1901 if (win == NULL) {
1902 return;
1903 }
1904 curx = getcurx(win);
1905 if (curx > 0) {
1906 int cury = getcury(win);
1907 wmove(win, cury + 1, 0);
1908 }
1909 }
1910
1911 void
win_command_list_error(ProfWin * window,const char * const error)1912 win_command_list_error(ProfWin* window, const char* const error)
1913 {
1914 assert(window != NULL);
1915
1916 win_println(window, THEME_ERROR, "!", "Error retrieving command list: %s", error);
1917 }
1918
1919 void
win_command_exec_error(ProfWin * window,const char * const command,const char * const error,...)1920 win_command_exec_error(ProfWin* window, const char* const command, const char* const error, ...)
1921 {
1922 assert(window != NULL);
1923 va_list arg;
1924 va_start(arg, error);
1925 GString* msg = g_string_new(NULL);
1926 g_string_vprintf(msg, error, arg);
1927
1928 win_println(window, THEME_ERROR, "!", "Error executing command %s: %s", command, msg->str);
1929
1930 g_string_free(msg, TRUE);
1931 va_end(arg);
1932 }
1933
1934 void
win_handle_command_list(ProfWin * window,GSList * cmds)1935 win_handle_command_list(ProfWin* window, GSList* cmds)
1936 {
1937 assert(window != NULL);
1938
1939 if (cmds) {
1940 win_println(window, THEME_DEFAULT, "!", "Ad hoc commands:");
1941 GSList* curr_cmd = cmds;
1942 while (curr_cmd) {
1943 const char* cmd = curr_cmd->data;
1944 win_println(window, THEME_DEFAULT, "!", " %s", cmd);
1945 curr_cmd = g_slist_next(curr_cmd);
1946 }
1947 win_println(window, THEME_DEFAULT, "!", "");
1948 } else {
1949 win_println(window, THEME_DEFAULT, "!", "No commands found");
1950 win_println(window, THEME_DEFAULT, "!", "");
1951 }
1952 }
1953
1954 void
win_handle_command_exec_status(ProfWin * window,const char * const command,const char * const value)1955 win_handle_command_exec_status(ProfWin* window, const char* const command, const char* const value)
1956 {
1957 assert(window != NULL);
1958 win_println(window, THEME_DEFAULT, "!", "%s %s", command, value);
1959 }
1960
1961 void
win_handle_command_exec_result_note(ProfWin * window,const char * const type,const char * const value)1962 win_handle_command_exec_result_note(ProfWin* window, const char* const type, const char* const value)
1963 {
1964 assert(window != NULL);
1965 win_println(window, THEME_DEFAULT, "!", value);
1966 }
1967
1968 void
win_insert_last_read_position_marker(ProfWin * window,char * id)1969 win_insert_last_read_position_marker(ProfWin* window, char* id)
1970 {
1971 int size;
1972 size = buffer_size(window->layout->buffer);
1973
1974 // TODO: this is somewhat costly. We should improve this later.
1975 // check if we already have a separator present
1976 for (int i = 0; i < size; i++) {
1977 ProfBuffEntry* e = buffer_get_entry(window->layout->buffer, i);
1978
1979 // if yes, don't print a new one
1980 if (e->id && (g_strcmp0(e->id, id) == 0)) {
1981 return;
1982 }
1983 }
1984
1985 GDateTime* time = g_date_time_new_now_local();
1986
1987 // the trackbar/separator will actually be print in win_redraw().
1988 // this only puts it in the buffer and win_redraw() will interpret it.
1989 // so that we have the correct length even when resizing.
1990 buffer_append(window->layout->buffer, " ", 0, time, 0, THEME_TEXT, NULL, NULL, "-", NULL, id);
1991 win_redraw(window);
1992
1993 g_date_time_unref(time);
1994 }
1995