mirror of https://gitlab.com/nakst/essence
rename button in file menu
This commit is contained in:
parent
b66cdda741
commit
c166eee594
|
@ -590,6 +590,29 @@ void _start() {
|
||||||
|
|
||||||
EsHandleClose(message->user.context1.u);
|
EsHandleClose(message->user.context1.u);
|
||||||
EsHeapFree(_path);
|
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) {
|
} else if (message->type == MESSAGE_BLOCKING_TASK_COMPLETE) {
|
||||||
Instance *instance = (Instance *) message->user.context1.p;
|
Instance *instance = (Instance *) message->user.context1.p;
|
||||||
if (message->user.context2.u == instance->blockingTaskID) BlockingTaskComplete(instance);
|
if (message->user.context2.u == instance->blockingTaskID) BlockingTaskComplete(instance);
|
||||||
|
|
|
@ -63,6 +63,7 @@ struct EnumString { const char *cName; int value; };
|
||||||
#define DESKTOP_MSG_UNHANDLED_KEY_EVENT (15)
|
#define DESKTOP_MSG_UNHANDLED_KEY_EVENT (15)
|
||||||
#define DESKTOP_MSG_START_USER_TASK (16)
|
#define DESKTOP_MSG_START_USER_TASK (16)
|
||||||
#define DESKTOP_MSG_SET_PROGRESS (17)
|
#define DESKTOP_MSG_SET_PROGRESS (17)
|
||||||
|
#define DESKTOP_MSG_RENAME (18)
|
||||||
|
|
||||||
struct EsFileStore {
|
struct EsFileStore {
|
||||||
#define FILE_STORE_HANDLE (1)
|
#define FILE_STORE_HANDLE (1)
|
||||||
|
@ -218,6 +219,11 @@ struct APIInstance {
|
||||||
EsInstanceClassEditorSettings editorSettings;
|
EsInstanceClassEditorSettings editorSettings;
|
||||||
EsInstanceClassViewerSettings viewerSettings;
|
EsInstanceClassViewerSettings viewerSettings;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// For the file menu.
|
||||||
|
EsPanel *fileMenuNameSwitcher;
|
||||||
|
EsPanel *fileMenuNamePanel;
|
||||||
|
EsTextbox *fileMenuNameTextbox;
|
||||||
};
|
};
|
||||||
|
|
||||||
MountPoint *NodeAddMountPoint(const char *prefix, size_t prefixBytes, EsHandle base, bool queryInformation) {
|
MountPoint *NodeAddMountPoint(const char *prefix, size_t prefixBytes, EsHandle base, bool queryInformation) {
|
||||||
|
@ -982,6 +988,41 @@ EsMessage *EsMessageReceive() {
|
||||||
EsHandleClose(message.message.tabOperation.handle);
|
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) {
|
} else if (type == ES_MSG_INSTANCE_DOCUMENT_RENAMED) {
|
||||||
char *buffer = (char *) EsHeapAllocate(message.message.tabOperation.bytes, false);
|
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:
|
case ES_ERROR_FILE_ALREADY_EXISTS:
|
||||||
errorMessage = interfaceString_FileSaveErrorAlreadyExists;
|
errorMessage = interfaceString_FileSaveErrorAlreadyExists;
|
||||||
break;
|
break;
|
||||||
|
case ES_ERROR_TOO_MANY_FILES_WITH_NAME:
|
||||||
|
errorMessage = interfaceString_FileSaveErrorTooManyFiles;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
EsDialogShowAlert(instance->window, INTERFACE_STRING(FileCannotSave),
|
EsDialogShowAlert(instance->window, INTERFACE_STRING(FileCannotSave),
|
||||||
|
|
|
@ -1624,7 +1624,7 @@ EsError TemporaryFileCreate(EsHandle *handle, char **path, size_t *pathBytes, ui
|
||||||
return file.error;
|
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;
|
if (!instance->processHandle) return;
|
||||||
|
|
||||||
EsMessage m = {};
|
EsMessage m = {};
|
||||||
|
@ -1640,9 +1640,9 @@ void ApplicationInstanceRequestSave(ApplicationInstance *instance, const char *n
|
||||||
EsHeapFree(folder);
|
EsHeapFree(folder);
|
||||||
size_t nameBytes = EsPathFindUniqueName(name, folderBytes + newNameBytes, folderBytes + newNameBytes + 32);
|
size_t nameBytes = EsPathFindUniqueName(name, folderBytes + newNameBytes, folderBytes + newNameBytes + 32);
|
||||||
|
|
||||||
if (!nameBytes) {
|
if (!nameBytes || (failIfAlreadyExists && nameBytes != folderBytes + newNameBytes)) {
|
||||||
EsHeapFree(name);
|
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);
|
EsMessagePostRemote(instance->processHandle, &m);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1710,22 +1710,10 @@ void ApplicationInstanceRequestSave(ApplicationInstance *instance, const char *n
|
||||||
EsMessagePostRemote(instance->processHandle, &m);
|
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 Update the location of installed applications and other things in the configuration.
|
||||||
// TODO Replace fromApplication with something better.
|
// 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;
|
EsObjectID documentID = 0;
|
||||||
|
|
||||||
for (uintptr_t i = 0; i < desktop.openDocuments.Count(); i++) {
|
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;
|
m.tabOperation.bytes = newPathBytes - newNameOffset;
|
||||||
EsMessagePostRemote(instance->processHandle, &m);
|
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) {
|
void ApplicationInstanceCompleteSave(ApplicationInstance *fromInstance) {
|
||||||
|
@ -2253,7 +2255,19 @@ void DesktopSyscall(EsMessage *message, uint8_t *buffer, EsBuffer *pipe) {
|
||||||
InstalledApplication *application = ApplicationFindByPID(message->desktop.processID);
|
InstalledApplication *application = ApplicationFindByPID(message->desktop.processID);
|
||||||
|
|
||||||
if (application && (application->permissions & APPLICATION_PERMISSION_ALL_FILES)) {
|
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) {
|
} else if (buffer[0] == DESKTOP_MSG_START_USER_TASK && pipe) {
|
||||||
InstalledApplication *application = ApplicationFindByPID(message->desktop.processID);
|
InstalledApplication *application = ApplicationFindByPID(message->desktop.processID);
|
||||||
|
@ -2335,7 +2349,39 @@ void DesktopSyscall(EsMessage *message, uint8_t *buffer, EsBuffer *pipe) {
|
||||||
EsElementRepaint(desktop.tasksButton);
|
EsElementRepaint(desktop.tasksButton);
|
||||||
}
|
}
|
||||||
} else if (buffer[0] == DESKTOP_MSG_REQUEST_SAVE) {
|
} 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) {
|
} else if (buffer[0] == DESKTOP_MSG_COMPLETE_SAVE) {
|
||||||
ApplicationInstanceCompleteSave(instance);
|
ApplicationInstanceCompleteSave(instance);
|
||||||
} else if (buffer[0] == DESKTOP_MSG_SHOW_IN_FILE_MANAGER) {
|
} else if (buffer[0] == DESKTOP_MSG_SHOW_IN_FILE_MANAGER) {
|
||||||
|
|
282
desktop/gui.cpp
282
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.
|
// --------------------------------- Paint targets.
|
||||||
|
|
||||||
bool EsPaintTargetTake(EsPaintTarget *target, size_t width, size_t height, bool hasAlphaChannel = true) {
|
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++) {
|
for (uintptr_t i = 0; i < element->GetChildCount(); i++) {
|
||||||
EsElement *child = element->GetChild(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);
|
int size = child->GetWidth(message->measure.height);
|
||||||
if (size > maximum) maximum = size;
|
if (size > maximum) maximum = size;
|
||||||
}
|
}
|
||||||
|
@ -3153,7 +3055,8 @@ int ProcessPanelMessage(EsElement *element, EsMessage *message) {
|
||||||
|
|
||||||
for (uintptr_t i = 0; i < element->GetChildCount(); i++) {
|
for (uintptr_t i = 0; i < element->GetChildCount(); i++) {
|
||||||
EsElement *child = element->GetChild(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);
|
int size = child->GetHeight(message->measure.width);
|
||||||
if (size > maximum) maximum = size;
|
if (size > maximum) maximum = size;
|
||||||
}
|
}
|
||||||
|
@ -5237,6 +5140,183 @@ EsSlider *EsSliderCreate(EsElement *parent, uint64_t flags, const EsStyle *style
|
||||||
return slider;
|
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.
|
// --------------------------------- Message loop and core UI infrastructure.
|
||||||
|
|
||||||
void EsElement::PrintTree(int depth) {
|
void EsElement::PrintTree(int depth) {
|
||||||
|
|
|
@ -331,6 +331,7 @@ define ES_ERROR_DIRECTORY_ENTRY_BEING_REMOVED (-72)
|
||||||
define ES_ERROR_CANCELLED (-73)
|
define ES_ERROR_CANCELLED (-73)
|
||||||
define ES_ERROR_BLOCK_ACCESS_INVALID (-74)
|
define ES_ERROR_BLOCK_ACCESS_INVALID (-74)
|
||||||
define ES_ERROR_DEVICE_REMOVED (-75)
|
define ES_ERROR_DEVICE_REMOVED (-75)
|
||||||
|
define ES_ERROR_TOO_MANY_FILES_WITH_NAME (-76)
|
||||||
|
|
||||||
define ES_INVALID_HANDLE ((EsHandle) (0))
|
define ES_INVALID_HANDLE ((EsHandle) (0))
|
||||||
define ES_CURRENT_THREAD ((EsHandle) (0x10))
|
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_HORIZONTAL (1 << 8)
|
||||||
define ES_PANEL_REVERSE (1 << 9) // Reverse layout is not supported with ES_PANEL_TABLE yet.
|
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.
|
// For ES_PANEL_TABLE.
|
||||||
// TODO Implement these!
|
// TODO Implement these!
|
||||||
define ES_PANEL_H_LEFT (1 << 16)
|
define ES_PANEL_H_LEFT (1 << 16)
|
||||||
|
@ -968,6 +973,7 @@ enum EsMessageType {
|
||||||
ES_MSG_INSTANCE_DOCUMENT_RENAMED = 0x4A04
|
ES_MSG_INSTANCE_DOCUMENT_RENAMED = 0x4A04
|
||||||
ES_MSG_INSTANCE_DOCUMENT_UPDATED = 0x4A05
|
ES_MSG_INSTANCE_DOCUMENT_UPDATED = 0x4A05
|
||||||
ES_MSG_PRIMARY_CLIPBOARD_UPDATED = 0x4A06
|
ES_MSG_PRIMARY_CLIPBOARD_UPDATED = 0x4A06
|
||||||
|
ES_MSG_INSTANCE_RENAME_RESPONSE = 0x4A07
|
||||||
|
|
||||||
// Debugger messages:
|
// Debugger messages:
|
||||||
ES_MSG_APPLICATION_CRASH = 0x4C00
|
ES_MSG_APPLICATION_CRASH = 0x4C00
|
||||||
|
@ -986,6 +992,7 @@ enum EsMessageType {
|
||||||
|
|
||||||
// File Manager messages:
|
// File Manager messages:
|
||||||
ES_MSG_FILE_MANAGER_FILE_MODIFIED = 0x5100
|
ES_MSG_FILE_MANAGER_FILE_MODIFIED = 0x5100
|
||||||
|
ES_MSG_FILE_MANAGER_PATH_MOVED = 0x5101
|
||||||
|
|
||||||
// Textbox messages:
|
// Textbox messages:
|
||||||
ES_MSG_TEXTBOX_UPDATED = 0x5200
|
ES_MSG_TEXTBOX_UPDATED = 0x5200
|
||||||
|
|
|
@ -141,6 +141,9 @@ DEFINE_INTERFACE_STRING(DesktopSettingsThemeWallpaper, "Wallpaper");
|
||||||
|
|
||||||
DEFINE_INTERFACE_STRING(FileCannotSave, "The document was not saved.");
|
DEFINE_INTERFACE_STRING(FileCannotSave, "The document was not saved.");
|
||||||
DEFINE_INTERFACE_STRING(FileCannotOpen, "The file could not be opened.");
|
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(FileSaveErrorFileDeleted, "Another application deleted the file.");
|
||||||
DEFINE_INTERFACE_STRING(FileSaveErrorCorrupt, "The file has been corrupted, and it cannot be modified.");
|
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(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(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(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(FileSaveErrorUnknown, "An unknown error occurred. Please try again later.");
|
||||||
|
|
||||||
DEFINE_INTERFACE_STRING(FileLoadErrorCorrupt, "The file has been corrupted, and it cannot be opened.");
|
DEFINE_INTERFACE_STRING(FileLoadErrorCorrupt, "The file has been corrupted, and it cannot be opened.");
|
||||||
|
|
Loading…
Reference in New Issue