abapGit/src/ui/zcl_abapgit_hotkeys.clas.abap
Alexander Tsybulsky 310d8673dd
Gui refactoring, chapter 3 (#3355)
* zcl_abapgit_html_parts primitive

* integrate parts in GUI

* bughunting and renames

* scripts collection, move hotkeys js to parts

* linter fixes

Co-authored-by: Lars Hvam <larshp@hotmail.com>
2020-05-13 12:58:43 +02:00

414 lines
12 KiB
ABAP

CLASS zcl_abapgit_hotkeys DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES:
zif_abapgit_gui_hotkey_ctl,
zif_abapgit_gui_hotkeys,
zif_abapgit_gui_renderable.
CONSTANTS:
c_showhotkeys_action TYPE string VALUE `showHotkeys` ##NO_TEXT.
CLASS-METHODS:
get_all_default_hotkeys
RETURNING
VALUE(rt_hotkey_actions) TYPE zif_abapgit_gui_hotkeys=>tty_hotkey_with_descr
RAISING
zcx_abapgit_exception,
merge_hotkeys_with_settings
CHANGING
ct_hotkey_actions TYPE zif_abapgit_gui_hotkeys=>tty_hotkey_with_descr
RAISING
zcx_abapgit_exception.
CLASS-METHODS:
should_show_hint
RETURNING
VALUE(rv_yes) TYPE abap_bool.
PROTECTED SECTION.
PRIVATE SECTION.
DATA mt_hotkey_providers TYPE TABLE OF REF TO zif_abapgit_gui_hotkeys.
CLASS-DATA:
gv_hint_was_shown TYPE abap_bool,
gt_interface_implementations TYPE saboo_iimpt.
CLASS-METHODS:
filter_relevant_classes
IMPORTING
it_classes TYPE seo_relkeys
RETURNING
VALUE(rt_classes) TYPE seo_relkeys,
get_class_package
IMPORTING
iv_class_name TYPE seoclsname
RETURNING
VALUE(rv_package) TYPE devclass,
get_referred_class_name
IMPORTING
io_ref TYPE any
RETURNING
VALUE(rv_name) TYPE seoclsname,
get_hotkeys_by_class_name
IMPORTING
iv_class_name TYPE seoclsname
RETURNING
VALUE(rt_hotkeys) TYPE zif_abapgit_gui_hotkeys=>tty_hotkey_with_descr,
get_hotkeys_from_global_intf
RETURNING
VALUE(rt_hotkeys) TYPE zif_abapgit_gui_hotkeys=>tty_hotkey_with_descr
RAISING
zcx_abapgit_exception,
get_hotkeys_from_local_intf
RETURNING
VALUE(rt_hotkeys) TYPE zif_abapgit_gui_hotkeys=>tty_hotkey_with_descr
RAISING
zcx_abapgit_exception,
get_local_intf_implementations
RETURNING
VALUE(rt_interface_implementations) TYPE saboo_iimpt
RAISING
zcx_abapgit_exception,
render_js_part
IMPORTING
it_hotkeys TYPE zif_abapgit_gui_hotkeys=>tty_hotkey_with_descr
RETURNING
VALUE(ro_html) TYPE REF TO zcl_abapgit_html.
ENDCLASS.
CLASS ZCL_ABAPGIT_HOTKEYS IMPLEMENTATION.
METHOD filter_relevant_classes.
DATA lv_this_class_name TYPE seoclsname.
DATA lv_this_class_pkg TYPE devclass.
DATA lv_class_pkg TYPE devclass.
DATA lo_dummy TYPE REF TO zcl_abapgit_hotkeys.
FIELD-SYMBOLS <ls_class> LIKE LINE OF it_classes.
lv_this_class_name = get_referred_class_name( lo_dummy ).
lv_this_class_pkg = get_class_package( lv_this_class_name ).
LOOP AT it_classes ASSIGNING <ls_class>.
lv_class_pkg = get_class_package( <ls_class>-clsname ).
IF lv_class_pkg = lv_this_class_pkg.
APPEND <ls_class> TO rt_classes.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD get_all_default_hotkeys.
IF zcl_abapgit_factory=>get_environment( )->is_merged( ) = abap_true.
rt_hotkey_actions = get_hotkeys_from_local_intf( ).
ELSE.
rt_hotkey_actions = get_hotkeys_from_global_intf( ).
ENDIF.
ENDMETHOD.
METHOD get_class_package.
SELECT SINGLE devclass FROM tadir
INTO rv_package
WHERE pgmid = 'R3TR'
AND object = 'CLAS'
AND obj_name = iv_class_name.
ENDMETHOD.
METHOD get_hotkeys_by_class_name.
CALL METHOD (iv_class_name)=>zif_abapgit_gui_hotkeys~get_hotkey_actions
RECEIVING
rt_hotkey_actions = rt_hotkeys.
ENDMETHOD.
METHOD get_hotkeys_from_global_intf.
DATA: lt_hotkey_actions LIKE rt_hotkeys,
lo_interface TYPE REF TO cl_oo_interface,
li_dummy TYPE REF TO zif_abapgit_gui_hotkeys,
lt_classes TYPE seo_relkeys.
FIELD-SYMBOLS: <ls_class> LIKE LINE OF lt_classes.
TRY.
lo_interface ?= cl_oo_class=>get_instance( get_referred_class_name( li_dummy ) ).
CATCH cx_class_not_existent.
RETURN.
ENDTRY.
lt_classes = lo_interface->get_implementing_classes( ).
lt_classes = filter_relevant_classes( lt_classes ). " For security reasons
LOOP AT lt_classes ASSIGNING <ls_class>.
lt_hotkey_actions = get_hotkeys_by_class_name( <ls_class>-clsname ).
INSERT LINES OF lt_hotkey_actions INTO TABLE rt_hotkeys.
ENDLOOP.
ENDMETHOD.
METHOD get_hotkeys_from_local_intf.
DATA lt_hotkey_actions LIKE rt_hotkeys.
DATA lt_interface_implementations TYPE saboo_iimpt.
FIELD-SYMBOLS <ls_intf_implementation> LIKE LINE OF lt_interface_implementations.
DATA lv_hotkeys_class_name LIKE <ls_intf_implementation>-refclsname.
DATA li_dummy TYPE REF TO zif_abapgit_gui_hotkeys.
lv_hotkeys_class_name = get_referred_class_name( li_dummy ).
lt_interface_implementations = get_local_intf_implementations( ).
LOOP AT lt_interface_implementations ASSIGNING <ls_intf_implementation> WHERE refclsname = lv_hotkeys_class_name.
lt_hotkey_actions = get_hotkeys_by_class_name( <ls_intf_implementation>-clsname ).
INSERT LINES OF lt_hotkey_actions INTO TABLE rt_hotkeys.
ENDLOOP.
ENDMETHOD.
METHOD get_local_intf_implementations.
DATA: ls_type_infos TYPE saboo_vseot,
lt_method_implementations TYPE saboo_method_impl_tab,
lt_source TYPE saboo_sourt.
IF gt_interface_implementations IS INITIAL.
READ REPORT sy-cprog INTO lt_source.
IF sy-subrc <> 0.
zcx_abapgit_exception=>raise( |Cannot read { sy-cprog }| ).
ENDIF.
CALL FUNCTION 'SCAN_ABAP_OBJECTS_CLASSES'
CHANGING
vseo_tabs = ls_type_infos
method_impls = lt_method_implementations
sourc_tab = lt_source
EXCEPTIONS
scan_abap_source_error = 1
scan_abap_src_line_too_long = 2
OTHERS = 3.
IF sy-subrc <> 0.
zcx_abapgit_exception=>raise_t100( ).
ENDIF.
gt_interface_implementations = ls_type_infos-iimpl_tab.
ENDIF.
rt_interface_implementations = gt_interface_implementations.
ENDMETHOD.
METHOD get_referred_class_name.
DATA lo_ref TYPE REF TO cl_abap_refdescr.
lo_ref ?= cl_abap_typedescr=>describe_by_data( io_ref ).
rv_name = lo_ref->get_referenced_type( )->get_relative_name( ).
ENDMETHOD.
METHOD merge_hotkeys_with_settings.
DATA lt_user_defined_hotkeys TYPE zif_abapgit_definitions=>tty_hotkey.
FIELD-SYMBOLS <ls_hotkey> LIKE LINE OF ct_hotkey_actions.
FIELD-SYMBOLS <ls_user_defined_hotkey> LIKE LINE OF lt_user_defined_hotkeys.
lt_user_defined_hotkeys = zcl_abapgit_persist_settings=>get_instance( )->read( )->get_hotkeys( ).
LOOP AT ct_hotkey_actions ASSIGNING <ls_hotkey>.
READ TABLE lt_user_defined_hotkeys ASSIGNING <ls_user_defined_hotkey>
WITH TABLE KEY action COMPONENTS
ui_component = <ls_hotkey>-ui_component
action = <ls_hotkey>-action.
IF sy-subrc = 0.
<ls_hotkey>-hotkey = <ls_user_defined_hotkey>-hotkey.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD render_js_part.
DATA lv_json TYPE string.
DATA lt_hotkeys TYPE zif_abapgit_gui_hotkeys=>tty_hotkey_with_descr.
FIELD-SYMBOLS: <ls_hotkey> LIKE LINE OF lt_hotkeys.
lv_json = `{`.
LOOP AT it_hotkeys ASSIGNING <ls_hotkey>.
IF sy-tabix > 1.
lv_json = lv_json && |,|.
ENDIF.
lv_json = lv_json && | "{ <ls_hotkey>-hotkey }" : "{ <ls_hotkey>-action }" |.
ENDLOOP.
lv_json = lv_json && `}`.
CREATE OBJECT ro_html.
ro_html->add( |setKeyBindings({ lv_json });| ).
ENDMETHOD.
METHOD should_show_hint.
IF gv_hint_was_shown = abap_false.
rv_yes = abap_true.
gv_hint_was_shown = abap_true.
ENDIF.
ENDMETHOD.
METHOD zif_abapgit_gui_hotkeys~get_hotkey_actions.
DATA ls_hotkey LIKE LINE OF rt_hotkey_actions.
ls_hotkey-ui_component = 'Hotkeys'.
ls_hotkey-action = c_showhotkeys_action.
ls_hotkey-description = 'Show hotkeys help'.
ls_hotkey-hotkey = '?'.
INSERT ls_hotkey INTO TABLE rt_hotkey_actions.
ENDMETHOD.
METHOD zif_abapgit_gui_hotkey_ctl~get_registered_hotkeys.
DATA li_hotkey_provider LIKE LINE OF mt_hotkey_providers.
DATA lt_hotkeys LIKE rt_registered_hotkeys.
FIELD-SYMBOLS <ls_hotkey> LIKE LINE OF lt_hotkeys.
LOOP AT mt_hotkey_providers INTO li_hotkey_provider.
APPEND LINES OF li_hotkey_provider->get_hotkey_actions( ) TO lt_hotkeys.
ENDLOOP.
merge_hotkeys_with_settings( CHANGING ct_hotkey_actions = lt_hotkeys ).
" Compress duplicates
LOOP AT lt_hotkeys ASSIGNING <ls_hotkey>.
READ TABLE rt_registered_hotkeys WITH KEY hotkey = <ls_hotkey>-hotkey TRANSPORTING NO FIELDS.
IF sy-subrc = 0. " If found command with same hotkey
DELETE rt_registered_hotkeys INDEX sy-tabix. " Later registered commands enjoys the priority
ENDIF.
APPEND <ls_hotkey> TO rt_registered_hotkeys.
ENDLOOP.
ENDMETHOD.
METHOD zif_abapgit_gui_hotkey_ctl~register_hotkeys.
IF ii_hotkeys IS BOUND.
APPEND ii_hotkeys TO mt_hotkey_providers.
ENDIF.
ENDMETHOD.
METHOD zif_abapgit_gui_hotkey_ctl~reset.
CLEAR mt_hotkey_providers.
ENDMETHOD.
METHOD zif_abapgit_gui_renderable~render.
DATA:
lv_hint TYPE string,
lt_registered_hotkeys TYPE zif_abapgit_gui_hotkeys=>tty_hotkey_with_descr,
lv_hotkey TYPE string.
FIELD-SYMBOLS <ls_hotkey> LIKE LINE OF lt_registered_hotkeys.
zif_abapgit_gui_hotkey_ctl~register_hotkeys( me ).
CREATE OBJECT ri_html TYPE zcl_abapgit_html.
lt_registered_hotkeys = zif_abapgit_gui_hotkey_ctl~get_registered_hotkeys( ).
SORT lt_registered_hotkeys BY ui_component description.
" Note
" normally render method should be able to call mi_gui_services->get_html_parts( )
" thus the class must inherit from zcl_abapgit_gui_component
" but problem is that component constructor calls get_gui which creates gui if it is missing
" and the hotkeys class itself is created during get_gui so it is infinite loop
" solutions:
" A) separate hotkeys into logic and render (which is actually a good way, but it so nicely fit together ...)
" B) convert mi_gui_services to a getter - which I will do but later
zcl_abapgit_ui_factory=>get_gui_services( )->get_html_parts( )->add_part(
iv_collection = zcl_abapgit_gui_component=>c_html_parts-scripts
ii_part = render_js_part( lt_registered_hotkeys ) ).
" Render hotkeys
ri_html->add( '<ul class="hotkeys">' ).
LOOP AT lt_registered_hotkeys ASSIGNING <ls_hotkey>.
ri_html->add( |<li>|
&& |<span class="key-id">{ <ls_hotkey>-hotkey }</span>|
&& |<span class="key-descr">{ <ls_hotkey>-description }</span>|
&& |</li>| ).
ENDLOOP.
ri_html->add( '</ul>' ).
CLEAR lv_hotkey.
READ TABLE lt_registered_hotkeys ASSIGNING <ls_hotkey>
WITH KEY action = c_showhotkeys_action.
IF sy-subrc = 0.
lv_hotkey = <ls_hotkey>-hotkey.
ENDIF.
lv_hint = |Close window with upper right corner 'X'|.
IF lv_hotkey IS NOT INITIAL.
lv_hint = lv_hint && | or '{ <ls_hotkey>-hotkey }'|.
ENDIF.
ri_html = zcl_abapgit_gui_chunk_lib=>render_infopanel(
iv_div_id = 'hotkeys'
iv_title = 'Hotkeys'
iv_hint = lv_hint
iv_hide = abap_true
iv_scrollable = abap_false
io_content = ri_html ).
IF lv_hotkey IS NOT INITIAL AND should_show_hint( ) = abap_true.
ri_html->add( |<div id="hotkeys-hint" class="corner-hint">|
&& |Press '{ <ls_hotkey>-hotkey }' to get keyboard shortcuts list|
&& |</div>| ).
ENDIF.
ENDMETHOD.
ENDCLASS.