mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-10-17 08:42:28 +00:00
use history variant
This commit is contained in:
1 parent
187296a277
commit
1bfd319758
14 files changed
+181
-121
No files matched your search
+106
-75
@@ -3,17 +3,17 @@
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use super::dto::*;
|
use super::dto::*;
|
||||||
use crate::{self as engine, Action, Command, Engine, LngLat, LngLatBounds};
|
use crate::{self as engine, Action, Command, Engine, History, LngLat, LngLatBounds};
|
||||||
|
|
||||||
/// Executes an action and tells what it did.
|
/// Executes an action and tells what it did.
|
||||||
pub fn execute(engine: &mut Engine, action: Action) -> Outcome {
|
pub fn execute<H: History>(engine: &mut Engine<H>, action: Action) -> Outcome {
|
||||||
match engine.execute(action) {
|
match engine.execute(action) {
|
||||||
Ok(outcome) => Outcome::new(&outcome, None),
|
Ok(outcome) => Outcome::new(&outcome, None),
|
||||||
Err(error) => Outcome::failed(error.to_string()),
|
Err(error) => Outcome::failed(error.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn edit(engine: &mut Engine, command: Command) -> Outcome {
|
pub fn edit<H: History>(engine: &mut Engine<H>, command: Command) -> Outcome {
|
||||||
execute(engine, Action::Edit(command))
|
execute(engine, Action::Edit(command))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +24,11 @@ fn file_ids(ids: &[Uuid]) -> Vec<engine::FileId> {
|
|||||||
// Selection
|
// Selection
|
||||||
|
|
||||||
/// Selects files. Unknown files are ignored, and selecting nothing deselects everything.
|
/// Selects files. Unknown files are ignored, and selecting nothing deselects everything.
|
||||||
pub fn select(engine: &mut Engine, file_ids_: &[Uuid], mode: engine::SelectMode) -> Outcome {
|
pub fn select<H: History>(
|
||||||
|
engine: &mut Engine<H>,
|
||||||
|
file_ids_: &[Uuid],
|
||||||
|
mode: engine::SelectMode,
|
||||||
|
) -> Outcome {
|
||||||
select_elements(
|
select_elements(
|
||||||
engine,
|
engine,
|
||||||
engine::Selection::File {
|
engine::Selection::File {
|
||||||
@@ -36,8 +40,8 @@ pub fn select(engine: &mut Engine, file_ids_: &[Uuid], mode: engine::SelectMode)
|
|||||||
|
|
||||||
/// Selects elements: they are merged with the selection when it holds elements of the same kind in
|
/// Selects elements: they are merged with the selection when it holds elements of the same kind in
|
||||||
/// the same place (same file, same track for segments), otherwise they replace it.
|
/// the same place (same file, same track for segments), otherwise they replace it.
|
||||||
pub fn select_elements(
|
pub fn select_elements<H: History>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine<H>,
|
||||||
selection: engine::Selection,
|
selection: engine::Selection,
|
||||||
mode: engine::SelectMode,
|
mode: engine::SelectMode,
|
||||||
) -> Outcome {
|
) -> Outcome {
|
||||||
@@ -47,19 +51,19 @@ pub fn select_elements(
|
|||||||
/// Selects all the elements of the same kind as the selected ones, in the same place: all the
|
/// Selects all the elements of the same kind as the selected ones, in the same place: all the
|
||||||
/// files, the tracks of the file, the segments of the track, the waypoints of the file. All the
|
/// files, the tracks of the file, the segments of the track, the waypoints of the file. All the
|
||||||
/// files when nothing is selected.
|
/// files when nothing is selected.
|
||||||
pub fn select_all(engine: &mut Engine) -> Outcome {
|
pub fn select_all<H: History>(engine: &mut Engine<H>) -> Outcome {
|
||||||
execute(engine, Action::SelectAll)
|
execute(engine, Action::SelectAll)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Moves the selection to the next (`down`) or previous element of the same kind, like the arrow
|
/// Moves the selection to the next (`down`) or previous element of the same kind, like the arrow
|
||||||
/// keys do in the file list. With `add` (shift + arrow), the element is added to the selection,
|
/// keys do in the file list. With `add` (shift + arrow), the element is added to the selection,
|
||||||
/// otherwise it replaces it.
|
/// otherwise it replaces it.
|
||||||
pub fn arrow_select(engine: &mut Engine, down: bool, add: bool) -> Outcome {
|
pub fn arrow_select<H: History>(engine: &mut Engine<H>, down: bool, add: bool) -> Outcome {
|
||||||
execute(engine, Action::ArrowSelect { down, add })
|
execute(engine, Action::ArrowSelect { down, add })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_tracks(
|
pub fn select_tracks<H: History>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine<H>,
|
||||||
file_id: Uuid,
|
file_id: Uuid,
|
||||||
track_ids: &[Uuid],
|
track_ids: &[Uuid],
|
||||||
mode: engine::SelectMode,
|
mode: engine::SelectMode,
|
||||||
@@ -71,8 +75,8 @@ pub fn select_tracks(
|
|||||||
select_elements(engine, selection, mode)
|
select_elements(engine, selection, mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_segments(
|
pub fn select_segments<H: History>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine<H>,
|
||||||
file_id: Uuid,
|
file_id: Uuid,
|
||||||
track_id: Uuid,
|
track_id: Uuid,
|
||||||
segment_ids: &[Uuid],
|
segment_ids: &[Uuid],
|
||||||
@@ -91,8 +95,8 @@ pub fn select_segments(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Selects the node standing for all the waypoints of a file.
|
/// Selects the node standing for all the waypoints of a file.
|
||||||
pub fn select_waypoint_group(
|
pub fn select_waypoint_group<H: History>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine<H>,
|
||||||
file_id: Uuid,
|
file_id: Uuid,
|
||||||
mode: engine::SelectMode,
|
mode: engine::SelectMode,
|
||||||
) -> Outcome {
|
) -> Outcome {
|
||||||
@@ -102,8 +106,8 @@ pub fn select_waypoint_group(
|
|||||||
select_elements(engine, selection, mode)
|
select_elements(engine, selection, mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_waypoints(
|
pub fn select_waypoints<H: History>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine<H>,
|
||||||
file_id: Uuid,
|
file_id: Uuid,
|
||||||
waypoint_ids: &[Uuid],
|
waypoint_ids: &[Uuid],
|
||||||
mode: engine::SelectMode,
|
mode: engine::SelectMode,
|
||||||
@@ -121,11 +125,11 @@ pub fn select_waypoints(
|
|||||||
|
|
||||||
// History
|
// History
|
||||||
|
|
||||||
pub fn undo(engine: &mut Engine) -> Outcome {
|
pub fn undo<H: History>(engine: &mut Engine<H>) -> Outcome {
|
||||||
execute(engine, Action::Undo)
|
execute(engine, Action::Undo)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn redo(engine: &mut Engine) -> Outcome {
|
pub fn redo<H: History>(engine: &mut Engine<H>) -> Outcome {
|
||||||
execute(engine, Action::Redo)
|
execute(engine, Action::Redo)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,8 +137,8 @@ pub fn redo(engine: &mut Engine) -> Outcome {
|
|||||||
|
|
||||||
/// Creates a file and selects it. With `lng` and `lat`, it starts with a track and a segment that
|
/// Creates a file and selects it. With `lng` and `lat`, it starts with a track and a segment that
|
||||||
/// hold that trackpoint (at `ele`, 0 by default).
|
/// hold that trackpoint (at `ele`, 0 by default).
|
||||||
pub fn new_file(
|
pub fn new_file<H: History>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine<H>,
|
||||||
name: &str,
|
name: &str,
|
||||||
lng: Option<f64>,
|
lng: Option<f64>,
|
||||||
lat: Option<f64>,
|
lat: Option<f64>,
|
||||||
@@ -152,8 +156,8 @@ pub fn new_file(
|
|||||||
/// `lengths` being the number of bytes of each one. `names`: the name of each file when its data
|
/// `lengths` being the number of bytes of each one. `names`: the name of each file when its data
|
||||||
/// has none (the name on disk, without extension). The files that cannot be read are skipped, the
|
/// has none (the name on disk, without extension). The files that cannot be read are skipped, the
|
||||||
/// first one that was read is selected.
|
/// first one that was read is selected.
|
||||||
pub fn load_files(
|
pub fn load_files<H: History>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine<H>,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
lengths: &[usize],
|
lengths: &[usize],
|
||||||
names: &[String],
|
names: &[String],
|
||||||
@@ -181,7 +185,11 @@ pub fn load_files(
|
|||||||
/// Brings changes of the files from the outside in (see [`engine::SyncFiles`]): each file is
|
/// Brings changes of the files from the outside in (see [`engine::SyncFiles`]): each file is
|
||||||
/// `(id, data, name)`, and replaces the file of that id, or is added if there is none. The files to
|
/// `(id, data, name)`, and replaces the file of that id, or is added if there is none. The files to
|
||||||
/// `remove` are deleted. The selection is left alone.
|
/// `remove` are deleted. The selection is left alone.
|
||||||
pub fn sync_files(engine: &mut Engine, files: &[(Uuid, &[u8], &str)], remove: &[Uuid]) -> Outcome {
|
pub fn sync_files<H: History>(
|
||||||
|
engine: &mut Engine<H>,
|
||||||
|
files: &[(Uuid, &[u8], &str)],
|
||||||
|
remove: &[Uuid],
|
||||||
|
) -> Outcome {
|
||||||
let files = files
|
let files = files
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(id, data, name)| engine::ExternalFile {
|
.map(|(id, data, name)| engine::ExternalFile {
|
||||||
@@ -199,22 +207,22 @@ pub fn sync_files(engine: &mut Engine, files: &[(Uuid, &[u8], &str)], remove: &[
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn duplicate(engine: &mut Engine) -> Outcome {
|
pub fn duplicate<H: History>(engine: &mut Engine<H>) -> Outcome {
|
||||||
edit(engine, Command::Duplicate(engine::Duplicate))
|
edit(engine, Command::Duplicate(engine::Duplicate))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deletes the selected elements. With `whole_files`, the files holding the selected elements are
|
/// Deletes the selected elements. With `whole_files`, the files holding the selected elements are
|
||||||
/// deleted instead, even if only a track or a waypoint is selected.
|
/// deleted instead, even if only a track or a waypoint is selected.
|
||||||
pub fn delete(engine: &mut Engine, whole_files: bool) -> Outcome {
|
pub fn delete<H: History>(engine: &mut Engine<H>, whole_files: bool) -> Outcome {
|
||||||
edit(engine, Command::Delete(engine::Delete { whole_files }))
|
edit(engine, Command::Delete(engine::Delete { whole_files }))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_all(engine: &mut Engine) -> Outcome {
|
pub fn delete_all<H: History>(engine: &mut Engine<H>) -> Outcome {
|
||||||
edit(engine, Command::DeleteAll(engine::DeleteAll))
|
edit(engine, Command::DeleteAll(engine::DeleteAll))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Moves the files to `index` among the other files, in the given order (not undoable).
|
/// Moves the files to `index` among the other files, in the given order (not undoable).
|
||||||
pub fn reorder(engine: &mut Engine, file_ids_: &[Uuid], index: usize) -> Outcome {
|
pub fn reorder<H: History>(engine: &mut Engine<H>, file_ids_: &[Uuid], index: usize) -> Outcome {
|
||||||
execute(
|
execute(
|
||||||
engine,
|
engine,
|
||||||
Action::Reorder {
|
Action::Reorder {
|
||||||
@@ -225,12 +233,12 @@ pub fn reorder(engine: &mut Engine, file_ids_: &[Uuid], index: usize) -> Outcome
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Puts the selected elements in the clipboard. Nothing happens when nothing is selected.
|
/// Puts the selected elements in the clipboard. Nothing happens when nothing is selected.
|
||||||
pub fn copy(engine: &mut Engine) -> Outcome {
|
pub fn copy<H: History>(engine: &mut Engine<H>) -> Outcome {
|
||||||
execute(engine, Action::Copy)
|
execute(engine, Action::Copy)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like `copy`, but the elements are moved when they are pasted.
|
/// Like `copy`, but the elements are moved when they are pasted.
|
||||||
pub fn cut(engine: &mut Engine) -> Outcome {
|
pub fn cut<H: History>(engine: &mut Engine<H>) -> Outcome {
|
||||||
execute(engine, Action::Cut)
|
execute(engine, Action::Cut)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,7 +246,7 @@ pub fn cut(engine: &mut Engine) -> Outcome {
|
|||||||
/// elements as they were when they were copied or cut, even if they were changed or deleted since.
|
/// elements as they were when they were copied or cut, even if they were changed or deleted since.
|
||||||
/// It can be pasted several times: what was copied is copied again, what was cut is moved the first
|
/// It can be pasted several times: what was copied is copied again, what was cut is moved the first
|
||||||
/// time (and the clipboard is not cut after that), then it is copied.
|
/// time (and the clipboard is not cut after that), then it is copied.
|
||||||
pub fn paste(engine: &mut Engine) -> Outcome {
|
pub fn paste<H: History>(engine: &mut Engine<H>) -> Outcome {
|
||||||
edit(engine, Command::Paste(engine::Paste))
|
edit(engine, Command::Paste(engine::Paste))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,7 +257,11 @@ pub fn paste(engine: &mut Engine) -> Outcome {
|
|||||||
/// of a file; segments among the files (each becomes a file), the tracks of a file (each becomes
|
/// of a file; segments among the files (each becomes a file), the tracks of a file (each becomes
|
||||||
/// a track) or the segments of a track; waypoints, or all the waypoints of a file, among the
|
/// a track) or the segments of a track; waypoints, or all the waypoints of a file, among the
|
||||||
/// waypoints of a file.
|
/// waypoints of a file.
|
||||||
pub fn move_elements(engine: &mut Engine, what: Selection, to: MoveTarget) -> Outcome {
|
pub fn move_elements<H: History>(
|
||||||
|
engine: &mut Engine<H>,
|
||||||
|
what: Selection,
|
||||||
|
to: MoveTarget,
|
||||||
|
) -> Outcome {
|
||||||
edit(
|
edit(
|
||||||
engine,
|
engine,
|
||||||
Command::Move(engine::Move {
|
Command::Move(engine::Move {
|
||||||
@@ -259,13 +271,13 @@ pub fn move_elements(engine: &mut Engine, what: Selection, to: MoveTarget) -> Ou
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn metadata(engine: &mut Engine, name: &str, desc: &str) -> Outcome {
|
pub fn metadata<H: History>(engine: &mut Engine<H>, name: &str, desc: &str) -> Outcome {
|
||||||
edit(engine, Command::Metadata(engine::Metadata { name, desc }))
|
edit(engine, Command::Metadata(engine::Metadata { name, desc }))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Only the given fields are changed.
|
/// Only the given fields are changed.
|
||||||
pub fn style(
|
pub fn style<H: History>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine<H>,
|
||||||
color: Option<&str>,
|
color: Option<&str>,
|
||||||
opacity: Option<f64>,
|
opacity: Option<f64>,
|
||||||
width: Option<f64>,
|
width: Option<f64>,
|
||||||
@@ -280,11 +292,11 @@ pub fn style(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_track(engine: &mut Engine) -> Outcome {
|
pub fn new_track<H: History>(engine: &mut Engine<H>) -> Outcome {
|
||||||
edit(engine, Command::NewTrack(engine::NewTrack))
|
edit(engine, Command::NewTrack(engine::NewTrack))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_track_segment(engine: &mut Engine) -> Outcome {
|
pub fn new_track_segment<H: History>(engine: &mut Engine<H>) -> Outcome {
|
||||||
edit(engine, Command::NewTrackSegment(engine::NewTrackSegment))
|
edit(engine, Command::NewTrackSegment(engine::NewTrackSegment))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,8 +304,8 @@ pub fn new_track_segment(engine: &mut Engine) -> Outcome {
|
|||||||
|
|
||||||
/// Adds a waypoint to each selected file (or to the file of the selected elements).
|
/// Adds a waypoint to each selected file (or to the file of the selected elements).
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new_waypoint(
|
pub fn new_waypoint<H: History>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine<H>,
|
||||||
lng: f64,
|
lng: f64,
|
||||||
lat: f64,
|
lat: f64,
|
||||||
ele: f64,
|
ele: f64,
|
||||||
@@ -317,8 +329,8 @@ pub fn new_waypoint(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Moves a waypoint of a file, whatever is selected.
|
/// Moves a waypoint of a file, whatever is selected.
|
||||||
pub fn move_waypoint(
|
pub fn move_waypoint<H: History>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine<H>,
|
||||||
file_id: Uuid,
|
file_id: Uuid,
|
||||||
waypoint_id: Uuid,
|
waypoint_id: Uuid,
|
||||||
lng: f64,
|
lng: f64,
|
||||||
@@ -338,7 +350,11 @@ pub fn move_waypoint(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Deletes a waypoint of a file, whatever is selected.
|
/// Deletes a waypoint of a file, whatever is selected.
|
||||||
pub fn delete_waypoint(engine: &mut Engine, file_id: Uuid, waypoint_id: Uuid) -> Outcome {
|
pub fn delete_waypoint<H: History>(
|
||||||
|
engine: &mut Engine<H>,
|
||||||
|
file_id: Uuid,
|
||||||
|
waypoint_id: Uuid,
|
||||||
|
) -> Outcome {
|
||||||
edit(
|
edit(
|
||||||
engine,
|
engine,
|
||||||
Command::DeleteWaypoint(engine::DeleteWaypoint {
|
Command::DeleteWaypoint(engine::DeleteWaypoint {
|
||||||
@@ -351,8 +367,8 @@ pub fn delete_waypoint(engine: &mut Engine, file_id: Uuid, waypoint_id: Uuid) ->
|
|||||||
/// Changes a waypoint of a file, whatever is selected: its name, description, icon, link,
|
/// Changes a waypoint of a file, whatever is selected: its name, description, icon, link,
|
||||||
/// position and elevation.
|
/// position and elevation.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn update_waypoint(
|
pub fn update_waypoint<H: History>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine<H>,
|
||||||
file_id: Uuid,
|
file_id: Uuid,
|
||||||
waypoint_id: Uuid,
|
waypoint_id: Uuid,
|
||||||
lng: f64,
|
lng: f64,
|
||||||
@@ -381,19 +397,19 @@ pub fn update_waypoint(
|
|||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
|
|
||||||
pub fn reverse(engine: &mut Engine) -> Outcome {
|
pub fn reverse<H: History>(engine: &mut Engine<H>) -> Outcome {
|
||||||
edit(engine, Command::Reverse(engine::Reverse))
|
edit(engine, Command::Reverse(engine::Reverse))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds to each selected segment a reversed copy of itself (without repeating its last point), so
|
/// Adds to each selected segment a reversed copy of itself (without repeating its last point), so
|
||||||
/// that it goes back to where it started.
|
/// that it goes back to where it started.
|
||||||
pub fn round_trip(engine: &mut Engine) -> Outcome {
|
pub fn round_trip<H: History>(engine: &mut Engine<H>) -> Outcome {
|
||||||
edit(engine, Command::RoundTrip(engine::RoundTrip))
|
edit(engine, Command::RoundTrip(engine::RoundTrip))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Keeps the trackpoints `start` to `end` (both included) of the selection, counted over its
|
/// Keeps the trackpoints `start` to `end` (both included) of the selection, counted over its
|
||||||
/// segments one after the other, and removes the others (and what is left empty).
|
/// segments one after the other, and removes the others (and what is left empty).
|
||||||
pub fn crop(engine: &mut Engine, start: u32, end: u32) -> Outcome {
|
pub fn crop<H: History>(engine: &mut Engine<H>, start: u32, end: u32) -> Outcome {
|
||||||
if start > end {
|
if start > end {
|
||||||
return Outcome::failed(format!("the range {start}..{end} is reversed"));
|
return Outcome::failed(format!("the range {start}..{end} is reversed"));
|
||||||
}
|
}
|
||||||
@@ -402,14 +418,19 @@ pub fn crop(engine: &mut Engine, start: u32, end: u32) -> Outcome {
|
|||||||
|
|
||||||
/// Removes the trackpoints of the selection that are less than `tolerance` meters away from the
|
/// Removes the trackpoints of the selection that are less than `tolerance` meters away from the
|
||||||
/// line of their neighbours, except the ends of the segments.
|
/// line of their neighbours, except the ends of the segments.
|
||||||
pub fn reduce(engine: &mut Engine, tolerance: f64) -> Outcome {
|
pub fn reduce<H: History>(engine: &mut Engine<H>, tolerance: f64) -> Outcome {
|
||||||
edit(engine, Command::Reduce(engine::Reduce { tolerance }))
|
edit(engine, Command::Reduce(engine::Reduce { tolerance }))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the timestamps of the selection, which starts at `start_time` (ms since the epoch): the
|
/// Sets the timestamps of the selection, which starts at `start_time` (ms since the epoch): the
|
||||||
/// trackpoints that have some keep their durations multiplied by `ratio`, the others follow the
|
/// trackpoints that have some keep their durations multiplied by `ratio`, the others follow the
|
||||||
/// previous one at `speed` (km/h).
|
/// previous one at `speed` (km/h).
|
||||||
pub fn change_timestamps(engine: &mut Engine, start_time: f64, speed: f64, ratio: f64) -> Outcome {
|
pub fn change_timestamps<H: History>(
|
||||||
|
engine: &mut Engine<H>,
|
||||||
|
start_time: f64,
|
||||||
|
speed: f64,
|
||||||
|
ratio: f64,
|
||||||
|
) -> Outcome {
|
||||||
edit(
|
edit(
|
||||||
engine,
|
engine,
|
||||||
Command::Time(engine::Time {
|
Command::Time(engine::Time {
|
||||||
@@ -421,8 +442,8 @@ pub fn change_timestamps(engine: &mut Engine, start_time: f64, speed: f64, ratio
|
|||||||
|
|
||||||
/// Makes up timestamps for the selection, which starts at `start_time` (ms since the epoch) and
|
/// Makes up timestamps for the selection, which starts at `start_time` (ms since the epoch) and
|
||||||
/// lasts `total_time` seconds.
|
/// lasts `total_time` seconds.
|
||||||
pub fn create_artificial_timestamps(
|
pub fn create_artificial_timestamps<H: History>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine<H>,
|
||||||
start_time: f64,
|
start_time: f64,
|
||||||
total_time: f64,
|
total_time: f64,
|
||||||
) -> Outcome {
|
) -> Outcome {
|
||||||
@@ -436,38 +457,43 @@ pub fn create_artificial_timestamps(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Merges the selection into its first element. `remove_gaps` brings the parts that are connected
|
/// Merges the selection into its first element. `remove_gaps` brings the parts that are connected
|
||||||
/// closer in time, if they have timestamps. With `keep_originals`, merged files are left as they
|
/// closer in time, if they have timestamps. Merged files are kept (and the result is a new file) if
|
||||||
/// are and the result is a new file.
|
/// the history needs it, see [`History::KEEPS_ORIGINALS`].
|
||||||
pub fn merge(
|
pub fn merge<H: History>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine<H>,
|
||||||
type_: engine::MergeType,
|
type_: engine::MergeType,
|
||||||
remove_gaps: bool,
|
remove_gaps: bool,
|
||||||
keep_originals: bool,
|
|
||||||
) -> Outcome {
|
) -> Outcome {
|
||||||
edit(
|
edit(
|
||||||
engine,
|
engine,
|
||||||
Command::Merge(engine::Merge {
|
Command::Merge(engine::Merge {
|
||||||
type_,
|
type_,
|
||||||
remove_gaps,
|
remove_gaps,
|
||||||
keep_originals,
|
keep_originals: H::KEEPS_ORIGINALS,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// With `keep_originals`, extracted files are left as they are and the result is new files.
|
/// Extracted files are kept (and the result is new files) if the history needs it, see
|
||||||
pub fn extract(engine: &mut Engine, keep_originals: bool) -> Outcome {
|
/// [`History::KEEPS_ORIGINALS`].
|
||||||
edit(engine, Command::Extract(engine::Extract { keep_originals }))
|
pub fn extract<H: History>(engine: &mut Engine<H>) -> Outcome {
|
||||||
|
edit(
|
||||||
|
engine,
|
||||||
|
Command::Extract(engine::Extract {
|
||||||
|
keep_originals: H::KEEPS_ORIGINALS,
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the elevation of the trackpoints of the selection, one elevation per trackpoint.
|
/// Sets the elevation of the trackpoints of the selection, one elevation per trackpoint.
|
||||||
pub fn elevation(engine: &mut Engine, ele: &[f64]) -> Outcome {
|
pub fn elevation<H: History>(engine: &mut Engine<H>, ele: &[f64]) -> Outcome {
|
||||||
edit(engine, Command::Elevation(engine::Elevation { ele }))
|
edit(engine, Command::Elevation(engine::Elevation { ele }))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes the trackpoints and/or waypoints of the selection inside (or outside) a rectangle.
|
/// Removes the trackpoints and/or waypoints of the selection inside (or outside) a rectangle.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn clean(
|
pub fn clean<H: History>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine<H>,
|
||||||
west: f64,
|
west: f64,
|
||||||
south: f64,
|
south: f64,
|
||||||
east: f64,
|
east: f64,
|
||||||
@@ -503,10 +529,10 @@ pub fn clean(
|
|||||||
// that use them do nothing if the selection is not the one of that revision anymore.
|
// that use them do nothing if the selection is not the one of that revision anymore.
|
||||||
|
|
||||||
/// Makes the call `f`, unless the selection is not the one of the revision anymore.
|
/// Makes the call `f`, unless the selection is not the one of the revision anymore.
|
||||||
fn at_revision(
|
fn at_revision<H: History>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine<H>,
|
||||||
revision: u32,
|
revision: u32,
|
||||||
f: impl FnOnce(&mut Engine) -> Outcome,
|
f: impl FnOnce(&mut Engine<H>) -> Outcome,
|
||||||
) -> Outcome {
|
) -> Outcome {
|
||||||
if engine.routing().revision == revision {
|
if engine.routing().revision == revision {
|
||||||
f(engine)
|
f(engine)
|
||||||
@@ -520,8 +546,8 @@ fn at_revision(
|
|||||||
/// surface, highway, SAC scale and MTB scale of the new points, as intervals, and `anchors` the
|
/// surface, highway, SAC scale and MTB scale of the new points, as intervals, and `anchors` the
|
||||||
/// indices among the new points of the ones that become anchors.
|
/// indices among the new points of the ones that become anchors.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn route(
|
pub fn route<H: History>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine<H>,
|
||||||
revision: u32,
|
revision: u32,
|
||||||
start: u32,
|
start: u32,
|
||||||
end: u32,
|
end: u32,
|
||||||
@@ -555,7 +581,12 @@ pub fn route(
|
|||||||
|
|
||||||
/// Makes an anchor of the point of the selected segments that is the closest to the coordinates,
|
/// Makes an anchor of the point of the selected segments that is the closest to the coordinates,
|
||||||
/// inserting a trackpoint on the path if none is there.
|
/// inserting a trackpoint on the path if none is there.
|
||||||
pub fn insert_anchor(engine: &mut Engine, revision: u32, lng: f64, lat: f64) -> Outcome {
|
pub fn insert_anchor<H: History>(
|
||||||
|
engine: &mut Engine<H>,
|
||||||
|
revision: u32,
|
||||||
|
lng: f64,
|
||||||
|
lat: f64,
|
||||||
|
) -> Outcome {
|
||||||
at_revision(engine, revision, |engine| {
|
at_revision(engine, revision, |engine| {
|
||||||
edit(
|
edit(
|
||||||
engine,
|
engine,
|
||||||
@@ -566,7 +597,7 @@ pub fn insert_anchor(engine: &mut Engine, revision: u32, lng: f64, lat: f64) ->
|
|||||||
|
|
||||||
/// Makes the trackpoint `index` of the selection the start of its segment, which is a loop (the
|
/// Makes the trackpoint `index` of the selection the start of its segment, which is a loop (the
|
||||||
/// trackpoints before it go to the end, and the loop closes on it).
|
/// trackpoints before it go to the end, and the loop closes on it).
|
||||||
pub fn change_loop_start(engine: &mut Engine, revision: u32, index: u32) -> Outcome {
|
pub fn change_loop_start<H: History>(engine: &mut Engine<H>, revision: u32, index: u32) -> Outcome {
|
||||||
at_revision(engine, revision, |engine| {
|
at_revision(engine, revision, |engine| {
|
||||||
edit(
|
edit(
|
||||||
engine,
|
engine,
|
||||||
@@ -576,14 +607,14 @@ pub fn change_loop_start(engine: &mut Engine, revision: u32, index: u32) -> Outc
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Cuts the file, the track or the segment (see `SplitType`) of the trackpoint `at` of the
|
/// Cuts the file, the track or the segment (see `SplitType`) of the trackpoint `at` of the
|
||||||
/// selection in two, there: the trackpoint ends the first part and starts the second one. With
|
/// selection in two, there: the trackpoint ends the first part and starts the second one. A file that
|
||||||
/// `keep_originals`, a file that is cut is left as it is and its two parts are new files.
|
/// is cut is kept (and its two parts are new files) if the history needs it, see
|
||||||
pub fn split(
|
/// [`History::KEEPS_ORIGINALS`].
|
||||||
engine: &mut Engine,
|
pub fn split<H: History>(
|
||||||
|
engine: &mut Engine<H>,
|
||||||
revision: u32,
|
revision: u32,
|
||||||
at: u32,
|
at: u32,
|
||||||
split_type: engine::SplitType,
|
split_type: engine::SplitType,
|
||||||
keep_originals: bool,
|
|
||||||
) -> Outcome {
|
) -> Outcome {
|
||||||
at_revision(engine, revision, |engine| {
|
at_revision(engine, revision, |engine| {
|
||||||
edit(
|
edit(
|
||||||
@@ -591,7 +622,7 @@ pub fn split(
|
|||||||
Command::Split(engine::Split {
|
Command::Split(engine::Split {
|
||||||
at,
|
at,
|
||||||
split_type,
|
split_type,
|
||||||
keep_originals,
|
keep_originals: H::KEEPS_ORIGINALS,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//! are numbers (ms since the epoch), absent fields are skipped.
|
//! are numbers (ms since the epoch), absent fields are skipped.
|
||||||
|
|
||||||
use crate as engine;
|
use crate as engine;
|
||||||
use engine::FileId;
|
use engine::{FileId, History};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ impl Outcome {
|
|||||||
|
|
||||||
/// The outcome of a frontend that knows nothing of the engine: all the files are added, and the
|
/// The outcome of a frontend that knows nothing of the engine: all the files are added, and the
|
||||||
/// order, the selection and the clipboard are to be read.
|
/// order, the selection and the clipboard are to be read.
|
||||||
pub fn everything(engine: &engine::Engine) -> Self {
|
pub fn everything<H: History>(engine: &engine::Engine<H>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
changed: true,
|
changed: true,
|
||||||
order_changed: true,
|
order_changed: true,
|
||||||
|
|||||||
+39
-31
@@ -8,60 +8,64 @@
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use super::dto::*;
|
use super::dto::*;
|
||||||
use crate::{self as engine, Engine, FileId};
|
use crate::{self as engine, Engine, FileId, History};
|
||||||
|
|
||||||
/// Ids of the files in display order.
|
/// Ids of the files in display order.
|
||||||
pub fn file_order(engine: &Engine) -> Vec<String> {
|
pub fn file_order<H: History>(engine: &Engine<H>) -> Vec<String> {
|
||||||
ids(engine.order())
|
ids(engine.order())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Structure of a file, `None` if the id is unknown.
|
/// Structure of a file, `None` if the id is unknown.
|
||||||
pub fn file_structure(engine: &Engine, file_id: Uuid) -> Option<FileStructure> {
|
pub fn file_structure<H: History>(engine: &Engine<H>, file_id: Uuid) -> Option<FileStructure> {
|
||||||
engine
|
engine
|
||||||
.file_structure(&FileId(file_id))
|
.file_structure(&FileId(file_id))
|
||||||
.map(FileStructure::new)
|
.map(FileStructure::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Global statistics of a file, `None` if the id is unknown.
|
/// Global statistics of a file, `None` if the id is unknown.
|
||||||
pub fn file_statistics(engine: &Engine, file_id: Uuid) -> Option<GlobalStatistics> {
|
pub fn file_statistics<H: History>(engine: &Engine<H>, file_id: Uuid) -> Option<GlobalStatistics> {
|
||||||
engine
|
engine
|
||||||
.file_statistics(&FileId(file_id))
|
.file_statistics(&FileId(file_id))
|
||||||
.map(|stats| GlobalStatistics::new(&stats))
|
.map(|stats| GlobalStatistics::new(&stats))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The current selection.
|
/// The current selection.
|
||||||
pub fn selection(engine: &Engine) -> Selection {
|
pub fn selection<H: History>(engine: &Engine<H>) -> Selection {
|
||||||
engine.selection().into()
|
engine.selection().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// What was copied or cut and is waiting to be pasted.
|
/// What was copied or cut and is waiting to be pasted.
|
||||||
pub fn clipboard(engine: &Engine) -> Option<Clipboard> {
|
pub fn clipboard<H: History>(engine: &Engine<H>) -> Option<Clipboard> {
|
||||||
engine.clipboard().map(Clipboard::new)
|
engine.clipboard().map(Clipboard::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_undo(engine: &Engine) -> bool {
|
pub fn can_undo<H: History>(engine: &Engine<H>) -> bool {
|
||||||
engine.can_undo()
|
engine.can_undo()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_redo(engine: &Engine) -> bool {
|
pub fn can_redo<H: History>(engine: &Engine<H>) -> bool {
|
||||||
engine.can_redo()
|
engine.can_redo()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the clipboard can be pasted with the current selection.
|
/// Whether the clipboard can be pasted with the current selection.
|
||||||
pub fn can_paste(engine: &Engine) -> bool {
|
pub fn can_paste<H: History>(engine: &Engine<H>) -> bool {
|
||||||
engine.can_paste()
|
engine.can_paste()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All the data of a waypoint of a file, `None` if it does not exist.
|
/// All the data of a waypoint of a file, `None` if it does not exist.
|
||||||
pub fn waypoint(engine: &Engine, file_id: Uuid, waypoint_id: Uuid) -> Option<WaypointDetails> {
|
pub fn waypoint<H: History>(
|
||||||
|
engine: &Engine<H>,
|
||||||
|
file_id: Uuid,
|
||||||
|
waypoint_id: Uuid,
|
||||||
|
) -> Option<WaypointDetails> {
|
||||||
engine
|
engine
|
||||||
.waypoint(&FileId(file_id), &engine::WaypointId(waypoint_id))
|
.waypoint(&FileId(file_id), &engine::WaypointId(waypoint_id))
|
||||||
.map(WaypointDetails::new)
|
.map(WaypointDetails::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trackpoint of a segment of a file, `None` if it does not exist.
|
/// A trackpoint of a segment of a file, `None` if it does not exist.
|
||||||
pub fn trackpoint(
|
pub fn trackpoint<H: History>(
|
||||||
engine: &Engine,
|
engine: &Engine<H>,
|
||||||
file_id: Uuid,
|
file_id: Uuid,
|
||||||
segment_id: Uuid,
|
segment_id: Uuid,
|
||||||
index: usize,
|
index: usize,
|
||||||
@@ -75,8 +79,8 @@ pub fn trackpoint(
|
|||||||
/// trackpoints to keep, and with `as_route` that the segments are written as routes (`rte`)
|
/// trackpoints to keep, and with `as_route` that the segments are written as routes (`rte`)
|
||||||
/// instead of tracks: the anchors are the route points, and the trackpoints between them the
|
/// instead of tracks: the anchors are the route points, and the trackpoints between them the
|
||||||
/// points of their path.
|
/// points of their path.
|
||||||
pub fn export_file(
|
pub fn export_file<H: History>(
|
||||||
engine: &Engine,
|
engine: &Engine<H>,
|
||||||
file_id: Uuid,
|
file_id: Uuid,
|
||||||
options: engine::ExportOptions,
|
options: engine::ExportOptions,
|
||||||
) -> Option<Vec<u8>> {
|
) -> Option<Vec<u8>> {
|
||||||
@@ -86,7 +90,7 @@ pub fn export_file(
|
|||||||
/// The data that some files have, so that the ones that none has are not offered when exporting
|
/// The data that some files have, so that the ones that none has are not offered when exporting
|
||||||
/// them: a bit for the time (1), heart rate (2), cadence (4), temperature (8), power (16) and the
|
/// them: a bit for the time (1), heart rate (2), cadence (4), temperature (8), power (16) and the
|
||||||
/// OpenStreetMap data (32).
|
/// OpenStreetMap data (32).
|
||||||
pub fn exportable_data(engine: &Engine, file_ids: &[Uuid]) -> u8 {
|
pub fn exportable_data<H: History>(engine: &Engine<H>, file_ids: &[Uuid]) -> u8 {
|
||||||
let ids: Vec<FileId> = file_ids.iter().copied().map(FileId).collect();
|
let ids: Vec<FileId> = file_ids.iter().copied().map(FileId).collect();
|
||||||
let data = engine.exportable_data(&ids);
|
let data = engine.exportable_data(&ids);
|
||||||
[
|
[
|
||||||
@@ -99,7 +103,7 @@ pub fn exportable_data(engine: &Engine, file_ids: &[Uuid]) -> u8 {
|
|||||||
|
|
||||||
/// For each trackpoint of the selection (numbered as in the statistics), the highest tolerance
|
/// For each trackpoint of the selection (numbered as in the statistics), the highest tolerance
|
||||||
/// (m) of `reduce` that keeps it: infinite for the ends of the segments.
|
/// (m) of `reduce` that keeps it: infinite for the ends of the segments.
|
||||||
pub fn reduction_distances(engine: &Engine) -> Vec<f64> {
|
pub fn reduction_distances<H: History>(engine: &Engine<H>) -> Vec<f64> {
|
||||||
engine.reduction_distances()
|
engine.reduction_distances()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,25 +111,29 @@ pub fn reduction_distances(engine: &Engine) -> Vec<f64> {
|
|||||||
// structures with the previous ones to know which ones actually changed.
|
// structures with the previous ones to know which ones actually changed.
|
||||||
|
|
||||||
/// Coordinates of the trackpoints of a segment (empty if the id is unknown).
|
/// Coordinates of the trackpoints of a segment (empty if the id is unknown).
|
||||||
pub fn segment_coordinates(engine: &Engine, segment_id: Uuid) -> &[f64] {
|
pub fn segment_coordinates<H: History>(engine: &Engine<H>, segment_id: Uuid) -> &[f64] {
|
||||||
engine.segment_coordinates(&engine::TrackSegmentId(segment_id))
|
engine.segment_coordinates(&engine::TrackSegmentId(segment_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Coordinates of the waypoints of a file (empty if the id is unknown).
|
/// Coordinates of the waypoints of a file (empty if the id is unknown).
|
||||||
pub fn waypoint_coordinates(engine: &Engine, file_id: Uuid) -> &[f64] {
|
pub fn waypoint_coordinates<H: History>(engine: &Engine<H>, file_id: Uuid) -> &[f64] {
|
||||||
engine.waypoint_coordinates(&FileId(file_id))
|
engine.waypoint_coordinates(&FileId(file_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Statistics of the selection
|
// Statistics of the selection
|
||||||
|
|
||||||
/// Global statistics of the whole selection (the buffers below cover it).
|
/// Global statistics of the whole selection (the buffers below cover it).
|
||||||
pub fn selection_statistics(engine: &Engine) -> GlobalStatistics {
|
pub fn selection_statistics<H: History>(engine: &Engine<H>) -> GlobalStatistics {
|
||||||
GlobalStatistics::new(&engine.statistics().global)
|
GlobalStatistics::new(&engine.statistics().global)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Global statistics of the trackpoints from `start` to `end` (both included) of the selection,
|
/// Global statistics of the trackpoints from `start` to `end` (both included) of the selection,
|
||||||
/// as indexed by the buffers. `None` when the range is not in the selection.
|
/// as indexed by the buffers. `None` when the range is not in the selection.
|
||||||
pub fn slice_statistics(engine: &Engine, start: usize, end: usize) -> Option<GlobalStatistics> {
|
pub fn slice_statistics<H: History>(
|
||||||
|
engine: &Engine<H>,
|
||||||
|
start: usize,
|
||||||
|
end: usize,
|
||||||
|
) -> Option<GlobalStatistics> {
|
||||||
engine
|
engine
|
||||||
.statistics()
|
.statistics()
|
||||||
.slice(start, end)
|
.slice(start, end)
|
||||||
@@ -134,7 +142,7 @@ pub fn slice_statistics(engine: &Engine, start: usize, end: usize) -> Option<Glo
|
|||||||
|
|
||||||
macro_rules! buffers {
|
macro_rules! buffers {
|
||||||
($($name:ident),*) => {$(
|
($($name:ident),*) => {$(
|
||||||
pub fn $name(engine: &Engine) -> &[f64] {
|
pub fn $name<H: History>(engine: &Engine<H>) -> &[f64] {
|
||||||
&engine.statistics().$name
|
&engine.statistics().$name
|
||||||
}
|
}
|
||||||
)*};
|
)*};
|
||||||
@@ -158,7 +166,7 @@ macro_rules! optional_buffers {
|
|||||||
/// `None` when no trackpoint of the selection has the value, else an entry per trackpoint,
|
/// `None` when no trackpoint of the selection has the value, else an entry per trackpoint,
|
||||||
/// the missing ones being NaN (`NO_TIME` for the timestamps, 0 before the first trackpoint
|
/// the missing ones being NaN (`NO_TIME` for the timestamps, 0 before the first trackpoint
|
||||||
/// that has them for the cumulative distances and durations).
|
/// that has them for the cumulative distances and durations).
|
||||||
pub fn $name(engine: &Engine) -> Option<&[$type]> {
|
pub fn $name<H: History>(engine: &Engine<H>) -> Option<&[$type]> {
|
||||||
engine.statistics().$name.as_deref()
|
engine.statistics().$name.as_deref()
|
||||||
}
|
}
|
||||||
)*};
|
)*};
|
||||||
@@ -168,7 +176,7 @@ optional_buffers!(moving_distance: f64, speed: f64, hr: f64, cad: f64, atemp: f6
|
|||||||
optional_buffers!(total_time: i64, moving_time: i64);
|
optional_buffers!(total_time: i64, moving_time: i64);
|
||||||
|
|
||||||
/// Timestamps in milliseconds since the epoch (`NO_TIME` when missing), see `optional_buffers`.
|
/// Timestamps in milliseconds since the epoch (`NO_TIME` when missing), see `optional_buffers`.
|
||||||
pub fn timestamps(engine: &Engine) -> Option<&[i64]> {
|
pub fn timestamps<H: History>(engine: &Engine<H>) -> Option<&[i64]> {
|
||||||
engine.statistics().time.as_deref()
|
engine.statistics().time.as_deref()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,17 +186,17 @@ pub fn timestamps(engine: &Engine) -> Option<&[i64]> {
|
|||||||
// A value that is unknown for every trackpoint is a single interval.
|
// A value that is unknown for every trackpoint is a single interval.
|
||||||
macro_rules! categories {
|
macro_rules! categories {
|
||||||
($starts:ident, $values:ident, $names:ident, $field:ident) => {
|
($starts:ident, $values:ident, $names:ident, $field:ident) => {
|
||||||
pub fn $starts(engine: &Engine) -> &[u32] {
|
pub fn $starts<H: History>(engine: &Engine<H>) -> &[u32] {
|
||||||
&engine.statistics().$field.starts
|
&engine.statistics().$field.starts
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn $values(engine: &Engine) -> &[u8] {
|
pub fn $values<H: History>(engine: &Engine<H>) -> &[u8] {
|
||||||
&engine.statistics().$field.values
|
&engine.statistics().$field.values
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The names of the category, in the order of their codes. The list only grows: a name
|
/// The names of the category, in the order of their codes. The list only grows: a name
|
||||||
/// keeps its index.
|
/// keeps its index.
|
||||||
pub fn $names(engine: &Engine) -> &[String] {
|
pub fn $names<H: History>(engine: &Engine<H>) -> &[String] {
|
||||||
engine.categories().$field.names()
|
engine.categories().$field.names()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -203,22 +211,22 @@ categories!(mtb_scale_starts, mtb_scale_values, mtb_scales, mtb_scale);
|
|||||||
// statistics). The ends of the segments are always anchors.
|
// statistics). The ends of the segments are always anchors.
|
||||||
|
|
||||||
/// The index in the selection of each anchor.
|
/// The index in the selection of each anchor.
|
||||||
pub fn anchor_indices(engine: &Engine) -> &[u32] {
|
pub fn anchor_indices<H: History>(engine: &Engine<H>) -> &[u32] {
|
||||||
&engine.routing().anchor_indices
|
&engine.routing().anchor_indices
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The lowest map zoom level at which each anchor is shown.
|
/// The lowest map zoom level at which each anchor is shown.
|
||||||
pub fn anchor_zooms(engine: &Engine) -> &[u8] {
|
pub fn anchor_zooms<H: History>(engine: &Engine<H>) -> &[u8] {
|
||||||
&engine.routing().anchor_zooms
|
&engine.routing().anchor_zooms
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Index in the selection of the first trackpoint of each selected segment that has some.
|
/// Index in the selection of the first trackpoint of each selected segment that has some.
|
||||||
pub fn segment_starts(engine: &Engine) -> &[u32] {
|
pub fn segment_starts<H: History>(engine: &Engine<H>) -> &[u32] {
|
||||||
&engine.routing().segment_starts
|
&engine.routing().segment_starts
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The id of each selected segment that has trackpoints, as `segment_starts`.
|
/// The id of each selected segment that has trackpoints, as `segment_starts`.
|
||||||
pub fn segment_ids(engine: &Engine) -> Vec<String> {
|
pub fn segment_ids<H: History>(engine: &Engine<H>) -> Vec<String> {
|
||||||
engine
|
engine
|
||||||
.routing()
|
.routing()
|
||||||
.segment_ids
|
.segment_ids
|
||||||
@@ -229,6 +237,6 @@ pub fn segment_ids(engine: &Engine) -> Vec<String> {
|
|||||||
|
|
||||||
/// Changes when the selected segments or their trackpoints change: the indices in the selection
|
/// Changes when the selected segments or their trackpoints change: the indices in the selection
|
||||||
/// are only valid while it stays the same.
|
/// are only valid while it stays the same.
|
||||||
pub fn routing_revision(engine: &Engine) -> u32 {
|
pub fn routing_revision<H: History>(engine: &Engine<H>) -> u32 {
|
||||||
engine.routing().revision
|
engine.routing().revision
|
||||||
}
|
}
|
||||||
@@ -57,6 +57,8 @@ impl GlobalHistory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl History for GlobalHistory {
|
impl History for GlobalHistory {
|
||||||
|
const KEEPS_ORIGINALS: bool = false;
|
||||||
|
|
||||||
fn restored(entry: Files) -> Self {
|
fn restored(entry: Files) -> Self {
|
||||||
Self {
|
Self {
|
||||||
entries: vec![entry],
|
entries: vec![entry],
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ pub type Files = HashMap<FileId, Rc<File>>;
|
|||||||
///
|
///
|
||||||
/// Files are told apart by their identity (`Rc::ptr_eq`), not their content.
|
/// Files are told apart by their identity (`Rc::ptr_eq`), not their content.
|
||||||
pub trait History: Default + Debug {
|
pub trait History: Default + Debug {
|
||||||
|
/// Whether the commands that replace files by others (split into files, extract, merge) must
|
||||||
|
/// keep the files they start from, and produce new ones. A history that forgets a file when it
|
||||||
|
/// goes could not undo the command, which a history of the whole workspace can.
|
||||||
|
const KEEPS_ORIGINALS: bool;
|
||||||
|
|
||||||
/// A history that starts from `files`, which cannot be undone.
|
/// A history that starts from `files`, which cannot be undone.
|
||||||
fn restored(files: Files) -> Self;
|
fn restored(files: Files) -> Self;
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,8 @@ impl PerFileHistory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl History for PerFileHistory {
|
impl History for PerFileHistory {
|
||||||
|
const KEEPS_ORIGINALS: bool = true;
|
||||||
|
|
||||||
fn restored(files: Files) -> Self {
|
fn restored(files: Files) -> Self {
|
||||||
let stacks = files
|
let stacks = files
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@@ -139,12 +139,12 @@ pub fn create_artificial_timestamps(
|
|||||||
|
|
||||||
#[tauri::command(async)]
|
#[tauri::command(async)]
|
||||||
pub fn merge(state: State<AppState>, merge_type: MergeType, remove_gaps: bool) -> api::Outcome {
|
pub fn merge(state: State<AppState>, merge_type: MergeType, remove_gaps: bool) -> api::Outcome {
|
||||||
state.run(move |e| api::merge(e, merge_type, remove_gaps, false))
|
state.run(move |e| api::merge(e, merge_type, remove_gaps))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command(async)]
|
#[tauri::command(async)]
|
||||||
pub fn extract(state: State<AppState>) -> api::Outcome {
|
pub fn extract(state: State<AppState>) -> api::Outcome {
|
||||||
state.run(|e| api::extract(e, false))
|
state.run(api::extract)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command(async)]
|
#[tauri::command(async)]
|
||||||
@@ -214,5 +214,5 @@ pub fn split(
|
|||||||
at: u32,
|
at: u32,
|
||||||
split_type: SplitType,
|
split_type: SplitType,
|
||||||
) -> api::Outcome {
|
) -> api::Outcome {
|
||||||
state.run(move |e| api::split(e, revision, at, split_type, false))
|
state.run(move |e| api::split(e, revision, at, split_type))
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,10 @@ mod workspace;
|
|||||||
use commands::*;
|
use commands::*;
|
||||||
use tauri::{Emitter, Manager};
|
use tauri::{Emitter, Manager};
|
||||||
|
|
||||||
|
/// The engine of the app: each file has its own history, so that undoing only concerns the
|
||||||
|
/// selected files, and opening or closing a file is not something to undo.
|
||||||
|
pub(crate) type Engine = gpx_engine::Engine<gpx_engine::PerFileHistory>;
|
||||||
|
|
||||||
/// The GPX files among what the system gave to the app (the arguments of the command line, when a
|
/// The GPX files among what the system gave to the app (the arguments of the command line, when a
|
||||||
/// file is opened with it).
|
/// file is opened with it).
|
||||||
fn gpx_paths(arguments: impl IntoIterator<Item = String>) -> Vec<std::path::PathBuf> {
|
fn gpx_paths(arguments: impl IntoIterator<Item = String>) -> Vec<std::path::PathBuf> {
|
||||||
|
|||||||
+2
-1
@@ -11,7 +11,8 @@ use std::sync::{Arc, Mutex};
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use gpx_engine::{Engine, Persister, Storage, api};
|
use crate::Engine;
|
||||||
|
use gpx_engine::{Persister, Storage, api};
|
||||||
use notify::RecursiveMode;
|
use notify::RecursiveMode;
|
||||||
use notify_debouncer_mini::{DebounceEventResult, Debouncer, new_debouncer};
|
use notify_debouncer_mini::{DebounceEventResult, Debouncer, new_debouncer};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|||||||
+3
-2
@@ -220,7 +220,8 @@ impl Storage for FileStorage {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use gpx_engine::{Action, Command, Engine, Load, LoadFiles, Persister};
|
use crate::Engine;
|
||||||
|
use gpx_engine::{Action, Command, Load, LoadFiles, Persister};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -255,7 +256,7 @@ mod tests {
|
|||||||
// another session
|
// another session
|
||||||
let storage = FileStorage::open(&root).unwrap();
|
let storage = FileStorage::open(&root).unwrap();
|
||||||
let restored = pollster::block_on(Persister::default().restore(&storage)).unwrap();
|
let restored = pollster::block_on(Persister::default().restore(&storage)).unwrap();
|
||||||
let mut other: Engine = Engine::default();
|
let mut other = Engine::default();
|
||||||
other.restore(restored);
|
other.restore(restored);
|
||||||
assert_eq!(other.order(), order);
|
assert_eq!(other.order(), order);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
+8
-3
@@ -165,7 +165,8 @@ fn edits_undoes_and_selects() {
|
|||||||
json!({ "name": "route", "lng": 4.4, "lat": 50.8 }),
|
json!({ "name": "route", "lng": 4.4, "lat": 50.8 }),
|
||||||
);
|
);
|
||||||
assert_eq!(outcome["changed"], true);
|
assert_eq!(outcome["changed"], true);
|
||||||
assert_eq!(app.call("can_undo", json!({})), true);
|
// each file has its own history, which starts when it appears
|
||||||
|
assert_eq!(app.call("can_undo", json!({})), false);
|
||||||
|
|
||||||
let id = app.call("file_order", json!({}))[0].clone();
|
let id = app.call("file_order", json!({}))[0].clone();
|
||||||
let outcome = app.call("metadata", json!({ "name": "renamed", "desc": "" }));
|
let outcome = app.call("metadata", json!({ "name": "renamed", "desc": "" }));
|
||||||
@@ -179,6 +180,7 @@ fn edits_undoes_and_selects() {
|
|||||||
assert_eq!(outcome["selectionChanged"], true);
|
assert_eq!(outcome["selectionChanged"], true);
|
||||||
assert_eq!(app.call("selection", json!({})), json!({ "type": "empty" }));
|
assert_eq!(app.call("selection", json!({})), json!({ "type": "empty" }));
|
||||||
app.call("select", json!({ "fileIds": [id], "mode": "toggle" }));
|
app.call("select", json!({ "fileIds": [id], "mode": "toggle" }));
|
||||||
|
assert_eq!(app.call("can_undo", json!({})), true);
|
||||||
|
|
||||||
app.call("undo", json!({}));
|
app.call("undo", json!({}));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -186,8 +188,11 @@ fn edits_undoes_and_selects() {
|
|||||||
"route"
|
"route"
|
||||||
);
|
);
|
||||||
assert_eq!(app.call("can_redo", json!({})), true);
|
assert_eq!(app.call("can_redo", json!({})), true);
|
||||||
app.call("undo", json!({}));
|
// creating the file is not something to undo
|
||||||
assert_eq!(app.call("file_order", json!({})), json!([]));
|
assert_eq!(app.call("can_undo", json!({})), false);
|
||||||
|
let outcome = app.call("undo", json!({}));
|
||||||
|
assert_eq!(outcome["changed"], false);
|
||||||
|
assert_eq!(app.call("file_order", json!({})), json!([id]));
|
||||||
|
|
||||||
// what cannot be done says why
|
// what cannot be done says why
|
||||||
let outcome = app.call("reverse", json!({}));
|
let outcome = app.call("reverse", json!({}));
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ use std::collections::BTreeMap;
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use gpx_engine::{Engine, ExportOptions, FileId, api};
|
use crate::Engine;
|
||||||
|
use gpx_engine::{ExportOptions, FileId, api};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
|||||||
@@ -62,5 +62,5 @@ pub fn change_loop_start(revision: u32, index: u32) -> Outcome {
|
|||||||
/// nothing, and returns false, if the revision of the routing buffers is not the given one.
|
/// nothing, and returns false, if the revision of the routing buffers is not the given one.
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn split(revision: u32, at: u32, split_type: SplitType) -> Outcome {
|
pub fn split(revision: u32, at: u32, split_type: SplitType) -> Outcome {
|
||||||
run(|e| api::split(e, revision, at, split_type.into(), false))
|
run(|e| api::split(e, revision, at, split_type.into()))
|
||||||
}
|
}
|
||||||
@@ -55,12 +55,12 @@ pub fn create_artificial_timestamps(start_time: f64, total_time: f64) -> Outcome
|
|||||||
/// that are connected closer in time, if they have timestamps.
|
/// that are connected closer in time, if they have timestamps.
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn merge(type_: MergeType, remove_gaps: bool) -> Outcome {
|
pub fn merge(type_: MergeType, remove_gaps: bool) -> Outcome {
|
||||||
run(|e| api::merge(e, type_.into(), remove_gaps, false))
|
run(|e| api::merge(e, type_.into(), remove_gaps))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn extract() -> Outcome {
|
pub fn extract() -> Outcome {
|
||||||
run(|e| api::extract(e, false))
|
run(api::extract)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the elevation of the trackpoints of the selection, one elevation per trackpoint.
|
/// Sets the elevation of the trackpoints of the selection, one elevation per trackpoint.
|
||||||
|
|||||||
Reference in new issue
Block a user