se

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

choose_one_of_selection.h (16745B)


      1 #ifndef CHOOSE_ONE_OF_SELECTION_H_
      2 #define CHOOSE_ONE_OF_SELECTION_H_
      3 
      4 #include "../../config.h"
      5 #include "../../extension.h"
      6 #include <ctype.h>
      7 #include <dirent.h>
      8 
      9 static int draw_dir(struct window_split_node* win);
     10 static int draw_search_buffers(struct window_split_node* wn);
     11 static int draw_search_keyword_in_all_buffers(struct window_split_node* wn);
     12 
     13 static int file_buffer_keypress_override_callback(int* skip_keypress_callback, struct window_split_node* wn, KeySym ksym, int modkey, const char* buf, int len);
     14 static int choose_one_of_selection_keypress_override_callback(int* skip_keypress_callback, struct window_split_node* wn, KeySym ksym, int modkey, const char* buf, int len);
     15 
     16 static const struct extension file_browser = {
     17 	.wn_custom_window_draw = draw_dir,
     18 	.wn_custom_window_keypress_override = file_buffer_keypress_override_callback,
     19 };
     20 
     21 static const struct extension search_open_fb = {
     22 	.wn_custom_window_draw = draw_search_buffers,
     23 	.wn_custom_window_keypress_override = choose_one_of_selection_keypress_override_callback
     24 };
     25 
     26 static const struct extension search_keywords_open_fb = {
     27 	.wn_custom_window_draw = draw_search_keyword_in_all_buffers,
     28 	.wn_custom_window_keypress_override = choose_one_of_selection_keypress_override_callback
     29 };
     30 
     31 struct keyword_pos {
     32 	int offset, fb_index;
     33 };
     34 
     35 static void choose_one_of_selection(const char* prefix, const char* search, const char* err,
     36 									const char*(*get_next_element)(const char*, const char*, int* offset, struct glyph* attr, void* data),
     37 									int* selected_line, int minx, int miny, int maxx, int maxy, int focused);
     38 
     39 // TODO: system for custom buffers
     40 // and allow for input overrides
     41 // have theese in their own file
     42 static const char* file_browser_next_item(const char* path, const char* search, int* offset, struct glyph* attr, void* data);
     43 // data pointer will give the file buffer of the current item
     44 static const char* buffer_search_next_item(const char* tmp, const char* search, int* offset, struct glyph* attr, void* data);
     45 // data pointer will give the keyword_pos of the current item
     46 static const char* buffers_search_keyword_next_item(const char* tmp, const char* search, int* offset, struct glyph* attr, void* data);
     47 
     48 const char*
     49 file_browser_next_item(const char* path, const char* search, int* offset, struct glyph* attr, void* data)
     50 {
     51 	static char filename_search[PATH_MAX];
     52 	static char filename[PATH_MAX];
     53 	static char full_path[PATH_MAX];
     54 	static DIR* dir;
     55 	if (!path || !search) {
     56 		if (dir) {
     57 			closedir(dir);
     58 			dir = NULL;
     59 		}
     60 		return NULL;
     61 	}
     62 	if (!dir)
     63 		dir = opendir(path);
     64 	int len = strlen(search);
     65 
     66     struct dirent *folder;
     67     while((folder = readdir(dir))) {
     68 		strcpy(filename, folder->d_name);
     69 		strcpy(full_path, path);
     70 		strcat(full_path, filename);
     71 		if (path_is_folder(full_path)) {
     72 			strcat(filename, "/");
     73 			if (attr)
     74 				attr->fg = path_color;
     75 		} else {
     76 			if (attr)
     77 				attr->fg = default_attributes.fg;
     78 		}
     79 
     80 		strcpy(filename_search, filename);
     81 		const char* s_repl = search;
     82 		while(!isupper(*s_repl++)) {
     83 			if (*s_repl == 0) {
     84 				for (char* fs = filename_search; *fs; fs++)
     85 					*fs = tolower(*fs);
     86 				break;
     87 			}
     88 		}
     89 
     90 		int f_len = strlen(filename_search);
     91 		char* search_start = filename_search;
     92 		if (!*search)
     93 			goto search_match;
     94 		while((search_start = memchr(search_start, *search, f_len))) {
     95 			if (memcmp(search_start, search, len) == 0) {
     96 				search_match:
     97 				if (search[0] != '.' && folder->d_name[0] == '.')
     98 					break;
     99 				if (strcmp(filename, "./") == 0 || strcmp(filename, "../") == 0)
    100 					break;
    101 				if (offset)
    102 					*offset = search_start - filename_search;
    103 				return filename;
    104 			}
    105 			search_start++;
    106 		}
    107 	}
    108 	*filename = *full_path = 0;
    109 	closedir(dir);
    110 	dir = NULL;
    111 	return NULL;
    112 }
    113 
    114 const char*
    115 buffer_search_next_item(const char* tmp, const char* search, int* offset, struct glyph* attr, void* data)
    116 {
    117 	static struct window_buffer wb;
    118 	static char file_path[PATH_MAX];
    119 	static int quit_next = 0;
    120 	if (!tmp || !search) {
    121 		wb.fb_index = 0;
    122 		quit_next = 0;
    123 		return NULL;
    124 	}
    125 
    126 	int len = strlen(search);
    127 	for(;;) {
    128 		if (quit_next) {
    129 			wb.fb_index = 0;
    130 			quit_next = 0;
    131 			return NULL;
    132 		}
    133 		struct file_buffer* fb = get_fb(&wb);
    134 		int last_fb_index = wb.fb_index;
    135 		wb.fb_index++;
    136 		get_fb(&wb);
    137 		if (wb.fb_index <= last_fb_index)
    138 			quit_next = 1;
    139 
    140 		strcpy(file_path, fb->file_path);
    141 
    142 		const char* s_repl = search;
    143 		while(!isupper(*s_repl++)) {
    144 			if (*s_repl == 0) {
    145 				for (char* fs = file_path; *fs; fs++)
    146 					*fs = tolower(*fs);
    147 				break;
    148 			}
    149 		}
    150 		char* search_start = file_path;
    151 
    152 		if (!len)
    153 			goto search_match;
    154 		while ((search_start = strchr(search_start+1, *search))) {
    155 			if (memcmp(search_start, search, len) == 0) {
    156 			search_match:
    157 				if (offset)
    158 					*offset = search_start - file_path;
    159 				if (data)
    160 					*(int*)data = last_fb_index;
    161 				return fb->file_path;
    162 			}
    163 		}
    164 	}
    165 }
    166 
    167 const char*
    168 buffers_search_keyword_next_item(const char* tmp, const char* search, int* offset, struct glyph* attr, void* data)
    169 {
    170 	static char item[LINE_MAX_LEN];
    171 	static struct window_buffer wb;
    172 	static int pos = -1;
    173 	if (!tmp || !search) {
    174 		wb.fb_index = 0;
    175 		pos = -1;
    176 		return NULL;
    177 	}
    178 	int len = strlen(search);
    179 	if (!len)
    180 		return NULL;
    181 
    182 	for (;;) {
    183 		struct file_buffer* fb = get_fb(&wb);
    184 
    185 		if ((pos = fb_seek_string(fb, pos+1, search)) >= 0) {
    186 			const char* filename = strrchr(fb->file_path, '/')+1;
    187 			if (!filename)
    188 				filename = fb->file_path;
    189 
    190 			int tmp, y;
    191 			fb_offset_to_xy(fb, pos, 0, wb.y_scroll, &tmp, &y, &tmp);
    192 
    193 			snprintf(item, LINE_MAX_LEN, "%s:%d: ", filename, y);
    194 			int itemlen = strlen(item);
    195 
    196 			char* line = fb_get_line_at_offset(fb, pos);
    197 			char* line_no_whitespace = line;
    198 			if (!isspace(*search))
    199 				while(isspace(*line_no_whitespace)) line_no_whitespace++;
    200 
    201 			snprintf(item + itemlen, LINE_MAX_LEN - itemlen, "%s", line_no_whitespace);
    202 
    203 			int line_start = fb_seek_char_backwards(fb, pos, '\n') + (line_no_whitespace - line);
    204 			free(line);
    205 
    206 			if (line_start < 0)
    207 				line_start = 0;
    208 			if (offset)
    209 				*offset = (pos - line_start) + itemlen;
    210 			if (data)
    211 				*(struct keyword_pos*)data = (struct keyword_pos){.offset = pos, .fb_index = wb.fb_index};
    212 			pos = fb_seek_char(fb, pos+1, '\n');
    213 			return item;
    214 		} else {
    215 			int last_fb_index = wb.fb_index;
    216 			wb.fb_index++;
    217 			get_fb(&wb);
    218 			if (wb.fb_index <= last_fb_index)
    219 				break;
    220 			pos = -1;
    221 		}
    222 	}
    223 	wb.fb_index = 0;
    224 	pos = -1;
    225 	return NULL;
    226 }
    227 
    228 static void
    229 choose_one_of_selection(const char* prefix, const char* search, const char* err,
    230 						const char*(*get_next_element)(const char*, const char*, int* offset, struct glyph* attr, void* data),
    231 						int* selected_line, int minx, int miny, int maxx, int maxy, int focused)
    232 {
    233 	soft_assert(prefix, prefix = "ERROR";);
    234 	soft_assert(search, search = "ERROR";);
    235 	soft_assert(err, search = "ERROR";);
    236 	soft_assert(selected_line, static int tmp; selected_line = &tmp;);
    237 	soft_assert(get_next_element, die("function choose_one_of_selection REQUIRES get_next_element to be a valid pointer"););
    238 
    239 	// change background color
    240 	global_attr.bg = alternate_bg_dark;
    241 	screen_set_region(minx, miny+1, maxx, maxy, ' ');
    242 	global_attr = default_attributes;
    243 	get_next_element(NULL, NULL, NULL, NULL, NULL);
    244 
    245 	int len = strlen(search);
    246 
    247 	// count folders to get scroll
    248 	int folder_lines = maxy - miny - 2;
    249 	int elements = 0;
    250 	int tmp_offset;
    251 	int limit = MAX(*selected_line, 999);
    252 	while(get_next_element(prefix, search, &tmp_offset, NULL, NULL) && elements <= limit)
    253 		elements++;
    254 	get_next_element(NULL, NULL, NULL, NULL, NULL);
    255 	*selected_line = MIN(*selected_line, elements-1);
    256 	int sel_local = *selected_line;
    257 
    258 	// print num of files
    259 	char count[256];
    260 	if (elements >= 1000)
    261 		snprintf(count, sizeof(count), "[>999:%2d] ", *selected_line+1);
    262 	else if (*selected_line > folder_lines)
    263 		snprintf(count, sizeof(count), "^[%3d:%2d] ", elements, *selected_line+1);
    264 	else if (elements-1 > folder_lines)
    265 		snprintf(count, sizeof(count), "ˇ[%3d:%2d] ", elements, *selected_line+1);
    266 	else
    267 		snprintf(count, sizeof(count), " [%3d:%2d] ", elements, *selected_line+1);
    268 
    269 	// print search term with prefix in front of it
    270 	// prefix is in path_color
    271 	global_attr.fg = path_color;
    272 	int new_x = write_string(count, miny, minx, maxx+1);
    273 	new_x = write_string(prefix, miny, new_x, maxx+1);
    274 	global_attr = default_attributes;
    275 	new_x = write_string(search, miny, new_x, maxx+1);
    276 
    277 	// print elements
    278 	int start_miny = miny;
    279 	int offset;
    280 	elements--;
    281 	miny++;
    282 	global_attr = default_attributes;
    283 	global_attr.bg = alternate_bg_dark;
    284 	const char* element;
    285 	while(miny <= maxy && (element = get_next_element(prefix, search, &offset, &global_attr, NULL))) {
    286 		if (elements > folder_lines && sel_local > folder_lines) {
    287 			elements--;
    288 			sel_local--;
    289 			continue;
    290 		}
    291 		write_string(element, miny, minx, maxx+1);
    292 
    293 		// change the color to highlight search term
    294 		for (int i = minx + offset; i < minx + len + offset && i < maxx+1; i++)
    295 			screen_set_attr(i, miny)->fg = highlight_color;
    296 		// change the background of the selected line
    297 		if (miny - start_miny - 1 == sel_local)
    298 			for (int i = minx; i < maxx+1; i++)
    299 				screen_set_attr(i, miny)->bg = selection_bg;
    300 		miny++;
    301 	}
    302 
    303 	if (elements < 0) {
    304 		global_attr = default_attributes;
    305 		global_attr.fg = warning_color;
    306 		write_string(err, start_miny, new_x, maxx+1);
    307 	}
    308 
    309 	// draw
    310 
    311 	for (int y = start_miny; y < maxy+1; y++)
    312 		xdrawline(minx, y, maxx+1);
    313 
    314 	draw_horisontal_line(maxy-1, minx, maxx);
    315 	xdrawcursor(new_x, start_miny, focused);
    316 
    317 	global_attr = default_attributes;
    318 }
    319 
    320 static int
    321 draw_dir(struct window_split_node* wn)
    322 {
    323 	soft_assert(wn->wb.mode < WB_MODES_END, return 1;);
    324 	if (wn->wb.mode != WB_FILE_BROWSER) return 0;
    325 
    326 	int focused = &wn->wb == focused_window;
    327 	struct file_buffer* fb = get_fb(&wn->wb);
    328 	char* folder = file_path_get_path(fb->file_path);
    329 
    330 	fb_change(fb, "\0", 1, fb->len, 1);
    331 	if (fb->len > 0) fb->len--;
    332 
    333 	choose_one_of_selection(folder, fb->contents, " [Create New File]", file_browser_next_item,
    334 							&wn->selected, wn->minx, wn->miny, wn->maxx, wn->maxy, focused);
    335 
    336 	free(folder);
    337 	return 1;
    338 }
    339 
    340 static int
    341 draw_search_buffers(struct window_split_node* wn)
    342 {
    343 	soft_assert(wn->wb.mode < WB_MODES_END, return 1;);
    344 	if (wn->wb.mode != WB_SEARCH_FOR_BUFFERS) return 0;
    345 
    346 	int focused = &wn->wb == focused_window;
    347 	choose_one_of_selection("Find loaded buffer: ", wn->search, " [No resuts]", buffer_search_next_item,
    348 							&wn->selected, wn->minx, wn->miny, wn->maxx, wn->maxy, focused);
    349 	return 1;
    350 }
    351 
    352 static int
    353 draw_search_keyword_in_all_buffers(struct window_split_node* wn)
    354 {
    355 	soft_assert(wn->wb.mode < WB_MODES_END, return 1;);
    356 	if (wn->wb.mode != WB_SEARCH_KEYWORD_ALL_BUFFERS) return 0;
    357 
    358 	int focused = &wn->wb == focused_window;
    359 	choose_one_of_selection("Find in all buffers: ", wn->search, " [No resuts]", buffers_search_keyword_next_item,
    360 							&wn->selected, wn->minx, wn->miny, wn->maxx, wn->maxy, focused);
    361 	return 1;
    362 }
    363 
    364 static int
    365 file_browser_actions(KeySym keysym, int modkey)
    366 {
    367 	static char full_path[PATH_MAX];
    368 	struct file_buffer* fb = get_fb(focused_window);
    369 	int offset = fb->len;
    370 
    371 	switch (keysym) {
    372 		int new_fb;
    373 	case XK_BackSpace:
    374 		if (offset <= 0) {
    375 			char* dest = strrchr(fb->file_path, '/');
    376 			if (dest && dest != fb->file_path) *dest = 0;
    377 			return 1;
    378 		}
    379 
    380 		focused_window->cursor_offset = offset;
    381 		wb_move_on_line(focused_window, -1, 0);
    382 		offset = focused_window->cursor_offset;
    383 
    384 		fb_remove(fb, offset, 1, 0, 0);
    385 		focused_node->selected = 0;
    386 		return 1;
    387 	case XK_Tab:
    388 	case XK_Return:
    389 	{
    390 		char* path = file_path_get_path(fb->file_path);
    391 		fb_change(fb, "\0", 1, fb->len, 1);
    392 		if (fb->len > 0) fb->len--;
    393 
    394 		file_browser_next_item(NULL, NULL, NULL, NULL, NULL);
    395 		const char* filename;
    396 		for (int y = 0; (filename = file_browser_next_item(path, fb->contents, NULL, NULL, NULL)); y++) {
    397 			strcpy(full_path, path);
    398 			strcat(full_path, filename);
    399 			if (y == focused_node->selected) {
    400 				if (path_is_folder(full_path)) {
    401 					strcpy(fb->file_path, full_path);
    402 
    403 					fb->len = 0;
    404 					fb->contents[0] = '\0';
    405 					focused_node->selected = 0;
    406 
    407 					free(path);
    408 					file_browser_next_item(NULL, NULL, NULL, NULL, NULL);
    409 					*full_path = 0;
    410 					return 1;
    411 				}
    412 				goto open_file;
    413 			}
    414 		}
    415 
    416 		if (fb->contents[fb->len-1] == '/') {
    417 			free(path);
    418 			*full_path = 0;
    419 			return 1;
    420 		}
    421 
    422 		strcpy(full_path, path);
    423 		strcat(full_path, fb->contents);
    424 open_file:
    425 		new_fb = fb_new_entry(full_path);
    426 		destroy_fb_entry(focused_node, &root_node);
    427 		focused_node->wb = wb_new(new_fb);
    428 		free(path);
    429 		*full_path = 0;
    430 		return 1;
    431 	}
    432 	case XK_Down:
    433 		focused_node->selected++;
    434 		return 1;
    435 	case XK_Up:
    436 		focused_node->selected--;
    437 		if (focused_node->selected < 0)
    438 			focused_node->selected = 0;
    439 		return 1;
    440 	case XK_Escape:
    441 		if (destroy_fb_entry(focused_node, &root_node))
    442 			writef_to_status_bar("Quit");
    443 		return 1;
    444 	}
    445 	return 0;
    446 }
    447 
    448 static void
    449 file_browser_string_insert(const char* buf, int buflen)
    450 {
    451 	static char full_path[PATH_MAX];
    452 	struct file_buffer* fb = get_fb(focused_window);
    453 
    454 	if (fb->len + buflen + strlen(fb->file_path) > PATH_MAX)
    455 		return;
    456 
    457 	if (buf[0] >= 32 || buflen > 1) {
    458 		fb_insert(fb, buf, buflen, fb->len, 0);
    459 		focused_node->selected = 0;
    460 
    461 		if (*buf == '/') {
    462 			fb_change(fb, "\0", 1, fb->len, 0);
    463 			if (fb->len > 0) fb->len--;
    464 			char* path = file_path_get_path(fb->file_path);
    465 			strcpy(full_path, path);
    466 			strcat(full_path, fb->contents);
    467 
    468 			free(path);
    469 
    470 			if (path_is_folder(full_path)) {
    471 				file_browser_actions(XK_Return, 0);
    472 				return;
    473 			}
    474 		}
    475 	} else {
    476 		writef_to_status_bar("unhandled control character 0x%x\n", buf[0]);
    477 	}
    478 }
    479 
    480 static int
    481 search_for_buffer_actions(KeySym keysym, int modkey)
    482 {
    483 	switch (keysym) {
    484 	case XK_Return:
    485 	case XK_Tab:
    486 		if (focused_window->mode == WB_SEARCH_KEYWORD_ALL_BUFFERS) {
    487 			struct keyword_pos kw;
    488 			int n = 0;
    489 			buffers_search_keyword_next_item(NULL, NULL, NULL, NULL, NULL);
    490 			while (buffers_search_keyword_next_item("", focused_node->search, NULL, NULL, &kw)) {
    491 				if (n == focused_node->selected) {
    492 					*focused_window = wb_new(kw.fb_index);
    493 					focused_window->cursor_offset = kw.offset;
    494 					return 1;
    495 				}
    496 				n++;
    497 			}
    498 			buffers_search_keyword_next_item(NULL, NULL, NULL, NULL, NULL);
    499 		} else if (focused_window->mode == WB_SEARCH_FOR_BUFFERS) {
    500 			int fb_index;
    501 			int n = 0;
    502 			buffer_search_next_item(NULL, NULL, NULL, NULL, NULL);
    503 			while (buffer_search_next_item("", focused_node->search, NULL, NULL, &fb_index)) {
    504 				if (n == focused_node->selected) {
    505 					*focused_window = wb_new(fb_index);
    506 					return 1;
    507 				}
    508 				n++;
    509 			}
    510 			buffer_search_next_item(NULL, NULL, NULL, NULL, NULL);
    511 		}
    512 		writef_to_status_bar("no results for \"%s\"", focused_node->search);
    513 		return 1;
    514 	case XK_BackSpace:
    515 		utf8_remove_string_end(focused_node->search);
    516 		focused_node->selected = 0;
    517 		return 1;
    518 	case  XK_Down:
    519 		focused_node->selected++;
    520 		return 1;
    521 	case  XK_Up:
    522 		focused_node->selected--;
    523 		if (focused_node->selected < 0)
    524 			focused_node->selected = 0;
    525 		return 1;
    526 	case  XK_Page_Down:
    527 		focused_node->selected += 10;
    528 		return 1;
    529 	case  XK_Page_Up:
    530 		focused_node->selected -= 10;
    531 		if (focused_node->selected < 0)
    532 			focused_node->selected = 0;
    533 		return 1;
    534 	case XK_Escape:
    535 		if (path_is_folder(get_fb(focused_window)->file_path))
    536 			focused_window->mode = WB_FILE_BROWSER;
    537 		else
    538 			focused_window->mode = WB_NORMAL;
    539 
    540 		writef_to_status_bar("Quit");
    541 		return 1;
    542 	}
    543 	return 0;
    544 }
    545 
    546 static void
    547 search_for_buffer_string_insert(const char* buf, int buflen)
    548 {
    549 	int len = strlen(focused_node->search);
    550 	if (buflen + len + 1 > SEARCH_TERM_MAX_LEN)
    551 		return;
    552 
    553 	if (buf[0] >= 32 || buflen > 1) {
    554 		memcpy(focused_node->search + len, buf, buflen);
    555 		focused_node->search[len + buflen] = 0;
    556 		focused_node->selected = 0;
    557 	} else {
    558 		writef_to_status_bar("unhandled control character 0x%x\n", buf[0]);
    559 	}
    560 }
    561 
    562 int
    563 file_buffer_keypress_override_callback(int* skip_keypress_callback, struct window_split_node* wn, KeySym ksym, int modkey, const char* buf, int len)
    564 {
    565 	soft_assert(wn->wb.mode < WB_MODES_END, return 1;);
    566 	if (wn->wb.mode != WB_FILE_BROWSER) return 0;
    567 
    568 	if (file_browser_actions(ksym, modkey)) {
    569 		*skip_keypress_callback = 1;
    570 		return 1;
    571 	}
    572 	file_browser_string_insert(buf, len);
    573 	*skip_keypress_callback = 1;
    574 	return 1;
    575 }
    576 
    577 int
    578 choose_one_of_selection_keypress_override_callback(int* skip_keypress_callback, struct window_split_node* wn, KeySym ksym, int modkey, const char* buf, int len)
    579 {
    580 	soft_assert(wn->wb.mode < WB_MODES_END, return 1;);
    581 	if (wn->wb.mode != WB_SEARCH_FOR_BUFFERS &&
    582 		wn->wb.mode != WB_SEARCH_KEYWORD_ALL_BUFFERS)
    583 		return 0;
    584 
    585 	if (search_for_buffer_actions(ksym, modkey)) {
    586 		*skip_keypress_callback = 1;
    587 		return 1;
    588 	}
    589 	search_for_buffer_string_insert(buf, len);
    590 	*skip_keypress_callback = 1;
    591 	return 1;
    592 }
    593 
    594 #endif // CHOOSE_ONE_OF_SELECTION_H_