summaryrefslogtreecommitdiffstats
path: root/src/ui.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui.c')
-rw-r--r--src/ui.c635
1 files changed, 268 insertions, 367 deletions
diff --git a/src/ui.c b/src/ui.c
index 725e7cc..79aa524 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -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, &timestruct);
- strftime(timestring, 64, DC_I18N_MSGTIMEF, &timestruct); /* 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;
}