mirror of https://gitlab.com/nakst/essence
				
				
				
			copying folders
This commit is contained in:
		
							parent
							
								
									5d9a6f72dc
								
							
						
					
					
						commit
						787e51b372
					
				|  | @ -152,21 +152,81 @@ void CommandCopy(Instance *instance, EsElement *, EsCommand *) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| EsError CommandPasteFile(String source, String destinationBase, void **copyBuffer, String *_destination = nullptr) { | ||||
| 	if (PathHasPrefix(destinationBase, source)) { | ||||
| 		return ES_ERROR_TARGET_WITHIN_SOURCE; | ||||
| 	} | ||||
| 
 | ||||
| 	String name = PathGetName(source); | ||||
| 	String destination = StringAllocateAndFormat("%s%z%s", STRFMT(destinationBase), PathHasTrailingSlash(destinationBase) ? "" : "/", STRFMT(name)); | ||||
| 
 | ||||
| 	if (StringEquals(PathGetParent(source), destinationBase)) { | ||||
| 		destination.allocated += 32; | ||||
| 		destination.text = (char *) EsHeapReallocate(destination.text, destination.allocated, false); | ||||
| 		size_t bytes = EsPathFindUniqueName(destination.text, destination.bytes, destination.allocated); | ||||
| 
 | ||||
| 		if (bytes) { | ||||
| 			destination.bytes = destination.allocated = bytes; | ||||
| 		} else { | ||||
| 			destination.allocated = destination.bytes; | ||||
| 			StringDestroy(&destination); | ||||
| 			return ES_ERROR_FILE_ALREADY_EXISTS; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	EsPrint("Copying %s -> %s...\n", STRFMT(source), STRFMT(destination)); | ||||
| 	EsError error = EsFileCopy(STRING(source), STRING(destination), copyBuffer); | ||||
| 
 | ||||
| 	if (error == ES_ERROR_INCORRECT_NODE_TYPE) { | ||||
| 		EsDirectoryChild *buffer; | ||||
| 		ptrdiff_t childCount = EsDirectoryEnumerateChildren(STRING(source), &buffer); | ||||
| 
 | ||||
| 		if (ES_CHECK_ERROR(childCount)) { | ||||
| 			error = (EsError) childCount; | ||||
| 		} else { | ||||
| 			error = EsPathCreate(STRING(destination), ES_NODE_DIRECTORY, false); | ||||
| 
 | ||||
| 			if (error == ES_SUCCESS) { | ||||
| 				for (intptr_t i = 0; i < childCount && error == ES_SUCCESS; i++) { | ||||
| 					String childSourcePath = StringAllocateAndFormat("%s%z%s", STRFMT(source),  | ||||
| 							PathHasTrailingSlash(source) ? "" : "/", buffer[i].nameBytes, buffer[i].name); | ||||
| 					error = CommandPasteFile(childSourcePath, destination, copyBuffer); | ||||
| 					StringDestroy(&childSourcePath); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			EsHeapFree(buffer); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (error == ES_SUCCESS) { | ||||
| 		FolderFileUpdatedAtPath(destination, nullptr); | ||||
| 	} | ||||
| 
 | ||||
| 	if (_destination && error == ES_SUCCESS) { | ||||
| 		*_destination = destination; | ||||
| 	} else { | ||||
| 		StringDestroy(&destination); | ||||
| 	} | ||||
| 
 | ||||
| 	return error; | ||||
| } | ||||
| 
 | ||||
| void CommandPaste(Instance *instance, EsElement *, EsCommand *) { | ||||
| 	if (EsClipboardHasFormat(ES_CLIPBOARD_PRIMARY, ES_CLIPBOARD_FORMAT_PATH_LIST)) { | ||||
| 		// TODO Background task.
 | ||||
| 		// TODO Renaming.
 | ||||
| 		// TODO Recursing into folders.
 | ||||
| 		// TODO Reporting errors properly.
 | ||||
| 		// TODO Reporting errors properly. Ask to retry or cancel.
 | ||||
| 		// TODO If the destination file already exists, ask to replace, skip, rename or cancel.
 | ||||
| 		// TODO Other namespace handlers.
 | ||||
| 		// TODO Selecting *all* pasted files.
 | ||||
| 		// TODO Update parent folders after copy complete.
 | ||||
| 		// TODO Undo.
 | ||||
| 
 | ||||
| 		void *copyBuffer = nullptr; | ||||
| 
 | ||||
| 		size_t bytes; | ||||
| 		char *pathList = EsClipboardReadText(ES_CLIPBOARD_PRIMARY, &bytes); | ||||
| 
 | ||||
| 		Array<String> itemsToSelect = {}; | ||||
| 
 | ||||
| 		if (pathList) { | ||||
| 			const char *position = pathList; | ||||
| 
 | ||||
|  | @ -175,29 +235,18 @@ void CommandPaste(Instance *instance, EsElement *, EsCommand *) { | |||
| 				if (!newline) break; | ||||
| 
 | ||||
| 				String source = StringFromLiteralWithSize(position, newline - position); | ||||
| 				String name = PathGetName(source); | ||||
| 				String destination = StringAllocateAndFormat("%s%s", STRFMT(instance->folder->path), STRFMT(name)); | ||||
| 				EsError error = EsFileCopy(STRING(source), STRING(destination), ©Buffer); | ||||
| 				String destination; | ||||
| 
 | ||||
| 				if (error == ES_SUCCESS) { | ||||
| 					EsMutexAcquire(&instance->folder->modifyEntriesMutex); | ||||
| 					EsAssert(instance->folder->doneInitialEnumeration); | ||||
| 					EsDirectoryChild directoryChild; | ||||
| 
 | ||||
| 					if (EsPathQueryInformation(STRING(destination), &directoryChild)) { | ||||
| 						FolderAddEntryAndUpdateInstances(instance->folder, STRING(name), &directoryChild, instance); | ||||
| 					} else { | ||||
| 						// File must have been deleted by the time we got here!
 | ||||
| 					} | ||||
| 
 | ||||
| 					EsMutexRelease(&instance->folder->modifyEntriesMutex); | ||||
| 				} else { | ||||
| 				if (ES_SUCCESS != CommandPasteFile(source, instance->folder->path, ©Buffer, &destination)) { | ||||
| 					goto encounteredError; | ||||
| 				} | ||||
| 
 | ||||
| 				String destinationItem = StringDuplicate(PathGetName(destination)); | ||||
| 				itemsToSelect.Add(destinationItem); | ||||
| 				StringDestroy(&destination); | ||||
| 
 | ||||
| 				position += source.bytes + 1; | ||||
| 				bytes -= source.bytes + 1; | ||||
| 				StringDestroy(&destination); | ||||
| 			} | ||||
| 		} else { | ||||
| 			encounteredError:; | ||||
|  | @ -205,8 +254,23 @@ void CommandPaste(Instance *instance, EsElement *, EsCommand *) { | |||
| 			EsAnnouncementShow(instance->window, ES_FLAGS_DEFAULT, point.x, point.y, INTERFACE_STRING(CommonAnnouncementPasteErrorOther)); | ||||
| 		} | ||||
| 
 | ||||
| 		size_t pathSectionCount = PathCountSections(instance->folder->path); | ||||
| 
 | ||||
| 		for (uintptr_t i = 0; i < pathSectionCount - 1; i++) { | ||||
| 			String parent = PathGetParent(instance->folder->path, i + 1); | ||||
| 			FolderFileUpdatedAtPath(parent, nullptr); | ||||
| 		} | ||||
| 
 | ||||
| 		EsListViewSelectNone(instance->list); | ||||
| 
 | ||||
| 		for (uintptr_t i = 0; i < itemsToSelect.Length(); i++) { | ||||
| 			InstanceSelectByName(instance, itemsToSelect[i], true, i == itemsToSelect.Length() - 1); | ||||
| 			StringDestroy(&itemsToSelect[i]); | ||||
| 		} | ||||
| 
 | ||||
| 		EsHeapFree(pathList); | ||||
| 		EsHeapFree(copyBuffer); | ||||
| 		itemsToSelect.Free(); | ||||
| 	} else { | ||||
| 		// TODO Paste the data into a new file.
 | ||||
| 	} | ||||
|  |  | |||
|  | @ -426,6 +426,29 @@ void FolderAddEntryAndUpdateInstances(Folder *folder, const char *name, size_t n | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| void FolderFileUpdatedAtPath(String path, Instance *instance) { | ||||
| 	path = PathRemoveTrailingSlash(path); | ||||
| 	String file = PathGetName(path); | ||||
| 	String folder = PathGetParent(path); | ||||
| 	EsDirectoryChild information = {}; | ||||
| 
 | ||||
| 	if (EsPathQueryInformation(STRING(path), &information)) { | ||||
| 		for (uintptr_t i = 0; i < loadedFolders.Length(); i++) { | ||||
| 			if (loadedFolders[i]->itemHandler->type != NAMESPACE_HANDLER_FILE_SYSTEM) continue; | ||||
| 			if (EsStringCompareRaw(STRING(loadedFolders[i]->path), STRING(folder))) continue; | ||||
| 
 | ||||
| 			EsMutexAcquire(&loadedFolders[i]->modifyEntriesMutex); | ||||
| 
 | ||||
| 			if (loadedFolders[i]->doneInitialEnumeration) { | ||||
| 				FolderAddEntryAndUpdateInstances(loadedFolders[i], file.text, file.bytes, &information, instance); | ||||
| 			} | ||||
| 
 | ||||
| 			EsMutexRelease(&loadedFolders[i]->modifyEntriesMutex); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| uint64_t FolderRemoveEntryAndUpdateInstances(Folder *folder, const char *name, size_t nameBytes) { | ||||
| 	FolderEntry *entry = (FolderEntry *) HashTableGetLong(&folder->entries, name, nameBytes); | ||||
| 	uint64_t id = 0; | ||||
|  |  | |||
|  | @ -230,6 +230,7 @@ void InstanceFolderPathChanged(Instance *instance, bool fromLoadFolder); | |||
| void InstanceAddContents(struct Instance *instance, HashTable *newEntries); | ||||
| void InstanceAddSingle(struct Instance *instance, ListEntry newEntry); | ||||
| void InstanceRemoveContents(struct Instance *instance); | ||||
| void InstanceSelectByName(Instance *instance, String name, bool addToExistingSelection, bool focusItem); | ||||
| ListEntry InstanceRemoveSingle(Instance *instance, FolderEntry *folderEntry); | ||||
| ListEntry *InstanceGetSelectedListEntry(Instance *instance); | ||||
| void ListItemCreated(EsElement *element, uintptr_t index, bool fromFolderRename); | ||||
|  | @ -549,25 +550,7 @@ void _start() { | |||
| 			size_t pathSectionCount = PathCountSections(fullPath); | ||||
| 
 | ||||
| 			for (uintptr_t i = 0; i < pathSectionCount; i++) { | ||||
| 				String path = PathGetParent(fullPath, i + 1); | ||||
| 				String file = PathGetSection(fullPath, i + 1); | ||||
| 				String folder = PathGetParent(path); | ||||
| 				EsDirectoryChild information = {}; | ||||
| 
 | ||||
| 				if (EsPathQueryInformation(STRING(path), &information)) { | ||||
| 					for (uintptr_t i = 0; i < loadedFolders.Length(); i++) { | ||||
| 						if (loadedFolders[i]->itemHandler->type != NAMESPACE_HANDLER_FILE_SYSTEM) continue; | ||||
| 						if (EsStringCompareRaw(STRING(loadedFolders[i]->path), STRING(folder))) continue; | ||||
| 
 | ||||
| 						EsMutexAcquire(&loadedFolders[i]->modifyEntriesMutex); | ||||
| 
 | ||||
| 						if (loadedFolders[i]->doneInitialEnumeration) { | ||||
| 							FolderAddEntryAndUpdateInstances(loadedFolders[i], file.text, file.bytes, &information, nullptr); | ||||
| 						} | ||||
| 
 | ||||
| 						EsMutexRelease(&loadedFolders[i]->modifyEntriesMutex); | ||||
| 					} | ||||
| 				} | ||||
| 				FolderFileUpdatedAtPath(PathGetParent(fullPath, i + 1), nullptr); | ||||
| 			} | ||||
| 
 | ||||
| 			EsHandleClose(message->user.context1.u); | ||||
|  |  | |||
|  | @ -176,7 +176,24 @@ String PathGetParent(String string) { | |||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| bool PathHasTrailingSlash(String path) { | ||||
| 	return path.bytes && path.text[path.bytes - 1] == '/'; | ||||
| } | ||||
| 
 | ||||
| String PathRemoveTrailingSlash(String path) { | ||||
| 	if (PathHasTrailingSlash(path)) path.bytes--; | ||||
| 	return path; | ||||
| } | ||||
| 
 | ||||
| String PathGetName(String path) { | ||||
| 	intptr_t i = path.bytes - 2; | ||||
| 	while (i >= 0 && path.text[i] != '/') i--; | ||||
| 	path.text += i + 1, path.bytes -= i + 1; | ||||
| 	return path; | ||||
| } | ||||
| 
 | ||||
| bool PathHasPrefix(String path, String prefix) { | ||||
| 	prefix = PathRemoveTrailingSlash(prefix); | ||||
| 	return StringStartsWith(path, prefix) && path.bytes > prefix.bytes && path.text[prefix.bytes] == '/'; | ||||
| } | ||||
| 
 | ||||
|  | @ -191,18 +208,3 @@ bool PathReplacePrefix(String *knownPath, String oldPath, String newPath) { | |||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| String PathRemoveTrailingSlash(String path) { | ||||
| 	if (path.bytes && path.text[path.bytes - 1] == '/') { | ||||
| 		path.bytes--; | ||||
| 	} | ||||
| 
 | ||||
| 	return path; | ||||
| } | ||||
| 
 | ||||
| String PathGetName(String path) { | ||||
| 	intptr_t i = path.bytes - 2; | ||||
| 	while (i >= 0 && path.text[i] != '/') i--; | ||||
| 	path.text += i + 1, path.bytes -= i + 1; | ||||
| 	return path; | ||||
| } | ||||
|  |  | |||
|  | @ -324,6 +324,16 @@ ES_MACRO_SORT(InstanceSortListContents, ListEntry, { | |||
| 	result = InstanceCompareFolderEntries(_left->entry, _right->entry, context); | ||||
| }, uint16_t); | ||||
| 
 | ||||
| void InstanceSelectByName(Instance *instance, String name, bool addToExistingSelection, bool focusItem) { | ||||
| 	for (uintptr_t i = 0; i < instance->listContents.Length(); i++) { | ||||
| 		if (0 == EsStringCompareRaw(STRING(instance->listContents[i].entry->GetName()), STRING(name))) { | ||||
| 			EsListViewSelect(instance->list, 0, i, addToExistingSelection); | ||||
| 			if (focusItem) EsListViewFocusItem(instance->list, 0, i); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void InstanceAddContents(Instance *instance, HashTable *newEntries) { | ||||
| 	// Call with the message mutex acquired.
 | ||||
| 
 | ||||
|  | @ -352,14 +362,7 @@ void InstanceAddContents(Instance *instance, HashTable *newEntries) { | |||
| 		EsListViewInsert(instance->list, 0, 0, instance->listContents.Length()); | ||||
| 
 | ||||
| 		if (instance->delayedFocusItem.bytes) { | ||||
| 			for (uintptr_t i = 0; i < instance->listContents.Length(); i++) { | ||||
| 				if (0 == EsStringCompareRaw(STRING(instance->listContents[i].entry->GetName()), STRING(instance->delayedFocusItem))) { | ||||
| 					EsListViewSelect(instance->list, 0, i); | ||||
| 					EsListViewFocusItem(instance->list, 0, i); | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			InstanceSelectByName(instance, instance->delayedFocusItem, false, true); | ||||
| 			StringDestroy(&instance->delayedFocusItem); | ||||
| 		} | ||||
| 	} | ||||
|  | @ -696,7 +699,7 @@ int ListItemMessage(EsElement *element, EsMessage *message) { | |||
| int ListCallback(EsElement *element, EsMessage *message) { | ||||
| 	Instance *instance = element->instance; | ||||
| 
 | ||||
| 	if (message->type == ES_MSG_FOCUSED_START) { | ||||
| 	if (message->type == ES_MSG_FOCUSED_START || message->type == ES_MSG_PRIMARY_CLIPBOARD_UPDATED) { | ||||
| 		InstanceUpdateItemSelectionCountCommands(instance); | ||||
| 		return 0; | ||||
| 	} else if (message->type == ES_MSG_FOCUSED_END) { | ||||
|  |  | |||
|  | @ -2900,8 +2900,8 @@ void ScrollPane::Refresh() { | |||
| 
 | ||||
| 	EsRectangle bounds = parent->GetBounds(); | ||||
| 
 | ||||
| 	if (bar[0]) ScrollbarSetMeasurements(bar[0], bounds.r - fixedViewport[0], contentWidth); | ||||
| 	if (bar[1]) ScrollbarSetMeasurements(bar[1], bounds.b - fixedViewport[1], contentHeight); | ||||
| 	if (bar[0]) ScrollbarSetMeasurements(bar[0], bounds.r - fixedViewport[0], contentWidth  - fixedViewport[0]); | ||||
| 	if (bar[1]) ScrollbarSetMeasurements(bar[1], bounds.b - fixedViewport[1], contentHeight - fixedViewport[1]); | ||||
| 
 | ||||
| 	SetPosition(0, position[0], true); | ||||
| 	SetPosition(1, position[1], true); | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| // TODO Consistent int64_t/intptr_t.
 | ||||
| // TODO Drag and drop.
 | ||||
| // TODO GetFirstIndex/GetLastIndex assume that every group is non-empty.
 | ||||
| // TODO Sticking to top/bottom scroll when inserting/removing space.
 | ||||
| 
 | ||||
| struct ListViewItemElement : EsElement { | ||||
| 	uintptr_t index; // Index into the visible items array.
 | ||||
|  | @ -482,6 +483,22 @@ struct EsListView : EsElement { | |||
| 	} | ||||
| 
 | ||||
| 	void Populate() { | ||||
| 		EsPrint("--- Before Populate() ---\n"); | ||||
| 		EsPrint("Scroll: %i\n", (int) (scroll.position[1] - currentStyle->insets.t)); | ||||
| 
 | ||||
| 		for (uintptr_t i = 0; i < visibleItems.Length(); i++) { | ||||
| 			EsMessage m = { ES_MSG_LIST_VIEW_GET_CONTENT }; | ||||
| 			uint8_t _buffer[512]; | ||||
| 			EsBuffer buffer = { .out = _buffer, .bytes = sizeof(_buffer) }; | ||||
| 			m.getContent.buffer = &buffer; | ||||
| 			m.getContent.index = visibleItems[i].index; | ||||
| 			m.getContent.group = visibleItems[i].group; | ||||
| 			EsMessageSend(this, &m); | ||||
| 			EsPrint("%d: %d '%s' at %i\n", i, visibleItems[i].index, buffer.position, _buffer, visibleItems[i].element->offsetY - GetListBounds().t); | ||||
| 		} | ||||
| 
 | ||||
| 		EsPrint("------\n"); | ||||
| 
 | ||||
| 		// TODO Keep one item before and after the viewport, so tab traversal on custom elements works.
 | ||||
| 		// TODO Always keep an item if it has FOCUS_WITHIN.
 | ||||
| 		// 	- But maybe we shouldn't allow focusable elements in a list view.
 | ||||
|  | @ -533,7 +550,7 @@ struct EsListView : EsElement { | |||
| 							: visibleItem->element->offsetY - contentBounds.t; | ||||
| 
 | ||||
| 					if (position < expectedPosition - 1 || position > expectedPosition + 1) { | ||||
| 						EsPrint("Item in unexpected position: expected %d, got %d; index %d, scroll %d.\n",  | ||||
| 						EsPrint("Item in unexpected position: got %d, should have been %d; index %d, scroll %d.\n",  | ||||
| 								expectedPosition, position, visibleItem->index, scroll); | ||||
| 						EsAssert(false); | ||||
| 					} | ||||
|  | @ -726,33 +743,19 @@ struct EsListView : EsElement { | |||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		int64_t currentScroll = (flags & ES_LIST_VIEW_HORIZONTAL) ? scroll.position[0] : scroll.position[1]; | ||||
| 		int64_t scrollLimit = (flags & ES_LIST_VIEW_HORIZONTAL) ? scroll.limit[0] : scroll.limit[1]; | ||||
| 
 | ||||
| 		totalSize += space; | ||||
| 
 | ||||
| 		if (((beforeItem == 0 && currentScroll) || currentScroll == scrollLimit) && firstLayout && space > 0 && scrollLimit) { | ||||
| 			scroll.Refresh(); | ||||
| 		for (uintptr_t i = beforeItem; i < visibleItems.Length(); i++) { | ||||
| 			ListViewItem *item = &visibleItems[i]; | ||||
| 
 | ||||
| 			if (flags & ES_LIST_VIEW_HORIZONTAL) { | ||||
| 				scroll.SetX(scroll.position[0] + space, false); | ||||
| 				item->element->offsetX += space; | ||||
| 			} else { | ||||
| 				scroll.SetY(scroll.position[1] + space, false); | ||||
| 				item->element->offsetY += space; | ||||
| 			} | ||||
| 		} else { | ||||
| 			for (uintptr_t i = beforeItem; i < visibleItems.Length(); i++) { | ||||
| 				ListViewItem *item = &visibleItems[i]; | ||||
| 
 | ||||
| 				if (flags & ES_LIST_VIEW_HORIZONTAL) { | ||||
| 					item->element->offsetX += space; | ||||
| 				} else { | ||||
| 					item->element->offsetY += space; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			scroll.Refresh(); | ||||
| 		} | ||||
| 
 | ||||
| 		scroll.Refresh(); | ||||
| 		EsElementUpdateContentSize(this); | ||||
| 	} | ||||
| 
 | ||||
|  | @ -1575,7 +1578,7 @@ struct EsListView : EsElement { | |||
| 
 | ||||
| 		if (message->type == ES_MSG_GET_WIDTH || message->type == ES_MSG_GET_HEIGHT) { | ||||
| 			if (flags & ES_LIST_VIEW_HORIZONTAL) { | ||||
| 				message->measure.width  = totalSize + currentStyle->insets.l + currentStyle->insets.r; | ||||
| 				message->measure.width = totalSize + currentStyle->insets.l + currentStyle->insets.r; | ||||
| 			} else { | ||||
| 				message->measure.height = totalSize + currentStyle->insets.t + currentStyle->insets.b; | ||||
| 
 | ||||
|  | @ -2305,10 +2308,20 @@ bool EsListViewGetFocusedItem(EsListView *view, EsListViewIndex *group, EsListVi | |||
| 	return view->hasFocusedItem; | ||||
| } | ||||
| 
 | ||||
| void EsListViewSelect(EsListView *view, EsListViewIndex group, EsListViewIndex index) { | ||||
| void EsListViewSelectNone(EsListView *view) { | ||||
| 	EsMessageMutexCheck(); | ||||
| 	view->Select(-1, 0, false, false, false); | ||||
| } | ||||
| 
 | ||||
| void EsListViewSelect(EsListView *view, EsListViewIndex group, EsListViewIndex index, bool addToExistingSelection) { | ||||
| 	EsMessageMutexCheck(); | ||||
| 
 | ||||
| 	view->Select(group, index, false, false, false); | ||||
| 	if (addToExistingSelection) { | ||||
| 		view->SetSelected(group, index, group, index, true, false); | ||||
| 		view->UpdateVisibleItemsSelectionState(); | ||||
| 	} else { | ||||
| 		view->Select(group, index, false, false, false); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void EsListViewSetEmptyMessage(EsListView *view, const char *message, ptrdiff_t messageBytes) { | ||||
|  |  | |||
|  | @ -2422,7 +2422,8 @@ function void EsListViewEnumerateVisibleItems(EsListView *view, EsListViewEnumer | |||
| function void EsListViewSetColumns(EsListView *view, EsListViewColumn *columns, size_t columnCount); | ||||
| function void EsListViewSetEmptyMessage(EsListView *view, STRING message = BLANK_STRING); | ||||
| function void EsListViewSetMaximumItemsPerBand(EsListView *view, int maximumItemsPerBand); | ||||
| function void EsListViewSelect(EsListView *view, EsListViewIndex group, EsListViewIndex index); | ||||
| function void EsListViewSelectNone(EsListView *view); | ||||
| function void EsListViewSelect(EsListView *view, EsListViewIndex group, EsListViewIndex index, bool addToExistingSelection = false); | ||||
| function void EsListViewFocusItem(EsListView *view, EsListViewIndex group, EsListViewIndex index); | ||||
| function bool EsListViewGetFocusedItem(EsListView *view, EsListViewIndex *group, EsListViewIndex *index); // Returns false if not item was focused. | ||||
| function void EsListViewInvalidateContent(EsListView *view, EsListViewIndex group, EsListViewIndex index); | ||||
|  |  | |||
|  | @ -235,25 +235,32 @@ EsError EsFileCopy(const char *source, ptrdiff_t sourceBytes, const char *destin | |||
| 	EsError error = ES_SUCCESS; | ||||
| 
 | ||||
| 	EsFileInformation sourceFile = EsFileOpen(source, sourceBytes, ES_FILE_READ | ES_NODE_FILE | ES_NODE_FAIL_IF_NOT_FOUND); | ||||
| 	EsFileInformation destinationFile = EsFileOpen(destination, destinationBytes, ES_FILE_WRITE_EXCLUSIVE | ES_NODE_FILE | ES_NODE_FAIL_IF_FOUND); | ||||
| 
 | ||||
| 	if (sourceFile.error == ES_SUCCESS && destinationFile.error == ES_SUCCESS) { | ||||
| 		error = EsFileResize(destinationFile.handle, sourceFile.size); | ||||
| 	if (sourceFile.error == ES_SUCCESS) { | ||||
| 		EsFileInformation destinationFile = EsFileOpen(destination, destinationBytes, ES_FILE_WRITE_EXCLUSIVE | ES_NODE_FILE | ES_NODE_FAIL_IF_FOUND); | ||||
| 
 | ||||
| 		if (error == ES_SUCCESS) { | ||||
| 			for (uintptr_t i = 0; i < sourceFile.size; i += copyBufferBytes) { | ||||
| 				size_t bytesRead = EsFileReadSync(sourceFile.handle, i, copyBufferBytes, copyBuffer); | ||||
| 				if (ES_CHECK_ERROR(bytesRead)) { error = bytesRead; break; } | ||||
| 				size_t bytesWritten = EsFileWriteSync(destinationFile.handle, i, bytesRead, copyBuffer); | ||||
| 				if (ES_CHECK_ERROR(bytesWritten)) { error = bytesWritten; break; } | ||||
| 		if (destinationFile.error == ES_SUCCESS) { | ||||
| 			error = EsFileResize(destinationFile.handle, sourceFile.size); | ||||
| 
 | ||||
| 			if (error == ES_SUCCESS) { | ||||
| 				for (uintptr_t i = 0; i < sourceFile.size; i += copyBufferBytes) { | ||||
| 					size_t bytesRead = EsFileReadSync(sourceFile.handle, i, copyBufferBytes, copyBuffer); | ||||
| 					if (ES_CHECK_ERROR(bytesRead)) { error = bytesRead; break; } | ||||
| 					size_t bytesWritten = EsFileWriteSync(destinationFile.handle, i, bytesRead, copyBuffer); | ||||
| 					if (ES_CHECK_ERROR(bytesWritten)) { error = bytesWritten; break; } | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			EsHandleClose(destinationFile.handle); | ||||
| 		} else { | ||||
| 			error = destinationFile.error; | ||||
| 		} | ||||
| 
 | ||||
| 		EsHandleClose(sourceFile.handle); | ||||
| 	} else { | ||||
| 		error = sourceFile.error == ES_SUCCESS ? destinationFile.error : sourceFile.error; | ||||
| 		error = sourceFile.error; | ||||
| 	} | ||||
| 
 | ||||
| 	if (sourceFile.error == ES_SUCCESS) EsHandleClose(sourceFile.handle); | ||||
| 	if (destinationFile.error == ES_SUCCESS) EsHandleClose(destinationFile.handle); | ||||
| 	if (!_copyBuffer) EsHeapFree(copyBuffer); | ||||
| 	return error; | ||||
| } | ||||
|  |  | |||
|  | @ -1606,13 +1606,17 @@ void EnterDebugger() { | |||
| 
 | ||||
| #ifndef KERNEL | ||||
| size_t EsPathFindUniqueName(char *buffer, size_t originalBytes, size_t bufferBytes) { | ||||
| 	// TODO Check that this runs in a reasonable amount of time when all files are already present.
 | ||||
| 	if (originalBytes && buffer[originalBytes - 1] == '/') { | ||||
| 		originalBytes--; | ||||
| 	} | ||||
| 
 | ||||
| 	size_t extensionPoint = originalBytes; | ||||
| 
 | ||||
| 	for (uintptr_t i = 0; i < originalBytes; i++) { | ||||
| 		if (buffer[i] == '.') { | ||||
| 			extensionPoint = i; | ||||
| 		} else if (buffer[i] == '/') { | ||||
| 			extensionPoint = originalBytes; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -1625,6 +1629,8 @@ size_t EsPathFindUniqueName(char *buffer, size_t originalBytes, size_t bufferByt | |||
| 
 | ||||
| 	uintptr_t attempt = 2; | ||||
| 
 | ||||
| 	// TODO Check that this runs in a reasonable amount of time when all files are already present.
 | ||||
| 
 | ||||
| 	while (attempt < 1000) { | ||||
| 		size_t length = EsStringFormat(buffer2, bufferBytes, "%s %d%s", extensionPoint, buffer,  | ||||
| 				attempt, originalBytes - extensionPoint, buffer + extensionPoint); | ||||
|  |  | |||
|  | @ -430,3 +430,4 @@ EsListViewFixedItemGetSelected=428 | |||
| EsClipboardHasFormat=429 | ||||
| EsClipboardHasData=430 | ||||
| EsFileCopy=431 | ||||
| EsListViewSelectNone=432 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 nakst
						nakst