1315 lines
32 KiB
C#
1315 lines
32 KiB
C#
using OperationControl.Controls;
|
|
using OperationControl.Mapas.Editing;
|
|
using OperationControl.Mapas.Interaction;
|
|
using OperationControl.Mapas.Models;
|
|
|
|
namespace OperationControl.Mapas
|
|
{
|
|
public sealed partial class MapaOperacaoController
|
|
{
|
|
public bool PodeDesfazerEdicao =>
|
|
_estadoEdicao.EmEdicao &&
|
|
_edicaoService
|
|
.PodeDesfazer(
|
|
_estadoEdicao
|
|
);
|
|
|
|
|
|
public bool PodeRefazerEdicao =>
|
|
_estadoEdicao.EmEdicao &&
|
|
_edicaoService
|
|
.PodeRefazer(
|
|
_estadoEdicao
|
|
);
|
|
|
|
|
|
|
|
private bool ValidarDocumentoAntesDoApply(MapaDocumento documento)
|
|
{
|
|
if (documento == null)
|
|
{
|
|
throw new ArgumentNullException(
|
|
nameof(documento)
|
|
);
|
|
}
|
|
|
|
|
|
MapaResultadoValidacao resultado =
|
|
_validationService
|
|
.Validar(
|
|
documento
|
|
);
|
|
|
|
|
|
/*
|
|
* Avisos não bloqueiam o Apply.
|
|
*
|
|
* Mas ficam registrados para diagnóstico.
|
|
*/
|
|
if (
|
|
resultado.QuantidadeAvisos > 0
|
|
)
|
|
{
|
|
Log(
|
|
"[MAPA][EDICAO][VALIDACAO] " +
|
|
$"Avisos={resultado.QuantidadeAvisos}"
|
|
);
|
|
|
|
|
|
foreach (
|
|
string aviso
|
|
in resultado.Avisos)
|
|
{
|
|
Log(
|
|
"[MAPA][EDICAO][VALIDACAO][AVISO] " +
|
|
aviso
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Sem erros:
|
|
* documento aprovado.
|
|
*/
|
|
if (resultado.Valido)
|
|
{
|
|
Log(
|
|
"[MAPA][EDICAO][VALIDACAO] " +
|
|
$"OK | " +
|
|
$"Ruas={documento.QuantidadeRuas} | " +
|
|
$"Avisos={resultado.QuantidadeAvisos}"
|
|
);
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* ERRO:
|
|
*
|
|
* absolutamente nada oficial é alterado.
|
|
*/
|
|
Log(
|
|
"[MAPA][EDICAO][VALIDACAO][FALHA] " +
|
|
$"Erros={resultado.QuantidadeErros} | " +
|
|
$"Avisos={resultado.QuantidadeAvisos}"
|
|
);
|
|
|
|
|
|
foreach (
|
|
string erro
|
|
in resultado.Erros)
|
|
{
|
|
Log(
|
|
"[MAPA][EDICAO][VALIDACAO][ERRO] " +
|
|
erro
|
|
);
|
|
}
|
|
|
|
|
|
ValidacaoEdicaoFalhou?
|
|
.Invoke(
|
|
resultado
|
|
);
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* =========================================================
|
|
* SESSÃO DE EDIÇÃO
|
|
* =========================================================
|
|
*/
|
|
|
|
public void IniciarEdicao(MapViewControl? mapaControl)
|
|
{
|
|
if (
|
|
mapaControl?.Mapa?.Map == null
|
|
)
|
|
{
|
|
throw new ArgumentNullException(
|
|
nameof(mapaControl)
|
|
);
|
|
}
|
|
|
|
|
|
if (_workspace.Documento == null)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Não existe mapa carregado para edição."
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
* Se ainda não há sessão, cria.
|
|
*/
|
|
if (!_estadoEdicao.EmEdicao)
|
|
{
|
|
_edicaoService
|
|
.Iniciar(
|
|
_estadoEdicao,
|
|
_workspace.Documento
|
|
);
|
|
|
|
|
|
/*
|
|
* Toda nova sessão começa no modo
|
|
* mais abrangente e previsível.
|
|
*/
|
|
_estadoRecorte
|
|
.Limpar();
|
|
}
|
|
|
|
|
|
RegistrarMapa(
|
|
mapaControl
|
|
);
|
|
|
|
|
|
/*
|
|
* Guarda o contexto somente na primeira entrada
|
|
* desta View na sessão de edição.
|
|
*/
|
|
if (
|
|
!_mapasEmEdicao.Contains(
|
|
mapaControl
|
|
)
|
|
)
|
|
{
|
|
_modoInteracaoAntesEdicao[
|
|
mapaControl
|
|
] =
|
|
mapaControl.ModoInteracao;
|
|
}
|
|
|
|
|
|
_mapasEmEdicao.Add(
|
|
mapaControl
|
|
);
|
|
|
|
|
|
DefinirModoInteracao(
|
|
mapaControl,
|
|
MapaModoInteracao.Edicao
|
|
);
|
|
|
|
|
|
_ferramentaEdicaoAtual = MapaFerramentaEdicao.Selecionar;
|
|
|
|
|
|
RenderizarEdicaoNoMapa(
|
|
mapaControl
|
|
);
|
|
|
|
SincronizarCockpitEdicao();
|
|
|
|
EstadoEdicaoAlterado?
|
|
.Invoke();
|
|
|
|
|
|
Log(
|
|
"[MAPA][EDICAO] " +
|
|
$"Modo edição iniciado | " +
|
|
$"Ruas=" +
|
|
$"{_estadoEdicao.DocumentoTrabalho?.QuantidadeRuas ?? 0}"
|
|
);
|
|
}
|
|
|
|
|
|
public bool AplicarEdicao()
|
|
{
|
|
if (
|
|
!_estadoEdicao.EmEdicao ||
|
|
_estadoEdicao.DocumentoTrabalho == null
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* O botão ficará desabilitado quando não houver
|
|
* alterações, mas mantemos a proteção também aqui.
|
|
*/
|
|
if (!EdicaoModificada)
|
|
{
|
|
Log(
|
|
"[MAPA][EDICAO][APLICAR] " +
|
|
"Nenhuma alteração pendente."
|
|
);
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
if (_workspace.Documento == null)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Não existe documento oficial para receber a edição."
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
* =====================================================
|
|
* 1. CONSTRUÇÃO DO NOVO ESTADO FORA DO WORKSPACE
|
|
* =====================================================
|
|
*
|
|
* ObterResultado já devolve uma cópia independente.
|
|
*
|
|
* Nada oficial é alterado até toda a preparação
|
|
* abaixo terminar com sucesso.
|
|
*/
|
|
MapaDocumento novoDocumento =
|
|
_edicaoService
|
|
.ObterResultado(
|
|
_estadoEdicao
|
|
);
|
|
|
|
|
|
/*
|
|
* O resultado agora representa uma alteração
|
|
* posterior à fonte originalmente carregada.
|
|
*/
|
|
novoDocumento.Modificado =
|
|
true;
|
|
|
|
|
|
/*
|
|
* =====================================================
|
|
* 2. VALIDAR DOCUMENTO EDITADO
|
|
* =====================================================
|
|
*
|
|
* Esta é a última fronteira antes do mapa
|
|
* poder se tornar oficial.
|
|
*
|
|
* Importante:
|
|
*
|
|
* novoDocumento é uma cópia independente.
|
|
*
|
|
* Portanto, se a validação falhar:
|
|
*
|
|
* - Workspace continua intacto;
|
|
* - DocumentoFonte continua intacto;
|
|
* - DocumentoTrabalho continua disponível;
|
|
* - Undo/Redo continuam disponíveis;
|
|
* - sessão de edição continua aberta.
|
|
*/
|
|
if (!ValidarDocumentoAntesDoApply(novoDocumento))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* =====================================================
|
|
* 3. RECONCILIAR RUAS DA MISSÃO
|
|
* =====================================================
|
|
*/
|
|
|
|
var idsExistentes =
|
|
novoDocumento
|
|
.Ruas
|
|
.Where(
|
|
x =>
|
|
x != null &&
|
|
!string.IsNullOrWhiteSpace(
|
|
x.Id
|
|
)
|
|
)
|
|
.Select(
|
|
x =>
|
|
x.Id
|
|
)
|
|
.ToHashSet(
|
|
StringComparer.OrdinalIgnoreCase
|
|
);
|
|
|
|
|
|
List<string> percursoAnterior =
|
|
_workspace
|
|
.Selecao
|
|
.RuasOrdenadas
|
|
.ToList();
|
|
|
|
|
|
List<string> percursoReconciliado =
|
|
percursoAnterior
|
|
.Where(
|
|
id =>
|
|
idsExistentes.Contains(
|
|
id
|
|
)
|
|
)
|
|
.ToList();
|
|
|
|
|
|
int ruasPercursoRemovidas =
|
|
percursoAnterior.Count -
|
|
percursoReconciliado.Count;
|
|
|
|
|
|
var novaSelecao =
|
|
new MapaSelecaoOperacao();
|
|
|
|
|
|
/*
|
|
* DefinirSelecao valida tudo antes de alterar
|
|
* novaSelecao.
|
|
*/
|
|
_selectionService
|
|
.DefinirSelecao(
|
|
novoDocumento,
|
|
novaSelecao,
|
|
percursoReconciliado
|
|
);
|
|
|
|
|
|
/*
|
|
* =====================================================
|
|
* 3. CLONAR ROTA DE RETORNO
|
|
* =====================================================
|
|
*
|
|
* Poderíamos reutilizar a mesma referência porque
|
|
* Apply não altera a rota, mas é melhor manter
|
|
* a transação completamente independente.
|
|
*/
|
|
var novaRotaRetorno =
|
|
new MapaRotaRetorno();
|
|
|
|
|
|
var contratoRota =
|
|
_rotaRetornoService
|
|
.ExportarContrato(
|
|
_workspace.RotaRetorno
|
|
)
|
|
.ToList();
|
|
|
|
|
|
_rotaRetornoService
|
|
.DefinirPeloContrato(
|
|
novaRotaRetorno,
|
|
contratoRota
|
|
);
|
|
|
|
|
|
/*
|
|
* Guardamos as Views antes de desmontar
|
|
* a sessão.
|
|
*/
|
|
var mapasEdicao =
|
|
_mapasEmEdicao
|
|
.ToArray();
|
|
|
|
|
|
int ruasAntes =
|
|
_workspace.Documento.QuantidadeRuas;
|
|
|
|
int ruasDepois =
|
|
novoDocumento.QuantidadeRuas;
|
|
|
|
|
|
/*
|
|
* =====================================================
|
|
* 4. COMMIT
|
|
* =====================================================
|
|
*
|
|
* Importante:
|
|
*
|
|
* DefinirEstado NÃO altera DocumentoFonte.
|
|
*/
|
|
_workspace.DefinirEstado(
|
|
novoDocumento,
|
|
novaSelecao,
|
|
novaRotaRetorno
|
|
);
|
|
|
|
|
|
/*
|
|
* =====================================================
|
|
* 5. ENCERRAR A SESSÃO DE EDIÇÃO
|
|
* =====================================================
|
|
*/
|
|
_edicaoService
|
|
.Cancelar(
|
|
_estadoEdicao
|
|
);
|
|
|
|
_estadoRecorte.Limpar();
|
|
|
|
|
|
foreach (
|
|
var mapa
|
|
in mapasEdicao)
|
|
{
|
|
mapa.DefinirEstadoEditor(
|
|
ativo: false,
|
|
ferramenta:
|
|
MapaFerramentaEdicao.Nenhuma,
|
|
quantidadeSelecionadas: 0,
|
|
modificado: false,
|
|
podeDesfazer: false,
|
|
podeRefazer: false
|
|
);
|
|
|
|
|
|
/*
|
|
* Remove a representação do DocumentoTrabalho.
|
|
*/
|
|
RemoverRenderEdicao(
|
|
mapa
|
|
);
|
|
|
|
|
|
MapaModoInteracao modoRestaurar =
|
|
_modoInteracaoAntesEdicao
|
|
.TryGetValue(
|
|
mapa,
|
|
out var anterior
|
|
)
|
|
? anterior
|
|
: MapaModoInteracao.Visualizacao;
|
|
|
|
|
|
_mapasEmEdicao.Remove(
|
|
mapa
|
|
);
|
|
|
|
|
|
_modoInteracaoAntesEdicao.Remove(
|
|
mapa
|
|
);
|
|
|
|
|
|
DefinirModoInteracao(
|
|
mapa,
|
|
modoRestaurar
|
|
);
|
|
}
|
|
|
|
|
|
_ferramentaEdicaoAtual =
|
|
MapaFerramentaEdicao.Nenhuma;
|
|
|
|
|
|
_mapasEmEdicao.Clear();
|
|
_modoInteracaoAntesEdicao.Clear();
|
|
|
|
|
|
/*
|
|
* =====================================================
|
|
* 6. RECONSTRUIR TODAS AS VIEWS DO MAPA
|
|
* =====================================================
|
|
*
|
|
* Não apenas a View que estava editando.
|
|
*
|
|
* Pode haver outro MapViewControl registrado mostrando
|
|
* o documento oficial antigo.
|
|
*/
|
|
InvalidarRendersWorkspace();
|
|
|
|
|
|
foreach (
|
|
var mapa
|
|
in _mapasRegistrados
|
|
.ToArray())
|
|
{
|
|
PrepararMapa(
|
|
mapa,
|
|
mapa.ModoInteracao,
|
|
enquadrarDocumento: false
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
* Estado oficial mudou E sessão de edição acabou.
|
|
*/
|
|
EstadoEdicaoAlterado?
|
|
.Invoke();
|
|
|
|
|
|
EstadoMapaAlterado?
|
|
.Invoke();
|
|
|
|
|
|
Log(
|
|
"[MAPA][EDICAO][APLICAR] " +
|
|
$"Ruas={ruasAntes}->{ruasDepois} | " +
|
|
$"Percurso={percursoAnterior.Count}" +
|
|
$"->{percursoReconciliado.Count} | " +
|
|
$"RemovidasDoPercurso={ruasPercursoRemovidas} | " +
|
|
$"Fonte={QuantidadeRuasFonte}"
|
|
);
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
public void CancelarEdicao(MapViewControl? mapaControl = null)
|
|
{
|
|
if (!_estadoEdicao.EmEdicao)
|
|
return;
|
|
|
|
|
|
/*
|
|
* A sessão de edição é global ao Controller.
|
|
*
|
|
* Mesmo que o chamador informe uma View específica,
|
|
* cancelar a sessão precisa retirar TODAS as Views que
|
|
* estejam mostrando o DocumentoTrabalho.
|
|
*/
|
|
var mapasEdicao =
|
|
_mapasEmEdicao
|
|
.ToArray();
|
|
|
|
|
|
_edicaoService
|
|
.Cancelar(
|
|
_estadoEdicao
|
|
);
|
|
|
|
|
|
_estadoRecorte.Limpar();
|
|
|
|
|
|
foreach (
|
|
var mapa
|
|
in mapasEdicao)
|
|
{
|
|
mapa.DefinirEstadoEditor(
|
|
ativo: false,
|
|
ferramenta: MapaFerramentaEdicao.Nenhuma,
|
|
quantidadeSelecionadas: 0,
|
|
modificado: false,
|
|
podeDesfazer: false,
|
|
podeRefazer: false
|
|
);
|
|
|
|
RemoverRenderEdicao(
|
|
mapa
|
|
);
|
|
|
|
|
|
MapaModoInteracao modoRestaurar =
|
|
_modoInteracaoAntesEdicao
|
|
.TryGetValue(
|
|
mapa,
|
|
out var anterior
|
|
)
|
|
? anterior
|
|
: MapaModoInteracao.Visualizacao;
|
|
|
|
|
|
_mapasEmEdicao.Remove(mapa);
|
|
|
|
_modoInteracaoAntesEdicao.Remove(mapa);
|
|
|
|
|
|
/*
|
|
* Volta imediatamente para a
|
|
* representação OFICIAL.
|
|
*/
|
|
PrepararMapa(
|
|
mapa,
|
|
modoRestaurar,
|
|
enquadrarDocumento: false
|
|
);
|
|
}
|
|
|
|
_ferramentaEdicaoAtual = MapaFerramentaEdicao.Nenhuma;
|
|
|
|
|
|
_mapasEmEdicao.Clear();
|
|
_modoInteracaoAntesEdicao.Clear();
|
|
|
|
|
|
EstadoEdicaoAlterado?
|
|
.Invoke();
|
|
|
|
|
|
Log(
|
|
"[MAPA][EDICAO] " +
|
|
"Sessão cancelada."
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
* =========================================================
|
|
* SELEÇÃO DE EDIÇÃO
|
|
* =========================================================
|
|
*/
|
|
|
|
public bool AlternarRuaEdicao(string ruaId)
|
|
{
|
|
bool selecionada =
|
|
_edicaoService
|
|
.AlternarRua(
|
|
_estadoEdicao,
|
|
ruaId
|
|
);
|
|
|
|
|
|
foreach (
|
|
var mapa
|
|
in _mapasEmEdicao
|
|
.ToArray())
|
|
{
|
|
if (
|
|
_rendersMapa
|
|
.TryGetValue(
|
|
mapa,
|
|
out var render
|
|
)
|
|
)
|
|
{
|
|
_edicaoRenderService
|
|
.AtualizarEstiloRua(
|
|
render,
|
|
_estadoEdicao,
|
|
ruaId
|
|
);
|
|
|
|
|
|
mapa.Mapa.Refresh();
|
|
}
|
|
}
|
|
|
|
SincronizarCockpitEdicao();
|
|
|
|
EstadoEdicaoAlterado?
|
|
.Invoke();
|
|
|
|
|
|
Log(
|
|
"[MAPA][EDICAO][SELECAO] " +
|
|
$"Rua={ruaId} | " +
|
|
$"Selecionada={selecionada} | " +
|
|
$"Total={QuantidadeRuasSelecionadasEdicao}"
|
|
);
|
|
|
|
|
|
return selecionada;
|
|
}
|
|
|
|
|
|
public void SelecionarRuaEdicao(string ruaId)
|
|
{
|
|
_edicaoService
|
|
.SelecionarRua(
|
|
_estadoEdicao,
|
|
ruaId
|
|
);
|
|
}
|
|
|
|
|
|
public void RemoverRuaSelecaoEdicao(string ruaId)
|
|
{
|
|
_edicaoService
|
|
.RemoverRuaSelecao(
|
|
_estadoEdicao,
|
|
ruaId
|
|
);
|
|
}
|
|
|
|
|
|
public void LimparSelecaoEdicao()
|
|
{
|
|
if (!_estadoEdicao.EmEdicao)
|
|
return;
|
|
|
|
|
|
_edicaoService
|
|
.LimparSelecao(
|
|
_estadoEdicao
|
|
);
|
|
|
|
|
|
foreach (
|
|
var mapa
|
|
in _mapasEmEdicao
|
|
.ToArray())
|
|
{
|
|
if (
|
|
_rendersMapa
|
|
.TryGetValue(
|
|
mapa,
|
|
out var render
|
|
)
|
|
)
|
|
{
|
|
_edicaoRenderService
|
|
.AplicarSelecao(
|
|
render,
|
|
_estadoEdicao
|
|
);
|
|
|
|
|
|
mapa.Mapa.Refresh();
|
|
}
|
|
}
|
|
|
|
SincronizarCockpitEdicao();
|
|
|
|
|
|
EstadoEdicaoAlterado?
|
|
.Invoke();
|
|
|
|
|
|
Log(
|
|
"[MAPA][EDICAO][SELECAO] " +
|
|
"Seleção limpa."
|
|
);
|
|
}
|
|
|
|
|
|
public void DefinirFerramentaEdicao(MapaFerramentaEdicao ferramenta)
|
|
{
|
|
if (!_estadoEdicao.EmEdicao)
|
|
return;
|
|
|
|
|
|
if (_ferramentaEdicaoAtual == ferramenta)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
_ferramentaEdicaoAtual = ferramenta;
|
|
|
|
|
|
if (ferramenta == MapaFerramentaEdicao.RecorteBordaGnss)
|
|
{
|
|
_estadoRecorte.AtivarBordaGnssManual();
|
|
}
|
|
|
|
|
|
AtualizarVisualBordaGnss();
|
|
|
|
|
|
SincronizarCockpitEdicao();
|
|
|
|
|
|
Log(
|
|
"[MAPA][EDICAO][FERRAMENTA] " +
|
|
$"Atual={ferramenta}"
|
|
);
|
|
}
|
|
|
|
|
|
private void SincronizarCockpitEdicao()
|
|
{
|
|
foreach (
|
|
var mapa
|
|
in _mapasEmEdicao
|
|
.ToArray())
|
|
{
|
|
mapa.DefinirEstadoEditor(
|
|
ativo: _estadoEdicao.EmEdicao,
|
|
ferramenta: _ferramentaEdicaoAtual,
|
|
quantidadeSelecionadas: QuantidadeRuasSelecionadasEdicao,
|
|
modificado: EdicaoModificada,
|
|
podeDesfazer: PodeDesfazerEdicao,
|
|
podeRefazer: PodeRefazerEdicao,
|
|
escopoRecorte: _estadoRecorte.Escopo,
|
|
bordaGnss: CriarResumoBordaGnss(),
|
|
possuiMapaFonte: _workspace.PossuiDocumentoFonte
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* =========================================================
|
|
* OPERAÇÕES DE EDIÇÃO
|
|
* =========================================================
|
|
*/
|
|
|
|
public int RemoverRuasSelecionadasEdicao()
|
|
{
|
|
if (!_estadoEdicao.EmEdicao)
|
|
return 0;
|
|
|
|
|
|
if (!_estadoEdicao.PossuiSelecao)
|
|
return 0;
|
|
|
|
|
|
int selecionadasAntes =
|
|
_estadoEdicao
|
|
.QuantidadeSelecionadas;
|
|
|
|
|
|
int removidas =
|
|
_edicaoService
|
|
.RemoverRuasSelecionadas(
|
|
_estadoEdicao
|
|
);
|
|
|
|
|
|
if (removidas <= 0)
|
|
return 0;
|
|
|
|
|
|
/*
|
|
* Aqui houve alteração estrutural:
|
|
*
|
|
* ruas deixaram de existir.
|
|
*
|
|
* Portanto atualizar somente estilo
|
|
* não é suficiente. Reconstruímos
|
|
* o render do DocumentoTrabalho.
|
|
*/
|
|
RenderizarDocumentoEdicaoAtual();
|
|
|
|
|
|
/*
|
|
* Atualiza:
|
|
*
|
|
* seleção = 0
|
|
* modificado = true
|
|
* Undo = habilitado
|
|
* Redo = conforme histórico
|
|
*/
|
|
SincronizarCockpitEdicao();
|
|
|
|
|
|
EstadoEdicaoAlterado?
|
|
.Invoke();
|
|
|
|
|
|
Log(
|
|
"[MAPA][EDICAO][REMOVER] " +
|
|
$"Selecionadas={selecionadasAntes} | " +
|
|
$"Removidas={removidas} | " +
|
|
$"Restantes=" +
|
|
$"{_estadoEdicao.DocumentoTrabalho?.QuantidadeRuas ?? 0} | " +
|
|
$"Undo={PodeDesfazerEdicao} | " +
|
|
$"Redo={PodeRefazerEdicao}"
|
|
);
|
|
|
|
|
|
return removidas;
|
|
}
|
|
|
|
|
|
public bool RestaurarMapaFonteEdicao()
|
|
{
|
|
if (
|
|
!_estadoEdicao.EmEdicao ||
|
|
_estadoEdicao.DocumentoTrabalho == null
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
if (
|
|
!_workspace.PossuiDocumentoFonte ||
|
|
_workspace.DocumentoFonte == null
|
|
)
|
|
{
|
|
Log(
|
|
"[MAPA][EDICAO][RESTAURAR-FONTE] " +
|
|
"Documento-fonte indisponível."
|
|
);
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
int ruasAntes =
|
|
_estadoEdicao
|
|
.DocumentoTrabalho
|
|
.QuantidadeRuas;
|
|
|
|
|
|
bool alterado =
|
|
_edicaoService
|
|
.RestaurarDocumentoFonte(
|
|
_estadoEdicao,
|
|
_workspace.DocumentoFonte
|
|
);
|
|
|
|
|
|
if (!alterado)
|
|
{
|
|
Log(
|
|
"[MAPA][EDICAO][RESTAURAR-FONTE] " +
|
|
"Documento de trabalho já corresponde " +
|
|
"ao mapa original."
|
|
);
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* Qualquer gesto ou levantamento temporário
|
|
* deixa de fazer sentido diante de uma
|
|
* restauração completa.
|
|
*/
|
|
_estadoRecorte
|
|
.Limpar();
|
|
|
|
|
|
_ferramentaEdicaoAtual =
|
|
MapaFerramentaEdicao
|
|
.Selecionar;
|
|
|
|
|
|
/*
|
|
* Se você já está usando o preview GNSS
|
|
* que construímos:
|
|
*/
|
|
AtualizarVisualBordaGnss();
|
|
|
|
|
|
/*
|
|
* Houve mudança estrutural completa.
|
|
*
|
|
* Não basta atualizar estilo.
|
|
*/
|
|
RenderizarDocumentoEdicaoAtual();
|
|
|
|
|
|
SincronizarCockpitEdicao();
|
|
|
|
|
|
EstadoEdicaoAlterado?
|
|
.Invoke();
|
|
|
|
|
|
Log(
|
|
"[MAPA][EDICAO][RESTAURAR-FONTE] " +
|
|
$"Ruas={ruasAntes}" +
|
|
$"->{_estadoEdicao.DocumentoTrabalho.QuantidadeRuas} | " +
|
|
$"Fonte={_workspace.DocumentoFonte.QuantidadeRuas} | " +
|
|
$"Undo={PodeDesfazerEdicao}"
|
|
);
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Sugere um nome previsível para a versão persistida do
|
|
/// DocumentoTrabalho sem alterar o documento nem a sessão.
|
|
/// </summary>
|
|
public string ObterNomeArquivoSugeridoMapaEditado()
|
|
{
|
|
MapaDocumento? documento =
|
|
_estadoEdicao
|
|
.DocumentoTrabalho;
|
|
|
|
|
|
string nomeBase =
|
|
string.Empty;
|
|
|
|
|
|
if (
|
|
documento != null &&
|
|
!string.IsNullOrWhiteSpace(
|
|
documento.CaminhoOrigem
|
|
)
|
|
)
|
|
{
|
|
nomeBase =
|
|
System.IO.Path
|
|
.GetFileNameWithoutExtension(
|
|
documento.CaminhoOrigem
|
|
);
|
|
}
|
|
|
|
|
|
if (
|
|
string.IsNullOrWhiteSpace(
|
|
nomeBase
|
|
)
|
|
)
|
|
{
|
|
nomeBase =
|
|
documento?.Nome;
|
|
}
|
|
|
|
|
|
if (
|
|
string.IsNullOrWhiteSpace(
|
|
nomeBase
|
|
)
|
|
)
|
|
{
|
|
nomeBase =
|
|
"mapa";
|
|
}
|
|
|
|
|
|
char[] invalidos =
|
|
System.IO.Path
|
|
.GetInvalidFileNameChars();
|
|
|
|
|
|
nomeBase =
|
|
new string(
|
|
nomeBase
|
|
.Select(
|
|
c =>
|
|
invalidos.Contains(c)
|
|
? '_'
|
|
: c
|
|
)
|
|
.ToArray()
|
|
)
|
|
.Trim();
|
|
|
|
|
|
if (
|
|
!nomeBase.EndsWith(
|
|
"_operacional",
|
|
StringComparison.OrdinalIgnoreCase
|
|
)
|
|
)
|
|
{
|
|
nomeBase +=
|
|
"_operacional";
|
|
}
|
|
|
|
|
|
return
|
|
nomeBase +
|
|
".geojson";
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Retorna a pasta do arquivo que originou o mapa somente
|
|
/// como conveniência para o SaveFileDialog.
|
|
/// </summary>
|
|
public string? ObterDiretorioOrigemMapaEdicao()
|
|
{
|
|
string? caminhoOrigem =
|
|
_estadoEdicao
|
|
.DocumentoTrabalho?
|
|
.CaminhoOrigem;
|
|
|
|
|
|
if (
|
|
string.IsNullOrWhiteSpace(
|
|
caminhoOrigem
|
|
)
|
|
)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
|
|
string? diretorio =
|
|
System.IO.Path
|
|
.GetDirectoryName(
|
|
caminhoOrigem
|
|
);
|
|
|
|
|
|
return
|
|
!string.IsNullOrWhiteSpace(
|
|
diretorio
|
|
) &&
|
|
System.IO.Directory.Exists(
|
|
diretorio
|
|
)
|
|
? diretorio
|
|
: null;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Salva uma fotografia independente do DocumentoTrabalho atual.
|
|
///
|
|
/// Não executa Apply, não fecha a sessão, não altera DocumentoFonte,
|
|
/// não altera o documento oficial, não limpa Undo/Redo e não toca
|
|
/// na seleção operacional.
|
|
/// </summary>
|
|
public bool SalvarMapaEditadoGeoJson(
|
|
string caminhoArquivo)
|
|
{
|
|
if (
|
|
!_estadoEdicao.EmEdicao ||
|
|
_estadoEdicao.DocumentoTrabalho == null
|
|
)
|
|
{
|
|
Log(
|
|
"[MAPA][EDICAO][SALVAR] " +
|
|
"Não existe sessão de edição ativa."
|
|
);
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
if (
|
|
string.IsNullOrWhiteSpace(
|
|
caminhoArquivo
|
|
)
|
|
)
|
|
{
|
|
throw new ArgumentException(
|
|
"O caminho para salvar o mapa não foi informado.",
|
|
nameof(caminhoArquivo)
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
* ObterResultado devolve uma cópia profunda.
|
|
* O exportador jamais recebe DocumentoTrabalho diretamente.
|
|
*/
|
|
MapaDocumento documentoSalvar =
|
|
_edicaoService
|
|
.ObterResultado(
|
|
_estadoEdicao
|
|
);
|
|
|
|
|
|
documentoSalvar.Modificado =
|
|
true;
|
|
|
|
|
|
/*
|
|
* Reaproveita a mesma fronteira que protege o Apply.
|
|
* Se a validação falhar, nenhum arquivo é gravado.
|
|
*/
|
|
if (
|
|
!ValidarDocumentoAntesDoApply(
|
|
documentoSalvar
|
|
)
|
|
)
|
|
{
|
|
Log(
|
|
"[MAPA][EDICAO][SALVAR] " +
|
|
"Cancelado por falha de validação."
|
|
);
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
_exportService
|
|
.SalvarGeoJson(
|
|
documentoSalvar,
|
|
caminhoArquivo
|
|
);
|
|
|
|
|
|
Log(
|
|
"[MAPA][EDICAO][SALVAR] " +
|
|
$"OK | " +
|
|
$"Ruas={documentoSalvar.QuantidadeRuas} | " +
|
|
$"Arquivo={System.IO.Path.GetFileName(caminhoArquivo)}"
|
|
);
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
public MapaDocumento ObterResultadoEdicao()
|
|
{
|
|
return _edicaoService
|
|
.ObterResultado(
|
|
_estadoEdicao
|
|
);
|
|
}
|
|
|
|
|
|
public bool DesfazerEdicao()
|
|
{
|
|
if (!_estadoEdicao.EmEdicao)
|
|
return false;
|
|
|
|
|
|
bool alterado =
|
|
_edicaoService
|
|
.Desfazer(
|
|
_estadoEdicao
|
|
);
|
|
|
|
|
|
if (!alterado)
|
|
return false;
|
|
|
|
|
|
RenderizarDocumentoEdicaoAtual();
|
|
|
|
SincronizarCockpitEdicao();
|
|
|
|
|
|
EstadoEdicaoAlterado?
|
|
.Invoke();
|
|
|
|
|
|
Log(
|
|
"[MAPA][EDICAO][UNDO] " +
|
|
$"Ruas=" +
|
|
$"{_estadoEdicao.DocumentoTrabalho?.QuantidadeRuas ?? 0} | " +
|
|
$"Undo={_estadoEdicao.QuantidadeHistoricoDesfazer} | " +
|
|
$"Redo={_estadoEdicao.QuantidadeHistoricoRefazer}"
|
|
);
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
public bool RefazerEdicao()
|
|
{
|
|
if (!_estadoEdicao.EmEdicao)
|
|
return false;
|
|
|
|
|
|
bool alterado =
|
|
_edicaoService
|
|
.Refazer(
|
|
_estadoEdicao
|
|
);
|
|
|
|
|
|
if (!alterado)
|
|
return false;
|
|
|
|
|
|
RenderizarDocumentoEdicaoAtual();
|
|
|
|
SincronizarCockpitEdicao();
|
|
|
|
|
|
EstadoEdicaoAlterado?
|
|
.Invoke();
|
|
|
|
|
|
Log(
|
|
"[MAPA][EDICAO][REDO] " +
|
|
$"Ruas=" +
|
|
$"{_estadoEdicao.DocumentoTrabalho?.QuantidadeRuas ?? 0} | " +
|
|
$"Undo={_estadoEdicao.QuantidadeHistoricoDesfazer} | " +
|
|
$"Redo={_estadoEdicao.QuantidadeHistoricoRefazer}"
|
|
);
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
}
|
|
} |