1 module unde.file_manager.draw_path.draw_path; 2 3 import unde.global_state; 4 import unde.lib; 5 import unde.lsblk; 6 import unde.scan; 7 import unde.clickable; 8 import unde.file_manager.draw_path.draw_elements; 9 import unde.file_manager.change_rights; 10 import unde.path_mnt; 11 import unde.viewers.image_viewer.lib; 12 import unde.viewers.text_viewer.lib; 13 import unde.slash; 14 15 import berkeleydb.all; 16 import core.thread; 17 18 import derelict.sdl2.sdl; 19 20 import std..string; 21 import std.format; 22 import std.conv; 23 import std.stdio; 24 import std.datetime; 25 import std.regex; 26 import std.process; 27 import std.utf; 28 29 import core.stdc..string; 30 import core.stdc.errno; 31 32 import std.file; 33 import core.sys.posix.sys.stat; 34 35 version (Windows) 36 { 37 import unde.scan; 38 } 39 40 private void 41 calculate_parent_animation_info(GlobalState gs, ref RectSize rectsize, 42 SortType sort, 43 ref SDL_Rect sdl_rect, 44 ref bool calculated, 45 ref CoordinatesPlusScale screen) 46 { 47 sdl_rect = rectsize.rect(sort).to_screen(screen); 48 49 sdl_rect.x = sdl_rect.x+sdl_rect.w/3; 50 sdl_rect.y = sdl_rect.y+sdl_rect.h/3; 51 sdl_rect.w /= 3; 52 sdl_rect.h /= 3; 53 54 calculated = true; 55 } 56 57 private void 58 calculate_directory_animation_info(GlobalState gs, ref RectSize rectsize, 59 SortType sort, 60 ref SDL_Rect sdl_rect, 61 ref bool calculated, 62 ref CoordinatesPlusScale screen) 63 { 64 sdl_rect = rectsize.rect(sort).to_screen(screen); 65 calculated = true; 66 } 67 68 private void 69 calculate_animation_info(GlobalState gs, ref RectSize rectsize, 70 string path, 71 SortType sort) 72 { 73 foreach (ref animation_info; gs.animation_info) 74 { 75 if ( animation_info.parent == path && !animation_info.from_calculated && 76 (animation_info.type == NameType.CreateDirectory || 77 (animation_info.type == NameType.Copy || 78 animation_info.type == NameType.Move) && animation_info.stage == 1) ) 79 { 80 calculate_parent_animation_info(gs, rectsize, 81 sort, 82 animation_info.from, 83 animation_info.from_calculated, 84 gs.screen); 85 } 86 if ( animation_info.parent == path && !animation_info.to_calculated && 87 (animation_info.type == NameType.Copy || 88 animation_info.type == NameType.Move) && 89 animation_info.stage == 0 ) 90 { 91 calculate_parent_animation_info(gs, rectsize, 92 sort, 93 animation_info.to, 94 animation_info.to_calculated, 95 gs.screen); 96 } 97 } 98 99 if ( path in gs.animation_info && 100 !gs.animation_info[path].to_calculated && 101 (gs.animation_info[path].type == NameType.CreateDirectory || 102 (gs.animation_info[path].type == NameType.Copy || 103 gs.animation_info[path].type == NameType.Move) && 104 gs.animation_info[path].stage == 1) ) 105 { 106 calculate_directory_animation_info(gs, rectsize, 107 sort, 108 gs.animation_info[path].to, 109 gs.animation_info[path].to_calculated, 110 gs.screen); 111 } 112 113 if ( path in gs.animation_info && 114 !gs.animation_info[path].from_calculated && 115 (gs.animation_info[path].type == NameType.Copy || 116 gs.animation_info[path].type == NameType.Move) && 117 gs.animation_info[path].stage == 0 ) 118 { 119 calculate_directory_animation_info(gs, rectsize, 120 sort, 121 gs.animation_info[path].from, 122 gs.animation_info[path].from_calculated, 123 gs.screen); 124 } 125 } 126 127 private string 128 size_to_string(ulong size) 129 { 130 string prefix; 131 double s; 132 if (size <= 1024UL) 133 { 134 prefix = ""; 135 s = cast(double)size; 136 } 137 else if (size <= 1024UL*1024) 138 { 139 prefix = "Ki"; 140 s = cast(double)size/1024.0; 141 } 142 else if (size <= 1024UL*1024*1024) 143 { 144 prefix = "Mi"; 145 s = cast(double)size/(1024.0*1024.0); 146 } 147 else if (size <= 1024UL*1024*1024*1024) 148 { 149 prefix = "Gi"; 150 s = cast(double)size/(1024.0*1024.0*1024.0); 151 } 152 else //if (size <= 1024UL*1024*1024*1024*1024) 153 { 154 prefix = "Ti"; 155 s = cast(double)size/(1024.0*1024.0*1024.0*1024.0); 156 } 157 158 return format("%.3f %sb", s, prefix); 159 } 160 161 private int 162 calc_selected_and_draw_blue_rect(GlobalState gs, 163 in string path, in ref SDL_Rect rect) 164 { 165 int selected = 0; 166 foreach(sel_path; gs.selection_hash.byKey()) 167 { 168 if (sel_path > path && sel_path.startsWith(path)) 169 selected++; 170 } 171 172 if (selected > 0) 173 { 174 SDL_Rect dup_rect = rect; 175 unDE_RenderFillRect(gs.renderer, &dup_rect, 0x808080FF); 176 } 177 178 return selected; 179 } 180 181 private int 182 draw_fs_info(GlobalState gs, 183 in ref SDL_Rect_With_Not_Visible_And_No_Draw rnvnd, 184 in PathMnt path, 185 in ref LsblkInfo info, 186 bool not_scanned, 187 SortType sort, 188 int line = 0) 189 { 190 SDL_Rect rect = rnvnd.sdl_rect; 191 192 rect.x = rect.x+rect.w/3; 193 rect.y = rect.y+rect.h/3; 194 rect.w /= 3; 195 rect.h /= 3; 196 197 draw_line(gs, format("Name: %s", info.name), 198 0, (line++)*36, 12, rect); 199 draw_line(gs, format("Mountpoint: %s", info.mountpoint), 200 0, (line++)*36, 12, rect); 201 draw_line(gs, format("Filesystem: %s", info.fstype), 202 0, (line++)*36, 12, rect); 203 draw_line(gs, format("Label: %s", info.label), 204 0, (line++)*36, 12, rect); 205 draw_line(gs, format("UUID: %s", info.uuid), 206 0, (line++)*36, 12, rect); 207 line++; 208 draw_line(gs, format("Size: %s", size_to_string(info.size)), 209 0, (line++)*36, 12, rect); 210 draw_line(gs, format("Used: %s", size_to_string(info.used)), 211 0, (line++)*36, 12, rect); 212 draw_line(gs, format("Available: %s", size_to_string(info.avail)), 213 0, (line++)*36, 12, rect); 214 line++; 215 216 if (not_scanned) 217 { 218 if (path !in gs.interface_flags) 219 gs.interface_flags[path] = false; 220 221 draw_line(gs, (gs.interface_flags[path]?"☑":"☐") ~ " Scan all other file systems recursively", 222 0, (line)*36, 12, rect); 223 224 auto tt = gs.text_viewer.font.get_char_from_cache( 225 "☐", 12, SDL_Color(0xFF, 0xFF, 0xFF, 0xFF)); 226 227 SDL_Rect dst = SDL_Rect(rect.x, rect.y + (line*36) * rect.h/1024, 228 cast(int)(tt.w*rect.w/1024.0/2), cast(int)(tt.h*rect.h/1024.0/2)); 229 230 void checkbox_clicked(GlobalState gs, int stage) 231 { 232 gs.interface_flags[path] = !gs.interface_flags[path]; 233 gs.dirty = true; 234 } 235 236 gs.new_clickable_list.insertFront(new Clickable(gs, dst, &checkbox_clicked)); 237 238 line++; 239 dst = draw_button(gs, 0, ((line)*36-4), 5*32, 42, rect); 240 241 void button_clicked(GlobalState gs, int stage) 242 { 243 start_scan(gs, path); 244 } 245 246 gs.new_clickable_list.insertFront(new Clickable(gs, dst, &button_clicked)); 247 draw_line(gs, " Scan", 0, (line++)*36, 12, rect); 248 } 249 250 return line; 251 } 252 253 private void 254 draw_de_info(GlobalState gs, 255 in ref SDL_Rect_With_Not_Visible_And_No_Draw rnvnd, 256 in PathMnt path, 257 in RectSize rectsize, 258 DirEntry de) 259 { 260 SDL_Rect rect = rnvnd.sdl_rect; 261 262 rect.x = rect.x+rect.w/3; 263 rect.y = rect.y+rect.h/3; 264 rect.w /= 3; 265 rect.h /= 3; 266 267 if (rect.w < 256) return; 268 269 int selected = calc_selected_and_draw_blue_rect(gs, path, rect); 270 271 if (rectsize.show_info == InfoType.Progress) 272 unDE_RenderFillRect(gs.renderer, &rect, 0x8080FF80); 273 else if ( rectsize.msg[0] != char.init && (rectsize.msg_time == 0 || rectsize.msg_time > gs.msg_stamp) ) 274 unDE_RenderFillRect(gs.renderer, &rect, rectsize.msg_color); 275 276 int line; 277 if (path in gs.lsblk) 278 { 279 line = draw_fs_info(gs, rnvnd, path, gs.lsblk[path], false, rectsize.sort, line); 280 } 281 282 if (!de.isSymlink && de.isDir) 283 { 284 string name = path[path.lastIndexOf(SL)+1..$]; 285 if (path == SL) 286 name = SL; 287 288 draw_line(gs, format("Name: %s", name), 289 0, (line++)*36, 12, rect); 290 draw_line(gs, format("Sorted %s", rectsize.sort), 291 0, (line++)*36, 12, rect); 292 draw_line(gs, format("Files: %s", rectsize.files), 293 0, (line++)*36, 12, rect); 294 line++; 295 } 296 if (!de.isSymlink) 297 { 298 if ( rectsize.size < 0 ) 299 { 300 draw_line(gs, format("Scanning..."), 301 0, (line++)*36, 12, rect); 302 } 303 else 304 { 305 draw_line(gs, format("Size: %s", size_to_string(rectsize.size)), 306 0, (line++)*36, 12, rect); 307 draw_line(gs, format("Disk Usage: %s", size_to_string(rectsize.disk_usage)), 308 0, (line++)*36, 12, rect); 309 version(DigitalMars) 310 { 311 auto time = SysTime.fromUnixTime(rectsize.mtime); 312 draw_line(gs, format("Mod. time: %s", time.toISOExtString()), 313 0, (line++)*36, 12, rect); 314 } 315 line++; 316 version (Posix) 317 { 318 string access = mode_to_string(de.statBuf.st_mode); 319 } 320 else version (Windows) 321 { 322 string access = "N/A"; 323 } 324 auto access_rect = draw_line(gs, format("Access: "), 325 0, (line)*36, 12, rect); 326 327 foreach (int i, a; access) 328 { 329 auto tt = gs.text_viewer.font.get_char_from_cache( 330 ""~a, 12, SDL_Color(0xFF, 0xFF, 0xFF, 0xFF)); 331 332 SDL_Rect dst = SDL_Rect(access_rect.x + access_rect.w, 333 rect.y + (line*36) * rect.h/1024, 334 cast(int)(tt.w*rect.w/1024.0/2), 335 cast(int)(tt.h*rect.h/1024.0/2)); 336 access_rect.x += dst.w; 337 338 auto r = SDL_RenderCopy(gs.renderer, tt.texture, 339 null, &dst); 340 if (r < 0) 341 { 342 writefln( "Error while render copy (access symbol): %s", 343 SDL_GetError().to!string() ); 344 } 345 346 auto get_access_clicked(int i) 347 { 348 void access_clicked(GlobalState gs, int stage) 349 { 350 version(Posix) 351 { 352 mode_t new_mode = de.statBuf.st_mode ^ (1 << (11-i)); 353 354 DirOrFile dof = DirOrFile.All; 355 if (i == 5 || i == 8 || i == 11) 356 { 357 if (!de.isSymlink && de.isDir) 358 dof = DirOrFile.Dir; 359 else 360 dof = DirOrFile.File; 361 } 362 363 bool set = cast(bool)(new_mode & (1 << (11-i))); 364 365 string[] selection = gs.selection_hash.keys; 366 int res = change_rights(gs, selection.idup(), set, i, dof, gs.interface_flags[path]); 367 368 if (res == 0) 369 { 370 res = chmod(path.toStringz, new_mode); 371 if (res < 0) 372 { 373 string msg = format("Chmod error: %s", fromStringz(strerror(errno)).idup()); 374 gs.messages ~= ConsoleMessage( 375 SDL_Color(0xFF, 0x00, 0x00, 0xFF), 376 msg, 377 SDL_GetTicks() 378 ); 379 writeln(msg); 380 } 381 } 382 gs.dirty = true; 383 } 384 } 385 return &access_clicked; 386 } 387 388 gs.new_clickable_list.insertFront(new Clickable(gs, dst, get_access_clicked(i))); 389 } 390 line++; 391 392 version(Posix) 393 { 394 draw_line(gs, format("User: %s", uid_to_name(de.statBuf.st_uid)), 395 0, (line++)*36, 12, rect); 396 draw_line(gs, format("Group: %s", gid_to_name(de.statBuf.st_gid)), 397 0, (line++)*36, 12, rect); 398 } 399 400 if (path !in gs.interface_flags) 401 gs.interface_flags[path] = false; 402 403 auto tt = gs.text_viewer.font.get_char_from_cache( 404 "☐", 12, SDL_Color(0xFF, 0xFF, 0xFF, 0xFF)); 405 406 SDL_Rect dst = SDL_Rect(rect.x, rect.y + (line*36) * rect.h/1024, 407 cast(int)(tt.w*rect.w/1024.0/2), cast(int)(tt.h*rect.h/1024.0/2)); 408 409 draw_line(gs, format((gs.interface_flags[path]?"☑":"☐")~" Recursively change rights"), 410 0, (line++)*36, 12, rect); 411 412 void checkbox_clicked(GlobalState gs, int stage) 413 { 414 gs.interface_flags[path] = !gs.interface_flags[path]; 415 gs.dirty = true; 416 } 417 418 gs.new_clickable_list.insertFront(new Clickable(gs, dst, &checkbox_clicked)); 419 } 420 } 421 line++; 422 if (selected > 0) 423 draw_line(gs, format("Selected %d items", selected), 424 0, (line++)*36, 12, rect); 425 line++; 426 427 if (rectsize.show_info == InfoType.Progress) 428 { 429 draw_line(gs, from_char_array(rectsize.path), 430 0, (line++)*36, 12, rect); 431 432 draw_line(gs, format("Progress: %.2f %%", cast(double)rectsize.progress/100), 433 0, (line++)*36, 12, rect); 434 435 long estimate_time = rectsize.estimate_end - Clock.currTime().toUnixTime(); 436 long secs = estimate_time%60; 437 long mins = estimate_time/60%60; 438 long hours = estimate_time/3600; 439 440 draw_line(gs, format("Estimate Time: %d:%02d:%02d", hours, mins, secs), 441 0, (line++)*36, 12, rect); 442 } 443 else if (rectsize.msg[0] != char.init && (rectsize.msg_time == 0 || rectsize.msg_time > gs.msg_stamp) ) 444 { 445 draw_line(gs, from_char_array(rectsize.msg), 446 0, (line++)*36, 12, rect); 447 } 448 } 449 450 private void 451 draw_enter_name(GlobalState gs, 452 in ref SDL_Rect_With_Not_Visible_And_No_Draw rnvnd, 453 in string path0, 454 in RectSize rectsize) 455 { 456 SDL_Rect rect = rnvnd.sdl_rect; 457 458 rect.x = rect.x+rect.w/3; 459 rect.y = rect.y+rect.h/3; 460 rect.w /= 3; 461 rect.h /= 3; 462 463 if (rect.w < 256) return; 464 465 unDE_RenderFillRect(gs.renderer, &rect, 0x80FFFFFF); 466 467 if (gs.current_path !in gs.enter_names) 468 { 469 switch (rectsize.show_info) 470 { 471 case InfoType.CreateDirectory: 472 gs.enter_names[gs.current_path] = EnterName(NameType.CreateDirectory, "", 0); 473 break; 474 case InfoType.Copy: 475 gs.enter_names[gs.current_path] = EnterName(NameType.Copy, "", 0); 476 break; 477 case InfoType.Move: 478 gs.enter_names[gs.current_path] = EnterName(NameType.Move, "", 0); 479 break; 480 default: 481 assert(false); 482 } 483 writefln("Not in enter_names %s", gs.current_path); 484 } 485 486 with(gs.enter_names[gs.current_path]) 487 { 488 draw_direntry_name(gs, name[0..pos]~"|"~name[pos..$], rnvnd, true); 489 } 490 } 491 492 private void 493 draw_link_info(GlobalState gs, 494 in ref SDL_Rect_With_Not_Visible_And_No_Draw rnvnd, 495 in string path) 496 { 497 version(Posix) 498 { 499 SDL_Rect rect = rnvnd.sdl_rect; 500 501 /*rect.x = rect.x+rect.w/3; 502 rect.y = rect.y+rect.h/3; 503 rect.w /= 3; 504 rect.h /= 3;*/ 505 506 if (rect.w < 256) return; 507 508 string name = path[path.lastIndexOf(SL)+1..$]; 509 if (path == SL) 510 name = SL; 511 512 int line; 513 draw_line(gs, format("Name: %s", name), 514 0, (line++)*36, 12, rect); 515 draw_line(gs, format("Link to: %s", readLink(path)), 516 0, (line++)*36, 12, rect); 517 } 518 } 519 520 private bool 521 isSelected(GlobalState gs, in ref RectSize rectsize, 522 in string path, SortType sort) 523 { 524 525 bool selected = false; 526 bool full_selected = false; 527 528 foreach(i, sel; gs.selection_list) 529 { 530 string parent = getParent(path); 531 string par_from = getParent(sel.from); 532 string par_to = getParent(sel.to); 533 534 if (parent == par_from && parent == par_to) 535 { 536 if (sel.sort == SortType.BySize) 537 { 538 if (rectsize.disk_usage <= sel.size_from && 539 rectsize.disk_usage >= sel.size_to && 540 (rectsize.disk_usage == sel.size_from ? 541 path >= sel.from : 1) && 542 (rectsize.disk_usage == sel.size_to ? 543 path <= sel.to : 1)) 544 { 545 if (i < gs.selection_lsof) 546 selected = !selected; 547 full_selected = !full_selected; 548 } 549 } 550 else if (sel.sort == SortType.ByTime) 551 { 552 if (rectsize.mtime <= sel.mtime_from && 553 rectsize.mtime >= sel.mtime_to && 554 (rectsize.mtime == sel.mtime_from ? 555 path >= sel.from : 1) && 556 (rectsize.mtime == sel.mtime_to ? 557 path <= sel.to : 1)) 558 { 559 if (i < gs.selection_lsof) 560 selected = !selected; 561 full_selected = !full_selected; 562 } 563 } 564 else 565 { 566 if (path >= sel.from && path <= sel.to ) 567 { 568 if (i < gs.selection_lsof) 569 selected = !selected; 570 full_selected = !full_selected; 571 } 572 } 573 } 574 } 575 576 if (gs.selection_finish == 2 && selected) 577 { 578 if (path in gs.selection_hash) 579 gs.selection_hash.remove(path); 580 else 581 /* EN: FIXME: THe position of selected item can be changed 582 by changing sort ype 583 RU: ИСПРАВЬ_МЕНЯ: позиция выбранного элемента может 584 изменится при изменении порядка сортировки */ 585 gs.selection_hash[path] = rectsize.rect(sort); 586 587 full_selected = !full_selected; 588 gs.dirty = true; 589 } 590 591 592 return full_selected; 593 } 594 595 version(Windows) 596 { 597 int 598 draw_my_computer(GlobalState gs, DbTxn txn, 599 PathMnt path, 600 ref CoordinatesPlusScale surf, 601 DRect apply_rect, 602 SortType sort = SortType.ByName, 603 bool current = false, 604 bool fast = false, int level = 0) 605 { 606 RectSize rectsize = RectSize(apply_rect, apply_rect, apply_rect); 607 DRect full_rect = DRect(0, 0, 1024*1024, 1024*1024); 608 609 int ret = 0; 610 611 string[] paths; 612 613 foreach(char c; 'A'..'Z'+1) 614 { 615 string disk = c ~ ":"; 616 if ( disk in gs.lsblk ) 617 { 618 paths ~= disk; 619 } 620 } 621 622 int levels = 0; 623 long l = paths.length; 624 /*if (l > 0) 625 { 626 l--; 627 }*/ 628 for (long i=12; i < l; i=i*2+12) 629 { 630 l -= i; 631 levels++; 632 } 633 if (l > 0) levels++; 634 635 immutable long entries_on_last_level = l; 636 637 //RectSize[string] rect_sizes; 638 639 //writefln("levels = %s", levels); 640 long[] coords = new long[levels+1]; 641 RectSize full_size = RectSize(drect_zero, drect_zero, drect_zero, 642 0, 0, 643 0, 0, 0); 644 long i = 0; 645 size_t lev = 1; 646 bool first = true; 647 foreach (string name; paths) 648 { 649 DRect rect; 650 calculate_rect(full_rect, rect, coords, lev); 651 652 auto res = draw_path(gs, txn, path.next(name), 653 surf, rect, 654 rectsize.sort, 655 false, 656 ret < 0, level+1); 657 if (res == -1) 658 { 659 fast = true; 660 ret = -1; 661 } 662 663 //full_size.size += rect_size.size; 664 //full_size.disk_usage += rect_size.disk_usage; 665 //full_size.files += rect_size.files; 666 667 //rect_sizes[name] = rect_size; 668 669 calculate_coords(coords, lev, i, levels, entries_on_last_level); 670 first = false; 671 } 672 673 bool selected = isSelected(gs, rectsize, path, sort); 674 auto rnvnd = draw_rect_with_color_by_size( 675 gs, rectsize, sort, surf, 676 (cast(bool)(path in gs.selection_hash)) ^ selected, 677 path); 678 679 draw_fs_info(gs, rnvnd, path, gs.lsblk[path], false, SortType.ByName); 680 681 draw_direntry_name(gs, gs.lsblk[path].name, rnvnd); 682 683 return ret; 684 } 685 686 } 687 688 int 689 draw_path(GlobalState gs, DbTxn txn, 690 PathMnt path, 691 ref CoordinatesPlusScale surf, 692 DRect apply_rect, 693 SortType sort = SortType.ByName, 694 bool current = false, 695 bool fast = false, int level = 0) 696 { 697 Fiber.yield(); 698 699 int ret = 0; 700 if (level == 0) 701 { 702 clear_image_cache(gs); 703 } 704 705 DirEntry de; 706 bool no_de = false; 707 try 708 { 709 de = DirEntry(path); 710 } 711 catch (FileException e) 712 { 713 no_de = true; 714 } 715 716 bool pmnt; 717 Dbt key, data; 718 if (path in gs.lsblk) 719 { 720 check_if_fully_scanned(gs, path); 721 722 string path0 = path.get_key(gs.lsblk); 723 key = path0; 724 auto res = gs.db_map.get(txn, &key, &data); 725 if (res == 0) 726 { 727 RectSize rectsize; 728 rectsize = data.to!(RectSize); 729 apply_rect = rectsize.rect(sort).apply(apply_rect); 730 } 731 else 732 { 733 // TODO: Created mount point, rescan up path 734 } 735 736 pmnt = true; 737 } 738 739 try 740 { 741 path.update(gs.lsblk); 742 } 743 catch (Exception e) 744 { 745 return 0; 746 } 747 748 version(Windows) 749 { 750 if (path._next == SL) 751 { 752 return draw_my_computer(gs, txn, 753 path, surf, apply_rect, sort, current, fast, level); 754 } 755 } 756 757 string path0 = path.get_key(gs.lsblk); 758 key = path0; 759 //writefln("GET %s", path0.replace("\0", SL)); 760 auto res = gs.db_map.get(txn, &key, &data); 761 762 if (res == 0) 763 { 764 RectSize rectsize; 765 RectSize origrectsize; 766 origrectsize = rectsize = data.to!(RectSize); 767 rectsize.rect(sort) = rectsize.rect(sort).apply(apply_rect); 768 //writefln("path=%s, rect=%s", path, rectsize.rect(sort)); 769 //writefln("rectsize=%s", rectsize); 770 771 bool selected = isSelected(gs, rectsize, path, sort); 772 auto rnvnd = draw_rect_with_color_by_size( 773 gs, rectsize, sort, surf, 774 (cast(bool)(path in gs.selection_hash)) ^ selected, 775 path); 776 777 if (path in gs.selection_sub) 778 { 779 draw_center_rect_with_color( 780 gs, rnvnd, 0x8080FFFF); 781 } 782 783 try{ 784 if (no_de) 785 { 786 draw_center_rect_with_color( 787 gs, rnvnd, 0x808080FF); 788 } 789 else if (!de.isSymlink && de.isDir && rectsize.show_info == InfoType.Progress) 790 { 791 draw_center_rect_with_color( 792 gs, rnvnd, 0x8080FF80); 793 } 794 else if (!de.isSymlink && de.isDir && rectsize.newest_msg_time > gs.msg_stamp || 795 rectsize.msg[0] != char.init && rectsize.msg_time > gs.msg_stamp) 796 { 797 draw_center_rect_with_color( 798 gs, rnvnd, 0x80FF8080); 799 } 800 } 801 catch (Exception e) 802 { 803 return ret; 804 } 805 806 calculate_animation_info(gs, rectsize, path, sort); 807 808 void direntry_selected(GlobalState gs, int stage) 809 { 810 if (stage == 0) 811 { 812 Selection sel; 813 sel.from = path; 814 sel.to = path; 815 sel.sort = sort; 816 if (sort == SortType.BySize) 817 { 818 sel.size_from = rectsize.disk_usage; 819 sel.size_to = rectsize.disk_usage; 820 } 821 else if (sort == SortType.ByTime) 822 { 823 sel.mtime_from = rectsize.mtime; 824 sel.mtime_to = rectsize.mtime; 825 } 826 gs.selection_list ~= sel; 827 //writefln("%s Selection %s", stage, sel); 828 } 829 else if ( (stage == 1 || stage == 2) && 830 gs.selection_list.length > 0) 831 { 832 Selection* sel = &gs.selection_list[$-1]; 833 834 sel.to = path; 835 if (sel.sort == SortType.BySize) 836 sel.size_to = rectsize.disk_usage; 837 else if (sel.sort == SortType.ByTime) 838 sel.mtime_to = rectsize.mtime; 839 840 gs.redraw_fast = true; 841 //writefln("%s Selection %s", stage, *sel); 842 } 843 844 if ( stage == 2) 845 { 846 if (gs.selection_finish == 0) 847 gs.selection_finish++; 848 gs.redraw_fast = false; 849 } 850 851 gs.selection_stage = stage; 852 gs.dirty = true; 853 } 854 855 void run_viewer(GlobalState gs, int stage) 856 { 857 openFile(gs, path); 858 } 859 860 void switch_info(GlobalState gs, int stage) 861 { 862 if (origrectsize.show_info == InfoType.None) 863 origrectsize.show_info = InfoType.FileInfo; 864 else if (origrectsize.show_info == InfoType.FileInfo) 865 origrectsize.show_info = InfoType.None; 866 867 key = path0; 868 data = origrectsize; 869 auto res = gs.db_map.put(txn, &key, &data); 870 if (res != 0) 871 throw new Exception("Path info to map-db not written"); 872 } 873 874 void run_mime(GlobalState gs, int stage) 875 { 876 openFileByMime(gs, path); 877 } 878 879 if (rnvnd.current_path) 880 { 881 //writefln("current_path = %s", path0.replace("\0",SL)); 882 gs.current_path = path0; 883 gs.full_current_path = path; 884 current = false; 885 } 886 887 if (current || rnvnd.current_path && (!no_de && !de.isSymlink && !de.isDir)) 888 { 889 gs.new_double_clickable_list.insertFront(new Clickable(gs, rnvnd.sdl_rect, &run_viewer)); 890 gs.new_right_clickable_list.insertFront(new Clickable(gs, rnvnd.sdl_rect, &direntry_selected)); 891 } 892 893 if ((current || rnvnd.current_path) && (!no_de && !de.isDir)) 894 { 895 gs.new_double_right_clickable_list.insertFront(new Clickable(gs, rnvnd.sdl_rect, &switch_info)); 896 } 897 898 if ((current || rnvnd.current_path) && (!no_de && !de.isSymlink && !de.isDir)) 899 { 900 gs.new_middle_clickable_list.insertFront(new Clickable(gs, rnvnd.sdl_rect, &run_mime)); 901 } 902 903 if (rnvnd.no_draw) 904 { 905 if (rectsize.show_info == InfoType.CreateDirectory || 906 rectsize.show_info == InfoType.Copy || 907 rectsize.show_info == InfoType.Move) 908 { 909 draw_enter_name(gs, rnvnd, path0, rectsize); 910 } 911 else if (!no_de && !de.isSymlink && de.isDir) 912 { 913 draw_de_info(gs, rnvnd, path, rectsize, de); 914 } 915 } 916 917 /* EN: Only if it is not symbolic link and not too small rectangle 918 draw directory recursively 919 RU: Если только это не символическая ссылка или не слишком мелкий 920 прямоугольник, рисовать директорию рекурсивно */ 921 if (!no_de && de.isSymlink) 922 { 923 version(Posix) 924 { 925 draw_link_info(gs, rnvnd, path); 926 927 string link_to = readLink(path); 928 try 929 { 930 if (link_to > "" && (gs.redraw_fast ? level < 2 : level < 3)) 931 { 932 if (link_to[0] != SL[0]) link_to = getParent(path)~SL~link_to; 933 if (link_to[$-1] == SL[0]) link_to = link_to[0..$-1]; 934 935 link_to = replaceAll(link_to, regex(`/\./`), SL); 936 link_to = replaceAll(link_to, regex(`/\.$`), ""); 937 auto re = regex(`/[^/]*/\.\.`); 938 string old_link_to; 939 do 940 { 941 old_link_to = link_to; 942 link_to = replaceAll(link_to, re, ""); 943 } 944 while (old_link_to !is link_to); 945 DRect linkrect = rectsize.rect(sort); 946 if (link_to == "") link_to = SL; 947 948 res = draw_path(gs, txn, PathMnt(gs.lsblk, link_to), 949 surf, linkrect, 950 rectsize.sort, 951 rnvnd.current_path, 952 ret < 0, level+1); 953 } 954 } catch (UTFException e) 955 { 956 writefln("UTFException: %s", link_to); 957 } 958 } 959 } 960 else if (!no_de && de.isDir && 961 (rnvnd.sdl_rect.w >= 4 || rnvnd.sdl_rect.h >= 4) && 962 !rnvnd.not_visible) 963 { 964 string[] paths; 965 966 if (gs.redraw_fast ? level < 2 : level < 3) 967 { 968 try{ 969 foreach (string name; dirEntries(path, SpanMode.shallow)) 970 { 971 res = draw_path(gs, txn, path.next(name), 972 surf, rectsize.rect(sort), 973 rectsize.sort, 974 rnvnd.current_path, 975 ret < 0, level+1); 976 if (res == -1) 977 { 978 fast = true; 979 ret = -1; 980 } 981 else if (res == -2 && rectsize.size >= 0) 982 { 983 rescan_path(gs, path); 984 } 985 } 986 } catch (FileException e) 987 { 988 } 989 } 990 } 991 992 if (!no_de && !de.isSymlink && de.isFile && 993 rnvnd.sdl_rect.w > 256 && !rnvnd.not_visible ) 994 { 995 if (rectsize.show_info == InfoType.FileInfo || 996 rectsize.show_info == InfoType.Progress) 997 { 998 draw_de_info(gs, rnvnd, path, rectsize, de); 999 } 1000 else 1001 { 1002 bool is_picture = draw_picture(gs, rnvnd, path, fast, ret); 1003 if (!is_picture) 1004 draw_text_file(gs, rnvnd, path, ret); 1005 } 1006 } 1007 1008 string name = path[path.lastIndexOf(SL)+1..$]; 1009 if (path == SL) 1010 name = SL; 1011 draw_direntry_name(gs, name, rnvnd); 1012 } 1013 else if (pmnt) 1014 { 1015 RectSize rectsize; 1016 rectsize.rect(sort) = apply_rect; 1017 rectsize.size = -1; 1018 bool selected = isSelected(gs, rectsize, path, sort); 1019 auto rnvnd = draw_rect_with_color_by_size( 1020 gs, rectsize, sort, surf, 1021 (cast(bool)(path in gs.selection_hash)) ^ selected, 1022 path); 1023 1024 draw_fs_info(gs, rnvnd, path, gs.lsblk[path], true, SortType.ByName); 1025 1026 string name = path[path.lastIndexOf(SL)+1..$]; 1027 if (path == SL) 1028 name = SL; 1029 draw_direntry_name(gs, name, rnvnd); 1030 } 1031 else 1032 { 1033 if (false/*gs.copiers.length > 0 || gs.movers.length > 0*/) 1034 { 1035 // Nothing to do 1036 } 1037 else 1038 { 1039 //writefln("Can't find %s", path0.replace("\0", SL)); 1040 ret = -2; 1041 } 1042 } 1043 1044 return ret; 1045 }