diff options
Diffstat (limited to 'src/ui.c')
-rw-r--r-- | src/ui.c | 234 |
1 files changed, 114 insertions, 120 deletions
@@ -8,104 +8,20 @@ unsigned char dc_ui_def_u[] = { }; 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) +#define DC_UI_GTK /* indicating that the default ui will be used */ +#include <h.c> +G_DEFINE_AUTOPTR_CLEANUP_FUNC(gchar, g_free) 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; -}; -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 + strftime = format string */ void dc_ui_spawn_message (struct dc_message * m, struct dc_ui_data * d) { /* !m to clear messages */ size_t i = 0; @@ -113,10 +29,12 @@ void dc_ui_spawn_message (struct dc_message * m, struct dc_ui_data * d) { /* !m GtkWidget * w, * w2; #define DC_USMTL 32 char t[DC_USMTL]; + g_autoptr(gchar) c = g_key_file_get_string(d->k, "discord.c", "strftime", NULL); 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); + /* struct dc_channel * c = m->channel->guild->channel; */ /* XXX: I wrote this line of code but then I wen't to sleep and I can't remember what should I do here with the channel :shrug: */ return; } while ((w = gtk_grid_get_child_at(g, 0, i))) { /* now we get the index BEFORE which message will be placed */ @@ -128,23 +46,61 @@ void dc_ui_spawn_message (struct dc_message * m, struct dc_ui_data * d) { /* !m i++; /* BEFORE WHICH */ break; } + if (m->time <= before->time) /* we've found a spot at the top of the grid */ + break; 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; } + i++; } gtk_grid_insert_row(g, i); - gtk_grid_insert_column(g, i); + /* gtk_grid_insert_column(g, i); */ /* useless, we already *HAVE* columns */ b = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0 /* spacing pixels */); - gtk_container_add(GTK_CONTAINER(b), gtk_label_new(m->user->username)); + snprintf(t, DC_USMTL, "%s#%04d", m->user->username, m->user->discriminator); + gtk_container_add(GTK_CONTAINER(b), gtk_label_new(t)); /* 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 */ + strftime(t, DC_USMTL, c ? strcmp(c, "") ? c : "%c" : "%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); + g_object_set_data(G_OBJECT(b), "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 */ - } + /* 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); + gtk_widget_show_all(GTK_WIDGET(g)); + gtk_widget_show_all(GTK_WIDGET(g)); +} +void dc_ui_spawn_channel (struct dc_channel * c /* needs a functional guild or segfaults */, struct dc_ui_data * d) { /* adds a channel to the channel list, !c to clear all entries */ + GtkTreeStore * l = GTK_TREE_STORE(gtk_builder_get_object(d->b, "dc_main_tree")); /* this func is transfer none */ + GtkTreeView * t = GTK_TREE_VIEW(gtk_builder_get_object(d->b, "dc_main_channels")); + GtkTreeIter i; + if (!c) { + gtk_tree_store_clear(l); + struct dc_guild * g = c->guild->client->guild; + while (g) { + g->is_iter = FALSE; + struct dc_channel * c = g->channel; + while (c) { + c->is_iter = FALSE; + c = c->next; + } + g = g->next; + } + } + if (!c->guild->is_iter) { /* we don't have this guild already rendered */ + gtk_tree_store_insert(l, &i, NULL, -1 /* row position */); + gtk_tree_store_set(l, &i, 0, c->guild->name, -1); + gtk_tree_store_set(l, &i, 1, c->guild, -1); + memcpy(&c->guild->iter, &i, sizeof(GtkTreeIter)); + c->guild->is_iter = TRUE; + } + gtk_tree_store_insert(l, &i, &c->guild->iter, -1); /* for this to work, iter must not be same as parent! */ + gtk_tree_store_set(l, &i, 0, c->name, -1); /* TODO: set hover tooltip for c->topic */ + gtk_tree_store_set(l, &i, 1, c, -1); + memcpy(&c->iter, &i, sizeof(GtkTreeIter)); + c->is_iter = TRUE; + if (!gtk_tree_view_get_column(t, 0)) + gtk_tree_view_append_column(t, gtk_tree_view_column_new_with_attributes("Channel", gtk_cell_renderer_text_new(), "text", 0, NULL)); + gtk_tree_view_expand_all(t); } gchar * gtk_text_buffer_get_all_text(GtkTextBuffer * b) { GtkTextIter s, e; @@ -157,6 +113,7 @@ 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")))); + g_key_file_set_string(d->k, "discord.c", "strftime", gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(d->b, "dc_settings_strftime")))); 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) { @@ -186,6 +143,38 @@ void dc_ui_inputbox_activate (GtkWidget * a, struct dc_ui_data * d) { case 'C': dc_ui_spawn_message(NULL, d); break; + case 'd': /* debug create message TODO: delete before production useless */ + case 'D': /* /debug unixtimestamp message */ + ; + struct dc_user u; + u.username = "yeet"; + struct dc_message * m = malloc(sizeof(struct dc_message)); /* memory leak! */ + m->time = atoi((strchr(c, ' ') ? strchr(c, ' ') : c) + 1); + m->user = &u; + m->message = "test message"; + dc_ui_spawn_message(m, d); + break; + case 't': /* debug add string to the channel tree. TODO: delete before production useless */ + case 'T': /* /tree position string */ + ; + struct dc_channel * c = calloc(1, sizeof(struct dc_channel)); /* memory leak! */ + struct dc_guild * g = calloc(1, sizeof(struct dc_guild)); + c->guild = g; + c->name = "this is a parent."; + c->guild->name = "this is a child."; + dc_ui_spawn_channel(c, d); + break; + GtkTreeStore * l = GTK_TREE_STORE(gtk_builder_get_object(d->b, "dc_main_tree")); + GtkTreeView * t = GTK_TREE_VIEW(gtk_builder_get_object(d->b, "dc_main_channels")); + GtkTreeIter i; + GtkTreeIter j; + gtk_tree_store_insert(l, &i, NULL, 0); + gtk_tree_store_set(l, &i, 0, "eww", -1); + gtk_tree_store_insert(l, &j, &i, 0); /* for this to work, iter must not be the same as parent! */ + gtk_tree_store_set(l, &j, 0, "eww", -1); + if (!gtk_tree_view_get_column(t, 0)) + gtk_tree_view_append_column(t, gtk_tree_view_column_new_with_attributes("Channel", gtk_cell_renderer_text_new(), "text", 0, NULL)); + break; } else { /* send message to channel */ @@ -230,16 +219,37 @@ G_MODULE_EXPORT gboolean dc_ui_handle_close (GtkButton * b, gpointer u) { 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) { +void dc_ui_activate (GtkApplication * app, struct dc_ui_data * d) { 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); + 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 */ + 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); + /* gtk zdaj požene main loop */ +} +int dc_ui (int argc, char ** argv) { + GtkApplication *app; + int status; + struct dc_ui_data d; + gchar * s; + 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), &d); + /* pre app run setup */ + d.b = gtk_builder_new_from_string(dc_ui_def, -1); #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); @@ -249,31 +259,15 @@ void dc_ui_activate (GtkApplication * app, gpointer user_data) { 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(); + /* app run */ + status = g_application_run(G_APPLICATION(app), argc, argv); + gtk_main(); /* XXX: NO IDEA why this has to be run.... gtk_main loop should be started by g_application_run, but it doesn't do that FOR SOME REASON. HELP */ + /* post app cleanup */ 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); + /* dc_ui cleanup */ g_object_unref(app); return status; } |