diff --git a/apps/file_manager/main.cpp b/apps/file_manager/main.cpp index 83fcb7e..a05c825 100644 --- a/apps/file_manager/main.cpp +++ b/apps/file_manager/main.cpp @@ -590,6 +590,29 @@ void _start() { EsHandleClose(message->user.context1.u); EsHeapFree(_path); + } else if (message->type == ES_MSG_FILE_MANAGER_PATH_MOVED) { + char *data = (char *) EsHeapAllocate(message->user.context2.u, false); + uintptr_t *bytes = (uintptr_t *) data; + char *paths = data + sizeof(size_t) * 2; + EsConstantBufferRead(message->user.context1.u, data); + String oldPath = StringFromLiteralWithSize(paths, bytes[0]); + String newPath = StringFromLiteralWithSize(paths + bytes[0], bytes[1]); + FolderPathMoved(oldPath, newPath, false); + size_t pathSectionCount; + + pathSectionCount = PathCountSections(oldPath); + + for (uintptr_t i = 0; i < pathSectionCount; i++) { + FolderFileUpdatedAtPath(PathGetParent(oldPath, i + 1), nullptr); + } + + pathSectionCount = PathCountSections(newPath); + + for (uintptr_t i = 0; i < pathSectionCount; i++) { + FolderFileUpdatedAtPath(PathGetParent(newPath, i + 1), nullptr); + } + + EsHeapFree(data); } 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/desktop/api.cpp b/desktop/api.cpp index 75bc832..3e24bd5 100644 --- a/desktop/api.cpp +++ b/desktop/api.cpp @@ -63,6 +63,7 @@ struct EnumString { const char *cName; int value; }; #define DESKTOP_MSG_UNHANDLED_KEY_EVENT (15) #define DESKTOP_MSG_START_USER_TASK (16) #define DESKTOP_MSG_SET_PROGRESS (17) +#define DESKTOP_MSG_RENAME (18) struct EsFileStore { #define FILE_STORE_HANDLE (1) @@ -218,6 +219,11 @@ struct APIInstance { EsInstanceClassEditorSettings editorSettings; EsInstanceClassViewerSettings viewerSettings; }; + + // For the file menu. + EsPanel *fileMenuNameSwitcher; + EsPanel *fileMenuNamePanel; + EsTextbox *fileMenuNameTextbox; }; MountPoint *NodeAddMountPoint(const char *prefix, size_t prefixBytes, EsHandle base, bool queryInformation) { @@ -982,6 +988,41 @@ EsMessage *EsMessageReceive() { EsHandleClose(message.message.tabOperation.handle); } } + } else if (type == ES_MSG_INSTANCE_RENAME_RESPONSE) { + EsInstance *instance = InstanceFromWindowID(message.message.tabOperation.id); + + if (instance) { + if (message.message.tabOperation.error == ES_SUCCESS) { + EsRectangle bounds = EsElementGetWindowBounds(instance->window->toolbarSwitcher); + EsAnnouncementShow(instance->window, ES_FLAGS_DEFAULT, (bounds.l + bounds.r) / 2, bounds.b, INTERFACE_STRING(FileRenameSuccess)); + } else { + const char *errorMessage = interfaceString_FileSaveErrorUnknown; + + switch (message.message.tabOperation.error) { + case ES_ERROR_FILE_DOES_NOT_EXIST: + case ES_ERROR_NODE_DELETED: + case ES_ERROR_PERMISSION_NOT_GRANTED: + case ES_ERROR_INCORRECT_NODE_TYPE: + errorMessage = interfaceString_FileSaveErrorFileDeleted; + break; + case ES_ERROR_DRIVE_ERROR_FILE_DAMAGED: + errorMessage = interfaceString_FileSaveErrorCorrupt; + break; + case ES_ERROR_DRIVE_CONTROLLER_REPORTED: + errorMessage = interfaceString_FileSaveErrorDrive; + break; + case ES_ERROR_INSUFFICIENT_RESOURCES: + errorMessage = interfaceString_FileSaveErrorResourcesLow; + break; + case ES_ERROR_FILE_ALREADY_EXISTS: + errorMessage = interfaceString_FileSaveErrorAlreadyExists; + break; + } + + EsDialogShowAlert(instance->window, INTERFACE_STRING(FileCannotRename), + errorMessage, -1, ES_ICON_DIALOG_ERROR, ES_DIALOG_ALERT_OK_BUTTON); + } + } } else if (type == ES_MSG_INSTANCE_DOCUMENT_RENAMED) { char *buffer = (char *) EsHeapAllocate(message.message.tabOperation.bytes, false); @@ -1202,6 +1243,9 @@ void EsInstanceSaveComplete(EsMessage *message, bool success) { case ES_ERROR_FILE_ALREADY_EXISTS: errorMessage = interfaceString_FileSaveErrorAlreadyExists; break; + case ES_ERROR_TOO_MANY_FILES_WITH_NAME: + errorMessage = interfaceString_FileSaveErrorTooManyFiles; + break; } EsDialogShowAlert(instance->window, INTERFACE_STRING(FileCannotSave), diff --git a/desktop/desktop.cpp b/desktop/desktop.cpp index 2694e82..66032ff 100644 --- a/desktop/desktop.cpp +++ b/desktop/desktop.cpp @@ -1624,7 +1624,7 @@ EsError TemporaryFileCreate(EsHandle *handle, char **path, size_t *pathBytes, ui return file.error; } -void ApplicationInstanceRequestSave(ApplicationInstance *instance, const char *newName, size_t newNameBytes) { +void ApplicationInstanceRequestSave(ApplicationInstance *instance, const char *newName, size_t newNameBytes, bool failIfAlreadyExists) { if (!instance->processHandle) return; EsMessage m = {}; @@ -1640,9 +1640,9 @@ void ApplicationInstanceRequestSave(ApplicationInstance *instance, const char *n EsHeapFree(folder); size_t nameBytes = EsPathFindUniqueName(name, folderBytes + newNameBytes, folderBytes + newNameBytes + 32); - if (!nameBytes) { + if (!nameBytes || (failIfAlreadyExists && nameBytes != folderBytes + newNameBytes)) { EsHeapFree(name); - m.tabOperation.error = ES_ERROR_FILE_ALREADY_EXISTS; + m.tabOperation.error = nameBytes ? ES_ERROR_FILE_ALREADY_EXISTS : ES_ERROR_TOO_MANY_FILES_WITH_NAME; EsMessagePostRemote(instance->processHandle, &m); return; } @@ -1710,22 +1710,10 @@ void ApplicationInstanceRequestSave(ApplicationInstance *instance, const char *n EsMessagePostRemote(instance->processHandle, &m); } -void InstanceAnnouncePathMoved(InstalledApplication *fromApplication, const uint8_t *buffer, size_t embedWindowMessageBytes) { +void InstanceAnnouncePathMoved(InstalledApplication *fromApplication, const char *oldPath, size_t oldPathBytes, const char *newPath, size_t newPathBytes) { // TODO Update the location of installed applications and other things in the configuration. // TODO Replace fromApplication with something better. - uintptr_t oldPathBytes, newPathBytes; - EsMemoryCopy(&oldPathBytes, buffer + 1, sizeof(uintptr_t)); - EsMemoryCopy(&newPathBytes, buffer + 1 + sizeof(uintptr_t), sizeof(uintptr_t)); - - if (oldPathBytes >= 0x4000 || newPathBytes >= 0x4000 - || oldPathBytes + newPathBytes + sizeof(uintptr_t) * 2 + 1 != embedWindowMessageBytes) { - return; - } - - const char *oldPath = (const char *) buffer + 1 + sizeof(uintptr_t) * 2; - const char *newPath = (const char *) buffer + 1 + sizeof(uintptr_t) * 2 + oldPathBytes; - EsObjectID documentID = 0; for (uintptr_t i = 0; i < desktop.openDocuments.Count(); i++) { @@ -1769,6 +1757,20 @@ void InstanceAnnouncePathMoved(InstalledApplication *fromApplication, const uint m.tabOperation.bytes = newPathBytes - newNameOffset; EsMessagePostRemote(instance->processHandle, &m); } + + if (fromApplication != desktop.fileManager && desktop.fileManager && desktop.fileManager->singleProcessHandle) { + char *data = (char *) EsHeapAllocate(sizeof(size_t) * 2 + oldPathBytes + newPathBytes, false); + EsMemoryCopy(data + 0, &oldPathBytes, sizeof(size_t)); + EsMemoryCopy(data + sizeof(size_t), &newPathBytes, sizeof(size_t)); + EsMemoryCopy(data + sizeof(size_t) * 2, oldPath, oldPathBytes); + EsMemoryCopy(data + sizeof(size_t) * 2 + oldPathBytes, newPath, newPathBytes); + EsMessage m = {}; + m.type = ES_MSG_FILE_MANAGER_PATH_MOVED; + m.user.context2 = sizeof(size_t) * 2 + oldPathBytes + newPathBytes; + m.user.context1 = EsConstantBufferCreate(data, m.user.context2.u, desktop.fileManager->singleProcessHandle); + EsMessagePostRemote(desktop.fileManager->singleProcessHandle, &m); + EsHeapFree(data); + } } void ApplicationInstanceCompleteSave(ApplicationInstance *fromInstance) { @@ -2253,7 +2255,19 @@ void DesktopSyscall(EsMessage *message, uint8_t *buffer, EsBuffer *pipe) { InstalledApplication *application = ApplicationFindByPID(message->desktop.processID); if (application && (application->permissions & APPLICATION_PERMISSION_ALL_FILES)) { - InstanceAnnouncePathMoved(application, buffer, message->desktop.bytes); + uintptr_t oldPathBytes, newPathBytes; + EsMemoryCopy(&oldPathBytes, buffer + 1, sizeof(uintptr_t)); + EsMemoryCopy(&newPathBytes, buffer + 1 + sizeof(uintptr_t), sizeof(uintptr_t)); + + if (oldPathBytes >= 0x4000 || newPathBytes >= 0x4000 + || oldPathBytes + newPathBytes + sizeof(uintptr_t) * 2 + 1 != message->desktop.bytes) { + return; + } + + const char *oldPath = (const char *) buffer + 1 + sizeof(uintptr_t) * 2; + const char *newPath = (const char *) buffer + 1 + sizeof(uintptr_t) * 2 + oldPathBytes; + + InstanceAnnouncePathMoved(application, oldPath, oldPathBytes, newPath, newPathBytes); } } else if (buffer[0] == DESKTOP_MSG_START_USER_TASK && pipe) { InstalledApplication *application = ApplicationFindByPID(message->desktop.processID); @@ -2335,7 +2349,39 @@ void DesktopSyscall(EsMessage *message, uint8_t *buffer, EsBuffer *pipe) { EsElementRepaint(desktop.tasksButton); } } else if (buffer[0] == DESKTOP_MSG_REQUEST_SAVE) { - ApplicationInstanceRequestSave(instance, (const char *) buffer + 1, message->desktop.bytes - 1); + ApplicationInstanceRequestSave(instance, (const char *) buffer + 1, message->desktop.bytes - 1, false); + } else if (buffer[0] == DESKTOP_MSG_RENAME) { + const char *newName = (const char *) buffer + 1; + size_t newNameBytes = message->desktop.bytes - 1; + OpenDocument *document = desktop.openDocuments.Get(&instance->documentID); + + if (!instance->documentID) { + ApplicationInstanceRequestSave(instance, newName, newNameBytes, true); + } else if (document) { + size_t folderBytes = 0, oldPathBytes, newPathBytes; + + for (uintptr_t i = 0; i < document->pathBytes; i++) { + if (document->path[i] == '/') { + folderBytes = i; + } + } + + char *oldPath = EsStringAllocateAndFormat(&oldPathBytes, "%s", document->pathBytes, document->path); + char *newPath = EsStringAllocateAndFormat(&newPathBytes, "%s/%s", folderBytes, document->path, newNameBytes, newName); + + EsMessage m = {}; + m.type = ES_MSG_INSTANCE_RENAME_RESPONSE; + m.tabOperation.id = instance->embeddedWindowID; + m.tabOperation.error = EsPathMove(oldPath, oldPathBytes, newPath, newPathBytes); + EsMessagePostRemote(instance->processHandle, &m); + + if (m.tabOperation.error == ES_SUCCESS) { + InstanceAnnouncePathMoved(nullptr, oldPath, oldPathBytes, newPath, newPathBytes); + } + + EsHeapFree(oldPath); + EsHeapFree(newPath); + } } else if (buffer[0] == DESKTOP_MSG_COMPLETE_SAVE) { ApplicationInstanceCompleteSave(instance); } else if (buffer[0] == DESKTOP_MSG_SHOW_IN_FILE_MANAGER) { diff --git a/desktop/gui.cpp b/desktop/gui.cpp index 277ce79..7f1b2b8 100644 --- a/desktop/gui.cpp +++ b/desktop/gui.cpp @@ -1072,105 +1072,6 @@ void EsMenuCloseAll() { } } -// --------------------------------- File menu. - -const EsStyle styleFileMenuDocumentInformationPanel1 = { - .metrics = { - .mask = ES_THEME_METRICS_INSETS | ES_THEME_METRICS_GAP_MAJOR, - .insets = ES_RECT_4(10, 10, 5, 5), - .gapMajor = 5, - }, -}; - -const EsStyle styleFileMenuDocumentInformationPanel2 = { - .metrics = { - .mask = ES_THEME_METRICS_GAP_MAJOR, - .gapMajor = 5, - }, -}; - -void FileMenuCreate(EsInstance *_instance, EsElement *element, EsCommand *) { - // TODO Make this user-customizable? - - // const EsFileMenuSettings *settings = (const EsFileMenuSettings *) element->userData.p; - APIInstance *instance = (APIInstance *) _instance->_private; - EsAssert(instance->instanceClass == ES_INSTANCE_CLASS_EDITOR); - EsInstanceClassEditorSettings *editorSettings = &instance->editorSettings; - bool newDocument = !instance->startupInformation || !instance->startupInformation->filePath; - - EsMenu *menu = EsMenuCreate(element, ES_FLAGS_DEFAULT); - if (!menu) return; - EsPanel *panel1 = EsPanelCreate(menu, ES_PANEL_HORIZONTAL | ES_CELL_H_LEFT, &styleFileMenuDocumentInformationPanel1); - if (!panel1) goto show; - - { - // TODO Get this icon from the file type database? - // We'll probably need Desktop to send this via EsApplicationStartupInformation and when the file is renamed. - - EsIconDisplayCreate(panel1, ES_FLAGS_DEFAULT, 0, editorSettings->documentIconID); - EsSpacerCreate(panel1, ES_FLAGS_DEFAULT, 0, 5, 0); - - EsPanel *panel2 = EsPanelCreate(panel1, ES_FLAGS_DEFAULT, &styleFileMenuDocumentInformationPanel2); - if (!panel2) goto show; - EsPanel *panel3 = EsPanelCreate(panel2, ES_PANEL_HORIZONTAL | ES_PANEL_H_LEFT, &styleFileMenuDocumentInformationPanel2); - if (!panel3) goto show; - - if (newDocument) { - EsTextDisplayCreate(panel3, ES_FLAGS_DEFAULT, ES_STYLE_TEXT_LABEL, - editorSettings->newDocumentTitle, editorSettings->newDocumentTitleBytes); - } else { - EsTextDisplayCreate(panel3, ES_FLAGS_DEFAULT, ES_STYLE_TEXT_LABEL, - instance->startupInformation->filePath, instance->startupInformation->filePathBytes); - } - - EsButton *renameButton = EsButtonCreate(panel3, ES_BUTTON_TOOLBAR); // TODO. - if (!renameButton) goto show; - EsButtonSetIcon(renameButton, ES_ICON_DOCUMENT_EDIT_SYMBOLIC); - - if (!newDocument) { - EsPanel *panel4 = EsPanelCreate(panel2, ES_PANEL_TABLE | ES_PANEL_HORIZONTAL | ES_CELL_H_LEFT, &styleFileMenuDocumentInformationPanel2); - if (!panel4) goto show; - EsPanelSetBands(panel4, 2 /* columns */); - - char buffer[64]; - size_t bytes; - - bytes = EsStringFormat(buffer, sizeof(buffer), "%D", EsFileStoreGetSize(instance->fileStore)); - EsTextDisplayCreate(panel4, ES_CELL_H_RIGHT, ES_STYLE_TEXT_LABEL_SECONDARY, INTERFACE_STRING(CommonFileMenuFileSize)); - EsTextDisplayCreate(panel4, ES_CELL_H_LEFT, ES_STYLE_TEXT_LABEL, buffer, bytes); - - // TODO Modification date, author, etc. - } - } - - EsMenuAddSeparator(menu); - - if (instance->instanceClass == ES_INSTANCE_CLASS_EDITOR) { - if (instance->commandSave.disabled) { - EsMenuAddItem(menu, ES_ELEMENT_DISABLED, INTERFACE_STRING(CommonFileUnchanged)); - } else { - EsMenuAddCommand(menu, ES_FLAGS_DEFAULT, INTERFACE_STRING(CommonFileSave), &instance->commandSave); - } - - EsMenuAddItem(menu, newDocument ? ES_ELEMENT_DISABLED : ES_FLAGS_DEFAULT, INTERFACE_STRING(CommonFileMakeCopy)); // TODO. - EsMenuAddSeparator(menu); - } - - EsMenuAddItem(menu, newDocument ? ES_ELEMENT_DISABLED : ES_FLAGS_DEFAULT, INTERFACE_STRING(CommonFileShare)); // TODO. - EsMenuAddItem(menu, newDocument ? ES_ELEMENT_DISABLED : ES_FLAGS_DEFAULT, INTERFACE_STRING(CommonFileVersionHistory)); // TODO. - EsMenuAddCommand(menu, ES_FLAGS_DEFAULT, INTERFACE_STRING(CommonFileShowInFileManager), &instance->commandShowInFileManager); - - show: EsMenuShow(menu); -} - -void EsToolbarAddFileMenu(EsElement *element, const EsFileMenuSettings *settings) { - EsButton *button = EsButtonCreate(element, ES_BUTTON_DROPDOWN, 0, INTERFACE_STRING(CommonFileMenu)); - if (!button) return; - button->accessKey = 'F'; - button->userData = (void *) settings; - EsButtonOnCommand(button, FileMenuCreate); -} - // --------------------------------- Paint targets. bool EsPaintTargetTake(EsPaintTarget *target, size_t width, size_t height, bool hasAlphaChannel = true) { @@ -3132,7 +3033,8 @@ int ProcessPanelMessage(EsElement *element, EsMessage *message) { for (uintptr_t i = 0; i < element->GetChildCount(); i++) { EsElement *child = element->GetChild(i); - if (child->flags & (ES_ELEMENT_HIDDEN | ES_ELEMENT_NON_CLIENT)) continue; + if (child->flags & ES_ELEMENT_NON_CLIENT) continue; + if ((child->flags & ES_ELEMENT_HIDDEN) && (~panel->flags & ES_PANEL_SWITCHER_MEASURE_LARGEST)) continue; int size = child->GetWidth(message->measure.height); if (size > maximum) maximum = size; } @@ -3153,7 +3055,8 @@ int ProcessPanelMessage(EsElement *element, EsMessage *message) { for (uintptr_t i = 0; i < element->GetChildCount(); i++) { EsElement *child = element->GetChild(i); - if (child->flags & (ES_ELEMENT_HIDDEN | ES_ELEMENT_NON_CLIENT)) continue; + if (child->flags & ES_ELEMENT_NON_CLIENT) continue; + if ((child->flags & ES_ELEMENT_HIDDEN) && (~panel->flags & ES_PANEL_SWITCHER_MEASURE_LARGEST)) continue; int size = child->GetHeight(message->measure.width); if (size > maximum) maximum = size; } @@ -5237,6 +5140,183 @@ EsSlider *EsSliderCreate(EsElement *parent, uint64_t flags, const EsStyle *style return slider; } +// --------------------------------- File menu. + +const EsStyle styleFileMenuDocumentInformationPanel1 = { + .metrics = { + .mask = ES_THEME_METRICS_INSETS | ES_THEME_METRICS_GAP_MAJOR, + .insets = ES_RECT_4(10, 10, 5, 5), + .gapMajor = 5, + }, +}; + +const EsStyle styleFileMenuDocumentInformationPanel2 = { + .metrics = { + .mask = ES_THEME_METRICS_GAP_MAJOR, + .gapMajor = 5, + }, +}; + +const EsStyle styleFileMenuNameTextbox = { + .inherit = ES_STYLE_TEXTBOX_TRANSPARENT, + + .metrics = { + .mask = ES_THEME_METRICS_PREFERRED_WIDTH, + .preferredWidth = 0, + }, +}; + +int FileMenuNameTextboxMessage(EsElement *element, EsMessage *message) { + if (message->type == ES_MSG_TEXTBOX_EDIT_END) { + APIInstance *instance = (APIInstance *) element->instance->_private; + + if (!message->endEdit.rejected) { + size_t newNameBytes; + char *newName = EsTextboxGetContents(instance->fileMenuNameTextbox, &newNameBytes); + uint8_t *buffer = (uint8_t *) EsHeapAllocate(1 + newNameBytes, false); + buffer[0] = DESKTOP_MSG_RENAME; + EsMemoryCopy(buffer + 1, newName, newNameBytes); + MessageDesktop(buffer, 1 + newNameBytes, element->instance->window->handle); + EsHeapFree(buffer); + EsHeapFree(newName); + EsElementDestroy(element->window); + } else { + EsPanelSwitchTo(instance->fileMenuNameSwitcher, instance->fileMenuNamePanel, ES_TRANSITION_SLIDE_DOWN); + } + + return ES_HANDLED; + } + + return 0; +} + +void FileMenuRename(EsInstance *_instance, EsElement *, EsCommand *) { + APIInstance *instance = (APIInstance *) _instance->_private; + EsTextboxClear(instance->fileMenuNameTextbox, false); + + uintptr_t extensionOffset = 0; + const char *initialName = nullptr; + ptrdiff_t initialNameBytes = 0; + + if (instance->startupInformation && instance->startupInformation->filePathBytes) { + initialName = instance->startupInformation->filePath; + initialNameBytes = instance->startupInformation->filePathBytes; + } else { + EsInstanceClassEditorSettings *editorSettings = &instance->editorSettings; + initialName = editorSettings->newDocumentFileName; + initialNameBytes = editorSettings->newDocumentFileNameBytes; + } + + if (initialNameBytes == -1) { + initialNameBytes = EsCStringLength(initialName); + } + + EsTextboxInsert(instance->fileMenuNameTextbox, initialName, initialNameBytes, false); + + for (intptr_t i = 1; i < initialNameBytes; i++) { + if (initialName[i] == '.') { + extensionOffset = i; + } + } + + EsPanelSwitchTo(instance->fileMenuNameSwitcher, instance->fileMenuNameTextbox, ES_TRANSITION_SLIDE_UP); + EsElementFocus(instance->fileMenuNameTextbox); + EsTextboxStartEdit(instance->fileMenuNameTextbox); + if (extensionOffset) EsTextboxSetSelection(instance->fileMenuNameTextbox, 0, 0, 0, extensionOffset); + instance->fileMenuNameTextbox->messageUser = FileMenuNameTextboxMessage; +} + +void FileMenuCreate(EsInstance *_instance, EsElement *element, EsCommand *) { + // TODO Make this user-customizable? + + // const EsFileMenuSettings *settings = (const EsFileMenuSettings *) element->userData.p; + APIInstance *instance = (APIInstance *) _instance->_private; + EsAssert(instance->instanceClass == ES_INSTANCE_CLASS_EDITOR); + EsInstanceClassEditorSettings *editorSettings = &instance->editorSettings; + bool newDocument = !instance->startupInformation || !instance->startupInformation->filePath; + + EsMenu *menu = EsMenuCreate(element, ES_FLAGS_DEFAULT); + if (!menu) return; + EsPanel *panel1 = EsPanelCreate(menu, ES_PANEL_HORIZONTAL | ES_CELL_H_LEFT, &styleFileMenuDocumentInformationPanel1); + if (!panel1) goto show; + + { + // TODO Get this icon from the file type database? + // We'll probably need Desktop to send this via EsApplicationStartupInformation and when the file is renamed. + + EsIconDisplayCreate(panel1, ES_FLAGS_DEFAULT, 0, editorSettings->documentIconID); + EsSpacerCreate(panel1, ES_FLAGS_DEFAULT, 0, 5, 0); + + EsPanel *panel2 = EsPanelCreate(panel1, ES_FLAGS_DEFAULT, &styleFileMenuDocumentInformationPanel2); + if (!panel2) goto show; + EsPanel *switcher = EsPanelCreate(panel2, ES_PANEL_H_LEFT | ES_PANEL_SWITCHER | ES_PANEL_SWITCHER_MEASURE_LARGEST); + if (!switcher) goto show; + EsPanel *panel3 = EsPanelCreate(switcher, ES_PANEL_HORIZONTAL | ES_PANEL_H_LEFT, &styleFileMenuDocumentInformationPanel2); + if (!panel3) goto show; + + instance->fileMenuNameTextbox = EsTextboxCreate(switcher, ES_CELL_H_FILL | ES_TEXTBOX_EDIT_BASED, &styleFileMenuNameTextbox); + + instance->fileMenuNameSwitcher = switcher; + instance->fileMenuNamePanel = panel3; + EsPanelSwitchTo(instance->fileMenuNameSwitcher, instance->fileMenuNamePanel, ES_TRANSITION_NONE); + + if (newDocument) { + EsTextDisplayCreate(panel3, ES_FLAGS_DEFAULT, ES_STYLE_TEXT_LABEL, + editorSettings->newDocumentTitle, editorSettings->newDocumentTitleBytes); + } else { + EsTextDisplayCreate(panel3, ES_FLAGS_DEFAULT, ES_STYLE_TEXT_LABEL, + instance->startupInformation->filePath, instance->startupInformation->filePathBytes); + } + + EsButton *renameButton = EsButtonCreate(panel3, ES_BUTTON_TOOLBAR); // TODO. + if (!renameButton) goto show; + EsButtonSetIcon(renameButton, ES_ICON_DOCUMENT_EDIT_SYMBOLIC); + EsButtonOnCommand(renameButton, FileMenuRename); + + if (!newDocument) { + EsPanel *panel4 = EsPanelCreate(panel2, ES_PANEL_TABLE | ES_PANEL_HORIZONTAL | ES_CELL_H_LEFT, &styleFileMenuDocumentInformationPanel2); + if (!panel4) goto show; + EsPanelSetBands(panel4, 2 /* columns */); + + char buffer[64]; + size_t bytes; + + bytes = EsStringFormat(buffer, sizeof(buffer), "%D", EsFileStoreGetSize(instance->fileStore)); + EsTextDisplayCreate(panel4, ES_CELL_H_RIGHT, ES_STYLE_TEXT_LABEL_SECONDARY, INTERFACE_STRING(CommonFileMenuFileSize)); + EsTextDisplayCreate(panel4, ES_CELL_H_LEFT, ES_STYLE_TEXT_LABEL, buffer, bytes); + + // TODO Modification date, author, etc. + } + } + + EsMenuAddSeparator(menu); + + if (instance->instanceClass == ES_INSTANCE_CLASS_EDITOR) { + if (instance->commandSave.disabled) { + EsMenuAddItem(menu, ES_ELEMENT_DISABLED, INTERFACE_STRING(CommonFileUnchanged)); + } else { + EsMenuAddCommand(menu, ES_FLAGS_DEFAULT, INTERFACE_STRING(CommonFileSave), &instance->commandSave); + } + + EsMenuAddItem(menu, newDocument ? ES_ELEMENT_DISABLED : ES_FLAGS_DEFAULT, INTERFACE_STRING(CommonFileMakeCopy)); // TODO. + EsMenuAddSeparator(menu); + } + + EsMenuAddItem(menu, newDocument ? ES_ELEMENT_DISABLED : ES_FLAGS_DEFAULT, INTERFACE_STRING(CommonFileShare)); // TODO. + EsMenuAddItem(menu, newDocument ? ES_ELEMENT_DISABLED : ES_FLAGS_DEFAULT, INTERFACE_STRING(CommonFileVersionHistory)); // TODO. + EsMenuAddCommand(menu, ES_FLAGS_DEFAULT, INTERFACE_STRING(CommonFileShowInFileManager), &instance->commandShowInFileManager); + + show: EsMenuShow(menu); +} + +void EsToolbarAddFileMenu(EsElement *element, const EsFileMenuSettings *settings) { + EsButton *button = EsButtonCreate(element, ES_BUTTON_DROPDOWN, 0, INTERFACE_STRING(CommonFileMenu)); + if (!button) return; + button->accessKey = 'F'; + button->userData = (void *) settings; + EsButtonOnCommand(button, FileMenuCreate); +} + // --------------------------------- Message loop and core UI infrastructure. void EsElement::PrintTree(int depth) { diff --git a/desktop/os.header b/desktop/os.header index e550a0d..cd02ecb 100644 --- a/desktop/os.header +++ b/desktop/os.header @@ -331,6 +331,7 @@ define ES_ERROR_DIRECTORY_ENTRY_BEING_REMOVED (-72) define ES_ERROR_CANCELLED (-73) define ES_ERROR_BLOCK_ACCESS_INVALID (-74) define ES_ERROR_DEVICE_REMOVED (-75) +define ES_ERROR_TOO_MANY_FILES_WITH_NAME (-76) define ES_INVALID_HANDLE ((EsHandle) (0)) define ES_CURRENT_THREAD ((EsHandle) (0x10)) @@ -468,6 +469,10 @@ define ES_PANEL_VERTICAL (0) // Default. define ES_PANEL_HORIZONTAL (1 << 8) define ES_PANEL_REVERSE (1 << 9) // Reverse layout is not supported with ES_PANEL_TABLE yet. +// For ES_PANEL_SWITCHER. +define ES_PANEL_SWITCHER_MEASURE_SHOWN (0 << 15) // Use the shown child to determine size. +define ES_PANEL_SWITCHER_MEASURE_LARGEST (1 << 15) // Use the largest child to determine size. + // For ES_PANEL_TABLE. // TODO Implement these! define ES_PANEL_H_LEFT (1 << 16) @@ -968,6 +973,7 @@ enum EsMessageType { ES_MSG_INSTANCE_DOCUMENT_RENAMED = 0x4A04 ES_MSG_INSTANCE_DOCUMENT_UPDATED = 0x4A05 ES_MSG_PRIMARY_CLIPBOARD_UPDATED = 0x4A06 + ES_MSG_INSTANCE_RENAME_RESPONSE = 0x4A07 // Debugger messages: ES_MSG_APPLICATION_CRASH = 0x4C00 @@ -986,6 +992,7 @@ enum EsMessageType { // File Manager messages: ES_MSG_FILE_MANAGER_FILE_MODIFIED = 0x5100 + ES_MSG_FILE_MANAGER_PATH_MOVED = 0x5101 // Textbox messages: ES_MSG_TEXTBOX_UPDATED = 0x5200 diff --git a/shared/strings.cpp b/shared/strings.cpp index 610ac0a..85e5233 100644 --- a/shared/strings.cpp +++ b/shared/strings.cpp @@ -141,6 +141,9 @@ DEFINE_INTERFACE_STRING(DesktopSettingsThemeWallpaper, "Wallpaper"); DEFINE_INTERFACE_STRING(FileCannotSave, "The document was not saved."); DEFINE_INTERFACE_STRING(FileCannotOpen, "The file could not be opened."); +DEFINE_INTERFACE_STRING(FileCannotRename, "The file could not be renamed."); + +DEFINE_INTERFACE_STRING(FileRenameSuccess, "Renamed"); DEFINE_INTERFACE_STRING(FileSaveErrorFileDeleted, "Another application deleted the file."); DEFINE_INTERFACE_STRING(FileSaveErrorCorrupt, "The file has been corrupted, and it cannot be modified."); @@ -149,7 +152,8 @@ DEFINE_INTERFACE_STRING(FileSaveErrorTooLarge, "The drive does not support files DEFINE_INTERFACE_STRING(FileSaveErrorConcurrentAccess, "Another application is modifying the file."); DEFINE_INTERFACE_STRING(FileSaveErrorDriveFull, "The drive is full. Try deleting some files to free up space."); DEFINE_INTERFACE_STRING(FileSaveErrorResourcesLow, "The system is low on resources. Close some applcations and try again."); -DEFINE_INTERFACE_STRING(FileSaveErrorAlreadyExists, "Too many files already have the same name."); +DEFINE_INTERFACE_STRING(FileSaveErrorAlreadyExists, "There is already a file with this name."); +DEFINE_INTERFACE_STRING(FileSaveErrorTooManyFiles, "Too many files already have the same name."); DEFINE_INTERFACE_STRING(FileSaveErrorUnknown, "An unknown error occurred. Please try again later."); DEFINE_INTERFACE_STRING(FileLoadErrorCorrupt, "The file has been corrupted, and it cannot be opened.");