@@ -324,8 +324,12 @@ contains |
| 324 | ! Always use fast blocking read - timeouts are too slow | 324 | ! Always use fast blocking read - timeouts are too slow |
| 325 | call read_key(key) | 325 | call read_key(key) |
| 326 | | 326 | |
| 327 | - ! DEBUG: Log all control characters to see what we're getting | 327 | + ! DEBUG: Log ALL keys when in rename mode, otherwise just control chars |
| 328 | - if (ichar(key) < 32) then | 328 | + if (in_rename_mode) then |
| | 329 | + open(99, file='/tmp/fuss_debug.log', position='append') |
| | 330 | + write(99, '(A,I0,A)') 'RENAME INPUT: code=', ichar(key), ' (all keys logged in rename mode)' |
| | 331 | + close(99) |
| | 332 | + else if (ichar(key) < 32) then |
| 329 | open(99, file='/tmp/fuss_debug.log', position='append') | 333 | open(99, file='/tmp/fuss_debug.log', position='append') |
| 330 | write(99, '(A,I0)') 'Control char received: ', ichar(key) | 334 | write(99, '(A,I0)') 'Control char received: ', ichar(key) |
| 331 | close(99) | 335 | close(99) |
@@ -387,6 +391,7 @@ contains |
| 387 | rename_original_name = items(selected)%node%name | 391 | rename_original_name = items(selected)%node%name |
| 388 | rename_buffer = items(selected)%node%name | 392 | rename_buffer = items(selected)%node%name |
| 389 | rename_cursor_pos = len_trim(rename_buffer) | 393 | rename_cursor_pos = len_trim(rename_buffer) |
| | 394 | + ! Stay in cbreak mode - raw mode breaks terminal output |
| 390 | needs_full_redraw = .true. | 395 | needs_full_redraw = .true. |
| 391 | end if | 396 | end if |
| 392 | cycle | 397 | cycle |
@@ -395,7 +400,7 @@ contains |
| 395 | ! Handle ESC key - exit rename mode, git mode, or clear search | 400 | ! Handle ESC key - exit rename mode, git mode, or clear search |
| 396 | if (key == achar(27)) then | 401 | if (key == achar(27)) then |
| 397 | if (in_rename_mode) then | 402 | if (in_rename_mode) then |
| 398 | - ! Cancel rename - restore original name | 403 | + ! Cancel rename |
| 399 | in_rename_mode = .false. | 404 | in_rename_mode = .false. |
| 400 | rename_buffer = '' | 405 | rename_buffer = '' |
| 401 | rename_original_name = '' | 406 | rename_original_name = '' |
@@ -426,18 +431,13 @@ contains |
| 426 | | 431 | |
| 427 | ! Rename mode key handling - intercept all keys when in rename mode | 432 | ! Rename mode key handling - intercept all keys when in rename mode |
| 428 | if (in_rename_mode) then | 433 | if (in_rename_mode) then |
| 429 | - ! DEBUG: Log all keys in rename mode | 434 | + ! Handle Tab to confirm rename (Enter codes checked but won't work in cbreak mode) |
| 430 | - open(99, file='/tmp/fuss_debug.log', position='append') | 435 | + ! achar(9) = Tab, achar(10) = LF, achar(13) = CR (cbreak mode eats Enter) |
| 431 | - write(99, '(A,I0,A,I0)') 'RENAME MODE: key code=', ichar(key), ' decimal=', ichar(key) | | |
| 432 | - close(99) | | |
| 433 | - | | |
| 434 | - ! Handle Enter (multiple possible codes) or Tab to confirm rename | | |
| 435 | - ! achar(10) = LF, achar(13) = CR, achar(9) = Tab, achar(0) = null | | |
| 436 | if (key == achar(10) .or. key == achar(13) .or. key == achar(9) .or. & | 436 | if (key == achar(10) .or. key == achar(13) .or. key == achar(9) .or. & |
| 437 | key == achar(0) .or. ichar(key) == 10 .or. ichar(key) == 13) then | 437 | key == achar(0) .or. ichar(key) == 10 .or. ichar(key) == 13) then |
| 438 | - ! Enter or Tab - execute rename | 438 | + ! Tab saves rename (Enter codes kept for compatibility but don't work) |
| 439 | open(99, file='/tmp/fuss_debug.log', position='append') | 439 | open(99, file='/tmp/fuss_debug.log', position='append') |
| 440 | - write(99, '(A)') 'RENAME MODE: ENTER/TAB detected - executing rename' | 440 | + write(99, '(A)') 'RENAME MODE: TAB detected - executing rename' |
| 441 | close(99) | 441 | close(99) |
| 442 | call execute_rename(items(selected)%path, trim(rename_buffer)) | 442 | call execute_rename(items(selected)%path, trim(rename_buffer)) |
| 443 | in_rename_mode = .false. | 443 | in_rename_mode = .false. |
@@ -1858,9 +1858,9 @@ contains |
| 1858 | subroutine execute_rename(old_path, new_name) | 1858 | subroutine execute_rename(old_path, new_name) |
| 1859 | ! Execute file/directory rename | 1859 | ! Execute file/directory rename |
| 1860 | character(len=*), intent(in) :: old_path, new_name | 1860 | character(len=*), intent(in) :: old_path, new_name |
| 1861 | - character(len=1024) :: dirname, new_path, command | 1861 | + character(len=1024) :: dirname, new_path, command, old_path_lower, new_path_lower |
| 1862 | integer :: status, last_slash | 1862 | integer :: status, last_slash |
| 1863 | - logical :: file_exists | 1863 | + logical :: file_exists, case_only_change |
| 1864 | | 1864 | |
| 1865 | ! Validate new name | 1865 | ! Validate new name |
| 1866 | if (len_trim(new_name) == 0) then | 1866 | if (len_trim(new_name) == 0) then |
@@ -1879,8 +1879,20 @@ contains |
| 1879 | ! Build new full path | 1879 | ! Build new full path |
| 1880 | write(new_path, '(A,A)') trim(dirname), trim(new_name) | 1880 | write(new_path, '(A,A)') trim(dirname), trim(new_name) |
| 1881 | | 1881 | |
| 1882 | - ! Check if new path already exists (and it's not the same file) | 1882 | + ! If it's the exact same name, do nothing |
| 1883 | - if (trim(new_path) /= trim(old_path)) then | 1883 | + if (trim(new_path) == trim(old_path)) then |
| | 1884 | + return |
| | 1885 | + end if |
| | 1886 | + |
| | 1887 | + ! Check if this is a case-only change (for case-insensitive filesystems like macOS) |
| | 1888 | + old_path_lower = old_path |
| | 1889 | + new_path_lower = new_path |
| | 1890 | + call to_lowercase(old_path_lower) |
| | 1891 | + call to_lowercase(new_path_lower) |
| | 1892 | + case_only_change = (trim(old_path_lower) == trim(new_path_lower)) |
| | 1893 | + |
| | 1894 | + ! Check if new path already exists (skip check for case-only changes) |
| | 1895 | + if (.not. case_only_change) then |
| 1884 | inquire(file=trim(new_path), exist=file_exists) | 1896 | inquire(file=trim(new_path), exist=file_exists) |
| 1885 | if (file_exists) then | 1897 | if (file_exists) then |
| 1886 | call show_message_and_wait('Error: A file with that name already exists!') | 1898 | call show_message_and_wait('Error: A file with that name already exists!') |
@@ -1888,13 +1900,9 @@ contains |
| 1888 | end if | 1900 | end if |
| 1889 | end if | 1901 | end if |
| 1890 | | 1902 | |
| 1891 | - ! If it's the same name, do nothing | | |
| 1892 | - if (trim(new_path) == trim(old_path)) then | | |
| 1893 | - return | | |
| 1894 | - end if | | |
| 1895 | - | | |
| 1896 | ! Execute rename using mv command | 1903 | ! Execute rename using mv command |
| 1897 | - write(command, '(A,A,A,A,A)') 'mv "', trim(old_path), '" "', trim(new_path), '" 2>/dev/null' | 1904 | + ! Use -f flag to force case-only renames on case-insensitive filesystems |
| | 1905 | + write(command, '(A,A,A,A,A)') 'mv -f "', trim(old_path), '" "', trim(new_path), '" 2>/dev/null' |
| 1898 | call execute_command_line(trim(command), exitstat=status) | 1906 | call execute_command_line(trim(command), exitstat=status) |
| 1899 | | 1907 | |
| 1900 | if (status /= 0) then | 1908 | if (status /= 0) then |