[ABAP] Erweiterte Baumansicht mit einem cl_gui_column_tree, Eventhandling

* Demoprogramm: SAPCOLUMN_TREE_CONTROL_DEMO

CONSTANTS: co_root TYPE tv_nodekey VALUE 'Root'.
CONSTANTS: co_auart TYPE tv_itmname VALUE 'Auftragsart'.
CONSTANTS: co_matnr TYPE tv_itmname VALUE 'Mat.-Nr.'.
CONSTANTS: co_arktx TYPE tv_itmname VALUE 'Kurztext'.
CONSTANTS: co_netwr TYPE tv_itmname VALUE 'Wert'.
CONSTANTS: co_flag TYPE tv_itmname VALUE 'Flag'.

DATA: it_nodes TYPE treev_ntab.
DATA: it_items TYPE STANDARD TABLE OF mtreeitm WITH DEFAULT KEY.

TYPES: ty_it_events TYPE STANDARD TABLE OF cntl_simple_event WITH DEFAULT KEY.

TYPES: BEGIN OF ty_vbak_vbap,
         auart TYPE vbak-auart,
         vbeln TYPE vbak-vbeln,
         posnr TYPE vbap-posnr,
         matnr TYPE vbap-matnr,
         arktx TYPE vbap-arktx,
         netwr TYPE vbap-netwr,
         flag  TYPE boolean,
       END OF ty_vbak_vbap.

TYPES: ty_it_vbak_vbap TYPE STANDARD TABLE OF ty_vbak_vbap WITH DEFAULT KEY.

* Eventhandlerklasse
CLASS lcl_event DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS: on_node_double_click FOR EVENT node_double_click OF cl_gui_column_tree
      IMPORTING
          node_key
          sender.
    CLASS-METHODS: on_item_double_click FOR EVENT item_double_click OF cl_gui_column_tree
      IMPORTING
          node_key
          item_name
          sender.
    CLASS-METHODS: on_expand_no_children FOR EVENT expand_no_children OF cl_gui_column_tree
      IMPORTING
          node_key
          sender.
    CLASS-METHODS: on_header_click FOR EVENT header_click OF cl_gui_column_tree
      IMPORTING
          header_name
          sender.
    CLASS-METHODS: on_button_click FOR EVENT button_click OF cl_gui_column_tree
      IMPORTING
          node_key
          item_name
          sender.
    CLASS-METHODS: on_link_click FOR EVENT link_click OF cl_gui_column_tree
      IMPORTING
          node_key
          item_name
          sender.
    CLASS-METHODS: on_checkbox_change FOR EVENT checkbox_change OF cl_gui_column_tree
      IMPORTING
          node_key
          item_name
          checked
          sender.
    CLASS-METHODS: on_dft_context_menu_req FOR EVENT default_context_menu_request OF cl_gui_column_tree
      IMPORTING
          menu
          sender.
    CLASS-METHODS: on_dft_context_menu_sel FOR EVENT default_context_menu_select OF cl_gui_column_tree
      IMPORTING
          fcode
          sender.
    CLASS-METHODS: on_node_context_menu_req FOR EVENT node_context_menu_request OF cl_gui_column_tree
      IMPORTING
          node_key
          menu
          sender.
    CLASS-METHODS: on_node_context_menu_sel FOR EVENT node_context_menu_select OF cl_gui_column_tree
      IMPORTING
          node_key
          fcode
          sender.
    CLASS-METHODS: on_item_context_menu_req FOR EVENT item_context_menu_request OF cl_gui_column_tree
      IMPORTING
          node_key
          item_name
          menu
          sender.
    CLASS-METHODS: on_item_context_menu_sel FOR EVENT item_context_menu_select OF cl_gui_column_tree
      IMPORTING
          node_key
          item_name
          fcode
          sender.
    CLASS-METHODS: on_header_context_menu_req FOR EVENT header_context_menu_request OF cl_gui_column_tree
      IMPORTING
          header_name
          menu
          sender.
    CLASS-METHODS: on_header_context_menu_sel FOR EVENT header_context_menu_select OF cl_gui_column_tree
      IMPORTING
          header_name
          fcode
          sender.
ENDCLASS.

CLASS lcl_event IMPLEMENTATION.
* Doppelklick auf Icon
  METHOD on_node_double_click.
    MESSAGE node_key TYPE 'S'.
  ENDMETHOD.
* Doppelklick auf Item
  METHOD on_item_double_click.

    CASE item_name.
* wenn Item für Wert NETWR editiert werden soll
      WHEN co_netwr.
* Text aus der Nodes-Tabelle lesen
        ASSIGN it_items[ node_key = node_key item_name = item_name ] TO FIELD-SYMBOL(<i>).

        DATA: lv_changed TYPE as4flag.
        DATA: lv_value TYPE string.

        lv_value = condense( <i>-text ).

* Popup für Werte-Eingabe
        CALL FUNCTION 'CC_POPUP_STRING_INPUT'
          EXPORTING
            property_name = 'NETWR:'
          IMPORTING
            value_changed = lv_changed
          CHANGING
            string_value  = lv_value.

        IF lv_changed = abap_true.
* Text in der Item Table anpassen
          <i>-text = lv_value.

* Text in der Node anpassen
          sender->item_set_text( node_key  = node_key
                                 item_name = item_name
                                 text      = |{ <i>-text }| ).
        ENDIF.
    ENDCASE.

    MESSAGE |{ item_name }_{ node_key }| TYPE 'S'.
  ENDMETHOD.
* bei Expandierung eines Baumelements ohne Unterelemente
* hier können evtl. Daten nachgeladen werden
  METHOD on_expand_no_children.
    MESSAGE node_key TYPE 'S'.
  ENDMETHOD.
* Header-Klick
  METHOD on_header_click.
    MESSAGE |{ header_name }| TYPE 'S'.
  ENDMETHOD.
* Button-Klick
  METHOD on_button_click.
    MESSAGE |{ item_name }_{ node_key }| TYPE 'S'.
  ENDMETHOD.
* Link-Klick
  METHOD on_link_click.
    MESSAGE |{ item_name }_{ node_key }| TYPE 'S'.
  ENDMETHOD.
* Checkbox Change
  METHOD on_checkbox_change.
    MESSAGE |{ item_name }_{ node_key }: { checked }| TYPE 'S'.
  ENDMETHOD.
* Freier Klickbereich: Kontextmenu füllen
  METHOD on_dft_context_menu_req.

    menu->add_function( text = 'Message'
                        fcode = 'MSG' ).

    menu->add_function( text = 'Exit'
                        fcode = 'EXIT' ).

  ENDMETHOD.
* Freier Klickbereich: Klick im Kontextmenu auswerten
  METHOD on_dft_context_menu_sel.

    CASE fcode.

      WHEN 'MSG'.
        MESSAGE 'Message' TYPE 'S'.
      WHEN 'EXIT'.
        LEAVE PROGRAM.

    ENDCASE.

  ENDMETHOD.
* Node: Kontextmenu füllen
  METHOD on_node_context_menu_req.

    menu->add_function( text = |{ node_key }|
                        fcode = 'NODE' ).

  ENDMETHOD.
* Node: Klick im Kontextmenu auswerten
  METHOD on_node_context_menu_sel.

    CASE fcode.

      WHEN 'NODE'.
        MESSAGE node_key TYPE 'S'.

    ENDCASE.

  ENDMETHOD.
* Item: Kontextmenu füllen
  METHOD on_item_context_menu_req.

    menu->add_function( text = |{ node_key }_{ item_name }|
                        fcode = 'NODE_ITEM' ).

  ENDMETHOD.
* Item: Klick im Kontextmenu auswerten
  METHOD on_item_context_menu_sel.

    CASE fcode.

      WHEN 'NODE_ITEM'.
        MESSAGE |{ node_key }_{ item_name }| TYPE 'S'.

    ENDCASE.

  ENDMETHOD.
* Header: Kontextmenu füllen
  METHOD on_header_context_menu_req.

    menu->add_function( text = |{ header_name }|
                        fcode = 'HEADER' ).

  ENDMETHOD.
* Header: Klick im Kontextmenu auswerten
  METHOD on_header_context_menu_sel.

    CASE fcode.

      WHEN 'HEADER'.
        MESSAGE |{ header_name }| TYPE 'S'.

    ENDCASE.

  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.

  DATA: it_vbak_vbap TYPE ty_it_vbak_vbap.

* Daten holen -> bei zu vielen Elementen (-> 450?) kommt es zum Absturz der Trees
  SELECT vbak~auart, vbak~vbeln, vbap~posnr, vbap~matnr, vbap~arktx, vbap~netwr FROM vbak
    INNER JOIN vbap ON vbak~vbeln = vbap~vbeln
    INTO CORRESPONDING FIELDS OF TABLE @it_vbak_vbap
    UP TO 100 ROWS.

  SORT: it_vbak_vbap BY auart vbeln posnr.

* 1. Spalte des Trees
  DATA(lv_thhdr) = VALUE treev_hhdr( heading = co_auart
                                     width   = 50 ).

* Tree-Objekt im default_screen einbetten
  DATA(o_tree) = NEW cl_gui_column_tree( parent                = cl_gui_container=>default_screen
                                         node_selection_mode   = cl_gui_column_tree=>node_sel_mode_single
                                         item_selection        = abap_true
                                         hierarchy_column_name = co_auart
                                         hierarchy_header      = lv_thhdr ).

* Eventtypten müssen gesondert registriert werden
  DATA(it_events) = VALUE ty_it_events( ( eventid = cl_gui_column_tree=>eventid_node_double_click
                                          appl_event = abap_true )
                                        ( eventid = cl_gui_column_tree=>eventid_item_double_click
                                          appl_event = abap_true )
                                        ( eventid = cl_gui_column_tree=>eventid_expand_no_children
                                          appl_event = abap_true )
                                        ( eventid = cl_gui_column_tree=>eventid_link_click
                                          appl_event = abap_true )
                                        ( eventid = cl_gui_column_tree=>eventid_button_click
                                          appl_event = abap_true )
                                        ( eventid = cl_gui_column_tree=>eventid_checkbox_change
                                          appl_event = abap_true )
                                        ( eventid = cl_gui_column_tree=>eventid_header_click
                                          appl_event = abap_true )
                                        ( eventid = cl_gui_column_tree=>eventid_def_context_menu_req
                                          appl_event = abap_true )
                                        ( eventid = cl_gui_column_tree=>eventid_node_context_menu_req
                                          appl_event = abap_true )
                                        ( eventid = cl_gui_column_tree=>eventid_item_context_menu_req
                                          appl_event = abap_true )
                                        ( eventid = cl_gui_column_tree=>eventid_header_context_men_req
                                          appl_event = abap_true ) ).

* Events registrieren
  o_tree->set_registered_events( events = it_events ).

* Eventhandler für Klicks und Kontextmenüs
  SET HANDLER lcl_event=>on_node_double_click FOR o_tree.
  SET HANDLER lcl_event=>on_item_double_click FOR o_tree.
  SET HANDLER lcl_event=>on_expand_no_children FOR o_tree.
  SET HANDLER lcl_event=>on_header_click FOR o_tree.
  SET HANDLER lcl_event=>on_button_click FOR o_tree.
  SET HANDLER lcl_event=>on_link_click FOR o_tree.
  SET HANDLER lcl_event=>on_checkbox_change FOR o_tree.
  SET HANDLER lcl_event=>on_dft_context_menu_req FOR o_tree.
  SET HANDLER lcl_event=>on_dft_context_menu_sel FOR o_tree.
  SET HANDLER lcl_event=>on_node_context_menu_req FOR o_tree.
  SET HANDLER lcl_event=>on_node_context_menu_sel FOR o_tree.
  SET HANDLER lcl_event=>on_item_context_menu_req FOR o_tree.
  SET HANDLER lcl_event=>on_item_context_menu_sel FOR o_tree.
  SET HANDLER lcl_event=>on_header_context_menu_req FOR o_tree.
  SET HANDLER lcl_event=>on_header_context_menu_sel FOR o_tree.

* weitere Spalten entsprechend der Reihenfolge
  o_tree->add_column( name        = co_matnr
                      width       = 30
                      header_text = CONV #( co_matnr ) ).

  o_tree->add_column( name        = co_arktx
                      width       = 70
                      header_text = CONV #( co_arktx ) ).

  o_tree->add_column( name        = co_netwr
                      width       = 30
                      header_text = CONV #( co_netwr ) ).

  o_tree->add_column( name        = co_flag
                      width       = 5
                      header_text = CONV #( co_flag ) ).

* Inhalt des Trees komplett löschen
*  o_tree->delete_all_nodes( ).

* Rootnode einfügen
  it_nodes = VALUE #( ( node_key  = co_root
                        isfolder  = abap_true ) ).

* Items der Rootnode
  it_items = VALUE #( ( node_key  = co_root
                        item_name = co_auart
                        class     = cl_gui_column_tree=>item_class_text
                        text      = co_root
                        font      = cl_gui_column_tree=>item_font_fixed
                        style     = cl_gui_column_tree=>style_intensified )

                      ( node_key  = co_root
                        item_name = co_matnr
                        class     = cl_gui_column_tree=>item_class_text
                        text      = '' )

                      ( node_key  = co_root
                        item_name = co_arktx
                        class     = cl_gui_column_tree=>item_class_text
                        text      = '' )

                      ( node_key  = co_root
                        item_name = co_netwr
                        class     = cl_gui_column_tree=>item_class_text
                        text      = '' )

                      ( node_key  = co_root
                        item_name = co_flag
                        class     = cl_gui_column_tree=>item_class_text
                        text      = '' ) ).

* Unternodes: Auftragsarten <a> -> Belege <v> -> Positionen <p>
  LOOP AT it_vbak_vbap ASSIGNING FIELD-SYMBOL(<a>) GROUP BY <a>-auart ASCENDING.
* Node für Auftragsarten
    APPEND VALUE #( node_key  = <a>-auart
                    relatkey  = co_root
                    relatship = cl_gui_column_tree=>relat_last_child
                    isfolder  = abap_true
                    expander  = abap_true ) TO it_nodes.

* Items für Auftragsarten
    APPEND VALUE #( node_key  = <a>-auart
                    item_name = co_auart
                    class     = cl_gui_column_tree=>item_class_text
                    text      = <a>-auart
                    font      = cl_gui_column_tree=>item_font_fixed ) TO it_items.

    APPEND VALUE #( node_key  = <a>-auart
                    item_name = co_matnr
                    class     = cl_gui_column_tree=>item_class_text
                    text      = '' ) TO it_items.

    APPEND VALUE #( node_key  = <a>-auart
                    item_name = co_arktx
                    class     = cl_gui_column_tree=>item_class_text
                    text      = '' ) TO it_items.

    APPEND VALUE #( node_key  = <a>-auart
                    item_name = co_netwr
                    class     = cl_gui_column_tree=>item_class_text
                    text      = '' ) TO it_items.

    APPEND VALUE #( node_key  = <a>-auart
                    item_name = co_flag
                    class     = cl_gui_column_tree=>item_class_text
                    text      = '' ) TO it_items.

    LOOP AT GROUP <a> ASSIGNING FIELD-SYMBOL(<v>) GROUP BY <v>-vbeln ASCENDING.
* Node für Belegnummern
      APPEND VALUE #( node_key  = <v>-vbeln
                      relatkey  = <a>-auart
                      relatship = cl_gui_column_tree=>relat_last_child
                      isfolder  = abap_true
                      expander  = abap_true ) TO it_nodes.

* Items für Belegnummern
      APPEND VALUE #( node_key  = <v>-vbeln
                      item_name = co_auart
                      class     = cl_gui_column_tree=>item_class_text
                      text      = <v>-vbeln
                      font      = cl_gui_column_tree=>item_font_fixed ) TO it_items.

      APPEND VALUE #( node_key  = <v>-vbeln
                      item_name = co_matnr
                      class     = cl_gui_column_tree=>item_class_text
                      text      = '' ) TO it_items.

      APPEND VALUE #( node_key  = <v>-vbeln
                      item_name = co_arktx
                      class     = cl_gui_column_tree=>item_class_text
                      text      = '' ) TO it_items.

      APPEND VALUE #( node_key  = <v>-vbeln
                      item_name = co_flag
                      class     = cl_gui_column_tree=>item_class_text
                      text      = '' ) TO it_items.

      DATA(lv_sum_netwr) = VALUE vbap-netwr( ).
      DATA(lv_cnt) = 0.

      LOOP AT GROUP <v> ASSIGNING FIELD-SYMBOL(<p>).
* Node für Positionen
* eindeutigen Key erzeugen (max. 12 Zeichen lang)
        DATA(lv_pos_key) = |{ <p>-vbeln ALPHA = OUT }{ <p>-posnr ALPHA = OUT }|.
        APPEND VALUE #( node_key  = lv_pos_key
                        relatkey  = <v>-vbeln
                        relatship = cl_gui_column_tree=>relat_last_child
                        isfolder  = abap_false
                        expander  = abap_false ) TO it_nodes.

* Items für Positionen
        APPEND VALUE #( node_key  = lv_pos_key
                        item_name = co_auart
                        class     = cl_gui_column_tree=>item_class_button
                        text      = <p>-posnr
                        font      = cl_gui_column_tree=>item_font_default ) TO it_items.

        APPEND VALUE #( node_key  = lv_pos_key
                        item_name = co_matnr
                        class     = cl_gui_column_tree=>item_class_link
                        text      = <p>-matnr
                        style     = cl_gui_column_tree=>style_intensified
                        font      = cl_gui_column_tree=>item_font_fixed ) TO it_items.

        APPEND VALUE #( node_key  = lv_pos_key
                        item_name = co_arktx
                        class     = cl_gui_column_tree=>item_class_text
                        text      = <p>-arktx
                        font      = cl_gui_column_tree=>item_font_fixed ) TO it_items.

        APPEND VALUE #( node_key  = lv_pos_key
                        item_name = co_netwr
                        class     = cl_gui_column_tree=>item_class_text
                        text      = CONV string( <p>-netwr )
                        font      = cl_gui_column_tree=>item_font_fixed ) TO it_items.

        APPEND VALUE #( node_key  = lv_pos_key
                        item_name = co_flag
                        class     = cl_gui_column_tree=>item_class_checkbox
                        editable  = abap_true
                        text      = <p>-flag ) TO it_items.

* Summierung des NETWR für übergeordnetes Item
        lv_sum_netwr = lv_sum_netwr + <p>-netwr.
        lv_cnt = lv_cnt + 1.
      ENDLOOP.

* Ausgabe der Summe in Item für Position
      APPEND VALUE #( node_key  = <v>-vbeln
                      item_name = co_netwr
                      class     = cl_gui_column_tree=>item_class_text
                      text      = |Summe: { lv_sum_netwr } ({ lv_cnt })|
                      font      = cl_gui_column_tree=>item_font_fixed ) TO it_items.

    ENDLOOP.
  ENDLOOP.

* Nodes und Items zum Baum hinzufügen und anzeigen
  o_tree->add_nodes_and_items( node_table                = it_nodes
                               item_table                = it_items
                               item_table_structure_name = 'MTREEITM' ). " Typ muss gleich mit Zeilentyp von it_items sein

* Root-Nodes des Trees expandieren
  o_tree->expand_root_nodes( ).

* Spaltenbreiten anpassen
*  o_tree->adjust_column_width( all_columns     = abap_true
*                               include_heading = abap_true ).

* leere Toolbar ausblenden
  cl_abap_list_layout=>suppress_toolbar( ).

* Erzwingung der Listenausgabe in cl_gui_container=>default_screen und Anzeige des Trees
  WRITE space.