lunes, 5 de julio de 2021

Añadir componente personalizado en la zona derecha de Forms

ADVERTENCIA: Lo que voy a comentar en esta entrada utiliza funciones no documentadas de Forms. Funciona con la versión actual 12.2.1.4.0, pero no puedo ofrecer ninguna garantía de que en futuras versiones funcione o lo haga de esta forma.

Oracle Forms utiliza Java para ejecutarse en el cliente y proporciona herramientas para poder integrar componentes Java personalizados (Bean Area), precisamente estos componentes son los que voy a mostrar como a través de ellos alterar el comportamiento nativo de Forms.

Lo primero es conocer que el tipo de layout Java que usa Forms.

BorderLayout: https://docs.oracle.com/javase/7/docs/api/java/awt/BorderLayout.html


Donde:

  • North: Menú superior y botonera horizontal.
  • West: Botonera vertical.
  • South: Barra de estado.
  • Center: Ventana del programa Forms.
Del BorderLayout no han usado la zona East. Veamos cómo podemos utilizarla para lo que nos interese.

Una vez inicialicemos la zona "East" quedará visible para todos los programas Forms que tengamos abiertos, por lo que va a ser una zona común a toda la aplicación.

Para inicializarla vamos a utilizar un Bean Area que estará oculto en un programa, para ello añadimos al lienzo un ITEM de tipo "Bean Area", en "Implementation Class" indicaremos una clase de implementación que más adelante veremos en detalle y ponemos el item en el lienzo con tamaño 0, 0 para que no sea visible para el usuario.


Lo importante de esto es lo que se hace en la clase de implementación. Para que según se inicialice el bean area también inicialice la zona "East" del BorderLayout haremos lo siguiente:

package com.test.efm;

import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JLabel;
import oracle.forms.handler.IHandler;
import oracle.forms.ui.VBean;

public class BeanExample extends VBean {
  transient IHandler handler = null;
  private java.awt.Frame frame = null;

  public void init(IHandler handler) {
    super.init(handler);
    this.handler = handler;
    this.frame = this.handler.getApplet().getFrame();
    JLabel eastLabel = new JLabel("EAST", JLabel.CENTER);
    eastLabel.setOpaque(true);
    eastLabel.setBackground(Color.YELLOW);
    eastLabel.setPreferredSize(new Dimension(190,768));
    this.frame.add("East", eastLabel);
    frame.validate();
  }
}


Con handler.getApplet().getFrame() hacemos referencia al frame con el BorderLayout y luego con frame.add("East", xxxx); añadimos el componente.

Resultado:

Video ejemplo:




viernes, 2 de julio de 2021

NAME_IN y COPY con vitaminas

Para acceder a los valores de los campos del registro en el que se encuentra el cursor se puede hacer simplemente usando :bloque.campo.

Si se hace desde una unidad de programa de una PLL habría que usar el Bult-in NAME_IN que se usa de de la siguiente forma: NAME_IN('BLOQUE.CAMPO'). Además de permitir usarse en desde una librería también nos permite hacer código genérico en tiempo de ejecución.

De forma similar, para asignar un valor habría que usar COPY. Se usaría de la siguiente forma: COPY('VALOR A ASIGNAR', 'BLOQUE.CAMPO');

Cuando se quiere acceder a valores de campos de otros registros distintos al actual es donde entra en juego la propiedad DATA_VALUE de Get_Item_Instance_Property y Set_Item_Instance_Property.

Para acceder al valor de un campo del registro anterior al actual se haría con:

Get_Item_Instance_Property('BL.CAMPO', TO_NUMBER(:system.cursor_record) - 1, DATA_VALUE);

Para cambiar el valor de un campo del registro anterior: 

Set_Item_Instance_Property('BL.CAMPO', TO_NUMBER(:system.cursor_record) - 1, DATA_VALUE, 'VALOR A ASIGNAR');

Por ejemplo, si queremos colorear un registro basándonos en registros anteriores, se podría hacer de la siguiente forma. POST-QUERY:

DECLARE
  PROCEDURE pinta_campo(p_campo VARCHAR2) IS
  BEGIN
   Set_Item_Instance_Property(p_campo,
                              CURRENT_RECORD, 
                              VISUAL_ATTRIBUTE, 'AMARILLO');
  END;
BEGIN
  IF :system.trigger_record = 1 OR
    Get_Item_Instance_Property('USER_TAB_COLUMNS.TABLE_NAME',
       TO_NUMBER(:system.trigger_record) - 1,
       DATA_VALUE) != :user_tab_columns.table_name THEN
    pinta_campo('USER_TAB_COLUMNS.TABLE_NAME');
    pinta_campo('USER_TAB_COLUMNS.COLUMN_NAME');
    pinta_campo('USER_TAB_COLUMNS.DATA_TYPE');
  END IF;
END;



FMB de ejemplo: giip_data_value.fmb