Archivo de la etiqueta: teclas.mnu

Imprimir la configuración de teclado

Como ya sabes, el formato del archivo de teclas Teclas.mnu ha cambiado en Digi3D 2011.

El formato antiguo tenía una serie de limitaciones como era que únicamente permitía añadir una orden a una pulsación de tecla (lo que te obligaba a crear archivos de macroinstrucciones externos si querías asignar más de una orden a una pulsación de tecla, cosa que personalmente nunca me ha gustado) y peor aún, no había forma saber a que tecla o combinación de teclas se refería un determinado comando, ya que cada pulsación de tecla o combinación de teclas generaba un número único imposible de identificar. Recuerda que existe un post en este blog que te explica cómo convertir un archivo de teclas antiguo al nuevo formato https://desarrollodigi3d.digi21.net/2011/01/25/archivos-de-teclas-de-digi3d-2011/

En este post vamos a aprender cómo convertir un archivo de teclas en formato xml en algo que sea legible por el ser humano, la idea es modificar el archivo xml para que al hacer doble clic sobre él, el navegador/explorador web que utilices nos muestre una tabla con la tecla, indicando además si estaba pulsada la tecla Alt, Mayúsculas o Control y la descripción asociada a la pulsación de tecla.

La idea es convertir esto:

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns="http://schemas.digi21.net/Digi3D/keyboard/v1.0">
  <Key Name="NUMERO 0" Description="Cambia el tamaño del cursor"><![CDATA[CURSOR]]></Key>
  <Key Name="NUMERO 1" Description="Borra entidades por ventana"><![CDATA[BORRA_V]]></Key>
  <Key Name="TECLA DE ADICION" Description="Selecciona el código activo"><![CDATA[COD]]></Key>
  <Key Name="NUMERO 9" Description="Dibuja un cruce de caminos"><![CDATA[CRUCE]]></Key>
  <Key Name="NUMERO 6" Description="Dibuja una paralela a una línea existente"><![CDATA[PARALELA_DINAMICA]]></Key>
  <Key Name="NUMERO 4" Description="Asigna como código activo el de la línea seleccionada"><![CDATA[clonar]]></Key>
  <Key Name="NUMERO 5" Description="Finaliza la línea que está dibujando cerrándola"><![CDATA[cierra_ent]]></Key>
  <Key Name="NUMERO 7" Description="Cambia el sentido de la línea seleccionada"><![CDATA[CAMB_SEN]]></Key>
  <Key Name="NUMERO 8" Description="Une las dos líneas seleccionadas"><![CDATA[UNIR]]></Key>
  <Key Name="P" Shift="true" Description="Matorral"><![CDATA[cod=58]]></Key>
  <Key Name="A" Description="Construcción"><![CDATA[cod=30
modob=2 0]]></Key>
  <Key Name="G" Description="Muro malla"><![CDATA[cod=35
modob=2 0]]></Key>
  <Key Name="H" Description="Cerca de alambre"><![CDATA[cod=37
modob=2 0]]></Key>
  <Key Name="I" Description="Presa dique malecón"><![CDATA[COD=11
modob=2 5]]></Key>
  <Key Name="K" Description="Antena"><![CDATA[cod=34
modob=5]]></Key>
  <Key Name="5" Description="Tentativo a línea en 3D"><![CDATA[modob=5]]></Key>
  <Key Name="'" Description="Punto de cota fotogramétrica"><![CDATA[cod=41
punto
modob=5
cota]]></Key>
</Keyboard>

…en esto otro:

Listado de teclas

Tecla Mayúsculas Control Descripción
NUMERO 0 Cambia el tamaño del cursor
NUMERO 1 Borra entidades por ventana
TECLA DE ADICION Selecciona el código activo
NUMERO 9 Dibuja un cruce de caminos
NUMERO 6 Dibuja una paralela a una línea existente
NUMERO 4 Asigna como código activo el de la línea seleccionada
NUMERO 5 Finaliza la línea que está dibujando cerrándola
NUMERO 7 Cambia el sentido de la línea seleccionada
NUMERO 8 Une las dos líneas seleccionadas
P X Matorral
A Construcción
G Muro malla
H Cerca de alambre
I Presa dique malecón
K Antena
5 Tentativo a línea en 3D
Punto de cota fotogramétrica

Para realizar nuestra tarea lo único que tenemos que hacer es crear una transformación XML que transforme nuestro documento original en un archivo .html básicamente con una tabla con cuatro columnas, en la primera pondremos la tecla en cuestión, en la segunda una X si estaba pulsada la tecla mayúsculas, en la tercera una X si estaba pulsada la tecla control y por último la descripción.

Estas transformaciones de un documento .xml a otro tipo de documento (que podría ser otro .xml, un archivo .html, o un archivo .txt por ejemplo) se realizan mediente Transformaciones XSL que es un estándar precisamente para esto, transformar un archivo .xml en otra cosa.

No te voy a enseñar aquí XSLT, para eso existen multitud de tutoriales en internet. Yo no soy amigo de los tutoriales, prefiero comprarme un buen libro. Personalmente aprendí XSLT (y todo lo relacionado con XML) con el libro Essential XML Quick Reference: A Programmer’s Reference to XML, XPath, XSLT, XML Schema, SOAP, and More (DevelopMentor)

Primer paso: Indicar que nuestro archivo de teclas debe ser transformado mediante una transformación XSL

Para ello, lo único que tenemos que hacer es añadir la instrucción de preprocesador xml-stylesheet en nuestro archivo de teclas haciendo una referencia al archivo .xls en el que vamos a implementar la transformación:

<?xml-stylesheet type="text/xsl" href="keyboard.xsl" ?>

Vamos a poner nuestra instrucción al comienzo del documento, después de la instrucción xml, y antes del nodo raiz, de modo que las primeras líneas de nuestro archivo .xml original son las siguientes:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="keyboard.xsl" ?>
<Keyboard xmlns="http://schemas.digi21.net/Digi3D/keyboard/v1.0">
  <Key Name="NUMERO 0" Description="Cambia el tamaño del cursor"><![CDATA[CURSOR]]></Key>
  <Key Name="NUMERO 1" Description="Borra entidades por ventana"><![CDATA[BORRA_V]]></Key>
  <Key Name="TECLA DE ADICION" Description="Selecciona el código activo"><![CDATA[COD]]></Key>

Segundo paso: Crear la transformación

Ahora únicamente nos queda crear nuestro archivo de transformación. Creamos el archivo keyboard.xls con la siguiente información:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
</xsl:stylesheet>

A continuación añadimos la plantilla de transformación indicando sobre que nodo o conjunto de nodos queremos realizar la transformación, para ello indicamos la cadena XPath:

/Keyboard

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/Keyboard">
  </xsl:template>
</xsl:stylesheet>

y a continuación creamos el cuerpo del archivo html a crear:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/Keyboard">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <title>Listado de teclas</title>
      <body>
        <h3>Listado de teclas</h3>

        <table border="1">
          <tr>
            <td>Tecla</td>
            <td>Mayúsculas</td>
            <td>Control</td>
            <td>Descripción</td>
          </tr>

        </table>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

Tan solo nos queda añadir una entrada por cada nodo hijo Key tenga el nodo Keyboard del archivo de teclas original.

Para ello utilizaremos la instrucción for-each para añadir tantas filas como nodos hijos tenga el nodo Keyboard de la siguiente manera:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/Keyboard">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <title>Listado de teclas</title>
      <body>
        <h3>Listado de teclas</h3>

        <table border="1">
          <tr>
            <td>Tecla</td>
            <td>Mayúsculas</td>
            <td>Control</td>
            <td>Descripción</td>
          </tr>

          <xsl:for-each select="Key">
            <tr>
              <td>
              </td>

              <td>
              </td>

              <td>
              </td>

              <td>
              </td>
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

y para terminar tendremos que rellenar cada uno de los campos con el valor.

Para añadir el nombre de la tecla en el primer campo, utilizaremos la instrucción value-of, indicando que queremos almacenar el valor del atributo Name. Haremos lo mismo con el último campo, donde añadiremos la información del atributo Description.

Para los campos Mayúsculas y Control podemos utilizar la instrucción if, indicando que nos introduzca el carácter X únicamente si los atributos Shift o Control tienen asignado el valor verdadero.

Nuestra transformación tiene la siguiente forma:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/Keyboard">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <title>Listado de teclas</title>
      <body>
        <h3>Listado de teclas</h3>

        <table border="1">
          <tr>
            <td>Tecla</td>
            <td>Mayúsculas</td>
            <td>Control</td>
            <td>Descripción</td>
          </tr>

          <xsl:for-each select="Key">
            <tr>
              <td>
                <xsl:value-of select="@Name"/>
              </td>

              <td>
                <xsl:if test="@Shift">X</xsl:if>
              </td>

              <td>
                <xsl:if test="@Control">X</xsl:if>
              </td>

              <td>
                <xsl:value-of select="@Description"/>
              </td>
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

Pero aún no funcionará, ya que todos los nodos del archivo de teclas están almacenados en el espacio de nombres http://schemas.digi21.net/Digi3D/keyboard/v1.0, por lo tanto las consultas XPath a la hora de realizar la transformación no localizarán ni el nodo Keyboard ni el nodo Key.

Para solucionar nuestro problema y finalizar el ejercicio, tan solo nos queda añadir una definición de espacio de nombres a nuestro archivo de transformación xsl que denominaremos teclasMnu y que apuntará a la url: http://schemas.digi21.net/Digi3D/keyboard/v1.0 y sustituiremos las consultas XPath /Keyboard por teclasMnu:Keyboard y Key por teclasMnu:Key:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:teclasMnu="http://schemas.digi21.net/Digi3D/keyboard/v1.0">
  <xsl:template match="/teclasMnu:Keyboard">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <title>Listado de teclas</title>
      <body>
        <h3>Listado de teclas</h3>

        <table border="1">
          <tr>
            <td>Tecla</td>
            <td>Mayúsculas</td>
            <td>Control</td>
            <td>Descripción</td>
          </tr>

          <xsl:for-each select="teclasMnu:Key">
            <tr>
              <td>
                <xsl:value-of select="@Name"/>
              </td>

              <td>
                <xsl:if test="@Shift">X</xsl:if>
              </td>

              <td>
                <xsl:if test="@Control">X</xsl:if>
              </td>

              <td>
                <xsl:value-of select="@Description"/>
              </td>
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

Y ya hemos terminado. Te propongo que añadas un campo más, para indicar si estaba pulsada la tecla Alt y que además utilices una hoja de estilos en cascada para darle un aspecto más moderno.

Descarga los archivos de ejemplo de este ejercicio.

Archivos de teclas de Digi3D 2011

Hay una pregunta que me hacen siempre que hago un curso de Digi3D cuando explico cómo asignar órdenes a teclas:

¿Cómo se qué en qué tecla tengo asignada una determinada orden?

Y la respuesta muy a mi pesar es siempre la misma: No hay manera de saberlo.

Este escenario cambia completamente en Digi3D 2011, ya que los archivos de teclas son XML y están pensados para que se puedan imprimir para tener un listado de las teclas asignadas. Además ahora podemos tener asignadas varias órdenes a una pulsación de teclas, haciendo innecesario el uso de macroinstrucciones.

Al instalar Digi3D 2011, se crea en la carpeta %ProgramData%\Digi3D 2011\Esquemas XML una carpeta con los esquemas de los archivos XML soportados por Digi3D 2011, y entre otros se copia el archivo Keyboard.xsd con el siguiente contenido:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://schemas.digi21.net/Digi3D/keyboard/v1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Keyboard">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" name="Key">
          <xs:complexType>
            <xs:simpleContent>
              <xs:extension base="xs:string">
                <xs:attribute name="Name" type="xs:string" use="required" />
                <xs:attribute name="Shift" type="xs:boolean" use="optional" />
                <xs:attribute name="Control" type="xs:boolean" use="optional" />
                <xs:attribute name="Menu" type="xs:boolean" use="optional" />
              </xs:extension>
            </xs:simpleContent>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

En el que podemos ver que el nuevo formato está compuesto por un nodo raiz de tipo Keyboard que está compuesto por nodos Key que tienen que tener obligatoriamente un atributo Name y que opcionalmente pueden o no tener atributos de tipo Control, Shift o Menu

Veamos el contenido del archivo Default.keyboard.xml que instala el instalador de Digi3D 2011:

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns="http://schemas.digi21.net/Digi3D/keyboard/v1.0">
  <Key Name="AV PAG">rest_zoomin</Key>
  <Key Name="RE PAG">rest_zoomout</Key>
  <Key Name="F3">rest_zoome</Key>
  <Key Name="F4">rest_zoom1x1</Key>
  <Key Name="TECLA DE ADICION">velocidad_mas_xy</Key>
  <Key Name="TECLA DE SUSTRACCION">velocidad_menos_xy</Key>
  <Key Name="TECLA DE MULTIPLICACION">velocidad_mas_z</Key>
  <Key Name="TECLA DE DIVISION">velocidad_menos_z</Key>
  <Key Name="F2">tipo_indice</Key>
  <Key Name="F5">positivo_negativo</Key>
  <Key Name="B">correlar</Key>
  <Key Name="TABULACION">cambia_teclas_mnu</Key>
  <Key Name="F12">mostrar_estereo</Key>
  <Key Name="FIN" Control="true">salir_digi3d</Key>
  <Key Name="P" Control="true">ir_a_punto_apoyo</Key>
  <Key Name="F1">raton_digi3d</Key>
</Keyboard>

Se puede comprobar perfectamente que pulsando la combinación de teclas Control + Fin se ejecuta la orden SALIR_DIGI3D.

Ahora disponemos de un formato mucho mejor, pero tenemos un problema: Hay que hacer un programa que genere este tipo de archivos a partir de versiones anteriores.
El problema es que no es trivial, ya que no hay una forma directa de averiguar la tecla o la combinación de teclas que corresponde con el número de tecla almacenado en los archivos Teclas.mnu, ya que los números de tecla que aparecen en los archivos Teclas.mnu son números de teclas generados por MS-DOS y no por Windows.

Cuando pasamos de Digi (la versión de MS-DOS) a Digi21 (la versión de Windows) hicimos el esfuerzo de hacer que los archivos TECLAS.MNU de MS-DOS fueran compatibles con los archivos TECLAS.MNU del programa de Windows para así evitar que los usuarios de MS-DOS tuvieran que volver a generar sus archivos de teclas.

Para conseguirlo, dedicamos tiempo en intentar simular el número de tecla que genera MS-DOS a partir del número de tecla que nos envía Windows cuando el usuario realiza una pulsación.

Estos números no tienen nada que ver, pero al final conseguimos un algoritmo que acertaba en el 90% de los casos.

El algoritmo es el siguiente:

    UINT teclaDigi21 = 0;
    UINT nFlags = (pMsg->lParam & 0xffff0000) >> 16;

    if( mayusculasPulsada )
        nFlags += 25;
    else if( controlPulsada )
        nFlags += 35;
    else if( altPulsada )
        nFlags += 45;

    if( ::TranslateMessage(pMsg) ) {
        MSG msg;
        teclaDigi21 = msg.wParam;
    }

    int scanCode = (nFlags & 0xFF) << 8;
    teclaDigi21 |= scanCode;

Si te fijas, hay que hacer muchas operaciones para transformar el número de tecla que envía Windows para conseguir el valor teclaDigi21.

Una vez conocido el algoritmo, podríamos hacer un programa que crée una tabla con todas las posibles combinaciones. Este programa podría esperar a que el usuario pulse todo tipo de combinaciones y al finalizar almacenar la tabla que luego utilizará otro programa que es el que realmente transforma los archivos Teclas.mnu a Teclas.keyboard.xml.

Captura de pantalla del programa CreaTablaTeclasMnuAXml

Este programa espera a que el usuario pulse teclas. Por cada tecla pulsada añade una entrada en el ControlList donde nos muestra el código de tecla Digi21 calculado y la combinación de teclas que se ha pulsado.

Al aceptar el cuadro de diálogo, el programa pregunta la ruta del archivo de texto a crear, y lo que crea es código fuente en C# para luego añadirlo a un proyecto en C#.

A continuación tienes un ejemplo del resultado generado por este programa tras realizar una serie de pulsaciones de teclas:

    struct DatosTecla
    {
        public string Nombre;
        public bool Control;
        public bool Mayusculas;
        public bool Alt;

        public DatosTecla(string nombre, bool control, bool may, bool alt)
        {
            this.Nombre = nombre;
            this.Control = control;
            this.Mayusculas = may;
            this.Alt = alt;
        }
    };
private static Dictionary<int, DatosTecla> diccionario = new Dictionary<int, DatosTecla>
{
    { 8051, new DatosTecla("S", false, false, false) },
    { 8292, new DatosTecla("D", false, false, false) },
    { 7777, new DatosTecla("A", false, false, false) },
    { 8550, new DatosTecla("F", false, false, false) },
    { 9579, new DatosTecla("K", false, false, false) },
    { 9322, new DatosTecla("J", false, false, false) },
    { 11875, new DatosTecla("C", false, false, false) },
    { 12654, new DatosTecla("N", false, false, false) },
    { 12909, new DatosTecla("M", false, false, false) },
};

Si te fijas, este programa crea una estructura en C# con cuatro campos: Nombre, Control, Mayúsculas y Alt, y a continuación crea un diccionario cuya clave principal es el número de tecla de Digi21 y cuyo valor es esta estructura, de modo que el número de tecla 8051 es la tecla S sin Control, ni Mayúsculas ni Alt.

Ahora tan solo nos queda crear un proyecto de C# (yo he creado un proyecto de consola), copiar tanto la estructura como la instancia del diccionario dentro del proyecto y leer el archivo original para crear un archivo XML.

Y el algoritmo de este programa es muy sencillo:

  1. Abrimos el archivo Teclas.mnu antiguo.
  2. Leemos una línea
  3. Separamos la primera palabra de la línea y el resto.
  4. Transformamos la primera palabra a un número entero.
  5. Buscamos ese número en el diccionario.
  6. Almacenamos en el XML un nodo con la información devuelta por el diccionario.
  7. Volvemos al punto 2 mientras queden líneas

A continuación un recorte del código del programa que realiza las acciones 3, 4, 5 y 6:

                        int teclaDigi21 = int.Parse(partes[0]);

                        if (!diccionario.ContainsKey(teclaDigi21))
                        {
                            ++erroresLocalizados;
                            Console.WriteLine(string.Format("No ha sido posible traducir la tecla con número: {0}",
                                teclaDigi21));
                            continue;
                        }

                        DatosTecla datosTecla = diccionario[teclaDigi21];

                        escritor.WriteStartElement("Key");
                        escritor.WriteAttributeString("Name", datosTecla.Nombre);
                        if (datosTecla.Control)
                            escritor.WriteAttributeString("Control", "true");
                        if (datosTecla.Mayusculas)
                            escritor.WriteAttributeString("Shift", "true");
                        if (datosTecla.Alt)
                            escritor.WriteAttributeString("Menu", "true");

                        escritor.WriteString(partes[1]);
                        escritor.WriteEndElement();

y por último, el enlace al código fuente del programa en el repositorio de código fuente de Digi21 en GitHub: https://github.com/digi21/TeclasMnuATeclasMnuXml