diff --git a/docs/index.md b/docs/index.md index 186123e..36ebf95 100644 --- a/docs/index.md +++ b/docs/index.md @@ -68,7 +68,8 @@ will clear the numeric prefix if you want to cancel it. **Other Keybindings** -* `Ctrl-r` will enter range selection mode +* `Ctrl-r` will enter range selection mode. +* `v` will enter range selection mode with the start of the range already selected. * `Ctrl-s` will save the sheet. * `Ctrl-c`, `y` Copy the cell or range contents. * `Ctrl-v`, `p` Paste into the sheet. diff --git a/examples/test.icalc b/examples/test.icalc deleted file mode 100644 index c456599..0000000 Binary files a/examples/test.icalc and /dev/null differ diff --git a/examples/test.xlsx b/examples/test.xlsx index a2b60d1..ac5e7f9 100644 Binary files a/examples/test.xlsx and b/examples/test.xlsx differ diff --git a/src/book/mod.rs b/src/book/mod.rs index 8b345d6..f6dce1a 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -96,13 +96,29 @@ impl Book { Ok(&self.get_sheet()?.sheet_data) } - /// Move to a specific sheel location in the current sheet + /// Move to a specific sheet location in the current sheet pub fn move_to(&mut self, Address { row, col }: &Address) -> Result<()> { // FIXME(zaphar): Check that this is safe first. self.location.row = *row; self.location.col = *col; Ok(()) } + + /// Extend a cell to the rest of the range. + pub fn extend_to(&mut self, from: &Address, to: &Address) -> Result<()> { + for ri in from.row..=to.row { + for ci in from.col..=to.col { + if ri == from.row && ci == from.col { + continue; + } + let contents = self.model.extend_to(self.current_sheet, from.row as i32, from.col as i32, ri as i32, ci as i32).map_err(|e| anyhow!(e))?; + self.model.set_user_input(self.current_sheet, ri as i32, ci as i32, contents) + .map_err(|e| anyhow!(e))?; + } + } + self.evaluate(); + Ok(()) + } pub fn clear_current_cell(&mut self) -> Result<()> { self.clear_cell_contents(self.current_sheet as u32, self.location.clone()) diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 8b09161..c84a51e 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -317,6 +317,7 @@ impl<'ws> Workspace<'ws> { "Edit Mode:".to_string(), "* ENTER/RETURN: Exit edit mode and save changes".to_string(), "* Ctrl-r: Enter Range Selection mode".to_string(), + "* v: Enter Range Selection mode with the start of the range already selected".to_string(), "* ESC: Exit edit mode and discard changes".to_string(), "Otherwise edit as normal".to_string(), ], @@ -380,7 +381,7 @@ impl<'ws> Workspace<'ws> { return Ok(None); } KeyCode::Char('r') if key.modifiers == KeyModifiers::CONTROL => { - self.enter_range_select_mode(); + self.enter_range_select_mode(false); return Ok(None); } KeyCode::Char('p') if key.modifiers == KeyModifiers::CONTROL => { @@ -579,6 +580,12 @@ impl<'ws> Workspace<'ws> { self.copy_range(false)?; } KeyCode::Char('y') => self.copy_range(false)?, + KeyCode::Char('x') => { + if let (Some(from), Some(to)) = (self.state.range_select.start.as_ref(), self.state.range_select.end.as_ref()) { + self.book.extend_to(from, to)?; + } + self.exit_range_select_mode()?; + } _ => { // moop } @@ -670,7 +677,7 @@ impl<'ws> Workspace<'ws> { self.enter_edit_mode(); } KeyCode::Char('r') if key.modifiers == KeyModifiers::CONTROL => { - self.enter_range_select_mode(); + self.enter_range_select_mode(false); } KeyCode::Char('c') if key.modifiers == KeyModifiers::CONTROL => { self.state.clipboard = Some(ClipboardContents::Cell( @@ -697,7 +704,7 @@ impl<'ws> Workspace<'ws> { )); } KeyCode::Char('v') if key.modifiers != KeyModifiers::CONTROL => { - self.enter_range_select_mode() + self.enter_range_select_mode(true) } KeyCode::Char('p') if key.modifiers != KeyModifiers::CONTROL => { self.paste_range()?; @@ -810,6 +817,7 @@ impl<'ws> Workspace<'ws> { })?; } KeyCode::Char('g') => { + // TODO(zaphar): This really needs a better state machine. if self.state.char_queue.first().map(|c| *c == 'g').unwrap_or(false) { self.state.char_queue.pop(); self.move_to_top()?; @@ -819,6 +827,7 @@ impl<'ws> Workspace<'ws> { } _ => { // noop + self.state.char_queue.clear(); } } } @@ -829,6 +838,7 @@ impl<'ws> Workspace<'ws> { match &self.state.clipboard { Some(ClipboardContents::Cell(contents)) => { self.book.edit_current_cell(contents)?; + self.book.evaluate(); } Some(ClipboardContents::Range(ref rows)) => { let Address { row, col } = self.book.location.clone(); @@ -846,6 +856,7 @@ impl<'ws> Workspace<'ws> { )?; } } + self.book.evaluate(); } None => { // NOOP @@ -878,11 +889,15 @@ impl<'ws> Workspace<'ws> { self.state.modality_stack.push(Modality::Dialog); } - fn enter_range_select_mode(&mut self) { + fn enter_range_select_mode(&mut self, init_start: bool) { self.state.range_select.sheet = Some(self.book.current_sheet); self.state.range_select.original_sheet = Some(self.book.current_sheet); self.state.range_select.original_location = Some(self.book.location.clone()); - self.state.range_select.start = None; + if init_start { + self.state.range_select.start = Some(self.book.location.clone()); + } else { + self.state.range_select.start = None; + } self.state.range_select.end = None; self.state.modality_stack.push(Modality::RangeSelect); }