buffer.c (27624B)
1 #include "buffer.h" 2 3 #include "config.h" 4 #include "se.h" 5 #include "extension.h" 6 7 #include <unistd.h> 8 #include <sys/stat.h> 9 #include <errno.h> 10 #include <time.h> 11 12 // TODO: mark buffers as dirty and only redraw windows that have been changed 13 14 //////////////////////////////////////////////// 15 // Globals 16 // 17 18 static char root_node_search[SEARCH_TERM_MAX_LEN]; 19 struct window_split_node root_node = {.mode = WINDOW_SINGULAR, .search = root_node_search}; 20 struct window_split_node* focused_node = &root_node; 21 struct window_buffer* focused_window = &root_node.wb; 22 23 static struct file_buffer* file_buffers; 24 static int available_buffer_slots = 0; 25 26 ///////////////////////////////////////////////// 27 // Function implementations 28 // 29 30 //////////////////////////////////////////////// 31 // File buffer 32 // 33 34 static void 35 recursive_mkdir(char *path) { 36 if (!path || !strlen(path)) 37 return; 38 char *sep = strrchr(path, '/'); 39 if(sep) { 40 *sep = '\0'; 41 recursive_mkdir(path); 42 *sep = '/'; 43 } 44 if(mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) && errno != EEXIST) 45 fprintf(stderr, "error while trying to create '%s'\n%s\n", path, strerror(errno)); 46 } 47 48 49 // TODO: file open callback, implement as plugin 50 static int open_seproj(struct file_buffer fb); 51 int 52 open_seproj(struct file_buffer fb) 53 { 54 int first = -1; 55 56 char* path = file_path_get_path(fb.file_path); 57 chdir(path); 58 int offset = -1; 59 60 while((offset = fb_seek_char(&fb, offset+1, ' ')) >= 0) 61 fb_change(&fb, "\n", 1, offset, 0); 62 63 offset = -1; 64 while((offset = fb_seek_char(&fb, offset+1, '\n')) >= 0) { 65 char* line = fb_get_line_at_offset(&fb, offset); 66 if (strlen(line) && !is_file_type(line, ".seproj")) { 67 if (first < 0) 68 first = fb_new_entry(line); 69 else 70 fb_new_entry(line); 71 } 72 free(line); 73 } 74 75 if (first < 0) 76 first = fb_new_entry(NULL); 77 writef_to_status_bar("opened project %s", path); 78 free(path); 79 80 fb_destroy(&fb); 81 return first; 82 } 83 84 void 85 fb_write_to_filepath(struct file_buffer* fb) 86 { 87 if (!fb->file_path) 88 return; 89 soft_assert(fb->contents, return;); 90 FILE* file = fopen(fb->file_path, "w"); 91 soft_assert(file, return;); 92 93 if (fb->mode & FB_UTF8_SIGNED) 94 fwrite("\xEF\xBB\xBF", 1, 3, file); 95 fwrite(fb->contents, sizeof(char), fb->len, file); 96 writef_to_status_bar("saved buffer to %s", fb->file_path); 97 98 fclose(file); 99 call_extension(fb_written_to_file, fb); 100 } 101 102 103 int 104 destroy_fb_entry(struct window_split_node* node, struct window_split_node* root) 105 { 106 // do not allow deletion of the lst file buffer 107 int n = 0; 108 for(; n < available_buffer_slots; n++) 109 if (file_buffers[n].contents && n != node->wb.fb_index) 110 break; 111 if (n >= available_buffer_slots) { 112 writef_to_status_bar("can't delete last buffer"); 113 return 0; 114 } 115 116 if (window_other_nodes_contain_fb(node, root)) { 117 node->wb.fb_index++; 118 node->wb = wb_new(node->wb.fb_index); 119 writef_to_status_bar("swapped buffer"); 120 return 0; 121 } 122 fb_destroy(get_fb(&node->wb)); 123 124 node->wb = wb_new(node->wb.fb_index); 125 126 return 1; 127 } 128 129 130 131 struct file_buffer* 132 get_fb(struct window_buffer* wb) 133 { 134 soft_assert(wb, wb = focused_window;); 135 soft_assert(file_buffers, fb_new_entry(NULL);); 136 137 if (wb->fb_index < 0) 138 wb->fb_index = available_buffer_slots-1; 139 else if (wb->fb_index >= available_buffer_slots) 140 wb->fb_index = 0; 141 142 if (!file_buffers[wb->fb_index].contents) { 143 for(int n = wb->fb_index; n < available_buffer_slots; n++) { 144 if (file_buffers[n].contents) { 145 wb->fb_index = n; 146 return &file_buffers[n]; 147 } 148 } 149 for(int n = 0; n < available_buffer_slots; n++) { 150 if (file_buffers[n].contents) { 151 wb->fb_index = n; 152 return &file_buffers[n]; 153 } 154 } 155 } else { 156 soft_assert(file_buffers[wb->fb_index].contents, ); 157 return &file_buffers[wb->fb_index]; 158 } 159 160 wb->fb_index = fb_new_entry(NULL); 161 writef_to_status_bar("all buffers were somehow deleted, creating new one"); 162 status_bar_bg = warning_color; 163 return get_fb(wb); 164 } 165 166 int 167 fb_delete_selection(struct file_buffer* fb) 168 { 169 if (fb->mode & FB_SELECTION_ON) { 170 fb_remove_selection(fb); 171 wb_move_cursor_to_selection_start(focused_window); 172 fb->mode &= ~(FB_SELECTION_ON); 173 return 1; 174 } 175 return 0; 176 } 177 178 struct file_buffer 179 fb_new(const char* file_path) 180 { 181 struct file_buffer fb = {0}; 182 fb.file_path = xmalloc(PATH_MAX); 183 184 char* res = realpath(file_path, fb.file_path); 185 if (!res) { 186 char* path = file_path_get_path(file_path); 187 recursive_mkdir(path); 188 free(path); 189 190 FILE *new_file = fopen(file_path, "wb"); 191 fclose(new_file); 192 realpath(file_path, fb.file_path); 193 remove(file_path); 194 195 writef_to_status_bar("opened new file %s", fb.file_path); 196 } else if (path_is_folder(fb.file_path)) { 197 int len = strlen(fb.file_path); 198 if (fb.file_path[len-1] != '/' && len < PATH_MAX-1) { 199 fb.file_path[len] = '/'; 200 fb.file_path[len+1] = '\0'; 201 } 202 } else { 203 FILE *file = fopen(fb.file_path, "rb"); 204 if (file) { 205 fseek(file, 0L, SEEK_END); 206 long readsize = ftell(file); 207 rewind(file); 208 209 if (readsize > (long)1.048576e+7) { 210 fclose(file); 211 die("you are opening a huge file(>10MiB), not allowed"); 212 return fb; 213 // TODO: don't crash 214 } 215 216 fb.len = readsize; 217 fb.capacity = readsize + 100; 218 219 fb.contents = xmalloc(fb.capacity); 220 fb.contents[0] = 0; 221 222 char bom[4] = {0}; 223 fread(bom, 1, 3, file); 224 if (strcmp(bom, "\xEF\xBB\xBF")) 225 rewind(file); 226 else 227 fb.mode |= FB_UTF8_SIGNED; 228 fread(fb.contents, 1, readsize, file); 229 fclose(file); 230 231 fb.syntax_index = -1; 232 } 233 } 234 235 if (!fb.capacity) 236 fb.capacity = 100; 237 if (!fb.contents) { 238 fb.contents = xmalloc(fb.capacity); 239 memset(fb.contents, 0, fb.capacity); 240 } 241 fb.ub = xmalloc(sizeof(struct undo_buffer) * UNDO_BUFFERS_COUNT); 242 fb.search_term = xmalloc(SEARCH_TERM_MAX_LEN); 243 fb.non_blocking_search_term = xmalloc(SEARCH_TERM_MAX_LEN); 244 memset(fb.ub, 0, sizeof(struct undo_buffer) * UNDO_BUFFERS_COUNT); 245 memset(fb.search_term, 0, SEARCH_TERM_MAX_LEN); 246 memset(fb.non_blocking_search_term, 0, SEARCH_TERM_MAX_LEN); 247 fb.indent_len = default_indent_len; 248 249 // change line endings 250 int offset = 0; 251 while((offset = fb_seek_string(&fb, offset, "\r\n")) >= 0) 252 fb_remove(&fb, offset, 1, 1, 1); 253 offset = 0; 254 while((offset = fb_seek_char(&fb, offset, '\r')) >= 0) 255 fb_change(&fb, "\n", 1, offset, 1); 256 257 call_extension(fb_new_file_opened, &fb); 258 259 call_extension(fb_contents_updated, &fb, 0, FB_CONTENT_INIT); 260 261 if (res) 262 writef_to_status_bar("new fb %s", fb.file_path); 263 return fb; 264 } 265 266 int 267 fb_new_entry(const char* file_path) 268 { 269 static char full_path[PATH_MAX]; 270 if (!file_path) 271 file_path = "./"; 272 soft_assert(strlen(file_path) < PATH_MAX, file_path = "./";); 273 274 char* res = realpath(file_path, full_path); 275 276 if (available_buffer_slots) { 277 if (res) { 278 for(int n = 0; n < available_buffer_slots; n++) { 279 if (file_buffers[n].contents) { 280 if (strcmp(file_buffers[n].file_path, full_path) == 0) { 281 writef_to_status_bar("buffer exits"); 282 return n; 283 } 284 } 285 } 286 } else { 287 strcpy(full_path, file_path); 288 } 289 290 for(int n = 0; n < available_buffer_slots; n++) { 291 if (!file_buffers[n].contents) { 292 if (is_file_type(full_path, ".seproj")) 293 return open_seproj(fb_new(full_path)); 294 file_buffers[n] = fb_new(full_path); 295 return n; 296 } 297 } 298 } 299 300 if (is_file_type(full_path, ".seproj")) 301 return open_seproj(fb_new(full_path)); 302 303 available_buffer_slots++; 304 file_buffers = xrealloc(file_buffers, sizeof(struct file_buffer) * available_buffer_slots); 305 file_buffers[available_buffer_slots-1] = fb_new(full_path); 306 307 return available_buffer_slots-1; 308 } 309 310 void 311 fb_destroy(struct file_buffer* fb) 312 { 313 free(fb->ub); 314 free(fb->contents); 315 free(fb->file_path); 316 free(fb->search_term); 317 free(fb->non_blocking_search_term); 318 *fb = (struct file_buffer){0}; 319 } 320 321 void 322 fb_insert(struct file_buffer* fb, const char* new_content, const int len, const int offset, int do_not_callback) 323 { 324 soft_assert(fb, return;); 325 soft_assert(fb->contents, fb->capacity = 0;); 326 soft_assert(offset <= fb->len && offset >= 0, 327 fprintf(stderr, "writing past fb '%s'\n", fb->file_path); 328 return; 329 ); 330 331 if (fb->len + len >= fb->capacity) { 332 fb->capacity = fb->len + len + 256; 333 fb->contents = xrealloc(fb->contents, fb->capacity); 334 } 335 if (offset < fb->len) 336 memmove(fb->contents+offset+len, fb->contents+offset, fb->len-offset); 337 fb->len += len; 338 339 memcpy(fb->contents+offset, new_content, len); 340 if (!do_not_callback) 341 call_extension(fb_contents_updated, fb, offset, FB_CONTENT_NORMAL_EDIT); 342 } 343 344 void 345 fb_change(struct file_buffer* fb, const char* new_content, const int len, const int offset, int do_not_callback) 346 { 347 soft_assert(offset <= fb->len && offset >= 0, return;); 348 349 if (offset + len > fb->len) { 350 fb->len = offset + len; 351 if (fb->len >= fb->capacity) { 352 fb->capacity = fb->len + len + 256; 353 fb->contents = xrealloc(fb->contents, fb->capacity); 354 } 355 } 356 357 memcpy(fb->contents+offset, new_content, len); 358 if (!do_not_callback) 359 call_extension(fb_contents_updated, fb, offset, FB_CONTENT_NORMAL_EDIT); 360 } 361 362 int 363 fb_remove(struct file_buffer* fb, int offset, int len, int do_not_calculate_charsize, int do_not_callback) 364 { 365 LIMIT(offset, 0, fb->len-1); 366 if (len == 0) return 0; 367 soft_assert(fb->contents, return 0;); 368 soft_assert(offset + len <= fb->len, return 0;); 369 370 int removed_len = 0; 371 if (do_not_calculate_charsize) { 372 removed_len = len; 373 } else { 374 while (len--) { 375 int charsize = utf8_decode_buffer(fb->contents + offset, fb->len - offset, NULL); 376 if (fb->len - charsize < 0) 377 return 0; 378 removed_len += charsize; 379 } 380 } 381 fb->len -= removed_len; 382 memmove(fb->contents+offset, fb->contents+offset+removed_len, fb->len-offset); 383 if (!do_not_callback) 384 call_extension(fb_contents_updated, fb, offset, FB_CONTENT_NORMAL_EDIT); 385 return removed_len; 386 } 387 388 void 389 wb_copy_ub_to_current(struct window_buffer* wb) 390 { 391 struct file_buffer* fb = get_fb(wb); 392 struct undo_buffer* cub = &fb->ub[fb->current_undo_buffer]; 393 394 fb->contents = xrealloc(fb->contents, cub->capacity); 395 memcpy(fb->contents, cub->contents, cub->capacity); 396 fb->len = cub->len; 397 fb->capacity = cub->capacity; 398 399 wb_move_to_offset(wb, cub->cursor_offset, CURSOR_SNAPPED); 400 //TODO: remove y_scroll from undo buffer 401 wb->y_scroll = cub->y_scroll; 402 } 403 404 405 void 406 fb_undo(struct file_buffer* fb) 407 { 408 if (fb->current_undo_buffer == 0) { 409 writef_to_status_bar("end of undo buffer"); 410 return; 411 } 412 fb->current_undo_buffer--; 413 fb->available_redo_buffers++; 414 415 wb_copy_ub_to_current(focused_window); 416 writef_to_status_bar("undo"); 417 } 418 419 void 420 fb_redo(struct file_buffer* fb) 421 { 422 if (fb->available_redo_buffers == 0) { 423 writef_to_status_bar("end of redo buffer"); 424 return; 425 } 426 fb->available_redo_buffers--; 427 fb->current_undo_buffer++; 428 429 wb_copy_ub_to_current(focused_window); 430 writef_to_status_bar("redo"); 431 } 432 433 void 434 fb_add_to_undo(struct file_buffer* fb, int offset, enum buffer_content_reason reason) 435 { 436 static time_t last_normal_edit; 437 static int edits; 438 439 if (reason == FB_CONTENT_CURSOR_MOVE) { 440 struct undo_buffer* cub = &fb->ub[fb->current_undo_buffer]; 441 cub->cursor_offset = offset; 442 if (focused_window) 443 cub->y_scroll = focused_window->y_scroll; 444 else 445 cub->y_scroll = 0; 446 return; 447 } 448 449 if (reason == FB_CONTENT_NORMAL_EDIT) { 450 time_t previous_time = last_normal_edit; 451 last_normal_edit = time(NULL); 452 453 if (last_normal_edit - previous_time < 2 && edits < 30) { 454 edits++; 455 goto copy_undo_buffer; 456 } else { 457 edits = 0; 458 } 459 } else if (reason == FB_CONTENT_INIT) { 460 goto copy_undo_buffer; 461 } 462 463 fb->available_redo_buffers = 0; 464 if (fb->current_undo_buffer == UNDO_BUFFERS_COUNT-1) { 465 char* begin_buffer = fb->ub[0].contents; 466 memmove(fb->ub, &(fb->ub[1]), (UNDO_BUFFERS_COUNT-1) * sizeof(struct undo_buffer)); 467 fb->ub[fb->current_undo_buffer].contents = begin_buffer; 468 } else { 469 fb->current_undo_buffer++; 470 } 471 472 copy_undo_buffer: ; 473 struct undo_buffer* cub = fb->ub + fb->current_undo_buffer; 474 475 cub->contents = xrealloc(cub->contents, fb->capacity); 476 memcpy(cub->contents, fb->contents, fb->capacity); 477 cub->len = fb->len; 478 cub->capacity = fb->capacity; 479 cub->cursor_offset = offset; 480 if (focused_window) 481 cub->y_scroll = focused_window->y_scroll; 482 else 483 cub->y_scroll = 0; 484 } 485 486 487 char* 488 fb_get_string_between_offsets(struct file_buffer* fb, int start, int end) 489 { 490 int len = end - start; 491 492 char* string = xmalloc(len + 1); 493 memcpy(string, fb->contents+start, len); 494 string[len] = 0; 495 return string; 496 } 497 498 char* 499 fb_get_selection(struct file_buffer* fb, int* selection_len) 500 { 501 if (!(fb->mode & FB_SELECTION_ON)) 502 return NULL; 503 504 int start, end; 505 if (fb_is_selection_start_top_left(fb)) { 506 start = fb->s1o; 507 end = fb->s2o+1; 508 } else { 509 start = fb->s2o; 510 end = fb->s1o+1; 511 } 512 if (selection_len) 513 *selection_len = end - start; 514 return fb_get_string_between_offsets(fb, start, end); 515 } 516 517 518 int 519 fb_is_selection_start_top_left(const struct file_buffer* fb) 520 { 521 return (fb->s1o <= fb->s2o) ? 1 : 0; 522 } 523 524 void 525 fb_remove_selection(struct file_buffer* buffer) 526 { 527 if (!(buffer->mode & FB_SELECTION_ON)) 528 return; 529 530 int start, end, len; 531 if (fb_is_selection_start_top_left(buffer)) { 532 start = buffer->s1o; 533 end = buffer->s2o+1; 534 } else { 535 start = buffer->s2o; 536 end = buffer->s1o+1; 537 } 538 len = end - start; 539 fb_remove(buffer, start, len, 1, 1); 540 call_extension(fb_contents_updated, buffer, start, FB_CONTENT_BIG_CHANGE); 541 } 542 543 char* 544 fb_get_line_at_offset(const struct file_buffer* fb, int offset) 545 { 546 int start = fb_seek_char_backwards(fb, offset, '\n'); 547 if (start < 0) start = 0; 548 int end = fb_seek_char(fb, offset, '\n'); 549 if (end < 0) end = fb->len-1; 550 551 int len = end - start; 552 553 char* res = xmalloc(len + 1); 554 if (len > 0) 555 memcpy(res, fb->contents+start, len); 556 res[len] = 0; 557 return res; 558 } 559 560 void 561 fb_offset_to_xy(struct file_buffer* fb, int offset, int maxx, int y_scroll, int* cx, int* cy, int* xscroll) 562 { 563 *cx = *cy = *xscroll = 0; 564 soft_assert(fb, return;); 565 566 if (fb->len <= 0) 567 return; 568 LIMIT(offset, 0, fb->len); 569 570 char* repl = fb->contents; 571 char* last = repl + offset; 572 573 char* new_repl; 574 if (wrap_buffer && maxx > 0) { 575 int yscroll = 0; 576 while ((new_repl = memchr(repl, '\n', last - repl))) { 577 if (++yscroll >= y_scroll) 578 break; 579 repl = new_repl+1; 580 } 581 *cy = yscroll - y_scroll; 582 } else { 583 while ((new_repl = memchr(repl, '\n', last - repl))) { 584 repl = new_repl+1; 585 *cy += 1; 586 } 587 *cy -= y_scroll; 588 } 589 590 while (repl < last) { 591 if (wrap_buffer && maxx > 0 && (*repl == '\n' || *cx >= maxx)) { 592 *cy += 1; 593 *cx = 0; 594 repl++; 595 continue; 596 } 597 if (*repl == '\t') { 598 repl++; 599 if (*cx <= 0) *cx += 1; 600 while (*cx % tabspaces != 0) *cx += 1; 601 *cx += 1; 602 continue; 603 } 604 rune_t u; 605 repl += utf8_decode_buffer(repl, last - repl, &u); 606 *cx += wcwidth(u); 607 } 608 609 // TODO: make customizable 610 // -1 = wrap, >= 0 is padding or something like that 611 const int padding = 3; 612 613 if ((*cx - maxx) + padding > 0) 614 *xscroll = (*cx - maxx) + padding; 615 } 616 617 //////////////////////////////////////////////// 618 // Window buffer 619 // 620 621 struct window_buffer 622 wb_new(int fb_index) 623 { 624 struct window_buffer wb = {0}; 625 wb.fb_index = fb_index; 626 if (path_is_folder(get_fb(&wb)->file_path)) { 627 wb.mode = WB_FILE_BROWSER; 628 writef_to_status_bar("opened file browser %s", get_fb(&wb)->file_path); 629 } 630 631 return wb; 632 } 633 634 void 635 wb_move_on_line(struct window_buffer* wb, int amount, enum cursor_reason callback_reason) 636 { 637 const struct file_buffer* fb = get_fb(wb); 638 if (fb->len <= 0) 639 return; 640 641 if (amount < 0) { 642 while (wb->cursor_offset > 0 && fb->contents[wb->cursor_offset - 1] != '\n' && amount < 0) { 643 wb->cursor_offset--; 644 if ((fb->contents[wb->cursor_offset] & 0xC0) == 0x80) // if byte starts with 0b10 645 continue; // byte is UTF-8 extender 646 amount++; 647 } 648 LIMIT(wb->cursor_offset, 0, fb->len); 649 } else if (amount > 0) { 650 for (int charsize = 0; 651 wb->cursor_offset < fb->len && amount > 0 && fb->contents[wb->cursor_offset + charsize] != '\n'; 652 wb->cursor_offset += charsize, amount--) { 653 rune_t u; 654 charsize = utf8_decode_buffer(fb->contents + wb->cursor_offset, fb->len - wb->cursor_offset, &u); 655 if (u != '\n' && u != '\t') 656 if (wcwidth(u) <= 0) 657 amount++; 658 if (wb->cursor_offset + charsize > fb->len) 659 break; 660 } 661 } 662 663 if (callback_reason) 664 call_extension(wb_cursor_movement, wb, callback_reason); 665 } 666 667 void 668 wb_move_offset_relative(struct window_buffer* wb, int amount, enum cursor_reason callback_reason) 669 { 670 //NOTE: this does not check if the character on this offset is the start of a valid utf8 char 671 const struct file_buffer* fb = get_fb((wb)); 672 if (fb->len <= 0) 673 return; 674 wb->cursor_offset += amount; 675 LIMIT(wb->cursor_offset, 0, fb->len); 676 677 if (callback_reason) 678 call_extension(wb_cursor_movement, wb, callback_reason); 679 } 680 681 void 682 wb_move_lines(struct window_buffer* wb, int amount, enum cursor_reason callback_reason) 683 { 684 const struct file_buffer* fb = get_fb((wb)); 685 if (fb->len <= 0) 686 return; 687 int offset = wb->cursor_offset; 688 if (amount > 0) { 689 while (amount-- && offset >= 0) { 690 int new_offset = fb_seek_char(fb, offset, '\n'); 691 if (new_offset < 0) { 692 offset = fb->len; 693 break; 694 } 695 offset = new_offset+1; 696 } 697 } else if (amount < 0) { 698 while (amount++ && offset >= 0) 699 offset = fb_seek_char_backwards(fb, offset, '\n')-1; 700 } 701 wb_move_to_offset(wb, offset, callback_reason); 702 } 703 704 void 705 wb_move_to_offset(struct window_buffer* wb, int offset, enum cursor_reason callback_reason) 706 { 707 //NOTE: this does not check if the character on this offset is the start of a valid utf8 char 708 const struct file_buffer* fb = get_fb((wb)); 709 if (fb->len <= 0) 710 return; 711 LIMIT(offset, 0, fb->len); 712 wb->cursor_offset = offset; 713 714 if (callback_reason) 715 call_extension(wb_cursor_movement, wb, callback_reason); 716 } 717 718 void 719 wb_move_to_x(struct window_buffer* wb, int x, enum cursor_reason callback_reason) 720 { 721 soft_assert(wb, return;); 722 struct file_buffer* fb = get_fb(wb); 723 724 int offset = fb_seek_char_backwards(fb, wb->cursor_offset, '\n'); 725 if (offset < 0) 726 offset = 0; 727 wb_move_to_offset(wb, offset, 0); 728 729 int x_counter = 0; 730 731 while (offset < fb->len) { 732 if (fb->contents[offset] == '\t') { 733 offset++; 734 if (x_counter <= 0) x_counter += 1; 735 while (x_counter % tabspaces != 0) x_counter += 1; 736 x_counter += 1; 737 continue; 738 } else if (fb->contents[offset] == '\n') { 739 break; 740 } 741 rune_t u = 0; 742 int charsize = utf8_decode_buffer(fb->contents + offset, fb->len - offset, &u); 743 x_counter += wcwidth(u); 744 if (x_counter <= x) { 745 offset += charsize; 746 if (x_counter == x) 747 break; 748 } else { 749 break; 750 } 751 } 752 wb_move_to_offset(wb, offset, callback_reason); 753 } 754 755 //////////////////////////////////////////////// 756 // Window split node 757 // 758 759 static int 760 is_correct_mode(enum window_split_mode mode, enum move_directons move) 761 { 762 if (move == MOVE_RIGHT || move == MOVE_LEFT) 763 return (mode == WINDOW_HORISONTAL); 764 if (move == MOVE_UP || move == MOVE_DOWN) 765 return (mode == WINDOW_VERTICAL); 766 return 0; 767 } 768 769 void 770 window_node_split(struct window_split_node* parent, float ratio, enum window_split_mode mode) 771 { 772 soft_assert(parent, return;); 773 soft_assert(parent->mode == WINDOW_SINGULAR, return;); 774 soft_assert(mode != WINDOW_SINGULAR, return;); 775 776 if ((parent->maxx - parent->minx < MIN_WINDOW_SPLIT_SIZE_HORISONTAL && mode == WINDOW_HORISONTAL) 777 || (parent->maxy - parent->miny < MIN_WINDOW_SPLIT_SIZE_VERTICAL && mode == WINDOW_VERTICAL)) 778 return; 779 780 parent->node1 = xmalloc(sizeof(struct window_split_node)); 781 *parent->node1 = *parent; 782 parent->node1->search = xmalloc(SEARCH_TERM_MAX_LEN); 783 parent->node1->parent = parent; 784 parent->node1->node1 = NULL; 785 parent->node1->node2 = NULL; 786 787 788 parent->node2 = xmalloc(sizeof(struct window_split_node)); 789 *parent->node2 = *parent; 790 parent->node2->search = xmalloc(SEARCH_TERM_MAX_LEN); 791 parent->node2->parent = parent; 792 parent->node2->node1 = NULL; 793 parent->node2->node2 = NULL; 794 795 if (parent->mode == WINDOW_HORISONTAL) { 796 // NOTE: if the window resizing is changed, change in draw tree function as well 797 int middlex = ((float)(parent->maxx - parent->minx) * parent->ratio) + parent->minx; 798 parent->node1->minx = parent->minx; 799 parent->node1->miny = parent->miny; 800 parent->node1->maxx = middlex; 801 parent->node1->maxy = parent->maxy; 802 parent->node2->minx = middlex+2; 803 parent->node2->miny = parent->miny; 804 parent->node2->maxx = parent->maxx; 805 parent->node2->maxy = parent->maxy; 806 } else if (parent->mode == WINDOW_VERTICAL) { 807 // NOTE: if the window resizing is changed, change in draw tree function as well 808 int middley = ((float)(parent->maxy - parent->miny) * parent->ratio) + parent->miny; 809 parent->node1->minx = parent->minx; 810 parent->node1->miny = parent->miny; 811 parent->node1->maxx = parent->maxx; 812 parent->node1->maxy = middley; 813 parent->node2->minx = parent->miny; 814 parent->node2->miny = middley; 815 parent->node2->maxx = parent->maxx; 816 parent->node2->maxy = parent->maxy; 817 } 818 819 parent->mode = mode; 820 parent->ratio = ratio; 821 parent->wb = (struct window_buffer){0}; 822 } 823 824 struct window_split_node* 825 window_node_delete(struct window_split_node* node) 826 { 827 if (!node->parent) { 828 writef_to_status_bar("can't close root winodw"); 829 return node; 830 } 831 struct window_split_node* old = node; 832 node = node->parent; 833 struct window_split_node* other = (node->node1 == old) ? node->node2 : node->node1; 834 free(old->search); 835 free(old); 836 837 struct window_split_node* parent = node->parent; 838 *node = *other; 839 if (other->mode != WINDOW_SINGULAR) { 840 other->node1->parent = node; 841 other->node2->parent = node; 842 } 843 free(other); 844 node->parent = parent; 845 846 return node; 847 } 848 849 void 850 window_node_draw_tree_to_screen(struct window_split_node* root, int minx, int miny, int maxx, int maxy) 851 { 852 soft_assert(root, return;); 853 854 if (root->mode == WINDOW_SINGULAR) { 855 LIMIT(maxx, 0, screen.col-1); 856 LIMIT(maxy, 0, screen.row-1); 857 LIMIT(minx, 0, maxx); 858 LIMIT(miny, 0, maxy); 859 root->minx = minx; 860 root->miny = miny; 861 root->maxx = maxx; 862 root->maxy = maxy; 863 if (root->wb.mode != 0) { 864 int wn_custom_window_draw_callback_exists = 0; 865 extension_callback_exists(wn_custom_window_draw, wn_custom_window_draw_callback_exists = 1;); 866 soft_assert(wn_custom_window_draw_callback_exists, return;); 867 868 call_extension(wn_custom_window_draw, root); 869 870 return; 871 } else { 872 window_node_draw_to_screen(root); 873 } 874 } else if (root->mode == WINDOW_HORISONTAL) { 875 // NOTE: if the window resizing is changed, change in split function as well 876 int middlex = ((float)(maxx - minx) * root->ratio) + minx; 877 878 // print seperator 879 screen_set_region(middlex+1, miny, middlex+1, maxy, L'│'); 880 881 window_node_draw_tree_to_screen(root->node1, minx, miny, middlex, maxy); 882 window_node_draw_tree_to_screen(root->node2, middlex+2, miny, maxx, maxy); 883 884 for (int y = miny; y < maxy+1; y++) 885 xdrawline(middlex+1, y, middlex+2); 886 } else if (root->mode == WINDOW_VERTICAL) { 887 // NOTE: if the window resizing is changed, change in split function as well 888 int middley = ((float)(maxy - miny) * root->ratio) + miny; 889 890 window_node_draw_tree_to_screen(root->node1, minx, miny, maxx, middley); 891 window_node_draw_tree_to_screen(root->node2, minx, middley, maxx, maxy); 892 } 893 } 894 895 void 896 window_node_move_all_cursors_on_same_fb(struct window_split_node* root, struct window_split_node* excluded, int fb_index, int offset, 897 void(movement)(struct window_buffer*, int, enum cursor_reason), 898 int move, enum cursor_reason reason) 899 { 900 if (root->mode == WINDOW_SINGULAR) { 901 if (root->wb.fb_index == fb_index && root->wb.cursor_offset >= offset && root != excluded) 902 movement(&root->wb, move, reason); 903 } else { 904 window_node_move_all_cursors_on_same_fb(root->node1, excluded, fb_index, offset, movement, move, reason); 905 window_node_move_all_cursors_on_same_fb(root->node2, excluded, fb_index, offset, movement, move, reason); 906 } 907 } 908 909 void 910 window_node_move_all_yscrolls(struct window_split_node* root, struct window_split_node* excluded, int fb_index, int offset, int move) 911 { 912 if (root->mode == WINDOW_SINGULAR) { 913 if (root->wb.fb_index == fb_index && root->wb.cursor_offset >= offset && root != excluded) 914 root->wb.y_scroll += move; 915 } else { 916 window_node_move_all_yscrolls(root->node1, excluded, fb_index, offset, move); 917 window_node_move_all_yscrolls(root->node2, excluded, fb_index, offset, move); 918 } 919 } 920 921 int 922 window_other_nodes_contain_fb(struct window_split_node* node, struct window_split_node* root) 923 { 924 if (root->mode == WINDOW_SINGULAR) 925 return (root->wb.fb_index == node->wb.fb_index && root != node); 926 927 return (window_other_nodes_contain_fb(node, root->node1) || 928 window_other_nodes_contain_fb(node, root->node2)); 929 } 930 931 // TODO: create a distance type function and use that instead (from the current cursor position)? 932 // struct window_split_node* wincdow_closest(root, node ignore(get maxx/minx etc from this and make sure the it's outside the ignored node), enum move_directions move, int cx, int cy) 933 struct window_split_node* 934 window_switch_to_window(struct window_split_node* node, enum move_directons move) 935 { 936 soft_assert(node, return &root_node;); 937 if (!node->parent) return node; 938 soft_assert(node->mode == WINDOW_SINGULAR, 939 while(node->mode != WINDOW_SINGULAR) 940 node = node->node1; 941 return node; 942 ); 943 struct window_split_node* old_node = node; 944 945 if (move == MOVE_RIGHT || move == MOVE_DOWN) { 946 // traverse up the tree to the right 947 for (; node->parent; node = node->parent) { 948 if (is_correct_mode(node->parent->mode, move) && node->parent->node1 == node) { 949 // traverse down until a screen is found 950 node = node->parent->node2; 951 while(node->mode != WINDOW_SINGULAR) 952 node = node->node1; 953 954 return node; 955 } 956 } 957 } else if (move == MOVE_LEFT || move == MOVE_UP) { 958 // traverse up the tree to the left 959 for (; node->parent; node = node->parent) { 960 if (is_correct_mode(node->parent->mode, move) && node->parent->node2 == node) { 961 // traverse down until a screen is found 962 node = node->parent->node1; 963 while(node->mode != WINDOW_SINGULAR) 964 node = node->node2; 965 966 return node; 967 } 968 } 969 } 970 971 return old_node; 972 } 973 974 void 975 window_node_resize(struct window_split_node* node, enum move_directons move, float amount) 976 { 977 for (; node; node = node->parent) { 978 if (is_correct_mode(node->mode, move)) { 979 float amount = (move == MOVE_RIGHT || move == MOVE_LEFT) ? 0.1f : 0.05f; 980 if (move == MOVE_RIGHT || move == MOVE_DOWN) amount = -amount; 981 node->ratio -= amount; 982 LIMIT(node->ratio, 0.001f, 0.95f); 983 return; 984 } 985 } 986 } 987 988 void 989 window_node_resize_absolute(struct window_split_node* node, enum move_directons move, float amount) 990 { 991 for (; node; node = node->parent) { 992 if (is_correct_mode(node->mode, move)) { 993 node->ratio = amount; 994 LIMIT(node->ratio, 0.001f, 0.95f); 995 return; 996 } 997 } 998 }