#include #include #include #include #include typedef enum { FOCUS_GUILDS = 0, FOCUS_CHAT, FOCUS_INPUT, } focus_t; struct ncdc_mainwindow_ { dc_refable_t ref; WINDOW *guilds; int guilds_w; int guilds_h; int guilds_y; int guilds_x; WINDOW *chat; int chat_h; int chat_w; int chat_y; int chat_x; WINDOW *input; int input_w; int input_h; int input_y; int input_x; int input_curs_x; WINDOW *sep1; WINDOW *sep2; ncdc_input_t in; GPtrArray *views; int curview; ncdc_textview_t log; int focus; }; static void ncdc_mainwindow_resize(ncdc_mainwindow_t n); static void ncdc_mainwindow_update_focus(ncdc_mainwindow_t n); static bool ncdc_mainwindow_callback(ncdc_input_t i, wchar_t const *s, size_t len, void *arg); static void ncdc_mainwindow_free(ncdc_mainwindow_t n) { return_if_true(n == NULL,); delwin(n->guilds); delwin(n->chat); delwin(n->input); delwin(n->sep1); delwin(n->sep2); dc_unref(n->in); if (n->views != NULL) { g_ptr_array_unref(n->views); n->views = NULL; } free(n); } ncdc_mainwindow_t ncdc_mainwindow_new(void) { ncdc_mainwindow_t ptr = calloc(1, sizeof(struct ncdc_mainwindow_)); return_if_true(ptr == NULL, NULL); ptr->ref.cleanup = (dc_cleanup_t)ncdc_mainwindow_free; ptr->in = ncdc_input_new(); ncdc_input_set_callback(ptr->in, ncdc_mainwindow_callback, ptr); ptr->views = g_ptr_array_new_with_free_func( (GDestroyNotify)dc_unref ); ptr->log = ncdc_textview_new(); ncdc_textview_set_title(ptr->log, L"status"); g_ptr_array_add(ptr->views, ptr->log); ptr->guilds = newwin(5, 5, 1, 1); ptr->chat = newwin(5, 5, 4, 4); ptr->input = newwin(5, 5, 8, 8); keypad(ptr->input, TRUE); ptr->sep1 = newwin(5, 5, 10, 10); ptr->sep2 = newwin(5, 5, 12, 12); ncdc_mainwindow_resize(ptr); ptr->focus = FOCUS_INPUT; ncdc_mainwindow_update_focus(ptr); return ptr; } static bool ncdc_mainwindow_callback(ncdc_input_t i, wchar_t const *s, size_t len, void *arg) { ncdc_mainwindow_t mainwin = (ncdc_mainwindow_t)arg; bool ret = false; if (s == NULL || s[0] == '\0') { return false; } if (s[0] == '/') { ret = ncdc_dispatch(mainwin, s); } else { wchar_t *post = calloc(wcslen(s)+7, sizeof(wchar_t)); wcscat(post, L"/post "); wcscat(post, s); ret = ncdc_dispatch(mainwin, post); free(post); } return ret; } static void ncdc_mainwindow_resize(ncdc_mainwindow_t n) { n->guilds_h = LINES - 2; n->guilds_w = (COLS / 5); n->guilds_y = 0; n->guilds_x = 0; wresize(n->guilds, n->guilds_h, n->guilds_w); mvwin(n->guilds, n->guilds_y, n->guilds_x); wnoutrefresh(n->guilds); n->input_h = 1; n->input_w = COLS; n->input_y = LINES - n->input_h; n->input_x = 0; wresize(n->input, n->input_h, n->input_w); mvwin(n->input, n->input_y, n->input_x); wnoutrefresh(n->input); wresize(n->sep1, 1, COLS); mvwin(n->sep1, LINES - 2, 0); wbkgd(n->sep1, COLOR_PAIR(1)); wnoutrefresh(n->sep1); n->chat_h = LINES - n->input_h - 1; n->chat_w = COLS - n->guilds_w - 2; n->chat_y = 0; n->chat_x = n->guilds_w + 2; wresize(n->chat, n->chat_h, n->chat_w); mvwin(n->chat, n->chat_y, n->chat_x); wnoutrefresh(n->chat); wresize(n->sep2, LINES - 2, 1); mvwin(n->sep2, 0, n->guilds_w + 1); wnoutrefresh(n->sep2); ncdc_mainwindow_update_focus(n); } static void ncdc_mainwindow_update_focus(ncdc_mainwindow_t n) { switch (n->focus) { case FOCUS_GUILDS: { } break; case FOCUS_CHAT: { } break; case FOCUS_INPUT: { wmove(n->input, 0, ncdc_input_cursor(n->in)); wrefresh(n->input); } break; } } static void ncdc_mainwindow_render_status(ncdc_mainwindow_t n) { wchar_t *status = NULL; size_t statuslen = 0; wchar_t timestr[100] = {0}; time_t tm = time(NULL); struct tm *t = localtime(&tm); FILE *f = open_wmemstream(&status, &statuslen); wchar_t const *wintitle = NULL; ncdc_textview_t view = NULL; size_t i = 0; dc_channel_t channel = NULL; werase(n->sep1); return_if_true(f == NULL,); wcsftime(timestr, 99, L"[%H:%M]", t); fwprintf(f, L"%ls", timestr); if (!is_logged_in()) { fwprintf(f, L" [not logged in]"); } else { dc_account_t current_account = dc_session_me(current_session); fwprintf(f, L" [%s]", dc_account_fullname(current_account)); } view = g_ptr_array_index(n->views, n->curview); wintitle = ncdc_textview_title(view); fwprintf(f, L" [%d: %ls]", n->curview, (wintitle != NULL ? wintitle : L"n/a") ); fwprintf(f, L" [Act:"); for (i = 0; i < n->views->len; i++) { view = g_ptr_array_index(n->views, i); channel = ncdc_textview_channel(view); if (channel != NULL && dc_channel_has_new_messages(channel)) { fwprintf(f, L" %d", i); } } fwprintf(f, L"]"); fclose(f); mvwaddwstr(n->sep1, 0, 0, status); free(status); } void ncdc_mainwindow_input_ready(ncdc_mainwindow_t n) { wint_t i = 0; wchar_t *key = NULL; size_t keylen = 0; WINDOW *win = NULL; ncdc_keybinding_t *k = NULL; switch (n->focus) { case FOCUS_INPUT: win = n->input; break; case FOCUS_CHAT: win = n->chat; break; case FOCUS_GUILDS: win = n->guilds; break; } if (wget_wch(win, &i) == ERR) { return; } if (i == KEY_RESIZE) { ncdc_mainwindow_resize(n); return; } if (i == KEY_ESCAPE) { if ((key = util_readkey(i, n->input)) == NULL) { return; } keylen = wcslen(key); } FILE *f = fopen("keys.txt", "a+"); fwprintf(f, L"%d - %ls\n", i, (key == NULL ? L"n/a" : &key[1])); fclose(f); if (key != NULL && (k = ncdc_find_keybinding(keys_mainwin, key, keylen)) != NULL) { k->handler(n); return; } if (n->focus == FOCUS_INPUT) { if (key == NULL) { ncdc_input_feed(n->in, (wchar_t const *)&i, sizeof(wchar_t)); } else { ncdc_input_feed(n->in, key, wcslen(key)); } } free(key); } GPtrArray *ncdc_mainwindow_views(ncdc_mainwindow_t n) { return n->views; } void ncdc_mainwindow_switchview(ncdc_mainwindow_t n, int idx) { return_if_true(n == NULL || n->views == NULL,); return_if_true(idx >= n->views->len,); n->curview = idx; } void ncdc_mainwindow_refresh(ncdc_mainwindow_t n) { ncdc_textview_t v = 0; wnoutrefresh(n->guilds); /* render active text view */ v = g_ptr_array_index(n->views, n->curview); ncdc_textview_render(v, n->chat, n->chat_h, n->chat_w); wnoutrefresh(n->chat); ncdc_input_draw(n->in, n->input); wnoutrefresh(n->input); wbkgd(n->sep1, COLOR_PAIR(1)); ncdc_mainwindow_render_status(n); wnoutrefresh(n->sep1); wbkgd(n->sep2, COLOR_PAIR(1)); wnoutrefresh(n->sep2); ncdc_mainwindow_update_focus(n); } void ncdc_mainwindow_log(ncdc_mainwindow_t w, wchar_t const *fmt, ...) { va_list lst; wchar_t buf[256] = {0}; return_if_true(w == NULL || fmt == NULL,); va_start(lst, fmt); vswprintf(buf, 255, fmt, lst); va_end(lst); ncdc_textview_append(w->log, buf); } static void ncdc_mainwindow_ack_view(ncdc_mainwindow_t n) { #if 0 dc_channel_t c = ncdc_mainwindow_current_channel(n); return_if_true(c == NULL,); return_if_true(dc_channel_messages(c) == 0,); dc_message_t m = dc_channel_nth_message(c, dc_channel_messages(c)-1); dc_api_channel_ack(dc_session_api(current_session), dc_session_me(current_session), c, m ); #endif } void ncdc_mainwindow_rightview(ncdc_mainwindow_t n) { return_if_true(n == NULL,); n->curview = (n->curview + 1) % n->views->len; ncdc_mainwindow_ack_view(n); } void ncdc_mainwindow_leftview(ncdc_mainwindow_t n) { return_if_true(n == NULL,); n->curview = (n->curview - 1) % n->views->len; ncdc_mainwindow_ack_view(n); } dc_channel_t ncdc_mainwindow_current_channel(ncdc_mainwindow_t n) { return_if_true(n == NULL, NULL); ncdc_textview_t view = g_ptr_array_index(n->views, n->curview); /* can't post to the log channel, that's for internal use only */ return_if_true(view == n->log, NULL); return ncdc_textview_channel(view); }