/****************************************************/
/*  BLOCK.CPP                                       */
/****************************************************/

#include "fctsys.h"
#include "appl_wxstruct.h"
#include "gr_basic.h"
#include "common.h"
#include "class_drawpanel.h"
#include "confirm.h"
#include "block_commande.h"

#include "program.h"
#include "general.h"
#include "class_marker_sch.h"
#include "class_library.h"
#include "class_pin.h"
#include "protos.h"


// Imported functions:
void            MoveItemsInList( PICKED_ITEMS_LIST& aItemsList,
                                 const wxPoint      aMoveVector );
void            MirrorListOfItems( PICKED_ITEMS_LIST& aItemsList,
                                   wxPoint&           Center );
void            DeleteItemsInList( WinEDA_DrawPanel*  panel,
                                   PICKED_ITEMS_LIST& aItemsList );
void            DuplicateItemsInList( SCH_SCREEN*        screen,
                                      PICKED_ITEMS_LIST& aItemsList,
                                      const wxPoint      aMoveVector  );

static void     CollectStructsToDrag( SCH_SCREEN* screen );
static void     AddPickedItem( SCH_SCREEN* screen, wxPoint aPosition );
static LIB_PIN* GetNextPinPosition( SCH_COMPONENT* aDrawLibItem,
                                    wxPoint&       aPosition,
                                    bool           aSearchFirst );
static void     DrawMovingBlockOutlines( WinEDA_DrawPanel* panel,
                                         wxDC*             DC,
                                         bool              erase );
static void     SaveStructListForPaste( PICKED_ITEMS_LIST& aItemsList );


/* Return the block command (BLOCK_MOVE, BLOCK_COPY...) corresponding to
 *  the key (ALT, SHIFT ALT ..)
 */
int WinEDA_SchematicFrame::ReturnBlockCommand( int key )
{
    int cmd;

    switch( key )
    {
    default:
        cmd = key & 0xFF;
        break;

    case 0:
        cmd = BLOCK_MOVE;
        break;

    case GR_KB_ALT:
    case GR_KB_SHIFT:
        cmd = BLOCK_COPY;
        break;

    case GR_KB_CTRL:
        cmd = BLOCK_DRAG;
        break;

    case GR_KB_SHIFTCTRL:
        cmd = BLOCK_DELETE;
        break;

    case MOUSE_MIDDLE:
        cmd = BLOCK_ZOOM;
        break;
    }

    return cmd;
}


/* Init the parameters used by the block paste command
 */
void WinEDA_SchematicFrame::InitBlockPasteInfos()
{
    BLOCK_SELECTOR* block = &GetScreen()->m_BlockLocate;

    block->m_ItemsSelection.CopyList( g_BlockSaveDataList.m_ItemsSelection );
    DrawPanel->ManageCurseur = DrawMovingBlockOutlines;
}


/* Routine to handle the BLOCK PLACE command
 *  Last routine for block operation for:
 *  - block move & drag
 *  - block copy & paste
 */
void WinEDA_SchematicFrame::HandleBlockPlace( wxDC* DC )
{
    bool            err   = FALSE;
    BLOCK_SELECTOR* block = &GetScreen()->m_BlockLocate;

    if( DrawPanel->ManageCurseur == NULL )
    {
        err = TRUE;
        DisplayError( this, wxT( "HandleBlockPLace() : ManageCurseur = NULL" ) );
    }

    if( block->GetCount() == 0 )
    {
        wxString msg;
        err = TRUE;
        msg.Printf( wxT( "HandleBlockPLace() error : no items to place (cmd \
%d, state %d)" ),
                    block->m_Command, block->m_State );
        DisplayError( this, msg );
    }

    block->m_State = STATE_BLOCK_STOP;

    switch( block->m_Command )
    {
    case BLOCK_IDLE:
        err = TRUE;
        break;

    case BLOCK_DRAG:        /* Drag */
    case BLOCK_MOVE:        /* Move */
        if( DrawPanel->ManageCurseur )
            DrawPanel->ManageCurseur( DrawPanel, DC, FALSE );

        SaveCopyInUndoList( block->m_ItemsSelection,
                            UR_MOVED,
                            block->m_MoveVector );

        MoveItemsInList( block->m_ItemsSelection, block->m_MoveVector );
        block->ClearItemsList();
        break;

    case BLOCK_COPY:                /* Copy */
    case BLOCK_PRESELECT_MOVE:      /* Move with preselection list*/
        if( DrawPanel->ManageCurseur )
            DrawPanel->ManageCurseur( DrawPanel, DC, FALSE );

        DuplicateItemsInList(
            GetScreen(), block->m_ItemsSelection, block->m_MoveVector );

        SaveCopyInUndoList(
            block->m_ItemsSelection,
            (block->m_Command ==
             BLOCK_PRESELECT_MOVE) ? UR_CHANGED : UR_NEW );

        block->ClearItemsList();
        break;

    case BLOCK_PASTE:
        if( DrawPanel->ManageCurseur )
            DrawPanel->ManageCurseur( DrawPanel, DC, FALSE );
        PasteListOfItems( DC );
        block->ClearItemsList();
        break;

    case BLOCK_ZOOM:        // Handled by HandleBlockEnd()
    case BLOCK_DELETE:
    case BLOCK_SAVE:
    case BLOCK_ROTATE:
    case BLOCK_MIRROR_X:
    case BLOCK_MIRROR_Y:
    case BLOCK_FLIP:
    case BLOCK_ABORT:
    case BLOCK_SELECT_ITEMS_ONLY:
        break;
    }

    OnModify( );

    /* clear struct.m_Flags  */
    SCH_ITEM* Struct;
    for( Struct = GetScreen()->EEDrawList;
        Struct != NULL;
        Struct = Struct->Next() )
        Struct->m_Flags = 0;

    DrawPanel->ManageCurseur = NULL;
    DrawPanel->ForceCloseManageCurseur = NULL;
    block->m_Flags   = 0;
    block->m_State   = STATE_NO_BLOCK;
    block->m_Command = BLOCK_IDLE;
    GetScreen()->SetCurItem( NULL );

    TestDanglingEnds( GetScreen()->EEDrawList, DC );

    if( block->GetCount() )
    {
        DisplayError( this,
                      wxT( "HandleBlockPLace() error: some items left in buffer" ) );
        block->ClearItemsList();
    }

    SetToolID( m_ID_current_state, DrawPanel->m_PanelDefaultCursor,
               wxEmptyString );
    DrawPanel->Refresh();
}


/* Manage end block command
 * Returns:
 * 0 if no features selected
 * 1 otherwise
 * -1 If control ended and components selection (block delete, block save)
 */
int WinEDA_SchematicFrame::HandleBlockEnd( wxDC* DC )
{
    int             ii = 0;
    bool            zoom_command = FALSE;
    BLOCK_SELECTOR* block = &GetScreen()->m_BlockLocate;

    if( block->GetCount() )
    {
        BlockState   state   = block->m_State;
        CmdBlockType command = block->m_Command;
        if( DrawPanel->ForceCloseManageCurseur )
            DrawPanel->ForceCloseManageCurseur( DrawPanel, DC );
        block->m_State   = state;
        block->m_Command = command;
        DrawPanel->ManageCurseur = DrawAndSizingBlockOutlines;
        DrawPanel->ForceCloseManageCurseur = AbortBlockCurrentCommand;
        GetScreen()->m_Curseur = block->GetEnd();
        if( block->m_Command != BLOCK_ABORT )
            DrawPanel->MouseToCursorSchema();
    }

    if( DrawPanel->ManageCurseur != NULL )
        switch( block->m_Command )
        {
        case BLOCK_IDLE:
            DisplayError( this, wxT( "Error in HandleBlockPLace()" ) );
            break;

        case BLOCK_DRAG:    /* Drag */
            BreakSegmentOnJunction( (SCH_SCREEN*) GetScreen() );

        case BLOCK_MOVE:    /* Move */
        case BLOCK_COPY:    /* Copy */
            PickItemsInBlock( GetScreen()->m_BlockLocate, GetScreen() );

        case BLOCK_PRESELECT_MOVE: /* Move with preselection list*/
            if( block->GetCount() )
            {
                ii = 1;
                CollectStructsToDrag( GetScreen() );
                DrawPanel->ManageCurseur( DrawPanel, DC, FALSE );
                DrawPanel->ManageCurseur = DrawMovingBlockOutlines;
                DrawPanel->ManageCurseur( DrawPanel, DC, FALSE );
                block->m_State = STATE_BLOCK_MOVE;
            }
            else
            {
                DrawPanel->ManageCurseur( DrawPanel, DC, FALSE );
                DrawPanel->ManageCurseur = NULL;
                DrawPanel->ForceCloseManageCurseur = NULL;
            }
            break;

        case BLOCK_DELETE: /* Delete */
            PickItemsInBlock( GetScreen()->m_BlockLocate, GetScreen() );
            DrawAndSizingBlockOutlines( DrawPanel, DC, FALSE );
            if( block->GetCount() )
            {
                ii = -1;
                DeleteItemsInList( DrawPanel, block->m_ItemsSelection );
                OnModify( );
            }
            block->ClearItemsList();
            TestDanglingEnds( GetScreen()->EEDrawList, DC );
            DrawPanel->Refresh();
            break;

        case BLOCK_SAVE:  /* Save */
            PickItemsInBlock( GetScreen()->m_BlockLocate, GetScreen() );
            DrawAndSizingBlockOutlines( DrawPanel, DC, FALSE );
            if( block->GetCount() )
            {
                wxPoint move_vector =
                    -GetScreen()->m_BlockLocate.m_BlockLastCursorPosition;
                SaveStructListForPaste( block->m_ItemsSelection );
                MoveItemsInList( g_BlockSaveDataList.m_ItemsSelection,
                                 move_vector );
                ii = -1;
            }
            block->ClearItemsList();
            break;

        case BLOCK_PASTE:
            block->m_State = STATE_BLOCK_MOVE;
            break;

        case BLOCK_FLIP: /* pcbnew only! */
            break;

        case BLOCK_ROTATE:
        case BLOCK_MIRROR_X:
        case BLOCK_MIRROR_Y:
            break;

        case BLOCK_ZOOM: /* Window Zoom */
            zoom_command = TRUE;
            break;

        case BLOCK_SELECT_ITEMS_ONLY:   /* Not used */
        case BLOCK_ABORT:               /* not executed here */
            break;
        }

    if( block->m_Command  == BLOCK_ABORT )
    {
        /* clear struct.m_Flags  */
        EDA_BaseStruct* Struct;
        for( Struct = GetScreen()->EEDrawList;
            Struct != NULL;
            Struct = Struct->Next() )
            Struct->m_Flags = 0;
    }

    if( ii <= 0 )
    {
        block->m_Flags   = 0;
        block->m_State   = STATE_NO_BLOCK;
        block->m_Command = BLOCK_IDLE;
        DrawPanel->ManageCurseur = NULL;
        DrawPanel->ForceCloseManageCurseur = NULL;
        GetScreen()->SetCurItem( NULL );
        SetToolID( m_ID_current_state,
                   DrawPanel->m_PanelDefaultCursor,
                   wxEmptyString );
    }

    if( zoom_command )
        Window_Zoom( GetScreen()->m_BlockLocate );

    return ii;
}


/* Manage end block command from context menu.
 * Called after HandleBlockEnd.
 * From the command block move can execute a command other than block move.
 */
void WinEDA_SchematicFrame::HandleBlockEndByPopUp( int Command, wxDC* DC )
{
    int             ii    = 0;
    BLOCK_SELECTOR* block = &GetScreen()->m_BlockLocate;

    if( block->m_Command != BLOCK_MOVE )
        return;
    if( Command == BLOCK_MOVE )
        return;

    block->m_Command = (CmdBlockType) Command;
    block->SetMessageBlock( this );

    switch( block->m_Command )
    {
    case BLOCK_COPY:     /* move to copy */
        block->m_State = STATE_BLOCK_MOVE;
        ii = 1;
        break;

    case BLOCK_DRAG:     /* move to Drag */

        /* ???
         * Effacement de la liste des structures de pointage,
         * qui est devenue erronnee
         */
        if( DrawPanel->ManageCurseur )
            DrawPanel->ManageCurseur( DrawPanel, DC, FALSE );
        block->ClearItemsList();

        BreakSegmentOnJunction( GetScreen() );

        PickItemsInBlock( GetScreen()->m_BlockLocate, GetScreen() );
        if( block->GetCount() )
        {
            ii = 1;
            CollectStructsToDrag( (SCH_SCREEN*) GetScreen() );
            if( DrawPanel->ManageCurseur )
                DrawPanel->ManageCurseur( DrawPanel, DC, FALSE );
            block->m_State = STATE_BLOCK_MOVE;
        }
        break;

    case BLOCK_DELETE:     /* move to Delete */
        if( DrawPanel->ManageCurseur )
            DrawPanel->ManageCurseur( DrawPanel, DC, FALSE );
        if( block->GetCount() )
        {
            ii = -1;
            DeleteItemsInList( DrawPanel, block->m_ItemsSelection );
            OnModify( );
        }
        TestDanglingEnds( GetScreen()->EEDrawList, DC );
        DrawPanel->Refresh();
        DrawPanel->Refresh();
        break;

    case BLOCK_SAVE:     /* Save */
        if( DrawPanel->ManageCurseur )
            DrawPanel->ManageCurseur( DrawPanel, DC, FALSE );
        if( block->GetCount() )
        {
            wxPoint move_vector =
                -GetScreen()->m_BlockLocate.m_BlockLastCursorPosition;
            SaveStructListForPaste( block->m_ItemsSelection );
            MoveItemsInList( g_BlockSaveDataList.m_ItemsSelection, move_vector );
            ii = -1;
        }
        break;

    case BLOCK_ZOOM:     /* Window Zoom */
        DrawPanel->ForceCloseManageCurseur( DrawPanel, DC );
        DrawPanel->SetCursor(
            DrawPanel->m_PanelCursor = DrawPanel->m_PanelDefaultCursor );
        Window_Zoom( GetScreen()->m_BlockLocate );
        break;


    case BLOCK_ROTATE:
        break;

    case BLOCK_MIRROR_X:
    case BLOCK_MIRROR_Y:
        if( DrawPanel->ManageCurseur )
            DrawPanel->ManageCurseur( DrawPanel, DC, FALSE );
        if( block->GetCount() )
        {
            ii = -1;
            /* Compute the mirror center and put it on grid */
            wxPoint mirrorPoint = block->Centre();
            PutOnGrid( &mirrorPoint );
            SaveCopyInUndoList( block->m_ItemsSelection,
                                UR_MIRRORED_Y,
                                mirrorPoint );
            MirrorListOfItems( block->m_ItemsSelection, mirrorPoint );
            OnModify( );
        }
        TestDanglingEnds( GetScreen()->EEDrawList, DC );
        DrawPanel->Refresh();
        break;

    default:
        break;
    }

    if( ii <= 0 )
    {
        block->ClearItemsList();
        block->m_Flags   = 0;
        block->m_State   = STATE_NO_BLOCK;
        block->m_Command = BLOCK_IDLE;
        DrawPanel->ManageCurseur = NULL;
        DrawPanel->ForceCloseManageCurseur = NULL;
        GetScreen()->SetCurItem( NULL );
        SetToolID( m_ID_current_state,
                   DrawPanel->m_PanelDefaultCursor,
                   wxEmptyString );
    }
}


/* Traces the outline of the search block structures
 * The entire block follows the cursor
 */
static void DrawMovingBlockOutlines( WinEDA_DrawPanel* panel, wxDC* DC,
                                     bool erase )
{
    BLOCK_SELECTOR* block = &panel->GetScreen()->m_BlockLocate;;

    BASE_SCREEN*    screen = panel->GetScreen();
    SCH_ITEM*       schitem;

    /* Erase old block contents. */
    if( erase )
    {
        block->Draw( panel, DC, block->m_MoveVector, g_XorMode, block->m_Color );
        for( unsigned ii = 0; ii < block->GetCount(); ii++ )
        {
            schitem = (SCH_ITEM*) block->m_ItemsSelection.GetPickedItem( ii );
            DrawStructsInGhost( panel, DC, schitem, block->m_MoveVector );
        }
    }

    /* Repaint new view. */
    block->m_MoveVector = screen->m_Curseur - block->m_BlockLastCursorPosition;

    block->Draw( panel, DC, block->m_MoveVector, g_XorMode, block->m_Color );

    for( unsigned ii = 0; ii < block->GetCount(); ii++ )
    {
        schitem = (SCH_ITEM*) block->m_ItemsSelection.GetPickedItem( ii );
        DrawStructsInGhost( panel, DC, schitem, block->m_MoveVector );
    }
}


/* Routine to Save an object from global drawing object list.
 *  This routine is the same as delete but:
 *  - the original list is NOT removed.
 *  - List is saved in g_BlockSaveDataList
 */
void SaveStructListForPaste( PICKED_ITEMS_LIST& aItemsList )
{
    g_BlockSaveDataList.ClearListAndDeleteItems();      // delete previous
                                                        // saved list, if
                                                        // exists

    /* save the new list: */
    ITEM_PICKER item;
    // In list the wrapper is owner of the shematic item, we can use the UR_DELETED
    // status for the picker because pickers with this status are owner of the picked item
    // (or TODO ?: create a new status like UR_DUPLICATE)
    item.m_UndoRedoStatus = UR_DELETED;
    for( unsigned ii = 0; ii < aItemsList.GetCount(); ii++ )
    {
        /* Make a copy of the original picked item. */
        SCH_ITEM* DrawStructCopy = DuplicateStruct(
             (SCH_ITEM*) aItemsList.GetPickedItem( ii ) );
        DrawStructCopy->SetParent( NULL );
        item.m_PickedItem = DrawStructCopy;
        g_BlockSaveDataList.PushItem( item );
    }
}


/*****************************************************************************
* Routine to paste a structure from the g_BlockSaveDataList stack.
*   This routine is the same as undelete but original list is NOT removed.
*****************************************************************************/
void WinEDA_SchematicFrame::PasteListOfItems( wxDC* DC )
{
    SCH_ITEM* Struct;

    if( g_BlockSaveDataList.GetCount() == 0 )
    {
        DisplayError( this, wxT( "No struct to paste" ) );
        return;
    }

    PICKED_ITEMS_LIST picklist;

    // Creates data, and push it as new data in undo item list buffer
    ITEM_PICKER       picker( NULL, UR_NEW );
    for( unsigned ii = 0; ii < g_BlockSaveDataList.GetCount(); ii++ )
    {
        Struct = DuplicateStruct(
             (SCH_ITEM*) g_BlockSaveDataList.m_ItemsSelection.GetPickedItem(
                ii ) );
        picker.m_PickedItem = Struct;
        picklist.PushItem( picker );

        // Clear annotation and init new time stamp for the new components:
        if( Struct->Type() == TYPE_SCH_COMPONENT )
        {
            ( (SCH_COMPONENT*) Struct )->m_TimeStamp = GetTimeStamp();
            ( (SCH_COMPONENT*) Struct )->ClearAnnotation( NULL );
        }
        SetaParent( Struct, GetScreen() );
        RedrawOneStruct( DrawPanel, DC, Struct, GR_DEFAULT_DRAWMODE );
        Struct->SetNext( GetScreen()->EEDrawList );
        GetScreen()->EEDrawList = Struct;
    }

    SaveCopyInUndoList( picklist, UR_NEW );

    MoveItemsInList( picklist, GetScreen()->m_BlockLocate.m_MoveVector );

    /* clear .m_Flags member for all items */
    for( Struct = GetScreen()->EEDrawList;
        Struct != NULL;
        Struct = Struct->Next() )
        Struct->m_Flags = 0;

    OnModify( );

    return;
}


/* creates the list of items found when a drag block is initiated.
 * items are those selected in window block an some items outside this area but
 * connected to a selected item (connected wires to a component or an entry )
 */
static void CollectStructsToDrag( SCH_SCREEN* screen )
{
    SCH_ITEM* Struct;
    SCH_LINE* SegmStruct;

    PICKED_ITEMS_LIST*  pickedlist = &screen->m_BlockLocate.m_ItemsSelection;

    if( pickedlist->GetCount() == 0 )
        return;

    /* .m_Flags member is used to handle how a wire is exactly selected
     * (fully selected, or partially selected by an end point )
     */
    for( Struct = screen->EEDrawList; Struct != NULL; Struct = Struct->Next() )
        Struct->m_Flags = 0;

    for( unsigned ii = 0; ii < pickedlist->GetCount(); ii++ )
    {
        Struct = (SCH_ITEM*) pickedlist->GetPickedItem( ii );
        Struct->m_Flags = SELECTED;
    }

    if( screen->m_BlockLocate.m_Command != BLOCK_DRAG )
        return;


    /* Remove the displacement of segment and undo the selection. */
    for( unsigned ii = 0; ii < pickedlist->GetCount(); ii++ )
    {
        Struct = (SCH_ITEM*) (SCH_ITEM*) pickedlist->GetPickedItem( ii );
        if( Struct->Type() == DRAW_SEGMENT_STRUCT_TYPE )
        {
            SegmStruct = (SCH_LINE*) Struct;
            if( !screen->m_BlockLocate.Inside( SegmStruct->m_Start ) )
                SegmStruct->m_Flags |= STARTPOINT;

            if( !screen->m_BlockLocate.Inside( SegmStruct->m_End ) )
                SegmStruct->m_Flags |= ENDPOINT;

            // Save m_Flags for Undo/redo drag operations:
            pickedlist->SetPickerFlags( SegmStruct->m_Flags, ii );
        }
    }

    /* Search for other items to drag. They are end wires connected to selected
     * items
     */

    for( unsigned ii = 0; ii < pickedlist->GetCount(); ii++ )
    {
        Struct = (SCH_ITEM*) (SCH_ITEM*) pickedlist->GetPickedItem( ii );
    if( ( Struct->Type() == TYPE_SCH_LABEL )
        || ( Struct->Type() == TYPE_SCH_GLOBALLABEL )
        || ( Struct->Type() == TYPE_SCH_HIERLABEL ) )
    {
            #undef STRUCT
            #define STRUCT ( (SCH_TEXT*) Struct )
        if( !screen->m_BlockLocate.Inside( STRUCT->m_Pos ) )
        {
            AddPickedItem( screen, STRUCT->m_Pos );
        }
    }

        if( Struct->Type() == TYPE_SCH_COMPONENT )
        {
            // Add all pins of the selected component to list
            LIB_PIN* pin;
            wxPoint  pos;
            pin = GetNextPinPosition( (SCH_COMPONENT*) Struct, pos, true );
            while( pin )
            {
                if( !screen->m_BlockLocate.Inside( pos ) )
                {
                    // This pin is outside area,
                    // but because it it the pin of a selected component
                    // we must also select connected items to this pin
                    AddPickedItem( screen, pos );
                }

                pin = GetNextPinPosition( (SCH_COMPONENT*) Struct, pos, false );
            }
        }

        if( Struct->Type() == DRAW_SHEET_STRUCT_TYPE )
        {
            SCH_SHEET* sheet = (SCH_SHEET*) Struct;

            // Add all pins sheets of a selected hierarchical sheet to the list
            BOOST_FOREACH( SCH_SHEET_PIN label, sheet->GetSheetPins() )
            {
                AddPickedItem( screen, label.m_Pos );
            }
        }

        if( Struct->Type() == DRAW_BUSENTRY_STRUCT_TYPE )
        {
            SCH_BUS_ENTRY* item = (SCH_BUS_ENTRY*) Struct;
            AddPickedItem( screen, item->m_Pos );
            AddPickedItem( screen, item->m_End() );
        }
    }
}


/** AddPickedItem
 * add to the picked list in screen->m_BlockLocate items found at location
 * position
 * @param screen = the screen to consider
 * @param position = the wxPoint where items must be located to be select
 */
static void AddPickedItem( SCH_SCREEN* screen, wxPoint position )
{
    SCH_ITEM*          Struct;

    /* Review the list of already selected elements. */
    PICKED_ITEMS_LIST* pickedlist = &screen->m_BlockLocate.m_ItemsSelection;

    for( unsigned ii = 0; ii < pickedlist->GetCount(); ii++ )
    {
        Struct = (SCH_ITEM*) pickedlist->GetPickedItem( ii );

        switch( Struct->Type() )
        {
        case DRAW_SEGMENT_STRUCT_TYPE:
                #undef STRUCT
                #define STRUCT ( (SCH_LINE*) Struct )
            if( STRUCT->m_Start == position )
                STRUCT->m_Flags &= ~STARTPOINT;

            if( STRUCT->m_End == position )
                STRUCT->m_Flags &= ~ENDPOINT;

            // Save m_Flags for Undo/redo drag operations:
            pickedlist->SetPickerFlags( STRUCT->m_Flags, ii );
            break;

        default:
            break;
        }
    }

    /* Review the list of elements not selected. */

    ITEM_PICKER picker;
    Struct = screen->EEDrawList;
    while( Struct )
    {
        picker.m_PickedItem     = Struct;
        picker.m_PickedItemType = Struct->Type();
        switch( Struct->Type() )
        {
        case TYPE_NOT_INIT:
            break;

        case DRAW_POLYLINE_STRUCT_TYPE:
            if( Struct->m_Flags & SELECTED )
                break;
            break;

        case DRAW_JUNCTION_STRUCT_TYPE:
                #undef STRUCT
                #define STRUCT ( (SCH_JUNCTION*) Struct )
            if( Struct->m_Flags & SELECTED )
                break;
            if( STRUCT->m_Pos != position )
                break;
            pickedlist->PushItem( picker );
            break;

        case DRAW_SEGMENT_STRUCT_TYPE:
                #undef STRUCT
                #define STRUCT ( (SCH_LINE*) Struct )
            if( Struct->m_Flags & SELECTED )
                break;
            if( STRUCT->m_Start == position )
            {
                Struct->m_Flags  = SELECTED | ENDPOINT | STARTPOINT;
                Struct->m_Flags &= ~STARTPOINT;

                // Save m_Flags for Undo/redo drag operations:
                picker.m_PickerFlags = Struct->m_Flags;
                pickedlist->PushItem( picker );
            }
            else if( STRUCT->m_End == position )
            {
                Struct->m_Flags  = SELECTED | ENDPOINT | STARTPOINT;
                Struct->m_Flags &= ~ENDPOINT;

                // Save m_Flags for Undo/redo drag operations:
                picker.m_PickerFlags = Struct->m_Flags;
                pickedlist->PushItem( picker );
            }
            break;

        case DRAW_BUSENTRY_STRUCT_TYPE:
            break;

        case TYPE_SCH_TEXT:
            break;

        case TYPE_SCH_LABEL:
                #undef STRUCT
                #define STRUCT ( (SCH_LABEL*) Struct )
            if( Struct->m_Flags & SELECTED )
                break;  /* Already in list */
            if( STRUCT->m_Pos != position )
                break;
            Struct->m_Flags |= SELECTED;
            pickedlist->PushItem( picker );
            break;

        case TYPE_SCH_HIERLABEL:
        case TYPE_SCH_GLOBALLABEL:
                #undef STRUCT
                #define STRUCT ( (SCH_LABEL*) Struct )
            if( Struct->m_Flags & SELECTED )
                break;  /* Already in list */
            if( STRUCT->m_Pos != position )
                break;
            Struct->m_Flags |= SELECTED;
            pickedlist->PushItem( picker );
            break;

        case TYPE_SCH_COMPONENT:
        case DRAW_SHEET_STRUCT_TYPE:
        case DRAW_HIERARCHICAL_PIN_SHEET_STRUCT_TYPE:
            break;

        case TYPE_SCH_MARKER:
                #undef STRUCT
                #define STRUCT ( (SCH_MARKER*) Struct )
            if( Struct->m_Flags & SELECTED )
                break;  /* Already in list */
            if( STRUCT->m_Pos != position )
                break;
            Struct->m_Flags |= SELECTED;
            pickedlist->PushItem( picker );
            break;

        case DRAW_NOCONNECT_STRUCT_TYPE:
                #undef STRUCT
                #define STRUCT ( (SCH_NO_CONNECT*) Struct )
            if( Struct->m_Flags & SELECTED )
                break;  /* Already in list */
            if( STRUCT->m_Pos != position )
                break;
            Struct->m_Flags |= SELECTED;
            pickedlist->PushItem( picker );
            break;

        default:
            break;
        }
        Struct = Struct->Next();
    }
}


/** GetNextPinPosition()
 * calculate position of the "next" pin of the aDrawLibItem component
 * @param aDrawLibItem = component to test.
 * @param aPosition = the calculated pin position, according to the component
 * orientation and position
 * @param aSearchFirst = if true, search for the first pin
 * @return a pointer to the pin
 */
static LIB_PIN* GetNextPinPosition( SCH_COMPONENT* aDrawLibItem,
                                    wxPoint&       aPosition,
                                    bool           aSearchFirst )
{
    static LIB_COMPONENT* Entry;
    static int Multi, convert, TransMat[2][2];
    static wxPoint CmpPosition;
    static LIB_PIN* Pin;

    if( aSearchFirst )
    {
        Entry = CMP_LIBRARY::FindLibraryComponent( aDrawLibItem->m_ChipName );

        if( Entry == NULL )
            return NULL;

        Pin   = Entry->GetNextPin();
        Multi = aDrawLibItem->m_Multi;
        convert     = aDrawLibItem->m_Convert;
        CmpPosition = aDrawLibItem->m_Pos;
        memcpy( TransMat, aDrawLibItem->m_Transform, sizeof(TransMat) );
    }
    else
        Pin = Entry->GetNextPin( Pin );

    for( ; Pin != NULL; Pin = Entry->GetNextPin( Pin ) )
    {
        wxASSERT( Pin->Type() == COMPONENT_PIN_DRAW_TYPE );

        /* Skip items not used for this part */
        if( Multi && Pin->m_Unit && ( Pin->m_Unit != Multi ) )
            continue;
        if( convert && Pin->m_Convert && ( Pin->m_Convert != convert ) )
            continue;

        /* Calculate the pin position (according to the component orientation)
         */
        aPosition = TransformCoordinate( TransMat, Pin->m_Pos ) + CmpPosition;
        return Pin;
    }

    return NULL;
}
