clean this up jesus
- SHA
e7598ccb53918c03fd8a3d092ea8ec67d6e7c524- Parents
-
07219b0 - Tree
0b81b1c
e7598cc
e7598ccb53918c03fd8a3d092ea8ec67d6e7c52407219b0
0b81b1c| Status | File | + | - |
|---|---|---|---|
| A |
.gitignore
|
1 | 0 |
| A |
run.sh
|
34 | 0 |
| D |
src/fortress_clean.f90
|
0 | 278 |
| D |
src/fortress_v2.f90
|
0 | 289 |
| M |
src/main.f90
|
247 | 114 |
| D |
src/simple_main.f90
|
0 | 119 |
| D |
src/test_right_pane.f90
|
0 | 31 |
.gitignoreadded@@ -0,0 +1,1 @@ | |||
| 1 | +tests/ | ||
run.shadded@@ -0,0 +1,34 @@ | |||
| 1 | +#!/bin/bash | ||
| 2 | + | ||
| 3 | +echo "========================================" | ||
| 4 | +echo " FORTRESS File Manager " | ||
| 5 | +echo " Written in Modern Fortran " | ||
| 6 | +echo "========================================" | ||
| 7 | +echo | ||
| 8 | +echo "Features:" | ||
| 9 | +echo " • Dual-pane interface (parent | current)" | ||
| 10 | +echo " • Full keyboard navigation" | ||
| 11 | +echo " • Directory traversal" | ||
| 12 | +echo " • Clean, responsive display" | ||
| 13 | +echo | ||
| 14 | +echo "Controls:" | ||
| 15 | +echo " ↑/↓ or j/k : Navigate files" | ||
| 16 | +echo " → or l : Enter directory" | ||
| 17 | +echo " ← or h : Go to parent" | ||
| 18 | +echo " q : Quit" | ||
| 19 | +echo | ||
| 20 | +echo "Building FORTRESS..." | ||
| 21 | +fpm build --flag "-O2" 2>&1 | grep -E "(done|error|Error|ERROR)" || true | ||
| 22 | + | ||
| 23 | +# Find the built executable | ||
| 24 | +FORTRESS_BIN=$(find ./build -name fortress -type f -path "*/app/*" 2>/dev/null | head -1) | ||
| 25 | + | ||
| 26 | +if [ -n "$FORTRESS_BIN" ] && [ -f "$FORTRESS_BIN" ]; then | ||
| 27 | + echo | ||
| 28 | + echo "Starting FORTRESS..." | ||
| 29 | + echo "====================" | ||
| 30 | + $FORTRESS_BIN | ||
| 31 | +else | ||
| 32 | + echo "Build failed. Please run 'fpm build' to see full error messages." | ||
| 33 | + exit 1 | ||
| 34 | +fi | ||
src/fortress_clean.f90deleted@@ -1,278 +0,0 @@ | |||
| 1 | -program fortress_clean | ||
| 2 | - use iso_fortran_env, only: output_unit, error_unit | ||
| 3 | - implicit none | ||
| 4 | - | ||
| 5 | - ! Constants | ||
| 6 | - integer, parameter :: MAX_PATH = 512 | ||
| 7 | - integer, parameter :: MAX_FILES = 500 | ||
| 8 | - character(len=*), parameter :: ESC = char(27) | ||
| 9 | - character(len=*), parameter :: CLEAR = ESC // "[2J" // ESC // "[H" | ||
| 10 | - character(len=*), parameter :: BOLD = ESC // "[1m" | ||
| 11 | - character(len=*), parameter :: DIM = ESC // "[2m" | ||
| 12 | - character(len=*), parameter :: REVERSE = ESC // "[7m" | ||
| 13 | - character(len=*), parameter :: RESET = ESC // "[0m" | ||
| 14 | - | ||
| 15 | - ! Variables | ||
| 16 | - character(len=MAX_PATH) :: current_dir, parent_dir, temp_dir | ||
| 17 | - character(len=MAX_PATH), dimension(MAX_FILES) :: current_files, parent_files | ||
| 18 | - logical, dimension(MAX_FILES) :: current_is_dir, parent_is_dir | ||
| 19 | - integer :: current_count, parent_count | ||
| 20 | - integer :: selected = 1 | ||
| 21 | - integer :: parent_selected = -1 | ||
| 22 | - character(len=1) :: key | ||
| 23 | - logical :: running = .true. | ||
| 24 | - integer :: i, rows, cols | ||
| 25 | - | ||
| 26 | - ! Initialize | ||
| 27 | - current_dir = get_pwd() | ||
| 28 | - parent_dir = get_parent_path(current_dir) | ||
| 29 | - | ||
| 30 | - ! Setup terminal | ||
| 31 | - call execute_command_line("stty -icanon -echo min 1 time 0 2>/dev/null") | ||
| 32 | - | ||
| 33 | - ! Main loop | ||
| 34 | - do while (running) | ||
| 35 | - ! Get files | ||
| 36 | - call get_file_list(current_dir, current_files, current_is_dir, current_count) | ||
| 37 | - call get_file_list(parent_dir, parent_files, parent_is_dir, parent_count) | ||
| 38 | - | ||
| 39 | - ! Find current dir in parent | ||
| 40 | - parent_selected = find_in_parent(current_dir, parent_files, parent_count) | ||
| 41 | - | ||
| 42 | - ! Get terminal size | ||
| 43 | - call get_term_size(rows, cols) | ||
| 44 | - | ||
| 45 | - ! Draw interface | ||
| 46 | - write(output_unit, '(a)', advance='no') CLEAR | ||
| 47 | - call draw_interface(rows, cols) | ||
| 48 | - | ||
| 49 | - ! Get input | ||
| 50 | - read(*, '(a1)', advance='no') key | ||
| 51 | - | ||
| 52 | - ! Handle input | ||
| 53 | - select case(ichar(key)) | ||
| 54 | - case(27) ! ESC sequence | ||
| 55 | - call read_arrow_key(key) | ||
| 56 | - select case(key) | ||
| 57 | - case('A') ! Up | ||
| 58 | - if (selected > 1) selected = selected - 1 | ||
| 59 | - case('B') ! Down | ||
| 60 | - if (selected < current_count) selected = selected + 1 | ||
| 61 | - case('C') ! Right - enter | ||
| 62 | - if (current_is_dir(selected)) then | ||
| 63 | - if (trim(current_files(selected)) == "..") then | ||
| 64 | - temp_dir = current_dir | ||
| 65 | - current_dir = parent_dir | ||
| 66 | - parent_dir = get_parent_path(current_dir) | ||
| 67 | - selected = max(1, find_in_parent(temp_dir, current_files, MAX_FILES)) | ||
| 68 | - else if (trim(current_files(selected)) /= ".") then | ||
| 69 | - parent_dir = current_dir | ||
| 70 | - current_dir = join_path(current_dir, current_files(selected)) | ||
| 71 | - selected = 1 | ||
| 72 | - end if | ||
| 73 | - end if | ||
| 74 | - case('D') ! Left - back | ||
| 75 | - if (current_dir /= "/") then | ||
| 76 | - temp_dir = current_dir | ||
| 77 | - current_dir = parent_dir | ||
| 78 | - parent_dir = get_parent_path(current_dir) | ||
| 79 | - selected = max(1, find_in_parent(temp_dir, current_files, MAX_FILES)) | ||
| 80 | - end if | ||
| 81 | - end select | ||
| 82 | - case(113, 81) ! 'q' or 'Q' | ||
| 83 | - running = .false. | ||
| 84 | - end select | ||
| 85 | - end do | ||
| 86 | - | ||
| 87 | - ! Cleanup | ||
| 88 | - call execute_command_line("stty icanon echo 2>/dev/null") | ||
| 89 | - write(output_unit, '(a)', advance='no') CLEAR | ||
| 90 | - write(output_unit, '(a)') "Thanks for using FORTRESS!" | ||
| 91 | - | ||
| 92 | -contains | ||
| 93 | - | ||
| 94 | - function get_pwd() result(path) | ||
| 95 | - character(len=MAX_PATH) :: path | ||
| 96 | - integer :: unit, ios | ||
| 97 | - | ||
| 98 | - call execute_command_line("pwd > .fortress_pwd 2>/dev/null", wait=.true.) | ||
| 99 | - open(newunit=unit, file=".fortress_pwd", status='old', iostat=ios) | ||
| 100 | - if (ios == 0) then | ||
| 101 | - read(unit, '(a)') path | ||
| 102 | - close(unit) | ||
| 103 | - else | ||
| 104 | - path = "." | ||
| 105 | - end if | ||
| 106 | - call execute_command_line("rm -f .fortress_pwd 2>/dev/null") | ||
| 107 | - end function get_pwd | ||
| 108 | - | ||
| 109 | - function get_parent_path(path) result(parent) | ||
| 110 | - character(len=*), intent(in) :: path | ||
| 111 | - character(len=MAX_PATH) :: parent | ||
| 112 | - integer :: pos | ||
| 113 | - | ||
| 114 | - pos = index(path, "/", back=.true.) | ||
| 115 | - if (pos > 1) then | ||
| 116 | - parent = path(1:pos-1) | ||
| 117 | - else if (pos == 1) then | ||
| 118 | - parent = "/" | ||
| 119 | - else | ||
| 120 | - parent = "." | ||
| 121 | - end if | ||
| 122 | - end function get_parent_path | ||
| 123 | - | ||
| 124 | - function join_path(base, name) result(full) | ||
| 125 | - character(len=*), intent(in) :: base, name | ||
| 126 | - character(len=MAX_PATH) :: full | ||
| 127 | - | ||
| 128 | - if (base == "/") then | ||
| 129 | - full = "/" // trim(name) | ||
| 130 | - else | ||
| 131 | - full = trim(base) // "/" // trim(name) | ||
| 132 | - end if | ||
| 133 | - end function join_path | ||
| 134 | - | ||
| 135 | - function find_in_parent(dir, files, count) result(idx) | ||
| 136 | - character(len=*), intent(in) :: dir | ||
| 137 | - character(len=*), dimension(*), intent(in) :: files | ||
| 138 | - integer, intent(in) :: count | ||
| 139 | - integer :: idx, pos | ||
| 140 | - character(len=256) :: basename | ||
| 141 | - | ||
| 142 | - pos = index(dir, "/", back=.true.) | ||
| 143 | - if (pos > 0) then | ||
| 144 | - basename = dir(pos+1:) | ||
| 145 | - else | ||
| 146 | - basename = dir | ||
| 147 | - end if | ||
| 148 | - | ||
| 149 | - do idx = 1, count | ||
| 150 | - if (trim(files(idx)) == trim(basename)) return | ||
| 151 | - end do | ||
| 152 | - idx = 1 | ||
| 153 | - end function find_in_parent | ||
| 154 | - | ||
| 155 | - subroutine get_file_list(dir, files, is_dir, count) | ||
| 156 | - character(len=*), intent(in) :: dir | ||
| 157 | - character(len=*), dimension(*), intent(out) :: files | ||
| 158 | - logical, dimension(*), intent(out) :: is_dir | ||
| 159 | - integer, intent(out) :: count | ||
| 160 | - integer :: unit, ios, stat | ||
| 161 | - character(len=MAX_PATH) :: fullpath | ||
| 162 | - | ||
| 163 | - call execute_command_line("ls -1a '" // trim(dir) // "' > .fortress_ls 2>/dev/null", wait=.true.) | ||
| 164 | - | ||
| 165 | - open(newunit=unit, file=".fortress_ls", status='old', iostat=ios) | ||
| 166 | - if (ios /= 0) then | ||
| 167 | - count = 0 | ||
| 168 | - return | ||
| 169 | - end if | ||
| 170 | - | ||
| 171 | - count = 0 | ||
| 172 | - do | ||
| 173 | - count = count + 1 | ||
| 174 | - if (count > MAX_FILES) exit | ||
| 175 | - read(unit, '(a)', iostat=ios) files(count) | ||
| 176 | - if (ios /= 0) then | ||
| 177 | - count = count - 1 | ||
| 178 | - exit | ||
| 179 | - end if | ||
| 180 | - | ||
| 181 | - fullpath = join_path(dir, files(count)) | ||
| 182 | - call execute_command_line("test -d '" // trim(fullpath) // "'", exitstat=stat, wait=.true.) | ||
| 183 | - is_dir(count) = (stat == 0) | ||
| 184 | - end do | ||
| 185 | - | ||
| 186 | - close(unit) | ||
| 187 | - call execute_command_line("rm -f .fortress_ls 2>/dev/null") | ||
| 188 | - end subroutine get_file_list | ||
| 189 | - | ||
| 190 | - subroutine get_term_size(r, c) | ||
| 191 | - integer, intent(out) :: r, c | ||
| 192 | - integer :: unit, ios | ||
| 193 | - | ||
| 194 | - call execute_command_line("tput lines > .fortress_size 2>/dev/null", wait=.true.) | ||
| 195 | - open(newunit=unit, file=".fortress_size", status='old', iostat=ios) | ||
| 196 | - if (ios == 0) then | ||
| 197 | - read(unit, *) r | ||
| 198 | - close(unit) | ||
| 199 | - else | ||
| 200 | - r = 24 | ||
| 201 | - end if | ||
| 202 | - | ||
| 203 | - call execute_command_line("tput cols > .fortress_size 2>/dev/null", wait=.true.) | ||
| 204 | - open(newunit=unit, file=".fortress_size", status='old', iostat=ios) | ||
| 205 | - if (ios == 0) then | ||
| 206 | - read(unit, *) c | ||
| 207 | - close(unit) | ||
| 208 | - else | ||
| 209 | - c = 80 | ||
| 210 | - end if | ||
| 211 | - | ||
| 212 | - call execute_command_line("rm -f .fortress_size 2>/dev/null") | ||
| 213 | - end subroutine get_term_size | ||
| 214 | - | ||
| 215 | - subroutine draw_interface(r, c) | ||
| 216 | - integer, intent(in) :: r, c | ||
| 217 | - integer :: left_w, i | ||
| 218 | - character(len=256) :: fname | ||
| 219 | - | ||
| 220 | - left_w = c * 3 / 10 | ||
| 221 | - | ||
| 222 | - ! Header | ||
| 223 | - write(output_unit, '(a)') BOLD // "FORTRESS" // RESET // " - " // trim(current_dir) | ||
| 224 | - | ||
| 225 | - ! Files | ||
| 226 | - do i = 1, min(r-3, max(parent_count, current_count)) | ||
| 227 | - ! Parent pane | ||
| 228 | - if (i <= parent_count) then | ||
| 229 | - fname = parent_files(i) | ||
| 230 | - if (parent_is_dir(i) .and. fname /= "." .and. fname /= "..") then | ||
| 231 | - fname = trim(fname) // "/" | ||
| 232 | - end if | ||
| 233 | - if (i == parent_selected) then | ||
| 234 | - write(output_unit, '(a)', advance='no') DIM // BOLD // fname(1:min(len_trim(fname),left_w)) // RESET | ||
| 235 | - else | ||
| 236 | - write(output_unit, '(a)', advance='no') DIM // fname(1:min(len_trim(fname),left_w)) // RESET | ||
| 237 | - end if | ||
| 238 | - write(output_unit, '(a)', advance='no') repeat(" ", max(0, left_w - len_trim(fname))) | ||
| 239 | - else | ||
| 240 | - write(output_unit, '(a)', advance='no') repeat(" ", left_w) | ||
| 241 | - end if | ||
| 242 | - | ||
| 243 | - ! Separator | ||
| 244 | - write(output_unit, '(a)', advance='no') " │ " | ||
| 245 | - | ||
| 246 | - ! Current pane | ||
| 247 | - if (i <= current_count) then | ||
| 248 | - fname = current_files(i) | ||
| 249 | - if (current_is_dir(i) .and. fname /= "." .and. fname /= "..") then | ||
| 250 | - fname = trim(fname) // "/" | ||
| 251 | - end if | ||
| 252 | - if (i == selected) then | ||
| 253 | - write(output_unit, '(a)') REVERSE // trim(fname) // RESET | ||
| 254 | - else | ||
| 255 | - write(output_unit, '(a)') trim(fname) | ||
| 256 | - end if | ||
| 257 | - else | ||
| 258 | - write(output_unit, *) | ||
| 259 | - end if | ||
| 260 | - end do | ||
| 261 | - | ||
| 262 | - ! Footer | ||
| 263 | - write(output_unit, '(a,i0,a)') DIM // "↑↓:nav →:enter ←:back q:quit [", selected, "/" // trim(adjustl(char(current_count))) // "]" // RESET | ||
| 264 | - end subroutine draw_interface | ||
| 265 | - | ||
| 266 | - subroutine read_arrow_key(k) | ||
| 267 | - character(len=1), intent(out) :: k | ||
| 268 | - character(len=1) :: ch | ||
| 269 | - | ||
| 270 | - read(*, '(a1)', advance='no') ch | ||
| 271 | - if (ch == '[') then | ||
| 272 | - read(*, '(a1)', advance='no') k | ||
| 273 | - else | ||
| 274 | - k = ch | ||
| 275 | - end if | ||
| 276 | - end subroutine read_arrow_key | ||
| 277 | - | ||
| 278 | -end program fortress_clean | ||
src/fortress_v2.f90deleted@@ -1,289 +0,0 @@ | |||
| 1 | -program fortress_v2 | ||
| 2 | - use iso_fortran_env, only: output_unit | ||
| 3 | - implicit none | ||
| 4 | - | ||
| 5 | - ! File entry type | ||
| 6 | - type :: file_entry | ||
| 7 | - character(len=256) :: name = "" | ||
| 8 | - logical :: is_dir = .false. | ||
| 9 | - end type file_entry | ||
| 10 | - | ||
| 11 | - ! Constants | ||
| 12 | - integer, parameter :: MAX_FILES = 200 | ||
| 13 | - character(len=*), parameter :: ESC = char(27) | ||
| 14 | - | ||
| 15 | - ! State variables | ||
| 16 | - type(file_entry), dimension(MAX_FILES) :: current_files, parent_files | ||
| 17 | - integer :: current_count, parent_count | ||
| 18 | - integer :: selected = 1 | ||
| 19 | - character(len=512) :: current_path, parent_path | ||
| 20 | - logical :: running = .true. | ||
| 21 | - character(len=10) :: key | ||
| 22 | - integer :: rows, cols | ||
| 23 | - integer :: i | ||
| 24 | - | ||
| 25 | - ! Initialize | ||
| 26 | - call get_cwd(current_path) | ||
| 27 | - call get_parent(current_path, parent_path) | ||
| 28 | - | ||
| 29 | - ! Setup terminal | ||
| 30 | - call system("stty -icanon -echo min 1 time 0") | ||
| 31 | - call get_terminal_size(rows, cols) | ||
| 32 | - | ||
| 33 | - ! Main loop | ||
| 34 | - do while (running) | ||
| 35 | - ! Get directory contents | ||
| 36 | - call list_dir(current_path, current_files, current_count) | ||
| 37 | - call list_dir(parent_path, parent_files, parent_count) | ||
| 38 | - | ||
| 39 | - ! Draw screen | ||
| 40 | - call clear_screen() | ||
| 41 | - call draw_header(current_path) | ||
| 42 | - call draw_panes(parent_files, parent_count, current_files, current_count, selected, rows, cols) | ||
| 43 | - call draw_footer() | ||
| 44 | - | ||
| 45 | - ! Get keyboard input | ||
| 46 | - call get_key(key) | ||
| 47 | - | ||
| 48 | - ! Handle input | ||
| 49 | - select case(trim(key)) | ||
| 50 | - case('A') ! Up arrow | ||
| 51 | - if (selected > 1) selected = selected - 1 | ||
| 52 | - case('B') ! Down arrow | ||
| 53 | - if (selected < current_count) selected = selected + 1 | ||
| 54 | - case('C') ! Right arrow - enter directory | ||
| 55 | - if (selected <= current_count .and. current_files(selected)%is_dir) then | ||
| 56 | - if (trim(current_files(selected)%name) /= ".") then | ||
| 57 | - call change_dir(current_path, current_files(selected)%name, parent_path) | ||
| 58 | - selected = 1 | ||
| 59 | - end if | ||
| 60 | - end if | ||
| 61 | - case('D') ! Left arrow - go to parent | ||
| 62 | - if (trim(current_path) /= "/") then | ||
| 63 | - current_path = parent_path | ||
| 64 | - call get_parent(current_path, parent_path) | ||
| 65 | - selected = 1 | ||
| 66 | - end if | ||
| 67 | - case('q', 'Q') | ||
| 68 | - running = .false. | ||
| 69 | - end select | ||
| 70 | - end do | ||
| 71 | - | ||
| 72 | - ! Cleanup | ||
| 73 | - call system("stty icanon echo") | ||
| 74 | - call clear_screen() | ||
| 75 | - write(output_unit, '(a)') "Thanks for using FORTRESS v2!" | ||
| 76 | - | ||
| 77 | -contains | ||
| 78 | - | ||
| 79 | - subroutine get_cwd(path) | ||
| 80 | - character(len=*), intent(out) :: path | ||
| 81 | - integer :: unit, ios | ||
| 82 | - | ||
| 83 | - call execute_command_line("pwd > pwd_temp.txt", wait=.true.) | ||
| 84 | - open(newunit=unit, file="pwd_temp.txt", status='old', iostat=ios) | ||
| 85 | - if (ios == 0) then | ||
| 86 | - read(unit, '(a)') path | ||
| 87 | - close(unit) | ||
| 88 | - else | ||
| 89 | - path = "." | ||
| 90 | - end if | ||
| 91 | - call execute_command_line("rm -f pwd_temp.txt", wait=.false.) | ||
| 92 | - end subroutine get_cwd | ||
| 93 | - | ||
| 94 | - subroutine get_parent(path, parent) | ||
| 95 | - character(len=*), intent(in) :: path | ||
| 96 | - character(len=*), intent(out) :: parent | ||
| 97 | - integer :: last_slash | ||
| 98 | - | ||
| 99 | - last_slash = index(path, '/', back=.true.) | ||
| 100 | - if (last_slash > 1) then | ||
| 101 | - parent = path(1:last_slash-1) | ||
| 102 | - else | ||
| 103 | - parent = "/" | ||
| 104 | - end if | ||
| 105 | - end subroutine get_parent | ||
| 106 | - | ||
| 107 | - subroutine change_dir(current, new_name, parent) | ||
| 108 | - character(len=*), intent(inout) :: current | ||
| 109 | - character(len=*), intent(in) :: new_name | ||
| 110 | - character(len=*), intent(inout) :: parent | ||
| 111 | - | ||
| 112 | - if (trim(new_name) == "..") then | ||
| 113 | - if (trim(current) /= "/") then | ||
| 114 | - parent = current | ||
| 115 | - call get_parent(current, parent) | ||
| 116 | - current = parent | ||
| 117 | - call get_parent(current, parent) | ||
| 118 | - end if | ||
| 119 | - else | ||
| 120 | - parent = current | ||
| 121 | - if (trim(current) == "/") then | ||
| 122 | - current = "/" // trim(new_name) | ||
| 123 | - else | ||
| 124 | - current = trim(current) // "/" // trim(new_name) | ||
| 125 | - end if | ||
| 126 | - end if | ||
| 127 | - end subroutine change_dir | ||
| 128 | - | ||
| 129 | - subroutine list_dir(path, files, count) | ||
| 130 | - character(len=*), intent(in) :: path | ||
| 131 | - type(file_entry), dimension(MAX_FILES), intent(out) :: files | ||
| 132 | - integer, intent(out) :: count | ||
| 133 | - integer :: unit, ios | ||
| 134 | - character(len=512) :: cmd | ||
| 135 | - | ||
| 136 | - ! Initialize | ||
| 137 | - files%name = "" | ||
| 138 | - files%is_dir = .false. | ||
| 139 | - | ||
| 140 | - ! List files | ||
| 141 | - write(cmd, '(a)') "ls -1a " // trim(path) // " 2>/dev/null > ls_temp.txt" | ||
| 142 | - call execute_command_line(trim(cmd), wait=.true.) | ||
| 143 | - | ||
| 144 | - open(newunit=unit, file="ls_temp.txt", status='old', iostat=ios) | ||
| 145 | - if (ios /= 0) then | ||
| 146 | - count = 0 | ||
| 147 | - return | ||
| 148 | - end if | ||
| 149 | - | ||
| 150 | - count = 0 | ||
| 151 | - do | ||
| 152 | - count = count + 1 | ||
| 153 | - if (count > MAX_FILES) exit | ||
| 154 | - read(unit, '(a)', iostat=ios) files(count)%name | ||
| 155 | - if (ios /= 0) then | ||
| 156 | - count = count - 1 | ||
| 157 | - exit | ||
| 158 | - end if | ||
| 159 | - ! Check if directory | ||
| 160 | - call is_directory(path, files(count)%name, files(count)%is_dir) | ||
| 161 | - end do | ||
| 162 | - | ||
| 163 | - close(unit) | ||
| 164 | - call execute_command_line("rm -f ls_temp.txt", wait=.false.) | ||
| 165 | - end subroutine list_dir | ||
| 166 | - | ||
| 167 | - subroutine is_directory(base_path, name, is_dir) | ||
| 168 | - character(len=*), intent(in) :: base_path, name | ||
| 169 | - logical, intent(out) :: is_dir | ||
| 170 | - character(len=512) :: full_path | ||
| 171 | - integer :: stat | ||
| 172 | - | ||
| 173 | - if (trim(base_path) == "/") then | ||
| 174 | - full_path = "/" // trim(name) | ||
| 175 | - else | ||
| 176 | - full_path = trim(base_path) // "/" // trim(name) | ||
| 177 | - end if | ||
| 178 | - | ||
| 179 | - call execute_command_line("test -d '" // trim(full_path) // "'", & | ||
| 180 | - exitstat=stat, wait=.true.) | ||
| 181 | - is_dir = (stat == 0) | ||
| 182 | - end subroutine is_directory | ||
| 183 | - | ||
| 184 | - subroutine get_terminal_size(rows, cols) | ||
| 185 | - integer, intent(out) :: rows, cols | ||
| 186 | - integer :: unit, ios | ||
| 187 | - | ||
| 188 | - call execute_command_line("tput lines > size_temp.txt", wait=.true.) | ||
| 189 | - open(newunit=unit, file="size_temp.txt", status='old', iostat=ios) | ||
| 190 | - if (ios == 0) then | ||
| 191 | - read(unit, *) rows | ||
| 192 | - close(unit) | ||
| 193 | - else | ||
| 194 | - rows = 24 | ||
| 195 | - end if | ||
| 196 | - | ||
| 197 | - call execute_command_line("tput cols > size_temp.txt", wait=.true.) | ||
| 198 | - open(newunit=unit, file="size_temp.txt", status='old', iostat=ios) | ||
| 199 | - if (ios == 0) then | ||
| 200 | - read(unit, *) cols | ||
| 201 | - close(unit) | ||
| 202 | - else | ||
| 203 | - cols = 80 | ||
| 204 | - end if | ||
| 205 | - | ||
| 206 | - call execute_command_line("rm -f size_temp.txt", wait=.false.) | ||
| 207 | - end subroutine get_terminal_size | ||
| 208 | - | ||
| 209 | - subroutine clear_screen() | ||
| 210 | - write(output_unit, '(a)', advance='no') ESC // "[2J" // ESC // "[H" | ||
| 211 | - end subroutine clear_screen | ||
| 212 | - | ||
| 213 | - subroutine draw_header(path) | ||
| 214 | - character(len=*), intent(in) :: path | ||
| 215 | - write(output_unit, '(a)') ESC // "[1m" // "FORTRESS v2" // ESC // "[0m - " // trim(path) | ||
| 216 | - write(output_unit, '(a)') repeat("=", 70) | ||
| 217 | - end subroutine draw_header | ||
| 218 | - | ||
| 219 | - subroutine draw_panes(p_files, p_count, c_files, c_count, sel, rows, cols) | ||
| 220 | - type(file_entry), dimension(*), intent(in) :: p_files, c_files | ||
| 221 | - integer, intent(in) :: p_count, c_count, sel, rows, cols | ||
| 222 | - integer :: i | ||
| 223 | - integer :: left_width, right_width | ||
| 224 | - character(len=30) :: left_text | ||
| 225 | - character(len=50) :: right_text | ||
| 226 | - | ||
| 227 | - left_width = cols * 3 / 10 | ||
| 228 | - right_width = cols - left_width - 3 | ||
| 229 | - | ||
| 230 | - do i = 1, min(rows - 5, max(p_count, c_count)) | ||
| 231 | - ! Left pane | ||
| 232 | - if (i <= p_count) then | ||
| 233 | - left_text = p_files(i)%name | ||
| 234 | - if (p_files(i)%is_dir .and. trim(left_text) /= "." .and. trim(left_text) /= "..") then | ||
| 235 | - left_text = trim(left_text) // "/" | ||
| 236 | - end if | ||
| 237 | - if (len_trim(left_text) > left_width) then | ||
| 238 | - left_text = left_text(1:left_width-3) // "..." | ||
| 239 | - end if | ||
| 240 | - write(output_unit, '(a)', advance='no') ESC // "[2m" // left_text(1:left_width) // ESC // "[0m" | ||
| 241 | - else | ||
| 242 | - write(output_unit, '(a)', advance='no') repeat(" ", left_width) | ||
| 243 | - end if | ||
| 244 | - | ||
| 245 | - ! Separator | ||
| 246 | - write(output_unit, '(a)', advance='no') " | " | ||
| 247 | - | ||
| 248 | - ! Right pane | ||
| 249 | - if (i <= c_count) then | ||
| 250 | - right_text = c_files(i)%name | ||
| 251 | - if (c_files(i)%is_dir .and. trim(right_text) /= "." .and. trim(right_text) /= "..") then | ||
| 252 | - right_text = trim(right_text) // "/" | ||
| 253 | - end if | ||
| 254 | - | ||
| 255 | - if (i == sel) then | ||
| 256 | - write(output_unit, '(a)') ESC // "[7m" // trim(right_text) // ESC // "[0m" | ||
| 257 | - else | ||
| 258 | - write(output_unit, '(a)') trim(right_text) | ||
| 259 | - end if | ||
| 260 | - else | ||
| 261 | - write(output_unit, *) | ||
| 262 | - end if | ||
| 263 | - end do | ||
| 264 | - end subroutine draw_panes | ||
| 265 | - | ||
| 266 | - subroutine draw_footer() | ||
| 267 | - write(output_unit, *) | ||
| 268 | - write(output_unit, '(a)') ESC // "[2m" // "↑↓: navigate | →: enter | ←: back | q: quit" // ESC // "[0m" | ||
| 269 | - end subroutine draw_footer | ||
| 270 | - | ||
| 271 | - subroutine get_key(key) | ||
| 272 | - character(len=*), intent(out) :: key | ||
| 273 | - character(len=1) :: ch | ||
| 274 | - | ||
| 275 | - key = "" | ||
| 276 | - read(*, '(a1)', advance='no', iostat=i) ch | ||
| 277 | - | ||
| 278 | - if (ch == ESC) then | ||
| 279 | - read(*, '(a1)', advance='no', iostat=i) ch | ||
| 280 | - if (ch == '[') then | ||
| 281 | - read(*, '(a1)', advance='no', iostat=i) ch | ||
| 282 | - key = ch | ||
| 283 | - end if | ||
| 284 | - else | ||
| 285 | - key = ch | ||
| 286 | - end if | ||
| 287 | - end subroutine get_key | ||
| 288 | - | ||
| 289 | -end program fortress_v2 | ||
src/main.f90modified@@ -1,145 +1,278 @@ | |||
| 1 | -program fortress_main | 1 | +program fortress_clean |
| 2 | use iso_fortran_env, only: output_unit, error_unit | 2 | use iso_fortran_env, only: output_unit, error_unit |
| 3 | - use terminal_screen, only: init_screen, cleanup_screen, clear_screen | ||
| 4 | - use terminal_input, only: get_key, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_ENTER, KEY_QUIT | ||
| 5 | - use filesystem_ops, only: list_directory, get_parent_dir, get_current_dir, is_directory, & | ||
| 6 | - file_entry, MAX_FILES | ||
| 7 | - use ui_panes_buffered, only: draw_panes_buffered | ||
| 8 | - | ||
| 9 | implicit none | 3 | implicit none |
| 10 | 4 | ||
| 11 | - logical :: running | 5 | + ! Constants |
| 12 | - logical :: needs_full_redraw | 6 | + integer, parameter :: MAX_PATH = 512 |
| 13 | - integer :: key | 7 | + integer, parameter :: MAX_FILES = 500 |
| 14 | - character(len=256) :: current_dir | 8 | + character(len=*), parameter :: ESC = char(27) |
| 15 | - character(len=256) :: parent_dir | 9 | + character(len=*), parameter :: CLEAR = ESC // "[2J" // ESC // "[H" |
| 16 | - character(len=256) :: new_dir | 10 | + character(len=*), parameter :: BOLD = ESC // "[1m" |
| 17 | - integer :: selected_index | 11 | + character(len=*), parameter :: DIM = ESC // "[2m" |
| 18 | - integer :: parent_selected_index | 12 | + character(len=*), parameter :: REVERSE = ESC // "[7m" |
| 19 | - type(file_entry), dimension(MAX_FILES) :: current_files, parent_files | 13 | + character(len=*), parameter :: RESET = ESC // "[0m" |
| 20 | - integer :: file_count, parent_file_count, i | ||
| 21 | - character(len=256) :: base_name | ||
| 22 | 14 | ||
| 23 | - ! Initialize | 15 | + ! Variables |
| 24 | - running = .true. | 16 | + character(len=MAX_PATH) :: current_dir, parent_dir, temp_dir |
| 25 | - needs_full_redraw = .true. | 17 | + character(len=MAX_PATH), dimension(MAX_FILES) :: current_files, parent_files |
| 26 | - selected_index = 1 | 18 | + logical, dimension(MAX_FILES) :: current_is_dir, parent_is_dir |
| 27 | - parent_selected_index = 1 | 19 | + integer :: current_count, parent_count |
| 28 | - current_dir = get_current_dir() | 20 | + integer :: selected = 1 |
| 29 | - parent_dir = get_parent_dir(current_dir) | 21 | + integer :: parent_selected = -1 |
| 22 | + character(len=1) :: key | ||
| 23 | + logical :: running = .true. | ||
| 24 | + integer :: i, rows, cols | ||
| 30 | 25 | ||
| 31 | - ! Set up terminal | 26 | + ! Initialize |
| 32 | - call init_screen() | 27 | + current_dir = get_pwd() |
| 28 | + parent_dir = get_parent_path(current_dir) | ||
| 33 | 29 | ||
| 34 | - ! Initial clear screen | 30 | + ! Setup terminal |
| 35 | - call clear_screen() | 31 | + call execute_command_line("stty -icanon -echo min 1 time 0 2>/dev/null") |
| 36 | 32 | ||
| 37 | ! Main loop | 33 | ! Main loop |
| 38 | do while (running) | 34 | do while (running) |
| 39 | - ! Get current directory contents | 35 | + ! Get files |
| 40 | - current_files = list_directory(current_dir) | 36 | + call get_file_list(current_dir, current_files, current_is_dir, current_count) |
| 41 | - parent_files = list_directory(parent_dir) | 37 | + call get_file_list(parent_dir, parent_files, parent_is_dir, parent_count) |
| 42 | - | 38 | + |
| 43 | - ! Count actual files | 39 | + ! Find current dir in parent |
| 44 | - file_count = 0 | 40 | + parent_selected = find_in_parent(current_dir, parent_files, parent_count) |
| 45 | - do i = 1, MAX_FILES | 41 | + |
| 46 | - if (len_trim(current_files(i)%name) == 0) exit | 42 | + ! Get terminal size |
| 47 | - file_count = i | 43 | + call get_term_size(rows, cols) |
| 48 | - end do | 44 | + |
| 45 | + ! Draw interface | ||
| 46 | + write(output_unit, '(a)', advance='no') CLEAR | ||
| 47 | + call draw_interface(rows, cols) | ||
| 48 | + | ||
| 49 | + ! Get input | ||
| 50 | + read(*, '(a1)', advance='no') key | ||
| 51 | + | ||
| 52 | + ! Handle input | ||
| 53 | + select case(ichar(key)) | ||
| 54 | + case(27) ! ESC sequence | ||
| 55 | + call read_arrow_key(key) | ||
| 56 | + select case(key) | ||
| 57 | + case('A') ! Up | ||
| 58 | + if (selected > 1) selected = selected - 1 | ||
| 59 | + case('B') ! Down | ||
| 60 | + if (selected < current_count) selected = selected + 1 | ||
| 61 | + case('C') ! Right - enter | ||
| 62 | + if (current_is_dir(selected)) then | ||
| 63 | + if (trim(current_files(selected)) == "..") then | ||
| 64 | + temp_dir = current_dir | ||
| 65 | + current_dir = parent_dir | ||
| 66 | + parent_dir = get_parent_path(current_dir) | ||
| 67 | + selected = max(1, find_in_parent(temp_dir, current_files, MAX_FILES)) | ||
| 68 | + else if (trim(current_files(selected)) /= ".") then | ||
| 69 | + parent_dir = current_dir | ||
| 70 | + current_dir = join_path(current_dir, current_files(selected)) | ||
| 71 | + selected = 1 | ||
| 72 | + end if | ||
| 73 | + end if | ||
| 74 | + case('D') ! Left - back | ||
| 75 | + if (current_dir /= "/") then | ||
| 76 | + temp_dir = current_dir | ||
| 77 | + current_dir = parent_dir | ||
| 78 | + parent_dir = get_parent_path(current_dir) | ||
| 79 | + selected = max(1, find_in_parent(temp_dir, current_files, MAX_FILES)) | ||
| 80 | + end if | ||
| 81 | + end select | ||
| 82 | + case(113, 81) ! 'q' or 'Q' | ||
| 83 | + running = .false. | ||
| 84 | + end select | ||
| 85 | + end do | ||
| 86 | + | ||
| 87 | + ! Cleanup | ||
| 88 | + call execute_command_line("stty icanon echo 2>/dev/null") | ||
| 89 | + write(output_unit, '(a)', advance='no') CLEAR | ||
| 90 | + write(output_unit, '(a)') "Thanks for using FORTRESS!" | ||
| 91 | + | ||
| 92 | +contains | ||
| 93 | + | ||
| 94 | + function get_pwd() result(path) | ||
| 95 | + character(len=MAX_PATH) :: path | ||
| 96 | + integer :: unit, ios | ||
| 97 | + | ||
| 98 | + call execute_command_line("pwd > .fortress_pwd 2>/dev/null", wait=.true.) | ||
| 99 | + open(newunit=unit, file=".fortress_pwd", status='old', iostat=ios) | ||
| 100 | + if (ios == 0) then | ||
| 101 | + read(unit, '(a)') path | ||
| 102 | + close(unit) | ||
| 103 | + else | ||
| 104 | + path = "." | ||
| 105 | + end if | ||
| 106 | + call execute_command_line("rm -f .fortress_pwd 2>/dev/null") | ||
| 107 | + end function get_pwd | ||
| 108 | + | ||
| 109 | + function get_parent_path(path) result(parent) | ||
| 110 | + character(len=*), intent(in) :: path | ||
| 111 | + character(len=MAX_PATH) :: parent | ||
| 112 | + integer :: pos | ||
| 113 | + | ||
| 114 | + pos = index(path, "/", back=.true.) | ||
| 115 | + if (pos > 1) then | ||
| 116 | + parent = path(1:pos-1) | ||
| 117 | + else if (pos == 1) then | ||
| 118 | + parent = "/" | ||
| 119 | + else | ||
| 120 | + parent = "." | ||
| 121 | + end if | ||
| 122 | + end function get_parent_path | ||
| 123 | + | ||
| 124 | + function join_path(base, name) result(full) | ||
| 125 | + character(len=*), intent(in) :: base, name | ||
| 126 | + character(len=MAX_PATH) :: full | ||
| 127 | + | ||
| 128 | + if (base == "/") then | ||
| 129 | + full = "/" // trim(name) | ||
| 130 | + else | ||
| 131 | + full = trim(base) // "/" // trim(name) | ||
| 132 | + end if | ||
| 133 | + end function join_path | ||
| 134 | + | ||
| 135 | + function find_in_parent(dir, files, count) result(idx) | ||
| 136 | + character(len=*), intent(in) :: dir | ||
| 137 | + character(len=*), dimension(*), intent(in) :: files | ||
| 138 | + integer, intent(in) :: count | ||
| 139 | + integer :: idx, pos | ||
| 140 | + character(len=256) :: basename | ||
| 49 | 141 | ||
| 50 | - parent_file_count = 0 | 142 | + pos = index(dir, "/", back=.true.) |
| 51 | - do i = 1, MAX_FILES | 143 | + if (pos > 0) then |
| 52 | - if (len_trim(parent_files(i)%name) == 0) exit | 144 | + basename = dir(pos+1:) |
| 53 | - parent_file_count = i | 145 | + else |
| 146 | + basename = dir | ||
| 147 | + end if | ||
| 148 | + | ||
| 149 | + do idx = 1, count | ||
| 150 | + if (trim(files(idx)) == trim(basename)) return | ||
| 54 | end do | 151 | end do |
| 152 | + idx = 1 | ||
| 153 | + end function find_in_parent | ||
| 154 | + | ||
| 155 | + subroutine get_file_list(dir, files, is_dir, count) | ||
| 156 | + character(len=*), intent(in) :: dir | ||
| 157 | + character(len=*), dimension(*), intent(out) :: files | ||
| 158 | + logical, dimension(*), intent(out) :: is_dir | ||
| 159 | + integer, intent(out) :: count | ||
| 160 | + integer :: unit, ios, stat | ||
| 161 | + character(len=MAX_PATH) :: fullpath | ||
| 55 | 162 | ||
| 56 | - ! Find current directory in parent listing for highlighting | 163 | + call execute_command_line("ls -1a '" // trim(dir) // "' > .fortress_ls 2>/dev/null", wait=.true.) |
| 57 | - call get_basename(current_dir, base_name) | 164 | + |
| 58 | - parent_selected_index = 1 | 165 | + open(newunit=unit, file=".fortress_ls", status='old', iostat=ios) |
| 59 | - do i = 1, parent_file_count | 166 | + if (ios /= 0) then |
| 60 | - if (trim(parent_files(i)%name) == trim(base_name)) then | 167 | + count = 0 |
| 61 | - parent_selected_index = i | 168 | + return |
| 169 | + end if | ||
| 170 | + | ||
| 171 | + count = 0 | ||
| 172 | + do | ||
| 173 | + count = count + 1 | ||
| 174 | + if (count > MAX_FILES) exit | ||
| 175 | + read(unit, '(a)', iostat=ios) files(count) | ||
| 176 | + if (ios /= 0) then | ||
| 177 | + count = count - 1 | ||
| 62 | exit | 178 | exit |
| 63 | end if | 179 | end if |
| 180 | + | ||
| 181 | + fullpath = join_path(dir, files(count)) | ||
| 182 | + call execute_command_line("test -d '" // trim(fullpath) // "'", exitstat=stat, wait=.true.) | ||
| 183 | + is_dir(count) = (stat == 0) | ||
| 64 | end do | 184 | end do |
| 65 | 185 | ||
| 66 | - ! Only clear screen if needed (reduces flashing) | 186 | + close(unit) |
| 67 | - if (needs_full_redraw) then | 187 | + call execute_command_line("rm -f .fortress_ls 2>/dev/null") |
| 68 | - call clear_screen() | 188 | + end subroutine get_file_list |
| 69 | - needs_full_redraw = .false. | 189 | + |
| 190 | + subroutine get_term_size(r, c) | ||
| 191 | + integer, intent(out) :: r, c | ||
| 192 | + integer :: unit, ios | ||
| 193 | + | ||
| 194 | + call execute_command_line("tput lines > .fortress_size 2>/dev/null", wait=.true.) | ||
| 195 | + open(newunit=unit, file=".fortress_size", status='old', iostat=ios) | ||
| 196 | + if (ios == 0) then | ||
| 197 | + read(unit, *) r | ||
| 198 | + close(unit) | ||
| 199 | + else | ||
| 200 | + r = 24 | ||
| 70 | end if | 201 | end if |
| 71 | 202 | ||
| 72 | - call draw_panes_buffered(parent_dir, current_dir, selected_index, parent_selected_index) | 203 | + call execute_command_line("tput cols > .fortress_size 2>/dev/null", wait=.true.) |
| 204 | + open(newunit=unit, file=".fortress_size", status='old', iostat=ios) | ||
| 205 | + if (ios == 0) then | ||
| 206 | + read(unit, *) c | ||
| 207 | + close(unit) | ||
| 208 | + else | ||
| 209 | + c = 80 | ||
| 210 | + end if | ||
| 73 | 211 | ||
| 74 | - key = get_key() | 212 | + call execute_command_line("rm -f .fortress_size 2>/dev/null") |
| 213 | + end subroutine get_term_size | ||
| 75 | 214 | ||
| 76 | - select case(key) | 215 | + subroutine draw_interface(r, c) |
| 77 | - case(KEY_UP) | 216 | + integer, intent(in) :: r, c |
| 78 | - if (selected_index > 1) then | 217 | + integer :: left_w, i |
| 79 | - selected_index = selected_index - 1 | 218 | + character(len=256) :: fname |
| 80 | - end if | 219 | + |
| 81 | - case(KEY_DOWN) | 220 | + left_w = c * 3 / 10 |
| 82 | - if (selected_index < file_count) then | 221 | + |
| 83 | - selected_index = selected_index + 1 | 222 | + ! Header |
| 84 | - end if | 223 | + write(output_unit, '(a)') BOLD // "FORTRESS" // RESET // " - " // trim(current_dir) |
| 85 | - case(KEY_LEFT) | 224 | + |
| 86 | - ! Go to parent directory | 225 | + ! Files |
| 87 | - if (trim(current_dir) /= "/") then | 226 | + do i = 1, min(r-3, max(parent_count, current_count)) |
| 88 | - current_dir = parent_dir | 227 | + ! Parent pane |
| 89 | - parent_dir = get_parent_dir(current_dir) | 228 | + if (i <= parent_count) then |
| 90 | - selected_index = parent_selected_index | 229 | + fname = parent_files(i) |
| 91 | - needs_full_redraw = .true. | 230 | + if (parent_is_dir(i) .and. fname /= "." .and. fname /= "..") then |
| 92 | - end if | 231 | + fname = trim(fname) // "/" |
| 93 | - case(KEY_RIGHT, KEY_ENTER) | 232 | + end if |
| 94 | - ! Enter directory or open file | 233 | + if (i == parent_selected) then |
| 95 | - if (selected_index <= file_count) then | 234 | + write(output_unit, '(a)', advance='no') DIM // BOLD // fname(1:min(len_trim(fname),left_w)) // RESET |
| 96 | - if (current_files(selected_index)%is_dir) then | ||
| 97 | - ! Navigate into directory | ||
| 98 | - if (trim(current_files(selected_index)%name) == "..") then | ||
| 99 | - ! Same as pressing left arrow | ||
| 100 | - if (trim(current_dir) /= "/") then | ||
| 101 | - current_dir = parent_dir | ||
| 102 | - parent_dir = get_parent_dir(current_dir) | ||
| 103 | - selected_index = parent_selected_index | ||
| 104 | - needs_full_redraw = .true. | ||
| 105 | - end if | ||
| 106 | - else if (trim(current_files(selected_index)%name) /= ".") then | ||
| 107 | - ! Enter subdirectory | ||
| 108 | - new_dir = trim(current_dir) // "/" // trim(current_files(selected_index)%name) | ||
| 109 | - if (is_directory(new_dir)) then | ||
| 110 | - parent_dir = current_dir | ||
| 111 | - current_dir = new_dir | ||
| 112 | - selected_index = 1 | ||
| 113 | - needs_full_redraw = .true. | ||
| 114 | - end if | ||
| 115 | - end if | ||
| 116 | else | 235 | else |
| 117 | - ! Open file - TODO: implement file opening with $EDITOR | 236 | + write(output_unit, '(a)', advance='no') DIM // fname(1:min(len_trim(fname),left_w)) // RESET |
| 118 | end if | 237 | end if |
| 238 | + write(output_unit, '(a)', advance='no') repeat(" ", max(0, left_w - len_trim(fname))) | ||
| 239 | + else | ||
| 240 | + write(output_unit, '(a)', advance='no') repeat(" ", left_w) | ||
| 119 | end if | 241 | end if |
| 120 | - case(KEY_QUIT) | ||
| 121 | - running = .false. | ||
| 122 | - end select | ||
| 123 | - end do | ||
| 124 | 242 | ||
| 125 | - ! Cleanup | 243 | + ! Separator |
| 126 | - call cleanup_screen() | 244 | + write(output_unit, '(a)', advance='no') " │ " |
| 127 | 245 | ||
| 128 | - write(output_unit, *) "Thanks for using FORTRESS!" | 246 | + ! Current pane |
| 247 | + if (i <= current_count) then | ||
| 248 | + fname = current_files(i) | ||
| 249 | + if (current_is_dir(i) .and. fname /= "." .and. fname /= "..") then | ||
| 250 | + fname = trim(fname) // "/" | ||
| 251 | + end if | ||
| 252 | + if (i == selected) then | ||
| 253 | + write(output_unit, '(a)') REVERSE // trim(fname) // RESET | ||
| 254 | + else | ||
| 255 | + write(output_unit, '(a)') trim(fname) | ||
| 256 | + end if | ||
| 257 | + else | ||
| 258 | + write(output_unit, *) | ||
| 259 | + end if | ||
| 260 | + end do | ||
| 129 | 261 | ||
| 130 | -contains | 262 | + ! Footer |
| 263 | + write(output_unit, '(a,i0,a)') DIM // "↑↓:nav →:enter ←:back q:quit [", selected, "/" // trim(adjustl(char(current_count))) // "]" // RESET | ||
| 264 | + end subroutine draw_interface | ||
| 131 | 265 | ||
| 132 | - subroutine get_basename(path, basename) | 266 | + subroutine read_arrow_key(k) |
| 133 | - character(len=*), intent(in) :: path | 267 | + character(len=1), intent(out) :: k |
| 134 | - character(len=*), intent(out) :: basename | 268 | + character(len=1) :: ch |
| 135 | - integer :: last_slash | ||
| 136 | 269 | ||
| 137 | - last_slash = index(path, '/', back=.true.) | 270 | + read(*, '(a1)', advance='no') ch |
| 138 | - if (last_slash > 0 .and. last_slash < len_trim(path)) then | 271 | + if (ch == '[') then |
| 139 | - basename = path(last_slash+1:) | 272 | + read(*, '(a1)', advance='no') k |
| 140 | else | 273 | else |
| 141 | - basename = path | 274 | + k = ch |
| 142 | end if | 275 | end if |
| 143 | - end subroutine get_basename | 276 | + end subroutine read_arrow_key |
| 144 | 277 | ||
| 145 | -end program fortress_main | 278 | +end program fortress_clean |
src/simple_main.f90deleted@@ -1,119 +0,0 @@ | |||
| 1 | -program simple_fortress | ||
| 2 | - use iso_fortran_env, only: output_unit, input_unit | ||
| 3 | - implicit none | ||
| 4 | - | ||
| 5 | - character(len=256) :: current_path | ||
| 6 | - character(len=256) :: parent_path | ||
| 7 | - character(len=256), dimension(100) :: files | ||
| 8 | - character(len=256), dimension(100) :: parent_files | ||
| 9 | - integer :: num_files, num_parent_files | ||
| 10 | - integer :: selected = 1 | ||
| 11 | - integer :: i | ||
| 12 | - character(len=1) :: key | ||
| 13 | - logical :: running = .true. | ||
| 14 | - | ||
| 15 | - ! Start in current directory | ||
| 16 | - current_path = "." | ||
| 17 | - parent_path = ".." | ||
| 18 | - | ||
| 19 | - ! Main loop | ||
| 20 | - do while (running) | ||
| 21 | - ! Get file lists | ||
| 22 | - call get_files(current_path, files, num_files) | ||
| 23 | - call get_files(parent_path, parent_files, num_parent_files) | ||
| 24 | - | ||
| 25 | - ! Clear screen (simple way) | ||
| 26 | - write(output_unit, '(a)') char(27) // "[2J" // char(27) // "[H" | ||
| 27 | - | ||
| 28 | - ! Draw header | ||
| 29 | - write(output_unit, '(a)') "FORTRESS - Simple File Browser" | ||
| 30 | - write(output_unit, '(a)') "==============================" | ||
| 31 | - write(output_unit, *) | ||
| 32 | - | ||
| 33 | - ! Draw files in two columns | ||
| 34 | - write(output_unit, '(a)') "Parent Directory | Current Directory" | ||
| 35 | - write(output_unit, '(a)') "------------------------- | -------------------------" | ||
| 36 | - | ||
| 37 | - do i = 1, max(num_parent_files, num_files) | ||
| 38 | - if (i <= num_parent_files) then | ||
| 39 | - write(output_unit, '(a25)', advance='no') adjustl(parent_files(i)) | ||
| 40 | - else | ||
| 41 | - write(output_unit, '(a25)', advance='no') " " | ||
| 42 | - end if | ||
| 43 | - | ||
| 44 | - write(output_unit, '(a)', advance='no') " | " | ||
| 45 | - | ||
| 46 | - if (i <= num_files) then | ||
| 47 | - if (i == selected) then | ||
| 48 | - write(output_unit, '(a)') "> " // trim(files(i)) | ||
| 49 | - else | ||
| 50 | - write(output_unit, '(a)') " " // trim(files(i)) | ||
| 51 | - end if | ||
| 52 | - else | ||
| 53 | - write(output_unit, *) | ||
| 54 | - end if | ||
| 55 | - end do | ||
| 56 | - | ||
| 57 | - ! Show controls | ||
| 58 | - write(output_unit, *) | ||
| 59 | - write(output_unit, '(a)') "Controls: j=down, k=up, q=quit" | ||
| 60 | - write(output_unit, '(a)', advance='no') "Command: " | ||
| 61 | - | ||
| 62 | - ! Get input | ||
| 63 | - read(input_unit, '(a1)') key | ||
| 64 | - | ||
| 65 | - select case(key) | ||
| 66 | - case('j', 'J') | ||
| 67 | - if (selected < num_files) selected = selected + 1 | ||
| 68 | - case('k', 'K') | ||
| 69 | - if (selected > 1) selected = selected - 1 | ||
| 70 | - case('q', 'Q') | ||
| 71 | - running = .false. | ||
| 72 | - end select | ||
| 73 | - end do | ||
| 74 | - | ||
| 75 | - write(output_unit, '(a)') "Goodbye!" | ||
| 76 | - | ||
| 77 | -contains | ||
| 78 | - | ||
| 79 | - subroutine get_files(path, file_list, count) | ||
| 80 | - character(len=*), intent(in) :: path | ||
| 81 | - character(len=256), dimension(100), intent(out) :: file_list | ||
| 82 | - integer, intent(out) :: count | ||
| 83 | - | ||
| 84 | - character(len=512) :: cmd | ||
| 85 | - character(len=256) :: temp_file | ||
| 86 | - integer :: unit, ios | ||
| 87 | - | ||
| 88 | - ! Create temp file name | ||
| 89 | - temp_file = "temp_files.txt" | ||
| 90 | - | ||
| 91 | - ! List files to temp file | ||
| 92 | - write(cmd, '(a)') "ls -1 " // trim(path) // " > " // trim(temp_file) // " 2>/dev/null" | ||
| 93 | - call execute_command_line(cmd, wait=.true.) | ||
| 94 | - | ||
| 95 | - ! Read files | ||
| 96 | - open(newunit=unit, file=temp_file, status='old', iostat=ios) | ||
| 97 | - if (ios /= 0) then | ||
| 98 | - count = 0 | ||
| 99 | - return | ||
| 100 | - end if | ||
| 101 | - | ||
| 102 | - count = 0 | ||
| 103 | - do | ||
| 104 | - count = count + 1 | ||
| 105 | - if (count > 100) exit | ||
| 106 | - read(unit, '(a)', iostat=ios) file_list(count) | ||
| 107 | - if (ios /= 0) then | ||
| 108 | - count = count - 1 | ||
| 109 | - exit | ||
| 110 | - end if | ||
| 111 | - end do | ||
| 112 | - | ||
| 113 | - close(unit) | ||
| 114 | - | ||
| 115 | - ! Clean up | ||
| 116 | - call execute_command_line("rm -f " // trim(temp_file), wait=.false.) | ||
| 117 | - end subroutine get_files | ||
| 118 | - | ||
| 119 | -end program simple_fortress | ||
src/test_right_pane.f90deleted@@ -1,31 +0,0 @@ | |||
| 1 | -program test_right_pane | ||
| 2 | - use terminal_screen, only: init_screen, cleanup_screen, clear_screen | ||
| 3 | - use ui_panes_buffered, only: draw_panes_buffered | ||
| 4 | - | ||
| 5 | - implicit none | ||
| 6 | - character(len=256) :: current_dir, parent_dir | ||
| 7 | - | ||
| 8 | - ! Set test directories | ||
| 9 | - current_dir = "." | ||
| 10 | - parent_dir = ".." | ||
| 11 | - | ||
| 12 | - ! Initialize screen | ||
| 13 | - call init_screen() | ||
| 14 | - call clear_screen() | ||
| 15 | - | ||
| 16 | - ! Draw the panes with buffered rendering | ||
| 17 | - write(*, '(a)') "Testing buffered rendering - both panes should show files:" | ||
| 18 | - write(*, *) | ||
| 19 | - | ||
| 20 | - call draw_panes_buffered(parent_dir, current_dir, 2, 1) | ||
| 21 | - | ||
| 22 | - ! Wait for user input | ||
| 23 | - write(*, *) | ||
| 24 | - write(*, '(a)') "Press Enter to exit test..." | ||
| 25 | - read(*, *) | ||
| 26 | - | ||
| 27 | - ! Cleanup | ||
| 28 | - call cleanup_screen() | ||
| 29 | - write(*, '(a)') "Test complete - if you saw files in both panes, buffered rendering works!" | ||
| 30 | - | ||
| 31 | -end program test_right_pane | ||