import Foundation #if os(macOS) import AppKit #else import UIKit #endif @MainActor extension SheetModel { func clearCopyProvenance() { copied = nil copiedText = "" } func copy() { let range = selection let rows = (range.start.r...range.end.r).map { r in (range.start.c...range.end.c).map { c in CellWrite(r: r, c: c, raw: cells[Coordinate(r: r, c: c)]?.raw ?? "") } } copied = rows copiedText = TSV.encode(rows.map { $0.map(\.raw) }) #if os(macOS) NSPasteboard.general.clearContents() NSPasteboard.general.setString(copiedText, forType: .string) #else UIPasteboard.general.string = copiedText #endif } func paste() async { guard !dirty && !busy else { error = "Save or discard your cell edit first." return } #if os(macOS) let text = NSPasteboard.general.string(forType: .string) #else let text = UIPasteboard.general.string #endif guard let text else { error = "The clipboard has no text cells." return } let values = TSV.decode(text) guard values.reduce(0, { $0 + $1.count }) <= 500 else { error = "Paste supports up to 500 cells at once." return } var writes: [CellWrite] = [] for (r, row) in values.enumerated() { for (c, raw) in row.enumerated() { let dest = Coordinate(r: selection.start.r + r, c: selection.start.c + c) guard dest.r < rows && dest.c < cols else { error = "This paste extends beyond the visible sheet. Nothing was changed." return } let source = text == copiedText && copied?.indices.contains(r) == true && copied![r].indices.contains(c) ? copied![r][c] : nil writes.append(CellWrite(r: dest.r, c: dest.c, raw: raw, fromR: source?.r, fromC: source?.c)) } } _ = await write(writes) } func clear() async { guard !dirty && !busy else { error = "Save or discard your cell edit first." return } _ = await write( selection.coordinates.filter { !(cells[$0]?.raw ?? "").isEmpty }.map { CellWrite(r: $0.r, c: $0.c, raw: "") }) } func fill(down: Bool) async { guard !dirty && !busy else { error = "Save or discard your cell edit first." return } _ = await write(fillWrites(down: down)) } func fillWrites(down: Bool) -> [CellWrite] { let source = selection.start let destinations = selection.count == 1 ? [ Coordinate( r: min(source.r + (down ? 1 : 0), rows - 1), c: min(source.c + (down ? 0 : 1), cols - 1)) ] : selection.coordinates return destinations.compactMap { point -> CellWrite? in if source == point { return nil } return CellWrite( r: point.r, c: point.c, raw: cells[source]?.raw ?? "", fromR: source.r, fromC: source.c) } } }