AmendHub

Download:

jcs

/

wallops

/

amendments

/

109

chatter: Implement tab completion of nicks


jcs made amendment 109 about 1 month ago
--- chatter.c Mon Sep 16 10:34:36 2024 +++ chatter.c Mon Sep 16 11:56:02 2024 @@ -40,6 +40,7 @@ void chatter_focus_tab(struct chatter *chatter, struct void chatter_draw_grow_icon(struct chatter *chatter); void chatter_autoscroll(struct chatter *chatter, TEHandle te, ControlHandle scroller, bool force); +void chatter_set_input(struct chatter *chatter, char *input, size_t len); short chatter_wait_type(struct focusable *focusable); void chatter_key_down(struct focusable *focusable, EventRecord *event); void chatter_mouse_down(struct focusable *focusable, EventRecord *event); @@ -53,6 +54,7 @@ bool chatter_close(struct focusable *focusable); bool chatter_quit(struct focusable *focusable); void chatter_use_shadow(struct chatter *chatter); void chatter_reveal_shadow(struct chatter *chatter); +bool chatter_tab_complete(struct chatter *chatter); struct chatter * chatter_init(const char *server, const unsigned short port, @@ -344,6 +346,16 @@ chatter_layout_tab(struct chatter *chatter, struct cha } void +chatter_set_input(struct chatter *chatter, char *input, size_t len) +{ + TESetText(input, len, chatter->input_te); + TEPinScroll(-SHRT_MAX, SHRT_MAX, chatter->input_te); + TEIdle(chatter->input_te); + /* this is needed to get TEIdle working again */ + TESelView(chatter->input_te); +} + +void chatter_focus_tab(struct chatter *chatter, struct chatter_tab *tab) { RgnHandle clip; @@ -425,6 +437,9 @@ chatter_focus_tab(struct chatter *chatter, struct chat if (chatter->focusable) chatter_update_menu(chatter->focusable); + + /* invalidate any current tab completion */ + chatter->tab_comp_match[0] = chatter->tab_comp_input[0] = '\0'; } void @@ -1023,24 +1038,30 @@ chatter_key_down(struct focusable *focusable, EventRec char k; k = (event->message & charCodeMask); + + if (k == '\t') { + chatter_tab_complete(chatter); + return; + } + + if (chatter->tab_comp_input[0] != '\0') { + chatter->tab_comp_input[0] = '\0'; + chatter->tab_comp_match[0] = '\0'; + } + if (k == '\r') { HLock(chatter->input_te); te = *(chatter->input_te); HLock(te->hText); memcpy(chatter->input, *(te->hText), MIN(te->teLength, sizeof(chatter->input) - 1)); - chatter->input[MIN(te->teLength, sizeof(chatter->input) - 1)] = '\0'; - EraseRect(&te->viewRect); - TESetText("", 0, chatter->input_te); - TEPinScroll(-SHRT_MAX, SHRT_MAX, chatter->input_te); - ValidRect(&te->viewRect); - TEIdle(chatter->input_te); HUnlock(te->hText); HUnlock(chatter->input_te); + chatter->input[MIN(te->teLength, sizeof(chatter->input) - 1)] = '\0'; + EraseRect(&te->viewRect); + chatter_set_input(chatter, "", 0); irc_process_input(tab->conn, tab->channel, (tab->query_nick[0] ? tab->query_nick : NULL), chatter->input); - /* this is needed to get TEIdle working again */ - TESelView(chatter->input_te); } else if (te->teLength < IRC_MAX_MSG_SIZE - 1) { TEKey(k, chatter->input_te); TESelView(chatter->input_te); @@ -1476,4 +1497,72 @@ chatter_reveal_shadow(struct chatter *chatter) CopyBits(&chatter->shadow, &chatter->win->portBits, &chatter->shadow.bounds, &chatter->shadow.bounds, srcCopy, nil); ValidRect(&chatter->win->portRect); -} +} + +bool +chatter_tab_complete(struct chatter *chatter) +{ + TERec *te; + size_t size; + long n; + struct irc_channel *channel; + struct irc_channel_nick *nick; + char *skip_until = NULL; + + if (!chatter->current_tab || !chatter->current_tab->channel) + return false; + + if (chatter->tab_comp_input[0] == '\0') { + /* new search, store original input */ + HLock(chatter->input_te); + te = *(chatter->input_te); + HLock(te->hText); + size = MIN(te->teLength, sizeof(chatter->tab_comp_input) - 1); + memcpy(chatter->tab_comp_input, *(te->hText), size); + chatter->tab_comp_input[size] = '\0'; + } else + size = strlen(chatter->tab_comp_input); + + /* if previous match, skip nicks in chain until we see it again */ + if (chatter->tab_comp_match[0] != '\0') + skip_until = chatter->tab_comp_match; + + channel = chatter->current_tab->channel; + nick = &channel->nicks[channel->first_nick]; + while (nick) { + if (strncasecmp(nick->nick, chatter->tab_comp_input, size) != 0) + goto next_nick; + + /* but don't let us win */ + if (strcasecmp(nick->nick, channel->connection->nick) == 0) + goto next_nick; + + /* if we had a last match, skip until we see it again */ + if (skip_until) { + if (strcmp(nick->nick, skip_until) == 0) + skip_until = NULL; + goto next_nick; + } + + strlcpy(chatter->tab_comp_match, nick->nick, + sizeof(chatter->tab_comp_match)); + size = snprintf(chatter->input, sizeof(chatter->input), "%s: ", + nick->nick); + chatter_set_input(chatter, chatter->input, size); + return true; + +next_nick: + if (nick->next_nick == -1) + break; + nick = &channel->nicks[nick->next_nick]; + } + + /* no match (or cycled through all matches), return to original input */ + if (chatter->tab_comp_match[0] != '\0') { + chatter_set_input(chatter, chatter->tab_comp_input, + strlen(chatter->tab_comp_input)); + chatter->tab_comp_match[0] = chatter->tab_comp_input[0] = '\0'; + } + + return false; +} \ No newline at end of file --- chatter.h Mon Sep 16 09:16:13 2024 +++ chatter.h Mon Sep 16 11:20:22 2024 @@ -92,6 +92,8 @@ struct chatter { short shadow_refcnt; TEHandle input_te; char input[IRC_MAX_MSG_SIZE]; + char tab_comp_input[member_size(struct irc_user, nick)]; + char tab_comp_match[member_size(struct irc_user, nick)]; bool quitting; bool need_tab_bar_redraw; short ntabs;