dialogs/AffichageValeurDlg.js

/*
 * Created by yvesb on 12/03/2017.
 */
/*
 * MathGraph32 Javascript : Software for animating online dynamic mathematics figures
 * https://www.mathgraph32.org/
 * @Author Yves Biton (yves.biton@sesamath.net)
 * @License: GNU AGPLv3 https://www.gnu.org/licenses/agpl-3.0.html
 */

import { ce, cens, getStr } from '../kernel/kernel'
import MtgDlg from './MtgDlg'
import $ from 'jquery'
import NatCal from '../types/NatCal'
import PaneNombreDecimales from './PaneNombreDecimales'
import PaneAlignement from './PaneAlignement'
import PaneTaillePolice from './PaneTaillePolice'
import PaneModeAffichage from './PaneModeAffichage'
import MtgInputWithCharSpe from './MtgInputWithCharSpe'
import CValeurAffichee from '../objets/CValeurAffichee'
import CAffLiePt from '../objets/CAffLiePt'
import StyleEncadrement from '../types/StyleEncadrement'
import addQueue from 'src/kernel/addQueue'

export default AffichageValeurDlg

/**
 * Dialogue servant à créer ou modifier un affichage de valeur
 * @param {MtgApp} app L'application propriétaire
 * @param valAff L'affichage de valeur à modifier
 * @param modification true si on modifie un affichage de valeur déjà créé
 * @param callBackOK null ou fonction de callBack à appeler après OK
 * @param callBackCancel null ou fonction de callBack à appeler après annulation
 * @constructor
 * @extends MtgDlg
 */
function AffichageValeurDlg (app, valAff, modification, callBackOK, callBackCancel) {
  MtgDlg.call(this, app, 'affvaldlg', callBackOK, callBackCancel)
  this.valAff = valAff
  const list = app.listePr
  const estFinal = valAff.estElementFinal
  this.modification = modification
  const self = this
  const indmax = modification ? list.indexOf(valAff) - 1 : list.longueur() - 1
  const tabPrincipal = ce('table')
  this.appendChild(tabPrincipal)
  let tr = ce('tr')
  tabPrincipal.appendChild(tr)
  // La première ligne du tableau contient un tableau formé de trois colonnes : Le label, l'éditeur de nom de la valeur
  // et en troisième colonne un tableau de choix de nombre de décimales
  const tabHaut = ce('table')
  tr.appendChild(tabHaut)
  const trHaut = ce('tr')
  tabHaut.appendChild(trHaut)
  let td = ce('td')
  trHaut.appendChild(td)
  let caption
  if (!(this.modification && valAff.valeurAssociee.estElementIntermediaire())) {
    caption = ce('caption', {
      class: 'mtgcaption'
    })
    $(caption).html(getStr('Valeur'))
    td.appendChild(caption)
    // En haut et à gauche un tableau contenant le label, l'éditeur de nom et dessous le bouton Valeurs
    const tabHautGauche = ce('table')
    td.appendChild(tabHautGauche)
    tr = ce('tr')
    tabHautGauche.appendChild(tr)
    this.inf = list.listeParNatCal(app, NatCal.NTteValROuC, indmax)
    this.select = ce('select', {
      size: 8, // Le nombre de lignes visibles par défaut
      style: 'width:180px'
    })
    this.select.onchange = function () {
      self.updatePreview()
    }
    td.appendChild(this.select)
    if (estFinal) $(this.select).attr('disabled', true)
    // C'est là qu'on ajoute les noms des valeurs numériques disponibles dans la liste déroulante
    for (let i = 0; i < this.inf.noms.length; i++) {
      const option = ce('Option', {
        class: 'mtgOption'
      })
      if (modification ? (this.inf.pointeurs[i] === valAff.valeurAssociee) : i === 0) option.setAttribute('selected', 'selected')
      $(option).html(this.inf.noms[i])
      this.select.appendChild(option)
    }
  }

  td = ce('td')
  trHaut.appendChild(td)
  this.paneNbDec = new PaneNombreDecimales(valAff.nombreDecimales, function () { self.updatePreview() })
  td.appendChild(this.paneNbDec.container)

  // Un tableau central pour contenir les deux éditeurs de pré et post chaines
  tr = ce('tr')
  tabPrincipal.appendChild(tr)
  const tabCentre = ce('table')
  tr.appendChild(tabCentre)
  td = ce('td')
  $(td).css('vertical-align', 'top')
  tabCentre.appendChild(td)
  const tabCentreGauche = ce('table')
  td.appendChild(tabCentreGauche)
  tr = ce('tr')
  tabCentreGauche.appendChild(tr)
  td = ce('td')
  tr.appendChild(td)
  let label = ce('label')
  td.appendChild(label)
  $(label).html(getStr('EnTete'))
  td = ce('td')
  tr.appendChild(td)
  /*
  var input = ce("input", {
    type : "text",
    id : "entete",
  });
  */
  let input = new MtgInputWithCharSpe(app, { id: 'entete' }, ['grec', 'math', 'arrows'], false, this)
  td.appendChild(input)
  input.onkeyup = function () {
    self.updatePreview()
  }

  tr = ce('tr')
  tabCentreGauche.appendChild(tr)
  td = ce('td')
  tr.appendChild(td)
  label = ce('label')
  td.appendChild(label)
  $(label).html(getStr('SuiviDe'))
  td = ce('td')
  tr.appendChild(td)
  /*
  input = ce("input", {
    type : "text",
    id : "suivide",
  });
  */
  input = new MtgInputWithCharSpe(app, { id: 'suivide' }, ['grec', 'math', 'arrows'], false, this)
  td.appendChild(input)
  input.onkeyup = function () {
    self.updatePreview()
  }

  // Au centre à droite un svg d'aperçu
  td = ce('td')
  tabCentre.appendChild(td)
  const tabCentreDroit = ce('table')
  td.appendChild(tabCentreDroit)
  caption = ce('caption', {
    class: 'mtgcaption'
  })
  $(caption).html(getStr('Apercu'))
  tabCentreDroit.appendChild(caption)
  td = ce('td')
  $(td).css('border', '1px solid lightgray')
  tabCentreDroit.appendChild(td)
  this.svg = cens('svg', {
    width: '200px',
    height: '80px'
  })
  td.appendChild(this.svg)
  // Depuis le 18/3/2025 on impose la fonte 'Roboto' sur tous les conteneurs de svg mtg32
  this.svg.parentNode.style.fontFamily = 'Roboto'
  // Dans la troisième ligne du tableau principal, un tableau de trois colonnes contenant un panneau de choix de
  // mode d'affichage, un panneau de choix d'alignement et un manneua de choix de taille de police
  tr = ce('tr')
  tabPrincipal.appendChild(tr)
  this.paneAlign = new PaneAlignement(app, valAff.alignementHorizontal, valAff.alignementVertical)
  const tableBas = ce('table')
  tr.appendChild(tableBas)
  td = ce('td', {
    valign: 'top'
  })
  tableBas.appendChild(td)
  td.appendChild(this.paneAlign.container)
  td = ce('td', {
    valign: 'top'
  })
  tableBas.appendChild(td)
  this.paneTaillePolice = new PaneTaillePolice(valAff.taillePolice, function () { self.updatePreview() })
  tableBas.appendChild(this.paneTaillePolice.container)
  td = ce('td', {
    valign: 'top'
  })
  tableBas.appendChild(td)
  // Pas d'édition de l'angle d'affichage si c'est un objet final de macro.
  this.paneModeAffichage = new PaneModeAffichage(this, false, valAff.effacementFond, valAff.encadrement,
    valAff.couleurFond, estFinal ? null : valAff.angText.chaineInfo(), indmax, function () { self.updatePreview() })
  td.appendChild(this.paneModeAffichage.container)
  if (modification) {
    $(this.inputVal).val(valAff.valeurAssociee.getNom())
    $('#entete').val(valAff.enTete)
    $('#suivide').val(valAff.postChaine)
  }

  this.create('AffVal', 580)
}

AffichageValeurDlg.prototype = new MtgDlg()

AffichageValeurDlg.prototype.onOpen = function () {
  MtgDlg.prototype.onOpen.call(this)
  this.paneModeAffichage.init()
  this.updatePreview()
}

AffichageValeurDlg.prototype.OK = function () {
  // if (this.app.lastDlgId() !== this.id) return;
  const app = this.app
  const val = this.valAff
  const pma = this.paneModeAffichage
  if (!pma.validate()) {
    return
  } // Faute dans l'angle de rotation du texte
  val.encadrement = pma.getStyleEnc()
  val.effacementFond = pma.getEffFond()
  val.couleurFond = pma.getColor()
  val.nombreDecimales = this.paneNbDec.getDigits()
  // Si l'alignement vertical ou horizontal est changé on annule tout décalage manuel précédent
  const newHorAlign = this.paneAlign.getHorAlign()
  const newVerAlign = this.paneAlign.getVerAlign()
  if ((val.alignementHorizontal !== newHorAlign) || (val.alignementVertical !== newVerAlign)) {
    val.decX = 0
    val.decY = 0
  }
  if (!val.estElementFinal && !(this.modification && val.valeurAssociee.estElementIntermediaire())) {
    val.valeurAssociee = this.inf.pointeurs[this.select.selectedIndex]
    val.angText = pma.getAng()
  }
  //
  val.alignementHorizontal = newHorAlign
  val.alignementVertical = newVerAlign
  val.taillePolice = this.paneTaillePolice.getTaille()
  val.enTete = $('#entete').val()
  val.postChaine = $('#suivide').val()
  val.positionne(false, app.dimf)
  // Finalement il faut recréer l'affichage si, par exemple, on a choisi d'effacer le fond alors qu'il ne l'était pas
  val.setReady4MathJax()
  if (this.modification && this.app.dlg[0] !== 'ProtocoleDlg') {
    addQueue(function () {
      val.reCreateDisplay(app.svgFigure)
    })
  }
  if (this.callBackOK !== null) this.callBackOK()
  if (this.modification) app.gestionnaire.enregistreFigureEnCours('ModifObj')
  this.destroy()
}

/**
 * Met à jour l'aperçu
 * @returns {void}
 */
AffichageValeurDlg.prototype.updatePreview = function updatePreview () {
  const app = this.app
  const list = app.listePr
  const valAff = this.valAff.valeurAssociee
  const pointeur = (this.modification && valAff.estElementIntermediaire()) ? valAff : this.inf.pointeurs[this.select.selectedIndex]

  const pma = this.paneModeAffichage
  const affv = new CValeurAffichee(list, null, false, this.valAff.couleur, 0, 0, 0, 0, false, null,
    this.paneTaillePolice.getTaille(), pma.getStyleEnc(), pma.getEffFond(),
    pma.getColor(), CAffLiePt.alignHorLeft, CAffLiePt.alignVerTop,
    pointeur, $('#entete').val(), $('#suivide').val(), this.paneNbDec.getDigits())
  affv.positionneFull(false, app.dimf)
  if (affv.existe) {
    affv.setReady4MathJax()
    const self = this
    addQueue(function () {
      while (self.svg.childNodes.length !== 0) self.svg.removeChild(self.svg.childNodes[0])
      const g = affv.createg()
      if (affv.encadrement !== StyleEncadrement.Sans) $(g).attr('transform', 'translate(2,2)')
      self.svg.appendChild(g)
    })
  } else {
    while (this.svg.childNodes.length !== 0) this.svg.removeChild(this.svg.childNodes[0])
  }
}