Skip to Content
2
Jan 25, 2018 at 04:54 PM

Programming riddle: Sort rows via drag and drop

354 Views

Hi there!

today I doubt that I do programming for so many years...

The task: Provide an easy sorting mechanism in ALV grid.

The user should be able to mark any lines (one, a block of lines or arbitrary lines) and move them via drag and drop to another place.

I want to have a solution that can be used instead of the cl_gui_alv_grid. Therefore I inherited the class and implemented drag and drop feature.

The task seems to be quite easy: renumber the marked rows, find the new position in the data table and renumber the old entries.

May be I do think to complex or it really is not that trivial...

I would like to invite you to a challenge and try to find a working solution.

I myself had three approaches which either do not work for every selection or which use too much memory and performance.

The following code provides a demo report that

  • displays table T005T (countries) in alv grid
  • has a derived cl_gui_alv_grid class
  • has a dragdrop-data-object class
  • is prepared for dragging and dropping lines (or a single cell)

Just copy and paste the following code and try to find a simple solution that just sets the new order-id in the given field.

I am excited to see your solutions! :)

Enno

EXAMPLE

this is one of the most complex example: marking several lines that are not in one block and dragging them to another line:

Lines 4,6,7 and 9 are marked and are moved to line 2.

After dropping the lines the renumbering will be done and shown in field NATIO:

HINTS

It matters if you move the lines up or down.

Calculating the new order number should be preferred. I have a solution where I copy the selected lines to a temporary created identical table, deleting the marked lines, do the numbering and insertion at new position. This might be a problem in huge tables...

CODE

REPORT .

CLASS lcl_dragdrop_sort DEFINITION.
  PUBLIC SECTION.
    DATA at_rows TYPE lvc_t_row.
    DATA ao_data TYPE REF TO data.
ENDCLASS.

CLASS lcl_gui_alv_grid_sort DEFINITION
  INHERITING FROM cl_gui_alv_grid.
  PUBLIC SECTION.
    METHODS set_sort_field
      IMPORTING
        fieldname TYPE clike.
  PROTECTED SECTION.
    DATA mv_sort_field TYPE char30 .
    CONSTANTS mc_sort_flavor TYPE c LENGTH 10 VALUE 'SortMe'.
    METHODS on_drag            FOR EVENT ondrag       OF cl_gui_alv_grid
      IMPORTING e_column e_row e_dragdropobj es_row_no sender.
    METHODS on_drop            FOR EVENT ondrop       OF cl_gui_alv_grid
      IMPORTING e_column e_row e_dragdropobj es_row_no sender.
ENDCLASS.

CLASS lcl_gui_alv_grid_sort IMPLEMENTATION.
  METHOD on_drag.
    DATA lo_dragdrop_sort TYPE REF TO lcl_dragdrop_sort.
    lo_dragdrop_sort = NEW #( ).
    sender->get_selected_rows( IMPORTING et_index_rows = lo_dragdrop_sort->at_rows ).

    IF lo_dragdrop_sort->at_rows IS INITIAL.
     "No lines selected: get current cell
      sender->get_current_cell(
        IMPORTING
          e_row = DATA(lv_row) ).
      IF NOT lv_row IS INITIAL.
        APPEND VALUE #( index = lv_row ) TO lo_dragdrop_sort->at_rows.
      ENDIF.
    ENDIF.

    lo_dragdrop_sort->ao_data = mt_outtab.
    e_dragdropobj->object ?= lo_dragdrop_sort.

  ENDMETHOD.

  METHOD on_drop.
    DATA lo_dragdrop_sort    TYPE REF TO lcl_dragdrop_sort.
    FIELD-SYMBOLS <lft_data> TYPE table.

    CHECK e_dragdropobj->flavor = mc_sort_flavor.

    TRY.
        lo_dragdrop_sort ?= e_dragdropobj->object.
        CHECK lo_dragdrop_sort->at_rows  IS NOT INITIAL.
      CATCH cx_sy_move_cast_error.
        RETURN.
    ENDTRY.

    ASSIGN lo_dragdrop_sort->ao_data->* TO <lft_data>.

    LOOP AT <lft_data> ASSIGNING FIELD-SYMBOL(<lfs_data>).
      ASSIGN COMPONENT mv_sort_field OF STRUCTURE <lfs_data> 
          TO FIELD-SYMBOL(<lfv_sortn>).
      CHECK sy-subrc = 0.
      <lfv_sortn> = CONV numc10( sy-tabix ).
      "------->
      "PLACE YOUR SOLUTION HERE
      "AND HERE
      "...
      "....AND
      "------> HERE
      "---------------------------------------------> SOLUTION!! <------"
    ENDLOOP.
    
    "Set sort field
    me->get_sort_criteria( IMPORTING et_sort = DATA(lt_sort) ).
    IF NOT line_exists( lt_sort[ fieldname = mv_sort_field ] ).
      INSERT VALUE #( fieldname = mv_sort_field spos = 1 up = abap_true ) 
        INTO lt_sort INDEX 1.
    ENDIF.

   "Set sorting
    me->set_sort_criteria( lt_sort ).

    "Show changes
    refresh_table_display( ).

  ENDMETHOD.

  METHOD set_sort_field.

    mv_sort_field = fieldname.

    SET HANDLER on_drop    FOR me.
    SET HANDLER on_drag    FOR me.

    DATA(lo_dd) = NEW cl_dragdrop( ).
    lo_dd->add( dragsrc    = abap_true
                droptarget = abap_true
                flavor     = mc_sort_flavor
                effect     = cl_dragdrop=>copy
                effect_in_ctrl = cl_dragdrop=>move ).

    DATA(lv_dd_handle) = 0.
    lo_dd->get_handle( IMPORTING handle = lv_dd_handle ).

    "Set drag-drop-handle
    get_frontend_layout( IMPORTING es_layout = DATA(ls_layout) ).
    ls_layout-s_dragdrop-row_ddid = lv_dd_handle.
    set_frontend_layout( ls_layout ).
    refresh_table_display( ).

  ENDMETHOD.
ENDCLASS.

CLASS main DEFINITION.
  PUBLIC SECTION.
    TYPES ty_data       TYPE t005t.
    TYPES ty_data_t     TYPE STANDARD TABLE OF ty_data
                             WITH DEFAULT KEY.
    DATA ms_data        TYPE ty_data.
    DATA mt_data        TYPE ty_data_t.

    DATA mr_grid        TYPE REF TO lcl_gui_alv_grid_sort.
    METHODS start.
  PROTECTED SECTION.
    METHODS selection.
    METHODS display.

ENDCLASS.

CLASS main IMPLEMENTATION.
  METHOD selection.
    SELECT * FROM t005t INTO TABLE mt_data UP TO 10 ROWS 
     WHERE spras = sy-langu.
  ENDMETHOD.

  METHOD start.
    selection( ).
    display( ).
  ENDMETHOD.

  METHOD display.

   WRITE 'DUMMY'.
    
   CREATE OBJECT mr_grid
      EXPORTING
        i_parent      = cl_gui_container=>screen0
        i_appl_events = space.

    DATA lv_structure_name    TYPE dd02l-tabname VALUE 'T005T'.
    DATA ls_variant           TYPE disvariant.
    DATA lv_save              TYPE char01 VALUE 'U'.
    DATA lv_default           TYPE char01 VALUE abap_true.
    DATA ls_layout            TYPE lvc_s_layo.

    ls_layout-sel_mode       = 'A'.
    ls_layout-grid_title     = 'Countries'.

    mr_grid->set_table_for_first_display(
      EXPORTING
        i_structure_name              = lv_structure_name
        is_variant                    = ls_variant
        i_save                        = lv_save
        i_default                     = lv_default
        is_layout                     = ls_layout
      CHANGING
        it_outtab                     = mt_data ).

    mr_grid->set_sort_field( 'NATIO' ).

  ENDMETHOD.
ENDCLASS.


START-OF-SELECTION.
  NEW main( )->start( ).

Attachments

ddsort1.png (29.8 kB)
ddsort2.png (19.4 kB)
ddsort3.png (14.1 kB)