diff --git a/apps/file_manager/folder.cpp b/apps/file_manager/folder.cpp index 32f915d..96658c9 100644 --- a/apps/file_manager/folder.cpp +++ b/apps/file_manager/folder.cpp @@ -489,6 +489,10 @@ void FolderPathMoved(String oldPath, String newPath, bool saveConfiguration) { } } + for (uintptr_t i = 0; i < openDocuments.Length(); i++) { + PathReplacePrefix(&openDocuments[i], oldPath, newPath); + } + for (uintptr_t i = 0; i < bookmarks.Length(); i++) { PathReplacePrefix(&bookmarks[i], oldPath, newPath); } diff --git a/apps/file_manager/main.cpp b/apps/file_manager/main.cpp index 4cfc718..9b3e0ff 100644 --- a/apps/file_manager/main.cpp +++ b/apps/file_manager/main.cpp @@ -248,6 +248,8 @@ Array bookmarks; Array folderViewSettings; HashStore thumbnailCache; +Array openDocuments; + // Styles. const EsStyle styleFolderView = { @@ -541,9 +543,14 @@ void _start() { FolderDestroy(loadedFolders[i]); } + for (uintptr_t i = 0; i < openDocuments.Length(); i++) { + StringDestroy(&openDocuments[i]); + } + EsAssert(!instances.Length()); EsHeapFree(fileTypesBuffer.out); + openDocuments.Free(); bookmarks.Free(); drives.Free(); folderViewSettings.Free(); @@ -593,6 +600,34 @@ void _start() { } EsHeapFree(data); + } else if (message->type == ES_MSG_FILE_MANAGER_DOCUMENT_UPDATE) { + EsBuffer buffer = { .canGrow = true }; + _EsOpenDocumentEnumerate(&buffer); + + size_t count = 0; + EsBufferReadInto(&buffer, &count, sizeof(size_t)); + + for (uintptr_t i = 0; i < openDocuments.Length(); i++) { + StringDestroy(&openDocuments[i]); + } + + openDocuments.Free(); + + for (uintptr_t i = 0; i < count; i++) { + size_t pathBytes = 0; + EsBufferReadInto(&buffer, &pathBytes, sizeof(size_t)); + char *path = (char *) EsHeapAllocate(pathBytes, false); + EsBufferReadInto(&buffer, path, pathBytes); + String string = { .text = path, .bytes = pathBytes, .allocated = pathBytes }; + openDocuments.Add(string); + } + + for (uintptr_t i = 0; i < instances.Length(); i++) { + EsListViewInvalidateAll(instances[i]->list); + } + + EsHeapFree(buffer.out); + EsAssert(!buffer.error); } else if (message->type == MESSAGE_BLOCKING_TASK_COMPLETE) { Instance *instance = (Instance *) message->user.context1.p; if (message->user.context2.u == instance->blockingTaskID) BlockingTaskComplete(instance); diff --git a/apps/file_manager/string.cpp b/apps/file_manager/string.cpp index 962b3f3..4197174 100644 --- a/apps/file_manager/string.cpp +++ b/apps/file_manager/string.cpp @@ -201,7 +201,7 @@ String PathGetDrive(String path) { bool PathHasPrefix(String path, String prefix) { prefix = PathRemoveTrailingSlash(prefix); - return StringStartsWith(path, prefix) && path.bytes > prefix.bytes && path.text[prefix.bytes] == '/'; + return StringStartsWith(path, prefix) && ((path.bytes > prefix.bytes && path.text[prefix.bytes] == '/') || (path.bytes == prefix.bytes)); } bool PathReplacePrefix(String *knownPath, String oldPath, String newPath) { diff --git a/apps/file_manager/ui.cpp b/apps/file_manager/ui.cpp index 6af6740..1ac18a0 100644 --- a/apps/file_manager/ui.cpp +++ b/apps/file_manager/ui.cpp @@ -138,7 +138,7 @@ bool InstanceLoadFolder(Instance *instance, String path /* takes ownership */, i } else { // TODO Get default from configuration. instance->viewSettings.sortColumn = COLUMN_NAME; - instance->viewSettings.viewType = VIEW_DETAILS; + instance->viewSettings.viewType = VIEW_TILES; } } @@ -753,9 +753,29 @@ int ListCallback(EsElement *element, EsMessage *message) { FolderEntry *entry = listEntry->entry; FileType *fileType = FolderEntryGetType(instance->folder, entry); String name = entry->GetName(); - EsBufferFormat(message->getContent.buffer, "%s\n\a2]%s " HYPHENATION_POINT " %D", name.bytes, name.text, fileType->nameBytes, fileType->name, entry->size); + bool isOpen = false; + + { + // Check if the file is an open document. + // TODO Is this slow? + + String path = instance->folder->itemHandler->getPathForChild(instance->folder, entry); + + for (uintptr_t i = 0; i < openDocuments.Length(); i++) { + if (StringEquals(openDocuments[i], path)) { + isOpen = true; + break; + } + } + + StringDestroy(&path); + } + + EsBufferFormat(message->getContent.buffer, "%z%s\n\a2w4]%s " HYPHENATION_POINT " %D", + isOpen ? "\aw6]" : "", + name.bytes, name.text, fileType->nameBytes, fileType->name, entry->size); message->getContent.icon = fileType->iconID; - message->getContent.richText = true; + message->getContent.drawContentFlags = ES_DRAW_CONTENT_RICH_TEXT; } else if (message->type == ES_MSG_LIST_VIEW_SELECT_RANGE) { for (intptr_t i = message->selectRange.fromIndex; i <= message->selectRange.toIndex; i++) { ListEntry *entry = &instance->listContents[i]; diff --git a/desktop/api.cpp b/desktop/api.cpp index ab831d3..53091ae 100644 --- a/desktop/api.cpp +++ b/desktop/api.cpp @@ -61,6 +61,7 @@ struct EnumString { const char *cName; int value; }; #define DESKTOP_MSG_RENAME (18) #define DESKTOP_MSG_SET_MODIFIED (19) #define DESKTOP_MSG_QUERY_OPEN_DOCUMENT (20) +#define DESKTOP_MSG_LIST_OPEN_DOCUMENTS (21) struct EsFileStore { #define FILE_STORE_HANDLE (1) @@ -634,6 +635,11 @@ void EsOpenDocumentQueryInformation(const char *path, ptrdiff_t pathBytes, EsOpe } } +void _EsOpenDocumentEnumerate(EsBuffer *outputBuffer) { + uint8_t m = DESKTOP_MSG_LIST_OPEN_DOCUMENTS; + MessageDesktop(&m, 1, ES_INVALID_HANDLE, outputBuffer); +} + void EsApplicationRunTemporary(EsInstance *instance, const char *path, ptrdiff_t pathBytes) { if (pathBytes == -1) pathBytes = EsCStringLength(path); char *buffer = (char *) EsHeapAllocate(pathBytes + 1, false); diff --git a/desktop/desktop.cpp b/desktop/desktop.cpp index b741c6e..51d1564 100644 --- a/desktop/desktop.cpp +++ b/desktop/desktop.cpp @@ -490,6 +490,13 @@ void DesktopInspectorThread(EsGeneric) { EsTextDisplayCreate(panel, ES_CELL_H_FILL, &styleSmallParagraph, buffer, bytes); } + for (uintptr_t i = 0; i < desktop.openDocuments.Count(); i++) { + OpenDocument *document = &desktop.openDocuments[i]; + bytes = EsStringFormat(buffer, sizeof(buffer), "doc: '%s', id %d, refs %d", + document->pathBytes, document->path, document->id, document->referenceCount); + EsTextDisplayCreate(panel, ES_CELL_H_FILL, &styleSmallParagraph, buffer, bytes); + } + EsMessageMutexRelease(); EsSleep(500); } @@ -1896,6 +1903,13 @@ void ApplicationProcessTerminated(EsObjectID pid) { // Document management: ////////////////////////////////////////////////////// +void OpenDocumentListUpdated() { + if (desktop.fileManager && desktop.fileManager->singleProcess) { + EsMessage m = { .type = ES_MSG_FILE_MANAGER_DOCUMENT_UPDATE }; + EsMessagePostRemote(desktop.fileManager->singleProcess->handle, &m); + } +} + void OpenDocumentCloseReference(EsObjectID id) { OpenDocument *document = desktop.openDocuments.Get(&id); EsAssert(document->referenceCount && document->referenceCount < 0x10000000 /* sanity check */); @@ -1905,6 +1919,7 @@ void OpenDocumentCloseReference(EsObjectID id) { EsHeapFree(document->temporarySavePath); EsHandleClose(document->readHandle); desktop.openDocuments.Delete(&id); + OpenDocumentListUpdated(); } void OpenDocumentOpenReference(EsObjectID id) { @@ -1955,6 +1970,8 @@ void OpenDocumentWithApplication(EsApplicationStartupRequest *startupRequest) { startupInformation.readHandle = document.readHandle; startupInformation.documentID = document.id; + + OpenDocumentListUpdated(); } ApplicationInstanceCreate(startupInformation.id, &startupInformation, nullptr); @@ -2041,6 +2058,8 @@ void ApplicationInstanceRequestSave(ApplicationInstance *instance, const char *n EsMessagePostRemote(instance->process->handle, &m); EsHeapFree(data); } + + OpenDocumentListUpdated(); } OpenDocument *document = desktop.openDocuments.Get(&instance->documentID); @@ -2155,7 +2174,7 @@ void ApplicationInstanceCompleteSave(ApplicationInstance *fromInstance) { document->currentWriter = 0; - if (desktop.fileManager->singleProcess) { + if (desktop.fileManager && desktop.fileManager->singleProcess) { EsMessage m = {}; m.type = ES_MSG_FILE_MANAGER_FILE_MODIFIED; m.user.context1 = EsConstantBufferCreate(document->path, document->pathBytes, desktop.fileManager->singleProcess->handle); @@ -2732,6 +2751,19 @@ void DesktopSyscall(EsMessage *message, uint8_t *buffer, EsBuffer *pipe) { EsBufferWrite(pipe, &information, sizeof(information)); } + } else if (buffer[0] == DESKTOP_MSG_LIST_OPEN_DOCUMENTS) { + InstalledApplication *application = ApplicationFindByPID(message->desktop.processID); + + if (application && (application->permissions & APPLICATION_PERMISSION_ALL_FILES)) { + size_t count = desktop.openDocuments.Count(); + EsBufferWrite(pipe, &count, sizeof(size_t)); + + for (uintptr_t i = 0; i < count; i++) { + OpenDocument *document = &desktop.openDocuments[i]; + EsBufferWrite(pipe, &document->pathBytes, sizeof(size_t)); + EsBufferWrite(pipe, document->path, document->pathBytes); + } + } } else if (!instance) { // ------------------------------------------------- // | Messages below here require a valid instance. | diff --git a/desktop/list_view.cpp b/desktop/list_view.cpp index 3141aae..5669988 100644 --- a/desktop/list_view.cpp +++ b/desktop/list_view.cpp @@ -1190,8 +1190,7 @@ struct EsListView : EsElement { EsDrawContent(message->painter, element, element->GetBounds(), (char *) _buffer, buffer.position, m.getContent.icon, - m.getContent.richText ? ES_DRAW_CONTENT_RICH_TEXT : ES_FLAGS_DEFAULT, - &selection); + m.getContent.drawContentFlags, &selection); } } else if (message->type == ES_MSG_LAYOUT) { if (element->GetChildCount()) { diff --git a/desktop/os.header b/desktop/os.header index fc75dd0..6a6e455 100644 --- a/desktop/os.header +++ b/desktop/os.header @@ -1004,6 +1004,7 @@ enum EsMessageType { // File Manager messages: ES_MSG_FILE_MANAGER_FILE_MODIFIED = 0x5100 ES_MSG_FILE_MANAGER_PATH_MOVED = 0x5101 + ES_MSG_FILE_MANAGER_DOCUMENT_UPDATE = 0x5102 // The managed list of open documents has been updated. // Textbox messages: ES_MSG_TEXTBOX_UPDATED = 0x5200 @@ -1632,8 +1633,8 @@ struct EsMessageGetContent { EsListViewIndex index; EsListViewIndex group; uint32_t icon; + uint32_t drawContentFlags; EsBuffer *buffer; - bool richText; uint8_t column; }; @@ -2005,8 +2006,9 @@ function void *EsFileStoreMap(EsFileStore *file, size_t *fileSize, uint32_t flag // These calls require permission_all_files. function bool EsMountPointGetVolumeInformation(const char *prefix, size_t prefixBytes, EsVolumeInformation *information); // Returns false if the mount point does not exist. function void EsMountPointEnumerate(EsMountPointEnumerationCallback callback, EsGeneric context); -function void _EsPathAnnouncePathMoved(STRING oldPath, STRING newPath); function void EsOpenDocumentQueryInformation(STRING filePath, EsOpenDocumentInformation *information); +function void _EsPathAnnouncePathMoved(STRING oldPath, STRING newPath); +function void _EsOpenDocumentEnumerate(EsBuffer *outputBuffer); function void EsDeviceEnumerate(EsDeviceEnumerationCallback callback, EsGeneric context); function EsError EsDeviceControl(EsHandle handle, EsDeviceControlType type, void *dp, void *dq); diff --git a/util/api_table.ini b/util/api_table.ini index 4f9c27d..679c656 100644 --- a/util/api_table.ini +++ b/util/api_table.ini @@ -487,3 +487,4 @@ EsScrollViewGetLimit=485 EsScrollViewSetFixedViewport=486 EsScrollViewIsBarEnabled=487 EsScrollViewIsInDragScroll=488 +_EsOpenDocumentEnumerate=489