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
This commit is contained in:
parent
5845540b70
commit
47e50fcf8e
3 changed files with 169 additions and 11 deletions
|
@ -6,10 +6,11 @@
|
||||||
#define USERS_WINDOW 0
|
#define USERS_WINDOW 0
|
||||||
#define CHAT_WINDOW 1
|
#define CHAT_WINDOW 1
|
||||||
|
|
||||||
/* Keybindings */
|
/* Key code */
|
||||||
#define CTRLA 0x01
|
#define CTRLA 0x01
|
||||||
#define CTRLD 0x04
|
#define CTRLD 0x04
|
||||||
#define CTRLE 0x05
|
#define CTRLE 0x05
|
||||||
|
#define CTRLX 0x18
|
||||||
#define DOWN 0x102
|
#define DOWN 0x102
|
||||||
#define UP 0x103
|
#define UP 0x103
|
||||||
#define LEFT 0x104
|
#define LEFT 0x104
|
||||||
|
|
|
@ -19,3 +19,6 @@
|
||||||
#define TEXTBOX_HEIGHT 1
|
#define TEXTBOX_HEIGHT 1
|
||||||
|
|
||||||
#define CLIENT_DATA_DIR "~/.local/share/zsm/zen"
|
#define CLIENT_DATA_DIR "~/.local/share/zsm/zen"
|
||||||
|
|
||||||
|
/* Keybindings */
|
||||||
|
#define CLEAR_INPUT CTRLX
|
||||||
|
|
174
src/client/ui.c
174
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
|
* Add message to chat window
|
||||||
|
* if flag is 1, print date as well
|
||||||
* user_color is the color defined above at ncurses_init
|
* 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);
|
struct tm *timeinfo = localtime(&msg->creation);
|
||||||
char buffer[21];
|
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);
|
wprintw(chat_content, "%s ", buffer);
|
||||||
|
|
||||||
wattron(chat_content, A_BOLD);
|
wattron(chat_content, A_BOLD);
|
||||||
wattron(chat_content, COLOR_PAIR(user_color));
|
wattron(chat_content, COLOR_PAIR(user_color));
|
||||||
|
|
||||||
wprintw(chat_content, "<%s> ", msg->author);
|
wprintw(chat_content, "<%s> ", msg->author);
|
||||||
|
|
||||||
wattroff(chat_content, A_BOLD);
|
wattroff(chat_content, A_BOLD);
|
||||||
wattroff(chat_content, COLOR_PAIR(user_color));
|
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;
|
if (message.content == NULL) continue;
|
||||||
/* Find messages from recipient to client or vice versa */
|
/* Find messages from recipient to client or vice versa */
|
||||||
/* outgoing = 1, incoming = 2 */
|
/* 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 &&
|
if (strncmp(message.author, USERNAME, MAX_NAME) == 0 &&
|
||||||
strncmp(message.recipient, recipient, MAX_NAME) == 0) {
|
strncmp(message.recipient, recipient, MAX_NAME) == 0) {
|
||||||
print_message(&message, 1);
|
print_message(print_date, &message, 1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strncmp(message.author, recipient, MAX_NAME) == 0 &&
|
if (strncmp(message.author, recipient, MAX_NAME) == 0 &&
|
||||||
strncmp(message.recipient, USERNAME, MAX_NAME) == 0) {
|
strncmp(message.recipient, USERNAME, MAX_NAME) == 0) {
|
||||||
print_message(&message, 2);
|
print_message(print_date, &message, 2);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wrefresh(chat_content);
|
wrefresh(chat_content);
|
||||||
|
/* after printing move cursor back to textbox */
|
||||||
|
wmove(textbox, 0, curs_pos + 2);
|
||||||
|
wrefresh(textbox);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Require heap allocated username
|
* Require heap allocated username
|
||||||
|
@ -327,7 +474,7 @@ void get_chatbox_content(int ch)
|
||||||
curs_pos = 0;
|
curs_pos = 0;
|
||||||
|
|
||||||
/* Set content[0] for printing purposes */
|
/* Set content[0] for printing purposes */
|
||||||
content[curs_pos] = '\0';
|
content[0] = '\0';
|
||||||
}
|
}
|
||||||
/* Append it to the content if it is normal character */
|
/* Append it to the content if it is normal character */
|
||||||
else if (curs_pos < MAX_MESSAGE_LENGTH - 1) {
|
else if (curs_pos < MAX_MESSAGE_LENGTH - 1) {
|
||||||
|
@ -389,7 +536,7 @@ void send_message()
|
||||||
}
|
}
|
||||||
add_message(USERNAME, recipient, content, content_len, time(NULL));
|
add_message(USERNAME, recipient, content, content_len, time(NULL));
|
||||||
free_packet(pkt);
|
free_packet(pkt);
|
||||||
highlight_current_line();
|
show_chat(recipient);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -422,6 +569,7 @@ void ui(int fd)
|
||||||
wclear(textbox);
|
wclear(textbox);
|
||||||
mvwprintw(textbox, 0, 0, "> %s", content);
|
mvwprintw(textbox, 0, 0, "> %s", content);
|
||||||
wrefresh(textbox);
|
wrefresh(textbox);
|
||||||
|
wmove(textbox, 0, curs_pos + 2);
|
||||||
curs_set(2);
|
curs_set(2);
|
||||||
} else {
|
} else {
|
||||||
curs_set(0);
|
curs_set(0);
|
||||||
|
@ -464,12 +612,18 @@ void ui(int fd)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CLEAR_INPUT:
|
||||||
|
if (current_window == CHAT_WINDOW) {
|
||||||
|
curs_pos = 0;
|
||||||
|
content[0] = '\0';
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
if (current_window == CHAT_WINDOW)
|
if (current_window == CHAT_WINDOW)
|
||||||
get_chatbox_content(ch);
|
get_chatbox_content(ch);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
arraylist_free(users);
|
arraylist_free(users);
|
||||||
arraylist_free(marked);
|
arraylist_free(marked);
|
||||||
|
|
Loading…
Reference in a new issue