diff options
Diffstat (limited to 'src/ui.c')
-rw-r--r-- | src/ui.c | 635 |
1 files changed, 268 insertions, 367 deletions
@@ -1,378 +1,279 @@ -#pragma once #include <stdlib.h> -#include <stdio.h> -#include <time.h> -#include <api.c> -#include <ncursesw/ncurses.h> -#include <ncursesw/form.h> -#include <unistd.h> -#include <locale.h> +#include <glib.h> +#include <glib/gprintf.h> +#include <gtk/gtk.h> #include <string.h> -#include <assert.h> -#include <signal.h> -#define DC_SIMPLEPRINT(w, c, f, ...) do { wattron(w, COLOR_PAIR(c)); wprintw(w, f __VA_OPT__(,) __VA_ARGS__); wrefresh(w); } while (0) -/* link with -lncursesw and -lformw */ -void dc_null() { - return; /* a simple null function */ -} -int dc_ui_print_message (WINDOW * textwin, struct dc_message * msg2do) { - int y, x; - char timestring[64]; - struct tm timestruct; - localtime_r(&msg2do->time, ×truct); - strftime(timestring, 64, DC_I18N_MSGTIMEF, ×truct); /* recimo, da je 23 znakov */ - DC_SIMPLEPRINT(textwin, 1, "%.35s", msg2do->channel->name); - getyx(textwin, y, x); - wmove(textwin, y, 10); - DC_SIMPLEPRINT(textwin, 2, " %.18s ", timestring); - DC_SIMPLEPRINT(textwin, 4, "%.33s", msg2do->username); - getyx(textwin, y, x); - wmove(textwin, y, 37); /* quick mafs */ - DC_SIMPLEPRINT(textwin, 3, ": %s%s", msg2do->content ? msg2do->content : "", msg2do->attachment ? " " : "\n"); - if (msg2do->attachment) - DC_SIMPLEPRINT(textwin, 8, "[" DC_I18N_ATTACHMENT "]: %s\n", msg2do->attachment); - msg2do->status = 1; - if (x); /* set but not used */ - return 1; -} -struct dc_ui { - int maxy; /* max line of screen */ - int maxx; /* max column of screen */ +unsigned char dc_ui_def_u[] = { +#include <ui.xxd> +}; +char * dc_ui_def = (char *) dc_ui_def_u; +#define DC_UI_SET_STATUS(b, s) gtk_label_set_text(GTK_LABEL(gtk_builder_get_object(b, "dc_main_status")), s) +struct dc_ui_data { + GtkBuilder * b; + GKeyFile * k; +}; +enum dc_permissions { /* other permissions exist, but are not implemented/understood */ + DC_ALL_PERMISSIONS = 1 << 3, /* this is incredibly retarded, why is this SEPARATE?!? - admins */ + DC_CHANNEL_VIEW = 1 << 10, /* all enum fields here have values same as the server values */ + DC_MESSAGE_SEND = 1 << 11, + DC_MESSAGE_READ = 1 << 16, /* na tistem vegova serverju sem lahko pošiljal ne pa bral sporočil */ + DC_VOICE_LISTEN = 1 << 20, + DC_VOICE_SPEAK = 1 << 21 +}; +enum dc_channel_type { /* other types exist, but are not implemented/understood */ + DC_TEXT, /* all enum fields here have values same as the values that the server sends */ + DC_DM, + DC_VOICE, + DC_DM2 /* retarded. server sometimes sends this... converted to DC_DM at parsing server resp. */ +}; +struct dc_program { /* parent struct of dc_client, in case multi-login will ever be implemented (no) */ + struct dc_client * clients; /* yesfree */ /* dc_program contains the storage of all */ + size_t clients_sizeof; /* structs in program. freeing is done from */ + struct dc_guild * guilds; /* yesfree */ /* here and clasification (chans of a guild) */ + size_t guilds_sizeof; /* is done via the use of linked lists. */ + struct dc_channel * channels; /* yesfree */ /* before a network query, this storage may be */ + size_t channels_sizeof; /* used to check if we already have the info */ + struct dc_message * messages; /* yesfree */ /* already. for example to get dc_user from */ + size_t messages_sizeof; /* user id-user may be available on another */ + struct dc_role * roles; /* yesfree */ /* guild. sizeof=length so make sure heap */ + size_t roles_sizeof; /* *alloc()ations are fast. they are on linux */ +}; /* http://ž.ga/linuxfast */ +struct dc_user { + unsigned long long int id; + short int discriminator; + char * username; /* yesfree */ +}; +struct dc_role { + unsigned long long int id; + char * name; /* yesfree */ + enum dc_permissions permissions; + struct dc_guild * guild; /* nofree - owner of the role */ + struct dc_role * next; /* nofree - next role (linked list of all roles of dc_guild) */ +}; +struct dc_role_membership { + struct dc_guild * guild; /* nofree */ + struct dc_user * user; /* nofree */ + struct dc_role * role; /* nofree */ +}; +struct dc_client { + char * authorization; /* yesfree - authorization header value */ + char * email; /* yesfree */ + char * password; /* yesfree */ + struct dc_user * user; /* nofree - logged in user */ + struct dc_guild * guild; /* nofree - first guild */ +}; +struct dc_guild { + char * name; /* yesfree */ + unsigned long long int id; /* 0 for virtual DMs guild */ + struct dc_client * client; /* nofree */ + char * alternative_messages_url; /* yesfree, internal - alternative messages url - for virtual DMs guild */ + struct dc_guild * next; /* nofree - next guild (linked list of all guilds of dc_client) */ + struct dc_channel * channel; /* nofree - first channel */ + struct dc_role * role; /* nofree - first role. NOTE: first role is always role with role ID that is same as guild ID and it is the @everyone role */ + enum dc_permissions permissions; }; -int dc_ui_processline (struct dc_thread_control * t, char * l, WINDOW * textwin, struct dc_ui ui) { - struct dc_client * c = t->clients[0]; - /* first we trim spaces at the end */ - int i = 0, j = 0, k = 0, m = 0, n = 0; - char * jp; - for (i = strlen(l)-1; i >= 0; i--) - if (l[i] == ' ') - l[i] = '\0'; - else +struct dc_permission { /* permissions can be individual on a per-channel basis */ + struct dc_permission * next; /* nofree - next permission (linked list of all perms of channel) */ + enum dc_permissions allow; + enum dc_permissions deny; + unsigned long long int id; /* to whom does this permission apply */ + struct dc_channel * channel; /* nofree - on which channel does it apply */ + struct dc_user user; /* non-null if permission applies to a user */ + struct dc_role role; /* non-null if it applies to a role */ + int type; /* 0=role, 1=member NOTE: user and role may not be filled at start, check id in case */ +}; /* permissions are only useful for checking OUR permissions, not others'. keep that in mind. */ +struct dc_channel { + char * name; /* yesfree - name */ + char * topic; /* yesfree - topic */ + unsigned long long int id; + enum dc_channel_type type; + struct dc_guild * guild; /* nofree */ + struct dc_channel * next; /* nofree - next channel (linked list of all guilds of dc_guild) */ + struct dc_message * message; /* nofree - first message (ordered by time) */ +}; +struct dc_message { + struct dc_channel * channel; /* nofree */ + struct dc_user * user; /* nofree */ + char * message; /* yesfree */ + char * attachment; /* yesfree */ + time_t time; + unsigned long long int id; + struct dc_message * next; /* next message (linked list of all messages of dc_channel) */ +}; +/* + # configuration file - loaded at startup, saved at exit, comments persist - description: + [discord.c] + multiline = true|false + login = string + password = string +*/ +void dc_ui_spawn_message (struct dc_message * m, struct dc_ui_data * d) { /* !m to clear messages */ + size_t i = 0; + GtkWidget * b; + GtkWidget * w, * w2; +#define DC_USMTL 32 + char t[DC_USMTL]; + GtkGrid * g = GTK_GRID(gtk_builder_get_object(d->b, "dc_main_messages")); + if (!m) { + while (gtk_grid_get_child_at(g, 0, 0)) + gtk_grid_remove_row(g, 0); + return; + } + while ((w = gtk_grid_get_child_at(g, 0, i))) { /* now we get the index BEFORE which message will be placed */ + struct dc_message * before, * after; + before = (struct dc_message *) g_object_get_data(G_OBJECT(w), "message"); /* this literally mustn't and can't be NULL */ + if ((w2 = gtk_grid_get_child_at(g, 0, i+1))) + after = (struct dc_message *) g_object_get_data(G_OBJECT(w2), "message"); /* same here */ + else { /* there is nothing after, message is new */ + i++; /* BEFORE WHICH */ break; - if (l[0] == '/') - switch (l[1]) { - case 'g': - case 'G': - case 's': - case 'S': /* servers */ - DC_CRLE(c, c->guilds_lock); - for (i = 0; i < c->guilds_sizeof; i++) - DC_SIMPLEPRINT(textwin, 4, " %02d. %s\n", i, c->guilds[i]->name); - DC_CUE(c, c->guilds_lock); - break; - case 'c': - case 'C': - case 'k': - case 'K': - DC_CRLE(c, c->guilds_lock); - if (!strchr(l, ' ') || (j = atoi(strchr(l, ' ')+1)) >= c->guilds_sizeof) { - DC_SIMPLEPRINT(textwin, 1, DC_I18N_UI_USAGE ": " DC_I18N_UI_CHANNELS_USAGE "\n", c->guilds_sizeof-1); - DC_CUE(c, c->guilds_lock); - break; - } - for (i = 0; i < c->guilds[j]->channels_sizeof; i += 2) { - int y = 0; - int x = 0; - getyx(textwin, y, x); - DC_SIMPLEPRINT(textwin, 4, " %02d. %s - %s", i, c->guilds[j]->channels[i]->name, c->guilds[j]->channels[i]->topic); - if (c->guilds[j]->channels_sizeof-1 == i) { - DC_SIMPLEPRINT(textwin, 1, "\n"); - break; - } - wmove(textwin, y, ui.maxx/2); - if (x); /* prevent unused warnings */ - DC_SIMPLEPRINT(textwin, 7, " %02d. %s - %s\n", i+1, c->guilds[j]->channels[i+1]->name, c->guilds[j]->channels[i+1]->topic); - } - DC_CUE(c, c->guilds_lock); - break; - case 'j': - case 'J': - case 'p': - case 'P': - DC_CWLE(c, c->guilds_lock); -#define DC_UI_PL_GC() /* get guild and channel. ONLY USE IN THE CONTEXT (switch statement case) OF dc_ui_processline !!! */ \ - if (!(jp = strchr(l, ' ')) || (j = atoi(jp+1)) >= c->guilds_sizeof) { \ - DC_SIMPLEPRINT(textwin, 1, DC_I18N_UI_USAGE ": " DC_I18N_UI_GC_USAGE "\n", c->guilds_sizeof-1, 999); \ - DC_CUE(c, c->guilds_lock); \ - break; \ - } \ - if (!strchr(jp+1, ' ') || (k = atoi(strchr(jp+1, ' ')+1)) >= c->guilds[j]->channels_sizeof) { \ - DC_SIMPLEPRINT(textwin, 1, DC_I18N_UI_USAGE ": " DC_I18N_UI_GC_USAGE "\n", c->guilds_sizeof-1, c->guilds[j]->channels_sizeof-1); \ - DC_CUE(c, c->guilds_lock); \ - break; \ - } - DC_UI_PL_GC(); - for (i = c->guilds[j]->channels[k]->messages_sizeof-1; i >= 0; i--) - dc_ui_print_message(textwin, c->guilds[j]->channels[k]->messages[i]); - for (m = 0; m < c->guilds_sizeof; m++) /* we loop over all channels */ - for (n = 0; n < c->guilds[m]->channels_sizeof; n++) - c->guilds[m]->channels[n]->focused = 0; /* remove focus from all channels */ - c->guilds[j]->channels[k]->focused = 1; - c->guilds[j]->channels[k]->joined = 1; - DC_CUE(c, c->guilds_lock); - break; - case 'l': - case 'L': - case 'z': /* leave */ - case 'Z': - DC_CWLE(c, c->guilds_lock); - DC_UI_PL_GC(); - c->guilds[j]->channels[k]->focused = 0; - c->guilds[j]->channels[k]->joined = 0; - DC_CUE(c, c->guilds_lock); - break; - case 'q': - case 'Q': - case 'i': - case 'I': - t->power_api = 2; /* 2 for shutdown */ - t->power_ui = 2; - break; - case 'N': /* api nit (thread) control */ - case 'n': - if (!strchr(l, ' ')) { - DC_SIMPLEPRINT(textwin, 1, "!strchr(l, ' ')\n"); - break; - } - t->power_api = atoi(strchr(l, ' ')+1); - DC_SIMPLEPRINT(textwin, 4, "t->power_api = %d\n", atoi(strchr(l, ' ')+1)); - break; - default: - DC_SIMPLEPRINT(textwin, 1, DC_I18N_UI_CNF "\n"); - } - else { /* send the message, it's not a command */ - struct dc_channel * ch = NULL; - for (int m = 0; m < c->guilds_sizeof; m++) /* we loop over all channels */ - for (int n = 0; n < c->guilds[m]->channels_sizeof; n++) - if (c->guilds[m]->channels[n]->focused) - ch = c->guilds[m]->channels[n]; - /* DC_SIMPLEPRINT(textwin, 2, "%s - %s\n", ch->name, ch->guild->name); */ /* debug */ - if (!ch) { - DC_SIMPLEPRINT(textwin, 1, "!ch - %s\n", DC_I18N_UI_NOT_JOINED); - return -1; - } - if (!strlen(l)) { - DC_SIMPLEPRINT(textwin, 1, "!strlen(l) - %s\n", DC_I18N_UI_EMPTYMSG); - return -2; } - if (time(NULL) - c->last_sent_message <= ch->slowmode) { - DC_SIMPLEPRINT(textwin, 1, DC_I18N_UI_SLOWMODE "\n", ch->slowmode, ch->slowmode-(time(NULL)-c->last_sent_message)); - return -3; + if (m->time >= before->time && m->time <= after->time) { /* we've found a spot between two messages */ + i++; /* SAME HERE. if there are no messages already, while will fail immediatley and i will remain 0 */ + break; } - c->last_sent_message = time(NULL); /* because the other thread may not update counter before the next message is sent */ - /* raise(SIGINT); */ /* To continue from here in GDB: "signal 0". */ - DC_CWLE(c, c->sent_messages_lock); - c->sent_messages = realloc(c->sent_messages, sizeof(struct dc_message *)*++c->sent_messages_sizeof); -#define DC_UISM c->sent_messages[c->sent_messages_sizeof-1] /* ui send messaeg */ - DC_UISM = calloc(1, sizeof(struct dc_message)); - DC_UISM->content = malloc(strlen(l)+1); - strcpy(DC_UISM->content, l); - DC_UISM->channel = ch; - DC_CUE(c, c->sent_messages_lock); - /* DO NOT free it */ } - wrefresh(textwin); - return 1; + gtk_grid_insert_row(g, i); + gtk_grid_insert_column(g, i); + b = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0 /* spacing pixels */); + gtk_container_add(GTK_CONTAINER(b), gtk_label_new(m->user->username)); + /* TODO: implement parsing markup here: bold, italic, underline; REMOVE < character; implement tags, timestamps, channels and spoilers with GTK ahrefs */ + strftime(t, DC_USMTL, "%c", localtime(&m->time)); /* singlethreaded only */ + gtk_container_add(GTK_CONTAINER(b), gtk_label_new(t)); + g_object_set_data(G_OBJECT(w), "message", m); + gtk_grid_attach(g /* grid */, b /* widget to insert */, 0 /* left */, i /* top */, 1 /* width */, 1 /* height */); + if (m->user == m->channel->guild->client->user) { /* TODO: if I posted the message, make it an editable textview */ + } + gtk_grid_attach(g, GTK_WIDGET(gtk_label_new(m->message)), 1, i, 1, 1); } -int dc_ui_thread (struct dc_thread_control * t) { - while (!t->power_ui) usleep(250000); - FIELD * field[2]; /* field[0] je polje z besedilom */ - field[1] = NULL; - FORM * form; - int ret, x, y; - wint_t ch; - struct dc_client * c = t->clients[0]; - struct dc_ui ui; - initscr(); - cbreak(); - noecho(); - nodelay(stdscr, TRUE); - setlocale(LC_ALL, ""); - start_color(); - init_pair(2, COLOR_YELLOW, COLOR_BLACK); - init_pair(1, COLOR_RED, COLOR_BLACK); - init_pair(3, COLOR_WHITE, COLOR_BLACK); - init_pair(4, COLOR_GREEN, COLOR_BLACK); - init_pair(5, COLOR_CYAN, COLOR_BLACK); - init_pair(6, COLOR_BLACK, COLOR_CYAN); - init_pair(7, COLOR_MAGENTA, COLOR_BLACK); - init_pair(8, COLOR_BLUE, COLOR_WHITE); - keypad(stdscr, TRUE); - getmaxyx(stdscr, y, x); /* to je macro, zato y in x nista kazalca (;: */ - ui.maxy = y; - ui.maxx = x; - WINDOW * textwin = subwin(stdscr, y-3, x, 0, 0); - WINDOW * formwin = subwin(stdscr, 2, x, y-2, 0); - scrollok(textwin, TRUE); - field[0] = new_field(2, x, y-2, 0, 5 /* offscreen rows */, 0); - set_field_back(field[0], A_UNDERLINE); - field_opts_off(field[0], O_AUTOSKIP); - form = new_form(field); - set_form_win(form, formwin); - post_form(form); - int i = 0; - int updinforow = 1; - wmove(textwin, 0, 0); - refresh(); - while (t->power_ui != 2) { - if (!(rand() % 10)) { /* roughly every 10 cycles we get errors and messages */ - assert(!DC_CRLE(c, c->errors_lock)); - for (int i = 0; i < c->errors_sizeof; i++) { - if (!c->errors[i]->reported) { - DC_SIMPLEPRINT(textwin, 1, "[" DC_I18N_ERROR "] %s()@%s:%lu: %s\n", c->errors[i]->function, c->errors[i]->file, c->errors[i]->line, c->errors[i]->message); - c->errors[i]->reported = 1; - } - } - assert(!pthread_rwlock_unlock(c->errors_lock)); /* deadlock if we unlock errors with error reporting, duh */ - if (c->newmessages || !(rand() % 10)) { /* a race happens */ /* so we add the random every 10*10=100 cycles */ - c->newmessages = 0; /* here inbetween */ - DC_CRLE(c, c->guilds_lock); - for (int m = 0; m < c->guilds_sizeof; m++) /* we loop over all channels */ - for (int n = 0; n < c->guilds[m]->channels_sizeof; n++) { - if (!c->guilds[m]->channels[n]->joined) - continue; - for (int i = c->guilds[m]->channels[n]->messages_sizeof-1; i >= 0; i--) { - struct dc_message * msg2do = c->guilds[m]->channels[n]->messages[i]; - if (!msg2do->status) - dc_ui_print_message(textwin, msg2do); - } - } - DC_CUE(c, c->guilds_lock); - } - if (updinforow) { - updinforow = 0; - curs_set(0); /* too flashy */ - attron(COLOR_PAIR(5)); - int drawn = 0; - int k = 0; - move(y-3, 0); clrtoeol(); /* clear line */ - DC_CRLE(c, c->guilds_lock); - for (int m = 0; m < c->guilds_sizeof; m++) /* we loop over all channels */ - for (int n = 0; n < c->guilds[m]->channels_sizeof; n++) { - if (!c->guilds[m]->channels[n]->joined) - continue; - k++; - if (drawn + strlen(c->guilds[m]->channels[n]->name)+2 > x+1) { - attron(COLOR_PAIR(3)); - for (int l = x-3; l <= x; l++) - mvprintw(y-3, l, "."); - break; - } - if (c->guilds[m]->channels[n]->focused) - attron(COLOR_PAIR(6)); - else - attron(COLOR_PAIR(5)); - mvprintw(y-3, drawn, "#%s(%02.2d %02.2d)", c->guilds[m]->channels[n]->name, m, n); - drawn += strlen(c->guilds[m]->channels[n]->name)+9; /* plus 9: 8 are # and (00 00), the other is for the following space between channels */ - } - DC_CUE(c, c->guilds_lock); - if (!k) { - attron(COLOR_PAIR(2)); - if (t->power_api == 0) - mvprintw(y-3, 0, DC_I18N_UI_LINE_BEFORE_NETWORK); - else - mvprintw(y-3, 0, DC_I18N_UI_LINE_BEFORE_JOIN); - } - curs_set(1); - } - pos_form_cursor(form); - } - ret = get_wch(&ch); - if (ret != ERR) { - switch (ch) { - case KEY_LEFT: - form_driver(form, REQ_PREV_CHAR); - break; - case KEY_RIGHT: - form_driver(form, REQ_NEXT_CHAR); - break; - case KEY_BACKSPACE: - form_driver(form, REQ_DEL_PREV); - break; - case KEY_DOWN: - form_driver(form, REQ_NEXT_LINE); - break; - case KEY_UP: - form_driver(form, REQ_PREV_LINE); - break; - case KEY_DC: - form_driver(form, REQ_DEL_CHAR); - break; - case KEY_END: - form_driver(form, REQ_END_LINE); - break; - case KEY_HOME: - form_driver(form, REQ_BEG_LINE); - break; - case KEY_SLEFT: - form_driver(form, REQ_PREV_WORD); - break; - case KEY_SRIGHT: - form_driver(form, REQ_NEXT_WORD); - break; - case KEY_SDC: - form_driver(form, REQ_CLR_FIELD); - break; - /* case KEY_NPAGE: - wscrl(textwin, 10); - break; - case KEY_PPAGE: - wscrl(textwin, -10); - break; - */ /* you wish! ncurses does not keep scrollback. i could use fancy features such as pads, but I'll just make a gui instd */ - case 9: /* idk fucken keybd */ - case KEY_STAB: /* switch to next channel for sending */ - dc_null(); - int firstjoined_g = -1; - int firstjoined_c = -1; - int foundfocused = 0; - DC_CWLE(c, c->guilds_lock); - for (int m = 0; m < c->guilds_sizeof; m++) /* we loop over all channels */ - for (int n = 0; n < c->guilds[m]->channels_sizeof; n++) { - if (firstjoined_g == -1 && c->guilds[m]->channels[n]->joined) { - firstjoined_g = m; - firstjoined_c = n; - } - if (!foundfocused && c->guilds[m]->channels[n]->focused) { - c->guilds[m]->channels[n]->focused = 0; - foundfocused = 1; - continue; - } - if (foundfocused && c->guilds[m]->channels[n]->joined) { - c->guilds[m]->channels[n]->focused = 1; - goto found; - break; - } - } - if (firstjoined_g != -1) - c->guilds[firstjoined_g]->channels[firstjoined_c]->focused = 1; - found: - DC_CUE(c, c->guilds_lock); - updinforow++; - break; - case KEY_ENTER: - case 10: - form_driver(form, REQ_NEXT_FIELD); - form_driver(form, REQ_PREV_FIELD); - if (dc_ui_processline(t, field_buffer(field[0], 0), textwin, ui) > 0) - form_driver(form, REQ_CLR_FIELD); - pos_form_cursor(form); - updinforow++; - break; - default: - form_driver_w(form, ret, ch); - break; - } - wrefresh(formwin); - /* wrefresh(textwin); */ +gchar * gtk_text_buffer_get_all_text(GtkTextBuffer * b) { + GtkTextIter s, e; + gtk_text_buffer_get_start_iter(b, &s); + gtk_text_buffer_get_end_iter(b, &e); + gchar * c = gtk_text_iter_get_text(&s, &e); + return c; /* must-g_free, transfer-full */ +} +G_MODULE_EXPORT void dc_ui_settings_ok (GtkButton * b, struct dc_ui_data * d) { + g_key_file_set_boolean(d->k, "discord.c", "multiline", gtk_switch_get_active(GTK_SWITCH(gtk_builder_get_object(d->b, "dc_settings_multiline")))); + g_key_file_set_string(d->k, "discord.c", "login", gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(d->b, "dc_settings_login")))); + g_key_file_set_string(d->k, "discord.c", "password", gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(d->b, "dc_settings_password")))); + gtk_widget_hide(gtk_widget_get_toplevel(GTK_WIDGET(b))); +} +G_MODULE_EXPORT void dc_ui_inputbox_changed (GtkWidget * i, struct dc_ui_data * d) { + GtkTextView * t = GTK_TEXT_VIEW(gtk_builder_get_object(d->b, "dc_main_multiline")); + GtkEntry * e = GTK_ENTRY(gtk_builder_get_object(d->b, "dc_main_singleline")); + GtkWidget * b = GTK_WIDGET(gtk_builder_get_object(d->b, "dc_main_send")); + gchar * c = gtk_text_buffer_get_all_text(gtk_text_view_get_buffer(t)); + gtk_widget_set_sensitive(b, c[0] || gtk_entry_get_text(e)[0] ? TRUE : FALSE); + g_free(c); +} +void dc_ui_inputbox_activate (GtkWidget * a, struct dc_ui_data * d) { + GtkTextView * t = GTK_TEXT_VIEW(gtk_builder_get_object(d->b, "dc_main_multiline")); + GtkEntry * e = GTK_ENTRY(gtk_builder_get_object(d->b, "dc_main_singleline")); + GtkTextBuffer * b = gtk_text_view_get_buffer(t); + gchar * c = (gchar *) /* DROPPING const HERE!!! TODO: do this more politely with suppressions etc. */ gtk_entry_get_text(e); /* do not free this one */ + if (c[0]) + g_print("entry says: %s\n", c); + else { /* we need text from the textview, entry is empty */ + c = gtk_text_buffer_get_all_text(b); + g_print("textview says: %s\n", c); + a = NULL; /* so we mark the state, if we should free c or not */ + } + /* do stuff with c */ + if (c[0] == '/') /* handle command */ + switch (c[1]) { /* unlike before the rewrite / can't be escaped, because escaping / is useless. */ + case 'c': /* clear messages, developing debugging TODO: delete before production useless */ + case 'C': + dc_ui_spawn_message(NULL, d); + break; } - i++; - usleep(2500); - while (t->power_ui == 0) usleep(250000); + else { /* send message to channel */ + } - unpost_form(form); - free_form(form); - free_field(field[0]); - endwin(); - return 1; + /* stop doing stuff with c */ + if (!a) + g_free(c); + gtk_text_buffer_set_text(b, "", -1); + gtk_entry_set_text(e, ""); /* singleline */ +} +G_MODULE_EXPORT gboolean dc_ui_multiline_focus (GtkTextView * t, GtkDirectionType d /* pojma nimam, kako ta enum pove a mam fokus al ne... čudno */, gpointer u) { /* not working, there's not placeholder then */ + char * p = "Enter message in this multiline text field or switch to a single line in preferences. Send message with Ctrl+Enter."; + GtkTextBuffer * b = gtk_text_view_get_buffer(t); + gchar * c = gtk_text_buffer_get_all_text(b); + if (gtk_widget_has_focus(GTK_WIDGET(t))) { + if (!strcmp(p, c)) + gtk_text_buffer_set_text(b, "", -1); + } else + if (!c[0]) + gtk_text_buffer_set_text(b, p, -1); + g_free(c); + return FALSE; /* to keep executing other handles for signals instead of finishing here. AFAIK, RTFM */ +} +G_MODULE_EXPORT void dc_ui_set_multiline (GtkSwitch * a, gboolean s, struct dc_ui_data * d) { + GtkWidget * t = GTK_WIDGET(gtk_builder_get_object(d->b, "dc_main_multiline")); + GtkWidget * e = GTK_WIDGET(gtk_builder_get_object(d->b, "dc_main_singleline")); + gtk_widget_hide(e); + gtk_widget_hide(t); + if (s) + gtk_widget_show(t); + else + gtk_widget_show(e); + /* dc_ui_multiline_focus(GTK_TEXT_VIEW(t), 0, NULL); */ /* NOT WORKING, meh, there will be no placeholder */ /* just so we set the placeholder, the most important part, otherwise the user will not even see the textview on some themes <3 */ +} +G_MODULE_EXPORT void dc_ui_spawn_window (GtkToolButton * t, GtkWindow * w) { + gtk_widget_show_all(GTK_WIDGET(w)); +} +G_MODULE_EXPORT gboolean dc_ui_handle_close (GtkButton * b, gpointer u) { + gtk_widget_hide(gtk_widget_get_toplevel(GTK_WIDGET(b))); + return TRUE; /* so that it stays non-deleted, main window sould call/be handled with gtk_main_quit */ +} +G_MODULE_EXPORT void dc_ui_reveal_password (GtkSwitch * t, gboolean s, GtkEntry * e) { + gtk_entry_set_visibility(e, s); +} +void dc_ui_activate (GtkApplication * app, gpointer user_data) { + GtkWidget * w; + gchar * s; + struct dc_ui_data d; + d.b = gtk_builder_new_from_string(dc_ui_def, -1); + w = GTK_WIDGET(gtk_builder_get_object(d.b, "dc_window_main")); + gtk_builder_connect_signals(d.b, &d); + /* začetek definicije dodatnih signalov */ + /* g_signal_connect(gtk_builder_get_object(b, "dc_settings_multiline"), "state-set", G_CALLBACK(dc_ui_set_multiline), b); */ + /* konec definicije dodatnih signalov */ +#define dc_uacf "%s/%sdiscord.c", getenv("XDG_CONFIG_HOME") ? getenv("XDG_CONFIG_HOME") : getenv("HOME") ? getenv("HOME") : ".", getenv("XDG_CONFIG_HOME") ? "" : ".config/" /* as per XDG */ + gchar fn[snprintf(NULL, 0, dc_uacf)]; + sprintf(fn, dc_uacf); + s = strrchr(fn, '/'); + s[0] = '\0'; + g_mkdir_with_parents(fn, 0700 /* as per XDG */); + s[0] = '/'; + d.k = g_key_file_new(); + g_key_file_load_from_file(d.k, fn, G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, NULL); + gtk_widget_show_all(w); + dc_ui_set_multiline(NULL, g_key_file_get_boolean(d.k, "discord.c", "multiline", NULL), &d); + dc_ui_inputbox_changed(NULL, &d); + /* začetek aplikacije konfiguracijskih vrednosti v UI */ + gtk_switch_set_state(GTK_SWITCH(gtk_builder_get_object(d.b, "dc_settings_multiline")), g_key_file_get_boolean(d.k, "discord.c", "multiline", NULL)); + s = g_key_file_get_string(d.k, "discord.c", "login", NULL); + gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(d.b, "dc_settings_login")), s ? s : ""); + g_free(s); + s = g_key_file_get_string(d.k, "discord.c", "password", NULL); + gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(d.b, "dc_settings_password")), s ? s : ""); + g_free(s); + /* konec aplikacije konfiguracijskih vrednosti v UI */ + gtk_main(); + g_object_unref(d.b); + if (!g_key_file_save_to_file(d.k, fn, NULL)) + g_warning("couldn't save config"); + g_key_file_free(d.k); +} +int dc_ui (int argc, char ** argv) { + GtkApplication *app; + int status; + gtk_init(&argc, &argv); + app = gtk_application_new("eu.sijanec.discord.c", G_APPLICATION_FLAGS_NONE); + g_signal_connect(app, "activate", G_CALLBACK(dc_ui_activate), NULL); + status = g_application_run(G_APPLICATION(app), argc, argv); + g_object_unref(app); + return status; } |