void PCB_EDIT_FRAME::End_Move_Zone_Corner_Or_Outlines( wxDC* DC, ZONE_CONTAINER* aZone )
{
    aZone->ClearFlags();
    m_canvas->SetMouseCapture( NULL, NULL );

    if( DC )
        aZone->Draw( m_canvas, DC, GR_OR );

    OnModify();
    s_AddCutoutToCurrentZone = false;
    s_CurrentZone = NULL;

    SetCurItem( NULL );       // This outline can be deleted when merging outlines

    // Combine zones if possible
    GetBoard()->OnAreaPolygonModified( &s_AuxiliaryList, aZone );
    m_canvas->Refresh();

    int ii = GetBoard()->GetAreaIndex( aZone );     // test if aZone exists

    if( ii < 0 )
        aZone = NULL;                          // was removed by combining zones

    UpdateCopyOfZonesList( s_PickedList, s_AuxiliaryList, GetBoard() );
    SaveCopyInUndoList(s_PickedList, UR_UNSPECIFIED);
    s_PickedList.ClearItemsList(); // s_ItemsListPicker is no more owner of picked items

    int error_count = GetBoard()->Test_Drc_Areas_Outlines_To_Areas_Outlines( aZone, true );

    if( error_count )
    {
        DisplayError( this, _( "Area: DRC outline error" ) );
    }
}
void PCB_EDIT_FRAME::Remove_Zone_Corner( wxDC* DC, ZONE_CONTAINER* aZone )
{
    OnModify();

    if( aZone->m_Poly->GetNumCorners() <= 3 )
    {
        m_canvas->RefreshDrawingRect( aZone->GetBoundingBox() );

        if( DC )
        {  // Remove the full zone because this is no more an area
            aZone->UnFill();
            aZone->DrawFilledArea( m_canvas, DC, GR_XOR );
        }

        GetBoard()->Delete( aZone );
        return;
    }

    int layer = aZone->GetLayer();

    if( DC )
    {
        GetBoard()->RedrawAreasOutlines( m_canvas, DC, GR_XOR, layer );
        GetBoard()->RedrawFilledAreas( m_canvas, DC, GR_XOR, layer );
    }

    s_AuxiliaryList.ClearListAndDeleteItems();
    s_PickedList. ClearListAndDeleteItems();
    SaveCopyOfZones( s_PickedList, GetBoard(), aZone->GetNet(),
                     aZone->GetLayer() );
    aZone->m_Poly->DeleteCorner( aZone->m_CornerSelection );

    // modify zones outlines according to the new aZone shape
    GetBoard()->OnAreaPolygonModified( &s_AuxiliaryList, aZone );

    if( DC )
    {
        GetBoard()->RedrawAreasOutlines( m_canvas, DC, GR_OR, layer );
        GetBoard()->RedrawFilledAreas( m_canvas, DC, GR_OR, layer );
    }

    UpdateCopyOfZonesList( s_PickedList, s_AuxiliaryList, GetBoard() );
    SaveCopyInUndoList(s_PickedList, UR_UNSPECIFIED);
    s_PickedList.ClearItemsList(); // s_ItemsListPicker is no more owner of picked items

    int ii = GetBoard()->GetAreaIndex( aZone );     // test if aZone exists

    if( ii < 0 )
        aZone = NULL;   // aZone does not exist anymore, after combining zones

    int error_count = GetBoard()->Test_Drc_Areas_Outlines_To_Areas_Outlines( aZone, true );

    if( error_count )
    {
        DisplayError( this, _( "Area: DRC outline error" ) );
    }
}
void PCB_EDIT_FRAME::duplicateZone( wxDC* aDC, ZONE_CONTAINER* aZone )
{
    ZONE_CONTAINER* newZone = new ZONE_CONTAINER( GetBoard() );
    newZone->Copy( aZone );
    newZone->UnFill();
    ZONE_SETTINGS zoneSettings;
    zoneSettings << *aZone;

    bool success;

    if( aZone->GetIsKeepout() )
        success = InvokeKeepoutAreaEditor( this, &zoneSettings );
    else if( aZone->IsOnCopperLayer() )
        success = InvokeCopperZonesEditor( this, &zoneSettings );
    else
        success = InvokeNonCopperZonesEditor( this, aZone, &zoneSettings );

    if( success )
    {
        zoneSettings.ExportSetting( *newZone );
        newZone->m_Poly->Hatch();

        s_AuxiliaryList.ClearListAndDeleteItems();
        s_PickedList.ClearListAndDeleteItems();
        SaveCopyOfZones( s_PickedList, GetBoard(), newZone->GetNet(), newZone->GetLayer() );
        GetBoard()->Add( newZone );

        ITEM_PICKER picker( newZone, UR_NEW );
        s_PickedList.PushItem( picker );

        GetScreen()->SetCurItem( NULL );       // This outline may be deleted when merging outlines

        // Combine zones if possible
        GetBoard()->OnAreaPolygonModified( &s_AuxiliaryList, newZone );

        // Redraw zones
        GetBoard()->RedrawAreasOutlines( m_canvas, aDC, GR_OR, newZone->GetLayer() );
        GetBoard()->RedrawFilledAreas( m_canvas, aDC, GR_OR, newZone->GetLayer() );

        if( GetBoard()->GetAreaIndex( newZone ) >= 0
           && GetBoard()->Test_Drc_Areas_Outlines_To_Areas_Outlines( newZone, true ) )
        {
            DisplayError( this, _( "Duplicate Zone: The outline of the duplicated zone fails DRC check!" ) );
        }

        UpdateCopyOfZonesList( s_PickedList, s_AuxiliaryList, GetBoard() );
        SaveCopyInUndoList( s_PickedList, UR_UNSPECIFIED );
        s_PickedList.ClearItemsList();

        OnModify();
    }
    else
        delete newZone;
}
// Place a dragged (or moved) track segment or via
bool PCB_EDIT_FRAME::PlaceDraggedOrMovedTrackSegment( TRACK* Track, wxDC* DC )
{
    int        errdrc;

    if( Track == NULL )
        return false;

    int current_net_code = Track->GetNetCode();

    // DRC control:
    if( g_Drc_On )
    {
        errdrc = m_drc->Drc( Track, GetBoard()->m_Track );

        if( errdrc == BAD_DRC )
            return false;

        // Redraw the dragged segments
        for( unsigned ii = 0; ii < g_DragSegmentList.size(); ii++ )
        {
            errdrc = m_drc->Drc( g_DragSegmentList[ii].m_Track, GetBoard()->m_Track );

            if( errdrc == BAD_DRC )
                return false;
        }
    }

    // DRC Ok: place track segments
    Track->ClearFlags();
    Track->SetState( IN_EDIT, false );

    // Draw dragged tracks
    for( unsigned ii = 0; ii < g_DragSegmentList.size(); ii++ )
    {
        Track = g_DragSegmentList[ii].m_Track;
        Track->SetState( IN_EDIT, false );
        Track->ClearFlags();

        /* Test the connections modified by the move
         *  (only pad connection must be tested, track connection will be
         * tested by TestNetConnection() ) */
        LSET layerMask( Track->GetLayer() );

        Track->start = GetBoard()->GetPadFast( Track->GetStart(), layerMask );

        if( Track->start )
            Track->SetState( BEGIN_ONPAD, true );
        else
            Track->SetState( BEGIN_ONPAD, false );

        Track->end = GetBoard()->GetPadFast( Track->GetEnd(), layerMask );

        if( Track->end )
            Track->SetState( END_ONPAD, true );
        else
            Track->SetState( END_ONPAD, false );
    }

    EraseDragList();

    SaveCopyInUndoList( s_ItemsListPicker, UR_UNSPECIFIED );
    s_ItemsListPicker.ClearItemsList(); // s_ItemsListPicker is no more owner of picked items

    GetBoard()->PopHighLight();

    OnModify();
    m_canvas->SetMouseCapture( NULL, NULL );

    if( current_net_code > 0 )
        TestNetConnection( DC, current_net_code );

    m_canvas->Refresh();

    return true;
}
/* Route all traces
 * :
 *  1 if OK
 * -1 if escape (stop being routed) request
 * -2 if default memory allocation
 */
int PCB_EDIT_FRAME::Solve( wxDC* DC, int aLayersCount )
{
    int           current_net_code;
    int           row_source, col_source, row_target, col_target;
    int           success, nbsucces = 0, nbunsucces = 0;
    NETINFO_ITEM* net;
    bool          stop = false;
    wxString      msg;
    int           routedCount = 0;      // routed ratsnest count
    bool          two_sides = aLayersCount == 2;

    m_canvas->SetAbortRequest( false );

    s_Clearance = GetBoard()->GetDesignSettings().GetDefault()->GetClearance();

    // Prepare the undo command info
    s_ItemsListPicker.ClearListAndDeleteItems();  // Should not be necessary, but...

    // go until no more work to do
    GetWork( &row_source, &col_source, &current_net_code,
             &row_target, &col_target, &pt_cur_ch ); // First net to route.

    for( ; row_source != ILLEGAL; GetWork( &row_source, &col_source,
                                           &current_net_code, &row_target,
                                           &col_target,
                                           &pt_cur_ch ) )
    {
        // Test to stop routing ( escape key pressed )
        wxYield();

        if( m_canvas->GetAbortRequest() )
        {
            if( IsOK( this, _( "Abort routing?" ) ) )
            {
                success = STOP_FROM_ESC;
                stop    = true;
                break;
            }
            else
            {
                m_canvas->SetAbortRequest( false );
            }
        }

        EraseMsgBox();

        routedCount++;
        net = GetBoard()->FindNet( current_net_code );

        if( net )
        {
            msg.Printf( wxT( "[%8.8s]" ), GetChars( net->GetNetname() ) );
            AppendMsgPanel( wxT( "Net route" ), msg, BROWN );
            msg.Printf( wxT( "%d / %d" ), routedCount, RoutingMatrix.m_RouteCount );
            AppendMsgPanel( wxT( "Activity" ), msg, BROWN );
        }

        segm_oX = GetBoard()->GetBoundingBox().GetX() + (RoutingMatrix.m_GridRouting * col_source);
        segm_oY = GetBoard()->GetBoundingBox().GetY() + (RoutingMatrix.m_GridRouting * row_source);
        segm_fX = GetBoard()->GetBoundingBox().GetX() + (RoutingMatrix.m_GridRouting * col_target);
        segm_fY = GetBoard()->GetBoundingBox().GetY() + (RoutingMatrix.m_GridRouting * row_target);

        // Draw segment.
        GRLine( m_canvas->GetClipBox(), DC,
                segm_oX, segm_oY, segm_fX, segm_fY,
                0, WHITE );
        pt_cur_ch->m_PadStart->Draw( m_canvas, DC, GR_OR | GR_HIGHLIGHT );
        pt_cur_ch->m_PadEnd->Draw( m_canvas, DC, GR_OR | GR_HIGHLIGHT );

        success = Autoroute_One_Track( this, DC,
                                       two_sides, row_source, col_source,
                                       row_target, col_target, pt_cur_ch );

        switch( success )
        {
        case NOSUCCESS:
            pt_cur_ch->m_Status |= CH_UNROUTABLE;
            nbunsucces++;
            break;

        case STOP_FROM_ESC:
            stop = true;
            break;

        case ERR_MEMORY:
            stop = true;
            break;

        default:
            nbsucces++;
            break;
        }

        msg.Printf( wxT( "%d" ), nbsucces );
        AppendMsgPanel( wxT( "OK" ), msg, GREEN );
        msg.Printf( wxT( "%d" ), nbunsucces );
        AppendMsgPanel( wxT( "Fail" ), msg, RED );
        msg.Printf( wxT( "  %d" ), GetBoard()->GetUnconnectedNetCount() );
        AppendMsgPanel( wxT( "Not Connected" ), msg, CYAN );

        // Delete routing from display.
        pt_cur_ch->m_PadStart->Draw( m_canvas, DC, GR_AND );
        pt_cur_ch->m_PadEnd->Draw( m_canvas, DC, GR_AND );

        if( stop )
            break;
    }

    SaveCopyInUndoList( s_ItemsListPicker, UR_UNSPECIFIED );
    s_ItemsListPicker.ClearItemsList(); // s_ItemsListPicker is no more owner of picked items

    return SUCCESS;
}
void PCB_EDIT_FRAME::Edit_Zone_Params( wxDC* DC, ZONE_CONTAINER* aZone )
{
    ZONE_EDIT_T     edited;
    ZONE_SETTINGS   zoneInfo = GetZoneSettings();

    m_canvas->SetIgnoreMouseEvents( true );

    // Save initial zones configuration, for undo/redo, before adding new zone
    // note the net name and the layer can be changed, so we must save all zones
    s_AuxiliaryList.ClearListAndDeleteItems();
    s_PickedList.ClearListAndDeleteItems();
    SaveCopyOfZones(s_PickedList, GetBoard(), -1, -1 );

    if( aZone->GetIsKeepout() )
    {
        // edit a keepout area on a copper layer
        zoneInfo << *aZone;
        edited = InvokeKeepoutAreaEditor( this, &zoneInfo );
    }
    else if( aZone->GetLayer() < FIRST_NO_COPPER_LAYER )
    {
        // edit a zone on a copper layer

        zoneInfo << *aZone;

        edited = InvokeCopperZonesEditor( this, &zoneInfo );
    }
    else
    {
        edited = InvokeNonCopperZonesEditor( this, aZone, &zoneInfo );
    }

    m_canvas->MoveCursorToCrossHair();
    m_canvas->SetIgnoreMouseEvents( false );

    if( edited == ZONE_ABORT )
    {
        s_AuxiliaryList.ClearListAndDeleteItems();
        s_PickedList.ClearListAndDeleteItems();
        return;
    }

    SetZoneSettings( zoneInfo );

    if( edited == ZONE_EXPORT_VALUES )
    {
        UpdateCopyOfZonesList( s_PickedList, s_AuxiliaryList, GetBoard() );
        SaveCopyInUndoList(s_PickedList, UR_UNSPECIFIED);
        s_PickedList.ClearItemsList(); // s_ItemsListPicker is no more owner of picked items
        return;
    }

    // Undraw old zone outlines
    for( int ii = 0; ii < GetBoard()->GetAreaCount(); ii++ )
    {
        ZONE_CONTAINER* edge_zone = GetBoard()->GetArea( ii );
        edge_zone->Draw( m_canvas, DC, GR_XOR );
    }

    zoneInfo.ExportSetting( *aZone );

    NETINFO_ITEM* net = GetBoard()->FindNet( zoneInfo.m_NetcodeSelection );

    if( net )   // net == NULL should not occur
        aZone->SetNetName( net->GetNetname() );

    // Combine zones if possible
    GetBoard()->OnAreaPolygonModified( &s_AuxiliaryList, aZone );

    // Redraw the real new zone outlines
    GetBoard()->RedrawAreasOutlines( m_canvas, DC, GR_OR, -1 );

    UpdateCopyOfZonesList( s_PickedList, s_AuxiliaryList, GetBoard() );
    SaveCopyInUndoList(s_PickedList, UR_UNSPECIFIED);

    s_PickedList.ClearItemsList();  // s_ItemsListPicker is no longer owner of picked items

    OnModify();
}
bool PCB_EDIT_FRAME::End_Zone( wxDC* DC )
{
    ZONE_CONTAINER* zone = GetBoard()->m_CurrentZoneContour;

    if( !zone )
        return true;

    // Validate the current outline:
    if( zone->GetNumCorners() <= 2 )   // An outline must have 3 corners or more
    {
        Abort_Zone_Create_Outline( m_canvas, DC );
        return true;
    }

    // Remove the last corner if is is at the same location as the prevoius corner
    zone->m_Poly->RemoveNullSegments();

    // Validate the current edge:
    int icorner = zone->GetNumCorners() - 1;
    if( zone->IsOnCopperLayer() )
    {
        if( Drc_On && m_drc->Drc( zone, icorner - 1 ) == BAD_DRC )  // we can't validate last edge
            return false;

        if( Drc_On && m_drc->Drc( zone, icorner ) == BAD_DRC )      // we can't validate the closing edge
        {
            DisplayError( this,
                          _( "DRC error: closing this area creates a drc error with an other area" ) );
            m_canvas->MoveCursorToCrossHair();
            return false;
        }
    }

    zone->ClearFlags();

    zone->DrawWhileCreateOutline( m_canvas, DC, GR_XOR );

    m_canvas->SetMouseCapture( NULL, NULL );

    // Undraw old drawings, because they can have important changes
    int layer = zone->GetLayer();
    GetBoard()->RedrawAreasOutlines( m_canvas, DC, GR_XOR, layer );
    GetBoard()->RedrawFilledAreas( m_canvas, DC, GR_XOR, layer );

    // Save initial zones configuration, for undo/redo, before adding new zone
    s_AuxiliaryList.ClearListAndDeleteItems();
    s_PickedList.ClearListAndDeleteItems();
    SaveCopyOfZones(s_PickedList, GetBoard(), zone->GetNet(), zone->GetLayer() );

    // Put new zone in list
    if( !s_CurrentZone )
    {
        zone->m_Poly->CloseLastContour(); // Close the current corner list
        GetBoard()->Add( zone );
        GetBoard()->m_CurrentZoneContour = NULL;

        // Add this zone in picked list, as new item
        ITEM_PICKER picker( zone, UR_NEW );
        s_PickedList.PushItem( picker );
    }
    else    // Append this outline as a cutout to an existing zone
    {
        for( int ii = 0; ii < zone->GetNumCorners(); ii++ )
        {
            s_CurrentZone->AppendCorner( zone->GetCornerPosition( ii ) );
        }

        s_CurrentZone->m_Poly->CloseLastContour(); // Close the current corner list
        zone->RemoveAllContours();      // All corners are copied in s_CurrentZone. Free corner list.
        zone = s_CurrentZone;
    }

    s_AddCutoutToCurrentZone = false;
    s_CurrentZone = NULL;

    GetScreen()->SetCurItem( NULL );       // This outline can be deleted when merging outlines

    // Combine zones if possible :
    GetBoard()->OnAreaPolygonModified( &s_AuxiliaryList, zone );

    // Redraw the real edge zone :
    GetBoard()->RedrawAreasOutlines( m_canvas, DC, GR_OR, layer );
    GetBoard()->RedrawFilledAreas( m_canvas, DC, GR_OR, layer );

    int ii = GetBoard()->GetAreaIndex( zone );   // test if zone exists

    if( ii < 0 )
        zone = NULL;                        // was removed by combining zones

    int error_count = GetBoard()->Test_Drc_Areas_Outlines_To_Areas_Outlines( zone, true );

    if( error_count )
    {
        DisplayError( this, _( "Area: DRC outline error" ) );
    }

    UpdateCopyOfZonesList( s_PickedList, s_AuxiliaryList, GetBoard() );
    SaveCopyInUndoList(s_PickedList, UR_UNSPECIFIED);
    s_PickedList.ClearItemsList(); // s_ItemsListPicker is no more owner of picked items

    OnModify();
    return true;
}
void PCB_EDIT_FRAME::RunActionPlugin( ACTION_PLUGIN* aActionPlugin )
{
    PICKED_ITEMS_LIST itemsList;
    BOARD*  currentPcb  = GetBoard();
    bool    fromEmpty   = false;

    itemsList.m_Status = UR_CHANGED;

    OnModify();

    // Append tracks:
    for( BOARD_ITEM* item = currentPcb->m_Track; item != NULL; item = item->Next() )
    {
        ITEM_PICKER picker( item, UR_CHANGED );
        itemsList.PushItem( picker );
    }

    // Append modules:
    for( BOARD_ITEM* item = currentPcb->m_Modules; item != NULL; item = item->Next() )
    {
        ITEM_PICKER picker( item, UR_CHANGED );
        itemsList.PushItem( picker );
    }

    // Append drawings
    for( BOARD_ITEM* item = currentPcb->m_Drawings; item != NULL; item = item->Next() )
    {
        ITEM_PICKER picker( item, UR_CHANGED );
        itemsList.PushItem( picker );
    }

    // Append zones outlines
    for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
    {
        ITEM_PICKER picker( (EDA_ITEM*) currentPcb->GetArea(
                        ii ), UR_CHANGED );
        itemsList.PushItem( picker );
    }

    // Append zones segm:
    for( BOARD_ITEM* item = currentPcb->m_SegZoneDeprecated; item != NULL; item = item->Next() )
    {
        ITEM_PICKER picker( item, UR_CHANGED );
        itemsList.PushItem( picker );
    }

    if( itemsList.GetCount() > 0 )
        SaveCopyInUndoList( itemsList, UR_CHANGED, wxPoint( 0.0, 0.0 ) );
    else
        fromEmpty = true;

    itemsList.ClearItemsList();

    // Execute plugin itself...
    ACTION_PLUGINS::SetActionRunning( true );
    aActionPlugin->Run();
    ACTION_PLUGINS::SetActionRunning( false );

    currentPcb->m_Status_Pcb = 0;

    // Get back the undo buffer to fix some modifications
    PICKED_ITEMS_LIST* oldBuffer = NULL;

    if( fromEmpty )
    {
        oldBuffer = new PICKED_ITEMS_LIST();
        oldBuffer->m_Status = UR_NEW;
    }
    else
    {
        oldBuffer = GetScreen()->PopCommandFromUndoList();
        wxASSERT( oldBuffer );
    }

    // Try do discover what was modified

    PICKED_ITEMS_LIST deletedItemsList;

    // Found deleted modules
    for( unsigned int i = 0; i < oldBuffer->GetCount(); i++ )
    {
        BOARD_ITEM* item = (BOARD_ITEM*) oldBuffer->GetPickedItem( i );
        ITEM_PICKER picker( item, UR_DELETED );

        wxASSERT( item );

        switch( item->Type() )
        {
        case PCB_NETINFO_T:
        case PCB_MARKER_T:
        case PCB_MODULE_T:
        case PCB_TRACE_T:
        case PCB_VIA_T:
        case PCB_LINE_T:
        case PCB_TEXT_T:
        case PCB_DIMENSION_T:
        case PCB_TARGET_T:
        case PCB_SEGZONE_T:

            // If item has a list it's mean that the element is on the board
            if( item->GetList() == NULL )
            {
                deletedItemsList.PushItem( picker );
            }

            break;

        case PCB_ZONE_AREA_T:
        {
            bool zoneFound = false;

            for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
                zoneFound |= currentPcb->GetArea( ii ) == item;

            if( !zoneFound )
            {
                deletedItemsList.PushItem( picker );
            }

            break;
        }

        default:
            wxString msg;
            msg.Printf( _( "(PCB_EDIT_FRAME::OnActionPlugin) needs work: "
                                "BOARD_ITEM type (%d) not handled" ),
                    item->Type() );
            wxFAIL_MSG( msg );
            break;
        }
    }

    // Mark deleted elements in undolist
    for( unsigned int i = 0; i < deletedItemsList.GetCount(); i++ )
    {
        oldBuffer->PushItem( deletedItemsList.GetItemWrapper( i ) );
    }

    // Find new modules
    for( BOARD_ITEM* item = currentPcb->m_Modules; item != NULL; item = item->Next() )
    {
        if( !oldBuffer->ContainsItem( item ) )
        {
            ITEM_PICKER picker( item, UR_NEW );
            oldBuffer->PushItem( picker );
        }
    }

    for( BOARD_ITEM* item = currentPcb->m_Track; item != NULL; item = item->Next() )
    {
        if( !oldBuffer->ContainsItem( item ) )
        {
            ITEM_PICKER picker( item, UR_NEW );
            oldBuffer->PushItem( picker );
        }
    }

    for( BOARD_ITEM* item = currentPcb->m_Drawings; item != NULL; item = item->Next() )
    {
        if( !oldBuffer->ContainsItem( item ) )
        {
            ITEM_PICKER picker( item, UR_NEW );
            oldBuffer->PushItem( picker );
        }
    }

    for( BOARD_ITEM* item = currentPcb->m_SegZoneDeprecated; item != NULL; item = item->Next() )
    {
        if( !oldBuffer->ContainsItem( item ) )
        {
            ITEM_PICKER picker( item, UR_NEW );
            oldBuffer->PushItem( picker );
        }
    }

    for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
    {
        if( !oldBuffer->ContainsItem( (EDA_ITEM*) currentPcb->GetArea( ii ) ) )
        {
            ITEM_PICKER picker( (EDA_ITEM*) currentPcb->GetArea(
                            ii ), UR_NEW );
            oldBuffer->PushItem( picker );
        }
    }


    GetScreen()->PushCommandToUndoList( oldBuffer );

    if( IsGalCanvasActive() )
    {
        UseGalCanvas( GetGalCanvas() );
    }
    else
    {
        UpdateUserInterface();
        GetScreen()->SetModify();
        Refresh();
    }
}