bool PCB_BASE_FRAME::InvokeDialogGrid()
{
    wxPoint grid_origin = GetGridOrigin();

    DIALOG_SET_GRID dlg( this, &m_UserGridUnit, g_UserUnit, &m_UserGridSize,
        &grid_origin, &m_FastGrid1, &m_FastGrid2,
        m_gridSelectBox->GetStrings() );

    int ret = dlg.ShowModal();

    if( ret == wxID_OK )
    {
        if( GetGridOrigin() != grid_origin && IsType( FRAME_PCB ) )
            OnModify();     // because grid origin is saved in board, show as modified

        SetGridOrigin( grid_origin );

        GetScreen()->AddGrid( m_UserGridSize, m_UserGridUnit, ID_POPUP_GRID_USER );

        // If the user grid is the current option, recall SetGrid()
        // to force new values put in list as current grid value
        if( GetScreen()->GetGridId() == ID_POPUP_GRID_USER )
            GetScreen()->SetGrid( ID_POPUP_GRID_USER  );

        m_canvas->Refresh();

        return true;
    }

    return false;
}
bool PCB_BASE_FRAME::InvokeDialogGrid()
{
    wxPoint grid_origin = GetGridOrigin();

    DIALOG_SET_GRID dlg( this, &m_UserGridUnit, g_UserUnit, &m_UserGridSize,
        &grid_origin, &m_FastGrid1, &m_FastGrid2,
        m_gridSelectBox->GetStrings() );

    int ret = dlg.ShowModal();

    if( ret == wxID_OK )
    {
        if( GetGridOrigin() != grid_origin && IsType( FRAME_PCB ) )
            OnModify();     // because grid origin is saved in board, show as modified

        SetGridOrigin( grid_origin );

        BASE_SCREEN* screen = GetScreen();

        screen->AddGrid( m_UserGridSize, m_UserGridUnit, ID_POPUP_GRID_USER );

        // If the user grid is the current option, recall SetGrid()
        // to force new values put in list as current grid value
        if( screen->GetGridCmdId() == ID_POPUP_GRID_USER )
            screen->SetGrid( ID_POPUP_GRID_USER );

        // Notify GAL
        TOOL_MANAGER* mgr = GetToolManager();

        if( mgr && IsGalCanvasActive() )
        {
            mgr->RunAction( "common.Control.gridPreset", true,
                    screen->GetGridCmdId() - ID_POPUP_GRID_LEVEL_1000 );

            TOOL_EVENT gridOriginUpdate = COMMON_ACTIONS::gridSetOrigin.MakeEvent();
            gridOriginUpdate.SetParameter( new VECTOR2D( grid_origin ) );
            mgr->ProcessEvent( gridOriginUpdate );
        }

        m_canvas->Refresh();

        return true;
    }

    return false;
}
Exemple #3
0
bool PCB_BASE_FRAME::InvokeDialogGrid()
{
    wxPoint grid_origin = GetGridOrigin();

    DIALOG_SET_GRID dlg( this, &m_UserGridUnit, g_UserUnit, &m_UserGridSize,
        &grid_origin, &m_FastGrid1, &m_FastGrid2,
        m_gridSelectBox->GetStrings() );

    int ret = dlg.ShowModal();

    if( ret == wxID_OK )
    {
        if( GetGridOrigin() != grid_origin && IsType( FRAME_PCB ) )
            OnModify();     // because grid origin is saved in board, show as modified

        SetGridOrigin( grid_origin );

        BASE_SCREEN* screen = GetScreen();

        screen->AddGrid( m_UserGridSize, m_UserGridUnit, ID_POPUP_GRID_USER );

        // If the user grid is the current option, recall SetGrid()
        // to force new values put in list as current grid value
        if( screen->GetGridId() == ID_POPUP_GRID_USER )
            screen->SetGrid( ID_POPUP_GRID_USER );

        if( IsGalCanvasActive() )
        {
            GetGalCanvas()->GetGAL()->SetGridSize( VECTOR2D( screen->GetGrid().m_Size.x,
                                                             screen->GetGrid().m_Size.y ) );
            GetGalCanvas()->GetView()->MarkTargetDirty( KIGFX::TARGET_NONCACHED );
        }

        m_canvas->Refresh();

        return true;
    }

    return false;
}
/* OnHotKey.
 *  ** Commands are case insensitive **
 *  Some commands are relative to the item under the mouse cursor
 * aDC = current device context
 * aHotkeyCode = hotkey code (ascii or wxWidget code for special keys)
 * aPosition The cursor position in logical (drawing) units.
 * aItem = NULL or pointer on a EDA_ITEM under the mouse cursor
 */
void PL_EDITOR_FRAME::OnHotKey( wxDC* aDC, int aHotkeyCode,
                                const wxPoint& aPosition, EDA_ITEM* aItem )
{
    bool busy = GetScreen()->GetCurItem() != NULL;
    wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED );
    cmd.SetEventObject( this );

    /* Convert lower to upper case (the usual toupper function has problem with non ascii
     * codes like function keys */
    if( (aHotkeyCode >= 'a') && (aHotkeyCode <= 'z') )
        aHotkeyCode += 'A' - 'a';

    EDA_HOTKEY * HK_Descr = GetDescriptorFromHotkey( aHotkeyCode, s_PlEditor_Hotkey_List );

    if( HK_Descr == NULL )
        HK_Descr = GetDescriptorFromHotkey( aHotkeyCode, s_Common_Hotkey_List );

    if( HK_Descr == NULL )
        return;

    WORKSHEET_DATAITEM* item;

    switch( HK_Descr->m_Idcommand )
    {
    case HK_NOT_FOUND:
        return;

    case HK_LEFT_CLICK:
        OnLeftClick( aDC, aPosition );
        break;

    case HK_LEFT_DCLICK:    // Simulate a double left click: generate 2 events
        OnLeftClick( aDC, aPosition );
        OnLeftDClick( aDC, aPosition );
        break;

    case HK_HELP:       // Display Current hotkey list
        DisplayHotkeyList( this, s_PlEditor_Hokeys_Descr );
        break;

    case HK_UNDO:
    case HK_REDO:
        if( busy )
            break;
        cmd.SetId( HK_Descr->m_IdMenuEvent );
        GetEventHandler()->ProcessEvent( cmd );
        break;

    case HK_ZOOM_IN:
        cmd.SetId( ID_POPUP_ZOOM_IN );
        GetEventHandler()->ProcessEvent( cmd );
        break;

    case HK_ZOOM_OUT:
        cmd.SetId( ID_POPUP_ZOOM_OUT );
        GetEventHandler()->ProcessEvent( cmd );
        break;

    case HK_ZOOM_REDRAW:
        cmd.SetId( ID_ZOOM_REDRAW );
        GetEventHandler()->ProcessEvent( cmd );
        break;

    case HK_ZOOM_CENTER:
        cmd.SetId( ID_POPUP_ZOOM_CENTER );
        GetEventHandler()->ProcessEvent( cmd );
        break;

    case HK_ZOOM_AUTO:
        cmd.SetId( ID_ZOOM_PAGE );
        GetEventHandler()->ProcessEvent( cmd );
        break;

    case HK_RESET_LOCAL_COORD:      // Reset the relative coord
        GetScreen()->m_O_Curseur = GetCrossHairPosition();
        break;

    case HK_SET_GRID_ORIGIN:
        SetGridOrigin( GetCrossHairPosition() );
        break;

    case HK_MOVE_ITEM:
    case HK_MOVE_START_POINT:
    case HK_MOVE_END_POINT:
    case HK_DELETE_ITEM:
        if( busy )
            break;

        if( (item = Locate( aPosition )) == NULL )
            break;

        // Only rect and lines have a end point.
        if( HK_Descr->m_Idcommand == HK_MOVE_END_POINT && !item->HasEndPoint() )
            break;

        if( m_treePagelayout->GetPageLayoutSelectedItem() != item )
            m_treePagelayout->SelectCell( item );

        cmd.SetId( HK_Descr->m_IdMenuEvent );
        GetEventHandler()->ProcessEvent( cmd );
        break;

    case HK_PLACE_ITEM:
        if( busy )
        {
            cmd.SetId( HK_Descr->m_IdMenuEvent );
            GetEventHandler()->ProcessEvent( cmd );
        }
        break;

    default:
        wxMessageBox( wxT("Unknown hotkey") );
        return;
    }
}
bool PCB_EDIT_FRAME::OnHotKey( wxDC* aDC, int aHotkeyCode, const wxPoint& aPosition,
                               EDA_ITEM* aItem )
{
    if( aHotkeyCode == 0 )
        return false;

    bool itemCurrentlyEdited = GetCurItem() && GetCurItem()->GetFlags();
    MODULE* module = NULL;
    int evt_type = 0;       //Used to post a wxCommandEvent on demand
    PCB_SCREEN* screen = GetScreen();
    DISPLAY_OPTIONS* displ_opts = (DISPLAY_OPTIONS*)GetDisplayOptions();

    /* Convert lower to upper case
     * (the usual toupper function has problem with non ascii codes like function keys
     */
    if( (aHotkeyCode >= 'a') && (aHotkeyCode <= 'z') )
        aHotkeyCode += 'A' - 'a';

    EDA_HOTKEY* HK_Descr = GetDescriptorFromHotkey( aHotkeyCode, common_Hotkey_List );

    if( HK_Descr == NULL )
        HK_Descr = GetDescriptorFromHotkey( aHotkeyCode, board_edit_Hotkey_List );

    if( HK_Descr == NULL )
        return false;

    int hk_id = HK_Descr->m_Idcommand;

    if( (m_RecordingMacros != -1) &&
        !( hk_id > HK_MACRO_ID_BEGIN && hk_id < HK_MACRO_ID_END) )
    {
        MACROS_RECORD macros_record;
        macros_record.m_HotkeyCode = aHotkeyCode;
        macros_record.m_Idcommand = HK_Descr->m_Idcommand;
        macros_record.m_Position  = GetNearestGridPosition( aPosition ) -
                                    m_Macros[m_RecordingMacros].m_StartPosition;
        m_Macros[m_RecordingMacros].m_Record.push_back( macros_record );
        wxString msg;
        msg.Printf( _( "Add key [%c] in macro %d" ), aHotkeyCode, m_RecordingMacros );
        SetStatusText( msg );
    }

    // Create a wxCommandEvent that will be posted in some hot keys functions
    wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED );
    cmd.SetEventObject( this );

    LAYER_NUM  ll;

    switch( hk_id )
    {
    default:
    case HK_NOT_FOUND:
        return false;

    case HK_LEFT_CLICK:
        OnLeftClick( aDC, aPosition );
        break;

    case HK_LEFT_DCLICK:    // Simulate a double left click: generate 2 events
        OnLeftClick( aDC, aPosition );
        OnLeftDClick( aDC, aPosition );
        break;

    case HK_RECORD_MACROS_0:
    case HK_RECORD_MACROS_1:
    case HK_RECORD_MACROS_2:
    case HK_RECORD_MACROS_3:
    case HK_RECORD_MACROS_4:
    case HK_RECORD_MACROS_5:
    case HK_RECORD_MACROS_6:
    case HK_RECORD_MACROS_7:
    case HK_RECORD_MACROS_8:
    case HK_RECORD_MACROS_9:
        RecordMacros( aDC, hk_id - HK_RECORD_MACROS_0 );
        break;

    case HK_CALL_MACROS_0:
    case HK_CALL_MACROS_1:
    case HK_CALL_MACROS_2:
    case HK_CALL_MACROS_3:
    case HK_CALL_MACROS_4:
    case HK_CALL_MACROS_5:
    case HK_CALL_MACROS_6:
    case HK_CALL_MACROS_7:
    case HK_CALL_MACROS_8:
    case HK_CALL_MACROS_9:
        CallMacros( aDC, GetCrossHairPosition( false ), hk_id - HK_CALL_MACROS_0 );
        break;

    case HK_SWITCH_TRACK_WIDTH_TO_NEXT:
        if( GetCanvas()->IsMouseCaptured() )
            GetCanvas()->CallMouseCapture( aDC, wxDefaultPosition, false );

        if( GetDesignSettings().GetTrackWidthIndex() < GetDesignSettings().m_TrackWidthList.size() - 1 )
            GetDesignSettings().SetTrackWidthIndex( GetDesignSettings().GetTrackWidthIndex() + 1 );
        else
            GetDesignSettings().SetTrackWidthIndex( 0 );

        if( GetCanvas()->IsMouseCaptured() )
            GetCanvas()->CallMouseCapture( aDC, wxDefaultPosition, false );

        break;

    case HK_SWITCH_TRACK_WIDTH_TO_PREVIOUS:
        if( GetCanvas()->IsMouseCaptured() )
            GetCanvas()->CallMouseCapture( aDC, wxDefaultPosition, false );

        if( GetDesignSettings().GetTrackWidthIndex() <= 0 )
            GetDesignSettings().SetTrackWidthIndex( GetDesignSettings().m_TrackWidthList.size() -1 );
        else
            GetDesignSettings().SetTrackWidthIndex( GetDesignSettings().GetTrackWidthIndex() - 1 );

        if( GetCanvas()->IsMouseCaptured() )
            GetCanvas()->CallMouseCapture( aDC, wxDefaultPosition, false );

        break;

    case HK_SWITCH_GRID_TO_FASTGRID1:
        SetFastGrid1();
        break;

    case HK_SWITCH_GRID_TO_FASTGRID2:
        SetFastGrid2();
        break;

    case HK_SWITCH_GRID_TO_NEXT:
        SetNextGrid();
        break;

    case HK_SWITCH_GRID_TO_PREVIOUS:
        SetPrevGrid();
        break;

    case HK_SWITCH_LAYER_TO_PREVIOUS:
        ll = GetActiveLayer();

        if( !IsCopperLayer( ll ) )
            break;

        if( ll == F_Cu )
            ll = B_Cu;
        else if( ll == B_Cu )
            ll = ToLAYER_ID( GetBoard()->GetCopperLayerCount() - 2 );
        else
            ll = ll - 1;

        SwitchLayer( aDC, ToLAYER_ID( ll ) );
        break;

    case HK_SWITCH_LAYER_TO_NEXT:
        ll = GetActiveLayer();

        if( !IsCopperLayer( ll ) )
            break;

        if( ll == B_Cu )
            ll = F_Cu;
        else if( ++ll >= GetBoard()->GetCopperLayerCount() - 1 )
            ll = B_Cu;

        SwitchLayer( aDC, ToLAYER_ID( ll ) );
        break;

    case HK_SWITCH_LAYER_TO_COMPONENT:
        SwitchLayer( aDC, F_Cu );
        break;

    case HK_SWITCH_LAYER_TO_COPPER:
        SwitchLayer( aDC, B_Cu );
        break;

    case HK_SWITCH_LAYER_TO_INNER1:
        SwitchLayer( aDC, In1_Cu );
        break;

    case HK_SWITCH_LAYER_TO_INNER2:
        SwitchLayer( aDC, In2_Cu );
        break;

    case HK_SWITCH_LAYER_TO_INNER3:
        SwitchLayer( aDC, In3_Cu );
        break;

    case HK_SWITCH_LAYER_TO_INNER4:
        SwitchLayer( aDC, In4_Cu );
        break;

    case HK_SWITCH_LAYER_TO_INNER5:
        SwitchLayer( aDC, In5_Cu );
        break;

    case HK_SWITCH_LAYER_TO_INNER6:
        SwitchLayer( aDC, In6_Cu );
        break;

    case HK_HELP: // Display Current hotkey list
        DisplayHotkeyList( this, g_Board_Editor_Hokeys_Descr );
        break;

    case HK_ZOOM_IN:
        evt_type = ID_POPUP_ZOOM_IN;
        break;

    case HK_ZOOM_OUT:
        evt_type = ID_POPUP_ZOOM_OUT;
        break;

    case HK_ZOOM_REDRAW:
        evt_type = ID_ZOOM_REDRAW;
        break;

    case HK_ZOOM_AUTO:
        evt_type = ID_ZOOM_PAGE;
        break;

    case HK_ZOOM_CENTER:
        evt_type = ID_POPUP_ZOOM_CENTER;
        break;

    case HK_ADD_MODULE:
        evt_type = ID_PCB_MODULE_BUTT;
        break;

    case HK_UNDO:
    case HK_REDO:
        if( !itemCurrentlyEdited )
        {
            wxCommandEvent event( wxEVT_COMMAND_TOOL_CLICKED, HK_Descr->m_IdMenuEvent );
            wxPostEvent( this, event );
        }

        break;

    case HK_RESET_LOCAL_COORD:  // Set the relative coord
        GetScreen()->m_O_Curseur = GetCrossHairPosition();
        break;

    case HK_SET_GRID_ORIGIN:
        SetGridOrigin( GetCrossHairPosition() );
        OnModify();     // because grid origin is saved in board, show as modified
        m_canvas->Refresh();
        break;

    case HK_RESET_GRID_ORIGIN:
        SetGridOrigin( wxPoint( 0,0 ) );
        OnModify();     // because grid origin is saved in board, show as modified
        m_canvas->Refresh();
        break;

    case HK_SWITCH_UNITS:
        evt_type = (g_UserUnit == INCHES) ?
                    ID_TB_OPTIONS_SELECT_UNIT_MM : ID_TB_OPTIONS_SELECT_UNIT_INCH;
        break;

    case HK_SWITCH_TRACK_DISPLAY_MODE:
        displ_opts->m_DisplayPcbTrackFill = !displ_opts->m_DisplayPcbTrackFill;
        m_canvas->Refresh();
        break;

    case HK_DELETE:
        OnHotkeyDeleteItem( aDC );
        break;

    case HK_BACK_SPACE:
        if( IsCopperLayer( GetActiveLayer() ) )
        {
            if( !itemCurrentlyEdited )
            {
                // no track is currently being edited - select a segment and remove it.
                // @todo: possibly? pass the HK command code to PcbGeneralLocateAndDisplay()
                // so it can restrict its search to specific item types.
                BOARD_ITEM * item = PcbGeneralLocateAndDisplay();

                // don't let backspace delete modules!!
                if( item && item->IsTrack() )
                {
                    Delete_Segment( aDC, (TRACK*) item );
                    SetCurItem( NULL );
                }

                OnModify();
            }
            else if( GetCurItem()->IsTrack() )
            {
                // then an element is being edited - remove the last segment.
                // simple lines for debugger:
                TRACK* track = (TRACK*) GetCurItem();
                track = Delete_Segment( aDC, track );
                SetCurItem( track );
                OnModify();
            }
        }

        break;

    case HK_GET_AND_MOVE_FOOTPRINT:
        if( !itemCurrentlyEdited )
            evt_type = ID_POPUP_PCB_GET_AND_MOVE_MODULE_REQUEST;

        break;

    case HK_FIND_ITEM:
        if( !itemCurrentlyEdited )
            evt_type = ID_FIND_ITEMS;

        break;

    case HK_LOAD_BOARD:
        if( !itemCurrentlyEdited )
            evt_type = ID_LOAD_FILE ;

        break;

    case HK_SAVE_BOARD:
        if( !itemCurrentlyEdited )
            evt_type = ID_SAVE_BOARD;

        break;

    case HK_ADD_MICROVIA: // Place a micro via if a track is in progress
        if( GetToolId() != ID_TRACK_BUTT )
            return true;

        if( !itemCurrentlyEdited )                         // no track in progress: nothing to do
            break;

        if( GetCurItem()->Type() != PCB_TRACE_T )           // Should not occur
            return true;

        if( !GetCurItem()->IsNew() )
            return true;

        // place micro via and switch layer
        if( IsMicroViaAcceptable() )
            evt_type = ID_POPUP_PCB_PLACE_MICROVIA;

        break;

    case HK_ADD_BLIND_BURIED_VIA:
    case HK_ADD_THROUGH_VIA: // Switch to alternate layer and Place a via if a track is in progress
        if( GetBoard()->GetDesignSettings().m_BlindBuriedViaAllowed &&
            hk_id == HK_ADD_BLIND_BURIED_VIA  )
            GetBoard()->GetDesignSettings().m_CurrentViaType = VIA_BLIND_BURIED;
        else
            GetBoard()->GetDesignSettings().m_CurrentViaType = VIA_THROUGH;

        if( !itemCurrentlyEdited ) // no track in progress: switch layer only
        {
            Other_Layer_Route( NULL, aDC );
            if( displ_opts->m_ContrastModeDisplay )
                m_canvas->Refresh();
            break;
        }

        if( GetToolId() != ID_TRACK_BUTT )
            return true;

        if( GetCurItem()->Type() != PCB_TRACE_T )
            return true;

        if( !GetCurItem()->IsNew() )
            return true;

        evt_type = hk_id == HK_ADD_BLIND_BURIED_VIA ?
            ID_POPUP_PCB_PLACE_BLIND_BURIED_VIA : ID_POPUP_PCB_PLACE_THROUGH_VIA;
        break;

    case HK_SEL_LAYER_AND_ADD_THROUGH_VIA:
    case HK_SEL_LAYER_AND_ADD_BLIND_BURIED_VIA:
        if( GetCurItem() == NULL || !GetCurItem()->IsNew() ||
            GetCurItem()->Type() != PCB_TRACE_T )
            break;

        evt_type = hk_id == HK_SEL_LAYER_AND_ADD_BLIND_BURIED_VIA ?
            ID_POPUP_PCB_SELECT_CU_LAYER_AND_PLACE_BLIND_BURIED_VIA :
            ID_POPUP_PCB_SELECT_CU_LAYER_AND_PLACE_THROUGH_VIA;
        break;

    case HK_SWITCH_TRACK_POSTURE:
        /* change the position of initial segment when creating new tracks
         * switch from _/  to -\ .
         */
        evt_type = ID_POPUP_PCB_SWITCH_TRACK_POSTURE ;
        break;

    case HK_DRAG_TRACK_KEEP_SLOPE:
        OnHotkeyMoveItem( HK_DRAG_TRACK_KEEP_SLOPE );
        break;

    case HK_PLACE_ITEM:
        OnHotkeyPlaceItem( aDC );
        break;

    case HK_ADD_NEW_TRACK: // Start new track, if possible
        OnHotkeyBeginRoute( aDC );
        break;

    case HK_EDIT_ITEM:      // Edit board item
        OnHotkeyEditItem( HK_EDIT_ITEM );
        break;

    case HK_EDIT_MODULE_WITH_MODEDIT:      // Edit module with module editor
        OnHotkeyEditItem( HK_EDIT_MODULE_WITH_MODEDIT );
        break;

    // Footprint edition:
    case HK_LOCK_UNLOCK_FOOTPRINT: // toggle module "MODULE_is_LOCKED" status:
        // get any module, locked or not locked and toggle its locked status
        if( !itemCurrentlyEdited )
        {
            wxPoint pos = RefPos( true );
            module = GetBoard()->GetFootprint( pos, screen->m_Active_Layer, true );
        }
        else if( GetCurItem()->Type() == PCB_MODULE_T )
        {
            module = (MODULE*) GetCurItem();
        }

        if( module )
        {
            SetCurItem( module );
            module->SetLocked( !module->IsLocked() );
            OnModify();
            SetMsgPanel( module );
        }
        break;

    case HK_DRAG_ITEM:    // Start drag module or track segment
        OnHotkeyMoveItem( HK_DRAG_ITEM );
        break;

    case HK_MOVE_ITEM:                  // Start move item
        OnHotkeyMoveItem( HK_MOVE_ITEM );
        break;

    case HK_COPY_ITEM:
        evt_type = OnHotkeyCopyItem();
        break;

    case HK_ROTATE_ITEM:        // Rotation
        OnHotkeyRotateItem( HK_ROTATE_ITEM );
        break;

    case HK_FLIP_ITEM:
        OnHotkeyRotateItem( HK_FLIP_ITEM );
        break;

    case HK_MOVE_ITEM_EXACT:
    case HK_DUPLICATE_ITEM:
    case HK_DUPLICATE_ITEM_AND_INCREMENT:
    case HK_CREATE_ARRAY:
        OnHotkeyDuplicateOrArrayItem( HK_Descr->m_Idcommand );
        break;

    case HK_SWITCH_HIGHCONTRAST_MODE: // switch to high contrast mode and refresh the canvas
        displ_opts->m_ContrastModeDisplay = !displ_opts->m_ContrastModeDisplay;
        m_canvas->Refresh();
        break;

    case HK_CANVAS_CAIRO:
        evt_type = ID_MENU_CANVAS_CAIRO;
        break;

    case HK_CANVAS_OPENGL:
        evt_type = ID_MENU_CANVAS_OPENGL;
        break;

    case HK_CANVAS_LEGACY:
        evt_type = ID_MENU_CANVAS_LEGACY;
        break;
    case HK_ZONE_FILL_OR_REFILL:
        evt_type = ID_POPUP_PCB_FILL_ALL_ZONES;
        break;
    case HK_ZONE_REMOVE_FILLED:
        evt_type = ID_POPUP_PCB_REMOVE_FILLED_AREAS_IN_ALL_ZONES;
        break;

    }

    if( evt_type != 0 )
    {
        wxCommandEvent evt( wxEVT_COMMAND_MENU_SELECTED );
        evt.SetEventObject( this );
        evt.SetId( evt_type );
        GetEventHandler()->ProcessEvent( evt );
    }

    return true;
}
bool FOOTPRINT_EDIT_FRAME::OnHotKey( wxDC* aDC, int aHotKey, const wxPoint& aPosition,
                                     EDA_ITEM* aItem )
{
    if( aHotKey == 0 )
        return false;

    bool           blockActive = GetScreen()->m_BlockLocate.GetCommand() != BLOCK_IDLE;
    BOARD_ITEM*    item     = GetCurItem();
    bool           ItemFree = (item == 0) || (item->GetFlags() == 0);
    wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED );
    cmd.SetEventObject( this );

    /* Convert lower to upper case (the usual toupper function has problem with non ascii
     * codes like function keys */
    if( (aHotKey >= 'a') && (aHotKey <= 'z') )
        aHotKey += 'A' - 'a';

    EDA_HOTKEY* HK_Descr = GetDescriptorFromHotkey( aHotKey, common_Hotkey_List );

    if( HK_Descr == NULL )
        HK_Descr = GetDescriptorFromHotkey( aHotKey, module_edit_Hotkey_List );

    if( HK_Descr == NULL )
        return false;

    switch( HK_Descr->m_Idcommand )
    {
    default:
    case HK_NOT_FOUND:
        return false;

    case HK_HELP:                   // Display Current hotkey list
        DisplayHotkeyList( this, g_Module_Editor_Hokeys_Descr );
        break;

    case HK_RESET_LOCAL_COORD:      // set local (relative) coordinate origin
        GetScreen()->m_O_Curseur = GetCrossHairPosition();
        break;

    case HK_LEFT_CLICK:
        OnLeftClick( aDC, aPosition );
        break;

    case HK_LEFT_DCLICK:    // Simulate a double left click: generate 2 events
        OnLeftClick( aDC, aPosition );
        OnLeftDClick( aDC, aPosition );
        break;

    case HK_SET_GRID_ORIGIN:
        SetGridOrigin( GetCrossHairPosition() );
        m_canvas->Refresh();
        break;

    case HK_RESET_GRID_ORIGIN:
        SetGridOrigin( wxPoint(0,0) );
        m_canvas->Refresh();
        break;

    case HK_SWITCH_UNITS:
        cmd.SetId( (g_UserUnit == INCHES) ?
                    ID_TB_OPTIONS_SELECT_UNIT_MM : ID_TB_OPTIONS_SELECT_UNIT_INCH );
        GetEventHandler()->ProcessEvent( cmd );
        break;

    case HK_ZOOM_IN:
        cmd.SetId( ID_POPUP_ZOOM_IN );
        GetEventHandler()->ProcessEvent( cmd );
        break;

    case HK_ZOOM_OUT:
        cmd.SetId( ID_POPUP_ZOOM_OUT );
        GetEventHandler()->ProcessEvent( cmd );
        break;

    case HK_ZOOM_REDRAW:
        cmd.SetId( ID_ZOOM_REDRAW );
        GetEventHandler()->ProcessEvent( cmd );
        break;

    case HK_ZOOM_CENTER:
        cmd.SetId( ID_POPUP_ZOOM_CENTER );
        GetEventHandler()->ProcessEvent( cmd );
        break;

    case HK_UNDO:
    case HK_REDO:
        if( ItemFree && !blockActive )
        {
            wxCommandEvent event( wxEVT_COMMAND_TOOL_CLICKED, HK_Descr->m_IdMenuEvent );
            wxPostEvent( this, event );
        }
        break;

    case HK_ZOOM_AUTO:
        cmd.SetId( ID_ZOOM_PAGE );
        GetEventHandler()->ProcessEvent( cmd );
        break;

    case HK_EDIT_ITEM:
        OnHotkeyEditItem( HK_EDIT_ITEM );
        break;

    case HK_DELETE:
        OnHotkeyDeleteItem( HK_DELETE );
        break;

    case HK_MOVE_ITEM:
        OnHotkeyMoveItem( HK_MOVE_ITEM );
        break;

    case HK_ROTATE_ITEM:
        OnHotkeyRotateItem( HK_ROTATE_ITEM );
        break;
    }

    return true;
}
void FOOTPRINT_EDIT_FRAME::OnLeftClick( wxDC* DC, const wxPoint& MousePos )
{
    BOARD_ITEM* item = GetCurItem();

    m_canvas->CrossHairOff( DC );

    if( GetToolId() == ID_NO_TOOL_SELECTED )
    {
        if( item && item->GetFlags() ) // Move item command in progress
        {
            switch( item->Type() )
            {
            case PCB_MODULE_TEXT_T:
                PlaceTexteModule( static_cast<TEXTE_MODULE*>( item ), DC );
                break;

            case PCB_MODULE_EDGE_T:
                SaveCopyInUndoList( GetBoard()->m_Modules, UR_MODEDIT );
                Place_EdgeMod( static_cast<EDGE_MODULE*>( item ) );
                break;

            case PCB_PAD_T:
                PlacePad( static_cast<D_PAD*>( item ), DC );
                break;

            default:
            {
                wxString msg;
                msg.Printf( wxT( "WinEDA_ModEditFrame::OnLeftClick err:Struct %d, m_Flag %X" ),
                            item->Type(), item->GetFlags() );
                DisplayError( this, msg );
                item->ClearFlags();
                break;
            }
            }
        }

        else
        {
            if( !wxGetKeyState( WXK_SHIFT ) && !wxGetKeyState( WXK_ALT )
                    && !wxGetKeyState( WXK_CONTROL ) )
                item = ModeditLocateAndDisplay();

            SetCurItem( item );
        }
    }

    item = GetCurItem();
    bool no_item_edited = item == NULL || item->GetFlags() == 0;

    switch( GetToolId() )
    {
    case ID_NO_TOOL_SELECTED:
        break;

    case ID_MODEDIT_CIRCLE_TOOL:
    case ID_MODEDIT_ARC_TOOL:
    case ID_MODEDIT_LINE_TOOL:
        if( no_item_edited )
        {
            STROKE_T shape = S_SEGMENT;

            if( GetToolId() == ID_MODEDIT_CIRCLE_TOOL )
                shape = S_CIRCLE;

            if( GetToolId() == ID_MODEDIT_ARC_TOOL )
                shape = S_ARC;

            SetCurItem( Begin_Edge_Module( (EDGE_MODULE*) NULL, DC, shape ) );
        }
        else if( item->IsNew() )
        {
            if( ( (EDGE_MODULE*) item )->GetShape() == S_CIRCLE )
            {
                End_Edge_Module( (EDGE_MODULE*) item );
                SetCurItem( NULL );
                m_canvas->Refresh();
            }
            else if( ( (EDGE_MODULE*) item )->GetShape() == S_ARC )
            {
                End_Edge_Module( (EDGE_MODULE*) item );
                SetCurItem( NULL );
                m_canvas->Refresh();
            }
            else if( ( (EDGE_MODULE*) item )->GetShape() == S_SEGMENT )
            {
                SetCurItem( Begin_Edge_Module( (EDGE_MODULE*) item, DC, S_SEGMENT ) );
            }
            else
            {
                wxMessageBox( wxT( "ProcessCommand error: unknown shape" ) );
            }
        }
        break;

    case ID_MODEDIT_DELETE_TOOL:
        if( ! no_item_edited )    // Item in edit, cannot delete it
            break;

        item = ModeditLocateAndDisplay();

        if( item && item->Type() != PCB_MODULE_T ) // Cannot delete the module itself
        {
            SaveCopyInUndoList( GetBoard()->m_Modules, UR_MODEDIT );
            RemoveStruct( item );
            SetCurItem( NULL );
        }

        break;

    case ID_MODEDIT_ANCHOR_TOOL:
    {
        MODULE* module = GetBoard()->m_Modules;

        if( module == NULL    // No module loaded
                || (module->GetFlags() != 0) )
            break;

        SaveCopyInUndoList( module, UR_MODEDIT );

        // set the new relative internal local coordinates of footprint items
        wxPoint moveVector = module->GetPosition() - GetCrossHairPosition();
        module->MoveAnchorPosition( moveVector );

        // Usually, we do not need to change twice the anchor position,
        // so deselect the active tool
        SetToolID( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor(), wxEmptyString );
        SetCurItem( NULL );
        m_canvas->Refresh();
    }
    break;

    case ID_MODEDIT_PLACE_GRID_COORD:
        m_canvas->DrawGridAxis( DC, GR_XOR, GetBoard()->GetGridOrigin() );
        SetGridOrigin( GetCrossHairPosition() );
        m_canvas->DrawGridAxis( DC, GR_COPY, GetBoard()->GetGridOrigin() );
        GetScreen()->SetModify();
        break;

    case ID_MODEDIT_TEXT_TOOL:
        if( GetBoard()->m_Modules == NULL )
            break;

        SaveCopyInUndoList( GetBoard()->m_Modules, UR_MODEDIT );
        CreateTextModule( GetBoard()->m_Modules, DC );
        break;

    case ID_MODEDIT_PAD_TOOL:
        if( GetBoard()->m_Modules )
        {
            SaveCopyInUndoList( GetBoard()->m_Modules, UR_MODEDIT );
            AddPad( GetBoard()->m_Modules, true );
        }

        break;

    default:
        DisplayError( this, wxT( "FOOTPRINT_EDIT_FRAME::ProcessCommand error" ) );
        SetToolID( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor(), wxEmptyString );
    }

    m_canvas->CrossHairOn( DC );
}
/* Handle the left button mouse click, when a tool is active
 */
void PCB_EDIT_FRAME::OnLeftClick( wxDC* aDC, const wxPoint& aPosition )
{
    BOARD_ITEM* DrawStruct = GetCurItem();
    bool        exit = false;
    bool no_tool = GetToolId() == ID_NO_TOOL_SELECTED;

    if( no_tool || ( DrawStruct && DrawStruct->GetFlags() ) )
    {
        m_canvas->SetAutoPanRequest( false );

        if( DrawStruct && DrawStruct->GetFlags() ) // Command in progress
        {
            m_canvas->SetIgnoreMouseEvents( true );
            m_canvas->CrossHairOff( aDC );

            switch( DrawStruct->Type() )
            {
            case PCB_ZONE_AREA_T:
                if( DrawStruct->IsNew() )
                {
                    m_canvas->SetAutoPanRequest( true );
                    Begin_Zone( aDC );
                }
                else
                {
                    End_Move_Zone_Corner_Or_Outlines( aDC, (ZONE_CONTAINER*) DrawStruct );
                }

                exit = true;
                break;

            case PCB_TRACE_T:
            case PCB_VIA_T:
                if( DrawStruct->IsDragging() )
                {
                    PlaceDraggedOrMovedTrackSegment( (TRACK*) DrawStruct, aDC );
                    exit = true;
                }

                break;

            case PCB_TEXT_T:
                Place_Texte_Pcb( (TEXTE_PCB*) DrawStruct, aDC );
                exit = true;
                break;

            case PCB_MODULE_TEXT_T:
                PlaceTexteModule( (TEXTE_MODULE*) DrawStruct, aDC );
                exit = true;
                break;

            case PCB_PAD_T:
                PlacePad( (D_PAD*) DrawStruct, aDC );
                exit = true;
                break;

            case PCB_MODULE_T:
                PlaceModule( (MODULE*) DrawStruct, aDC );
                exit = true;
                break;

            case PCB_TARGET_T:
                PlaceTarget( (PCB_TARGET*) DrawStruct, aDC );
                exit = true;
                break;

            case PCB_LINE_T:
                if( no_tool )   // when no tools: existing item moving.
                {
                    Place_DrawItem( (DRAWSEGMENT*) DrawStruct, aDC );
                    exit = true;
                }

                break;

            case PCB_DIMENSION_T:
                if( ! DrawStruct->IsNew() )
                {   // We are moving the text of an existing dimension. Place it
                    PlaceDimensionText( (DIMENSION*) DrawStruct, aDC );
                    exit = true;
                }
                break;

            default:
                DisplayError( this,
                              wxT( "PCB_EDIT_FRAME::OnLeftClick() err: DrawType %d m_Flags != 0" ),
                              DrawStruct->Type() );
                exit = true;
                break;
            }

            m_canvas->SetIgnoreMouseEvents( false );
            m_canvas->CrossHairOn( aDC );

            if( exit )
                return;
        }
        else if( !wxGetKeyState( WXK_SHIFT ) && !wxGetKeyState( WXK_ALT )
                && !wxGetKeyState( WXK_CONTROL ) )
        {
            DrawStruct = PcbGeneralLocateAndDisplay();

            if( DrawStruct )
                SendMessageToEESCHEMA( DrawStruct );
        }
    }

    if( DrawStruct ) // display netclass info for zones, tracks and pads
    {
        switch( DrawStruct->Type() )
        {
        case PCB_ZONE_AREA_T:
        case PCB_TRACE_T:
        case PCB_VIA_T:
        case PCB_PAD_T:
            GetDesignSettings().SetCurrentNetClass(
                ((BOARD_CONNECTED_ITEM*)DrawStruct)->GetNetClassName() );
            updateTraceWidthSelectBox();
            updateViaSizeSelectBox();
            break;

        default:
           break;
        }
    }

    switch( GetToolId() )
    {
    case ID_MAIN_MENUBAR:
    case ID_NO_TOOL_SELECTED:
        break;

    case ID_PCB_MUWAVE_TOOL_SELF_CMD:
    case ID_PCB_MUWAVE_TOOL_GAP_CMD:
    case ID_PCB_MUWAVE_TOOL_STUB_CMD:
    case ID_PCB_MUWAVE_TOOL_STUB_ARC_CMD:
    case ID_PCB_MUWAVE_TOOL_FUNCTION_SHAPE_CMD:
        MuWaveCommand( aDC, aPosition );
        break;

    case ID_PCB_HIGHLIGHT_BUTT:
    {
        int netcode = SelectHighLight( aDC );

        if( netcode < 0 )
            SetMsgPanel( GetBoard() );
        else
        {
            NETINFO_ITEM* net = GetBoard()->FindNet( netcode );

            if( net )
            {
                MSG_PANEL_ITEMS items;
                net->GetMsgPanelInfo( items );
                SetMsgPanel( items );
            }
        }
    }
    break;

    case ID_PCB_SHOW_1_RATSNEST_BUTT:
        DrawStruct = PcbGeneralLocateAndDisplay();
        Show_1_Ratsnest( DrawStruct, aDC );

        if( DrawStruct )
            SendMessageToEESCHEMA( DrawStruct );

        break;

    case ID_PCB_MIRE_BUTT:
        if( (DrawStruct == NULL) || (DrawStruct->GetFlags() == 0) )
        {
            SetCurItem( (BOARD_ITEM*) CreateTarget( aDC ) );
            m_canvas->MoveCursorToCrossHair();
        }
        else if( DrawStruct->Type() == PCB_TARGET_T )
        {
            PlaceTarget( (PCB_TARGET*) DrawStruct, aDC );
        }
        else
        {
            DisplayError( this, wxT( "OnLeftClick err: not a PCB_TARGET_T" ) );
        }

        break;

    case ID_PCB_CIRCLE_BUTT:
    case ID_PCB_ARC_BUTT:
    case ID_PCB_ADD_LINE_BUTT:
        {
            STROKE_T shape = S_SEGMENT;

            if( GetToolId() == ID_PCB_CIRCLE_BUTT )
                shape = S_CIRCLE;

            if( GetToolId() == ID_PCB_ARC_BUTT )
                shape = S_ARC;

            if( IsCopperLayer( GetActiveLayer() ) )
            {
                DisplayError( this, _( "Graphic not allowed on Copper layers" ) );
                break;
            }

            if( (DrawStruct == NULL) || (DrawStruct->GetFlags() == 0) )
            {
                DrawStruct = (BOARD_ITEM*) Begin_DrawSegment( NULL, shape, aDC );
                SetCurItem( DrawStruct );
                m_canvas->SetAutoPanRequest( true );
            }
            else if( DrawStruct
                   && (DrawStruct->Type() == PCB_LINE_T)
                   && DrawStruct->IsNew() )
            {
                DrawStruct = (BOARD_ITEM*) Begin_DrawSegment( (DRAWSEGMENT*) DrawStruct, shape, aDC );
                SetCurItem( DrawStruct );
                m_canvas->SetAutoPanRequest( true );
            }
        }
        break;

    case ID_TRACK_BUTT:
        if( !IsCopperLayer( GetActiveLayer() ) )
        {
            DisplayError( this, _( "Tracks on Copper layers only " ) );
            break;
        }

        if( (DrawStruct == NULL) || (DrawStruct->GetFlags() == 0) )
        {
            DrawStruct = (BOARD_ITEM*) Begin_Route( NULL, aDC );
            SetCurItem( DrawStruct );

            if( DrawStruct )
                m_canvas->SetAutoPanRequest( true );
        }
        else if( DrawStruct && DrawStruct->IsNew() )
        {
            TRACK* track = Begin_Route( (TRACK*) DrawStruct, aDC );

            // SetCurItem() must not write to the msg panel
            // because a track info is displayed while moving the mouse cursor
            if( track )  // A new segment was created
                SetCurItem( DrawStruct = (BOARD_ITEM*) track, false );

            m_canvas->SetAutoPanRequest( true );
        }

        break;

    case ID_PCB_ZONES_BUTT:
    case ID_PCB_KEEPOUT_AREA_BUTT:
        /* ZONE or KEEPOUT Tool is selected. Determine action for a left click:
         *  this can be start a new zone or select and move an existing zone outline corner
         *  if found near the mouse cursor
         */
        if( (DrawStruct == NULL) || (DrawStruct->GetFlags() == 0) )
        {
            if( Begin_Zone( aDC ) )
            {
                m_canvas->SetAutoPanRequest( true );
                DrawStruct = GetBoard()->m_CurrentZoneContour;
                GetScreen()->SetCurItem( DrawStruct );
            }
        }
        else if( DrawStruct && (DrawStruct->Type() == PCB_ZONE_AREA_T) && DrawStruct->IsNew() )
        {   // Add a new corner to the current outline being created:
            m_canvas->SetAutoPanRequest( true );
            Begin_Zone( aDC );
            DrawStruct = GetBoard()->m_CurrentZoneContour;
            GetScreen()->SetCurItem( DrawStruct );
        }
        else
        {
            DisplayError( this, wxT( "PCB_EDIT_FRAME::OnLeftClick() zone internal error" ) );
        }

        break;

    case ID_PCB_ADD_TEXT_BUTT:
        if( IsLayerInList( EDGE_LAYER, GetActiveLayer() ) )
        {
            DisplayError( this,
                          _( "Texts not allowed on Edge Cut layer" ) );
            break;
        }

        if( (DrawStruct == NULL) || (DrawStruct->GetFlags() == 0) )
        {
            SetCurItem( CreateTextePcb( aDC ) );
            m_canvas->MoveCursorToCrossHair();
            m_canvas->SetAutoPanRequest( true );
        }
        else if( DrawStruct->Type() == PCB_TEXT_T )
        {
            Place_Texte_Pcb( (TEXTE_PCB*) DrawStruct, aDC );
            m_canvas->SetAutoPanRequest( false );
        }
        else
        {
            DisplayError( this, wxT( "OnLeftClick err: not a PCB_TEXT_T" ) );
        }

        break;

    case ID_PCB_MODULE_BUTT:
        if( (DrawStruct == NULL) || (DrawStruct->GetFlags() == 0) )
        {
            m_canvas->MoveCursorToCrossHair();
            DrawStruct = (BOARD_ITEM*) LoadModuleFromLibrary(
                    wxEmptyString, Prj().PcbFootprintLibs(), true, aDC );

            SetCurItem( DrawStruct );

            if( DrawStruct )
                StartMoveModule( (MODULE*) DrawStruct, aDC, false );
        }
        else if( DrawStruct->Type() == PCB_MODULE_T )
        {
            PlaceModule( (MODULE*) DrawStruct, aDC );
            m_canvas->SetAutoPanRequest( false );
        }
        else
        {
            DisplayError( this, wxT( "Internal err: Struct not PCB_MODULE_T" ) );
        }

        break;

    case ID_PCB_DIMENSION_BUTT:
        if( IsLayerInList( EDGE_LAYER|ALL_CU_LAYERS, GetActiveLayer() ) )
        {
            DisplayError( this,
                          _( "Dimension not allowed on Copper or Edge Cut layers" ) );
            break;
        }

        if( (DrawStruct == NULL) || (DrawStruct->GetFlags() == 0) )
        {
            DrawStruct = (BOARD_ITEM*) EditDimension( NULL, aDC );
            SetCurItem( DrawStruct );
            m_canvas->SetAutoPanRequest( true );
        }
        else if( DrawStruct && (DrawStruct->Type() == PCB_DIMENSION_T) && DrawStruct->IsNew() )
        {
            DrawStruct = (BOARD_ITEM*) EditDimension( (DIMENSION*) DrawStruct, aDC );
            SetCurItem( DrawStruct );
            m_canvas->SetAutoPanRequest( true );
        }
        else
        {
            DisplayError( this,
                          wxT( "PCB_EDIT_FRAME::OnLeftClick() error item is not a DIMENSION" ) );
        }

        break;

    case ID_PCB_DELETE_ITEM_BUTT:
        if( !DrawStruct || !DrawStruct->GetFlags() )
        {
            DrawStruct = PcbGeneralLocateAndDisplay();

            if( DrawStruct && (DrawStruct->GetFlags() == 0) )
            {
                RemoveStruct( DrawStruct, aDC );
                SetCurItem( DrawStruct = NULL );
            }
        }

        break;

    case ID_PCB_PLACE_OFFSET_COORD_BUTT:
        m_canvas->DrawAuxiliaryAxis( aDC, GR_XOR );
        SetAuxOrigin( GetCrossHairPosition() );
        m_canvas->DrawAuxiliaryAxis( aDC, GR_COPY );
        OnModify();
        break;

    case ID_PCB_PLACE_GRID_COORD_BUTT:
        m_canvas->DrawGridAxis( aDC, GR_XOR, GetBoard()->GetGridOrigin() );
        SetGridOrigin( GetCrossHairPosition() );
        m_canvas->DrawGridAxis( aDC, GR_COPY, GetBoard()->GetGridOrigin() );
        break;

    default:
        DisplayError( this, wxT( "PCB_EDIT_FRAME::OnLeftClick() id error" ) );
        SetToolID( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor(), wxEmptyString );
        break;
    }
}
void PL_EDITOR_FRAME::UpdateStatusBar()
{
    PL_EDITOR_SCREEN* screen = (PL_EDITOR_SCREEN*) GetScreen();

    if( !screen )
        return;

    // Display Zoom level:
    EDA_DRAW_FRAME::UpdateStatusBar();

    // coordinate origin can be the paper Top Left corner, or each of 4 page corners
    wxPoint originCoord;
    int Xsign = 1;
    int Ysign = 1;

    WS_DATA_ITEM dummy( WS_DATA_ITEM::WS_SEGMENT );

    switch( m_originSelectChoice )
    {
    default:
    case 0: // Origin = paper Left Top corner
        break;

    case 1: // Origin = page Right Bottom corner
        Xsign = -1;
        Ysign = -1;
        dummy.SetStart( 0, 0, RB_CORNER );
        originCoord = dummy.GetStartPosUi();
        break;

    case 2: // Origin = page Left Bottom corner
        Ysign = -1;
        dummy.SetStart( 0, 0, LB_CORNER );
        originCoord = dummy.GetStartPosUi();
        break;

    case 3: // Origin = page Right Top corner
        Xsign = -1;
        dummy.SetStart( 0, 0, RT_CORNER );
        originCoord = dummy.GetStartPosUi();
        break;

    case 4: // Origin = page Left Top corner
        dummy.SetStart( 0, 0, LT_CORNER );
        originCoord = dummy.GetStartPosUi();
        break;
    }

    SetGridOrigin( originCoord );

    // Display absolute coordinates:
    wxPoint coord = GetCrossHairPosition() - originCoord;
    double dXpos = To_User_Unit( GetUserUnits(), coord.x*Xsign );
    double dYpos = To_User_Unit( GetUserUnits(), coord.y*Ysign );

    wxString pagesizeformatter = _( "Page size: width %.4g height %.4g" );
    wxString absformatter = wxT( "X %.4g  Y %.4g" );
    wxString locformatter = wxT( "dx %.4g  dy %.4g" );

    switch( GetUserUnits() )
    {
    case INCHES:         SetStatusText( _("inches"), 5 );   break;
    case MILLIMETRES:    SetStatusText( _("mm"), 5 );       break;
    case UNSCALED_UNITS: SetStatusText( wxEmptyString, 5 ); break;
    case DEGREES:        wxASSERT( false );                 break;
    }

    wxString line;

    // Display page size
    #define MILS_TO_MM (25.4/1000)
    DSIZE size = GetPageSettings().GetSizeMils();
    size = size * MILS_TO_MM;
    line.Printf( pagesizeformatter, size.x, size.y );
    SetStatusText( line, 0 );

    // Display abs coordinates
    line.Printf( absformatter, dXpos, dYpos );
    SetStatusText( line, 2 );

    // Display relative coordinates:
    int dx = GetCrossHairPosition().x - screen->m_O_Curseur.x;
    int dy = GetCrossHairPosition().y - screen->m_O_Curseur.y;
    dXpos = To_User_Unit( GetUserUnits(), dx * Xsign );
    dYpos = To_User_Unit( GetUserUnits(), dy * Ysign );
    line.Printf( locformatter, dXpos, dYpos );
    SetStatusText( line, 3 );

    // Display corner reference for coord origin
    line.Printf( _("coord origin: %s"),
                m_originSelectBox->GetString( m_originSelectChoice ). GetData() );
    SetStatusText( line, 4 );

    // Display units
}