space
Previous Page | Next Page

Extending Ready-Made Applications

Subclassing Viewers

You can modify the functionality of SAS/EIS viewers by subclassing the viewers and then adding your own components or methods to them. You can use this technique to add logos, additional titles, or additional controls to the window. The subclassed viewer can then provide methods to control the interaction between the new controls and the SAS/EIS components.

The steps for subclassing a viewer are:

  1. Determine which existing SAS/EIS class you want to subclass.

  2. Determine which new components you will be adding.

  3. Determine the new methods, new attributes, and overridden methods.

  4. Build the new class, specifying the existing SAS/EIS class as the parent. Add the new controls in the component definition window. Add the new methods, new attributes, and method overrides.

  5. Code the new functionality.

  6. Build a SAS/EIS application, specifying your viewer as the new viewer.

  7. Test the application.

The following example shows you how to create a viewer that displays a table and an area on the screen that enables you to enter notes for the currently selected data cell. The notes can then be saved and redisplayed as needed.

Note:   This example has been simplified to illustrate the technique that you will use. You will need to extend the example code with additional error checking in order for it to be a complete customization.  [cautionend]


Step 1: Determine which existing SAS/EIS class you want to subclass

Because you are using a table and adding components, you will be subclassing the SASHELP.EIS.TABLET_V.CLASS viewer. You can view a list of the viewers that are supplied by SAS software by going to the build window for the object, clicking Advanced, and then selecting the down arrow next to the Viewer field.


Step 2: Determine which new components you will be adding

First, look at which components are currently on the viewer. To do this, follow these steps:

  1. Type BUILD SASHELP.EIS.TABLET_V.CLASS on the command line.

  2. Select the Attributes node on the left-hand side of the Class Editor.

  3. In the table on the right-hand side of the Class Editor, select the componentDefinition attribute.

  4. Edit the Initial Value field.

    The TABLET_V class contains a table editor and an area for titles. You will be adding an Extended Text Entry field below the table editor.


Step 3: Determine the new methods, new attributes, and overridden methods

In your code, you need to

To provide this functionality you will

  1. Override the _postInit method to load any existing notes for a catalog entry into an SLIST entry. To simplify this example, assume that the cell notes are stored in SASUSER.EISNOTE.<applname>.SLIST, where applname is the 8-character application name.

  2. Override the _refresh method to change the cell border for cells that contain notes.

  3. Override the _objectLabel method to process a single click on a cell. If the user selects a cell that does not contain a note, the note area will be cleared. If the user selects a cell that currently contains a stored note, the stored note will be loaded into the note field. The selected cell will have its border changed. If there was an "active" cell and a new cell is selected, the contents of the note field will be saved.

  4. Override the _term method to save the notes to the SLIST catalog entry.

  5. Add a new method, updateNote, which will update an internal list with the contents of the note field.

New attributes are the following:

notesList;

is a list that will be loaded in _postInit with the contents of the SLIST catalog entry. At _term, the contents of this list will be written back out to the catalog entry. updateNote will update the contents of the list.

currentNote;

is a list that will contain the address for the current note.

notePosition;

is a number indicating the position within notesList of the current note.


Step 4: Build the new class, adding the new controls, methods, and attributes

To create the new class, type BUILD SASUSER.EIS.TABNOTE.CLASS on the command line. When the Class Editor appears, enter SASHELP.EIS.TABLET_V.CLASS for the parent and press ENTER. At this point, you can modify the attributes and methods of the class. See Class Editor for more information on creating new classes with the Class Editor.

  1. Add the attributes that you defined in Step 3 by following these steps:

    1. Select the Attributes node on the left-hand side of the Class Editor.

    2. On the right-hand side of the Class Editor, open the pop-up menu over the table and select New Attribute. Enter the following information for the new attributes:

      Attribute Name Type Auto Create Initial Value
      notesList List No
      currentNote List No
      notePosition Number
      0

  2. Override the methods by following these steps:

    1. Select the Methods node on the left-hand side of the Class Editor.

    2. On the right-hand side of the Class Editor, find the method that you want to override in the table. Open the pop-up menu over the method and select Override.

      Override the following methods:

      Method Name Source Entry Source Label
      _postInit SASUSER.EIS.TABNOTE.SCL postInit
      _refresh SASUSER.EIS.TABNOTE.SCL refresh
      _objectLabel SASUSER.EIS.TABNOTE.SCL objlabel
      _term SASUSER.EIS.TABNOTE.SCL term

      Note:   Be sure to override both of the _term methods.  [cautionend]

  3. Add a new method by following these steps:

    1. Select Methods on the left-hand side of the Class Editor.

    2. On the right-hand side, open the pop-up menu over the table and select New Method.

      For the new method, enter the following:

      Method Name Source Entry Source Label
      updateNote SASUSER.EIS.TABNOTE.SCL UpdateNote

  4. Add the new Notes field to the composite. Composites are custom components that consist of at least two existing components. For more information on composites, refer to Combining Components to Create Composites. To add the Notes field, follow these steps:

    1. Select the Attributes node on the left-hand side of the Class Editor.

    2. On the right-hand side of the Class Editor, open the pop-up menu over the table and select the componentDefinition attribute.

    3. Edit the Initial Value field.

    4. In the Composite Definition window, select the composite outline. Then select Layout [arrow] Attach [arrow] Define attachments.

    5. In the Define Attachment window, remove the attachment from the table to the bottom of the region. Click OK.

    6. Make the composite area bigger by dragging out the bottom of the region.

    7. Add the Extended Text Entry (ETE) field to the bottom of the region. Resize the field.

    8. Open the pop-up menu over the ETE and select Object Attributes. Change the name of the field to CELLNOTE. Click OK.

    9. Go back to the Define Attachment window by selecting Layout [arrow] Attach [arrow] Define Attachment.

    10. In the Define Attachment window, add the following attachments:

      1. From the middle of the ETE to the bottom of the region.

      2. From the bottom of the table to the top of the ETE.

      3. From the side of the ETE to the side of the region.

      For more information on defining attachments, refer to Adding Attachments to Frame Controls.
    11. After adding the attachments, close the Define Attachment window and save the class.


Step 5: Code the methods

Use the following code for the methods:

/* Copyright(c) 2004 by SAS Institute Inc., Cary, NC USA */
/*-------------------------------------------------------+
 | Set SCL variable lengths and declare the model as an object.
 +-------------------------------------------------------*/
length class_name $ 32
       applname   $ 8
       status     $ 1
       rc           8 ;

dcl object modelid _frame_ cellnote;
/*-------------------------------------------------------+
 | Initialize attributes to remove warnings.
 +-------------------------------------------------------*/
modelid = modelid;
_self_  = _self_;
_frame_ = _frame_;
cellnote = cellnote;

postInit: method;
   call super(_self_,'_postInit_');
/*-------------------------------------------------------+
 | Get the application name from the frame's arguments.
 +-------------------------------------------------------*/
   args = makelist();
   _frame_._getArglist(args);
   applname = getitemc(args,9);
   rc = dellist(args);
/*-------------------------------------------------------+
 | Check to see if sasuser.eisnote.<applname>.slist exists.
 |    If not, gray the cellnote field.
 |    If so, load the list.
 +-------------------------------------------------------*/
   fullname = 'sasuser.eisnote.' || trim(applname) || '.slist';
   _self_.notesList = makelist();
   if ^cexist(fullname) then
      cellnote._gray();
   else
      rc = fillist('slist',fullname,_self_.notesList);
endmethod;


refresh: method;
/*-------------------------------------------------------+
 | For each sublist, get the class address list and set
 |    the border style, width, and color.
 | NOTE:  This example will only work at a single drill
 |    level.  Additional code should be added to allow
 |    for drill downs, expands, and so on.
 +-------------------------------------------------------*/
   do i = 1 to listlen(_self_.notesList);
      sublist = getiteml(_self_.notesList,i);
      classAddress = getniteml(sublist,'address',1,1,0);
      if listlen(classAddress) <= 0 then continue;
      rc = sortlist(classAddress,'NAME');
      notCurrent = comparelist(classAddress,_self_.currentNote);
      _self_.modelid._setCellBorderStyle(classAddress,'ALL',
                                         'SOLID');
      if notCurrent then
         _self_.modelid._setCellBorderColor(classAddress,'ALL',
                                            'blue');
      else
         _self_.modelid._setCellBorderColor(classAddress,'ALL',
                                            'red');
      _self_.modelid._setCellBorderWidth(classAddress,'ALL',
                                         'thick');
   end;
   call super(_self_,'_refresh');
endmethod;

objlabel: method;
   call super(_self_,'_object_label_');
/*-------------------------------------------------------+
 | Get the frame status.  If not single click, return.
 +-------------------------------------------------------*/
   _frame_._getStatus(status);
   if status ^= '' then
      return;
/*-------------------------------------------------------+
 | Check to see if you are in the data or on a class value.
 |    Return if not in the data.
 +-------------------------------------------------------*/
   if _self_.modelid.in_data ^= 'Y' then
      return;
/*-------------------------------------------------------+
 | Get the current active value.
 +-------------------------------------------------------*/
   cell_list = makelist();
   hlist = makelist();
   _self_.modelid._getActiveValue(cell_list,class_name,hlist);
   rc = dellist(hlist);
/*-------------------------------------------------------+
 | Is the selected cell the current note cell?
 |    If the same, return.
 +-------------------------------------------------------*/
   rc = sortlist(cell_list,'NAME');
   newCell = comparelist(cell_list,_self_.currentNote);
   if ^newCell then
      return;
/*-------------------------------------------------------+
 | Update the notes list for the current note.
 +-------------------------------------------------------*/
   _self_.updateNote();
/*-------------------------------------------------------+
 | Make the selected cell the current note cell.
 | Turn the cell border for the new current to red.
 +-------------------------------------------------------*/
   _self_.currentNote = cell_list;
   _self_.modelid._setCellBorderStyle(_self_.currentNote,
                                      'ALL','SOLID');
   _self_.modelid._setCellBorderWidth(_self_.currentNote,
                                      'ALL','thick');
   _self_.modelid._setCellBorderColor(_self_.currentNote,
                                      'ALL','RED');
/*-------------------------------------------------------+
 | See if there is a stored note for this cell.
 +-------------------------------------------------------*/
   found = 0;
   do i = 1 to listlen(_self_.notesList) until(found);
      sublist = getiteml(_self_.notesList,i);
      classList = getniteml(sublist,'address');
      rc = sortlist(classList,'NAME');
      newNote = comparelist(classList,_self_.currentNote);
      if ^newNote then
         found = 1;
   end;
/*-------------------------------------------------------+
 | If not found, clear the text entry field and
 |    set _self_.notePosition to zero.
 +-------------------------------------------------------*/
   if ^found then do;
      cellnote._clear();
      _self_.notePosition = 0;
   end;
/*-------------------------------------------------------+
 | If found, set the note position and set the text.
 +-------------------------------------------------------*/
   else do;
      _self_.notePosition = i;
      textList = getniteml(sublist,'text');
      cellnote._setValue(textList);
   end;
/*-------------------------------------------------------+
 | Ungray the text entry fields.
 +-------------------------------------------------------*/
   cellnote._ungray();
endmethod;

updateNote:  method;
/*-------------------------------------------------------+
 | Different cell.  Save the current note, if any.
 +-------------------------------------------------------*/
   textList = makelist();
   cellnote._getValue(textList);
   hasText = 0;
   do i = 1 to listlen(textList) until(hasText);
      item = getitemc(textList,i);
      if item ^= '' then
         hasText = 1;
   end;
   if hasText then do;
/*-------------------------------------------------------+
 | Is this note already on the list?
 +-------------------------------------------------------*/
      if _self_.notePosition then do;
         sublist = getiteml(_self_.notesList,_self_.notePosition);
         oldlist = getniteml(sublist,'text');
         rc = dellist(oldlist);
         rc = setniteml(sublist,textlist,'text');
      end;
/*-------------------------------------------------------+
 | Not on the list.  Create a new sublist.
 +-------------------------------------------------------*/
      else do;
         sublist = makelist();
         rc = insertl(_self_.notesList,sublist,-1);
         rc = insertl(sublist,_self_.currentNote,-1,'address');
         rc = insertl(sublist,textList,-1,'text');
      end;
/*-------------------------------------------------------+
 | Return the cell borders to blue.
 +-------------------------------------------------------*/
      if listlen(_self_.currentNote) > 0 then do;
         _self_.modelid._setCellBorderStyle(_self_.currentNote,
                                            'ALL','SOLID');
         _self_.modelid._setCellBorderWidth(_self_.currentNote,
                                            'ALL','thick');
         _self_.modelid._setCellBorderColor(_self_.currentNote,
                                            'ALL','blue');
      end;
   end;
   else do;
/*-------------------------------------------------------+
 | Note contains no text.  If looking at a note
 |    that was stored, delete it from the list.
 +-------------------------------------------------------*/
      if _self_.notePosition then do;
         sublist = getiteml(_self_.notesList,_self_.notePosition);
         rc = dellist(sublist,'Y');
         rc = delitem(_self_.notesList,_self_.notePosition);
      end;
/*-------------------------------------------------------+
 | Return the cell border to black, none.
 +-------------------------------------------------------*/
      if listlen(_self_.currentNote) > 0 then do;
         _self_.modelid._setCellBorderStyle(_self_.currentNote,
                                            'ALL','NONE');
         _self_.modelid._setCellBorderColor(_self_.currentNote,
                                            'ALL','BLACK');
         _self_.modelid._setCellBorderWidth(_self_.currentNote,
                                            'ALL','NORMAL');
      end;
   end;
endmethod;

term: method optional = yorn $1;
/*-------------------------------------------------------+
 | Update the current note, if any.
 +-------------------------------------------------------*/
   _self_.updateNote();
/*-------------------------------------------------------+
 | Get the application name from the frame's arguments.
 +-------------------------------------------------------*/
   args = makelist();
   _frame_._getArglist(args);
   applname = getitemc(args,9);
   rc = dellist(args);
   fullname = 'sasuser.eisnote.' || trim(applname) || '.slist';
/*-------------------------------------------------------+
 | Save the notesList, if there are notes.
 +-------------------------------------------------------*/
   if listlen(_self_.notesList) > 0 then do;
      descript = 'Notes for ' || trim(applname);
      rc = savelist('slist',fullname,_self_.notesList,0,descript);
   end;
/*-------------------------------------------------------+
 | If there are no notes, delete the old .slist, if any.
 +-------------------------------------------------------*/
   else do;
      if cexist(fullname) then
         rc = delete(fullname,'catalog');
   end;
   rc = dellist(_self_.notesList,'Y');
   rc = dellist(_self_.currentNote);
   call super(_self_,'_term_',yorn);
endmethod;


Step 6: Build an EIS application, specifying a new viewer

To build an EIS and specify a new viewer, follow these steps:

  1. Double-click Build EIS in the EIS Main Menu to display the Build EIS window. Click Add.

  2. In the Add window, select Multidimensional Reports, if not already selected, from the Object Databases list box, and select Multidimensional report from the Objects list box. Click Build.

  3. In the Multidimensional Report window, type the name and description for your report. Select a table and the columns for your report. Click Advanced to display the Advanced window.

  4. In the Advanced window, specify SASUSER.EIS.TABNOTE.CLASS as the Viewer.

  5. Save the application.


Step 7: Test the application

To test the application, click Test in the Build EIS window. The new viewer should appear. The table will contain the report, and the notes field should be initially grayed. Select any cell in the table and then enter a note. To test whether the notes are saved, end the report and test it again. The cells for which you entered notes should be highlighted. When you select any of the cells, the corresponding note should be displayed in the notes field.

In this example, you added components to an existing SAS/EIS viewer. You can use this same technique to create a new composite that contains one or more SAS/EIS base viewers. Or you could subclass a base (non-composite) viewer to override or add methods to the base functionality.

space
Previous Page | Next Page | Top of Page