Utility class to generate the Legend ALV using SALV Popup and attach it onto ALV Grid generated by SALV Model
Preface
When I design the ALV to display the report, many times I use the Icons on the output. Icons enhances the visual display of the grid and would provide nice look to the reports. Read SALV Table 18 – ICONs and Tooltips to understand more about adding the icons on SALV.
On other day, I thought of adding the legend button on the ALV so user can better understand the purpose of the icon, to make it more user friendly. I added the button on the grid, I handled the event and I displayed the ALV in SALV Popup. I designed another ALV report and had to generate the legend again. So, I thought of creating an utility class which can be bolt on to the ALV with minimal coding by inheritance.
Utility class ZCL_ICON_LEGEND
The class is based on the idea:
- Get the parent ALV data table. Here the Parent ALV is the ALV on which you want to add the legend ALV
- Fill up the Icon and its descriptions
- Add button on the Parent ALV to display the legend ALV
- Event handler for the button on Parent ALV to display the legend ALV
Class Attributes:
These are the attributes in the utility class ZCL_ICON_LEGEND
- T_LEGEND – Table with Icon and its description
- V_BTN_OKCODE – OK code for Button on Parent ALV
- V_BTN_ICON – Button’s ICON
- V_BTN_TOOLTIP – Button’s tooltip
- V_BTN_TEXT – Button’s Text
- V_FIELD – Protected with Field name for the Icon column from parent ALV
- O_DATA – Protected data reference to the parent ALV data
Class methods:
These are the methods in the utility class ZCL_ICON_LEGEND
- GENERATE_LEGEND_OUTPUT – This method adds the button on parent ALV using the attributes and have event handler
- SET_DESCRIPTION – protected method to allow to change the description on the legend output table
Features
Few highlights of the features:
- If parent ALV is not SALV Grid, the object would raise an exception {Read Class-based Exceptions I – Basics}
- You can add more than buttons with more than one instance of the objects
- Default button property is set, no need to change anything if only one legend
- You can change the button properties for each button
- Default description for blank icon is set
UML
High level UML on how ZCL_ICON_LEGEND can be used:
Class Code Lines
Here we go
*----------------------------------------------------------------------* * Author: Naimesh Patel * Desc : Utility to generate Legend ALV on SALV Grid ALV * Link : http://zevolving.com/?p=2786 *----------------------------------------------------------------------* CLASS zcl_icon_legend DEFINITION. PUBLIC SECTION. TYPES: BEGIN OF ty_legend, icon_val TYPE char30, desc TYPE char50, END OF ty_legend. TYPES: tt_legend TYPE STANDARD TABLE OF ty_legend. METHODS: constructor IMPORTING it_data TYPE STANDARD TABLE iv_field TYPE char30 io_parent_salv TYPE REF TO cl_salv_table EXCEPTIONS type cx_salv_method_not_supported, generate_legend_output. DATA: t_legend TYPE tt_legend. DATA: v_btn_okcode TYPE salv_de_function. DATA: v_btn_icon TYPE string. DATA: v_btn_tooltip TYPE string. DATA: v_btn_text TYPE string. PROTECTED SECTION. METHODS: set_description. DATA: v_field TYPE char30. DATA: o_data TYPE REF TO data. PRIVATE SECTION. DATA: o_parent_salv TYPE REF TO cl_salv_table. DATA: o_legend_salv TYPE REF TO cl_salv_table. METHODS: on_user_command FOR EVENT added_function OF cl_salv_events_table IMPORTING e_salv_function. ENDCLASS. "zcl_icon_legend DEFINITION * CLASS zcl_icon_legend IMPLEMENTATION. METHOD constructor. * Only on Grid o_parent_salv = io_parent_salv. IF o_parent_salv->get_display_object( ) NE if_salv_c_table_objects=>grid. RAISE cx_salv_method_not_supported. ENDIF. me->v_field = iv_field. GET REFERENCE OF it_data INTO o_data. ** Fill up the Legend table * me->set_description( it_data ). * Default values for the button * Change in the legend object, if there are more than one legend buttons me->v_btn_okcode = 'LEGEND'. me->v_btn_icon = icon_question. me->v_btn_tooltip = 'Legend'. ENDMETHOD. "constructor METHOD set_description. FIELD-SYMBOLS: <ft_data> TYPE ANY TABLE, <fs_data> TYPE any, <fs_fld> TYPE any. DATA: ls_legend LIKE LINE OF t_legend. ASSIGN o_data->* TO <ft_data>. LOOP AT <ft_data> ASSIGNING <fs_data>. ASSIGN COMPONENT me->v_field OF STRUCTURE <fs_data> TO <fs_fld>. IF <fs_fld> IS NOT ASSIGNED. "raise" EXIT. ENDIF. ls_legend-icon_val = <fs_fld>. COLLECT ls_legend INTO t_legend. CLEAR ls_legend. ENDLOOP. * For empty READ TABLE t_legend INTO ls_legend WITH KEY icon_val = space. IF sy-subrc EQ 0. ls_legend-desc = '<no desc>'. MODIFY t_legend FROM ls_legend INDEX sy-tabix. ENDIF. ENDMETHOD. "set_description METHOD generate_legend_output. * Get the descriptions me->set_description( ). * Add button for Legend on Parent DATA: lo_functions TYPE REF TO cl_salv_functions_list. DATA: lo_events TYPE REF TO cl_salv_events_table. lo_functions = o_parent_salv->get_functions( ). TRY. lo_functions->add_function( name = me->v_btn_okcode icon = me->v_btn_icon tooltip = me->v_btn_tooltip text = me->v_btn_text position = if_salv_c_function_position=>right_of_salv_functions ). CATCH cx_salv_existing . CATCH cx_salv_wrong_call . ENDTRY. * Add event handler for parent lo_events = o_parent_salv->get_event( ). SET HANDLER me->on_user_command FOR lo_events. ENDMETHOD. "generate_legend_output * METHOD on_user_command. * Handle the Legend Button event. CHECK e_salv_function = me->v_btn_okcode. DATA: lr_functions TYPE REF TO cl_salv_functions_list. DATA: lo_columns TYPE REF TO cl_salv_columns. DATA: lo_column TYPE REF TO cl_salv_column_table. DATA: ls_color TYPE lvc_s_colo. " Colors strucutre INCLUDE <color>. * Create new Legend ALV IF o_legend_salv IS NOT BOUND. TRY. cl_salv_table=>factory( IMPORTING r_salv_table = o_legend_salv CHANGING t_table = t_legend ). CATCH cx_salv_msg. "#EC NO_HANDLER ENDTRY. * default functions lr_functions = o_legend_salv->get_functions( ). lr_functions->set_default( 'X' ). lr_functions->set_group_sort( ' ' ). * ICON column with color background lo_columns = o_legend_salv->get_columns( ). ls_color-col = col_total. TRY. lo_column ?= lo_columns->get_column( 'ICON_VAL' ). lo_column->set_icon( if_salv_c_bool_sap=>true ). lo_column->set_long_text( 'ICON' ). lo_column->set_alignment( if_salv_c_alignment=>centered ). lo_column->set_output_length( 10 ). lo_column->set_color( ls_color ). CATCH cx_salv_not_found. "#EC NO_HANDLER ENDTRY. TRY. lo_column ?= lo_columns->get_column( 'DESC' ). lo_column->set_long_text( 'Description' ). CATCH cx_salv_not_found. "#EC NO_HANDLER ENDTRY. * .. as popup o_legend_salv->set_screen_popup( start_column = 80 end_column = 140 start_line = 3 end_line = 10 ). ENDIF. * And Display o_legend_salv->display( ). ENDMETHOD. "on_user_command ENDCLASS. "zcl_icon_legend IMPLEMENTATION
Utility Usage
Inherit the utility class in your local class. This would allow you to modify the description of the icons. Once the class is inherited, redefine the method SET_DESCRIPTION( ).
While generating the ALV, instantiate the legend object using the inherited local class.
Demo Code lines
* CLASS lcl_icon_legend_status DEFINITION INHERITING FROM zcl_icon_legend. PROTECTED SECTION. METHODS: set_description REDEFINITION. ENDCLASS. "lcl_icon_legend_status DEFINITION INCLUDE <icon>. * CLASS lcl_report DEFINITION. * PUBLIC SECTION. TYPES: BEGIN OF ty_t100. INCLUDE TYPE t100. TYPES: s_icon TYPE char08, s_new_stat TYPE char08, END OF ty_t100. DATA: t_t100 TYPE STANDARD TABLE OF ty_t100. DATA: o_salv TYPE REF TO cl_salv_table. DATA: o_container TYPE REF TO cl_gui_custom_container. METHODS: get_data, generate_output. ENDCLASS. "lcl_report DEFINITION START-OF-SELECTION. DATA: lo_report TYPE REF TO lcl_report. CREATE OBJECT lo_report. lo_report->get_data( ). CALL SCREEN 9000. *----------------------------------------------------------------------* * CLASS lcl_report IMPLEMENTATION *----------------------------------------------------------------------* CLASS lcl_report IMPLEMENTATION. METHOD get_data. * data selection SELECT * FROM t100 INTO TABLE t_t100 UP TO 10 ROWS WHERE sprsl = sy-langu. FIELD-SYMBOLS: <ls_t100> LIKE LINE OF t_t100. DO 3 TIMES. READ TABLE t_t100 ASSIGNING <ls_t100> INDEX sy-index. <ls_t100>-s_icon = icon_red_light. ENDDO. DO 2 TIMES. READ TABLE t_t100 ASSIGNING <ls_t100> INDEX ( sy-tabix + 3 ). <ls_t100>-s_icon = icon_yellow_light. ENDDO. ENDMETHOD. "get_data * * METHOD generate_output. CREATE OBJECT o_container EXPORTING container_name = 'CONTAINER'. DATA: lo_msg TYPE REF TO cx_salv_msg. TRY. cl_salv_table=>factory( EXPORTING r_container = o_container container_name = 'CONTAINER' IMPORTING r_salv_table = o_salv CHANGING t_table = t_t100 ). CATCH cx_salv_msg INTO lo_msg. ENDTRY. DATA: lo_functions TYPE REF TO cl_salv_functions_list. lo_functions = o_salv->get_functions( ). lo_functions->set_all( abap_true ). * First Legend button TRY . DATA: o_legend TYPE REF TO lcl_icon_legend_status. CREATE OBJECT o_legend EXPORTING it_data = t_t100 iv_field = 'S_ICON' io_parent_salv = o_salv. "SET HANDLER on_modify_desc FOR o_legend. o_legend->generate_legend_output( ). CATCH cx_salv_method_not_supported . MESSAGE 'Legend Toolbar not supported' TYPE 'S'. ENDTRY. * second Legend button * Notice the changes to the button properties TRY. DATA: o_legend_2 TYPE REF TO zcl_icon_legend. CREATE OBJECT o_legend_2 EXPORTING it_data = t_t100 iv_field = 'S_NEW_STAT' io_parent_salv = o_salv. o_legend_2->v_btn_okcode = 'LEGEND_NEW'. o_legend_2->v_btn_tooltip = 'New Legend'. o_legend_2->v_btn_text = 'New Legend'. o_legend_2->v_btn_icon = icon_okay. "SET HANDLER on_modify_desc FOR o_legend_2. o_legend_2->generate_legend_output( ). CATCH cx_salv_method_not_supported . MESSAGE 'Legend Toolbar not supported' TYPE 'S'. ENDTRY. * default descriptions DATA: lo_cols TYPE REF TO cl_salv_columns, lo_column TYPE REF TO cl_salv_column_list, lt_cols TYPE salv_t_column_ref, ls_cols LIKE LINE OF lt_cols. DATA: lv_short TYPE scrtext_s. DATA: lv_med TYPE scrtext_m. DATA: lv_long TYPE scrtext_l. lo_cols = o_salv->get_columns( ). lo_cols->set_optimize( 'X' ). lt_cols = lo_cols->get( ). LOOP AT lt_cols INTO ls_cols. lo_column ?= ls_cols-r_column. "Narrow casting IF lo_column->get_short_text( ) IS INITIAL. lv_short = ls_cols-columnname. lo_column->set_short_text( lv_short ). ENDIF. IF lo_column->get_medium_text( ) IS INITIAL. lv_med = ls_cols-columnname. lo_column->set_medium_text( lv_med ). ENDIF. IF lo_column->get_long_text( ) IS INITIAL. lv_long = ls_cols-columnname. lo_column->set_long_text( lv_long ). ENDIF. ENDLOOP. * Displaying the ALV o_salv->display( ). ENDMETHOD. "generate_output ENDCLASS. "lcl_report IMPLEMENTATION * CLASS lcl_icon_legend_status IMPLEMENTATION. METHOD set_description. * call default logic super->set_description( ). * FIELD-SYMBOLS: <ls_legend> LIKE LINE OF me->t_legend. READ TABLE me->t_legend ASSIGNING <ls_legend> WITH KEY icon_val = icon_red_light. IF sy-subrc EQ 0. <ls_legend>-desc = 'Critical Error'. ENDIF. READ TABLE me->t_legend ASSIGNING <ls_legend> WITH KEY icon_val = icon_yellow_light. IF sy-subrc EQ 0. <ls_legend>-desc = 'Warning, with caution'. ENDIF. ENDMETHOD. "set_description ENDCLASS. "lcl_icon_legend_status IMPLEMENTATION *----------------------------------------------------------------------* * MODULE STATUS_9000 OUTPUT *----------------------------------------------------------------------* MODULE status_9000 OUTPUT. IF lo_report->o_salv IS NOT BOUND. lo_report->generate_output( ). ELSE. lo_report->o_salv->refresh( ). ENDIF. ENDMODULE. "STATUS_9000 OUTPUT
Demo Output
In Action:
One more button, in action
Hi,
Nice idea!
I would provide a static method for adding the legends buttons in a standard way… 🙂
Very cool! Haven’t ever thought about doing that.
I will see if I can use this next ALV I write!
Can we do something like this in webdynpro?
Hi,
Nice approach.
Two suggestions:
1. I would prefer one context/column related legend button over separate buttons. i.e. displaying relevant legend according selected column.
2. Wouldn’t it be more elegant to use icons with tooltips?
In such case, you may build the legend automaically/dynamically according to ALV data table contents (instead of implementing set_description).
Hello Steve,
I haven’t tried to do so, but I think, it would be possible. I’ll give a try and see..
Regards,
Naimesh Patel
Hello Shai,
Thanks for the suggestions. I guess, we can achieve both of them. To achieve the first option, we would need to implement the object pool pattern. The responsibility than need to be transferred to the class who controls and keep tracks of the buttons.
I would try to research on them and see hot it goes. Thanks for the ideas.
Regards,
Naimesh Patel