From 47e50fcf8e0b4632d0b11073d3a00bbc06d63507 Mon Sep 17 00:00:00 2001 From: night0721 Date: Fri, 20 Sep 2024 15:51:07 +0100 Subject: [PATCH] Parse markdown-like messages to italic, underline, block and color Print date only if current message is newer than previous message by a day Make keybinding configurable for clear input --- include/client/ui.h | 3 +- include/config.h | 3 + src/client/ui.c | 174 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 169 insertions(+), 11 deletions(-) diff --git a/include/client/ui.h b/include/client/ui.h index 7bcf036..f8a150d 100644 --- a/include/client/ui.h +++ b/include/client/ui.h @@ -6,10 +6,11 @@ #define USERS_WINDOW 0 #define CHAT_WINDOW 1 -/* Keybindings */ +/* Key code */ #define CTRLA 0x01 #define CTRLD 0x04 #define CTRLE 0x05 +#define CTRLX 0x18 #define DOWN 0x102 #define UP 0x103 #define LEFT 0x104 diff --git a/include/config.h b/include/config.h index af6bc05..f77a191 100644 --- a/include/config.h +++ b/include/config.h @@ -19,3 +19,6 @@ #define TEXTBOX_HEIGHT 1 #define CLIENT_DATA_DIR "~/.local/share/zsm/zen" + +/* Keybindings */ +#define CLEAR_INPUT CTRLX diff --git a/src/client/ui.c b/src/client/ui.c index ccd15ae..ee29fa3 100644 --- a/src/client/ui.c +++ b/src/client/ui.c @@ -255,23 +255,161 @@ void add_message(uint8_t *author, uint8_t *recipient, uint8_t *content, uint32_t /* * Add message to chat window + * if flag is 1, print date as well * user_color is the color defined above at ncurses_init */ -void print_message(message_t *msg, int user_color) +void print_message(int flag, message_t *msg, int user_color) { struct tm *timeinfo = localtime(&msg->creation); char buffer[21]; - strftime(buffer, sizeof(buffer), "%b %d %Y %H:%M:%S", timeinfo); - + if (flag) { + strftime(buffer, sizeof(buffer), "%b %d %Y %H:%M:%S", timeinfo); + } else { + strftime(buffer, sizeof(buffer), "%H:%M:%S", timeinfo); + } wprintw(chat_content, "%s ", buffer); + wattron(chat_content, A_BOLD); wattron(chat_content, COLOR_PAIR(user_color)); - wprintw(chat_content, "<%s> ", msg->author); - wattroff(chat_content, A_BOLD); wattroff(chat_content, COLOR_PAIR(user_color)); - wprintw(chat_content, "%s", msg->content); + + int i = 0; + int n = strlen(msg->content); + int in_bold = 0, in_italic = 0, in_underline = 0, in_block = 0; + + while (i < n) { + /* Bold */ + if (msg->content[i] == '*' && msg->content[i + 1] == '*') { + if (!in_bold) { + /* Look ahead for the matching closing delimiter */ + int closing_pos = i + 2; + while (closing_pos < n && !(msg->content[closing_pos] == '*' && msg->content[closing_pos + 1] == '*')) { + closing_pos++; + } + if (closing_pos < n) { + wattron(chat_content, A_BOLD); + in_bold = 1; + } else { + /* Treat as regular text if closing delimiter */ + waddch(chat_content, msg->content[i++]); + } + } else { + wattroff(chat_content, A_BOLD); + in_bold = 0; + } + /* Skip */ + i += 2; + + /* Italic */ + } else if (msg->content[i] == '*') { + if (!in_italic) { + /* Look ahead for the matching closing delimiter */ + int closing_pos = i + 1; + while (closing_pos < n && msg->content[closing_pos] != '*') { + closing_pos++; + } + if (closing_pos < n) { + wattron(chat_content, A_ITALIC); + in_italic = 1; + } else { + /* Treat as regular text if closing delimiter */ + waddch(chat_content, msg->content[i++]); + } + } else { + wattroff(chat_content, A_ITALIC); + in_italic = 0; + } + /* Skip */ + i += 1; + + /* Underline */ + } else if (msg->content[i] == '_') { + if (!in_underline) { + /* Look ahead for the matching closing delimiter */ + int closing_pos = i + 1; + while (closing_pos < n && msg->content[closing_pos] != '_') { + closing_pos++; + } + if (closing_pos < n) { + wattron(chat_content, A_UNDERLINE); + in_underline = 1; + } else { + /* Treat as regular text if closing delimiter */ + waddch(chat_content, msg->content[i++]); + } + } else { + wattroff(chat_content, A_UNDERLINE); + in_underline = 0; + } + /* Skip */ + i += 1; + + /* Block */ + } else if (msg->content[i] == '`') { + if (!in_block) { + /* Look ahead for the matching closing delimiter */ + int closing_pos = i + 1; + while (closing_pos < n && msg->content[closing_pos] != '`') { + closing_pos++; + } + if (closing_pos < n) { + wattron(chat_content, A_STANDOUT); + in_block = 1; + } else { + /* Treat as regular text if closing delimiter */ + waddch(chat_content, msg->content[i++]); + } + } else { + wattroff(chat_content, A_STANDOUT); + in_block = 0; + } + /* Skip */ + i += 1; + + /* Allow escape sequence for genuine backslash */ + } else if (msg->content[i] == '\\' && msg->content[i + 1] == '\\') { + /* Print a literal backslash */ + waddch(chat_content, '\\'); + /* Skip both backslashes */ + i += 2; + + /* Color, new line and tab */ + } else if (msg->content[i] == '\\') { + /* Skip the backslash and check the next character */ + i++; + /* Handle color codes \1 to \8 */ + if (msg->content[i] >= '1' && msg->content[i] <= '8') { + /* Convert char to int */ + int color_pair = msg->content[i] - '0'; + wattron(chat_content, COLOR_PAIR(color_pair)); + /* Skip the color code character */ + i++; + + /* Handle new line */ + } else if (msg->content[i] == 'n') { + waddch(chat_content, '\n'); + /* Skip the 'n' */ + i++; + + } else { + /* Invalid sequence, just print the backslash and character */ + waddch(chat_content, '\\'); + waddch(chat_content, msg->content[i]); + i++; + } + } else { + /* Print regular character */ + waddch(chat_content, msg->content[i]); + i++; + } + } + /* Ensure attributes are turned off after printing */ + wattroff(chat_content, A_BOLD); + wattroff(chat_content, A_ITALIC); + wattroff(chat_content, A_UNDERLINE); + wattroff(chat_content, A_STANDOUT); } /* @@ -285,19 +423,28 @@ void show_chat(uint8_t *recipient) if (message.content == NULL) continue; /* Find messages from recipient to client or vice versa */ /* outgoing = 1, incoming = 2 */ + /* if message to print is older than previous message by a day, + * enable flag in print_message to include date */ + int print_date = 0; + if (i > 0 && messages[i - 1].content != NULL && message.creation >= messages[i - 1].creation + 86400) { + print_date = 1; + } if (strncmp(message.author, USERNAME, MAX_NAME) == 0 && strncmp(message.recipient, recipient, MAX_NAME) == 0) { - print_message(&message, 1); + print_message(print_date, &message, 1); continue; } if (strncmp(message.author, recipient, MAX_NAME) == 0 && strncmp(message.recipient, USERNAME, MAX_NAME) == 0) { - print_message(&message, 2); + print_message(print_date, &message, 2); continue; } } wrefresh(chat_content); + /* after printing move cursor back to textbox */ + wmove(textbox, 0, curs_pos + 2); + wrefresh(textbox); } /* * Require heap allocated username @@ -327,7 +474,7 @@ void get_chatbox_content(int ch) curs_pos = 0; /* Set content[0] for printing purposes */ - content[curs_pos] = '\0'; + content[0] = '\0'; } /* Append it to the content if it is normal character */ else if (curs_pos < MAX_MESSAGE_LENGTH - 1) { @@ -389,7 +536,7 @@ void send_message() } add_message(USERNAME, recipient, content, content_len, time(NULL)); free_packet(pkt); - highlight_current_line(); + show_chat(recipient); } /* @@ -422,6 +569,7 @@ void ui(int fd) wclear(textbox); mvwprintw(textbox, 0, 0, "> %s", content); wrefresh(textbox); + wmove(textbox, 0, curs_pos + 2); curs_set(2); } else { curs_set(0); @@ -464,12 +612,18 @@ void ui(int fd) } break; + case CLEAR_INPUT: + if (current_window == CHAT_WINDOW) { + curs_pos = 0; + content[0] = '\0'; + } default: if (current_window == CHAT_WINDOW) get_chatbox_content(ch); } } + cleanup: arraylist_free(users); arraylist_free(marked);