feat: files app - render modified dates in Properties dialog
This commit is contained in:
@@ -12,4 +12,4 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define MONTAUK_BUILD_NUMBER 4
|
#define MONTAUK_BUILD_NUMBER 6
|
||||||
|
|||||||
@@ -158,6 +158,48 @@ inline void format_size(char* buf, uint64_t size) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Date/time formatting
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Format a UTC unix timestamp as a local "YYYY-MM-DD HH:MM" string.
|
||||||
|
// tz_offset_minutes shifts UTC to local time (pass montauk::gettz()). A zero
|
||||||
|
// or negative timestamp -- meaning the filesystem did not record a time -- is
|
||||||
|
// rendered as "--". buf must hold at least 17 bytes.
|
||||||
|
inline void format_datetime(char* buf, int buf_size, int64_t unix_utc, int tz_offset_minutes) {
|
||||||
|
if (buf_size <= 0) return;
|
||||||
|
if (unix_utc <= 0) { snprintf(buf, buf_size, "--"); return; }
|
||||||
|
|
||||||
|
int64_t t = unix_utc + (int64_t)tz_offset_minutes * 60;
|
||||||
|
if (t < 0) t = 0;
|
||||||
|
|
||||||
|
int64_t days = t / 86400;
|
||||||
|
int64_t rem = t % 86400;
|
||||||
|
int hour = (int)(rem / 3600);
|
||||||
|
int minute = (int)((rem % 3600) / 60);
|
||||||
|
|
||||||
|
int year = 1970;
|
||||||
|
for (;;) {
|
||||||
|
bool leap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
|
||||||
|
int diy = leap ? 366 : 365;
|
||||||
|
if (days < diy) break;
|
||||||
|
days -= diy;
|
||||||
|
year++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int mdays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
||||||
|
int month = 0;
|
||||||
|
for (; month < 12; month++) {
|
||||||
|
int dim = mdays[month];
|
||||||
|
if (month == 1 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) dim = 29;
|
||||||
|
if (days < dim) break;
|
||||||
|
days -= dim;
|
||||||
|
}
|
||||||
|
|
||||||
|
int day = (int)days + 1;
|
||||||
|
snprintf(buf, buf_size, "%04d-%02d-%02d %02d:%02d", year, month + 1, day, hour, minute);
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Desktop builtins
|
// Desktop builtins
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ struct FileManagerState {
|
|||||||
char (*entry_names)[128];
|
char (*entry_names)[128];
|
||||||
int* entry_types;
|
int* entry_types;
|
||||||
uint64_t* entry_sizes; // 64-bit: avoids overflow on files >= 2 GiB
|
uint64_t* entry_sizes; // 64-bit: avoids overflow on files >= 2 GiB
|
||||||
|
int64_t* entry_mtimes; // last-modified time, UTC unix seconds (0 = unknown)
|
||||||
int entry_count;
|
int entry_count;
|
||||||
int entry_cap; // allocated capacity of the per-entry arrays
|
int entry_cap; // allocated capacity of the per-entry arrays
|
||||||
int selected;
|
int selected;
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ bool filemanager_ensure_entry_capacity(FileManagerState* fm, int needed) {
|
|||||||
uint64_t* sizes = (uint64_t*)montauk::realloc(fm->entry_sizes, (uint64_t)new_cap * sizeof(uint64_t));
|
uint64_t* sizes = (uint64_t*)montauk::realloc(fm->entry_sizes, (uint64_t)new_cap * sizeof(uint64_t));
|
||||||
if (!sizes) return false;
|
if (!sizes) return false;
|
||||||
fm->entry_sizes = sizes;
|
fm->entry_sizes = sizes;
|
||||||
|
int64_t* mtimes = (int64_t*)montauk::realloc(fm->entry_mtimes, (uint64_t)new_cap * sizeof(int64_t));
|
||||||
|
if (!mtimes) return false;
|
||||||
|
fm->entry_mtimes = mtimes;
|
||||||
bool* isdir = (bool*)montauk::realloc(fm->is_dir, (uint64_t)new_cap * sizeof(bool));
|
bool* isdir = (bool*)montauk::realloc(fm->is_dir, (uint64_t)new_cap * sizeof(bool));
|
||||||
if (!isdir) return false;
|
if (!isdir) return false;
|
||||||
fm->is_dir = isdir;
|
fm->is_dir = isdir;
|
||||||
@@ -81,6 +84,7 @@ void filemanager_free_arrays(FileManagerState* fm) {
|
|||||||
montauk::mfree(fm->entry_names); fm->entry_names = nullptr;
|
montauk::mfree(fm->entry_names); fm->entry_names = nullptr;
|
||||||
montauk::mfree(fm->entry_types); fm->entry_types = nullptr;
|
montauk::mfree(fm->entry_types); fm->entry_types = nullptr;
|
||||||
montauk::mfree(fm->entry_sizes); fm->entry_sizes = nullptr;
|
montauk::mfree(fm->entry_sizes); fm->entry_sizes = nullptr;
|
||||||
|
montauk::mfree(fm->entry_mtimes); fm->entry_mtimes = nullptr;
|
||||||
montauk::mfree(fm->is_dir); fm->is_dir = nullptr;
|
montauk::mfree(fm->is_dir); fm->is_dir = nullptr;
|
||||||
montauk::mfree(fm->drive_indices); fm->drive_indices = nullptr;
|
montauk::mfree(fm->drive_indices); fm->drive_indices = nullptr;
|
||||||
montauk::mfree(fm->drive_kinds); fm->drive_kinds = nullptr;
|
montauk::mfree(fm->drive_kinds); fm->drive_kinds = nullptr;
|
||||||
@@ -125,6 +129,7 @@ static void filemanager_add_root_entry(FileManagerState* fm,
|
|||||||
montauk::strncpy(fm->entry_names[idx], name, 63);
|
montauk::strncpy(fm->entry_names[idx], name, 63);
|
||||||
fm->entry_types[idx] = type;
|
fm->entry_types[idx] = type;
|
||||||
fm->entry_sizes[idx] = 0;
|
fm->entry_sizes[idx] = 0;
|
||||||
|
fm->entry_mtimes[idx] = 0;
|
||||||
fm->is_dir[idx] = true;
|
fm->is_dir[idx] = true;
|
||||||
fm->drive_indices[idx] = drive_index;
|
fm->drive_indices[idx] = drive_index;
|
||||||
fm->drive_kinds[idx] = 0;
|
fm->drive_kinds[idx] = 0;
|
||||||
@@ -143,6 +148,7 @@ static int filemanager_add_virtual_entry(FileManagerState* fm,
|
|||||||
montauk::strncpy(fm->entry_names[idx], name, 63);
|
montauk::strncpy(fm->entry_names[idx], name, 63);
|
||||||
fm->entry_types[idx] = FM_ENTRY_VIRTUAL_APP;
|
fm->entry_types[idx] = FM_ENTRY_VIRTUAL_APP;
|
||||||
fm->entry_sizes[idx] = 0;
|
fm->entry_sizes[idx] = 0;
|
||||||
|
fm->entry_mtimes[idx] = 0;
|
||||||
fm->is_dir[idx] = false;
|
fm->is_dir[idx] = false;
|
||||||
fm->drive_indices[idx] = -1;
|
fm->drive_indices[idx] = -1;
|
||||||
filemanager_reset_virtual_entry(&fm->virtual_entries[idx]);
|
filemanager_reset_virtual_entry(&fm->virtual_entries[idx]);
|
||||||
@@ -322,9 +328,10 @@ void filemanager_read_dir(FileManagerState* fm) {
|
|||||||
|
|
||||||
fm->entry_types[i] = detect_file_type(fm->entry_names[i], fm->is_dir[i]);
|
fm->entry_types[i] = detect_file_type(fm->entry_names[i], fm->is_dir[i]);
|
||||||
|
|
||||||
// Get file size (64-bit: no truncation for files >= 2 GiB)
|
// Fetch size + modified time in one stat call (works for files and
|
||||||
|
// directories). Filesystems without stat support leave both 0.
|
||||||
fm->entry_sizes[i] = 0;
|
fm->entry_sizes[i] = 0;
|
||||||
if (!fm->is_dir[i]) {
|
fm->entry_mtimes[i] = 0;
|
||||||
char fullpath[512];
|
char fullpath[512];
|
||||||
montauk::strcpy(fullpath, fm->current_path);
|
montauk::strcpy(fullpath, fm->current_path);
|
||||||
int plen = montauk::slen(fullpath);
|
int plen = montauk::slen(fullpath);
|
||||||
@@ -332,11 +339,11 @@ void filemanager_read_dir(FileManagerState* fm) {
|
|||||||
str_append(fullpath, "/", 512);
|
str_append(fullpath, "/", 512);
|
||||||
}
|
}
|
||||||
str_append(fullpath, fm->entry_names[i], 512);
|
str_append(fullpath, fm->entry_names[i], 512);
|
||||||
int fd = montauk::open(fullpath);
|
|
||||||
if (fd >= 0) {
|
montauk::abi::FileStat stt;
|
||||||
fm->entry_sizes[i] = montauk::getsize(fd);
|
if (montauk::stat(fullpath, &stt) == 0) {
|
||||||
montauk::close(fd);
|
if (!fm->is_dir[i]) fm->entry_sizes[i] = stt.size;
|
||||||
}
|
fm->entry_mtimes[i] = stt.mtime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,6 +356,7 @@ void filemanager_read_dir(FileManagerState* fm) {
|
|||||||
char tmp_name[128];
|
char tmp_name[128];
|
||||||
int tmp_type = fm->entry_types[i];
|
int tmp_type = fm->entry_types[i];
|
||||||
uint64_t tmp_size = fm->entry_sizes[i];
|
uint64_t tmp_size = fm->entry_sizes[i];
|
||||||
|
int64_t tmp_mtime = fm->entry_mtimes[i];
|
||||||
bool tmp_isdir = fm->is_dir[i];
|
bool tmp_isdir = fm->is_dir[i];
|
||||||
montauk::strcpy(tmp_name, fm->entry_names[i]);
|
montauk::strcpy(tmp_name, fm->entry_names[i]);
|
||||||
|
|
||||||
@@ -367,12 +375,14 @@ void filemanager_read_dir(FileManagerState* fm) {
|
|||||||
montauk::strcpy(fm->entry_names[j + 1], fm->entry_names[j]);
|
montauk::strcpy(fm->entry_names[j + 1], fm->entry_names[j]);
|
||||||
fm->entry_types[j + 1] = fm->entry_types[j];
|
fm->entry_types[j + 1] = fm->entry_types[j];
|
||||||
fm->entry_sizes[j + 1] = fm->entry_sizes[j];
|
fm->entry_sizes[j + 1] = fm->entry_sizes[j];
|
||||||
|
fm->entry_mtimes[j + 1] = fm->entry_mtimes[j];
|
||||||
fm->is_dir[j + 1] = fm->is_dir[j];
|
fm->is_dir[j + 1] = fm->is_dir[j];
|
||||||
j--;
|
j--;
|
||||||
}
|
}
|
||||||
montauk::strcpy(fm->entry_names[j + 1], tmp_name);
|
montauk::strcpy(fm->entry_names[j + 1], tmp_name);
|
||||||
fm->entry_types[j + 1] = tmp_type;
|
fm->entry_types[j + 1] = tmp_type;
|
||||||
fm->entry_sizes[j + 1] = tmp_size;
|
fm->entry_sizes[j + 1] = tmp_size;
|
||||||
|
fm->entry_mtimes[j + 1] = tmp_mtime;
|
||||||
fm->is_dir[j + 1] = tmp_isdir;
|
fm->is_dir[j + 1] = tmp_isdir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,10 +21,12 @@ struct FilePropertiesDialogState {
|
|||||||
char path[512];
|
char path[512];
|
||||||
char size[32];
|
char size[32];
|
||||||
char contains[32];
|
char contains[32];
|
||||||
|
char modified[24];
|
||||||
|
|
||||||
bool has_path;
|
bool has_path;
|
||||||
bool has_size;
|
bool has_size;
|
||||||
bool has_contains;
|
bool has_contains;
|
||||||
|
bool has_modified;
|
||||||
bool owns_icon;
|
bool owns_icon;
|
||||||
bool fallback_is_dir;
|
bool fallback_is_dir;
|
||||||
SvgIcon icon;
|
SvgIcon icon;
|
||||||
@@ -293,6 +295,14 @@ static void props_populate(FilePropertiesDialogState* st,
|
|||||||
st->has_size = true;
|
st->has_size = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Modified time (files and directories), when the filesystem recorded one.
|
||||||
|
if (!filemanager_is_computer_view(fm) && !filemanager_is_virtual_view(fm) &&
|
||||||
|
fm->entry_mtimes[idx] > 0) {
|
||||||
|
format_datetime(st->modified, sizeof(st->modified),
|
||||||
|
fm->entry_mtimes[idx], montauk::gettz());
|
||||||
|
st->has_modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (props_entry_is_directory_like(fm, idx) && st->has_path) {
|
if (props_entry_is_directory_like(fm, idx) && st->has_path) {
|
||||||
const char* names[256];
|
const char* names[256];
|
||||||
int count = montauk::readdir(st->path, names, 256);
|
int count = montauk::readdir(st->path, names, 256);
|
||||||
@@ -368,6 +378,8 @@ static void props_on_draw(Window* win, Framebuffer& fb) {
|
|||||||
y = props_draw_row(c, theme, pad, y, 82, "Size", st->size);
|
y = props_draw_row(c, theme, pad, y, 82, "Size", st->size);
|
||||||
if (st->has_contains)
|
if (st->has_contains)
|
||||||
y = props_draw_row(c, theme, pad, y, 82, "Contains", st->contains);
|
y = props_draw_row(c, theme, pad, y, 82, "Contains", st->contains);
|
||||||
|
if (st->has_modified)
|
||||||
|
y = props_draw_row(c, theme, pad, y, 82, "Modified", st->modified);
|
||||||
|
|
||||||
Rect close = props_close_button_rect(c.w, c.h);
|
Rect close = props_close_button_rect(c.w, c.h);
|
||||||
mtk::draw_button(c, close, "Close", mtk::BUTTON_PRIMARY,
|
mtk::draw_button(c, close, "Close", mtk::BUTTON_PRIMARY,
|
||||||
@@ -932,7 +944,7 @@ void filemanager_show_properties(FileManagerState* fm, int target_idx) {
|
|||||||
props_populate(st, fm, target_idx);
|
props_populate(st, fm, target_idx);
|
||||||
|
|
||||||
int w = 420;
|
int w = 420;
|
||||||
int h = 300;
|
int h = 330;
|
||||||
int wx = 0;
|
int wx = 0;
|
||||||
int wy = 0;
|
int wy = 0;
|
||||||
props_center_dialog(ds, fm, w, h, &wx, &wy);
|
props_center_dialog(ds, fm, w, h, &wx, &wy);
|
||||||
|
|||||||
Reference in New Issue
Block a user