schedit.cpp 30.9 KB
Newer Older
1
 /*
2 3
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
4 5 6
 * Copyright (C) 2013 Jean-Pierre Charras, jp.charras at wanadoo.fr
 * Copyright (C) 2008-2013 Wayne Stambaugh <stambaughw@verizon.net>
 * Copyright (C) 2004-2013 KiCad Developers, see change_log.txt for contributors.
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you may find one here:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * or you may search the http://www.gnu.org website for the version 2 license,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

26 27 28
/**
 * @file schedit.cpp
 */
plyatov's avatar
plyatov committed
29

30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
#include <fctsys.h>
#include <gr_basic.h>
#include <appl_wxstruct.h>
#include <class_drawpanel.h>
#include <confirm.h>
#include <eda_doc.h>
#include <wxEeschemaStruct.h>
#include <kicad_device_context.h>
#include <hotkeys_basic.h>

#include <general.h>
#include <eeschema_id.h>
#include <protos.h>
#include <class_library.h>
#include <sch_bus_entry.h>
#include <sch_marker.h>
#include <sch_component.h>
#include <sch_junction.h>
#include <sch_line.h>
#include <sch_sheet.h>
50
#include <sch_sheet_path.h>
plyatov's avatar
plyatov committed
51

52

53
void SCH_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event )
plyatov's avatar
plyatov committed
54
{
55 56 57
    int         id = event.GetId();
    wxPoint     pos;
    SCH_SCREEN* screen = GetScreen();
58
    SCH_ITEM*   item = screen->GetCurItem();
59 60 61 62 63 64 65 66

    pos = wxGetMousePosition();

    pos.y += 20;

    // If needed, stop the current command and deselect current tool
    switch( id )
    {
67 68 69
    case wxID_CUT:
    case wxID_COPY:
    case ID_POPUP_CANCEL_CURRENT_COMMAND:
70 71
    case ID_POPUP_SCH_ENTRY_SELECT_SLASH:
    case ID_POPUP_SCH_ENTRY_SELECT_ANTISLASH:
72 73
    case ID_POPUP_SCH_BEGIN_WIRE:
    case ID_POPUP_SCH_BEGIN_BUS:
74 75 76 77 78
    case ID_POPUP_END_LINE:
    case ID_POPUP_SCH_SET_SHAPE_TEXT:
    case ID_POPUP_SCH_CLEANUP_SHEET:
    case ID_POPUP_SCH_END_SHEET:
    case ID_POPUP_SCH_RESIZE_SHEET:
charras's avatar
charras committed
79
    case ID_POPUP_IMPORT_GLABEL:
80 81 82 83 84 85 86 87 88 89 90 91 92 93
    case ID_POPUP_SCH_INIT_CMP:
    case ID_POPUP_SCH_DISPLAYDOC_CMP:
    case ID_POPUP_SCH_EDIT_CONVERT_CMP:
    case ID_POPUP_DELETE_BLOCK:
    case ID_POPUP_PLACE_BLOCK:
    case ID_POPUP_ZOOM_BLOCK:
    case ID_POPUP_DRAG_BLOCK:
    case ID_POPUP_COPY_BLOCK:
    case ID_POPUP_SCH_DELETE_NODE:
    case ID_POPUP_SCH_DELETE_CONNECTION:
    case ID_POPUP_SCH_ENTER_SHEET:
    case ID_POPUP_SCH_LEAVE_SHEET:
    case ID_POPUP_SCH_ADD_JUNCTION:
    case ID_POPUP_SCH_ADD_LABEL:
94
    case ID_POPUP_SCH_GETINFO_MARKER:
95

96 97 98
        /* At this point: Do nothing. these commands do not need to stop the
         * current command (mainly a block command) or reset the current state
         * They will be executed later, in next switch structure.
99 100 101
         */
        break;

102
    case ID_POPUP_SCH_DELETE_CMP:
103
    case ID_POPUP_SCH_DELETE:
104

charras's avatar
charras committed
105
        // Stop the current command (if any) but keep the current tool
106
        m_canvas->EndMouseCapture();
107 108
        break;

109
    default:
110

111
        // Stop the current command and deselect the current tool
112
        m_canvas->EndMouseCapture( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor() );
113 114 115
        break;
    }

116
    INSTALL_UNBUFFERED_DC( dc, m_canvas );
117
    item = screen->GetCurItem();    // Can be modified by previous calls.
118

119
    switch( id )
120 121 122
    {
    case ID_HIERARCHY:
        InstallHierarchyFrame( &dc, pos );
123
        SetRepeatItem( NULL );
124 125 126
        break;

    case wxID_CUT:
127
        if( screen->m_BlockLocate.GetCommand() != BLOCK_MOVE )
128
            break;
129

130 131 132
        screen->m_BlockLocate.SetCommand( BLOCK_DELETE );
        screen->m_BlockLocate.SetMessageBlock( this );
        HandleBlockEnd( &dc );
133
        SetRepeatItem( NULL );
134
        SetSheetNumberAndCount();
135 136 137
        break;

    case wxID_PASTE:
138
        HandleBlockBegin( &dc, BLOCK_PASTE, GetCrossHairPosition() );
139 140 141
        break;

    case ID_POPUP_SCH_ENTRY_SELECT_SLASH:
142
        m_canvas->MoveCursorToCrossHair();
143
        SetBusEntryShape( &dc, dynamic_cast<SCH_BUS_ENTRY_BASE*>( item ), '/' );
144 145 146
        break;

    case ID_POPUP_SCH_ENTRY_SELECT_ANTISLASH:
147
        m_canvas->MoveCursorToCrossHair();
148
        SetBusEntryShape( &dc, dynamic_cast<SCH_BUS_ENTRY_BASE*>( item ), '\\' );
149 150 151
        break;

    case ID_POPUP_CANCEL_CURRENT_COMMAND:
152
        if( m_canvas->IsMouseCaptured() )
153
        {
154 155
            m_canvas->EndMouseCapture();
            SetToolID( GetToolId(), m_canvas->GetCurrentCursor(), wxEmptyString );
156 157
        }
        else
158
        {
159
            SetToolID( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor(), wxEmptyString );
160 161
        }

162 163 164
        break;

    case ID_POPUP_END_LINE:
165
        m_canvas->MoveCursorToCrossHair();
166 167 168
        EndSegment( &dc );
        break;

169 170
    case ID_POPUP_SCH_BEGIN_WIRE:
        m_canvas->MoveCursorToCrossHair();
171
        OnLeftClick( &dc, GetCrossHairPosition() );
172
        break;
173

174 175
    case ID_POPUP_SCH_BEGIN_BUS:
        m_canvas->MoveCursorToCrossHair();
176
        OnLeftClick( &dc, GetCrossHairPosition() );
177 178 179
        break;

    case ID_POPUP_SCH_SET_SHAPE_TEXT:
180
        // Not used
181 182 183 184
        break;

    case ID_POPUP_SCH_DELETE_NODE:
    case ID_POPUP_SCH_DELETE_CONNECTION:
185
        m_canvas->MoveCursorToCrossHair();
186
        DeleteConnection( id == ID_POPUP_SCH_DELETE_CONNECTION );
187
        screen->SetCurItem( NULL );
188
        SetRepeatItem( NULL );
189 190
        screen->TestDanglingEnds( m_canvas, &dc );
        m_canvas->Refresh();
191 192 193 194
        break;

    case ID_POPUP_SCH_BREAK_WIRE:
    {
195 196 197
        DLIST< SCH_ITEM > oldWires;

        oldWires.SetOwnership( false );      // Prevent DLIST for deleting items in destructor.
198
        m_canvas->MoveCursorToCrossHair();
199
        screen->ExtractWires( oldWires, true );
200
        screen->BreakSegment( GetCrossHairPosition() );
201

202 203 204 205 206 207 208 209 210 211 212 213 214 215
        if( oldWires.GetCount() != 0 )
        {
            PICKED_ITEMS_LIST oldItems;

            oldItems.m_Status = UR_WIRE_IMAGE;

            while( oldWires.GetCount() != 0 )
            {
                ITEM_PICKER picker = ITEM_PICKER( oldWires.PopFront(), UR_WIRE_IMAGE );
                oldItems.PushItem( picker );
            }

            SaveCopyInUndoList( oldItems, UR_WIRE_IMAGE );
        }
216

217
        screen->TestDanglingEnds( m_canvas, &dc );
218
    }
219
    break;
220 221 222

    case ID_POPUP_SCH_DELETE_CMP:
    case ID_POPUP_SCH_DELETE:
223 224
        if( item == NULL )
            break;
225

226
        DeleteItem( item );
227
        screen->SetCurItem( NULL );
228
        SetRepeatItem( NULL );
229
        screen->TestDanglingEnds( m_canvas, &dc );
230 231
        SetSheetNumberAndCount();
        OnModify();
232
        break;
233 234

    case ID_POPUP_SCH_END_SHEET:
235
        m_canvas->MoveCursorToCrossHair();
236
        addCurrentItemToList( &dc );
237 238 239
        break;

    case ID_POPUP_SCH_RESIZE_SHEET:
240
        ReSizeSheet( (SCH_SHEET*) item, &dc );
241
        screen->TestDanglingEnds( m_canvas, &dc );
242 243
        break;

charras's avatar
charras committed
244
    case ID_POPUP_IMPORT_GLABEL:
245
        if( item != NULL && item->Type() == SCH_SHEET_T )
246
            screen->SetCurItem( ImportSheetPin( (SCH_SHEET*) item, &dc ) );
charras's avatar
charras committed
247
        break;
248 249

    case ID_POPUP_SCH_CLEANUP_SHEET:
250
        if( item != NULL && item->Type() == SCH_SHEET_T )
251
        {
252
            SCH_SHEET* sheet = (SCH_SHEET*) item;
253

254
            if( !sheet->HasUndefinedPins() )
255 256
            {
                DisplayInfoMessage( this,
257
                                    _( "There are no undefined labels in this sheet to clean up." ) );
258 259 260
                return;
            }

261
            if( !IsOK( this, _( "Do you wish to cleanup this sheet?" ) ) )
262 263 264 265 266 267
                return;

            /* Save sheet in undo list before cleaning up unreferenced hierarchical labels. */
            SaveCopyInUndoList( sheet, UR_CHANGED );
            sheet->CleanupSheet();
            OnModify();
268
            m_canvas->RefreshDrawingRect( sheet->GetBoundingBox() );
269
        }
270 271 272
        break;

    case ID_POPUP_SCH_INIT_CMP:
273
        m_canvas->MoveCursorToCrossHair();
274 275
        break;

276
    case ID_POPUP_SCH_EDIT_CONVERT_CMP:
277

278
        // Ensure the struct is a component (could be a struct of a component, like Field, text..)
279 280
        if( item && item->Type() == SCH_COMPONENT_T )
        {
281
            m_canvas->MoveCursorToCrossHair();
282 283
            ConvertPart( (SCH_COMPONENT*) item, &dc );
        }
284

285 286 287
        break;

    case ID_POPUP_SCH_DISPLAYDOC_CMP:
288

289
        // Ensure the struct is a component (could be a piece of a component, like Field, text..)
290
        if( item && item->Type() == SCH_COMPONENT_T )
291
        {
292
            LIB_ALIAS* LibEntry;
293
            LibEntry = CMP_LIBRARY::FindLibraryEntry( ( (SCH_COMPONENT*) item )->GetLibName() );
294

295
            if( LibEntry && LibEntry->GetDocFileName() != wxEmptyString )
296
            {
297
                GetAssociatedDocument( this, LibEntry->GetDocFileName(),
298
                                       &wxGetApp().GetLibraryPathList() );
299
            }
300 301 302 303
        }
        break;

    case ID_POPUP_SCH_ENTER_SHEET:
304

305
        if( item && (item->Type() == SCH_SHEET_T) )
306
        {
307 308
            m_CurrentSheet->Push( (SCH_SHEET*) item );
            DisplayCurrentSheet();
309
        }
310 311

        break;
312 313

    case ID_POPUP_SCH_LEAVE_SHEET:
314 315
        m_CurrentSheet->Pop();
        DisplayCurrentSheet();
316 317 318
        break;

    case wxID_COPY:         // really this is a Save block for paste
319 320 321
        screen->m_BlockLocate.SetCommand( BLOCK_SAVE );
        screen->m_BlockLocate.SetMessageBlock( this );
        HandleBlockEnd( &dc );
322 323 324
        break;

    case ID_POPUP_PLACE_BLOCK:
325
        m_canvas->SetAutoPanRequest( false );
326
        m_canvas->MoveCursorToCrossHair();
327 328 329 330
        HandleBlockPlace( &dc );
        break;

    case ID_POPUP_ZOOM_BLOCK:
331 332 333
        screen->m_BlockLocate.SetCommand( BLOCK_ZOOM );
        screen->m_BlockLocate.SetMessageBlock( this );
        HandleBlockEnd( &dc );
334 335 336
        break;

    case ID_POPUP_DELETE_BLOCK:
337
        m_canvas->MoveCursorToCrossHair();
338 339 340
        screen->m_BlockLocate.SetCommand( BLOCK_DELETE );
        screen->m_BlockLocate.SetMessageBlock( this );
        HandleBlockEnd( &dc );
341
        SetSheetNumberAndCount();
342 343 344
        break;

    case ID_POPUP_COPY_BLOCK:
345
        m_canvas->MoveCursorToCrossHair();
346 347 348
        screen->m_BlockLocate.SetCommand( BLOCK_COPY );
        screen->m_BlockLocate.SetMessageBlock( this );
        HandleBlockEnd( &dc );
349 350 351
        break;

    case ID_POPUP_DRAG_BLOCK:
352
        m_canvas->MoveCursorToCrossHair();
353 354 355
        screen->m_BlockLocate.SetCommand( BLOCK_DRAG );
        screen->m_BlockLocate.SetMessageBlock( this );
        HandleBlockEnd( &dc );
356 357 358
        break;

    case ID_POPUP_SCH_ADD_JUNCTION:
359
        m_canvas->MoveCursorToCrossHair();
360
        screen->SetCurItem( AddJunction( &dc, GetCrossHairPosition(), true ) );
361
        screen->TestDanglingEnds( m_canvas, &dc );
362
        screen->SetCurItem( NULL );
363 364 365 366
        break;

    case ID_POPUP_SCH_ADD_LABEL:
    case ID_POPUP_SCH_ADD_GLABEL:
367 368
        screen->SetCurItem( CreateNewText( &dc, id == ID_POPUP_SCH_ADD_LABEL ?
                                           LAYER_LOCLABEL : LAYER_GLOBLABEL ) );
369 370 371
        item = screen->GetCurItem();

        if( item )
372
            addCurrentItemToList( &dc );
373

374 375
        break;

376
    case ID_POPUP_SCH_GETINFO_MARKER:
377 378
        if( item && item->Type() == SCH_MARKER_T )
            ( (SCH_MARKER*) item )->DisplayMarkerInfo( this );
379

380 381
        break;

382
    default:        // Log error:
383 384
        wxFAIL_MSG( wxString::Format( wxT( "Cannot process command event ID %d" ),
                                      event.GetId() ) );
385 386 387
        break;
    }

388
    // End switch ( id )    (Command execution)
389

390
    if( GetToolId() == ID_NO_TOOL_SELECTED )
391
        SetRepeatItem( NULL );
plyatov's avatar
plyatov committed
392 393 394
}


395
void SCH_EDIT_FRAME::OnMoveItem( wxCommandEvent& aEvent )
plyatov's avatar
plyatov committed
396
{
397
    SCH_SCREEN* screen = GetScreen();
398
    SCH_ITEM*   item = screen->GetCurItem();
399

400 401 402 403 404 405
    if( screen->m_BlockLocate.GetState() != STATE_NO_BLOCK )
    {
        // trying to move an item when there is a block at the same time is not acceptable
        return;
    }

406
    if( item == NULL )
407 408 409 410 411
    {
        // If we didn't get here by a hot key, then something has gone wrong.
        if( aEvent.GetInt() == 0 )
            return;

412
        EDA_HOTKEY_CLIENT_DATA* data = (EDA_HOTKEY_CLIENT_DATA*) aEvent.GetClientObject();
413

414
        wxCHECK_RET( data != NULL, wxT( "Invalid hot key client object." ) );
415 416 417 418 419 420 421 422

        item = LocateAndShowItem( data->GetPosition(), SCH_COLLECTOR::MovableItems,
                                  aEvent.GetInt() );

        // Exit if no item found at the current location or the item is already being edited.
        if( (item == NULL) || (item->GetFlags() != 0) )
            return;
    }
423

424
    INSTALL_UNBUFFERED_DC( dc, m_canvas );
425 426

    switch( item->Type() )
427
    {
428
    case SCH_LINE_T:
429 430
        break;

431 432
    case SCH_JUNCTION_T:
    case SCH_NO_CONNECT_T:
433 434
    case SCH_BUS_BUS_ENTRY_T:
    case SCH_BUS_WIRE_ENTRY_T:
435 436 437 438
    case SCH_LABEL_T:
    case SCH_GLOBAL_LABEL_T:
    case SCH_HIERARCHICAL_LABEL_T:
    case SCH_TEXT_T:
439
    case SCH_COMPONENT_T:
440
    case SCH_SHEET_PIN_T:
441
    case SCH_FIELD_T:
442
        MoveItem( item, &dc );
443 444
        break;

445 446 447 448
    case SCH_BITMAP_T:
        MoveImage( (SCH_BITMAP*) item, &dc );
        break;

449
    case SCH_SHEET_T:
450
        StartMoveSheet( (SCH_SHEET*) item, &dc );
451 452
        break;

453
    case SCH_MARKER_T:
454
    default:
455 456
        wxFAIL_MSG( wxString::Format( wxT( "Cannot move item type %s" ),
                                      GetChars( item->GetClass() ) ) );
457 458
        break;
    }
459 460

    if( GetToolId() == ID_NO_TOOL_SELECTED )
461
        SetRepeatItem( NULL );
plyatov's avatar
plyatov committed
462
}
463 464 465 466 467 468 469 470


void SCH_EDIT_FRAME::OnCancelCurrentCommand( wxCommandEvent& aEvent )
{
    SCH_SCREEN* screen = GetScreen();

    if( screen->IsBlockActive() )
    {
471
        m_canvas->SetCursor( (wxStockCursor) m_canvas->GetDefaultCursor() );
472 473 474
        screen->ClearBlockCommand();

        // Stop the current command (if any) but keep the current tool
475
        m_canvas->EndMouseCapture();
476 477 478
    }
    else
    {
479 480
        if( m_canvas->IsMouseCaptured() ) // Stop the current command but keep the current tool
            m_canvas->EndMouseCapture();
481
        else                    // Deselect current tool
482
            m_canvas->EndMouseCapture( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor() );
483
     }
484
}
485 486 487 488


void SCH_EDIT_FRAME::OnSelectTool( wxCommandEvent& aEvent )
{
489
    int id = aEvent.GetId();
490 491

    // Stop the current command and deselect the current tool.
492
    m_canvas->EndMouseCapture( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor() );
493 494 495

    switch( id )
    {
496
    case ID_NO_TOOL_SELECTED:
497
        SetToolID( id, m_canvas->GetDefaultCursor(), _( "No tool selected" ) );
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
        break;

    case ID_HIERARCHY_PUSH_POP_BUTT:
        SetToolID( id, wxCURSOR_HAND, _( "Descend or ascend hierarchy" ) );
        break;

    case ID_NOCONN_BUTT:
        SetToolID( id, wxCURSOR_PENCIL, _( "Add no connect" ) );
        break;

    case ID_WIRE_BUTT:
        SetToolID( id, wxCURSOR_PENCIL, _( "Add wire" ) );
        break;

    case ID_BUS_BUTT:
        SetToolID( id, wxCURSOR_PENCIL, _( "Add bus" ) );
        break;

    case ID_LINE_COMMENT_BUTT:
        SetToolID( id, wxCURSOR_PENCIL, _( "Add lines" ) );
        break;

    case ID_JUNCTION_BUTT:
        SetToolID( id, wxCURSOR_PENCIL, _( "Add junction" ) );
        break;

    case ID_LABEL_BUTT:
        SetToolID( id, wxCURSOR_PENCIL, _( "Add label" ) );
        break;

    case ID_GLABEL_BUTT:
        SetToolID( id, wxCURSOR_PENCIL, _( "Add global label" ) );
        break;

    case ID_HIERLABEL_BUTT:
        SetToolID( id, wxCURSOR_PENCIL, _( "Add hierarchical label" ) );
        break;

    case ID_TEXT_COMMENT_BUTT:
        SetToolID( id, wxCURSOR_PENCIL, _( "Add text" ) );
        break;

540 541 542 543
    case ID_ADD_IMAGE_BUTT:
        SetToolID( id, wxCURSOR_PENCIL, _( "Add image" ) );
        break;

544 545 546 547 548 549 550 551 552 553 554 555
    case ID_WIRETOBUS_ENTRY_BUTT:
        SetToolID( id, wxCURSOR_PENCIL, _( "Add wire to bus entry" ) );
        break;

    case ID_BUSTOBUS_ENTRY_BUTT:
        SetToolID( id, wxCURSOR_PENCIL, _( "Add bus to bus entry" ) );
        break;

    case ID_SHEET_SYMBOL_BUTT:
        SetToolID( id, wxCURSOR_PENCIL, _( "Add sheet" ) );
        break;

556
    case ID_SHEET_PIN_BUTT:
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
        SetToolID( id, wxCURSOR_PENCIL, _( "Add sheet pins" ) );
        break;

    case ID_IMPORT_HLABEL_BUTT:
        SetToolID( id, wxCURSOR_PENCIL, _( "Import sheet pins" ) );
        break;

    case ID_SCH_PLACE_COMPONENT:
        SetToolID( id, wxCURSOR_PENCIL, _( "Add component" ) );
        break;

    case ID_PLACE_POWER_BUTT:
        SetToolID( id, wxCURSOR_PENCIL, _( "Add power" ) );
        break;

    case ID_SCHEMATIC_DELETE_ITEM_BUTT:
        SetToolID( id, wxCURSOR_BULLSEYE, _( "Delete item" ) );
        break;

    default:
577
        SetRepeatItem( NULL );
578
    }
579 580

    // Simulate left click event if we got here from a hot key.
581
    if( aEvent.GetClientObject() != NULL )
582
    {
583
        EDA_HOTKEY_CLIENT_DATA* data = (EDA_HOTKEY_CLIENT_DATA*) aEvent.GetClientObject();
584 585 586

        wxPoint pos = data->GetPosition();

587
        INSTALL_UNBUFFERED_DC( dc, m_canvas );
588 589
        OnLeftClick( &dc, pos );
    }
590 591 592 593 594
}


void SCH_EDIT_FRAME::OnUpdateSelectTool( wxUpdateUIEvent& aEvent )
{
595
    if( aEvent.GetEventObject() == m_drawToolBar )
596
        aEvent.Check( GetToolId() == aEvent.GetId() );
597
}
598 599 600 601


void SCH_EDIT_FRAME::DeleteConnection( bool aFullConnection )
{
602 603
    PICKED_ITEMS_LIST   pickList;
    SCH_SCREEN*         screen = GetScreen();
604
    wxPoint             pos = GetCrossHairPosition();
605 606 607

    if( screen->GetConnection( pos, pickList, aFullConnection ) != 0 )
    {
608
        DeleteItemsInList( m_canvas, pickList );
609 610 611 612 613 614 615
        OnModify();
    }
}


bool SCH_EDIT_FRAME::DeleteItemAtCrossHair( wxDC* DC )
{
616
    SCH_ITEM*   item;
617 618
    SCH_SCREEN* screen = GetScreen();

619
    item = LocateItem( GetCrossHairPosition(), SCH_COLLECTOR::ParentItems );
620 621 622 623 624 625 626 627 628 629

    if( item )
    {
        bool itemHasConnections = item->IsConnectable();

        screen->SetCurItem( NULL );
        SetRepeatItem( NULL );
        DeleteItem( item );

        if( itemHasConnections )
630
            screen->TestDanglingEnds( m_canvas, DC );
631 632 633 634 635 636 637

        OnModify();
        return true;
    }

    return false;
}
638 639 640 641 642


static void moveItem( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aPosition, bool aErase )
{
    SCH_SCREEN* screen = (SCH_SCREEN*) aPanel->GetScreen();
643
    SCH_ITEM*   item   = screen->GetCurItem();
644 645 646

    wxCHECK_RET( (item != NULL), wxT( "Cannot move invalid schematic item." ) );

647
#ifndef USE_WX_OVERLAY
648 649 650
    // Erase the current item at its current position.
    if( aErase )
        item->Draw( aPanel, aDC, wxPoint( 0, 0 ), g_XorMode );
651
#endif
652

653
    item->SetPosition( aPanel->GetParent()->GetCrossHairPosition() );
654 655 656 657 658 659 660 661

    // Draw the item item at it's new position.
    item->Draw( aPanel, aDC, wxPoint( 0, 0 ), g_XorMode );
}


static void abortMoveItem( EDA_DRAW_PANEL* aPanel, wxDC* aDC )
{
662 663
    SCH_SCREEN*     screen = (SCH_SCREEN*) aPanel->GetScreen();
    SCH_ITEM*       item = screen->GetCurItem();
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710
    SCH_EDIT_FRAME* parent = ( SCH_EDIT_FRAME* ) aPanel->GetParent();

    parent->SetRepeatItem( NULL );
    screen->SetCurItem( NULL );

    if( item == NULL )  /* no current item */
        return;

    if( item->IsNew() )
    {
        delete item;
        item = NULL;
    }
    else
    {
        SCH_ITEM* oldItem = parent->GetUndoItem();

        SCH_ITEM* currentItem;

        // Items that are children of other objects are undone by swapping the contents
        // of the parent items.
        if( (item->Type() == SCH_SHEET_PIN_T) || (item->Type() == SCH_FIELD_T) )
        {
            currentItem = (SCH_ITEM*) item->GetParent();
        }
        else
        {
            currentItem = item;
        }

        wxCHECK_RET( oldItem != NULL && currentItem->Type() == oldItem->Type(),
                     wxT( "Cannot restore undefined or bad last schematic item." ) );

        // Never delete existing item, because it can be referenced by an undo/redo command
        // Just restore its data
        currentItem->SwapData( oldItem );
        item->ClearFlags();
    }

    aPanel->Refresh();
}


void SCH_EDIT_FRAME::MoveItem( SCH_ITEM* aItem, wxDC* aDC )
{
    wxCHECK_RET( aItem != NULL, wxT( "Cannot move invalid schematic item" ) );

711
    SetRepeatItem( NULL );
712 713 714 715 716 717 718 719 720 721

    if( !aItem->IsNew() )
    {
        if( (aItem->Type() == SCH_SHEET_PIN_T) || (aItem->Type() == SCH_FIELD_T) )
            SetUndoItem( (SCH_ITEM*) aItem->GetParent() );
        else
            SetUndoItem( aItem );
    }

    aItem->SetFlags( IS_MOVED );
722 723 724 725
#ifdef USE_WX_OVERLAY
    this->Refresh();
    this->Update();
#endif
726
    m_canvas->CrossHairOff( aDC );
727 728

    if( aItem->Type() != SCH_SHEET_PIN_T )
729
        SetCrossHairPosition( aItem->GetPosition() );
730

731
    m_canvas->MoveCursorToCrossHair();
732 733

    OnModify();
734
    m_canvas->SetMouseCapture( moveItem, abortMoveItem );
735
    GetScreen()->SetCurItem( aItem );
736 737
    moveItem( m_canvas, aDC, wxDefaultPosition, true );
    m_canvas->CrossHairOn( aDC );
738
}
739 740 741 742 743


void SCH_EDIT_FRAME::OnRotate( wxCommandEvent& aEvent )
{
    SCH_SCREEN* screen = GetScreen();
744
    SCH_ITEM*   item = screen->GetCurItem();
745

746
    INSTALL_UNBUFFERED_DC( dc, m_canvas );
747

748 749
    // Allows block rotate operation on hot key.
    if( screen->m_BlockLocate.GetState() != STATE_NO_BLOCK )
750
    {
751 752
        screen->m_BlockLocate.SetCommand( BLOCK_ROTATE );
        HandleBlockEnd( &dc );
753 754
        return;
    }
755

756 757
    if( item == NULL )
    {
758 759 760 761
        // If we didn't get here by a hot key, then something has gone wrong.
        if( aEvent.GetInt() == 0 )
            return;

762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
        EDA_HOTKEY_CLIENT_DATA* data = (EDA_HOTKEY_CLIENT_DATA*) aEvent.GetClientObject();

        wxCHECK_RET( data != NULL, wxT( "Invalid hot key client object." ) );

        item = LocateAndShowItem( data->GetPosition(), SCH_COLLECTOR::RotatableItems,
                                  aEvent.GetInt() );

        // Exit if no item found at the current location or the item is already being edited.
        if( (item == NULL) || (item->GetFlags() != 0) )
            return;
    }

    switch( item->Type() )
    {
    case SCH_COMPONENT_T:
777
        if( aEvent.GetId() == ID_SCH_ROTATE_CLOCKWISE )
778
            OrientComponent( CMP_ROTATE_CLOCKWISE );
779 780 781 782 783 784
        else if( aEvent.GetId() == ID_SCH_ROTATE_COUNTERCLOCKWISE )
            OrientComponent( CMP_ROTATE_COUNTERCLOCKWISE );
        else
            wxFAIL_MSG( wxT( "Unknown rotate item command ID." ) );

        break;
785 786 787 788 789

    case SCH_TEXT_T:
    case SCH_LABEL_T:
    case SCH_GLOBAL_LABEL_T:
    case SCH_HIERARCHICAL_LABEL_T:
790
        m_canvas->MoveCursorToCrossHair();
791 792 793 794
        ChangeTextOrient( (SCH_TEXT*) item, &dc );
        break;

    case SCH_FIELD_T:
795
        m_canvas->MoveCursorToCrossHair();
796 797 798 799 800 801 802 803 804
        RotateField( (SCH_FIELD*) item, &dc );
        break;

    case SCH_BITMAP_T:
        RotateImage( (SCH_BITMAP*) item );
        break;

    case SCH_SHEET_T:           /// @todo allow sheet rotate on hotkey
    default:
805 806 807 808 809 810 811 812 813 814 815 816 817 818
        wxFAIL_MSG( wxString::Format( wxT( "Cannot rotate schematic item type %s." ),
                                      GetChars( item->GetClass() ) ) );
    }

    if( item->GetFlags() == 0 )
        screen->SetCurItem( NULL );
}


void SCH_EDIT_FRAME::OnEditItem( wxCommandEvent& aEvent )
{
    SCH_SCREEN* screen = GetScreen();
    SCH_ITEM* item = screen->GetCurItem();

819
    INSTALL_UNBUFFERED_DC( dc, m_canvas );
820 821 822 823 824 825 826 827 828 829 830

    if( item == NULL )
    {
        // If we didn't get here by a hot key, then something has gone wrong.
        if( aEvent.GetInt() == 0 )
            return;

        EDA_HOTKEY_CLIENT_DATA* data = (EDA_HOTKEY_CLIENT_DATA*) aEvent.GetClientObject();

        wxCHECK_RET( data != NULL, wxT( "Invalid hot key client object." ) );

831 832 833
        // Set the locat filter, according to the edit command
        const KICAD_T* filterList = SCH_COLLECTOR::EditableItems;
        const KICAD_T* filterListAux = NULL;
834

835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
        switch( aEvent.GetId() )
        {
        case ID_SCH_EDIT_COMPONENT_REFERENCE:
            filterList = SCH_COLLECTOR::CmpFieldReferenceOnly;
            filterListAux = SCH_COLLECTOR::ComponentsOnly;
            break;

        case ID_SCH_EDIT_COMPONENT_VALUE:
            filterList = SCH_COLLECTOR::CmpFieldValueOnly;
            filterListAux = SCH_COLLECTOR::ComponentsOnly;
            break;

        case ID_SCH_EDIT_COMPONENT_FOOTPRINT:
            filterList = SCH_COLLECTOR::CmpFieldFootprintOnly;
            filterListAux = SCH_COLLECTOR::ComponentsOnly;
            break;

        default:
            break;
        }
855 856

        item = LocateAndShowItem( data->GetPosition(), filterList, aEvent.GetInt() );
857 858 859

        // If no item found, and if an auxiliary filter exists, try to use it
        if( !item && filterListAux )
860
            item = LocateAndShowItem( data->GetPosition(), filterListAux, aEvent.GetInt() );
861 862 863 864 865 866 867 868 869 870 871 872 873

        // Exit if no item found at the current location or the item is already being edited.
        if( (item == NULL) || (item->GetFlags() != 0) )
            return;
    }

    switch( item->Type() )
    {
    case SCH_COMPONENT_T:
    {
        switch( aEvent.GetId() )
        {
        case ID_SCH_EDIT_COMPONENT_REFERENCE:
874
            EditComponentFieldText( ( (SCH_COMPONENT*) item )->GetField( REFERENCE ) );
875 876 877
            break;

        case ID_SCH_EDIT_COMPONENT_VALUE:
878
            EditComponentFieldText( ( (SCH_COMPONENT*) item )->GetField( VALUE ) );
879 880 881
            break;

        case ID_SCH_EDIT_COMPONENT_FOOTPRINT:
882
            EditComponentFieldText( ( (SCH_COMPONENT*) item )->GetField( FOOTPRINT ) );
883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898
            break;

        case ID_SCH_EDIT_ITEM:
            EditComponent( (SCH_COMPONENT*) item );
            break;

        default:
            wxFAIL_MSG( wxString::Format( wxT( "Invalid schematic component edit command ID %d" ),
                                          aEvent.GetId() ) );
        }

        break;
    }

    case SCH_SHEET_T:
        EditSheet( (SCH_SHEET*) item, &dc );
899
        break;
900 901 902 903 904 905 906 907 908 909 910 911 912

    case SCH_SHEET_PIN_T:
        EditSheetPin( (SCH_SHEET_PIN*) item, &dc );
        break;

    case SCH_TEXT_T:
    case SCH_LABEL_T:
    case SCH_GLOBAL_LABEL_T:
    case SCH_HIERARCHICAL_LABEL_T:
        EditSchematicText( (SCH_TEXT*) item );
        break;

    case SCH_FIELD_T:
913
        EditComponentFieldText( (SCH_FIELD*) item );
914 915 916 917 918 919 920 921 922
        break;

    case SCH_BITMAP_T:
        EditImage( (SCH_BITMAP*) item );
        break;

    default:
        wxFAIL_MSG( wxString::Format( wxT( "Cannot edit schematic item type %s." ),
                                      GetChars( item->GetClass() ) ) );
923 924 925 926 927
    }

    if( item->GetFlags() == 0 )
        screen->SetCurItem( NULL );
}
928 929 930 931 932 933 934


void SCH_EDIT_FRAME::OnDragItem( wxCommandEvent& aEvent )
{
    SCH_SCREEN* screen = GetScreen();
    SCH_ITEM* item = screen->GetCurItem();

935
    INSTALL_UNBUFFERED_DC( dc, m_canvas );
936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956

    if( item == NULL )
    {
        // If we didn't get here by a hot key, then something has gone wrong.
        if( aEvent.GetInt() == 0 )
            return;

        EDA_HOTKEY_CLIENT_DATA* data = (EDA_HOTKEY_CLIENT_DATA*) aEvent.GetClientObject();

        wxCHECK_RET( data != NULL, wxT( "Invalid hot key client object." ) );

        item = LocateAndShowItem( data->GetPosition(), SCH_COLLECTOR::DraggableItems,
                                  aEvent.GetInt() );

        // Exit if no item found at the current location or the item is already being edited.
        if( (item == NULL) || (item->GetFlags() != 0) )
            return;
    }

    switch( item->Type() )
    {
957 958
    case SCH_BUS_BUS_ENTRY_T:
    case SCH_BUS_WIRE_ENTRY_T:
959 960 961 962 963 964 965
    case SCH_LINE_T:
    case SCH_JUNCTION_T:
        if( item->GetLayer() == LAYER_BUS )
            break;

        // Fall thru if item is not on bus layer.
    case SCH_COMPONENT_T:
966
    case SCH_LABEL_T:
967 968 969
    case SCH_GLOBAL_LABEL_T:
    case SCH_HIERARCHICAL_LABEL_T:
    case SCH_SHEET_T:
970
        m_canvas->MoveCursorToCrossHair();
971 972 973

        // The easiest way to handle a drag component or sheet command
        // is to simulate a block drag command
974
        if( screen->m_BlockLocate.GetState() == STATE_NO_BLOCK )
975
        {
976
            if( !HandleBlockBegin( &dc, BLOCK_DRAG, GetCrossHairPosition() ) )
977 978 979 980 981 982 983 984 985 986 987 988 989 990
                break;

            // Give a non null size to the search block:
            screen->m_BlockLocate.Inflate( 1 );
            HandleBlockEnd( &dc );
        }

        break;

    default:
        wxFAIL_MSG( wxString::Format( wxT( "Cannot drag schematic item type %s." ),
                                      GetChars( item->GetClass() ) ) );
    }
}
991 992 993 994 995


void SCH_EDIT_FRAME::OnOrient( wxCommandEvent& aEvent )
{
    SCH_SCREEN* screen = GetScreen();
996
    SCH_ITEM*   item   = screen->GetCurItem();
997

998
    INSTALL_UNBUFFERED_DC( dc, m_canvas );
999

1000 1001
    // Allows block rotate operation on hot key.
    if( screen->m_BlockLocate.GetState() != STATE_NO_BLOCK )
1002
    {
1003
        if( aEvent.GetId() == ID_SCH_MIRROR_X )
1004 1005 1006 1007 1008 1009
        {
            m_canvas->MoveCursorToCrossHair();
            screen->m_BlockLocate.SetMessageBlock( this );
            screen->m_BlockLocate.SetCommand( BLOCK_MIRROR_X );
            HandleBlockEnd( &dc );
        }
1010
        else if( aEvent.GetId() == ID_SCH_MIRROR_Y )
1011 1012 1013 1014 1015 1016
        {
            m_canvas->MoveCursorToCrossHair();
            screen->m_BlockLocate.SetMessageBlock( this );
            screen->m_BlockLocate.SetCommand( BLOCK_MIRROR_Y );
            HandleBlockEnd( &dc );
        }
1017
        else
1018
        {
1019
            wxFAIL_MSG( wxT( "Unknown block oriention command ID." ) );
1020
        }
1021

1022 1023
        return;
    }
1024

1025 1026
    if( item == NULL )
    {
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
        // If we didn't get here by a hot key, then something has gone wrong.
        if( aEvent.GetInt() == 0 )
            return;

        EDA_HOTKEY_CLIENT_DATA* data = (EDA_HOTKEY_CLIENT_DATA*) aEvent.GetClientObject();

        wxCHECK_RET( data != NULL, wxT( "Invalid hot key client object." ) );

        item = LocateAndShowItem( data->GetPosition(), SCH_COLLECTOR::OrientableItems,
                                  aEvent.GetInt() );

        // Exit if no item found at the current location or the item is already being edited.
        if( (item == NULL) || (item->GetFlags() != 0) )
            return;
    }


    switch( item->Type() )
    {
    case SCH_COMPONENT_T:
        if( aEvent.GetId() == ID_SCH_MIRROR_X )
            OrientComponent( CMP_MIRROR_X );
        else if( aEvent.GetId() == ID_SCH_MIRROR_Y )
            OrientComponent( CMP_MIRROR_Y );
        else if( aEvent.GetId() == ID_SCH_ORIENT_NORMAL )
            OrientComponent( CMP_NORMAL );
        else
            wxFAIL_MSG( wxT( "Invalid orient schematic component command ID." ) );

        break;

    case SCH_BITMAP_T:
        if( aEvent.GetId() == ID_SCH_MIRROR_X )
            MirrorImage( (SCH_BITMAP*) item, true );
        else if( aEvent.GetId() == ID_SCH_MIRROR_Y )
            MirrorImage( (SCH_BITMAP*) item, false );

        break;

    default:
        wxFAIL_MSG( wxString::Format( wxT( "Schematic object type %s cannot be oriented." ),
                                      GetChars( item->GetClass() ) ) );
    }

    if( item->GetFlags() == 0 )
        screen->SetCurItem( NULL );
}