Hotpot-proto/main.c
2022-07-06 22:07:48 -04:00

1394 lines
36 KiB
C

#define _DEFAULT_SOURCE
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "btk/btk.h"
/* for filesystem navigation */
/* change depending on your system */
#define NAME_SIZE 255
#define PATH_SIZE 4096
/* buffer increments */
#define DIR_BUFFER_INC 100
#define DATA_BUFFER_INC 2
#define READ_SIZE 2048
#define TEXT_SIZE 4096
#define ITEM_SIZE 256
#define TAG_COL 0
#define UID_COL 1
#define NAME_COL 2
#define DATA_COLS_N 9 /* excludes id, filename and tag */
enum tag_type {
TAG_NONE = 0,
TAG_STRAY = 1U << 0,
TAG_NEW = 1U << 1,
TAG_DEL = 1U << 2,
TAG_EDITED = 1U << 3,
TAG_VISIBILITY = 1U << 4
};
enum status {
STATUS_NONE,
STATUS_ERROR,
STATUS_LATEST,
STATUS_EDITED,
STATUS_BUSY
};
enum dir_mode {
DIR_FILES = 1U << 0,
DIR_HIDDEN = 1U << 1
};
typedef struct {
char uid[ITEM_SIZE];
char filename[ITEM_SIZE];
char misc[DATA_COLS_N][ITEM_SIZE];
char notes[TEXT_SIZE];
unsigned int notes_w;
} data_entry_t;
/* window creation functions */
void build_entry_form ();
void build_file_picker ();
void build_main_window ();
void build_settings ();
/* functions */
void clean_string_copy (char *, char *, unsigned int, int);
int cmp_str_for_qsort (const void *, const void*);
void entry_form_apply ();
void entry_form_close ();
void entry_form_open ();
int file_exists ();
void file_picker_close ();
void file_picker_open (int);
void file_picker_open_entry ();
void file_picker_open_open ();
void file_picker_open_new ();
void file_picker_select ();
int find_dir_spot (char *, char *);
char* get_data_item (unsigned int, unsigned int);
void hotpot_close ();
void hotpot_load ();
void hotpot_reload ();
void hotpot_sync ();
void hotpot_unload ();
void move_dir (int);
void open_ext_dir ();
void open_ext_entry (int);
void quit ();
int parse_dir (char *, int);
void push_entry (int, data_entry_t);
void settings_close ();
void settings_open ();
void set_activity_status (int, int);
void set_entry_tag_col (unsigned int, int);
void set_main_win_states (int);
void set_file_picker_states (int);
void str_to_lower_case (char *);
void toggle_del_tag ();
void toggle_col (int, btk_arg_t);
void toggle_hidden (int, btk_arg_t);
void toggle_notes (int, btk_arg_t);
void toggle_visibility_tag ();
void update_select_data (int);
/* btk windows */
btk_session_t *session;
btk_window_t *entry_form;
btk_window_t *file_picker;
btk_window_t *main_win;
btk_window_t *settings;
btk_window_t *windows[4];
/* btk important cells */
btk_cell_t *table_cell;
btk_cell_t *dir_list_cell;
btk_cell_t *notes_view_cell;
btk_cell_t *file_select_cell;
btk_cell_t *activity_cell;
btk_cell_t *edited_note_cell;
unsigned int table_cell_id;
unsigned int dir_list_cell_id;
unsigned int notes_view_cell_id;
unsigned int file_select_cell_id;
unsigned int activity_cell_id;
unsigned int edited_note_cell_id;
/* file navigation */
char dir_current[PATH_SIZE];
char *dir_list = NULL;
int *dir_spot = NULL;
unsigned int dir_list_n = 0;
int nav_select = -1;
int nav_mode;
/* hotpot data */
int file_picker_mode;
char open_dir[PATH_SIZE] = "";
int hotpot_open = 0;
char *data = NULL;
char *data_notes = NULL;
int *data_tags = NULL;
int *data_spot = NULL;
char *data_rename = NULL;
unsigned int data_n = 0;
unsigned int data_buffer_n = 0;
unsigned int last_uid = 0;
/* selected data */
int select_data = -1;
char select_note[TEXT_SIZE];
/* edited data */
char edited_uid[ITEM_SIZE];
char edited_filename[ITEM_SIZE];
char edited_misc[DATA_COLS_N][ITEM_SIZE];
char edited_note[TEXT_SIZE];
/* misc gui stuff */
char status_open_file[PATH_SIZE + NAME_SIZE];
char status_files_n[15];
char status_activity[9];
char misc_titles[DATA_COLS_N][ITEM_SIZE];
char misc_switches[DATA_COLS_N][ITEM_SIZE];
unsigned int data_cols_cw[DATA_COLS_N + 3] = {1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 2};
int data_cols_ena[DATA_COLS_N + 3] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
char data_header[DATA_COLS_N + 3][ITEM_SIZE] = {
"TAGS",
"UID",
"FILENAME",
"TOPICS",
"AUTHORS",
"INSTITUTIONS",
"PUBLISHER",
"YEAR",
"VOL",
"NO",
"PAGES",
"ACCESSED"
};
/* misc */
btk_arg_t dummy_arg = {.i = 0, .f = 0.0f, .c = NULL, .p = NULL};
/*******************************************************************/
void
build_entry_form()
{
entry_form = btk_window_create(session->x_con,
session->x_scr,
session->x_vis,
8, 6 + DATA_COLS_N,
4, 3 + DATA_COLS_N,
100, 0,
10 + 2 * DATA_COLS_N,
entry_form_close);
btk_window_set_name(entry_form, "hotpot - entry form");
edited_note_cell_id = 0;
edited_note_cell = &(entry_form->cells[edited_note_cell_id]);
btk_cell_set_editor(edited_note_cell, 2, 2 + DATA_COLS_N, 6, 4, (char*)&edited_note, TEXT_SIZE);
btk_cell_set_mark (&(entry_form->cells[1]), 0, 0, 2, BTK_JUSTIFY_RIGHT, "uid :");
btk_cell_set_mark (&(entry_form->cells[2]), 0, 1, 2, BTK_JUSTIFY_RIGHT, "filename :");
btk_cell_set_mark (&(entry_form->cells[3]), 2, 0, 6, BTK_JUSTIFY_LEFT, edited_uid);
btk_cell_set_input (&(entry_form->cells[4]), 2, 1, 6, (char*)edited_filename, NAME_SIZE);
btk_cell_set_mark (&(entry_form->cells[5]), 0, 2 + DATA_COLS_N, 2, BTK_JUSTIFY_RIGHT, "notes :");
btk_cell_set_empty (&(entry_form->cells[6]), 0, 3 + DATA_COLS_N, 2, 1);
btk_cell_set_button (&(entry_form->cells[7]), 0, 4 + DATA_COLS_N, 2, "apply", entry_form_apply);
btk_cell_set_button (&(entry_form->cells[8]), 0, 5 + DATA_COLS_N, 2, "close", entry_form_close);
/* generate input fields for misc data */
for (int i = 0; i < DATA_COLS_N; i++) {
strcpy(misc_titles[i], data_header[i + 3]);
str_to_lower_case(misc_titles[i]);
strcat(misc_titles[i], " :");
btk_cell_set_mark (&(entry_form->cells[9 + i]), 0, 2 + i, 2, BTK_JUSTIFY_RIGHT, misc_titles[i]);
btk_cell_set_input (&(entry_form->cells[10 + DATA_COLS_N + i]), 2, 2 + i, 6, (char*)edited_misc[i], NAME_SIZE);
}
}
void
build_file_picker()
{
file_picker = btk_window_create(session->x_con,
session->x_scr,
session->x_vis,
4, 9,
2, 2,
0, 300,
5,
file_picker_close);
btk_window_set_name(file_picker, "hotpot - file picker");
dir_list_cell_id = 0;
dir_list_cell = &(file_picker->cells[dir_list_cell_id]);
btk_cell_set_list (dir_list_cell,
0, 1, 4, 5,
&dir_list,
&dir_list_n,
NAME_SIZE,
NULL, NULL,
&dir_spot,
move_dir,
set_file_picker_states);
file_select_cell_id = 1;
file_select_cell = &(file_picker->cells[file_select_cell_id]);
btk_cell_set_button (file_select_cell, 0, 7, 4, "select", file_picker_select);
btk_cell_set_mark (&(file_picker->cells[2]), 0, 0, 4, BTK_JUSTIFY_RIGHT, (char*)&dir_current);
btk_cell_set_switch (&(file_picker->cells[3]), 0, 6, 4, "show hidden", toggle_hidden, dummy_arg);
btk_cell_set_button (&(file_picker->cells[4]), 0, 8, 4, "cancel", file_picker_close);
}
void
build_main_window()
{
main_win = btk_window_create(session->x_con,
session->x_scr,
session->x_vis,
13, 16,
4, 7,
400, 0,
21,
quit);
btk_window_set_name(main_win, "hotpot");
table_cell_id = 0;
table_cell = &(main_win->cells[table_cell_id]);
btk_cell_set_table(table_cell,
3, 0, 10, 15,
(char*)data_header,
&data,
ITEM_SIZE,
DATA_COLS_N + 3,
&data_n,
(int*)data_cols_ena,
data_cols_cw,
2,
NULL, NULL,
&data_spot,
open_ext_entry,
update_select_data);
strcpy(select_note, "");
notes_view_cell_id = 1;
notes_view_cell = &(main_win->cells[notes_view_cell_id]);
btk_cell_set_editor(notes_view_cell,
3, 9, 10, 6,
select_note,
TEXT_SIZE);
main_win->cells[notes_view_cell_id].state |= BTK_CELL_STATE_DISABLED;
main_win->cells[notes_view_cell_id].state |= BTK_CELL_STATE_HIDDEN;
activity_cell_id = 18;
activity_cell = &(main_win->cells[activity_cell_id]);
btk_cell_set_mark (activity_cell,
11, 15, 2,
BTK_JUSTIFY_RIGHT,
(char*)&status_activity);
btk_cell_set_button (&(main_win->cells[2]), 0, 0, 3, "open", file_picker_open_open);
btk_cell_set_button (&(main_win->cells[3]), 0, 1, 3, "new", file_picker_open_new);
btk_cell_set_button (&(main_win->cells[4]), 0, 2, 3, "sync", hotpot_sync);
btk_cell_set_button (&(main_win->cells[5]), 0, 3, 3, "view directory", open_ext_dir);
btk_cell_set_button (&(main_win->cells[6]), 0, 4, 3, "reload", hotpot_reload);
btk_cell_set_button (&(main_win->cells[7]), 0, 5, 3, "close", hotpot_close);
btk_cell_set_button (&(main_win->cells[8]), 0, 6, 3, "quit", quit);
btk_cell_set_empty (&(main_win->cells[9]), 0, 7, 3, 1);
btk_cell_set_button (&(main_win->cells[10]), 0, 8, 3, "add file", file_picker_open_entry);
btk_cell_set_button (&(main_win->cells[11]), 0, 9, 3, "edit entry", entry_form_open);
btk_cell_set_button (&(main_win->cells[12]), 0, 10, 3, "tag for deletion", toggle_del_tag);
btk_cell_set_button (&(main_win->cells[19]), 0, 11, 3, "tag for visibility", toggle_visibility_tag);
btk_cell_set_button (&(main_win->cells[20]), 0, 12, 3, "export citation", NULL);
btk_cell_set_empty (&(main_win->cells[13]), 0, 13, 3, 1);
btk_cell_set_switch (&(main_win->cells[14]), 0, 14, 3, "show notes", toggle_notes, dummy_arg);
btk_cell_set_button (&(main_win->cells[15]), 0, 15, 3, "settings", settings_open);
btk_cell_set_mark (&(main_win->cells[16]), 3, 15, 6, BTK_JUSTIFY_RIGHT, (char*)&status_open_file);
btk_cell_set_mark (&(main_win->cells[17]), 9, 15, 2, BTK_JUSTIFY_RIGHT, (char*)&status_files_n);
main_win->cells[15].state |= BTK_CELL_STATE_ON;
main_win->cells[20].state |= BTK_CELL_STATE_DISABLED;
set_main_win_states(0);
}
void
build_settings()
{
settings = btk_window_create(session->x_con,
session->x_scr,
session->x_vis,
3, 3 + DATA_COLS_N,
-1, -1,
0, 0,
3 + DATA_COLS_N,
settings_close);
btk_window_set_name(settings, "hotpot - settings");
btk_arg_t col_arg= {.i = TAG_COL};
btk_cell_set_switch (&(settings->cells[0]), 0, 0, 3, "show tags", toggle_col, col_arg);
btk_cell_set_empty (&(settings->cells[1 + DATA_COLS_N]), 0, 1 + DATA_COLS_N, 3, 1);
btk_cell_set_button (&(settings->cells[2 + DATA_COLS_N]), 0, 2 + DATA_COLS_N, 3, "close", settings_close);
settings->cells[0].state |= BTK_CELL_STATE_ON;
/* generate col toggles for misc data */
for (int i = 0; i < DATA_COLS_N; i++) {
sprintf(misc_switches[i], "show %s", data_header[i + 3]);
str_to_lower_case(misc_switches[i]);
btk_arg_t col_arg = {.i = i + 3};
btk_cell_set_switch (&(settings->cells[1 + i]), 0, 1 + i, 3, misc_switches[i], toggle_col, col_arg);
settings->cells[1 + i].state |= BTK_CELL_STATE_ON;
}
}
/****************************************************************************************/
int
main()
{
getcwd(dir_current, PATH_SIZE);
strcat(dir_current, "/");
/* btk init */
session = btk_open();
build_entry_form();
build_file_picker();
build_main_window();
build_settings();
windows[0] = main_win;
windows[1] = file_picker;
windows[2] = entry_form;
windows[3] = settings;
btk_map(session, main_win);
btk_loop(session, windows, 4);
btk_close(session);
quit();
return 0;
}
/****************************************************************************************/
void
clean_string_copy(char* dest, char* src, unsigned int max_size, int del_newline)
{
if (!max_size)
return;
char mid[max_size];
strncpy(mid, src, max_size);
if (del_newline)
*strchr(mid, '\n') = '\0';
mid[max_size - 1] = '\0';
strcpy(dest, mid);
}
int
cmp_str_for_qsort(const void *a, const void *b)
{
return strcmp((char*)a, (char*)b);
}
/* check if any data changed, copy it and update status if so */
void
entry_form_apply()
{
int changed = 0;
if (strcmp(edited_filename, get_data_item(NAME_COL, select_data))) {
strcpy(data_rename + select_data * ITEM_SIZE, get_data_item(NAME_COL, select_data));
strcpy(get_data_item(NAME_COL, select_data), edited_filename);
changed = 1;
}
for (int i = 0; i < DATA_COLS_N; i++) {
if (strcmp(edited_misc[i], get_data_item(i + 3, select_data))) {
strcpy(get_data_item(i + 3, select_data), edited_misc[i]);
changed = 1;
}
}
if (strcmp(edited_note, select_note)) {
strcpy(data_notes + select_data * TEXT_SIZE, edited_note);
strcpy(select_note, edited_note);
btk_cell_editor_update_text(notes_view_cell);
changed = 1;
}
if (changed) {
data_tags[select_data] |= TAG_EDITED;
set_entry_tag_col(select_data, data_tags[select_data]);
set_activity_status(STATUS_EDITED, 1);
btk_window_redraw(main_win);
}
}
void
entry_form_open()
{
if (select_data < 0 || select_data >= data_n)
return;
strcpy(edited_note, select_note);
btk_cell_editor_reset_caret(edited_note_cell);
strcpy(edited_uid, get_data_item(1, select_data));
strcpy(edited_filename, get_data_item(2, select_data));
btk_cell_input_reset_caret(&(entry_form->cells[4]));
for (int i = 0; i < DATA_COLS_N; i++) {
strcpy(edited_misc[i], get_data_item(i + 3, select_data));
btk_cell_input_reset_caret(&(entry_form->cells[10 + DATA_COLS_N + i]));
}
btk_cell_editor_update_text(edited_note_cell);
btk_window_disable(main_win);
btk_map(session, entry_form);
}
void
entry_form_close()
{
btk_window_enable(main_win);
btk_unmap(session, entry_form);
}
/* checks if currently selected file in the file picker
* exists in the open hotpot directory */
int
file_exists()
{
DIR *d;
struct dirent *dir;
d = opendir(open_dir);
if (!d) {
printf("ERROR - couldn't read directory");
return 0;
}
while ((dir = readdir(d))) {
if (!strcmp(dir->d_name, dir_list + nav_select * NAME_SIZE))
return 1;
}
return 0;
}
void
file_picker_close()
{
btk_window_enable(main_win);
btk_unmap(session, file_picker);
}
void
file_picker_open(int mode)
{
btk_cell_list_deselect(dir_list_cell);
set_file_picker_states(-1);
file_picker_mode = mode;
if (!file_picker_mode) {
nav_mode |= DIR_FILES;
} else {
nav_mode &= ~DIR_FILES;
}
btk_window_disable(main_win);
btk_map(session, file_picker);
parse_dir(dir_current, nav_mode);
}
void file_picker_open_entry() { file_picker_open(0); }
void file_picker_open_open() { file_picker_open(1); }
void file_picker_open_new() { file_picker_open(2); }
void
file_picker_select()
{
if (nav_select < 0 || nav_select >= dir_list_n) {
printf("ERROR -> tried to open out of bound selection\n");
return;
}
switch(file_picker_mode) {
/* copy selected file into open hotpot directory, then add it to data */
case 0:;
/* compose command */
char cmd[12 + 2 * PATH_SIZE + NAME_SIZE];
sprintf(cmd,
"cp -n \'%s%s\' \'%s\'",
dir_current,
dir_list + nav_select * NAME_SIZE,
open_dir);
system(cmd);
/* create new entry */
data_entry_t new_entry;
new_entry.notes_w = 0;
new_entry.notes[0] = '\0';
new_entry.uid[0] = '\0';
for (int i = 0; i < DATA_COLS_N; i++)
new_entry.misc[i][0] = '\0';
strcpy(new_entry.filename, dir_list + nav_select * NAME_SIZE);
push_entry(TAG_NEW, new_entry);
/* update window */
set_activity_status(STATUS_EDITED, 0);
set_entry_tag_col(data_n - 1, data_tags[data_n - 1]);
set_main_win_states(1);
break;
/* open hotpot */
case 1:
strcpy(open_dir, dir_current);
strcat(open_dir, dir_list + nav_select * NAME_SIZE);
hotpot_load();
break;
/* create hotpot file, then open it */
case 2:
strcpy(open_dir, dir_current);
strcat(open_dir, dir_list + nav_select * NAME_SIZE);
char hotpot_file[PATH_SIZE + NAME_SIZE];
strcpy(hotpot_file, open_dir);
strcat(hotpot_file, "/.htpt");
FILE *f = fopen(hotpot_file, "w");
if (!f) {
set_activity_status(STATUS_ERROR, 1);
printf("ERROR - couldn't create new hotpot file");
return;
}
fclose(f);
hotpot_load();
break;
}
file_picker_close();
set_main_win_states(1);
}
/* returns the hilight color index of the directory */
int
find_dir_spot(char *search_dir, char *sub_dir)
{
char search_path[PATH_SIZE];
strcpy(search_path, search_dir);
strcat(search_path, sub_dir);
strcat(search_path, "/");
DIR *d;
struct dirent *dir;
struct stat file_stats;
char file_path[PATH_SIZE + NAME_SIZE];
d = opendir(search_path);
if (!d) {
printf("ERROR - couldn't read directory");
return 0;
}
while ((dir = readdir(d))) {
strcpy(file_path, search_path);
strcat(file_path, dir->d_name);
stat(file_path, &file_stats);
if ((file_stats.st_mode & S_IFMT) != S_IFDIR) {
if (strcmp(dir->d_name, ".htpt") == 0) {
return 1;
}
}
}
return 2;
}
char*
get_data_item(unsigned int col, unsigned int row)
{
return data + ITEM_SIZE * (col + row * (DATA_COLS_N + 3));
}
void
hotpot_close()
{
hotpot_unload();
select_data = -1;
strcpy(select_note, "");
strcpy(open_dir, "");
set_main_win_states(1);
}
void
hotpot_load()
{
set_activity_status(STATUS_BUSY, 1);
char hotpot_file[PATH_SIZE + NAME_SIZE];
strcpy(hotpot_file, open_dir);
strcat(hotpot_file, "/.htpt");
FILE *f = fopen(hotpot_file, "r");
if (!f) {
printf("ERROR - couldn't read hotpot file\n");
set_activity_status(STATUS_ERROR, 1);
return;
}
hotpot_unload();
/* arrays preparation for on the go resizing */
data_buffer_n = DATA_BUFFER_INC;
data = malloc(data_buffer_n * ITEM_SIZE * (DATA_COLS_N + 3));
data_notes = malloc(data_buffer_n * TEXT_SIZE);
data_tags = malloc(data_buffer_n * sizeof(int));
data_spot = malloc(data_buffer_n * sizeof(int));
data_rename = malloc(data_buffer_n * ITEM_SIZE); /* used for syncing later */
data_entry_t entry_read;
char line_read[READ_SIZE];
char *last_newline;
int in_section = 0, col = 0, w = 0;
/* get lastest used uid on the first line */
/* if it is not present it will be regenerated after the rest of the file has
* been parsed */
if (fgets(line_read, READ_SIZE, f) && line_read[0] == '-') {
last_uid = strtol(line_read + 1, NULL, 16);
} else {
last_uid = 0; /* to be regenerated */
}
/* parse rest of .htpt file */
while (fgets(line_read, READ_SIZE, f)) {
switch (line_read[0]) {
/* open section, reset entry_read */
case '[':
if (in_section)
break;
in_section = 1;
col = 0;
w = 0;
entry_read.notes_w = 0;
entry_read.notes[0] = '\0';
entry_read.uid[0] = '\0';
entry_read.filename[0] = '\0';
for (int i = 0; i < DATA_COLS_N; i++)
entry_read.misc[i][0] = '\0';
break;
/* close section, push entry_read to data */
case ']':
if (!in_section)
break;
in_section = 0;
last_newline = strrchr(entry_read.notes, '\n');
if (last_newline) /* remove last unecessary newline */
*last_newline = '\0';
push_entry(TAG_STRAY, entry_read);
break;
/* get uid */
case '-':
if (!in_section)
break;
clean_string_copy(entry_read.uid, line_read + 1, ITEM_SIZE, 1);
break;
/* get filename */
case '+':
if (!in_section)
break;
clean_string_copy(entry_read.filename, line_read + 1, ITEM_SIZE, 1);
break;
/* get other data */
case '.':
if (!in_section || col >= DATA_COLS_N)
break;
clean_string_copy(entry_read.misc[col], line_read + 1, ITEM_SIZE, 1);
col++;
break;
/* get notes on multiple lines until section close or reaching TEXT_SIZE
* character limit */
case '*':
if (!in_section)
break;
w = strlen(line_read + 1);
if (entry_read.notes_w + w >= TEXT_SIZE) {
w = TEXT_SIZE - entry_read.notes_w;
} else {
strncat(entry_read.notes, line_read + 1, w);
}
entry_read.notes_w += w;
break;
default:
break;
}
}
fclose(f);
/* regenerate uid if needed */
if (!last_uid) {
for (int i = 0; i < data_n; i++)
if (last_uid < strtol(get_data_item(UID_COL, i), NULL, 16))
last_uid = strtol(get_data_item(UID_COL, i), NULL, 16);
}
/* parse all files in hotpot directory and match them to parsed data */
DIR *d = opendir(open_dir);
struct dirent *dir;
struct stat file_stats;
char file_path[PATH_SIZE + NAME_SIZE];
int found = 0;
int new_files = 0;
int n = data_n;
int is_dir;
data_entry_t dummy_entry;
dummy_entry.uid[0] = '\0';
dummy_entry.notes[0] = '\0';
dummy_entry.notes_w = 0;
for (int i = 0; i < DATA_COLS_N; i++)
dummy_entry.misc[i][0] = '\0';
/* scan found files */
while ((dir = readdir(d))) {
found = 0;
strcpy(file_path, open_dir);
strcat(file_path, dir->d_name);
stat(file_path, &file_stats);
is_dir = (file_stats.st_mode & S_IFMT) == S_IFDIR ? 1 : 0;
//printf("%i\t%s\n", is_dir, dir->d_name); TODO fix directory detection
if (is_dir || dir->d_name[0] == '.') {
continue;
}
for (int i = 0; i < n; i++) {
if (strcmp(dir->d_name, get_data_item(NAME_COL, i)) == 0) {
data_tags[i] = TAG_NONE;
found = 1;
break;
}
}
/* if file not found, create new entry */
if (!found) {
clean_string_copy(dummy_entry.filename, dir->d_name, ITEM_SIZE, 0);
push_entry(TAG_NEW, dummy_entry);
new_files = 1;
}
}
/* set tag values in data */
for (int i = 0; i < data_n; i++) {
set_entry_tag_col(i, data_tags[i]);
}
btk_cell_table_deselect(table_cell);
strcpy(select_note, "");
set_activity_status((new_files ? STATUS_EDITED : STATUS_LATEST), 1);
return;
}
void
hotpot_reload()
{
hotpot_unload();
select_data = -1;
strcpy(select_note, "");
hotpot_load();
set_main_win_states(1);
}
void
hotpot_sync()
{
set_activity_status(STATUS_BUSY, 1);
char hotpot_file[PATH_SIZE + NAME_SIZE];
strcpy(hotpot_file, open_dir);
strcat(hotpot_file, "/.htpt");
FILE *f = fopen(hotpot_file, "w");
if (!f) {
printf("ERROR - couldn't write hotpot file\n");
set_activity_status(STATUS_ERROR, 1);
return;
}
/* output last uid */
fprintf(f, "-%X\n\n", last_uid);
/* output rows of data */
char *note_token;
char note[TEXT_SIZE];
for (int i = 0; i < data_n; i++) {
if ((data_tags[i] & TAG_DEL))
continue;
fprintf(f, "[\n");
fprintf(f, "-%s\n", get_data_item(UID_COL, i));
fprintf(f, "+%s\n", get_data_item(NAME_COL, i));
for (int j = 0; j < DATA_COLS_N; j++)
fprintf(f, ".%s\n", get_data_item(j + 3, i));
// TODO
/*
strcpy(note, data_notes + i * TEXT_SIZE);
char *lim = strchr(note, '\n');
while (lim) {
*lim = '\0';
printf("%lu >> \'%s\'\n", strlen(note), note);
fprintf(f, "*%s\n", note);
strcpy(note, ++lim);
lim = strchr(note, '\n');
}
*/
strcpy(note, data_notes + i * TEXT_SIZE);
note_token = strtok(note, "\n");
while (note_token) {
fprintf(f, "*%s\n", note_token);
note_token = strtok(NULL, "\n");
}
fprintf(f, "]\n\n");
}
fclose(f);
/* rename edited files if those are not stray entries */
char cmd_mv[11 + 2 * (PATH_SIZE + NAME_SIZE)];
for (int i = 0; i < data_n; i++) {
if (!(data_tags[i] & TAG_STRAY) && strcmp(data_rename + i * ITEM_SIZE, "")) {
sprintf(cmd_mv,
"mv \'%s/%s\' \'%s/%s\'",
open_dir,
data_rename + i * ITEM_SIZE,
open_dir,
get_data_item(NAME_COL, i));
system(cmd_mv);
}
}
/* delete rows with DEL tag by shifting down */
int c = 0;
char mid[TEXT_SIZE]; /* just to get rid of pointer overlap warning */
char cmd_rm[5 + PATH_SIZE + NAME_SIZE];
for (int i = 0; i < data_n; i++) {
if (c) {
data_tags[i - c] = data_tags[i];
strcpy(mid, data_notes + i * TEXT_SIZE);
strcpy(data_notes + (i - c) * TEXT_SIZE, mid);
for (int j = 0; j < DATA_COLS_N + 3; j++)
strcpy(get_data_item(j, i - c), get_data_item(j, i));
}
if ((data_tags[i] & TAG_DEL)) {
if (!(data_tags[i] & TAG_STRAY)) {
sprintf(cmd_rm,
"rm \'%s/%s\'",
open_dir,
get_data_item(NAME_COL, i));
system(cmd_rm);
}
c++;
}
}
data_n -= c;
/* remove EDIT and NEW tags */
for (int i = 0; i < data_n; i++) {
data_tags[i] &= ~(TAG_EDITED | TAG_NEW);
}
/* update tag values in data */
for (int i = 0; i < data_n; i++) {
set_entry_tag_col(i, data_tags[i]);
}
set_activity_status(STATUS_LATEST, 0);
btk_window_redraw(main_win);
return;
}
void
hotpot_unload()
{
if (data_n) {
free(data);
free(data_tags);
free(data_spot);
free(data_notes);
free(data_rename);
data_n = 0;
}
}
void
move_dir(int dir_sel)
{
if (dir_sel < 0 || !dir_spot[dir_sel])
return;
/* if first item selected ("..") go one dir up, unless at / */
if (!dir_sel) {
if (strlen(dir_current) <= 1)
return;
dir_current[strlen(dir_current) - 1] = '\0';
*(strrchr(dir_current, '/')) = '\0';
} else {
char dir_sel_name[PATH_SIZE];
strcpy(dir_sel_name, dir_list + dir_sel * NAME_SIZE);
strcat(dir_current, dir_sel_name);
}
strcat(dir_current, "/");
parse_dir(dir_current, nav_mode);
btk_window_redraw_cell(file_picker, dir_list_cell_id);
btk_window_redraw_cell(file_picker, 2);
if (dir_sel) {
btk_cell_list_deselect(dir_list_cell);
set_file_picker_states(-1);
}
}
void
open_ext_dir()
{
char *args[PATH_SIZE]={"./EXEC", open_dir, NULL};
int pid = fork();
if (pid == 0) {
execvp("xdg-open", args);
} else if (pid < 0) {
printf("ERROR - couldn't open hotpot directory in file explorer\n");
}
}
void
open_ext_entry(int sel)
{
if (sel < 0)
return;
char file[PATH_SIZE + ITEM_SIZE];
strcpy(file, open_dir);
strcat(file, "/");
strcat(file, get_data_item(NAME_COL, sel));
char *args[PATH_SIZE]={"./EXEC", file, NULL};
int pid = fork();
if (pid == 0) {
execvp("xdg-open", args);
} else if (pid < 0) {
printf("ERROR - couldn't open item externaly\n");
}
}
int
parse_dir(char *dir_path, int mode)
{
DIR *d = opendir(dir_path);
if (!d) {
printf("ERROR - couldn't read directory");
return 1;
}
struct dirent *dir;
struct stat file_stats;
char file_path[PATH_SIZE + NAME_SIZE];
unsigned int dir_buffer_n = DIR_BUFFER_INC;
int c = 0;
int is_dir;
if (dir_list_n) {
free(dir_list);
free(dir_spot);
}
dir_list_n = 0;
dir_list = malloc(dir_buffer_n * NAME_SIZE);
dir_spot = malloc(dir_buffer_n * sizeof(int));
while ((dir = readdir(d))) {
sprintf(file_path, "%s%s", dir_path, dir->d_name);
stat(file_path, &file_stats);
is_dir = (file_stats.st_mode & S_IFMT) == S_IFDIR ? 1 : 0;
if (((mode & DIR_FILES) || is_dir) && c &&
((mode & DIR_HIDDEN) || dir->d_name[0] != '.' || c < 2)) {
strcpy(dir_list + dir_list_n * NAME_SIZE, dir->d_name);
if (is_dir) /* adding '/' to dir items to easily detect them when sorting */
strcat(dir_list + dir_list_n * NAME_SIZE, "/");
dir_spot[dir_list_n] = 0;
dir_list_n++;
}
c++;
if (dir_list_n >= dir_buffer_n - 1) {
dir_buffer_n += DIR_BUFFER_INC;
dir_spot = realloc(dir_spot, dir_buffer_n * sizeof(int));
dir_list = realloc(dir_list, dir_buffer_n * NAME_SIZE);
}
}
/* sort dir list and set hilights */
char *a;
qsort(dir_list, dir_list_n, NAME_SIZE, cmp_str_for_qsort);
for (int i = 0; i < dir_list_n; i++) {
a = strchr(dir_list + i * NAME_SIZE, '/');
if (a) {
*a = '\0';
dir_spot[i] = find_dir_spot(dir_path, dir_list + i * NAME_SIZE);
}
}
return 0;
}
void
push_entry(int tag, data_entry_t entry)
{
if (!data) {
printf("ERROR - tried to push entry into NULL data array\n");
return;
}
if (data_n >= data_buffer_n - 1) {
data_buffer_n += DATA_BUFFER_INC;
data = realloc(data, data_buffer_n * ITEM_SIZE * (DATA_COLS_N + 3));
data_notes = realloc(data_notes, data_buffer_n * TEXT_SIZE);
data_tags = realloc(data_tags, data_buffer_n * sizeof(int));
data_spot = realloc(data_spot, data_buffer_n * sizeof(int));
data_rename = realloc(data_rename, data_buffer_n * ITEM_SIZE);
}
if (!strcmp(entry.uid, "")) {
char new_uid[64];
sprintf(new_uid, "%X", ++last_uid);
strcpy(get_data_item(UID_COL, data_n), new_uid);
} else {
strcpy(get_data_item(UID_COL, data_n), entry.uid);
}
data_tags[data_n] = tag;
strcpy(data_rename + data_n * ITEM_SIZE, "");
strcpy(get_data_item(NAME_COL, data_n), entry.filename);
for (int i = 0; i < DATA_COLS_N; i++)
strcpy(get_data_item(i + 3, data_n), entry.misc[i]);
strcpy(data_notes + data_n * TEXT_SIZE, entry.notes);
data_n++;
}
void
quit()
{
hotpot_unload();
if (dir_list_n) {
free(dir_list);
free(dir_spot);
}
btk_window_destroy(entry_form);
btk_window_destroy(file_picker);
btk_window_destroy(main_win);
btk_window_destroy(settings);
btk_close(session);
exit(0);
}
void
settings_close()
{
btk_window_enable(main_win);
btk_unmap(session, settings);
}
void
settings_open()
{
btk_window_disable(main_win);
btk_map(session, settings);
}
void
set_activity_status(int status, int redraw)
{
switch (status) {
case STATUS_NONE:
strcpy(status_activity, "n/a");
activity_cell->state |= BTK_CELL_STATE_DISABLED;
activity_cell->state &= ~BTK_CELL_STATE_BELL;
activity_cell->state &= ~BTK_CELL_STATE_ON;
break;
case STATUS_ERROR:
strcpy(status_activity, "error");
activity_cell->state &= ~BTK_CELL_STATE_DISABLED;
activity_cell->state |= BTK_CELL_STATE_BELL;
activity_cell->state &= ~BTK_CELL_STATE_ON;
break;
case STATUS_LATEST:
strcpy(status_activity, "synced");
activity_cell->state &= ~BTK_CELL_STATE_DISABLED;
activity_cell->state &= ~BTK_CELL_STATE_BELL;
activity_cell->state &= ~BTK_CELL_STATE_ON;
break;
case STATUS_EDITED:
strcpy(status_activity, "unsynced");
activity_cell->state &= ~BTK_CELL_STATE_DISABLED;
activity_cell->state &= ~BTK_CELL_STATE_BELL;
activity_cell->state |= BTK_CELL_STATE_ON;
break;
case STATUS_BUSY:
strcpy(status_activity, "busy");
activity_cell->state &= ~BTK_CELL_STATE_DISABLED;
activity_cell->state |= BTK_CELL_STATE_BELL;
activity_cell->state &= ~BTK_CELL_STATE_ON;
break;
}
if (redraw)
btk_window_redraw_cell(main_win, activity_cell_id);
}
void
set_edit_tag()
{
if (select_data < 0 || select_data >= data_n) {
printf("ERROR - can't tag out of bounds selection\n");
return;
}
data_tags[select_data] |= TAG_DEL;
btk_window_redraw_cell(main_win, table_cell_id);
}
void
set_entry_tag_col(unsigned int id, int tag)
{
if (tag & TAG_DEL) {
strcpy(get_data_item(TAG_COL, id), "DEL");
data_spot[id] = 1;
} else if (tag & TAG_VISIBILITY) {
strcpy(get_data_item(TAG_COL, id), "FOCUS");
data_spot[id] = 4;
} else if (tag & TAG_EDITED){
strcpy(get_data_item(TAG_COL, id), "EDIT");
data_spot[id] = 1;
} else if (tag & TAG_STRAY){
strcpy(get_data_item(TAG_COL, id), "STRAY");
data_spot[id] = 3;
} else if (tag & TAG_NEW){
strcpy(get_data_item(TAG_COL, id), "NEW");
data_spot[id] = 2;
} else {
strcpy(get_data_item(TAG_COL, id), "");
data_spot[id] = 0;
}
}
void
set_file_picker_states(int sel)
{
nav_select = sel;
if (sel < 1) { /* separate to avoid segfault */
file_select_cell->state |= BTK_CELL_STATE_DISABLED;
btk_window_redraw_cell(file_picker, file_select_cell_id);
return;
}
switch (file_picker_mode) {
case 0:;
char forbidden_path[PATH_SIZE];
strcpy(forbidden_path, open_dir);
strcat(forbidden_path, "/");
if(!dir_spot[sel] && strcmp(dir_current, forbidden_path) != 0 && !file_exists()) {
file_select_cell->state &= ~(BTK_CELL_STATE_DISABLED);
} else {
file_select_cell->state |= BTK_CELL_STATE_DISABLED;
}
break;
case 1:
if (dir_spot[sel] == 1) {
file_select_cell->state &= ~(BTK_CELL_STATE_DISABLED);
} else {
file_select_cell->state |= BTK_CELL_STATE_DISABLED;
}
break;
case 2:
if (dir_spot[sel] == 1) {
file_select_cell->state |= BTK_CELL_STATE_DISABLED;
} else {
file_select_cell->state &= ~(BTK_CELL_STATE_DISABLED);
}
break;
}
btk_window_redraw_cell(file_picker, file_select_cell_id);
}
void
set_main_win_states(int redraw)
{
if (!(strcmp(open_dir, ""))) {
main_win->cells[4].state |= BTK_CELL_STATE_DISABLED;
main_win->cells[5].state |= BTK_CELL_STATE_DISABLED;
main_win->cells[6].state |= BTK_CELL_STATE_DISABLED;
main_win->cells[7].state |= BTK_CELL_STATE_DISABLED;
main_win->cells[10].state |= BTK_CELL_STATE_DISABLED;
main_win->cells[16].state |= BTK_CELL_STATE_DISABLED;
main_win->cells[17].state |= BTK_CELL_STATE_DISABLED;
strcpy(status_open_file, "no hotpot opened");
strcpy(status_files_n, "n/a entries");
set_activity_status(STATUS_NONE, redraw);
} else {
main_win->cells[4].state &= ~BTK_CELL_STATE_DISABLED;
main_win->cells[5].state &= ~BTK_CELL_STATE_DISABLED;
main_win->cells[6].state &= ~BTK_CELL_STATE_DISABLED;
main_win->cells[7].state &= ~BTK_CELL_STATE_DISABLED;
main_win->cells[10].state &= ~BTK_CELL_STATE_DISABLED;
main_win->cells[16].state &= ~BTK_CELL_STATE_DISABLED;
main_win->cells[17].state &= ~BTK_CELL_STATE_DISABLED;
strcpy(status_open_file, open_dir);
sprintf(status_files_n, "%i entries", data_n);
}
if (data_n < 1 || select_data < 0) {
main_win->cells[11].state |= BTK_CELL_STATE_DISABLED;
main_win->cells[12].state |= BTK_CELL_STATE_DISABLED;
main_win->cells[19].state |= BTK_CELL_STATE_DISABLED;
} else {
main_win->cells[11].state &= ~BTK_CELL_STATE_DISABLED;
main_win->cells[12].state &= ~BTK_CELL_STATE_DISABLED;
main_win->cells[19].state &= ~BTK_CELL_STATE_DISABLED;
}
if (redraw)
btk_window_redraw(main_win);
}
void
str_to_lower_case(char *str)
{
for (int i = 0; str[i] != '\0'; i++)
if(str[i] >= 'A' && str[i] <= 'Z')
str[i] += 32;
}
void
toggle_col(int val, btk_arg_t arg)
{
data_cols_ena[arg.i] = val;
btk_cell_table_update_geometry(table_cell);
btk_window_redraw_cell(main_win, table_cell_id);
}
void
toggle_del_tag()
{
if (select_data < 0 || select_data >= data_n) {
printf("ERROR - can't tag out of bounds selection\n");
return;
}
data_tags[select_data] ^= TAG_DEL;
set_entry_tag_col(select_data, data_tags[select_data]);
set_activity_status(STATUS_EDITED, 1);
btk_window_redraw_cell(main_win, table_cell_id);
}
void
toggle_hidden(int i, btk_arg_t arg)
{
if (i) {
nav_mode |= DIR_HIDDEN;
} else {
nav_mode &= ~DIR_HIDDEN;
}
parse_dir(dir_current, nav_mode);
btk_window_redraw_cell(file_picker, dir_list_cell_id);
}
void
toggle_notes(int val, btk_arg_t arg)
{
if (val) {
table_cell->ca.h = 9;
notes_view_cell->state &= ~BTK_CELL_STATE_HIDDEN;
} else {
table_cell->ca.h = 15;
notes_view_cell->state |= BTK_CELL_STATE_HIDDEN;
}
btk_window_update_cell_area(main_win, table_cell);
btk_window_redraw(main_win);
}
void
toggle_visibility_tag()
{
if (select_data < 0 || select_data >= data_n) {
printf("ERROR - can't tag out of bounds selection\n");
return;
}
data_tags[select_data] ^= TAG_VISIBILITY;
set_entry_tag_col(select_data, data_tags[select_data]);
btk_window_redraw_cell(main_win, table_cell_id);
}
void
update_select_data(int sel)
{
select_data = sel;
if (sel > data_n)
select_data = -1;
if (select_data < 0) {
strcpy(select_note, "");
main_win->cells[11].state |= BTK_CELL_STATE_DISABLED;
main_win->cells[12].state |= BTK_CELL_STATE_DISABLED;
main_win->cells[19].state |= BTK_CELL_STATE_DISABLED;
} else {
strcpy(select_note,data_notes + sel * TEXT_SIZE);
main_win->cells[11].state &= ~BTK_CELL_STATE_DISABLED;
main_win->cells[12].state &= ~BTK_CELL_STATE_DISABLED;
main_win->cells[19].state &= ~BTK_CELL_STATE_DISABLED;
}
btk_cell_editor_update_text(notes_view_cell);
btk_window_redraw_cell(main_win, notes_view_cell_id);
btk_window_redraw_cell(main_win, 11);
btk_window_redraw_cell(main_win, 12);
btk_window_redraw_cell(main_win, 19);
}