dialogs/MacConsIterDlg.js

/*
 * 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 } from 'src/kernel/dom'
import { getStr } from '../kernel/kernel'
import MacroDlg from './MacroDlg'
import ListeConstFig from './ListeConstFig'
import constantes from '../kernel/constantes'
import ColorButton from '../interface/ColorButton'
import LineStylePanel from '../interface/LineStylePanel'
import $ from 'jquery'
import { getMtgInput } from './dom'
import AvertDlg from '../dialogs/AvertDlg'
import CCbGlob from '../kernel/CCbGlob'
import EditeurConst from '../dialogs/EditeurConst'
import StyleTrait from '../types/StyleTrait'

export default MacConsIterDlg

/**
 * Dialogue de création ou modification d'une macro d'implémentation de construction itérative
 * @constructor
 * @param {MtgApp} app La mtgApp propriétaire
 * @param {CMacroConstructionIterative} mac  La macro qui doit être modifiée
 * @param modification {boolean} : true si le dialogue est ouvert pour modifier une macro déjà existante
 * @param callBackOK Fonction de callBack à appeler si on valide par OK
 * @param callBackCancel Fonction de callBack à appeler si on referme sans valider

 */
function MacConsIterDlg (app, mac, modification, callBackOK, callBackCancel) {
  MacroDlg.call(this, app, mac, modification, callBackOK, callBackCancel)
  this.mac = mac
  this.modification = modification
  let i; let label
  const self = this
  const tabPrincipal = ce('table', {
    cellspacing: 2
  })
  let tr
  let div
  this.appendChild(tabPrincipal)
  // Ajout de l'éditeur d'intitulé /////////////////////////////////
  this.addEditInt(tabPrincipal)
  /// ///////////////////////////////////////////////////////////////
  // Ajout du pane d'information sur la macro //////////////////////
  this.addPaneInfo(tabPrincipal, true) // True pour que le textarea n'ait qu'une ligne
  /// //////////////////////////////////////////////////////////////////////////////////////////////
  // Un panneau pour le choix de la construction à implémenter
  /// //////////////////////////////////////////////////////////////////////////////////////////////
  tr = ce('tr')
  tabPrincipal.appendChild(tr)
  const tabChoix = ce('table', {
    cellspacing: 2
  })
  tr.appendChild(tabChoix)
  tr = ce('tr')
  tabChoix.appendChild(tr)
  let td = ce('td', {
    style: 'vertical-align:middle;'
  })
  tr.appendChild(td)
  label = ce('label')
  $(label).html(getStr('Const') + ' : ')
  td.appendChild(label)
  td = ce('td', {
    style: 'vertical-align:top;width:250px;'
  })
  tr.appendChild(td)
  this.listeConst = new ListeConstFig(app)
  td.appendChild(this.listeConst.select)
  this.listeConst.selectElt(modification ? mac.proto : app.doc.tablePrototypes[0])

  tr = ce('tr')
  tabPrincipal.appendChild(tr)
  const tabInput = ce('table', { cellspacing: 2 })
  tr.appendChild(tabInput)
  tr = ce('tr')
  tabInput.appendChild(tr)
  td = ce('td')
  tr.appendChild(td)
  label = ce('label')
  $(label).html(getStr('MacItDlg1')) // Nombre d'itération
  td.appendChild(label)
  td = ce('td')
  tr.appendChild(td)
  const input1 = getMtgInput({ id: 'input1', size: 3 })
  td.appendChild(input1)
  tr = ce('tr')
  tabInput.appendChild(tr)
  td = ce('td')
  tr.appendChild(td)
  label = ce('label')
  $(label).html(getStr('MacRecDlg5')) // Nombre d'objets sources communs à toutes les itérations
  td.appendChild(label)
  td = ce('td')
  tr.appendChild(td)
  const input2 = getMtgInput({ id: 'input2', size: 3 })
  td.appendChild(input2)

  // CheckBox pour choisir ou non de donner une couleur spécifique aux objets créés.
  tr = ce('tr')
  tabPrincipal.appendChild(tr)
  const tabCoul = ce('table', {
    cellspacing: 0
  })
  tr.appendChild(tabCoul)
  tr = ce('tr')
  tabCoul.appendChild(tr)
  td = ce('td')
  tr.appendChild(td)
  div = ce('div')
  td.appendChild(div)
  const cb2 = ce('input', {
    id: 'cb2',
    type: 'checkbox'
  })
  div.appendChild(cb2)
  cb2.onclick = function () {
    $('#tdcoul').css('visibility', $('#cb2').prop('checked') ? 'visible' : 'hidden')
  }
  label = ce('label', {
    for: 'cb2'
  })
  $(label).html(getStr('MacRecDlg7'))
  div.appendChild(label)
  td = ce('td', {
    id: 'tdcoul'
  })
  tr.appendChild(td)
  let svg = cens('svg', {
    width: Math.floor(constantes.lineStyleWidth),
    height: String(constantes.lineStyleWidth / 2)
  })
  td.appendChild(svg)
  this.colorButton = new ColorButton(this, mac.imposerCouleur ? mac.couleurImp : app.getCouleur())
  // this.colorButton.container.setAttribute("transform","translate(" + String(constantes.lineStyleWidth + 5) + ",0)");
  svg.appendChild(this.colorButton.container)

  // Un tableau contenant 3 boutons radio pour choisir si on joint ou non les points fianux
  // et si oui si c'est par un polygone ou une ligne brisée
  tr = ce('tr')
  tabPrincipal.appendChild(tr)
  const tabPtsFinaux = ce('table', {
    cellspacing: 2,
    style: 'border:1px solid lightgray'
  })
  tr.appendChild(tabPtsFinaux)
  const tdLeft = ce('td', {
    style: 'vertical-align:top;'
  })
  tabPtsFinaux.appendChild(tdLeft)
  const tabPtsFinauxLeft = ce('table', {
    cellspacing: 2
  })
  tdLeft.appendChild(tabPtsFinauxLeft)
  const tdRight = ce('td', {
    id: 'tdright'
  }) // Contiendra un éventuel panneau de choix de couleur et de style de trait
  tabPtsFinaux.appendChild(tdRight)
  tr = ce('tr')
  tabPtsFinauxLeft.appendChild(tr)
  div = ce('div')
  tr.appendChild(div)

  const rad1 = ce('input', {
    id: 'radlig0',
    type: 'radio',
    name: 'pts'
  })
  div.appendChild(rad1)
  rad1.onclick = function () {
    self.onRadioChange()
  }

  label = ce('label')
  $(label).html(getStr('MacRecDlg9')) // Ne pas joindre
  div.appendChild(label)

  tr = ce('tdr')
  tabPtsFinauxLeft.appendChild(tr)
  div = ce('div')
  tr.appendChild(div)
  const rad2 = ce('input', {
    id: 'radlig1',
    type: 'radio',
    name: 'pts'
  })
  div.appendChild(rad2)
  label = ce('label')
  $(label).html(getStr('MacRecDlg10')) // Joindre par un polygone
  div.appendChild(label)
  rad2.onclick = function () {
    self.onRadioChange()
  }
  // Si on choisit de remplir par un polygone, on propose une checkbow pour le remplir ou non

  tr = ce('tr', {
    id: 'trpol'
  })
  div = ce('div')
  tr.appendChild(div)
  tabPtsFinauxLeft.appendChild(tr)
  const cb3 = ce('input', {
    id: 'cb3',
    type: 'checkbox',
    style: 'margin-left:20px;'
  })
  div.appendChild(cb3)
  label = ce('label', {
    for: 'cb3'
  })
  $(label).html(getStr('MacRecDlg8'))
  div.appendChild(label)

  tr = ce('tr')
  tabPtsFinauxLeft.appendChild(tr)
  div = ce('div')
  tr.appendChild(div)
  const rad3 = ce('input', {
    id: 'radlig2',
    type: 'radio',
    name: 'pts'
  })
  div.appendChild(rad3)
  rad3.onclick = function () {
    self.onRadioChange()
  }

  label = ce('label')
  $(label).html(getStr('MacRecDlg11')) // Joindre par une ligne brisée
  div.appendChild(label)

  // A droite des trois boutons radio un panneau de choix de couleur et de style si on chosit d'utiliser un
  // polygone ou une ligne brisée pour joindre les points finaux
  const tabLigne = ce('table', {
    cellSpacing: 0,
    id: 'tabligne'
  })
  tdRight.appendChild(tabLigne)
  td = ce('td')
  tabLigne.appendChild(td)
  svg = cens('svg', {
    width: Math.floor(2 * constantes.lineStyleWidth + 5),
    height: String(6 * constantes.lineStyleButtonHeight)
  })
  td.appendChild(svg)
  const bligne = mac.choixLigne !== 0
  this.lineStylePanel = new LineStylePanel(app, bligne ? mac.styleLigne.style : app.getStyleTrait().style)
  svg.appendChild(this.lineStylePanel.g)
  this.colorLineButton = new ColorButton(this, bligne ? mac.couleurLigne : app.getCouleur())
  this.colorLineButton.container.setAttribute('transform', 'translate(' + String(constantes.lineStyleWidth + 5) + ',0)')
  svg.appendChild(this.colorLineButton.container)

  td = ce('td', {
    style: 'vertical-align:top;'
  })
  tabLigne.appendChild(td)
  const caption = ce('caption', {
    class: 'mtgcaption',
    style: 'width:80px;'
  })
  $(caption).html(getStr('Epaisseur'))
  td.appendChild(caption)
  this.select = ce('select', {
    size: 4, // Le nombre de lignes visibles par défaut
    style: 'width:80px;'
  })
  td.appendChild(this.select)
  for (i = 0; i < 5; i++) {
    const option = ce('Option', {
      class: 'mtgOption'
    })
    if (i === ((mac.choixLigne === 0) ? 0 : mac.styleLigne.strokeWidth - 1)) option.setAttribute('selected', 'selected')
    $(option).html(String(i + 1))
    this.select.appendChild(option)
  }

  if (modification) {
    $(input1).val(mac.nbIter)
    $(input2).val(mac.nbSourcesPourIter)
    // $(input2).attr('disabled', true)  // Supprimé version 6.4.2
  }
  if (mac.imposerCouleur) $(cb2).attr('checked', 'checked')
  else $('#tdcoul').css('visibility', 'hidden')
  const choixLigne = mac.choixLigne
  for (i = 0; i < 3; i++) if (choixLigne === i) $('#radlig' + i).attr('checked', 'checked')
  if ((choixLigne === 1) && mac.polygoneRempli) $('#cb3').attr('checked', 'checked')
  this.onRadioChange()

  this.editeur1 = new EditeurConst(app, input1, 2, 500000, true) // Pour le nombre d'itérations par niveau
  this.editeur2 = new EditeurConst(app, input2, 0, 1000, true) // Pour le nombre d'objets communs à toutes les itérations

  /// ////////////////////////////////////////////////////////////////////////////////////////////////////
  this.addPaneBas(tabPrincipal)
  this.create('MacConsItDlg1', 650)
}

MacConsIterDlg.prototype = new MacroDlg()

MacConsIterDlg.prototype.onRadioChange = function () {
  $('#tdright').css('display', $('#radlig0').prop('checked') ? 'none' : 'block')
  $('#trpol').css('display', $('#radlig1').prop('checked') ? 'block' : 'none')
}

MacConsIterDlg.prototype.OK = function () {
  const mac = this.mac
  if ($('#inputint').val() !== '') {
    if (this.editeur1.validate() && this.editeur2.validate()) {
      const nbIter = parseInt($('#input1').val())
      const nbSources = parseInt($('#input2').val())
      const ch = this.validate(nbSources, nbIter)
      if (ch === '') {
        mac.nbSourcesPourIter = nbSources
        mac.nbIter = nbIter
        mac.proto = this.listeConst.getSelectedItem()
        mac.imposerCouleur = $('#cb2').prop('checked')
        if (mac.imposerCouleur) mac.couleurImp = this.colorButton.color
        // On regarde quel est le choix pour les points finaux parmi les 3 rboutons radios proposés
        let choix
        for (let i = 0; i < 3; i++) {
          if ($('#radlig' + i).prop('checked')) {
            choix = i
            break
          }
        }
        mac.choixLigne = choix
        const thickness = this.select.selectedIndex + 1
        if (mac.choixLigne !== 0) { // Si on a demandé que les points finaux soient reliés par un polygone ou une ligne brisée
          mac.styleLigne = new StyleTrait(this.app.listePr, this.lineStylePanel.style, thickness)
          mac.couleurLigne = this.colorLineButton.color
        }
        if (mac.choixLigne === 1) mac.polygoneRempli = $('#cb3').prop('checked')
        this.saveBasic()// Sauvegarde les autres caractéristiques da la macro
        this.callBackOK()
        if (this.modification) this.app.gestionnaire.enregistreFigureEnCours('ModifObj')
        this.destroy()
      } else new AvertDlg(this.app, ch, function () { $('#input1').focus() })
    }
  } else this.avertIncorrect()
}

/**
 * Fonction qui analyse si les macros constructions choisies pour chaque niveau d'itération peuvent bien
 * être implémentées récursivement avec les choix effectués dans la boîte de dialogue.
 * Renvoie "" si tout va bien et sinon une chaîne contenant l'erreur à afficher dans un message d'erreur
 */
/**
 * Fonction qui analyse si la macro choisie peut bien être implémentée de façon itérative
 * avec les choix effectués dans la boîte de dialogue.
 * Renvoie "" si tout va bien et sinon une chaîne contenant l'erreur à afficher dans un message d'erreur
 * @param {number} nbSources Le nombre d'objets sources à réutiliser à chaque itération
 * @param {number} nbIter Le nombre d'itérations à effectuer

 */
MacConsIterDlg.prototype.validate = function (nbSources, nbIter) {
  const
    app = this.app
  const list = app.listePr
  const proto = this.listeConst.getSelectedItem()
  const chErr = proto.implementPossible(list)
  if (chErr !== '') {
    return getStr('ImpImposs') + '\n' + chErr
  }
  if ((list.pointeurLongueurUnite == null) && proto.utiliseLongueurUnite()) {
    // this.listeConst.selectedIndex = i // Supprimé version 6.4.1
    return getStr('ErrLong')
  }
  if (!proto.estIterable(nbSources)) {
    // this.listeConst.selectedIndex = i // Supprimé version 6.4.1
    return getStr('errIter')
  }
  // On fait une estimation du nombre d'objets à créer avec le prototype de niveau de récursion 0

  const n = (proto.longueur() - proto.nbObjSources - nbSources) * nbIter
  if (n > CCbGlob.nombreMaxObj) return getStr('errImpMax')
  return ''
}