Overblog Folge diesem Blog
Edit post Administration Create my blog

An "Expand/Collapse"-Canvas Java-Bean

22. Januar 2011 , Geschrieben von Andreas Weiden

The task

In one of the posts on the OTN forms forum last week there was a question of if there is something like an expandable/collapsable Component like its known from outlook. It seems there is no component like this at the time, so i had the idea to write a java-bean to implement such a feature.

The idea

So what should such a component be capable of

  • the panel should integrate in the standard-lookandfeeld
  • the panle should be capable of containing anything possible in forms
  • everything on such a panel should be a "standard" forms-object
  • you should be able to configure as many single "Panel"

The implementation

And thats my approach.

  • Several panels can be grouped in one panelgroup, from each panelgroup always one panel is active.
  • For each panel you define one stacked canvas, and you can put on that canvas everything you like (items, tree, whole blocks)
  • On each of these stacked canvases there has to be one button which has my javabean as implementation class
  • The stacked canvases have to be arranged on a content canvas that way, that the viewport form one stacked canvas is fully visible, the others are shrinked to show just the button
  • There is one initialization procedure called in the WHEN-NEW-FORM-INSTANCE-trigger
  • You can react on a WHEN-BUTTON-PRESSED-trigger for the created buttons to do some initialization for the shown "Panel"

 

Thats it. Everything else is handled at the java-side, there is no more interaction between the client and application-server.

The code

I've made up a first version of my bean, heres the java-code

package forms;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Toolkit;
import java.net.URL;
import java.util.StringTokenizer;
import javax.swing.ImageIcon;
import oracle.forms.handler.IHandler;
import oracle.forms.ui.DrawnPanel;
import oracle.forms.ui.VBean;
import oracle.forms.properties.ID;
import oracle.forms.ui.VButton;
import java.awt.event.MouseEvent;
import java.awt.Event;
import java.awt.event.ActionEvent;
import java.awt.Graphics;

/**
    This is just sample code, its free to use.
    It is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

    The code relies on the internal structure of forms when rendering stacked canvases
    It is tested against Forms 10.1.2.0.2, but may stop working with any patch or future version of forms
    
    Sample code for a Javabean to implement an expandabled/collapseable canvas.
    For usage-notes see the the package PK_ACCORDION, which is the counterpart for this code
    on the forms-side
*/
public class AccordionButton extends VButton implements Runnable
{
  /** Ids for Forms-events */
  public static final ID  INIT_ACCORDION    =ID.registerProperty("INIT_ACCORDION");
  public static final ID  SCALE_ACCORDION   =ID.registerProperty("SCALE_ACCORDION");
  public static final ID  ACTIVATE          =ID.registerProperty("ACTIVATE");

  /** default-images */
  private final ImageIcon IMG_COLLAPSED=new ImageIcon(this.getClass().getResource("collapsed.png"));
  private final ImageIcon IMG_EXPANDED=new ImageIcon(this.getClass().getResource("expanded.png"));  
  /** constants */
  private static final int MODE_SHRINK=0;
  private static final int MODE_EXPAND=1;
  /** class variables */
  private static boolean  c_processing=false;

  /** currently used images */
  private ImageIcon     m_collapsed=null;
  private ImageIcon     m_expanded=null;
  /** codebase */
  private URL           m_codeBase=null;  

  /** other vars */
  private int             m_mode=-1;
  private String          m_buttonName=null;
  private Dimension       m_dimension=null;
  private DrawnPanel      m_masterCanvas=null;
  private String          m_previousItemName=null;
  private String          m_nextItemName=null;
  private boolean         m_active=false;
  private AccordionButton m_previousItem=null;
  private AccordionButton m_nextItem=null;
  private AccordionButton m_toMakeActive=null;
  private int             m_minHeight=-1;
  private int             m_maxHeight=-1;

  /**
   * Initialize the codebase, used for image-loading
   *
   * @param handler
   */
  public void init(IHandler handler)
  {
    // Remember Codebase
    m_codeBase = handler.getCodeBase();
    super.init(handler);
  }

  /**
   *
   * @return Searches for an AccordionButton with the given name,
   *         starting the search at the given Container
   * @param name Name of the accordion button to search
   * @param c    Container to start at
   */
  private AccordionButton rekuFindChild(Container c, String name)
  {
    AccordionButton result=null;
    for (int i=0;i<c.getComponentCount();i++)
    {
      Component comp=c.getComponent(i);
      if (comp instanceof AccordionButton)
      {
        if (name.equals(((AccordionButton)comp).getButtonName()))
        {
          result=(AccordionButton)comp;
          break;
        }
      }
    }
    if (result==null)
    {
      for (int i=0;i<c.getComponentCount();i++)
      {
        if (c.getComponent(i) instanceof Container)
        {
          result=rekuFindChild((Container)c.getComponent(i), name);
          if (result!=null)
          {
            break;
          }
        }
      }
    }
    return result;
  }

  /**
   * finds the "Neighbor"-Accordion-Button by takeing the names given by
   * the initialization property
   */
  private void findNeighbors()
  {
    if (!".".equals(m_previousItemName))
    {
      m_previousItem=rekuFindChild(m_masterCanvas, m_previousItemName);
    }
    if (!".".equals(m_nextItemName))
    {
      m_nextItem=rekuFindChild(m_masterCanvas, m_nextItemName);
    }
  }

  /**
   * Initializationof the AccordionButton. The data is given as a string, the
   * values are concatenated by |.
   * @param data
   */
  private void init(String data)
  {
    StringTokenizer st=new StringTokenizer(data, "|");
    if (st.hasMoreTokens())
    {
      m_buttonName=st.nextToken();
      //System.out.println("My name is " + m_buttonName);
    }
    String active="N";
    if (st.hasMoreTokens())
    {
      active=st.nextToken();
    }
    m_active="J".equals(active);
    if (st.hasMoreTokens())
    {
      m_previousItemName=st.nextToken();
    }
    if (st.hasMoreTokens())
    {
      m_nextItemName=st.nextToken();
    }
    if (st.hasMoreTokens())
    {
      String image=st.nextToken();
      m_expanded=loadImage(image);
    }
    if (st.hasMoreTokens())
    {
      String image=st.nextToken();
      m_collapsed=loadImage(image);
    }


    int dp=0;
    Container c=this;
    while (c.getParent()!=null && dp<2)
    {
      c=c.getParent();
      if (c instanceof DrawnPanel)
      {
        dp++;
      }
    }
    if (c instanceof DrawnPanel)
    {
      m_masterCanvas=(DrawnPanel)c;
    }
    m_dimension=this.getParent().getParent().getParent().getSize();
  }

  /**
   * Delegates the current maximum height to the next AccordionButon in the group
   * @param maxHeight
   */
  protected void scaleNext(int maxHeight)
  {
    // init Neighbours
    findNeighbors();
 
    m_minHeight=this.getX()+this.getHeight();
    m_maxHeight=maxHeight;
    if (m_active)
    {
      m_maxHeight=(int)m_dimension.getHeight()-m_minHeight;
    }
    if (m_nextItem!=null)
    {
      m_nextItem.scaleNext(m_maxHeight);
    } else if (m_previousItem!=null)
    {
      m_previousItem.scalePrev(m_maxHeight);
    }
  }

  /**
   * Delegates the current maximum height to the previous AccordionButon in the group
   * @param maxHeight
   */
  protected void scalePrev(int maxHeight)
  {
    m_maxHeight=maxHeight;
    if (m_previousItem!=null)
    {
      m_previousItem.scalePrev(m_maxHeight);
    }
  }


  /**
   * Searches the previous buttons if one of them is the active one,
   * if found, it will start the logic to deactivate (shrink) it
   *
   * @return true if one of the previous buttons was the active one
   * @param toMakeActive the button which should be activated
   */
  protected boolean closePrevActive(AccordionButton toMakeActive)
  {
    if (m_active)
    {
      m_mode=MODE_SHRINK;
      m_toMakeActive=toMakeActive;
      m_active=false;
      Thread t=new Thread(this);
      t.start();
      return true;
    }
    if (m_previousItem!=null)
    {
      return m_previousItem.closePrevActive(toMakeActive);
    }
    return false;
  }

  /**
   * Searches the following buttons if one of them is the active one,
   * if found, it will start the logic to deactivate (shrink) it
   *
   * @return true if one of the following buttons was the active one
   * @param toMakeActive the button which should be activated
   */
  protected boolean closeNextActive(AccordionButton toMakeActive)
  {
    if (m_active)
    {
      m_mode=MODE_SHRINK;
      m_toMakeActive=toMakeActive;
      m_active=false;
      Thread t=new Thread(this);
      t.start();
      return true;
    }
    if (m_nextItem!=null)
    {
      return m_nextItem.closeNextActive(toMakeActive);
    }
    return false;
  }


  /**
   * closes the active Accordion, either a previos or a following one
   */
  private void closeActive()
  {
    // Try to close to prev-direction
    if (!closePrevActive(this))
    {
      // Not found, close next active
      closeNextActive(this);
    }
  }

  /**
   * Standard Method, overwritten to make the bean-specific properties from forms
   * @return true
   * @param value
   * @param id    
   */
  public boolean setProperty(ID id, Object value)
  {
    if (id==INIT_ACCORDION)
    {
      //System.out.println(INIT_ACCORDION);    
      init((String)value);
      return true;
    } else if (id==SCALE_ACCORDION)
    {
      //System.out.println(SCALE_ACCORDION);    
      // Scale
      scaleNext(-1);
      // Adjust Initial Layout
      if (m_nextItem!=null)
      {
        m_nextItem.adjustPosition(this.getParent().getParent().getParent().getY()+this.getParent().getParent().getParent().getHeight());
      }
      return true;
    } else if (id==ACTIVATE)
    {
      processActionEvent(null);
      return true;
    } else
    {
      return super.setProperty(id, value);
    }
  }

  /**
   * Run-Method of the Runnable-interface, does the animation of
   * slowly shrinking or growing the canvas
   */
  public void run()
  {
    boolean done=false;
    int height=this.getParent().getParent().getHeight();
    //System.out.println("Start mit Höhe" + height);
    int offset=(m_mode==MODE_SHRINK ? -10 : 10);
    while (!done)
    {
      try
      {
        height=height+offset;
        if (m_mode==MODE_SHRINK && height<=m_minHeight)
        {
          height=m_minHeight;
          done=true;
        } else if (m_mode==MODE_EXPAND && height>=m_maxHeight+m_minHeight)
        {
          height=m_maxHeight+m_minHeight;
          done=true;
        }
        this.getParent().getParent().setSize((int)m_dimension.getWidth(), height);
        this.getParent().getParent().getParent().setSize((int)m_dimension.getWidth(), height);
        if (m_nextItem!=null)
        {
          m_nextItem.adjustPosition(this.getParent().getParent().getParent().getY()+this.getParent().getParent().getParent().getHeight());
        }
        Thread.sleep(10);
      } catch (Exception e)
      {
      }
    }
    if (m_mode==MODE_SHRINK && m_toMakeActive!=null)
    {
      // Now make the other one active
      m_toMakeActive.makeActive();
    }
    c_processing=false;
    ActionEvent e=new ActionEvent(this, ActionEvent.ACTION_PERFORMED, null);
    super.processActionEvent(e);
  }

  /**
   * Intercepty the standard WHEN-BUTTON-PRESSED and activates the
   * Accordion instead. The WHEN-BUTTON-PRESSED-event is deferred until
   * the current accordion has become active.
   * @param p0
   */
  protected void processActionEvent(ActionEvent p0)
  {
    // Nothing happens whn already active
    if (!m_active && !c_processing)
    {
      c_processing=true;
      // close the active item
      // This will in turn start the thread to open this accordion
      closeActive();
    }
  }

  /**
   * Name assigned to the button
   * @return Name of the button
   */
  public String getButtonName()
  {
    return m_buttonName;
  }

  /**
   * Set flags and start expanding thread
   */
  public void makeActive()
  {
    m_active=true;
    m_mode=MODE_EXPAND;
    m_toMakeActive=null;
    Thread t=new Thread(this);
    t.start();
  }
 
  /**
   * adjust the position of the stacked canvas and delegate to the following Accordion.
   * @param yPosition
   */
  protected void adjustPosition(int yPosition)
  {
    this.getParent().getParent().getParent().setLocation(this.getParent().getParent().getParent().getX(), yPosition);
    if (m_nextItem!=null)
    {
      m_nextItem.adjustPosition(this.getParent().getParent().getParent().getY()+this.getParent().getParent().getParent().getHeight());
    }
    
  }

  /**
   * Paint the button
   * @param g
   */
  public void paint(Graphics g)
  {
    g.setColor(getBackground());
    g.fillRect(getX()+1, getY()+1, getWidth()-2, getHeight()-2);
    getBorderPainter().paint(getPaintContext(),g, getX(), getY(), getWidth(), getHeight());
    int textpos=4;
    if (m_collapsed!=null || m_expanded!=null)
    {
      if (m_active && m_expanded!=null)
      {
        int yPos=((m_expanded.getIconHeight()<getHeight()) ? ((getHeight()-m_expanded.getIconHeight())/2) : 2);
        int height=((m_expanded.getIconHeight()<getHeight()) ? m_expanded.getIconHeight() : getHeight()-4);
        g.drawImage(m_expanded.getImage(), 4, yPos, m_expanded.getIconWidth(), height, null);
      } else if (!m_active && m_collapsed!=null)
      {
        int yPos=((m_collapsed.getIconHeight()<getHeight()) ? ((getHeight()-m_collapsed.getIconHeight())/2) : 2);
        int height=((m_collapsed.getIconHeight()<getHeight()) ? m_collapsed.getIconHeight() : getHeight()-4);
        g.drawImage(m_collapsed.getImage(), 4, yPos, m_collapsed.getIconWidth(), height, m_collapsed.getImageObserver());
      }
      textpos=textpos+Math.max(m_collapsed.getIconWidth(), m_expanded.getIconWidth());
    }
    g.setFont(getFont());
    g.setColor(getForeground());
    g.drawString(getLabel(), textpos, (getHeight()/2)+(g.getFontMetrics().getHeight()-g.getFontMetrics().getDescent())/2);
  }

  /**
   * Load the image given by name. Code is taken from oracle-demo RolloverButton
   * @return loaded Icon or null
   * @param imageName
   */
  private ImageIcon loadImage(String imageName)
  {
    // LoadImage, taken from oracle-demo RolloverButton
    URL imageURL = null;
    ImageIcon result=null;
    boolean loadSuccess=false;
    if ("DEFAULT_EXPANDED".equals(imageName))
    {
      result=IMG_EXPANDED;
    } else if ("DEFAULT_COLLAPSED".equals(imageName))
    {
      result=IMG_COLLAPSED;
    } else if (!".".equals(imageName))
    {
      //JAR
      imageURL = getClass().getResource("/"+imageName);
      if (imageURL != null)
      {
        try
        {
          result = new ImageIcon(Toolkit.getDefaultToolkit().getImage(imageURL));
          loadSuccess = true;
        }
        catch (Exception ilex)
        {
        }
        //DOCBASE
        if (!loadSuccess)
        {
          try
          {
            if (imageName.toLowerCase().startsWith("http://")||imageName.toLowerCase().startsWith("https://"))
            {
              imageURL = new URL(imageName);
            }
            else
            {
              imageURL = new URL(m_codeBase.getProtocol() + "://" + m_codeBase.getHost() + ":" + m_codeBase.getPort() + imageName);
            }
            try
            {
              result= new ImageIcon(createImage((java.awt.image.ImageProducer) imageURL.getContent()));
              loadSuccess = true;
            }
            catch (Exception ilex)
            {
            }
          }
          catch (java.net.MalformedURLException urlex)
          {
          }
        }
        //CODEBASE
        if (!loadSuccess)
        {
          try
          {
            imageURL = new URL(m_codeBase, imageName);
            try
            {
              result= new ImageIcon(createImage((java.awt.image.ImageProducer) imageURL.getContent()));
              loadSuccess = true;
            }
            catch (Exception ilex)
            {
            }
          }
          catch (java.net.MalformedURLException urlex)
          {
          }
        }
      }
    }
    return result;
  }

}

 

And here's the forms-code

 

PACKAGE PK_ACCORDION IS
/**
   
    This is just sample code, its free to use.
    It is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

    The code relies on the internal structure of forms when rendering stacked canvases
    It is tested against Forms 10.1.2.0.2, but may stop working with any patch or future version of forms

    Sample code for a forms-Side Package for the Accordion Java-Bean

    To build up an accordion, follow these steps
   
    -Create as many stacked canvases as you want to have accordion-canvases
    -Create a Button on each canvas at position 0,0 with the full width of the canvas and
     set the Implementation class for that buttons to forms.AccordionButton
    -Arrange these canvases on one content canvas, so that all canvases are placed beneath each other
     +The viewport for one canvas should be so that the canvas is fully shown
     +The viewport for all other canvases should be so that just the button is visible.
    
    -Initialize the Accordion in the WHEN-NEW-FORM-INSTANCE-trigger with code lik
   
        DECLARE
            lAccordionList PK_ACCORDION.tAccordionList;
            rAccordion     PK_ACCORDION.tAccordion;
        BEGIN
            rAccordion.vcCanvas:='CANVAS1';
            rAccordion.vcButton:='BLOCK.BUTTON1';
            rAccordion.vcExpandedImage:=PK_ACCORDION.VCC_DEFAULT_EXPANDED_IMAGE;
            rAccordion.vcCollapsedImage:=PK_ACCORDION.VCC_DEFAULT_COLLAPSED_IMAGE;
            rAccordion.bOpened:=TRUE;
            lAccordionList(1):=rAccordion;
   
                rAccordion.vcCanvas:='CANVAS2';
                rAccordion.vcButton:='BLOCK.BUTTON2';
                rAccordion.vcExpandedImage:=PK_ACCORDION.VCC_DEFAULT_EXPANDED_IMAGE;
                rAccordion.vcCollapsedImage:=PK_ACCORDION.VCC_DEFAULT_COLLAPSED_IMAGE;
                rAccordion.bOpened:=FALSE;
                lAccordionList(2):=rAccordion;
   
          PK_ACCORDION.PR_INIT_ACCORDION('MAIN', lAccordionList);
        END;
   
    -make sure the jar is on the archive or achive_jini-tag   
   
*/
  -- constants used for Default-Images conatined in the jar-file
  VCC_DEFAULT_EXPANDED_IMAGE  CONSTANT VARCHAR2(30):='DEFAULT_EXPANDED';
  VCC_DEFAULT_COLLAPSED_IMAGE CONSTANT VARCHAR2(30):='DEFAULT_COLLAPSED';
 
  -- Type which describes one Accordion
  TYPE tAccordion IS RECORD (
    vcCanvas         VARCHAR2(30), -- the Canvas which represents the Accordion-area
    vcButton         VARCHAR2(61), -- the "Activation"-button on that canvas which is used to activate the Accordion
    vcExpandedImage  VARCHAR2(255),-- image-name for Expanded-state, image must be accessible by forms
    vcCollapsedImage VARCHAR2(255),-- image-name for Collapsed-state, image must be accessible by forms
    bOpened          BOOLEAN       -- Flag, if this accordion is the one that is displayed at startup
                                        -- Must match the canvas. The canvases area beneath the button is taken
                                        -- as the area which is given to other accordions when opened
  );
 
  -- Table-Type of Accordion-records
  TYPE tAccordionList IS TABLE OF tAccordion INDEX BY BINARY_INTEGER;
 
  /** Initialization-method for an accordion group
      i_vcAccordionGroup indicates a logical name. It gives the capability of having several Accordion-groups inside one form.
      i_lAccordionList   is a list of the Accordion-entries which belong to the accordion-group
  */   
  PROCEDURE PR_INIT_ACCORDION(i_vcAccordionGroup IN VARCHAR2, i_lAccordionList IN tAccordionList);
 
  /** method to activate a specific accordion programmatically
      i_vcAccordionGroup indicates a logical name. Must be a name which has been initialized via PR_INIT_ACCORDION before
      i_vcCanvas         is the name of the canvas in one of the accordion-entries in the group
  */   
  PROCEDURE PR_ACTIVATE(i_vcAccordionGroup IN VARCHAR2, i_vcCanvas IN VARCHAR2);

  /** method to go to an item placed on an accordion-canvas.
      i_vcItem Name of the item which should get the focus
     
      Thanks to Francois Degrelle for supplying the code
  */   
  PROCEDURE PR_GO_ITEM(i_vcItem IN VARCHAR2);

  /** method to go to a block the first item of which is placed on an accordion-canvas.
      i_vcBlock Name of the block which should get the focus
     
      Thanks to Francois Degrelle for supplying the code
  */   
  PROCEDURE PR_GO_BLOCK(i_vcBlock IN VARCHAR2);
 
 
END;

PACKAGE BODY PK_ACCORDION IS

  TYPE tList IS TABLE OF tAccordionList INDEX BY VARCHAR2(30);
  lList tList;

  -- --------------------------------------------------------------------------------------
 
  PROCEDURE PR_INIT_ACCORDION(i_vcAccordionGroup IN VARCHAR2, i_lAccordionList IN tAccordionList) IS
    vcPrior VARCHAR2(4000);
    vcNext  VARCHAR2(4000);
  BEGIN
      lList(i_vcAccordionGroup):=i_lAccordionList;
      FOR i IN 1..i_lAccordionList.COUNT LOOP
          -- prior Accordion-entry
          IF i>1 THEN
              vcPrior:=i_lAccordionList(i-1).vcButton;
          ELSE
              vcPrior:='.';
          END IF;
          -- following Accordion-entries
          IF i<i_lAccordionList.COUNT THEN
            vcNext:=i_lAccordionList(i+1).vcButton;
          ELSE
              vcNext:='.';
          END IF;
          SET_CUSTOM_ITEM_PROPERTY(i_lAccordionList(i).vcButton, 'INIT_ACCORDION', i_lAccordionList(i).vcButton || '|' ||
                                                                                             CASE WHEN i_lAccordionList(i).bOpened THEN
                                                                                               'J'
                                                                                             ELSE
                                                                                                 'N'
                                                                                             END     || '|' || 
                                                                                             vcPrior || '|' || 
                                                                                             vcNext  || '|' ||
                                                                                             NVL(i_lAccordionList(i).vcExpandedImage, '.')  || '|' ||
                                                                                             NVL(i_lAccordionList(i).vcCollapsedImage, '.') || '|'
                                            );
    END LOOP;
    SYNCHRONIZE;
    -- Make first item scale, it will delegate to others
    SET_CUSTOM_ITEM_PROPERTY(i_lAccordionList(1).vcButton, 'SCALE_ACCORDION', ' ');
  END;
 
  -- --------------------------------------------------------------------------------------
 
  PROCEDURE PR_ACTIVATE(i_vcAccordionGroup IN VARCHAR2, i_vcCanvas IN VARCHAR2) IS
    lAccordionList tAccordionList;
  BEGIN
    IF lList.EXISTS(i_vcAccordionGroup) THEN
        lAccordionList:=lList(i_vcAccordionGroup);
        FOR i IN 1..lAccordionList.COUNT LOOP
            IF lAccordionList(i).vcCanvas=i_vcCanvas THEN
                SET_CUSTOM_ITEM_PROPERTY(lAccordionList(i).vcButton, 'ACTIVATE', ' ');
                EXIT;
            END IF;
        END LOOP;
    END IF;
  END;
     
  -- --------------------------------------------------------------------------------------
 
  PROCEDURE PR_GO_ITEM(i_vcItem IN VARCHAR2) IS
    lAccordionList tAccordionList;
    vcCanvas       VARCHAR2(100);
    vcGroup        VARCHAR2(30);
    itId           ITEM;
  BEGIN
    -- find the item canvas --
    itId:=FIND_ITEM(i_vcItem);
    IF NOT ID_NULL(itId) THEN
      vcCanvas:=GET_ITEM_PROPERTY(itId, ITEM_CANVAS ) ;
      -- find the accordion group --
      vcGroup:=lList.FIRST ;
      LOOP
        EXIT WHEN vcGroup IS NULL;
        lAccordionList:=lList(vcGroup);
        FOR i IN 1..lAccordionList.COUNT LOOP
          IF UPPER(lAccordionList(i).vcCanvas) = vcCanvas THEN
            PR_ACTIVATE(vcGroup, vcCanvas);
            GO_ITEM(i_vcItem);
            RETURN;
          END IF;
        END LOOP;
        vcGroup:=lList.NEXT(vcGroup) ;
      END LOOP;
    END IF;
  END;
  
  -- --------------------------------------------------------------------------------------
 
  PROCEDURE PR_GO_BLOCK(i_vcBlock IN VARCHAR2) IS
    lAccordionList tAccordionList;
    vcCanvas       VARCHAR2(100);
    vcGroup        VARCHAR2(30);
    vcItem         VARCHAR2(61);
    blId           BLOCK;
    bOk            BOOLEAN:= FALSE ;
  BEGIN
    -- find the canvas --
    blId := FIND_BLOCK(i_vcBlock);
    IF NOT ID_NULL(blId) THEN
      vcItem:= GET_BLOCK_PROPERTY(blId, FIRST_ITEM);
      LOOP
        vcCanvas:= GET_ITEM_PROPERTY(vcItem, ITEM_CANVAS ) ;
        IF vcCanvas IS NOT NULL THEN
          bOk:=TRUE;
          EXIT;
        END IF ;
        vcItem:= GET_ITEM_PROPERTY(vcItem, NEXTITEM ) ;
        EXIT WHEN vcItem IS NULL;
      END LOOP;
      IF bOk THEN
        -- find the accordion group --
        vcGroup:=lList.FIRST;
        LOOP
          EXIT WHEN vcGroup IS NULL;
          lAccordionList:=lList(vcGroup);
          FOR i IN 1..lAccordionList.COUNT LOOP
            If UPPER(lAccordionList(i).vcCanvas)=vcCanvas THEN
              PR_ACTIVATE(vcGroup, vcCanvas);
              GO_BLOCK(i_vcBlock);
              RETURN;
            END IF;
          END LOOP;
          vcGroup:=lList.NEXT(vcGroup);
        END LOOP;
      END IF;
    END IF ;
  END PR_GO_BLOCK; 
END;

 

This video shows the usage of the bean with the Oracle demo-tables EMPLOYEES and DEPARTMENTS

 

And this one is just a show-case which shows the usage of several accordions inside one form, including a short tutorial

A compiled version with a demo-fmb can be found at Francois Degrelle's PJC-site here

Diesen Post teilen

Repost 0

Kommentiere diesen Post

Richard 02/01/2011 10:30


very impresssive, can't wait to see how you've actually accomplished this.


Pedro Leite 01/27/2011 16:14


looking forward...when u release a "stable" version :) keep up the good work!


Pedro Leite 01/27/2011 16:13


nice!!! i'm looking for something like that...