diff --git a/website/src/lib/components/Menu.svelte b/website/src/lib/components/Menu.svelte
index 5722a19b..0b938b39 100644
--- a/website/src/lib/components/Menu.svelte
+++ b/website/src/lib/components/Menu.svelte
@@ -1,652 +1,621 @@
-
-
-
-
-
-
-
-
-
- {$_('gpx.file')}
-
-
-
-
- {$_('menu.new')}
-
-
-
-
-
- {$_('menu.open')}
-
-
-
-
-
- {$_('menu.duplicate')}
-
-
-
-
-
- {$_('menu.close')}
-
-
-
-
- {$_('menu.close_all')}
-
-
-
- ($exportState = ExportState.SELECTION)}
- disabled={$selection.size == 0}
- >
-
- {$_('menu.export')}
-
-
- ($exportState = ExportState.ALL)}
- disabled={$fileObservers.size == 0}
- >
-
- {$_('menu.export_all')}
-
-
-
-
-
-
-
- {$_('menu.edit')}
-
-
-
-
- {$_('menu.undo')}
-
-
-
-
- {$_('menu.redo')}
-
-
-
-
- item instanceof ListFileItem ||
- item instanceof ListTrackItem
- )}
- on:click={() => ($editMetadata = true)}
- >
-
- {$_('menu.metadata.button')}
-
-
-
- item instanceof ListFileItem ||
- item instanceof ListTrackItem
- )}
- on:click={() => ($editStyle = true)}
- >
-
- {$_('menu.style.button')}
-
- {
- if ($allHidden) {
- dbUtils.setHiddenToSelection(false);
- } else {
- dbUtils.setHiddenToSelection(true);
- }
- }}
- disabled={$selection.size == 0}
- >
- {#if $allHidden}
-
- {$_('menu.unhide')}
- {:else}
-
- {$_('menu.hide')}
- {/if}
-
-
-
-
-
- {$_('menu.select_all')}
-
-
- {
- if ($selection.size > 0) {
- centerMapOnSelection();
- }
- }}
- >
-
- {$_('menu.center')}
-
-
- {#if $verticalFileView}
-
-
-
- {$_('menu.copy')}
-
-
-
-
- {$_('menu.cut')}
-
-
- 0 &&
- !allowedPastes[$copied[0].level].includes(
- $selection.getSelected().pop()?.level
- ))}
- on:click={pasteSelection}
- >
-
- {$_('menu.paste')}
-
-
- {/if}
-
-
-
- {$_('menu.delete')}
-
-
-
-
-
-
-
- {$_('menu.view')}
-
-
-
-
- {$_('menu.elevation_profile')}
-
-
-
-
- {$_('menu.vertical_file_view')}
-
-
-
-
- {$_('menu.switch_basemap')}
-
-
- {$_('menu.toggle_overlays')}
-
-
-
- {$_('menu.distance_markers')}
-
-
- {$_('menu.direction_markers')}
-
-
-
-
- {$_('menu.toggle_3d')}
-
-
-
-
-
-
-
-
- {$_('menu.settings')}
-
-
-
-
- {$_('menu.distance_units')}
-
-
-
- {$_('menu.metric')}
- {$_('menu.imperial')}
- {$_('menu.nautical')}
-
-
-
-
- {$_(
- 'menu.velocity_units'
- )}
-
-
- {$_('quantities.speed')}
- {$_('quantities.pace')}
-
-
-
-
-
- {$_('menu.temperature_units')}
-
-
-
- {$_('menu.celsius')}
- {$_('menu.fahrenheit')}
-
-
-
-
-
-
-
- {$_('menu.language')}
-
-
-
- {#each Object.entries(languages) as [lang, label]}
-
- {label}
-
- {/each}
-
-
-
-
-
- {#if selectedMode === 'light'}
-
- {:else}
-
- {/if}
- {$_('menu.mode')}
-
-
- {
- setMode(value);
- }}
- >
- {$_('menu.light')}
- {$_('menu.dark')}
-
-
-
-
-
-
-
- {$_('menu.street_view_source')}
-
-
-
- {$_('menu.mapillary')}
- {$_('menu.google')}
-
-
-
- (layerSettingsOpen = true)}>
-
- {$_('menu.layers')}
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ {$_('gpx.file')}
+
+
+
+
+ {$_('menu.new')}
+
+
+
+
+
+ {$_('menu.open')}
+
+
+
+
+
+ {$_('menu.duplicate')}
+
+
+
+
+
+ {$_('menu.close')}
+
+
+
+
+ {$_('menu.close_all')}
+
+
+
+ ($exportState = ExportState.SELECTION)}
+ disabled={$selection.size == 0}
+ >
+
+ {$_('menu.export')}
+
+
+ ($exportState = ExportState.ALL)}
+ disabled={$fileObservers.size == 0}
+ >
+
+ {$_('menu.export_all')}
+
+
+
+
+
+
+
+ {$_('menu.edit')}
+
+
+
+
+ {$_('menu.undo')}
+
+
+
+
+ {$_('menu.redo')}
+
+
+
+ item instanceof ListFileItem || item instanceof ListTrackItem)}
+ on:click={() => ($editMetadata = true)}
+ >
+
+ {$_('menu.metadata.button')}
+
+
+ item instanceof ListFileItem || item instanceof ListTrackItem)}
+ on:click={() => ($editStyle = true)}
+ >
+
+ {$_('menu.style.button')}
+
+ {
+ if ($allHidden) {
+ dbUtils.setHiddenToSelection(false);
+ } else {
+ dbUtils.setHiddenToSelection(true);
+ }
+ }}
+ disabled={$selection.size == 0}
+ >
+ {#if $allHidden}
+
+ {$_('menu.unhide')}
+ {:else}
+
+ {$_('menu.hide')}
+ {/if}
+
+
+ {#if $verticalFileView}
+ {#if $selection.getSelected().some((item) => item instanceof ListFileItem)}
+
+ dbUtils.addNewTrack($selection.getSelected()[0].getFileId())}
+ disabled={$selection.size !== 1}
+ >
+
+ {$_('menu.new_track')}
+
+ {:else if $selection.getSelected().some((item) => item instanceof ListTrackItem)}
+
+ {
+ let item = $selection.getSelected()[0];
+ dbUtils.addNewSegment(item.getFileId(), item.getTrackIndex());
+ }}
+ disabled={$selection.size !== 1}
+ >
+
+ {$_('menu.new_segment')}
+
+ {/if}
+
+
+
+ {$_('menu.select_all')}
+
+
+ {
+ if ($selection.size > 0) {
+ centerMapOnSelection();
+ }
+ }}
+ >
+
+ {$_('menu.center')}
+
+
+
+
+
+ {$_('menu.copy')}
+
+
+
+
+ {$_('menu.cut')}
+
+
+ 0 &&
+ !allowedPastes[$copied[0].level].includes($selection.getSelected().pop()?.level))}
+ on:click={pasteSelection}
+ >
+
+ {$_('menu.paste')}
+
+
+ {/if}
+
+
+
+ {$_('menu.delete')}
+
+
+
+
+
+
+
+ {$_('menu.view')}
+
+
+
+
+ {$_('menu.elevation_profile')}
+
+
+
+
+ {$_('menu.vertical_file_view')}
+
+
+
+
+ {$_('menu.switch_basemap')}
+
+
+ {$_('menu.toggle_overlays')}
+
+
+
+ {$_('menu.distance_markers')}
+
+
+ {$_('menu.direction_markers')}
+
+
+
+
+ {$_('menu.toggle_3d')}
+
+
+
+
+
+
+
+
+ {$_('menu.settings')}
+
+
+
+
+
+ {$_('menu.distance_units')}
+
+
+
+ {$_('menu.metric')}
+ {$_('menu.imperial')}
+ {$_('menu.nautical')}
+
+
+
+
+
+ {$_('menu.velocity_units')}
+
+
+
+ {$_('quantities.speed')}
+ {$_('quantities.pace')}
+
+
+
+
+
+ {$_('menu.temperature_units')}
+
+
+
+ {$_('menu.celsius')}
+ {$_('menu.fahrenheit')}
+
+
+
+
+
+
+
+ {$_('menu.language')}
+
+
+
+ {#each Object.entries(languages) as [lang, label]}
+
+ {label}
+
+ {/each}
+
+
+
+
+
+ {#if selectedMode === 'light'}
+
+ {:else}
+
+ {/if}
+ {$_('menu.mode')}
+
+
+ {
+ setMode(value);
+ }}
+ >
+ {$_('menu.light')}
+ {$_('menu.dark')}
+
+
+
+
+
+
+
+ {$_('menu.street_view_source')}
+
+
+
+ {$_('menu.mapillary')}
+ {$_('menu.google')}
+
+
+
+ (layerSettingsOpen = true)}>
+
+ {$_('menu.layers')}
+
+
+
+
+
+
+
+
+
{
- let targetInput =
- e.target.tagName === 'INPUT' ||
- e.target.tagName === 'TEXTAREA' ||
- e.target.tagName === 'SELECT' ||
- e.target.role === 'combobox' ||
- e.target.role === 'radio' ||
- e.target.role === 'menu' ||
- e.target.role === 'menuitem' ||
- e.target.role === 'menuitemradio' ||
- e.target.role === 'menuitemcheckbox';
+ on:keydown={(e) => {
+ let targetInput =
+ e.target.tagName === 'INPUT' ||
+ e.target.tagName === 'TEXTAREA' ||
+ e.target.tagName === 'SELECT' ||
+ e.target.role === 'combobox' ||
+ e.target.role === 'radio' ||
+ e.target.role === 'menu' ||
+ e.target.role === 'menuitem' ||
+ e.target.role === 'menuitemradio' ||
+ e.target.role === 'menuitemcheckbox';
- if (e.key === '+' && (e.metaKey || e.ctrlKey)) {
- createFile();
- e.preventDefault();
- } else if (e.key === 'o' && (e.metaKey || e.ctrlKey)) {
- triggerFileInput();
- e.preventDefault();
- } else if (e.key === 'd' && (e.metaKey || e.ctrlKey)) {
- dbUtils.duplicateSelection();
- e.preventDefault();
- } else if (e.key === 'c' && (e.metaKey || e.ctrlKey)) {
- if (!targetInput) {
- copySelection();
- e.preventDefault();
- }
- } else if (e.key === 'x' && (e.metaKey || e.ctrlKey)) {
- if (!targetInput) {
- cutSelection();
- e.preventDefault();
- }
- } else if (e.key === 'v' && (e.metaKey || e.ctrlKey)) {
- if (!targetInput) {
- pasteSelection();
- e.preventDefault();
- }
- } else if ((e.key === 's' || e.key == 'S') && (e.metaKey || e.ctrlKey)) {
- if (e.shiftKey) {
- if ($fileObservers.size > 0) {
- $exportState = ExportState.ALL;
- }
- } else if ($selection.size > 0) {
- $exportState = ExportState.SELECTION;
- }
- e.preventDefault();
- } else if ((e.key === 'z' || e.key == 'Z') && (e.metaKey || e.ctrlKey)) {
- if (e.shiftKey) {
- dbUtils.redo();
- } else {
- dbUtils.undo();
- }
- e.preventDefault();
- } else if ((e.key === 'Backspace' || e.key === 'Delete') && (e.metaKey || e.ctrlKey)) {
- if (!targetInput) {
- if (e.shiftKey) {
- dbUtils.deleteAllFiles();
- } else {
- dbUtils.deleteSelection();
- }
- e.preventDefault();
- }
- } else if (e.key === 'a' && (e.metaKey || e.ctrlKey)) {
- if (!targetInput) {
- selectAll();
- e.preventDefault();
- }
- } else if (e.key === 'i' && (e.metaKey || e.ctrlKey)) {
- if (
- $selection.size === 1 &&
- $selection
- .getSelected()
- .every((item) => item instanceof ListFileItem || item instanceof ListTrackItem)
- ) {
- $editMetadata = true;
- }
- e.preventDefault();
- } else if (e.key === 'p' && (e.metaKey || e.ctrlKey)) {
- $elevationProfile = !$elevationProfile;
- e.preventDefault();
- } else if (e.key === 'l' && (e.metaKey || e.ctrlKey)) {
- $verticalFileView = !$verticalFileView;
- e.preventDefault();
- } else if (e.key === 'h' && (e.metaKey || e.ctrlKey)) {
- if ($allHidden) {
- dbUtils.setHiddenToSelection(false);
- } else {
- dbUtils.setHiddenToSelection(true);
- }
- e.preventDefault();
- } else if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
- if ($selection.size > 0) {
- centerMapOnSelection();
- }
- } else if (e.key === 'F1') {
- switchBasemaps();
- e.preventDefault();
- } else if (e.key === 'F2') {
- toggleOverlays();
- e.preventDefault();
- } else if (e.key === 'F3') {
- $distanceMarkers = !$distanceMarkers;
- e.preventDefault();
- } else if (e.key === 'F4') {
- $directionMarkers = !$directionMarkers;
- e.preventDefault();
- } else if (e.key === 'F5') {
- $routing = !$routing;
- e.preventDefault();
- } else if (
- e.key === 'ArrowRight' ||
- e.key === 'ArrowDown' ||
- e.key === 'ArrowLeft' ||
- e.key === 'ArrowUp'
- ) {
- if (!targetInput) {
- updateSelectionFromKey(e.key === 'ArrowRight' || e.key === 'ArrowDown', e.shiftKey);
- e.preventDefault();
- }
- }
- }}
- on:dragover={(e) => e.preventDefault()}
- on:drop={(e) => {
- e.preventDefault();
- if (e.dataTransfer.files.length > 0) {
- loadFiles(e.dataTransfer.files);
- }
- }}
+ if (e.key === '+' && (e.metaKey || e.ctrlKey)) {
+ createFile();
+ e.preventDefault();
+ } else if (e.key === 'o' && (e.metaKey || e.ctrlKey)) {
+ triggerFileInput();
+ e.preventDefault();
+ } else if (e.key === 'd' && (e.metaKey || e.ctrlKey)) {
+ dbUtils.duplicateSelection();
+ e.preventDefault();
+ } else if (e.key === 'c' && (e.metaKey || e.ctrlKey)) {
+ if (!targetInput) {
+ copySelection();
+ e.preventDefault();
+ }
+ } else if (e.key === 'x' && (e.metaKey || e.ctrlKey)) {
+ if (!targetInput) {
+ cutSelection();
+ e.preventDefault();
+ }
+ } else if (e.key === 'v' && (e.metaKey || e.ctrlKey)) {
+ if (!targetInput) {
+ pasteSelection();
+ e.preventDefault();
+ }
+ } else if ((e.key === 's' || e.key == 'S') && (e.metaKey || e.ctrlKey)) {
+ if (e.shiftKey) {
+ if ($fileObservers.size > 0) {
+ $exportState = ExportState.ALL;
+ }
+ } else if ($selection.size > 0) {
+ $exportState = ExportState.SELECTION;
+ }
+ e.preventDefault();
+ } else if ((e.key === 'z' || e.key == 'Z') && (e.metaKey || e.ctrlKey)) {
+ if (e.shiftKey) {
+ dbUtils.redo();
+ } else {
+ dbUtils.undo();
+ }
+ e.preventDefault();
+ } else if ((e.key === 'Backspace' || e.key === 'Delete') && (e.metaKey || e.ctrlKey)) {
+ if (!targetInput) {
+ if (e.shiftKey) {
+ dbUtils.deleteAllFiles();
+ } else {
+ dbUtils.deleteSelection();
+ }
+ e.preventDefault();
+ }
+ } else if (e.key === 'a' && (e.metaKey || e.ctrlKey)) {
+ if (!targetInput) {
+ selectAll();
+ e.preventDefault();
+ }
+ } else if (e.key === 'i' && (e.metaKey || e.ctrlKey)) {
+ if (
+ $selection.size === 1 &&
+ $selection
+ .getSelected()
+ .every((item) => item instanceof ListFileItem || item instanceof ListTrackItem)
+ ) {
+ $editMetadata = true;
+ }
+ e.preventDefault();
+ } else if (e.key === 'p' && (e.metaKey || e.ctrlKey)) {
+ $elevationProfile = !$elevationProfile;
+ e.preventDefault();
+ } else if (e.key === 'l' && (e.metaKey || e.ctrlKey)) {
+ $verticalFileView = !$verticalFileView;
+ e.preventDefault();
+ } else if (e.key === 'h' && (e.metaKey || e.ctrlKey)) {
+ if ($allHidden) {
+ dbUtils.setHiddenToSelection(false);
+ } else {
+ dbUtils.setHiddenToSelection(true);
+ }
+ e.preventDefault();
+ } else if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
+ if ($selection.size > 0) {
+ centerMapOnSelection();
+ }
+ } else if (e.key === 'F1') {
+ switchBasemaps();
+ e.preventDefault();
+ } else if (e.key === 'F2') {
+ toggleOverlays();
+ e.preventDefault();
+ } else if (e.key === 'F3') {
+ $distanceMarkers = !$distanceMarkers;
+ e.preventDefault();
+ } else if (e.key === 'F4') {
+ $directionMarkers = !$directionMarkers;
+ e.preventDefault();
+ } else if (e.key === 'F5') {
+ $routing = !$routing;
+ e.preventDefault();
+ } else if (
+ e.key === 'ArrowRight' ||
+ e.key === 'ArrowDown' ||
+ e.key === 'ArrowLeft' ||
+ e.key === 'ArrowUp'
+ ) {
+ if (!targetInput) {
+ updateSelectionFromKey(e.key === 'ArrowRight' || e.key === 'ArrowDown', e.shiftKey);
+ e.preventDefault();
+ }
+ }
+ }}
+ on:dragover={(e) => e.preventDefault()}
+ on:drop={(e) => {
+ e.preventDefault();
+ if (e.dataTransfer.files.length > 0) {
+ loadFiles(e.dataTransfer.files);
+ }
+ }}
/>
diff --git a/website/src/lib/components/file-list/FileListNodeLabel.svelte b/website/src/lib/components/file-list/FileListNodeLabel.svelte
index f6880557..811fa855 100644
--- a/website/src/lib/components/file-list/FileListNodeLabel.svelte
+++ b/website/src/lib/components/file-list/FileListNodeLabel.svelte
@@ -248,10 +248,7 @@
{#if item instanceof ListFileItem}
- dbUtils.applyToFile(item.getFileId(), (file) =>
- file.replaceTracks(file.trk.length, file.trk.length, [new Track()])
- )}
+ on:click={() => dbUtils.addNewTrack(item.getFileId())}
>
{$_('menu.new_track')}
@@ -260,17 +257,7 @@
{:else if item instanceof ListTrackItem}
{
- let trackIndex = item.getTrackIndex();
- dbUtils.applyToFile(item.getFileId(), (file) =>
- file.replaceTrackSegments(
- trackIndex,
- file.trk[trackIndex].trkseg.length,
- file.trk[trackIndex].trkseg.length,
- [new TrackSegment()]
- )
- );
- }}
+ on:click={() => dbUtils.addNewSegment(item.getFileId(), item.getTrackIndex())}
>
{$_('menu.new_segment')}
diff --git a/website/src/lib/db.ts b/website/src/lib/db.ts
index 1ce5d3cc..eef1987c 100644
--- a/website/src/lib/db.ts
+++ b/website/src/lib/db.ts
@@ -512,6 +512,15 @@ export const dbUtils = {
});
});
},
+ addNewTrack: (fileId: string) => {
+ dbUtils.applyToFile(fileId, (file) => file.replaceTracks(file.trk.length, file.trk.length, [new Track()]));
+ },
+ addNewSegment: (fileId: string, trackIndex: number) => {
+ dbUtils.applyToFile(fileId, (file) => {
+ let track = file.trk[trackIndex];
+ track.replaceTrackSegments(track.trkseg.length, track.trkseg.length, [new TrackSegment()]);
+ });
+ },
reverseSelection: () => {
if (!get(selection).hasAnyChildren(new ListRootItem(), true, ['waypoints']) || get(gpxStatistics).local.points?.length <= 1) {
return;
diff --git a/website/src/lib/docs/en/menu/edit.mdx b/website/src/lib/docs/en/menu/edit.mdx
index 1584e881..42cf1f47 100644
--- a/website/src/lib/docs/en/menu/edit.mdx
+++ b/website/src/lib/docs/en/menu/edit.mdx
@@ -3,7 +3,7 @@ title: Edit actions
---
@@ -31,6 +31,28 @@ Open the appearance dialog, where you can change the color, opacity, and width o
Toggle the visibility of the selected file items on the map.
+### New track
+
+Create a new track in the selected file.
+
+
+
+This action is only available when the vertical layout of the files list is enabled.
+Additionally, the selection must be a single file.
+
+
+
+### New segment
+
+Create a new segment in the selected track.
+
+
+
+This action is only available when the vertical layout of the files list is enabled.
+Additionally, the selection must be a single track.
+
+
+
### Select all
Add all file items in the current hierarchy level to the selection.