use crate::docx::{Block, Row, Table};
use crate::font::Fonts;
use super::Editor;
use super::clipboard::{Fragment, fresh};
use super::commands::Outcome;
use super::selection::{CellShape, Selection};
use super::table::Grid;
use super::tables::empty_cell;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CellCopy {
pub whole_rows: bool,
pub whole_table: bool,
}
impl Editor {
pub(super) fn copy_cells(&mut self, id: u64) -> Option<Fragment> {
let tg = self.table_target()?;
let Selection::Cells { shape, .. } = self.selection else { return None };
let t = self.index.table(&self.doc, tg.table)?;
let g = Grid::of(t);
let (r0, r1) = tg.rows;
let (c0, c1) = tg.cols;
let mut rows = Vec::new();
for r in r0..=r1.min(t.rows.len().saturating_sub(1)) {
let mut row: Row = t.rows[r].clone();
row.src = None;
row.grid_before = 0;
row.grid_after = 0;
row.cells = t.rows[r]
.cells
.iter()
.enumerate()
.filter(|(k, _)| g.rows[r].get(*k).is_some_and(|&(c, s, _)| c + s.max(1) > c0 && c <= c1))
.map(|(k, cell)| {
let (c, s, _) = g.rows[r][k];
let mut cell = cell.clone();
cell.span = (c + s.max(1)).min(c1 + 1) - c.max(c0);
cell
})
.collect();
rows.push(row);
}
let whole_rows = shape == CellShape::Rows || (c0 == 0 && c1 + 1 >= g.cols);
let whole_table = whole_rows && r0 == 0 && r1 + 1 >= t.rows.len();
let sub = Table {
grid: t.grid[c0..=c1.min(t.grid.len().saturating_sub(1))].to_vec(),
rows,
src: None,
id: crate::docx::next_para_id(),
..t.clone()
};
Some(Fragment {
id,
blocks: vec![Block::Table(sub)],
open_end: false,
cells: Some(CellCopy { whole_rows, whole_table }),
controls: Default::default(),
})
}
pub(super) fn paste_cells(&mut self, f: &Fragment, fonts: &Fonts) -> Option<Outcome> {
let copy = f.cells?;
let Some(Block::Table(src)) = f.blocks.first() else { return None };
let mut stripped = vec![Block::Table(src.clone())];
super::clipboard::adopt(&mut stripped, &Default::default());
super::clipboard::strip_controls(&mut stripped);
let Some(Block::Table(src)) = stripped.first() else { return None };
let wide = |grid: &[f64]| -> Vec<f64> { grid.iter().chain(src.grid.iter().skip(grid.len())).copied().collect() };
if copy.whole_rows
&& !matches!(self.selection, Selection::Cells { .. })
&& let Some((para, _)) = self.selection.focus().and_then(|p| p.para_offset())
&& let Some(slot) = self.index.para_slot(para)
&& slot.block > 0
&& let Some(Block::Table(t)) = self.index.blocks(&self.doc, slot.container).and_then(|b| b.get(slot.block - 1))
{
let (id, mut rows, grid) = (crate::layout::TableId(t.id), t.rows.clone(), t.grid.clone());
let at = rows.len();
rows.extend(src.rows.iter().map(|r| {
let mut r = r.clone();
r.src = None;
r.cells.iter_mut().for_each(|c| c.blocks = c.blocks.iter().map(fresh).collect());
r
}));
return Some(self.rebuild("Paste Rows", id, rows, wide(&grid), (at, 0), fonts));
}
let tg = self.table_target()?;
let block = matches!(self.selection, Selection::Cells { .. });
let single = tg.rows.0 == tg.rows.1 && tg.cols.0 == tg.cols.1;
if copy.whole_table && !block {
return None;
}
let t = self.index.table(&self.doc, tg.table)?.clone();
let g = Grid::of(&t);
let mut rows = t.rows.clone();
let fresh_cells = |r: usize, j: usize| -> Option<Vec<Block>> {
let row = &src.rows[r % src.rows.len().max(1)];
row.cells.get(j % row.cells.len().max(1)).map(|c| c.blocks.iter().map(fresh).collect())
};
let at_row_start = !block && self.selection.focus().is_some_and(|p| p.para_offset().is_some_and(|(_, o)| o.0 == 0)) && tg.cols.0 == 0;
let over_rows = matches!(self.selection, Selection::Cells { shape: CellShape::Rows, .. });
if copy.whole_rows && (at_row_start || over_rows) {
let new: Vec<Row> = src
.rows
.iter()
.map(|r| {
let mut r = r.clone();
r.src = None;
r.cells.iter_mut().for_each(|c| c.blocks = c.blocks.iter().map(fresh).collect());
r
})
.collect();
rows.splice(tg.rows.0..tg.rows.0, new);
return Some(self.rebuild("Paste Rows", tg.table, rows, wide(&t.grid), (tg.rows.0, 0), fonts));
}
if block && single {
let k = g.cell_at(tg.rows.0, tg.cols.0)?;
let mut sub = src.clone();
sub.id = crate::docx::next_para_id();
let Block::Table(sub) = fresh(&Block::Table(sub)) else { return None };
let cell = &mut rows[tg.rows.0].cells[k];
let closing = empty_cell(cell).blocks;
cell.blocks.push(Block::Table(sub));
cell.blocks.extend(closing);
return Some(self.rebuild("Paste Cells", tg.table, rows, t.grid.clone(), (tg.rows.0, tg.cols.0), fonts));
}
if block {
for (i, r) in (tg.rows.0..=tg.rows.1).enumerate() {
let ks: Vec<usize> = (0..rows[r].cells.len())
.filter(|&k| g.rows[r].get(k).is_some_and(|&(c, s, _)| c + s.max(1) > tg.cols.0 && c <= tg.cols.1))
.collect();
for (j, k) in ks.into_iter().enumerate() {
if let Some(b) = fresh_cells(i, j) {
rows[r].cells[k].blocks = b;
}
}
}
return Some(self.rebuild("Paste Cells", tg.table, rows, t.grid.clone(), (tg.rows.0, tg.cols.0), fonts));
}
let k0 = g.cell_at(tg.rows.0, tg.cols.0)?;
for i in 0..src.rows.len() {
let r = tg.rows.0 + i;
if r >= rows.len() {
let mut add = rows.last()?.clone();
add.src = None;
add.cells.iter_mut().for_each(|c| *c = empty_cell(c));
rows.push(add);
}
for j in 0..src.rows[i].cells.len() {
let k = if i == 0 { k0 + j } else { g.cell_at(r.min(t.rows.len() - 1), tg.cols.0).unwrap_or(0) + j };
if k < rows[r].cells.len()
&& let Some(b) = fresh_cells(i, j)
{
rows[r].cells[k].blocks = b;
}
}
}
Some(self.rebuild("Paste Cells", tg.table, rows, t.grid.clone(), (tg.rows.0, tg.cols.0), fonts))
}
}