void kill_chat_window(ToxWindow *self) { ChatContext *ctx = self->chatwin; StatusBar *statusbar = self->stb; log_disable(ctx->log); line_info_cleanup(ctx->hst); #ifdef _AUDIO stop_current_call(self); #endif delwin(ctx->linewin); delwin(ctx->history); delwin(statusbar->topline); free(ctx->log); free(ctx->hst); free(ctx); free(self->help); free(statusbar); disable_chatwin(self->num); del_window(self); }
void execute(ToxWindow *self, ChatContext *ctx, char *cmd) { if (!strcmp(cmd, "/clear") || !strcmp(cmd, "/c")) { wclear(self->window); wclear(ctx->history); } else if (!strcmp(cmd, "/help") || !strcmp(cmd, "/h")) print_help(ctx); else if (!strcmp(cmd, "/quit") || !strcmp(cmd, "/exit") || !strcmp(cmd, "/q")) { endwin(); exit(0); } else if (!strncmp(cmd, "/status ", strlen("/status "))) { char *msg; msg = strchr(cmd, ' '); if (msg == NULL) { wprintw(ctx->history, "Invalid syntax.\n"); return; } msg++; m_set_userstatus(USERSTATUS_KIND_RETAIN, (uint8_t*) msg, strlen(msg)+1); wprintw(ctx->history, "Status set to: %s\n", msg); } else if (!strncmp(cmd, "/nick ", strlen("/nick "))) { char *nick; nick = strchr(cmd, ' '); if (nick == NULL) { wprintw(ctx->history, "Invalid syntax.\n"); return; } nick++; setname((uint8_t*) nick, strlen(nick)+1); wprintw(ctx->history, "Nickname set to: %s\n", nick); } else if (!strcmp(cmd, "/myid")) { char id[32*2 + 1] = {0}; int i; for (i = 0; i < 32; i++) { char xx[3]; snprintf(xx, sizeof(xx), "%02x", self_public_key[i] & 0xff); strcat(id, xx); } wprintw(ctx->history, "Your ID: %s\n", id); } else if (strcmp(ctx->line, "/close") == 0) { active_window = 0; // Go to prompt screen int f_num = ctx->friendnum; delwin(ctx->linewin); del_window(self, f_num); } else wprintw(ctx->history, "Invalid command.\n"); }
void kill_friendlist(ToxWindow *self) { for (size_t i = 0; i < Friends.max_idx; ++i) { if (Friends.list[i].active && Friends.list[i].group_invite.key != NULL) { free(Friends.list[i].group_invite.key); } } realloc_blocklist(0); realloc_friends(0); free(self->help); del_window(self); }
static void kill_groupchat_window(ToxWindow *self) { ChatContext *ctx = self->chatwin; log_disable(ctx->log); line_info_cleanup(ctx->hst); delwin(ctx->linewin); delwin(ctx->history); delwin(ctx->sidebar); free(ctx->log); free(ctx); free(self->help); del_window(self); }
void kill_prompt_window(ToxWindow *self) { ChatContext *ctx = self->chatwin; StatusBar *statusbar = self->stb; log_disable(ctx->log); line_info_cleanup(ctx->hst); delwin(ctx->linewin); delwin(ctx->history); delwin(statusbar->topline); free(ctx->log); free(ctx); free(self->help); free(statusbar); del_window(self); }
static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key) { ChatContext *ctx = (ChatContext *) self->chatwin; struct tm *timeinfo = get_time(); int x, y, y2, x2; getyx(self->window, y, x); getmaxyx(self->window, y2, x2); /* BACKSPACE key: Remove one character from line */ if (key == 0x107 || key == 0x8 || key == 0x7f) { if (ctx->pos > 0) { ctx->line[--ctx->pos] = L'\0'; if (x == 0) mvwdelch(self->window, y - 1, x2 - 1); else mvwdelch(self->window, y, x - 1); } } else /* Add printable chars to buffer and print on input space */ #if HAVE_WIDECHAR if (iswprint(key)) { #else if (isprint(key)) { #endif if (ctx->pos < (MAX_STR_SIZE-1)) { mvwaddstr(self->window, y, x, wc_to_char(key)); ctx->line[ctx->pos++] = key; ctx->line[ctx->pos] = L'\0'; } } /* RETURN key: Execute command or print line */ else if (key == '\n') { uint8_t *line = wcs_to_char(ctx->line); wclear(ctx->linewin); wmove(self->window, y2 - CURS_Y_OFFSET, 0); wclrtobot(self->window); bool close_win = false; if (line[0] == '/') { if (close_win = strncmp(line, "/close", strlen("/close")) == 0) { set_active_window(0); int groupnum = self->num; delwin(ctx->linewin); del_window(self); close_groupchatwin(m, groupnum); } else if (strncmp(line, "/help", strlen("/help")) == 0) print_groupchat_help(ctx); else execute(ctx->history, self->prompt, m, line, ctx->pos); } else { /* make sure the string has at least non-space character */ if (!string_is_empty(line)) { // uint8_t selfname[TOX_MAX_NAME_LENGTH]; // tox_getselfname(m, selfname, TOX_MAX_NAME_LENGTH); // wattron(ctx->history, COLOR_PAIR(CYAN)); // wprintw(ctx->history, "[%02d:%02d:%02d] ", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec); // wattroff(ctx->history, COLOR_PAIR(CYAN)); // wattron(ctx->history, COLOR_PAIR(GREEN)); // wprintw(ctx->history, "%s: ", selfname); // wattroff(ctx->history, COLOR_PAIR(GREEN)); // wprintw(ctx->history, "%s\n", line); if (tox_group_message_send(m, self->num, line, strlen(line) + 1) == -1) { wattron(ctx->history, COLOR_PAIR(RED)); wprintw(ctx->history, " * Failed to send message.\n"); wattroff(ctx->history, COLOR_PAIR(RED)); } } } if (close_win) free(ctx); else { ctx->line[0] = L'\0'; ctx->pos = 0; } free(line); } } static void groupchat_onDraw(ToxWindow *self, Tox *m) { curs_set(1); int x, y; getmaxyx(self->window, y, x); ChatContext *ctx = (ChatContext *) self->chatwin; mvwhline(ctx->linewin, 0, 0, '_', x); wrefresh(self->window); }
/* * Display a dialog box with a list of options that can be turned on or off * The `flag' parameter is used to select between radiolist and checklist. */ int dialog_checklist(const char *title, const char *cprompt, int height, int width, int list_height, int item_no, char **items, int flag, int separate_output) { int i, j, key2, found, x, y, cur_x, cur_y, box_x, box_y; int key = 0, fkey; int button = 0; int choice = 0; int scrollamt = 0; int max_choice, *status; int use_width, name_width, text_width; int result = DLG_EXIT_UNKNOWN; WINDOW *dialog, *list; char *prompt = strclone(cprompt); const char **buttons = dlg_ok_labels(); tab_correct_str(prompt); if (list_height == 0) { use_width = calc_listw(item_no, items, CHECKBOX_TAGS) + 10; /* calculate height without items (4) */ auto_size(title, prompt, &height, &width, 4, MAX(26, use_width)); calc_listh(&height, &list_height, item_no); } else { auto_size(title, prompt, &height, &width, 4 + list_height, 26); } print_size(height, width); ctl_size(height, width); checkflag = flag; /* Allocate space for storing item on/off status */ status = malloc(sizeof(int) * item_no); assert_ptr(status, "dialog_checklist"); /* Initializes status */ for (i = 0; i < item_no; i++) status[i] = !dlg_strcmp(ItemStatus(i), "on"); max_choice = MIN(list_height, item_no); x = box_x_ordinate(width); y = box_y_ordinate(height); dialog = new_window(height, width, y, x); mouse_setbase(x, y); draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); draw_bottom_box(dialog); draw_title(dialog, title); wattrset(dialog, dialog_attr); print_autowrap(dialog, prompt, height, width); list_width = width - 6; getyx(dialog, cur_y, cur_x); box_y = cur_y + 1; box_x = (width - list_width) / 2 - 1; /* create new window for the list */ list = sub_window(dialog, list_height, list_width, y + box_y + 1, x + box_x + 1); /* draw a box around the list items */ draw_box(dialog, box_y, box_x, list_height + 2 * MARGIN, list_width + 2 * MARGIN, menubox_border_attr, menubox_attr); text_width = 0; name_width = 0; /* Find length of longest item to center checklist */ for (i = 0; i < item_no; i++) { text_width = MAX(text_width, (int) strlen(ItemText(i))); name_width = MAX(name_width, (int) strlen(ItemName(i))); } /* If the name+text is wider than the list is allowed, then truncate * one or both of them. If the name is no wider than 1/4 of the list, * leave it intact. */ use_width = (list_width - 6); if (text_width + name_width > use_width) { int need = 0.25 * use_width; if (name_width > need) { int want = use_width * ((double) name_width) / (text_width + name_width); name_width = (want > need) ? want : need; } text_width = use_width - name_width; } check_x = (use_width - (text_width + name_width)) / 2; item_x = name_width + check_x + 6; /* Print the list */ for (i = 0; i < max_choice; i++) print_item(list, ItemData(i), status[i], i, i == choice); (void) wnoutrefresh(list); /* register the new window, along with its borders */ mouse_mkbigregion(box_y + 1, box_x, list_height, list_width + 2, KEY_MAX, 1, 1, 1 /* by lines */ ); dlg_draw_arrows(dialog, scrollamt, scrollamt + max_choice < item_no - 1, box_x + check_x + 5, box_y, box_y + list_height + 1); dlg_draw_buttons(dialog, height - 2, 0, buttons, 0, FALSE, width); wtimeout(dialog, WTIMEOUT_VAL); while (result == DLG_EXIT_UNKNOWN) { key = mouse_wgetch(dialog, &fkey); if (fkey && (key >= (M_EVENT + KEY_MAX))) { getyx(dialog, cur_y, cur_x); /* De-highlight current item */ print_item(list, ItemData(scrollamt + choice), status[scrollamt + choice], choice, FALSE); /* Highlight new item */ choice = (key - (M_EVENT + KEY_MAX)); print_item(list, ItemData(scrollamt + choice), status[scrollamt + choice], choice, TRUE); (void) wnoutrefresh(list); (void) wmove(dialog, cur_y, cur_x); key = ' '; /* force the selected item to toggle */ fkey = FALSE; } /* * A space toggles the item status. We handle either a checklist * (any number of items can be selected) or radio list (zero or one * items can be selected). */ if (key == ' ') { getyx(dialog, cur_y, cur_x); if (flag == FLAG_CHECK) { /* checklist? */ status[scrollamt + choice] = !status[scrollamt + choice]; print_item(list, ItemData(scrollamt + choice), status[scrollamt + choice], choice, TRUE); } else { /* radiolist */ if (!status[scrollamt + choice]) { for (i = 0; i < item_no; i++) status[i] = FALSE; status[scrollamt + choice] = TRUE; for (i = 0; i < max_choice; i++) print_item(list, ItemData(scrollamt + i), status[scrollamt + i], i, i == choice); } } (void) wnoutrefresh(list); (void) wmove(dialog, cur_y, cur_x); wrefresh(dialog); continue; /* wait for another key press */ } else if (key == ESC) { result = DLG_EXIT_ESC; continue; } if (!fkey) { fkey = TRUE; switch (key) { case '\n': case '\r': key = KEY_ENTER; break; case '-': key = KEY_UP; break; case '+': key = KEY_DOWN; break; case TAB: key = KEY_RIGHT; break; default: fkey = FALSE; break; } } /* * Check if key pressed matches first character of any item tag in * list. If there is more than one match, we will cycle through * each one as the same key is pressed repeatedly. */ found = FALSE; if (!fkey) { for (j = scrollamt + choice + 1; j < item_no; j++) { if (dlg_match_char(dlg_last_getc(), ItemName(j))) { found = TRUE; i = j - scrollamt; break; } } if (!found) { for (j = 0; j <= scrollamt + choice; j++) { if (dlg_match_char(dlg_last_getc(), ItemName(j))) { found = TRUE; i = j - scrollamt; break; } } } if (found) dlg_flush_getc(); } /* * A single digit (1-9) positions the selection to that line in the * current screen. */ if (!found && (key <= '9') && (key > '0') && (key - '1' < max_choice)) { found = TRUE; i = key - '1'; } if (!found) { if (fkey) { found = TRUE; switch (key) { case KEY_HOME: i = -scrollamt; break; case KEY_LL: case KEY_END: i = item_no - 1 - scrollamt; break; case M_EVENT + KEY_PPAGE: case KEY_PPAGE: if (choice) i = 0; else if (scrollamt != 0) i = -MIN(scrollamt, max_choice); else continue; break; case M_EVENT + KEY_NPAGE: case KEY_NPAGE: i = MIN(choice + max_choice, item_no - scrollamt - 1); break; case KEY_UP: i = choice - 1; if (choice == 0 && scrollamt == 0) continue; break; case KEY_DOWN: i = choice + 1; if (scrollamt + choice >= item_no - 1) continue; break; default: found = FALSE; break; } } } if (found) { if (i != choice) { getyx(dialog, cur_y, cur_x); if (i < 0 || i >= max_choice) { #if defined(NCURSES_VERSION_MAJOR) && NCURSES_VERSION_MAJOR < 5 /* * Using wscrl to assist ncurses scrolling is not needed * in version 5.x */ if (i == -1) { if (list_height > 1) { /* De-highlight current first item */ print_item(list, ItemData(scrollamt), status[scrollamt], 0, FALSE); scrollok(list, TRUE); wscrl(list, -1); scrollok(list, FALSE); } scrollamt--; print_item(list, ItemData(scrollamt), status[scrollamt], 0, TRUE); } else if (i == max_choice) { if (list_height > 1) { /* De-highlight current last item before scrolling up */ print_item(list, ItemData(scrollamt + max_choice - 1), status[scrollamt + max_choice - 1], max_choice - 1, FALSE); scrollok(list, TRUE); wscrl(list, 1); scrollok(list, FALSE); } scrollamt++; print_item(list, ItemData(scrollamt + max_choice - 1), status[scrollamt + max_choice - 1], max_choice - 1, TRUE); } else #endif { if (i < 0) { scrollamt += i; choice = 0; } else { choice = max_choice - 1; scrollamt += (i - max_choice + 1); } for (i = 0; i < max_choice; i++) { print_item(list, ItemData(scrollamt + i), status[scrollamt + i], i, i == choice); } } (void) wnoutrefresh(list); dlg_draw_arrows(dialog, scrollamt, scrollamt + choice < item_no - 1, box_x + check_x + 5, box_y, box_y + list_height + 1); } else { /* De-highlight current item */ print_item(list, ItemData(scrollamt + choice), status[scrollamt + choice], choice, FALSE); /* Highlight new item */ choice = i; print_item(list, ItemData(scrollamt + choice), status[scrollamt + choice], choice, TRUE); (void) wnoutrefresh(list); (void) wmove(dialog, cur_y, cur_x); wrefresh(dialog); } } continue; /* wait for another key press */ } if (fkey) { switch (key) { case KEY_ENTER: result = dlg_ok_buttoncode(button); break; case KEY_BTAB: case KEY_LEFT: button = dlg_prev_button(buttons, button); dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); break; case KEY_RIGHT: button = dlg_next_button(buttons, button); dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); break; default: if (key >= M_EVENT) { if ((key2 = dlg_ok_buttoncode(key - M_EVENT)) >= 0) { result = key2; break; } beep(); } } } else { beep(); } } del_window(dialog); switch (result) { case DLG_EXIT_OK: /* FALLTHRU */ case DLG_EXIT_EXTRA: for (i = 0; i < item_no; i++) { if (status[i]) { if (flag == FLAG_CHECK) { if (separate_output) { dlg_add_result(ItemName(i)); dlg_add_result("\n"); } else { dlg_add_result("\""); dlg_add_result(ItemName(i)); dlg_add_result("\" "); } } else { dlg_add_result(ItemName(i)); } } } break; case DLG_EXIT_HELP: dlg_add_result("HELP "); if (USE_ITEM_HELP(ItemHelp(scrollamt + choice))) { dlg_add_result(ItemHelp(scrollamt + choice)); result = DLG_EXIT_OK; /* this is inconsistent */ } else { dlg_add_result(ItemName(scrollamt + choice)); } break; } mouse_free_regions(); free(status); free(prompt); return result; }
static void chat_onKey(ToxWindow *self, Tox *m, wint_t key) { ChatContext *ctx = (ChatContext *) self->chatwin; StatusBar *statusbar = (StatusBar *) self->stb; int x, y, y2, x2; getyx(self->window, y, x); getmaxyx(self->window, y2, x2); /* BACKSPACE key: Remove one character from line */ if (key == 0x107 || key == 0x8 || key == 0x7f) { if (ctx->pos > 0) { ctx->line[--ctx->pos] = L'\0'; if (x == 0) mvwdelch(self->window, y - 1, x2 - 1); else mvwdelch(self->window, y, x - 1); } } else /* Add printable chars to buffer and print on input space */ #if HAVE_WIDECHAR if (iswprint(key)) { #else if (isprint(key)) { #endif if (ctx->pos < (MAX_STR_SIZE-1)) { mvwaddstr(self->window, y, x, wc_to_char(key)); ctx->line[ctx->pos++] = key; ctx->line[ctx->pos] = L'\0'; } } /* RETURN key: Execute command or print line */ else if (key == '\n') { uint8_t *line = wcs_to_char(ctx->line); line[ctx->pos+1] = '\0'; wclear(ctx->linewin); wmove(self->window, y2 - CURS_Y_OFFSET, 0); wclrtobot(self->window); bool close_win = false; if (line[0] == '/') { if (close_win = !strncmp(line, "/close", strlen("/close"))) { int f_num = self->num; delwin(ctx->linewin); delwin(statusbar->topline); del_window(self); disable_chatwin(f_num); } else if (!strncmp(line, "/me ", strlen("/me "))) send_action(self, ctx, m, line + strlen("/me ")); else if (!strncmp(line, "/help", strlen("/help"))) print_chat_help(ctx); else if (!strncmp(line, "/invite", strlen("/invite"))) chat_groupinvite(self, ctx, m, line + strlen("/invite ")); else if(!strncmp(line, "/sendfile ", strlen("/sendfile "))) chat_sendfile(self, ctx, m, line + strlen("/sendfile ")); else execute(ctx->history, self->prompt, m, line, ctx->pos); } else { /* make sure the string has at least non-space character */ if (!string_is_empty(line)) { uint8_t selfname[TOX_MAX_NAME_LENGTH]; tox_getselfname(m, selfname, TOX_MAX_NAME_LENGTH); print_time(ctx->history); wattron(ctx->history, COLOR_PAIR(GREEN)); wprintw(ctx->history, "%s: ", selfname); wattroff(ctx->history, COLOR_PAIR(GREEN)); if (line[0] == '>') { wattron(ctx->history, COLOR_PAIR(GREEN)); wprintw(ctx->history, "%s\n", line); wattroff(ctx->history, COLOR_PAIR(GREEN)); } else wprintw(ctx->history, "%s\n", line); if (!statusbar->is_online || tox_sendmessage(m, self->num, line, strlen(line) + 1) == 0) { wattron(ctx->history, COLOR_PAIR(RED)); wprintw(ctx->history, " * Failed to send message.\n"); wattroff(ctx->history, COLOR_PAIR(RED)); } } } if (close_win) { free(ctx); free(statusbar); } else { ctx->line[0] = L'\0'; ctx->pos = 0; } free(line); } } static void chat_onDraw(ToxWindow *self, Tox *m) { curs_set(1); int x, y; getmaxyx(self->window, y, x); ChatContext *ctx = (ChatContext *) self->chatwin; /* Draw status bar */ StatusBar *statusbar = (StatusBar *) self->stb; mvwhline(statusbar->topline, 1, 0, '-', x); wmove(statusbar->topline, 0, 0); /* Draw name, status and note in statusbar */ if (statusbar->is_online) { char *status_text = "Unknown"; int colour = WHITE; TOX_USERSTATUS status = statusbar->status; switch(status) { case TOX_USERSTATUS_NONE: status_text = "Online"; colour = GREEN; break; case TOX_USERSTATUS_AWAY: status_text = "Away"; colour = YELLOW; break; case TOX_USERSTATUS_BUSY: status_text = "Busy"; colour = RED; break; } wattron(statusbar->topline, A_BOLD); wprintw(statusbar->topline, " %s ", self->name); wattroff(statusbar->topline, A_BOLD); wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); wprintw(statusbar->topline, "[%s]", status_text); wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); } else { wattron(statusbar->topline, A_BOLD); wprintw(statusbar->topline, " %s ", self->name); wattroff(statusbar->topline, A_BOLD); wprintw(statusbar->topline, "[Offline]"); } /* Reset statusbar->statusmsg on window resize */ if (x != self->x) { uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH] = {'\0'}; tox_copy_statusmessage(m, self->num, statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH); snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg); statusbar->statusmsg_len = tox_get_statusmessage_size(m, self->num); } self->x = x; /* Truncate note if it doesn't fit in statusbar */ uint16_t maxlen = x - getcurx(statusbar->topline) - 6; if (statusbar->statusmsg_len > maxlen) { statusbar->statusmsg[maxlen] = '\0'; statusbar->statusmsg_len = maxlen; } if (statusbar->statusmsg[0]) { wattron(statusbar->topline, A_BOLD); wprintw(statusbar->topline, " | %s | ", statusbar->statusmsg); wattroff(statusbar->topline, A_BOLD); } wprintw(statusbar->topline, "\n"); mvwhline(ctx->linewin, 0, 0, '_', x); wrefresh(self->window); }
void execute(ToxWindow *self, ChatContext *ctx, Messenger *m, char *cmd) { if (!strcmp(cmd, "/clear") || !strcmp(cmd, "/c")) { wclear(self->window); wclear(ctx->history); int x, y; getmaxyx(self->window, y, x); (void) x; wmove(self->window, y - CURS_Y_OFFSET, 0); } else if (!strcmp(cmd, "/help") || !strcmp(cmd, "/h")) print_help(ctx); else if (!strcmp(cmd, "/quit") || !strcmp(cmd, "/exit") || !strcmp(cmd, "/q")) { endwin(); exit(0); } else if (!strncmp(cmd, "/me ", strlen("/me "))) { struct tm *timeinfo = get_time(); char *action = strchr(cmd, ' '); if (action == NULL) { wprintw(self->window, "Invalid syntax.\n"); return; } action++; wattron(ctx->history, COLOR_PAIR(2)); wprintw(ctx->history, "[%02d:%02d:%02d] ", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec); wattroff(ctx->history, COLOR_PAIR(2)); uint8_t selfname[MAX_NAME_LENGTH]; int len = getself_name(m, selfname, sizeof(selfname)); char msg[MAX_STR_SIZE - len - 4]; snprintf(msg, sizeof(msg), "* %s %s\n", (uint8_t *) selfname, action); wattron(ctx->history, COLOR_PAIR(5)); wprintw(ctx->history, msg); wattroff(ctx->history, COLOR_PAIR(5)); if (m_sendaction(m, ctx->friendnum, (uint8_t *) msg, strlen(msg) + 1) < 0) { wattron(ctx->history, COLOR_PAIR(3)); wprintw(ctx->history, " * Failed to send action\n"); wattroff(ctx->history, COLOR_PAIR(3)); } } else if (!strncmp(cmd, "/status ", strlen("/status "))) { char *status = strchr(cmd, ' '); char *msg; char *status_text; if (status == NULL) { wprintw(ctx->history, "Invalid syntax.\n"); return; } status++; USERSTATUS status_kind; if (!strncmp(status, "online", strlen("online"))) { status_kind = USERSTATUS_NONE; status_text = "ONLINE"; } else if (!strncmp(status, "away", strlen("away"))) { status_kind = USERSTATUS_AWAY; status_text = "AWAY"; } else if (!strncmp(status, "busy", strlen("busy"))) { status_kind = USERSTATUS_BUSY; status_text = "BUSY"; } else { wprintw(ctx->history, "Invalid status.\n"); return; } msg = strchr(status, ' '); if (msg == NULL) { m_set_userstatus(m, status_kind); wprintw(ctx->history, "Status set to: %s\n", status_text); } else { msg++; m_set_userstatus(m, status_kind); m_set_statusmessage(m, ( uint8_t *) msg, strlen(msg) + 1); wprintw(ctx->history, "Status set to: %s, %s\n", status_text, msg); } } else if (!strncmp(cmd, "/nick ", strlen("/nick "))) { char *nick; nick = strchr(cmd, ' '); if (nick == NULL) { wprintw(ctx->history, "Invalid syntax.\n"); return; } nick++; setname(m, (uint8_t *) nick, strlen(nick) + 1); wprintw(ctx->history, "Nickname set to: %s\n", nick); } else if (!strcmp(cmd, "/myid")) { char id[FRIEND_ADDRESS_SIZE * 2 + 1] = {0}; int i; uint8_t address[FRIEND_ADDRESS_SIZE]; getaddress(m, address); for (i = 0; i < FRIEND_ADDRESS_SIZE; i++) { char xx[3]; snprintf(xx, sizeof(xx), "%02X", address[i] & 0xff); strcat(id, xx); } wprintw(ctx->history, "%s\n", id); } else if (strcmp(ctx->line, "/close") == 0) { int f_num = ctx->friendnum; delwin(ctx->linewin); del_window(self); disable_chatwin(f_num); } else wprintw(ctx->history, "Invalid command.\n"); }
/* * Display a dialog box for entering a date */ int dialog_calendar(const char *title, const char *subtitle, int height, int width, int day, int month, int year) { BOX dy_box, mn_box, yr_box; int fkey; int key = 0; int key2; int step; int button = 0; int result = DLG_EXIT_UNKNOWN; WINDOW *dialog; time_t now_time = time((time_t *) 0); struct tm current; STATES state = 0; const char **buttons = dlg_ok_labels(); char *prompt = strclone(subtitle); dlg_does_output(); now_time = time((time_t *) 0); current = *localtime(&now_time); if (day < 0) day = current.tm_mday; if (month < 0) month = current.tm_mon + 1; if (year < 0) year = current.tm_year + 1900; /* compute a struct tm that matches the day/month/year parameters */ if (((year -= 1900) > 0) && (year < 200)) { /* ugly, but I'd like to run this on older machines w/o mktime -TD */ for (;;) { if (year > current.tm_year) { now_time += ONE_DAY * days_in_year(¤t, 0); } else if (year < current.tm_year) { now_time -= ONE_DAY * days_in_year(¤t, -1); } else if (month > current.tm_mon + 1) { now_time += ONE_DAY * days_in_month(¤t, 0); } else if (month < current.tm_mon + 1) { now_time -= ONE_DAY * days_in_month(¤t, -1); } else if (day > current.tm_mday) { now_time += ONE_DAY; } else if (day < current.tm_mday) { now_time -= ONE_DAY; } else { break; } current = *localtime(&now_time); } } auto_size(title, prompt, &height, &width, 0, 0); height += MIN_HIGH; if (width < MIN_WIDE) width = MIN_WIDE; print_size(height, width); ctl_size(height, width); /* FIXME: how to make this resizable? */ dialog = new_window(height, width, box_y_ordinate(height), box_x_ordinate(width)); draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); draw_bottom_box(dialog); draw_title(dialog, title); wattrset(dialog, dialog_attr); print_autowrap(dialog, prompt, height, width); /* compute positions of day, month and year boxes */ memset(&dy_box, 0, sizeof(dy_box)); memset(&mn_box, 0, sizeof(mn_box)); memset(&yr_box, 0, sizeof(yr_box)); if (init_object(&dy_box, dialog, (width - DAY_WIDE) / 2, (height - (DAY_HIGH + BTN_HIGH + (4 * MARGIN))), DAY_WIDE, DAY_HIGH + (2 * MARGIN), draw_day, 'D') < 0 || DrawObject(&dy_box) < 0) return DLG_EXIT_ERROR; if (init_object(&mn_box, dialog, dy_box.x, dy_box.y - (HDR_HIGH + 2 * MARGIN), (DAY_WIDE / 2) - MARGIN, HDR_HIGH, draw_month, 'M') < 0 || DrawObject(&mn_box) < 0) return DLG_EXIT_ERROR; if (init_object(&yr_box, dialog, dy_box.x + mn_box.width + 2, mn_box.y, mn_box.width, mn_box.height, draw_year, 'Y') < 0 || DrawObject(&yr_box) < 0) return DLG_EXIT_ERROR; while (result == DLG_EXIT_UNKNOWN) { BOX *obj = (state == sDAY ? &dy_box : (state == sMONTH ? &mn_box : (state == sYEAR ? &yr_box : 0))); button = (state < 0) ? 0 : state; dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); if (obj != 0) dlg_set_focus(dialog, obj->window); key = mouse_wgetch(dialog, &fkey); if ((key2 = dlg_char_to_button(key, buttons)) >= 0) { result = key2; } else { /* handle non-functionkeys */ if (!fkey) { fkey = TRUE; switch (key) { case ' ': case '\n': case '\r': key = KEY_ENTER; break; case TAB: key = KEY_RIGHT; break; case CHR_PREVIOUS: case CHR_NEXT: case CHR_BACKSPACE: case 'h': case 'j': case 'k': case 'l': /* treat these as function-keys */ break; case ESC: result = DLG_EXIT_ESC; fkey = FALSE; break; default: fkey = FALSE; break; } } /* handle functionkeys */ if (fkey) { switch (key) { case M_EVENT + 'D': state = sDAY; break; case M_EVENT + 'M': state = sMONTH; break; case M_EVENT + 'Y': state = sYEAR; break; case KEY_ENTER: result = dlg_ok_buttoncode(button); break; case KEY_LEFT: case KEY_BTAB: state = dlg_prev_ok_buttonindex(state, sMONTH); break; case KEY_RIGHT: state = dlg_next_ok_buttonindex(state, sMONTH); break; default: step = 0; key2 = -1; if (key >= M_EVENT) { if ((key2 = dlg_ok_buttoncode(key - M_EVENT)) >= 0) { result = key2; break; } else if (key >= (M_EVENT + KEY_MAX)) { state = sDAY; obj = &dy_box; key2 = 1; step = (key - (M_EVENT + KEY_MAX) - day_cell_number(¤t)); } } if (obj != 0) { if (key2 < 0) step = next_or_previous(key, (obj == &dy_box)); if (step != 0) { struct tm old = current; /* see comment regarding mktime -TD */ if (obj == &dy_box) { now_time += ONE_DAY * step; } else if (obj == &mn_box) { if (step > 0) now_time += ONE_DAY * days_in_month(¤t, 0); else now_time -= ONE_DAY * days_in_month(¤t, -1); } else if (obj == &yr_box) { if (step > 0) now_time += (ONE_DAY * days_in_year(¤t, 0)); else now_time -= (ONE_DAY * days_in_year(¤t, -1)); } current = *localtime(&now_time); if (obj != &dy_box && (current.tm_mday != old.tm_mday || current.tm_mon != old.tm_mon || current.tm_year != old.tm_year)) DrawObject(&dy_box); if (obj != &mn_box && current.tm_mon != old.tm_mon) DrawObject(&mn_box); if (obj != &yr_box && current.tm_year != old.tm_year) DrawObject(&yr_box); (void) DrawObject(obj); } } break; } } } } del_window(dialog); sprintf(dialog_vars.input_result, "%02d/%02d/%0d\n", current.tm_mday, current.tm_mon + 1, current.tm_year + 1900); mouse_free_regions(); free(prompt); return result; }
static void chat_onKey(ToxWindow *self, Tox *m, wint_t key) { ChatContext *ctx = (ChatContext *) self->x; struct tm *timeinfo = get_time(); int x, y, y2, x2; getyx(self->window, y, x); getmaxyx(self->window, y2, x2); /* Add printable chars to buffer and print on input space */ #if HAVE_WIDECHAR if (iswprint(key)) { #else if (isprint(key)) { #endif if (ctx->pos != sizeof(ctx->line) - 1) { mvwaddstr(self->window, y, x, wc_to_char(key)); ctx->line[ctx->pos++] = key; ctx->line[ctx->pos] = L'\0'; } } /* BACKSPACE key: Remove one character from line */ else if (key == 0x107 || key == 0x8 || key == 0x7f) { if (ctx->pos > 0) { ctx->line[--ctx->pos] = L'\0'; if (x == 0) mvwdelch(self->window, y - 1, x2 - 1); else mvwdelch(self->window, y, x - 1); } } /* RETURN key: Execute command or print line */ else if (key == '\n') { char *line = wcs_to_char(ctx->line); wclear(ctx->linewin); wmove(self->window, y2 - CURS_Y_OFFSET, 0); wclrtobot(self->window); if (line[0] == '/') execute(self, ctx, m, line); else { /* make sure the string has at least non-space character */ if (!string_is_empty(line)) { uint8_t selfname[TOX_MAX_NAME_LENGTH]; tox_getselfname(m, selfname, sizeof(selfname)); wattron(ctx->history, COLOR_PAIR(2)); wprintw(ctx->history, "[%02d:%02d:%02d] ", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec); wattroff(ctx->history, COLOR_PAIR(2)); wattron(ctx->history, COLOR_PAIR(1)); wprintw(ctx->history, "%s: ", selfname); wattroff(ctx->history, COLOR_PAIR(1)); wprintw(ctx->history, "%s\n", line); if (tox_sendmessage(m, ctx->friendnum, (uint8_t *) line, strlen(line) + 1) == 0) { wattron(ctx->history, COLOR_PAIR(3)); wprintw(ctx->history, " * Failed to send message.\n"); wattroff(ctx->history, COLOR_PAIR(3)); } } } ctx->line[0] = L'\0'; ctx->pos = 0; free(line); } } void execute(ToxWindow *self, ChatContext *ctx, Tox *m, char *cmd) { if (!strcmp(cmd, "/clear") || !strcmp(cmd, "/c")) { wclear(self->window); wclear(ctx->history); int x, y; getmaxyx(self->window, y, x); (void) x; wmove(self->window, y - CURS_Y_OFFSET, 0); } else if (!strcmp(cmd, "/help") || !strcmp(cmd, "/h")) print_help(ctx); else if (!strcmp(cmd, "/quit") || !strcmp(cmd, "/exit") || !strcmp(cmd, "/q")) { endwin(); exit(0); } else if (!strncmp(cmd, "/me ", strlen("/me "))) { struct tm *timeinfo = get_time(); char *action = strchr(cmd, ' '); if (action == NULL) { wprintw(self->window, "Invalid syntax.\n"); return; } action++; wattron(ctx->history, COLOR_PAIR(2)); wprintw(ctx->history, "[%02d:%02d:%02d] ", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec); wattroff(ctx->history, COLOR_PAIR(2)); uint8_t selfname[TOX_MAX_NAME_LENGTH]; int len = tox_getselfname(m, selfname, sizeof(selfname)); char msg[MAX_STR_SIZE - len - 4]; snprintf(msg, sizeof(msg), "* %s %s\n", (uint8_t *) selfname, action); wattron(ctx->history, COLOR_PAIR(5)); wprintw(ctx->history, msg); wattroff(ctx->history, COLOR_PAIR(5)); if (tox_sendaction(m, ctx->friendnum, (uint8_t *) msg, strlen(msg) + 1) < 0) { wattron(ctx->history, COLOR_PAIR(3)); wprintw(ctx->history, " * Failed to send action\n"); wattroff(ctx->history, COLOR_PAIR(3)); } } else if (!strncmp(cmd, "/status ", strlen("/status "))) { char *status = strchr(cmd, ' '); char *msg; char *status_text; if (status == NULL) { wprintw(ctx->history, "Invalid syntax.\n"); return; } status++; TOX_USERSTATUS status_kind; if (!strncmp(status, "online", strlen("online"))) { status_kind = TOX_USERSTATUS_NONE; status_text = "ONLINE"; } else if (!strncmp(status, "away", strlen("away"))) { status_kind = TOX_USERSTATUS_AWAY; status_text = "AWAY"; } else if (!strncmp(status, "busy", strlen("busy"))) { status_kind = TOX_USERSTATUS_BUSY; status_text = "BUSY"; } else { wprintw(ctx->history, "Invalid status.\n"); return; } msg = strchr(status, ' '); if (msg == NULL) { tox_set_userstatus(m, status_kind); wprintw(ctx->history, "Status set to: %s\n", status_text); } else { msg++; tox_set_userstatus(m, status_kind); tox_set_statusmessage(m, ( uint8_t *) msg, strlen(msg) + 1); wprintw(ctx->history, "Status set to: %s, %s\n", status_text, msg); } } else if (!strncmp(cmd, "/nick ", strlen("/nick "))) { char *nick; nick = strchr(cmd, ' '); if (nick == NULL) { wprintw(ctx->history, "Invalid syntax.\n"); return; } nick++; tox_setname(m, (uint8_t *) nick, strlen(nick) + 1); wprintw(ctx->history, "Nickname set to: %s\n", nick); } else if (!strcmp(cmd, "/myid")) { char id[TOX_FRIEND_ADDRESS_SIZE * 2 + 1] = {0}; int i; uint8_t address[TOX_FRIEND_ADDRESS_SIZE]; tox_getaddress(m, address); for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) { char xx[3]; snprintf(xx, sizeof(xx), "%02X", address[i] & 0xff); strcat(id, xx); } wprintw(ctx->history, "%s\n", id); } else if (strcmp(cmd, "/close") == 0) { int f_num = ctx->friendnum; delwin(ctx->linewin); del_window(self); disable_chatwin(f_num); } else wprintw(ctx->history, "Invalid command.\n"); } static void chat_onDraw(ToxWindow *self, Tox *m) { curs_set(1); int x, y; getmaxyx(self->window, y, x); (void) y; ChatContext *ctx = (ChatContext *) self->x; mvwhline(ctx->linewin, 0, 0, '_', x); wrefresh(self->window); }
/* * Display a menu for choosing among a number of options */ int dialog_menu(const char *title, const char *cprompt, int height, int width, int menu_height, int item_no, const char **items) { int i, j, x, y, cur_x, cur_y, box_x, box_y; int key = 0; int button = 0; int choice = dlg_default_item(items, MENUBOX_TAGS); int scrollamt = 0; int max_choice, min_width; int found; int use_width, name_width, text_width; WINDOW *dialog, *menu; char *prompt = strclone(cprompt); const char **buttons = dlg_ok_labels(); tab_correct_str(prompt); if (menu_height == 0) { min_width = calc_listw(item_no, items, MENUBOX_TAGS) + 10; /* calculate height without items (4) */ auto_size(title, prompt, &height, &width, 4, MAX(26, min_width)); calc_listh(&height, &menu_height, item_no); } else { auto_size(title, prompt, &height, &width, 4 + menu_height, 26); } print_size(height, width); ctl_size(height, width); max_choice = MIN(menu_height, item_no); x = box_x_ordinate(width); y = box_y_ordinate(height); dialog = new_window(height, width, y, x); mouse_setbase(x, y); draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); draw_bottom_box(dialog); draw_title(dialog, title); wattrset(dialog, dialog_attr); print_autowrap(dialog, prompt, height, width); menu_width = width - 6; getyx(dialog, cur_y, cur_x); box_y = cur_y + 1; box_x = (width - menu_width) / 2 - 1; /* create new window for the menu */ menu = sub_window(dialog, menu_height, menu_width, y + box_y + 1, x + box_x + 1); /* draw a box around the menu items */ draw_box(dialog, box_y, box_x, menu_height + 2, menu_width + 2, menubox_border_attr, menubox_attr); name_width = 0; text_width = 0; /* Find length of longest item to center menu */ for (i = 0; i < item_no; i++) { name_width = MAX(name_width, (int) strlen(ItemName(i))); text_width = MAX(text_width, (int) strlen(ItemText(i))); } /* If the name+text is wider than the list is allowed, then truncate * one or both of them. If the name is no wider than 1/4 of the list, * leave it intact. */ use_width = (menu_width - 2); if (text_width + name_width > use_width) { int need = 0.25 * use_width; if (name_width > need) { int want = use_width * ((double) name_width) / (text_width + name_width); name_width = (want > need) ? want : need; } text_width = use_width - name_width; } tag_x = (use_width - text_width - name_width) / 2; item_x = name_width + tag_x + 2; if (choice - scrollamt >= max_choice) { scrollamt = choice - (max_choice - 1); choice = max_choice - 1; } /* Print the menu */ for (i = 0; i < max_choice; i++) print_item(menu, ItemData(i + scrollamt), i, i == choice); (void) wnoutrefresh(menu); /* register the new window, along with its borders */ mouse_mkbigregion(box_y, box_x, menu_height + 2, menu_width + 2, item_no, item_x, /* the threshold */ 1 /* dirty mode */ ); dlg_draw_arrows(dialog, scrollamt, scrollamt + max_choice < item_no, box_x + tag_x + 1, box_y, box_y + menu_height + 1); dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); wtimeout(dialog, WTIMEOUT_VAL); while (key != ESC) { key = mouse_wgetch(dialog); /* Check if key pressed matches first character of any item tag in menu */ for (i = 0; i < max_choice; i++) if (toupper(key) == toupper(UCH(ItemName(scrollamt + i)[0]))) break; /* * Check if key pressed matches first character of any item tag in * list. If there is more than one match, we will cycle through * each one as the same key is pressed repeatedly. */ found = FALSE; for (j = scrollamt + choice + 1; j < item_no; j++) { if (toupper(key) == toupper(UCH(ItemName(j)[0]))) { found = TRUE; i = j - scrollamt; break; } } if (!found) { for (j = 0; j <= scrollamt + choice; j++) { if (toupper(key) == toupper(UCH(ItemName(j)[0]))) { found = TRUE; i = j - scrollamt; break; } } } /* * A single digit (1-9) positions the selection to that line in the * current screen. */ if (!found && (key <= '9') && (key > '0') && (key - '1' <= max_choice)) { found = TRUE; i = key - '1'; } if (!found) { found = TRUE; switch (key) { case KEY_HOME: i = -scrollamt; break; case KEY_LL: case KEY_END: i = item_no - 1 - scrollamt; break; case KEY_PPAGE: if (choice) i = 0; else if (scrollamt != 0) i = -MIN(scrollamt, max_choice); else continue; break; case KEY_NPAGE: i = MIN(choice + max_choice, item_no - scrollamt - 1); break; case KEY_UP: case '-': i = choice - 1; if (choice == 0 && scrollamt == 0) continue; break; case KEY_DOWN: case '+': i = choice + 1; if (scrollamt + choice >= item_no - 1) continue; break; default: found = FALSE; break; } } if (found) { if (i != choice) { getyx(dialog, cur_y, cur_x); if (i < 0 || i >= max_choice) { #if defined(NCURSES_VERSION_MAJOR) && NCURSES_VERSION_MAJOR < 5 /* * Using wscrl to assist ncurses scrolling is not needed * in version 5.x */ if (i == -1) { if (menu_height > 1) { /* De-highlight current first item */ print_item(menu, ItemData(scrollamt), 0, FALSE); scrollok(menu, TRUE); wscrl(menu, -1); scrollok(menu, FALSE); } scrollamt--; print_item(menu, ItemData(scrollamt), 0, TRUE); } else if (i == max_choice) { if (menu_height > 1) { /* De-highlight current last item before scrolling up */ print_item(menu, ItemData(scrollamt + max_choice - 1), max_choice - 1, FALSE); scrollok(menu, TRUE); wscrl(menu, 1); scrollok(menu, FALSE); } scrollamt++; print_item(menu, ItemData(scrollamt + max_choice - 1), max_choice - 1, TRUE); } else #endif { if (i < 0) { scrollamt += i; choice = 0; } else { choice = max_choice - 1; scrollamt += (i - max_choice + 1); } for (i = 0; i < max_choice; i++) { print_item(menu, ItemData(scrollamt + i), i, i == choice); } } (void) wnoutrefresh(menu); dlg_draw_arrows(dialog, scrollamt, scrollamt + choice < item_no - 1, box_x + tag_x + 1, box_y, box_y + menu_height + 1); } else { /* De-highlight current item */ print_item(menu, ItemData(scrollamt + choice), choice, FALSE); /* Highlight new item */ choice = i; print_item(menu, ItemData(scrollamt + choice), choice, TRUE); (void) wnoutrefresh(menu); (void) wmove(dialog, cur_y, cur_x); wrefresh(dialog); } } continue; /* wait for another key press */ } switch (key) { case M_EVENT + 'O': del_window(dialog); return scrollamt + choice; case M_EVENT + 'C': del_window(dialog); return -2; case M_EVENT + 'o': /* mouse enter... */ case M_EVENT + 'c': /* use the code for toggling */ button = (key == M_EVENT + 'o'); /* FALLTHRU */ case ' ': case KEY_BTAB: case TAB: case KEY_LEFT: case KEY_RIGHT: if (!dialog_vars.nocancel) button = !button; dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); break; case '\n': del_window(dialog); return (button ? -2 : (scrollamt + choice)); } } del_window(dialog); return -1; /* ESC pressed */ }
/* * Display a gauge, or progress meter. Starts at percent% and reads stdin. If * stdin is not XXX, then it is interpreted as a percentage, and the display is * updated accordingly. Otherwise the next line is the percentage, and * subsequent lines up to another XXX are used for the new prompt. Note that * the size of the window never changes, so the prompt can not get any larger * than the height and width specified. */ int dialog_gauge(const char *title, const char *prompt, int height, int width, int percent) { int i, x, y; char buf[MY_LEN]; char prompt_buf[MY_LEN]; WINDOW *dialog; auto_size(title, prompt, &height, &width, MIN_HIGH, MIN_WIDE); print_size(height, width); ctl_size(height, width); /* center dialog box on screen */ x = box_x_ordinate(width); y = box_y_ordinate(height); dialog = new_window(height, width, y, x); curs_set(0); do { (void) werase(dialog); draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); draw_title(dialog, title); wattrset(dialog, dialog_attr); print_autowrap(dialog, prompt, height, width - (2 * MARGIN)); draw_box(dialog, height - 4, 2 + MARGIN, 2 + MARGIN, width - 2 * (2 + MARGIN), dialog_attr, border_attr); (void) wmove(dialog, height - 3, 4); wattrset(dialog, title_attr); for (i = 0; i < (width - 2 * (3 + MARGIN)); i++) (void) waddch(dialog, ' '); wattrset(dialog, title_attr); (void) wmove(dialog, height - 3, (width / 2) - 2); (void) wprintw(dialog, "%3d%%", percent); x = (percent * (width - 2 * (3 + MARGIN))) / 100; wattrset(dialog, A_REVERSE); (void) wmove(dialog, height - 3, 4); for (i = 0; i < x; i++) (void) waddch(dialog, winch(dialog)); (void) wrefresh(dialog); if (read_data(buf, pipe_fp) == 0) break; if (isMarker(buf)) { /* * Historically, next line should be percentage, but one of the * worse-written clones of 'dialog' assumes the number is missing. * (Gresham's Law applied to software). */ if (read_data(buf, pipe_fp) == 0) break; prompt_buf[0] = '\0'; if (decode_percent(buf)) percent = atoi(buf); else strcpy(prompt_buf, buf); /* Rest is message text */ while (read_data(buf, pipe_fp) != 0 && !isMarker(buf)) { if (strlen(prompt_buf) + strlen(buf) < sizeof(prompt_buf) - 1) { strcat(prompt_buf, buf); } } prompt = prompt_buf; } else if (decode_percent(buf)) { percent = atoi(buf); } } while (1); curs_set(1); del_window(dialog); return (DLG_EXIT_OK); }
static void chat_onKey(ToxWindow *self, Tox *m, wint_t key) { ChatContext *ctx = self->chatwin; StatusBar *statusbar = self->stb; int x, y, y2, x2; getyx(self->window, y, x); getmaxyx(self->window, y2, x2); int cur_len = 0; if (key == 0x107 || key == 0x8 || key == 0x7f) { /* BACKSPACE key: Remove character behind pos */ if (ctx->pos > 0) { cur_len = MAX(1, wcwidth(ctx->line[ctx->pos - 1])); del_char_buf_bck(ctx->line, &ctx->pos, &ctx->len); if (x == 0) wmove(self->window, y-1, x2 - cur_len); else wmove(self->window, y, x - cur_len); } else { beep(); } } else if (key == KEY_DC) { /* DEL key: Remove character at pos */ if (ctx->pos != ctx->len) del_char_buf_frnt(ctx->line, &ctx->pos, &ctx->len); else beep(); } else if (key == T_KEY_DISCARD) { /* CTRL-U: Delete entire line behind pos */ if (ctx->pos > 0) { discard_buf(ctx->line, &ctx->pos, &ctx->len); wmove(self->window, y2 - CURS_Y_OFFSET, 0); } else { beep(); } } else if (key == T_KEY_KILL) { /* CTRL-K: Delete entire line in front of pos */ if (ctx->pos != ctx->len) kill_buf(ctx->line, &ctx->pos, &ctx->len); else beep(); } else if (key == KEY_HOME) { /* HOME key: Move cursor to beginning of line */ if (ctx->pos > 0) { ctx->pos = 0; wmove(self->window, y2 - CURS_Y_OFFSET, 0); } } else if (key == KEY_END) { /* END key: move cursor to end of line */ if (ctx->pos != ctx->len) { ctx->pos = ctx->len; mv_curs_end(self->window, MAX(0, wcswidth(ctx->line, (CHATBOX_HEIGHT-1)*x2)), y2, x2); } } else if (key == KEY_LEFT) { if (ctx->pos > 0) { --ctx->pos; cur_len = MAX(1, wcwidth(ctx->line[ctx->pos])); if (x == 0) wmove(self->window, y-1, x2 - cur_len); else wmove(self->window, y, x - cur_len); } else { beep(); } } else if (key == KEY_RIGHT) { if (ctx->pos < ctx->len) { cur_len = MAX(1, wcwidth(ctx->line[ctx->pos])); ++ctx->pos; if (x == x2-1) wmove(self->window, y+1, 0); else wmove(self->window, y, x + cur_len); } else { beep(); } } else if (key == KEY_UP) { /* fetches previous item in history */ fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot, &ctx->hst_pos, LN_HIST_MV_UP); mv_curs_end(self->window, ctx->len, y2, x2); } else if (key == KEY_DOWN) { /* fetches next item in history */ fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot, &ctx->hst_pos, LN_HIST_MV_DWN); mv_curs_end(self->window, ctx->len, y2, x2); } else if (key == '\t') { /* TAB key: completes command */ if (ctx->len > 1 && ctx->line[0] == '/') { int diff = complete_line(ctx->line, &ctx->pos, &ctx->len, chat_cmd_list, AC_NUM_CHAT_COMMANDS, MAX_CMDNAME_SIZE); if (diff != -1) { if (x + diff > x2 - 1) { int ofst = (x + diff - 1) - (x2 - 1); wmove(self->window, y+1, ofst); } else { wmove(self->window, y, x+diff); } } else { beep(); } } else { beep(); } } else #if HAVE_WIDECHAR if (iswprint(key)) #else if (isprint(key)) #endif { /* prevents buffer overflows and strange behaviour when cursor goes past the window */ if ( (ctx->len < MAX_STR_SIZE-1) && (ctx->len < (x2 * (CHATBOX_HEIGHT - 1)-1)) ) { add_char_to_buf(ctx->line, &ctx->pos, &ctx->len, key); if (x == x2-1) wmove(self->window, y+1, 0); else wmove(self->window, y, x + MAX(1, wcwidth(key))); } } /* RETURN key: Execute command or print line */ else if (key == '\n') { uint8_t line[MAX_STR_SIZE]; if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) memset(&line, 0, sizeof(line)); wclear(ctx->linewin); wmove(self->window, y2 - CURS_Y_OFFSET, 0); wclrtobot(self->window); bool close_win = false; if (!string_is_empty(line)) add_line_to_hist(ctx->line, ctx->len, ctx->ln_history, &ctx->hst_tot, &ctx->hst_pos); if (line[0] == '/') { if (close_win = !strcmp(line, "/close")) { int f_num = self->num; delwin(ctx->linewin); delwin(statusbar->topline); del_window(self); disable_chatwin(f_num); } else if (strncmp(line, "/me ", strlen("/me ")) == 0) send_action(self, ctx, m, line + strlen("/me ")); else execute(ctx->history, self, m, line, CHAT_COMMAND_MODE); } else if (!string_is_empty(line)) { uint8_t selfname[TOX_MAX_NAME_LENGTH]; tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH); print_time(ctx->history); wattron(ctx->history, COLOR_PAIR(GREEN)); wprintw(ctx->history, "%s: ", selfname); wattroff(ctx->history, COLOR_PAIR(GREEN)); if (line[0] == '>') { wattron(ctx->history, COLOR_PAIR(GREEN)); wprintw(ctx->history, "%s\n", line); wattroff(ctx->history, COLOR_PAIR(GREEN)); } else wprintw(ctx->history, "%s\n", line); if (!statusbar->is_online || tox_send_message(m, self->num, line, strlen(line) + 1) == 0) { wattron(ctx->history, COLOR_PAIR(RED)); wprintw(ctx->history, " * Failed to send message.\n"); wattroff(ctx->history, COLOR_PAIR(RED)); } } if (close_win) { free(ctx); free(statusbar); } else { reset_buf(ctx->line, &ctx->pos, &ctx->len); } } }
/* * Display a dialog box for entering a date */ int dialog_timebox(const char *title, const char *subtitle, int height, int width, int hour, int minute, int second) { BOX hr_box, mn_box, sc_box; int key = 0, key2, button = sOK; int result = -2; WINDOW *dialog; time_t now_time = time((time_t *) 0); struct tm current; STATES state = sOK; const char **buttons = dlg_ok_labels(); char *prompt = strclone(subtitle); now_time = time((time_t *) 0); current = *localtime(&now_time); auto_size(title, prompt, &height, &width, 0, 0); height += MIN_HIGH; if (width < MIN_WIDE) width = MIN_WIDE; dlg_button_layout(buttons, &width); print_size(height, width); ctl_size(height, width); /* FIXME: how to make this resizable? */ dialog = new_window(height, width, box_y_ordinate(height), box_x_ordinate(width)); draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); draw_bottom_box(dialog); draw_title(dialog, title); wattrset(dialog, dialog_attr); print_autowrap(dialog, prompt, height, width); /* compute positions of hour, month and year boxes */ memset(&hr_box, 0, sizeof(hr_box)); memset(&mn_box, 0, sizeof(mn_box)); memset(&sc_box, 0, sizeof(sc_box)); if (init_object(&hr_box, dialog, (width - MIN_WIDE + 1) / 2 + MARGIN, (height - MIN_HIGH + MARGIN), ONE_WIDE, ONE_HIGH, 24, hour >= 0 ? hour : current.tm_hour, 'H') < 0 || DrawObject(&hr_box) < 0) return -1; mvwprintw(dialog, hr_box.y, hr_box.x + ONE_WIDE + MARGIN, ":"); if (init_object(&mn_box, dialog, hr_box.x + (ONE_WIDE + 2 * MARGIN + 1), hr_box.y, hr_box.width, hr_box.height, 60, minute >= 0 ? minute : current.tm_min, 'M') < 0 || DrawObject(&mn_box) < 0) return -1; mvwprintw(dialog, mn_box.y, mn_box.x + ONE_WIDE + MARGIN, ":"); if (init_object(&sc_box, dialog, mn_box.x + (ONE_WIDE + 2 * MARGIN + 1), mn_box.y, mn_box.width, mn_box.height, 60, second >= 0 ? second : current.tm_sec, 'S') < 0 || DrawObject(&sc_box) < 0) return -1; while (result == -2) { BOX *obj = (state == sHR ? &hr_box : (state == sMN ? &mn_box : (state == sSC ? &sc_box : 0))); button = (state == sCANCEL) ? 1 : 0; dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); if (obj != 0) dlg_set_focus(dialog, obj->window); key = mouse_wgetch(dialog); if ((key2 = dlg_char_to_button(key, buttons)) >= 0) { result = key2; } else { switch (key) { case M_EVENT + 0: result = DLG_EXIT_OK; break; case M_EVENT + 1: result = DLG_EXIT_CANCEL; break; case M_EVENT + 'H': state = sHR; break; case M_EVENT + 'M': state = sMN; break; case M_EVENT + 'S': state = sSC; break; case ESC: result = -1; break; case ' ': case '\n': result = button; break; case KEY_BTAB: state = prev_object(state); break; case TAB: state = next_object(state); break; default: if (obj != 0) { int step = next_or_previous(key); if (step != 0) { obj->value += step; while (obj->value < 0) obj->value += obj->period; obj->value %= obj->period; (void) DrawObject(obj); } } break; } } } del_window(dialog); sprintf(dialog_vars.input_result, "%02d:%02d:%02d\n", hr_box.value, mn_box.value, sc_box.value); mouse_free_regions(); return result; }
/* * Display a menu for choosing among a number of options */ int dialog_menu(const char *title, const char *cprompt, int height, int width, int menu_height, int item_no, char **items) { int i, j, x, y, cur_x, cur_y, box_x, box_y; int key = 0, fkey; int button = 0; int choice = dlg_default_item(items, MENUBOX_TAGS); int result = DLG_EXIT_UNKNOWN; int scrollamt = 0; int max_choice, min_width; int found; int use_width, name_width, text_width; WINDOW *dialog, *menu; char *prompt = strclone(cprompt); const char **buttons = dlg_ok_labels(); tab_correct_str(prompt); if (menu_height == 0) { min_width = calc_listw(item_no, items, MENUBOX_TAGS) + 10; /* calculate height without items (4) */ auto_size(title, prompt, &height, &width, 4, MAX(26, min_width)); calc_listh(&height, &menu_height, item_no); } else { auto_size(title, prompt, &height, &width, 4 + menu_height, 26); } print_size(height, width); ctl_size(height, width); /* Find out maximal number of displayable items at once. */ max_choice = MIN(menu_height, RowHeight(item_no)); if (dialog_vars.input_menu) max_choice /= INPUT_ROWS; x = box_x_ordinate(width); y = box_y_ordinate(height); dialog = new_window(height, width, y, x); mouse_setbase(x, y); draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); draw_bottom_box(dialog); draw_title(dialog, title); wattrset(dialog, dialog_attr); print_autowrap(dialog, prompt, height, width); menu_width = width - 6; getyx(dialog, cur_y, cur_x); box_y = cur_y + 1; box_x = (width - menu_width) / 2 - 1; /* create new window for the menu */ menu = sub_window(dialog, menu_height, menu_width, y + box_y + 1, x + box_x + 1); /* draw a box around the menu items */ draw_box(dialog, box_y, box_x, menu_height + 2, menu_width + 2, menubox_border_attr, menubox_attr); name_width = 0; text_width = 0; /* Find length of longest item to center menu * * only if --menu was given, using --inputmenu * * won't be centered. */ for (i = 0; i < item_no; i++) { name_width = MAX(name_width, dlg_count_columns(ItemName(i))); text_width = MAX(text_width, dlg_count_columns(ItemText(i))); } /* If the name+text is wider than the list is allowed, then truncate * one or both of them. If the name is no wider than 1/4 of the list, * leave it intact. */ use_width = (menu_width - GUTTER); if (text_width + name_width > use_width) { int need = 0.25 * use_width; if (name_width > need) { int want = use_width * ((double) name_width) / (text_width + name_width); name_width = (want > need) ? want : need; } text_width = use_width - name_width; } tag_x = (dialog_vars.input_menu ? 0 : (use_width - text_width - name_width) / 2); item_x = name_width + tag_x + GUTTER; if (choice - scrollamt >= max_choice) { scrollamt = choice - (max_choice - 1); choice = max_choice - 1; } /* Print the menu */ for (i = 0; i < max_choice; i++) print_item(menu, ItemData(i + scrollamt), i, i == choice); (void) wnoutrefresh(menu); /* register the new window, along with its borders */ mouse_mkbigregion(box_y + 1, box_x, menu_height + 2, menu_width + 2, KEY_MAX, 1, 1, 1 /* by lines */ ); dlg_draw_arrows(dialog, scrollamt, scrollamt + max_choice < item_no, box_x + tag_x + 1, box_y, box_y + menu_height + 1); dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); wtimeout(dialog, WTIMEOUT_VAL); while (result == DLG_EXIT_UNKNOWN) { key = mouse_wgetch(dialog, &fkey); if (!fkey) { fkey = TRUE; switch (key) { case '\n': case '\r': key = KEY_ENTER; break; case '-': key = KEY_UP; break; case '+': key = KEY_DOWN; break; case ' ': case TAB: key = KEY_RIGHT; break; case ESC: result = DLG_EXIT_ESC; continue; default: fkey = FALSE; break; } } found = FALSE; if (fkey) { /* * Allow a mouse-click on a box to switch selection to that box. * Handling a button click is a little more complicated, since we * push a KEY_ENTER back onto the input stream so we'll put the * cursor at the right place before handling the "keypress". */ if (key >= (M_EVENT + KEY_MAX)) { key -= (M_EVENT + KEY_MAX); i = RowToItem(key); found = TRUE; } else if (key >= M_EVENT && dlg_ok_buttoncode(key - M_EVENT) >= 0) { button = (key - M_EVENT); ungetch('\n'); continue; } } else { /* * Check if key pressed matches first character of any item tag in * list. If there is more than one match, we will cycle through * each one as the same key is pressed repeatedly. */ for (j = scrollamt + choice + 1; j < item_no; j++) { if (dlg_match_char(dlg_last_getc(), ItemName(j))) { found = TRUE; i = j - scrollamt; break; } } if (!found) { for (j = 0; j <= scrollamt + choice; j++) { if (dlg_match_char(dlg_last_getc(), ItemName(j))) { found = TRUE; i = j - scrollamt; break; } } } if (found) dlg_flush_getc(); /* * A single digit (1-9) positions the selection to that line in the * current screen. */ if (!found && (key <= '9') && (key > '0') && (key - '1' < max_choice)) { found = TRUE; i = key - '1'; } } if (!found && fkey) { found = TRUE; switch (key) { case KEY_HOME: i = -scrollamt; break; case KEY_LL: case KEY_END: i = item_no - 1 - scrollamt; break; case M_EVENT + KEY_PPAGE: case KEY_PPAGE: if (choice) i = 0; else if (scrollamt != 0) i = -MIN(scrollamt, max_choice); else continue; break; case M_EVENT + KEY_NPAGE: case KEY_NPAGE: i = MIN(choice + max_choice, item_no - scrollamt - 1); break; case KEY_UP: i = choice - 1; if (choice == 0 && scrollamt == 0) continue; break; case KEY_DOWN: i = choice + 1; if (scrollamt + choice >= item_no - 1) continue; break; default: found = FALSE; break; } } if (found) { if (i != choice) { getyx(dialog, cur_y, cur_x); if (i < 0 || i >= max_choice) { #if defined(NCURSES_VERSION_MAJOR) && NCURSES_VERSION_MAJOR < 5 /* * Using wscrl to assist ncurses scrolling is not needed * in version 5.x */ if (i == -1) { if (menu_height > 1) { /* De-highlight current first item */ print_item(menu, ItemData(scrollamt), 0, FALSE); scrollok(menu, TRUE); wscrl(menu, -RowHeight(1)); scrollok(menu, FALSE); } scrollamt--; print_item(menu, ItemData(scrollamt), 0, TRUE); } else if (i == max_choice) { if (menu_height > 1) { /* De-highlight current last item before scrolling up */ print_item(menu, ItemData(scrollamt + max_choice - 1), max_choice - 1, FALSE); scrollok(menu, TRUE); wscrl(menu, RowHeight(1)); scrollok(menu, FALSE); } scrollamt++; print_item(menu, ItemData(scrollamt + max_choice - 1), max_choice - 1, TRUE); } else #endif { if (i < 0) { scrollamt += i; choice = 0; } else { choice = max_choice - 1; scrollamt += (i - max_choice + 1); } for (i = 0; i < max_choice; i++) { print_item(menu, ItemData(scrollamt + i), i, i == choice); } } /* Clean bottom lines */ if (dialog_vars.input_menu) { int spare_lines, x_count; spare_lines = menu_height % INPUT_ROWS; wattrset(menu, menubox_attr); for (; spare_lines; spare_lines--) { wmove(menu, menu_height - spare_lines, 0); for (x_count = 0; x_count < menu_width; x_count++) { waddch(menu, ' '); } } } (void) wnoutrefresh(menu); dlg_draw_arrows(dialog, scrollamt, scrollamt + choice < item_no - 1, box_x + tag_x + 1, box_y, box_y + menu_height + 1); } else { /* De-highlight current item */ print_item(menu, ItemData(scrollamt + choice), choice, FALSE); /* Highlight new item */ choice = i; print_item(menu, ItemData(scrollamt + choice), choice, TRUE); (void) wnoutrefresh(menu); (void) wmove(dialog, cur_y, cur_x); wrefresh(dialog); } } continue; /* wait for another key press */ } if (fkey) { switch (key) { case KEY_BTAB: case KEY_LEFT: button = dlg_prev_button(buttons, button); dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); break; case KEY_RIGHT: button = dlg_next_button(buttons, button); dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); break; case KEY_ENTER: del_window(dialog); result = handle_button(dlg_ok_buttoncode(button), items, scrollamt + choice); if (dialog_vars.input_menu && result == DLG_EXIT_EXTRA) { char *tmp; tmp = input_menu_edit(menu, ItemData(scrollamt + choice), choice); dialog_vars.input_result[0] = '\0'; dlg_add_result("RENAMED "); dlg_add_result(ItemName(scrollamt + choice)); dlg_add_result(" "); dlg_add_result(tmp); free(tmp); dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); } break; default: flash(); break; } } } mouse_free_regions(); del_window(dialog); free(prompt); return result; }