Skip to content

Commit

Permalink
select multiple emojis at once
Browse files Browse the repository at this point in the history
  • Loading branch information
amitanshusahu committed Jul 18, 2024
1 parent d2d5f4d commit e17ecba
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 35 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ https://github.com/amitanshusahu/EmojiX/assets/83657737/c3ee5ad1-57e8-4a15-8690-
## contribute to this project
### Features
- [x] search
- [ ] tabs of different emojis ( all, people, celbrate )
- [x] auto focused on search when opened
- [x] recent used emojis tab
- [x] select multiple emojis (hold shift)
- [x] keyboard navigation (tab, enter, left, right, up, down)
- [ ] theme-selector
- [ ] common phrases
- [ ] emoji groups
- [ ] custom tags

## depnedency
- gtk-3
Expand Down
2 changes: 2 additions & 0 deletions include/callback.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ void save_recent_emojis(GList *emojis);
GList* load_recent_emojis();
void add_recent_emoji(GList **emojis, const char *emoji);
void check_and_add_recent_emoji(GList **emojis, const char *emoji);
void emoji_button_clicked(GtkWidget *button, gpointer data);
void clear_selected_emojis();


#endif // CALLBACKS_H
2 changes: 2 additions & 0 deletions include/emoji.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@


#include <stddef.h>
#include <gtk/gtk.h>

typedef struct {
const char *emoji;
Expand All @@ -13,5 +14,6 @@ typedef struct {
// Declaration of extern variables
extern const EmojiEntry emojis[];
extern const size_t NUM_EMOJIS;
extern GList *selected_emojis;

#endif // EMOJI_H
135 changes: 102 additions & 33 deletions src/callback.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
#include <unistd.h>
#include <json-c/json.h>

void copy_to_clipboard(GtkWidget *widget, gpointer data) {
void copy_to_clipboard(GtkWidget *widget, gpointer data)
{
const char *emoji = (const char *)data;
GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
gtk_clipboard_set_text(clipboard, emoji, -1);
Expand All @@ -20,21 +21,68 @@ void copy_to_clipboard(GtkWidget *widget, gpointer data) {
GtkNotebook *notebook = GTK_NOTEBOOK(app_widgets->emoji_book);
int active_tab = gtk_notebook_get_current_page(notebook);

// Assume that tab 0 is the emoji tab and tab 1 is the recent tab
if (active_tab == 0) {
// Assume that tab 0 is the emoji tab and tab 1 is the recent tab
if (active_tab == 0)
{
// Emoji tab is active
check_and_add_recent_emoji(&app_widgets->recent_emojis, emoji);
} else if (active_tab == 1) {
}
else if (active_tab == 1)
{
// Recent tab is active
add_recent_emoji(&app_widgets->recent_emojis, emoji);
}


save_recent_emojis(app_widgets->recent_emojis);
populate_recent_emojis(app_widgets);
}

void filter_emojis(GtkEntry *entry, gpointer data) {
void clear_selected_emojis()
{
g_list_free_full(selected_emojis, g_free);
selected_emojis = NULL;
}

void emoji_button_clicked(GtkWidget *button, gpointer data)
{
const char *emoji = (const char *)data;

// Check if Shift key is pressed
GdkModifierType state;
gdk_window_get_pointer(gtk_widget_get_window(GTK_WIDGET(button)), NULL, NULL, &state);
gboolean shift_pressed = (state & GDK_SHIFT_MASK);

if (shift_pressed)
{
// Add emoji to selected list
selected_emojis = g_list_append(selected_emojis, g_strdup(emoji));
const char *emoji = (const char *)data;
GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);

if (selected_emojis != NULL)
{
// Shift key was pressed and multiple emojis are selected
GString *copy_text = g_string_new("");
for (GList *iter = selected_emojis; iter != NULL; iter = g_list_next(iter))
{
const char *selected_emoji = (const char *)iter->data;
g_string_append(copy_text, selected_emoji);
g_string_append_c(copy_text, ' '); // Example: Space separated
}
gtk_clipboard_set_text(clipboard, copy_text->str, -1);
g_string_free(copy_text, TRUE);
}
}
else
{
// Single emoji selected, copy to clipboard
clear_selected_emojis();
copy_to_clipboard(button, emoji);
}
}

void filter_emojis(GtkEntry *entry, gpointer data)
{
AppWidgets *app_widgets = (AppWidgets *)data;
const char *search_text = gtk_entry_get_text(entry);

Expand All @@ -43,25 +91,28 @@ void filter_emojis(GtkEntry *entry, gpointer data) {
if (gtk_notebook_get_current_page(notebook) != 0)
gtk_notebook_set_current_page(notebook, 0);


// Remove all current children from the grid
GList *children, *iter;
children = gtk_container_get_children(GTK_CONTAINER(app_widgets->grid));
for (iter = children; iter != NULL; iter = g_list_next(iter)) {
for (iter = children; iter != NULL; iter = g_list_next(iter))
{
gtk_widget_destroy(GTK_WIDGET(iter->data));
}
g_list_free(children);

// Add emojis that match the search text
int col = 0, row = 0;
for (size_t i = 0; i < NUM_EMOJIS; ++i) {
if (strstr(emojis[i].keywords, search_text) != NULL) {
for (size_t i = 0; i < NUM_EMOJIS; ++i)
{
if (strstr(emojis[i].keywords, search_text) != NULL)
{
GtkWidget *button = gtk_button_new_with_label(emojis[i].emoji);
g_signal_connect(button, "clicked", G_CALLBACK(copy_to_clipboard), (gpointer)emojis[i].emoji);
g_signal_connect(button, "clicked", G_CALLBACK(emoji_button_clicked), (gpointer)emojis[i].emoji);
g_object_set_data(G_OBJECT(button), "app_widgets", app_widgets); // set app_widgets data
gtk_grid_attach(GTK_GRID(app_widgets->grid), button, col, row, 1, 1);
col++;
if (col >= 5) {
if (col >= 5)
{
col = 0;
row++;
}
Expand All @@ -70,11 +121,13 @@ void filter_emojis(GtkEntry *entry, gpointer data) {
gtk_widget_show_all(app_widgets->grid);
}

void populate_recent_emojis(AppWidgets *app_widgets) {
void populate_recent_emojis(AppWidgets *app_widgets)
{
// Remove all current children from the recent grid
GList *children, *iter;
children = gtk_container_get_children(GTK_CONTAINER(app_widgets->recent_grid));
for (iter = children; iter != NULL; iter = g_list_next(iter)) {
for (iter = children; iter != NULL; iter = g_list_next(iter))
{
gtk_widget_destroy(GTK_WIDGET(iter->data));
}
g_list_free(children);
Expand All @@ -84,23 +137,27 @@ void populate_recent_emojis(AppWidgets *app_widgets) {

// Add recent emojis to the grid
int col = 0, row = 0;
for (GList *l = app_widgets->recent_emojis; l != NULL; l = l->next) {
for (GList *l = app_widgets->recent_emojis; l != NULL; l = l->next)
{
GtkWidget *button = gtk_button_new_with_label((char *)l->data);
g_signal_connect(button, "clicked", G_CALLBACK(copy_to_clipboard), (gpointer)l->data);
g_object_set_data(G_OBJECT(button), "app_widgets", app_widgets); // set app_widgets data
gtk_grid_attach(GTK_GRID(app_widgets->recent_grid), button, col, row, 1, 1);
col++;
if (col >= 5) {
if (col >= 5)
{
col = 0;
row++;
}
}
gtk_widget_show_all(app_widgets->recent_grid);
}

char* get_recent_emojis_file_path() {
char *get_recent_emojis_file_path()
{
const char *data_home = getenv("XDG_DATA_HOME");
if (!data_home) {
if (!data_home)
{
data_home = g_strconcat(g_get_home_dir(), "/.local/share", NULL);
}

Expand All @@ -109,25 +166,29 @@ char* get_recent_emojis_file_path() {

// Create the directory if it doesn't exist
struct stat st = {0};
if (stat(dir_path, &st) == -1) {
if (stat(dir_path, &st) == -1)
{
mkdir(dir_path, 0700);
}

g_free(dir_path);
return file_path;
}

void save_recent_emojis(GList *emojis) {
void save_recent_emojis(GList *emojis)
{
char *file_path = get_recent_emojis_file_path();
json_object *jarray = json_object_new_array();

for (GList *l = emojis; l != NULL; l = l->next) {
for (GList *l = emojis; l != NULL; l = l->next)
{
json_object *jstring = json_object_new_string((char *)l->data);
json_object_array_add(jarray, jstring);
}

FILE *fp = fopen(file_path, "w");
if (fp != NULL) {
if (fp != NULL)
{
fprintf(fp, "%s\n", json_object_to_json_string(jarray));
fclose(fp);
}
Expand All @@ -136,11 +197,13 @@ void save_recent_emojis(GList *emojis) {
g_free(file_path);
}

GList* load_recent_emojis() {
GList *load_recent_emojis()
{
char *file_path = get_recent_emojis_file_path();
GList *emojis = NULL;
FILE *fp = fopen(file_path, "r");
if (fp != NULL) {
if (fp != NULL)
{
fseek(fp, 0, SEEK_END);
long fsize = ftell(fp);
fseek(fp, 0, SEEK_SET);
Expand All @@ -151,9 +214,11 @@ GList* load_recent_emojis() {
fclose(fp);

json_object *jarray = json_tokener_parse(json_string);
if (jarray != NULL) {
if (jarray != NULL)
{
int array_len = json_object_array_length(jarray);
for (int i = 0; i < array_len; i++) {
for (int i = 0; i < array_len; i++)
{
json_object *jstring = json_object_array_get_idx(jarray, i);
const char *emoji = json_object_get_string(jstring);
emojis = g_list_append(emojis, g_strdup(emoji));
Expand All @@ -166,29 +231,33 @@ GList* load_recent_emojis() {
return emojis;
}

void add_recent_emoji(GList **emojis, const char *emoji) {
*emojis = g_list_remove(*emojis, emoji); // Remove if already in list
void add_recent_emoji(GList **emojis, const char *emoji)
{
*emojis = g_list_remove(*emojis, emoji); // Remove if already in list
*emojis = g_list_prepend(*emojis, g_strdup(emoji)); // Add to the front

if (g_list_length(*emojis) > MAX_RECENT_EMOJIS) {
if (g_list_length(*emojis) > MAX_RECENT_EMOJIS)
{
GList *last = g_list_last(*emojis);
*emojis = g_list_remove_link(*emojis, last);
g_free(last->data);
g_list_free(last);
}
}


void check_and_add_recent_emoji(GList **emojis, const char *emoji) {
void check_and_add_recent_emoji(GList **emojis, const char *emoji)
{
GList *existing = g_list_find_custom(*emojis, emoji, (GCompareFunc)strcmp);
if (existing) {
if (existing)
{
*emojis = g_list_remove_link(*emojis, existing);
g_free(existing->data);
g_list_free(existing);
}
*emojis = g_list_prepend(*emojis, g_strdup(emoji)); // Add to the front

if (g_list_length(*emojis) > MAX_RECENT_EMOJIS) {
if (g_list_length(*emojis) > MAX_RECENT_EMOJIS)
{
GList *last = g_list_last(*emojis);
*emojis = g_list_remove_link(*emojis, last);
g_free(last->data);
Expand Down
2 changes: 2 additions & 0 deletions src/emoji.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "emoji.h"

GList *selected_emojis = NULL;

// Update the emoji list with image file paths
const EmojiEntry emojis[] = {
{"😃", "grinning face with big eyes"},
Expand Down

0 comments on commit e17ecba

Please sign in to comment.