diff --git a/bitmaps_png/CMakeLists.txt b/bitmaps_png/CMakeLists.txt index 322836ed2f..ea627d5f82 100644 --- a/bitmaps_png/CMakeLists.txt +++ b/bitmaps_png/CMakeLists.txt @@ -217,6 +217,9 @@ set( BMAPS_MID drag_track_segment drc_off drc + duplicate_line + duplicate_pad + duplicate_text edges_sketch edit_comp_footprint edit_component diff --git a/bitmaps_png/cpp_26/duplicate_line.cpp b/bitmaps_png/cpp_26/duplicate_line.cpp new file mode 100644 index 0000000000..097a1826b7 --- /dev/null +++ b/bitmaps_png/cpp_26/duplicate_line.cpp @@ -0,0 +1,37 @@ + +/* Do not modify this file, it was automatically generated by the + * PNG2cpp CMake script, using a *.png file as input. + */ + +#include + +static const unsigned char png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1a, 0x08, 0x06, 0x00, 0x00, 0x00, 0xa9, 0x4a, 0x4c, + 0xce, 0x00, 0x00, 0x01, 0x41, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xed, 0x96, 0xb1, 0x4a, 0x03, + 0x41, 0x14, 0x45, 0xcf, 0xbc, 0x59, 0x0d, 0xc4, 0x08, 0xb1, 0x90, 0x4d, 0x0c, 0x6c, 0x61, 0xb0, + 0x88, 0xda, 0xa7, 0x16, 0x0b, 0xc1, 0xc2, 0x0f, 0xf0, 0x23, 0xfc, 0x06, 0xbb, 0x94, 0xd6, 0x62, + 0x21, 0x6c, 0x76, 0x9d, 0xd5, 0x14, 0x42, 0x2c, 0x2c, 0x44, 0x44, 0xd2, 0x07, 0x2b, 0x2d, 0x52, + 0x08, 0x62, 0x61, 0x11, 0x14, 0x4c, 0xc2, 0x82, 0x1a, 0xc7, 0x4f, 0x50, 0x21, 0x93, 0xc2, 0x58, + 0x9c, 0xfa, 0xf0, 0x86, 0xfb, 0xde, 0x1d, 0xac, 0xb5, 0x8c, 0x03, 0xc6, 0x2e, 0xe2, 0x00, 0xeb, + 0x82, 0x09, 0x10, 0x7d, 0xc7, 0x15, 0x78, 0x11, 0x6c, 0x1b, 0x58, 0x70, 0x1a, 0x86, 0x18, 0x96, + 0x62, 0xa5, 0xd2, 0x08, 0x6c, 0x22, 0x72, 0x1f, 0xc3, 0x86, 0xb3, 0xd4, 0x85, 0x50, 0x35, 0x4a, + 0xf5, 0xae, 0xf3, 0xf9, 0xcf, 0x86, 0xe7, 0xf5, 0x13, 0xad, 0xc3, 0x10, 0x66, 0x9c, 0xc4, 0x3b, + 0x84, 0x55, 0x23, 0xf2, 0xdc, 0xf6, 0xfd, 0xe1, 0x45, 0x2e, 0x97, 0x26, 0x4a, 0x35, 0x9d, 0xed, + 0x51, 0x02, 0x65, 0x23, 0xf2, 0xd4, 0xf6, 0xfd, 0x8f, 0xd3, 0xe9, 0xe9, 0xbe, 0x11, 0xd9, 0x71, + 0xb6, 0xb0, 0x47, 0xb0, 0x72, 0x2c, 0x32, 0x78, 0x58, 0x5c, 0xb4, 0x46, 0xa9, 0xc1, 0x09, 0xcc, + 0x3b, 0xbb, 0x0c, 0x89, 0xd6, 0x57, 0x37, 0x85, 0xc2, 0x67, 0x6b, 0x6e, 0x2e, 0x4d, 0x44, 0x6a, + 0xce, 0x44, 0x31, 0xac, 0x35, 0x3c, 0xaf, 0xff, 0x58, 0x2e, 0x5b, 0x23, 0xd2, 0x8b, 0x61, 0x33, + 0x82, 0xbd, 0x43, 0xc8, 0x8f, 0x54, 0x54, 0x87, 0x62, 0xac, 0x54, 0xf7, 0xb6, 0x54, 0xb2, 0x97, + 0xb3, 0xb3, 0x69, 0x22, 0xf2, 0x76, 0xac, 0x75, 0x6b, 0xa4, 0x13, 0x45, 0xb0, 0x1e, 0x43, 0xda, + 0xd0, 0xfa, 0xbd, 0x13, 0x04, 0xf6, 0xa5, 0x52, 0xb1, 0xcd, 0x4c, 0xe6, 0xb5, 0x0e, 0x5b, 0x23, + 0x7f, 0xba, 0x08, 0x96, 0x8d, 0xc8, 0xdd, 0x79, 0x36, 0xdb, 0xef, 0x04, 0x81, 0x35, 0x22, 0xdd, + 0x13, 0xd0, 0x4e, 0x6a, 0x62, 0x1f, 0xa6, 0x8e, 0x44, 0x6a, 0x11, 0x0c, 0x63, 0x91, 0x5d, 0xe7, + 0x7d, 0x14, 0x42, 0xb5, 0x0e, 0xc5, 0x9f, 0xf4, 0xd1, 0x99, 0x0b, 0x26, 0x40, 0xf4, 0xf7, 0x3e, + 0x27, 0xff, 0xa2, 0xdf, 0xf2, 0x05, 0x63, 0x00, 0xb0, 0xc1, 0xf1, 0xcc, 0x65, 0x5d, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +}; + +const BITMAP_OPAQUE duplicate_line_xpm[1] = {{ png, sizeof( png ), "duplicate_line_xpm" }}; + +//EOF diff --git a/bitmaps_png/cpp_26/duplicate_pad.cpp b/bitmaps_png/cpp_26/duplicate_pad.cpp new file mode 100644 index 0000000000..7a60b513d4 --- /dev/null +++ b/bitmaps_png/cpp_26/duplicate_pad.cpp @@ -0,0 +1,50 @@ + +/* Do not modify this file, it was automatically generated by the + * PNG2cpp CMake script, using a *.png file as input. + */ + +#include + +static const unsigned char png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1a, 0x08, 0x06, 0x00, 0x00, 0x00, 0xa9, 0x4a, 0x4c, + 0xce, 0x00, 0x00, 0x02, 0x12, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xd5, 0x54, 0x4d, 0x48, 0x54, + 0x51, 0x18, 0x3d, 0xef, 0x4e, 0xf8, 0x83, 0x0c, 0x31, 0x9a, 0x1b, 0x45, 0x66, 0xd1, 0x6a, 0x60, + 0xda, 0xb6, 0x69, 0xe1, 0xaa, 0x89, 0x36, 0x41, 0x20, 0x41, 0xab, 0x20, 0xc6, 0xa5, 0xd0, 0xc2, + 0xad, 0x36, 0x69, 0x30, 0xd0, 0x66, 0x08, 0x5d, 0xd8, 0x22, 0x06, 0x52, 0xf3, 0xe9, 0x4a, 0x26, + 0xa9, 0x45, 0x42, 0x09, 0xea, 0xea, 0x6d, 0x24, 0x10, 0x8d, 0x76, 0x12, 0x21, 0x44, 0x8c, 0xe2, + 0x28, 0xa2, 0xf5, 0x3a, 0xdf, 0x7d, 0xdf, 0x44, 0x0c, 0x09, 0xce, 0xcc, 0xf3, 0x41, 0x8b, 0xc3, + 0x7d, 0xef, 0xfb, 0xee, 0x3d, 0xe7, 0x9e, 0xef, 0xbb, 0xf7, 0xc2, 0xf7, 0x7d, 0x44, 0x81, 0x9a, + 0x1f, 0x38, 0xc8, 0x21, 0x8b, 0x27, 0xf8, 0xc8, 0xb1, 0x6c, 0x11, 0x7c, 0x67, 0x25, 0x17, 0x8a, + 0x10, 0xc9, 0x7a, 0x88, 0x35, 0x62, 0x87, 0xf0, 0x88, 0x65, 0x85, 0xa7, 0x31, 0xc9, 0xf5, 0x34, + 0x25, 0xa4, 0x4e, 0x84, 0x68, 0x8b, 0x28, 0x9d, 0x81, 0x2d, 0x3b, 0xa7, 0x41, 0x67, 0x55, 0x37, + 0x59, 0xdd, 0x75, 0x09, 0x8f, 0xf1, 0x86, 0xe3, 0x06, 0xb1, 0xab, 0xd8, 0xd0, 0x98, 0x88, 0xed, + 0xc4, 0x46, 0x31, 0xf8, 0x0a, 0x48, 0xb9, 0xc0, 0xd5, 0x05, 0xe0, 0x72, 0x7d, 0x42, 0x41, 0x1f, + 0x3c, 0x25, 0x13, 0x91, 0x4f, 0x18, 0x43, 0xbf, 0x85, 0x7c, 0x33, 0x96, 0x18, 0xc6, 0xbb, 0xfb, + 0x37, 0x9c, 0xaf, 0x2f, 0x3b, 0xcc, 0xf1, 0x0c, 0x97, 0xb9, 0xc6, 0x1c, 0xbd, 0x76, 0x9c, 0xc3, + 0x69, 0xe0, 0x4e, 0x3d, 0x8e, 0xca, 0xda, 0x8f, 0x92, 0x75, 0x41, 0x81, 0x3f, 0x13, 0x02, 0xb1, + 0xdd, 0xf4, 0x03, 0xac, 0x3c, 0xef, 0x32, 0x07, 0xe3, 0x49, 0x73, 0xb2, 0x10, 0x8b, 0xad, 0xce, + 0x39, 0xce, 0xd1, 0x6a, 0x22, 0xf1, 0x6b, 0xde, 0x98, 0x43, 0x0a, 0x0f, 0x84, 0x26, 0xa4, 0x39, + 0x99, 0x53, 0x96, 0x38, 0xc9, 0xaf, 0xd1, 0x55, 0x65, 0xb3, 0xb7, 0x57, 0xdc, 0x7d, 0xfb, 0x00, + 0x5c, 0x0a, 0xa5, 0x74, 0x9a, 0xf3, 0x64, 0x6e, 0x75, 0x31, 0x05, 0x8a, 0x5e, 0x77, 0xf7, 0xcf, + 0xa5, 0xf6, 0xf6, 0x03, 0x0a, 0x0f, 0xb1, 0x8c, 0xf7, 0x38, 0xde, 0x0d, 0xe5, 0x30, 0xc8, 0xdc, + 0xea, 0x62, 0x92, 0x3e, 0x5c, 0x6c, 0x69, 0xd9, 0xfb, 0x92, 0x4c, 0xfa, 0xec, 0xd7, 0xa9, 0xf4, + 0x6e, 0x16, 0xb8, 0x1e, 0xda, 0xf1, 0x7e, 0x0b, 0xb4, 0x92, 0x74, 0x49, 0x88, 0x97, 0xe3, 0xf1, + 0xe3, 0x1f, 0xa9, 0x94, 0xbf, 0xdd, 0xd7, 0x27, 0x25, 0xfc, 0x1c, 0xfa, 0x85, 0x2d, 0x02, 0x6d, + 0x24, 0x7e, 0xc1, 0x83, 0x51, 0x11, 0x47, 0xef, 0xe3, 0xf1, 0x7d, 0x0a, 0x0f, 0x5e, 0xd8, 0x13, + 0xc4, 0xbe, 0xdc, 0x9c, 0x33, 0xe6, 0xfb, 0xac, 0xe3, 0x54, 0x78, 0xbf, 0x3a, 0xce, 0x25, 0x54, + 0xff, 0x6d, 0xb7, 0x1b, 0xcb, 0x5c, 0x79, 0x84, 0xc2, 0xed, 0x5b, 0x58, 0xe7, 0xb7, 0x4b, 0xe4, + 0x25, 0x56, 0xbb, 0xb1, 0xc6, 0x45, 0x72, 0xe8, 0x24, 0x9e, 0x11, 0x45, 0xa2, 0x40, 0x3c, 0x55, + 0x14, 0x34, 0x26, 0xb9, 0xce, 0xa6, 0x84, 0xd4, 0x89, 0x10, 0x4d, 0x61, 0x14, 0x23, 0xff, 0x84, + 0xe4, 0x64, 0x8e, 0x3a, 0x6b, 0xd4, 0x4d, 0xc6, 0xee, 0x5a, 0x08, 0x85, 0x36, 0x87, 0x09, 0x62, + 0x46, 0x31, 0x61, 0x63, 0x81, 0x98, 0x38, 0xcb, 0x34, 0x23, 0x94, 0xb7, 0x25, 0x0a, 0xc8, 0x44, + 0x64, 0x92, 0x48, 0x2b, 0x26, 0x6d, 0x2c, 0xc8, 0x49, 0x19, 0xf3, 0xcd, 0x08, 0xb9, 0xb6, 0x1f, + 0x01, 0x99, 0xb8, 0x48, 0xff, 0x95, 0x4b, 0xdb, 0x58, 0x90, 0x93, 0x9e, 0xb9, 0xff, 0x85, 0x50, + 0x64, 0xa5, 0x8b, 0xe8, 0x30, 0x44, 0x75, 0xbc, 0x23, 0xbb, 0xb0, 0xb5, 0x4f, 0x90, 0xf6, 0xcc, + 0xbd, 0x90, 0x27, 0xa8, 0x5e, 0xfc, 0x06, 0x78, 0x64, 0xb7, 0x0e, 0x00, 0x4c, 0xaa, 0xda, 0x00, + 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +}; + +const BITMAP_OPAQUE duplicate_pad_xpm[1] = {{ png, sizeof( png ), "duplicate_pad_xpm" }}; + +//EOF diff --git a/bitmaps_png/cpp_26/duplicate_text.cpp b/bitmaps_png/cpp_26/duplicate_text.cpp new file mode 100644 index 0000000000..a231825612 --- /dev/null +++ b/bitmaps_png/cpp_26/duplicate_text.cpp @@ -0,0 +1,40 @@ + +/* Do not modify this file, it was automatically generated by the + * PNG2cpp CMake script, using a *.png file as input. + */ + +#include + +static const unsigned char png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1a, 0x08, 0x06, 0x00, 0x00, 0x00, 0xa9, 0x4a, 0x4c, + 0xce, 0x00, 0x00, 0x01, 0x6e, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0x63, 0xf8, 0xff, 0xff, 0x3f, + 0x03, 0x3d, 0x30, 0x84, 0x60, 0x98, 0xfd, 0x9f, 0x54, 0xbc, 0x98, 0x81, 0x41, 0x65, 0x19, 0x03, + 0x83, 0x3d, 0x90, 0xd6, 0x5f, 0xc5, 0xc0, 0xc0, 0x49, 0x75, 0x8b, 0x0c, 0x18, 0xf2, 0xfe, 0xb7, + 0x31, 0x29, 0xfd, 0x5f, 0x02, 0xd4, 0xba, 0x94, 0x81, 0xe1, 0xdf, 0x6a, 0x16, 0x96, 0xaf, 0x2b, + 0x98, 0x98, 0x6e, 0x00, 0x2d, 0x94, 0x24, 0xc5, 0xa2, 0x06, 0xb8, 0x04, 0x92, 0xe1, 0x48, 0x62, + 0x0d, 0x91, 0x4c, 0x5e, 0xff, 0x2b, 0x98, 0x0c, 0xfe, 0x03, 0x0d, 0x9f, 0xb1, 0x92, 0x89, 0xe9, + 0xd2, 0x2a, 0x16, 0x96, 0xef, 0x87, 0x05, 0x05, 0x7f, 0x2f, 0x63, 0x64, 0x7c, 0x39, 0x97, 0x81, + 0x81, 0x97, 0x18, 0x8b, 0x1a, 0x50, 0x24, 0xb0, 0x58, 0x04, 0xb3, 0x0c, 0x59, 0x0c, 0x68, 0xe1, + 0xb4, 0x7d, 0xfc, 0xfc, 0x5f, 0x77, 0xf3, 0xf0, 0x7c, 0x5d, 0xc6, 0xc4, 0x54, 0x8d, 0xd7, 0x22, + 0xac, 0x12, 0x38, 0x2c, 0x42, 0xc7, 0xc0, 0x20, 0xe4, 0x03, 0xfa, 0xe6, 0xfb, 0x3d, 0x05, 0x85, + 0xff, 0xcb, 0x99, 0x98, 0x3e, 0x02, 0x83, 0xd3, 0x0b, 0x28, 0xd6, 0x3f, 0x9f, 0x81, 0x41, 0x80, + 0xaa, 0x16, 0x81, 0xe2, 0x66, 0x39, 0x23, 0xe3, 0xb5, 0x2b, 0x52, 0x52, 0xff, 0xf7, 0xf2, 0xf2, + 0x7e, 0x03, 0xfa, 0xf0, 0xd7, 0x4a, 0x66, 0xe6, 0xc3, 0x54, 0xf5, 0x11, 0xd0, 0xe5, 0xce, 0x40, + 0x1f, 0x7c, 0x5f, 0xcd, 0xcc, 0xfc, 0xfb, 0x8e, 0xbc, 0xfc, 0xff, 0xf7, 0x9a, 0x9a, 0xff, 0x37, + 0xb1, 0xb3, 0x7f, 0x02, 0x5a, 0xee, 0x47, 0x8b, 0xa0, 0xd3, 0x02, 0x06, 0xd9, 0xf5, 0xed, 0x5c, + 0x5c, 0x5f, 0x6e, 0xcb, 0xc9, 0x81, 0x82, 0xef, 0x0d, 0x30, 0xb9, 0x33, 0x53, 0xdd, 0x22, 0x10, + 0x9e, 0xc9, 0xc0, 0xc0, 0x0a, 0x4c, 0x08, 0xed, 0x40, 0x4b, 0xff, 0x2e, 0x65, 0x62, 0x6a, 0xa0, + 0x49, 0x62, 0x40, 0xc6, 0x8b, 0x18, 0x18, 0xcc, 0x71, 0xe5, 0x27, 0x92, 0x2d, 0x02, 0xf2, 0x37, + 0x93, 0x8a, 0x07, 0xd6, 0x22, 0xa0, 0x84, 0x03, 0x10, 0x1f, 0x40, 0x2b, 0x7a, 0x40, 0x7c, 0x07, + 0x54, 0x8b, 0xe6, 0x44, 0x61, 0xb3, 0x1c, 0x21, 0x36, 0x27, 0x8a, 0x90, 0x45, 0x38, 0xcb, 0x39, + 0x84, 0xa1, 0x08, 0x4b, 0x70, 0x59, 0x84, 0x6c, 0x19, 0xde, 0xa0, 0x23, 0xa9, 0x0a, 0xc0, 0x61, + 0x11, 0x51, 0x89, 0x61, 0xd4, 0xa2, 0x51, 0x8b, 0x46, 0xb8, 0x45, 0x0c, 0x0c, 0x33, 0x75, 0x81, + 0x86, 0xb7, 0xa3, 0x15, 0x39, 0xed, 0x20, 0x71, 0x2a, 0x5b, 0x84, 0xbf, 0x7c, 0xa3, 0x7a, 0xd0, + 0x11, 0x83, 0x01, 0xfa, 0xc4, 0x94, 0x7d, 0x09, 0xb7, 0x71, 0x10, 0x00, 0x00, 0x00, 0x00, 0x49, + 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +}; + +const BITMAP_OPAQUE duplicate_text_xpm[1] = {{ png, sizeof( png ), "duplicate_text_xpm" }}; + +//EOF diff --git a/bitmaps_png/sources/duplicate_line.svg b/bitmaps_png/sources/duplicate_line.svg new file mode 100644 index 0000000000..3f19b6fd2f --- /dev/null +++ b/bitmaps_png/sources/duplicate_line.svg @@ -0,0 +1,95 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/bitmaps_png/sources/duplicate_pad.svg b/bitmaps_png/sources/duplicate_pad.svg new file mode 100644 index 0000000000..6841fbd5e5 --- /dev/null +++ b/bitmaps_png/sources/duplicate_pad.svg @@ -0,0 +1,70 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/bitmaps_png/sources/duplicate_text.svg b/bitmaps_png/sources/duplicate_text.svg new file mode 100644 index 0000000000..419b9452f5 --- /dev/null +++ b/bitmaps_png/sources/duplicate_text.svg @@ -0,0 +1,90 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/common/base_units.cpp b/common/base_units.cpp index 846c8b0eb3..7cf035c470 100644 --- a/common/base_units.cpp +++ b/common/base_units.cpp @@ -97,6 +97,9 @@ double To_User_Unit( EDA_UNITS_T aUnit, double aValue ) case INCHES: return IU_TO_IN( aValue ); + case DEGREES: + return aValue / 10.0f; + default: return aValue; } @@ -246,6 +249,10 @@ wxString StringFromValue( EDA_UNITS_T aUnit, int aValue, bool aAddUnitSymbol ) stringValue += _( " mm" ); break; + case DEGREES: + stringValue += _( " deg" ); + break; + case UNSCALED_UNITS: break; } @@ -277,6 +284,11 @@ double From_User_Unit( EDA_UNITS_T aUnit, double aValue ) value = IN_TO_IU( aValue ); break; + case DEGREES: + // Convert to "decidegrees" + value = aValue * 10; + break; + default: case UNSCALED_UNITS: value = aValue; @@ -286,7 +298,7 @@ double From_User_Unit( EDA_UNITS_T aUnit, double aValue ) } -int ValueFromString( EDA_UNITS_T aUnits, const wxString& aTextValue ) +double DoubleValueFromString( EDA_UNITS_T aUnits, const wxString& aTextValue ) { double value; double dtmp = 0; @@ -328,22 +340,39 @@ int ValueFromString( EDA_UNITS_T aUnits, const wxString& aTextValue ) // Check the optional unit designator (2 ch significant) wxString unit( buf.Mid( brk_point ).Strip( wxString::leading ).Left( 2 ).Lower() ); - if( unit == wxT( "in" ) || unit == wxT( "\"" ) ) + if( aUnits == INCHES || aUnits == MILLIMETRES ) { - aUnits = INCHES; + if( unit == wxT( "in" ) || unit == wxT( "\"" ) ) + { + aUnits = INCHES; + } + else if( unit == wxT( "mm" ) ) + { + aUnits = MILLIMETRES; + } + else if( unit == wxT( "mi" ) || unit == wxT( "th" ) ) // Mils or thous + { + aUnits = INCHES; + dtmp /= 1000; + } } - else if( unit == wxT( "mm" ) ) + else if( aUnits == DEGREES ) { - aUnits = MILLIMETRES; - } - else if( unit == wxT( "mi" ) || unit == wxT( "th" ) ) // Mils or thous - { - aUnits = INCHES; - dtmp /= 1000; + if( unit == wxT( "ra" ) ) // Radians + { + dtmp *= 180.0f / M_PI; + } } value = From_User_Unit( aUnits, dtmp ); + return value; +} + + +int ValueFromString( EDA_UNITS_T aUnits, const wxString& aTextValue ) +{ + double value = DoubleValueFromString( aUnits, aTextValue ); return KiROUND( value ); } diff --git a/common/block_commande.cpp b/common/block_commande.cpp index 990fa6cee0..73e5223d6c 100644 --- a/common/block_commande.cpp +++ b/common/block_commande.cpp @@ -64,6 +64,7 @@ void BLOCK_SELECTOR::SetMessageBlock( EDA_DRAW_FRAME* frame ) case BLOCK_MOVE: // Move case BLOCK_PRESELECT_MOVE: // Move with preselection list + case BLOCK_MOVE_EXACT: msg = _( "Block Move" ); break; diff --git a/common/common.cpp b/common/common.cpp index 51d7b96e25..dbee8dcd41 100644 --- a/common/common.cpp +++ b/common/common.cpp @@ -144,6 +144,10 @@ wxString ReturnUnitSymbol( EDA_UNITS_T aUnit, const wxString& formatString ) tmp = _( "mm" ); break; + case DEGREES: + label = _( "\u00b0" ); // Ring symbol + break; + case UNSCALED_UNITS: break; } @@ -171,6 +175,10 @@ wxString GetUnitsLabel( EDA_UNITS_T aUnit ) label = _( "millimeters" ); break; + case DEGREES: + label = _( "degrees" ); + break; + case UNSCALED_UNITS: label = _( "units" ); break; @@ -194,6 +202,10 @@ wxString GetAbbreviatedUnitsLabel( EDA_UNITS_T aUnit ) label = _( "mm" ); break; + case DEGREES: + label = _( "deg" ); + break; + case UNSCALED_UNITS: break; } diff --git a/common/tool/tool_manager.cpp b/common/tool/tool_manager.cpp index 35bd3af2d0..d1f842612d 100644 --- a/common/tool/tool_manager.cpp +++ b/common/tool/tool_manager.cpp @@ -195,7 +195,8 @@ private: TOOL_MANAGER::TOOL_MANAGER() : - m_model( NULL ), m_view( NULL ), m_viewControls( NULL ), m_editFrame( NULL ) + m_model( NULL ), m_view( NULL ), m_viewControls( NULL ), m_editFrame( NULL ), + m_undoInhibit( false ) { m_actionMgr = new ACTION_MANAGER( this ); @@ -713,3 +714,23 @@ bool TOOL_MANAGER::isActive( TOOL_BASE* aTool ) // Just check if the tool is on the active tools stack return std::find( m_activeTools.begin(), m_activeTools.end(), aTool->GetId() ) != m_activeTools.end(); } + + +void TOOL_MANAGER::IncUndoInhibit() +{ + m_undoInhibit++; +} + + +void TOOL_MANAGER::DecUndoInhibit() +{ + m_undoInhibit--; + + wxASSERT_MSG( m_undoInhibit >= 0, wxT( "Undo inhibit count decremented past zero" ) ); +} + + +bool TOOL_MANAGER::IsUndoInhibited() const +{ + return m_undoInhibit > 0; +} diff --git a/eeschema/block_libedit.cpp b/eeschema/block_libedit.cpp index 794ce6a52e..cf0430110f 100644 --- a/eeschema/block_libedit.cpp +++ b/eeschema/block_libedit.cpp @@ -197,6 +197,9 @@ bool LIB_EDIT_FRAME::HandleBlockEnd( wxDC* DC ) case BLOCK_SELECT_ITEMS_ONLY: break; + + case BLOCK_MOVE_EXACT: + break; } if( !nextCmd ) diff --git a/eeschema/sch_base_frame.cpp b/eeschema/sch_base_frame.cpp index e32d89dd8a..a20ee98614 100644 --- a/eeschema/sch_base_frame.cpp +++ b/eeschema/sch_base_frame.cpp @@ -156,6 +156,11 @@ void SCH_BASE_FRAME::UpdateStatusBar() locformatter = wxT( "dx %.2f dy %.2f d %.2f" ); break; + // Not a length unit, shouldn't be possible in g_UserUnit? + case DEGREES: + wxASSERT_MSG( false, wxT( "Not a length unit: " + g_UserUnit ) ); + // no break + case UNSCALED_UNITS: absformatter = wxT( "X %f Y %f" ); locformatter = wxT( "dx %f dy %f d %f" ); diff --git a/gerbview/gerbview_frame.cpp b/gerbview/gerbview_frame.cpp index 29b61c6ca8..bbf7e92ab1 100644 --- a/gerbview/gerbview_frame.cpp +++ b/gerbview/gerbview_frame.cpp @@ -843,6 +843,10 @@ void GERBVIEW_FRAME::UpdateStatusBar() formatter = wxT( "Ro %.5f Th %.1f" ); break; + case DEGREES: + wxASSERT_MSG( false, wxT( "Not a length unit " + g_UserUnit ) ); + // no break + case UNSCALED_UNITS: formatter = wxT( "Ro %f Th %f" ); break; @@ -871,6 +875,10 @@ void GERBVIEW_FRAME::UpdateStatusBar() locformatter = wxT( "dx %.5f dy %.5f dist %.3f" ); break; + case DEGREES: + wxASSERT_MSG( false, wxT( "Not a length unit " + g_UserUnit ) ); + // no break + case UNSCALED_UNITS: absformatter = wxT( "X %f Y %f" ); locformatter = wxT( "dx %f dy %f d %f" ); diff --git a/include/base_units.h b/include/base_units.h index 2858cc7388..c4092b1dd9 100644 --- a/include/base_units.h +++ b/include/base_units.h @@ -148,10 +148,21 @@ wxString& operator <<( wxString& aString, const wxPoint& aPoint ); void PutValueInLocalUnits( wxTextCtrl& aTextCtr, int aValue ); /** - * Return in internal units the value "val" given in inch or mm + * Return in internal units the value "val" given in a real unit + * such as "in", "mm" or "deg" */ double From_User_Unit( EDA_UNITS_T aUnit, double aValue ); + +/** + * Function DoubleValueFromString + * converts \a aTextValue to a double + * @param aUnits The units of \a aTextValue. + * @param aTextValue A reference to a wxString object containing the string to convert. + * @return A double representing that value in internal units + */ +double DoubleValueFromString( EDA_UNITS_T aUnits, const wxString& aTextValue ); + /** * Function ValueFromString * converts \a aTextValue in \a aUnits to internal units used by the application. diff --git a/include/bitmaps.h b/include/bitmaps.h index 3f8482951a..a5364bc684 100644 --- a/include/bitmaps.h +++ b/include/bitmaps.h @@ -174,6 +174,9 @@ EXTERN_BITMAP( drag_segment_withslope_xpm ) EXTERN_BITMAP( drag_track_segment_xpm ) EXTERN_BITMAP( drc_off_xpm ) EXTERN_BITMAP( drc_xpm ) +EXTERN_BITMAP( duplicate_line_xpm ) +EXTERN_BITMAP( duplicate_pad_xpm ) +EXTERN_BITMAP( duplicate_text_xpm ) EXTERN_BITMAP( edges_sketch_xpm ) EXTERN_BITMAP( edit_comp_footprint_xpm ) EXTERN_BITMAP( edit_component_xpm ) diff --git a/include/block_commande.h b/include/block_commande.h index cf2c140237..8d2b94fa0e 100644 --- a/include/block_commande.h +++ b/include/block_commande.h @@ -62,6 +62,7 @@ typedef enum { BLOCK_ZOOM, BLOCK_ABORT, BLOCK_PRESELECT_MOVE, + BLOCK_MOVE_EXACT, BLOCK_SELECT_ITEMS_ONLY, BLOCK_MIRROR_X, BLOCK_MIRROR_Y diff --git a/include/class_board_item.h b/include/class_board_item.h index 19e3f9ab6e..f1cb93247e 100644 --- a/include/class_board_item.h +++ b/include/class_board_item.h @@ -89,6 +89,16 @@ public: virtual const wxPoint& GetPosition() const = 0; + /** + * Function GetCenter() + * + * This defaults to the same point as returned by GetPosition(), unless + * overridden + * + * @return centre point of the item + */ + virtual const wxPoint GetCenter() const { return GetPosition(); } + virtual void SetPosition( const wxPoint& aPos ) = 0; /** diff --git a/include/common.h b/include/common.h index ed663942e9..337196ad60 100644 --- a/include/common.h +++ b/include/common.h @@ -166,7 +166,8 @@ inline int Mils2mm( double x ) { return KiROUND( x * 25.4 / 1000. ); } enum EDA_UNITS_T { INCHES = 0, MILLIMETRES = 1, - UNSCALED_UNITS = 2 + UNSCALED_UNITS = 2, + DEGREES = 3, }; diff --git a/include/id.h b/include/id.h index d37c8149b0..32ea99c4ea 100644 --- a/include/id.h +++ b/include/id.h @@ -158,6 +158,7 @@ enum main_id ID_POPUP_CANCEL_CURRENT_COMMAND, ID_POPUP_CLOSE_CURRENT_TOOL, ID_POPUP_MOVE_BLOCK, + ID_POPUP_MOVE_BLOCK_EXACT, ID_POPUP_DRAG_BLOCK, ID_POPUP_COPY_BLOCK, ID_POPUP_ROTATE_BLOCK, diff --git a/include/tool/tool_manager.h b/include/tool/tool_manager.h index 3805fe85a8..2323919881 100644 --- a/include/tool/tool_manager.h +++ b/include/tool/tool_manager.h @@ -301,6 +301,29 @@ public: return actionList; } + /** + * Increments the undo inhibit counter. This will indicate that tools + * should not create an undo point, as another tool is doing it already, + * and considers that its operation is atomic, even if it calls another one + * (for example a duplicate calls a move) + */ + void IncUndoInhibit(); + + /** + * Decrements the inhibit counter. An assert is raised if the counter drops + * below zero + */ + void DecUndoInhibit(); + + /** + * Report if the tool manager has been told at least once that undo + * points should not be created. This can be ignored if the undo point + * is still required. + * + * @return true if undo are inhibited + */ + bool IsUndoInhibited() const; + private: struct TOOL_STATE; typedef std::pair TRANSITION; @@ -428,6 +451,9 @@ private: /// Flag saying if the currently processed event should be passed to other tools. bool m_passEvent; + + /// Counter of undo inhibitions. When zero, undo is not inhibited + int m_undoInhibit; }; #endif diff --git a/pagelayout_editor/pl_editor_frame.cpp b/pagelayout_editor/pl_editor_frame.cpp index 699c02be2c..bfa04ada50 100644 --- a/pagelayout_editor/pl_editor_frame.cpp +++ b/pagelayout_editor/pl_editor_frame.cpp @@ -471,6 +471,10 @@ void PL_EDITOR_FRAME::UpdateStatusBar() SetStatusText( _("mm"), 5 ); break; + case DEGREES: + wxASSERT_MSG( false, wxT( "Not a length unit: " + g_UserUnit ) ); + // no break + case UNSCALED_UNITS: SetStatusText( wxEmptyString, 5 ); break; diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 0e916943cf..615d99afa7 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -104,6 +104,8 @@ set( PCBNEW_DIALOGS dialogs/dialog_pcb_text_properties_base.cpp dialogs/dialog_pns_settings.cpp dialogs/dialog_pns_settings_base.cpp + dialogs/dialog_move_exact.cpp + dialogs/dialog_move_exact_base.cpp dialogs/dialog_non_copper_zones_properties.cpp dialogs/dialog_non_copper_zones_properties_base.cpp dialogs/dialog_pad_properties.cpp diff --git a/pcbnew/basepcbframe.cpp b/pcbnew/basepcbframe.cpp index 6f076ae8a9..a780b6b2fe 100644 --- a/pcbnew/basepcbframe.cpp +++ b/pcbnew/basepcbframe.cpp @@ -607,6 +607,7 @@ GENERAL_COLLECTORS_GUIDE PCB_BASE_FRAME::GetCollectorsGuide() return guide; } + void PCB_BASE_FRAME::SetToolID( int aId, int aCursor, const wxString& aToolMsg ) { bool redraw = false; @@ -674,6 +675,10 @@ void PCB_BASE_FRAME::UpdateStatusBar() formatter = wxT( "Ro %.6f Th %.1f" ); break; + case DEGREES: + wxASSERT_MSG( false, wxT( "Not a length unit " + g_UserUnit ) ); + // no break + case UNSCALED_UNITS: formatter = wxT( "Ro %f Th %f" ); break; @@ -703,6 +708,10 @@ void PCB_BASE_FRAME::UpdateStatusBar() locformatter = wxT( "dx %.6f dy %.6f dist %.3f" ); break; + case DEGREES: // not a length unit, shouldn't be possible in g_UserUnit? + wxASSERT_MSG( false, wxT( "Not a length unit: " + g_UserUnit ) ); + //no break; + case UNSCALED_UNITS: absformatter = wxT( "X %f Y %f" ); locformatter = wxT( "dx %f dy %f dist %f" ); @@ -804,6 +813,7 @@ void PCB_BASE_FRAME::updateGridSelectBox() // Update grid values with the current units setting. m_gridSelectBox->Clear(); + wxArrayString gridsList; int icurr = GetScreen()->BuildGridsChoiceList( gridsList, g_UserUnit != INCHES ); diff --git a/pcbnew/block_module_editor.cpp b/pcbnew/block_module_editor.cpp index 2898af9260..4a9f1ea0ec 100644 --- a/pcbnew/block_module_editor.cpp +++ b/pcbnew/block_module_editor.cpp @@ -53,14 +53,19 @@ #include #include +#include + #define BLOCK_COLOR BROWN // Functions defined here, but used also in other files -// These 2 functions are used in modedit to rotate or mirror the whole footprint -// so they are called with force_all = true +// These 3 functions are used in modedit to rotate, mirror or move the +// whole footprint so they are called with force_all = true void MirrorMarkedItems( MODULE* module, wxPoint offset, bool force_all = false ); void RotateMarkedItems( MODULE* module, wxPoint offset, bool force_all = false ); +void MoveMarkedItemsExactly( MODULE* module, const wxPoint& centre, + const wxPoint& translation, double rotation, + bool force_all = false ); // Local functions: static void DrawMovingBlockOutlines( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aPosition, @@ -166,6 +171,26 @@ bool FOOTPRINT_EDIT_FRAME::HandleBlockEnd( wxDC* DC ) break; + case BLOCK_MOVE_EXACT: + itemsCount = MarkItemsInBloc( currentModule, GetScreen()->m_BlockLocate ); + + if( itemsCount ) + { + wxPoint translation; + double rotation = 0; + + DIALOG_MOVE_EXACT dialog( this, translation, rotation ); + int ret = dialog.ShowModal(); + + if( ret == DIALOG_MOVE_EXACT::MOVE_OK ) + { + SaveCopyInUndoList( currentModule, UR_MODEDIT ); + const wxPoint blockCentre = GetScreen()->m_BlockLocate.Centre(); + MoveMarkedItemsExactly( currentModule, blockCentre, translation, rotation ); + } + } + break; + case BLOCK_PRESELECT_MOVE: // Move with preselection list nextcmd = true; m_canvas->SetMouseCaptureCallback( DrawMovingBlockOutlines ); @@ -194,7 +219,6 @@ bool FOOTPRINT_EDIT_FRAME::HandleBlockEnd( wxDC* DC ) RotateMarkedItems( currentModule, GetScreen()->m_BlockLocate.Centre() ); break; - case BLOCK_MIRROR_X: case BLOCK_MIRROR_Y: case BLOCK_FLIP: // mirror @@ -706,6 +730,78 @@ void ClearMarkItems( MODULE* module ) } +void MoveMarkedItemsExactly( MODULE* module, const wxPoint& centre, + const wxPoint& translation, + double rotation, bool force_all ) +{ + if( module == NULL ) + return; + + if( module->Reference().IsSelected() || force_all ) + { + module->Reference().RotateTransformWithModule( centre, rotation ); + module->Reference().MoveTransformWithModule( translation ); + } + + if( module->Value().IsSelected() || force_all ) + { + module->Value().RotateTransformWithModule( centre, rotation ); + module->Value().MoveTransformWithModule( translation ); + } + + D_PAD* pad = module->Pads(); + + for( ; pad != NULL; pad = pad->Next() ) + { + if( !pad->IsSelected() && !force_all ) + continue; + + // rotate about centre point, + wxPoint newPos = pad->GetPosition(); + RotatePoint( &newPos, centre, rotation ); + + // shift and update + newPos += translation; + pad->SetPosition( newPos ); + pad->SetPos0( newPos ); + + // finally apply rotation to the pad itself + pad->Rotate( newPos, rotation ); + } + + EDA_ITEM* item = module->GraphicalItems(); + + for( ; item != NULL; item = item->Next() ) + { + if( !item->IsSelected() && !force_all ) + continue; + + switch( item->Type() ) + { + case PCB_MODULE_TEXT_T: + { + TEXTE_MODULE* text = static_cast( item ); + + text->RotateTransformWithModule( centre, rotation ); + text->MoveTransformWithModule( translation ); + break; + } + case PCB_MODULE_EDGE_T: + { + EDGE_MODULE* em = static_cast( item ); + em->Rotate( centre, rotation ); + em->Move( translation ); + break; + } + default: + ; + } + } + + ClearMarkItems( module ); +} + + /* Mark items inside rect. * Items are inside rect when an end point is inside rect */ diff --git a/pcbnew/class_drawsegment.cpp b/pcbnew/class_drawsegment.cpp index 2ff4342e29..ef5ea9c9a8 100644 --- a/pcbnew/class_drawsegment.cpp +++ b/pcbnew/class_drawsegment.cpp @@ -94,10 +94,41 @@ void DRAWSEGMENT::Copy( DRAWSEGMENT* source ) void DRAWSEGMENT::Rotate( const wxPoint& aRotCentre, double aAngle ) { - RotatePoint( &m_Start, aRotCentre, aAngle ); - RotatePoint( &m_End, aRotCentre, aAngle ); -} + switch( m_Shape ) + { + case S_ARC: + case S_SEGMENT: + case S_CIRCLE: + // these can all be done by just rotating the start and end points + RotatePoint( &m_Start, aRotCentre, aAngle); + RotatePoint( &m_End, aRotCentre, aAngle); + break; + case S_POLYGON: + for( unsigned ii = 0; ii < m_PolyPoints.size(); ii++ ) + { + RotatePoint( &m_PolyPoints[ii], aRotCentre, aAngle); + } + break; + + case S_CURVE: + RotatePoint( &m_Start, aRotCentre, aAngle); + RotatePoint( &m_End, aRotCentre, aAngle); + + for( unsigned int ii = 0; ii < m_BezierPoints.size(); ii++ ) + { + RotatePoint( &m_BezierPoints[ii], aRotCentre, aAngle); + } + break; + + case S_RECT: + default: + // un-handled edge transform + wxASSERT_MSG( false, wxT( "DRAWSEGMENT::Rotate not implemented for " + + ShowShape( m_Shape ) ) ); + break; + } +}; void DRAWSEGMENT::Flip( const wxPoint& aCentre ) { @@ -112,6 +143,37 @@ void DRAWSEGMENT::Flip( const wxPoint& aCentre ) SetLayer( FlipLayer( GetLayer() ) ); } +const wxPoint DRAWSEGMENT::GetCenter() const +{ + wxPoint c; + + switch( m_Shape ) + { + case S_ARC: + case S_CIRCLE: + c = m_Start; + break; + + case S_SEGMENT: + // Midpoint of the line + c = ( GetStart() + GetEnd() ) / 2; + break; + + case S_POLYGON: + case S_RECT: + case S_CURVE: + c = GetBoundingBox().Centre(); + break; + + default: + wxASSERT_MSG( false, "DRAWSEGMENT::GetCentre not implemented for shape" + + ShowShape( GetShape() ) ); + break; + } + + return c; +} + const wxPoint DRAWSEGMENT::GetArcEnd() const { wxPoint endPoint; // start of arc diff --git a/pcbnew/class_drawsegment.h b/pcbnew/class_drawsegment.h index 7316b4e554..10920dd1a6 100644 --- a/pcbnew/class_drawsegment.h +++ b/pcbnew/class_drawsegment.h @@ -117,7 +117,7 @@ public: // m_Start, m_End, and m_Angle. // No Set...() function for these attributes. - const wxPoint& GetCenter() const { return m_Start; } + const wxPoint GetCenter() const; //override const wxPoint& GetArcStart() const { return m_End; } const wxPoint GetArcEnd() const; diff --git a/pcbnew/class_module.cpp b/pcbnew/class_module.cpp index ecf7c45caa..ac669085af 100644 --- a/pcbnew/class_module.cpp +++ b/pcbnew/class_module.cpp @@ -1111,3 +1111,101 @@ void MODULE::SetOrientation( double newangle ) CalculateBoundingBox(); } +BOARD_ITEM* MODULE::DuplicateAndAddItem( const BOARD_ITEM* aItem, + bool aIncrementPadNumbers ) +{ + BOARD_ITEM* new_item = NULL; + + switch( aItem->Type() ) + { + case PCB_PAD_T: + { + D_PAD* new_pad = new D_PAD( *static_cast( aItem ) ); + + if( aIncrementPadNumbers ) + { + // Take the next available pad number + new_pad->IncrementPadName( true, true ); + } + + Pads().PushBack( new_pad ); + new_item = new_pad; + break; + } + case PCB_MODULE_TEXT_T: + { + const TEXTE_MODULE* old_text = static_cast( aItem ); + + // do not duplicate value or reference fields + // (there can only be one of each) + if( old_text->GetType() == TEXTE_MODULE::TEXT_is_DIVERS ) + { + TEXTE_MODULE* new_text = new TEXTE_MODULE( *old_text ); + + GraphicalItems().PushBack( new_text ); + new_item = new_text; + } + break; + } + case PCB_MODULE_EDGE_T: + { + EDGE_MODULE* new_edge = new EDGE_MODULE( + *static_cast(aItem) ); + + GraphicalItems().PushBack( new_edge ); + new_item = new_edge; + break; + } + default: + // Un-handled item for duplication + wxASSERT_MSG( false, "Duplication not supported for items of class " + + aItem->GetClass() ); + break; + } + + return new_item; +} + +wxString MODULE::GetNextPadName( bool aFillSequenceGaps ) const +{ + std::set usedNumbers; + + // Create a set of used pad numbers + for( D_PAD* pad = Pads(); pad; pad = pad->Next() ) + { + wxString padName = pad->GetPadName(); + int padNumber = 0; + int base = 1; + + // Trim and extract the trailing numeric part + while( padName.Len() && padName.Last() >= '0' && padName.Last() <= '9' ) + { + padNumber += ( padName.Last() - '0' ) * base; + padName.RemoveLast(); + base *= 10; + } + + usedNumbers.insert( padNumber ); + } + + // By default go to the end of the sequence + int candidate = *usedNumbers.end(); + + // Filling in gaps in pad numbering + if( aFillSequenceGaps ) + { + // start at the beginning + candidate = *usedNumbers.begin(); + + for( std::set::iterator it = usedNumbers.begin(), + itEnd = usedNumbers.end(); it != itEnd; ++it ) + { + if( *it - candidate > 1 ) + break; + + candidate = *it; + } + } + + return wxString::Format( wxT( "%i" ), ++candidate ); +} diff --git a/pcbnew/class_module.h b/pcbnew/class_module.h index 266b033e3f..9790822c62 100644 --- a/pcbnew/class_module.h +++ b/pcbnew/class_module.h @@ -453,6 +453,16 @@ public: */ unsigned GetPadCount( INCLUDE_NPTH_T aIncludeNPTH = INCLUDE_NPTH_T( INCLUDE_NPTH ) ) const; + /** + * Function GetNextPadName + * returns the next available pad name in the module + * + * @param aFillSequenceGaps true if the numbering should "fill in" gaps in + * the sequence, else return the highest value + 1 + * @return the next available pad name + */ + wxString GetNextPadName( bool aFillSequenceGaps ) const; + double GetArea() const { return m_Surface; } time_t GetLink() const { return m_Link; } @@ -464,6 +474,9 @@ public: int GetPlacementCost90() const { return m_CntRot90; } void SetPlacementCost90( int aCost ) { m_CntRot90 = aCost; } + BOARD_ITEM* DuplicateAndAddItem( const BOARD_ITEM* item, + bool aIncrementPadNumbers ); + /** * Function Add3DModel * adds \a a3DModel definition to the end of the 3D model list. diff --git a/pcbnew/class_pad.cpp b/pcbnew/class_pad.cpp index 4abd48edcf..840355a54e 100644 --- a/pcbnew/class_pad.cpp +++ b/pcbnew/class_pad.cpp @@ -408,6 +408,16 @@ void D_PAD::SetPadName( const wxString& name ) } +void D_PAD::IncrementPadName( bool aSkipUnconnectable, bool aFillSequenceGaps ) +{ + bool skip = aSkipUnconnectable + && ( GetAttribute() == PAD_HOLE_NOT_PLATED ); + + if( !skip ) + SetPadName(GetParent()->GetNextPadName( aFillSequenceGaps )); +} + + void D_PAD::Copy( D_PAD* source ) { if( source == NULL ) diff --git a/pcbnew/class_pad.h b/pcbnew/class_pad.h index c68596bd29..cc453a5cd3 100644 --- a/pcbnew/class_pad.h +++ b/pcbnew/class_pad.h @@ -103,6 +103,16 @@ public: void SetPadName( const wxString& name ); // Change pad name const wxString GetPadName() const; + /** + * Function IncrementPadName + * + * Increments the pad name to the next available name in the module. + * + * @param aSkipUnconnectable skips any pads that are not connectable (for + * example NPTH) + */ + void IncrementPadName( bool aSkipUnconnectable, bool aFillSequenceGaps ); + bool PadNameEqual( const D_PAD* other ) const { return m_NumPadName == other->m_NumPadName; // hide tricks behind sensible API diff --git a/pcbnew/dialogs/dialog_move_exact.cpp b/pcbnew/dialogs/dialog_move_exact.cpp new file mode 100644 index 0000000000..0f8c4d9919 --- /dev/null +++ b/pcbnew/dialogs/dialog_move_exact.cpp @@ -0,0 +1,208 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 John Beard, john.j.beard@gmail.com + * Copyright (C) 1992-2014 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 + */ + +#include +#include + +#include + +#include "dialog_move_exact.h" + +// initialise statics +DIALOG_MOVE_EXACT::MOVE_EXACT_OPTIONS DIALOG_MOVE_EXACT::m_options; + + +DIALOG_MOVE_EXACT::DIALOG_MOVE_EXACT( PCB_BASE_FRAME* aParent, + wxPoint& translation, double& rotation ): + DIALOG_MOVE_EXACT_BASE( aParent ), + m_translation( translation ), + m_rotation( rotation ) +{ + // set the unit labels + m_xUnit->SetLabelText( GetAbbreviatedUnitsLabel( g_UserUnit ) ); + m_yUnit->SetLabelText( GetAbbreviatedUnitsLabel( g_UserUnit ) ); + // rotation is always degrees + m_rotUnit->SetLabelText( _( "deg" ) ); + + // tabbing goes through the entries in sequence + m_yEntry->MoveAfterInTabOrder( m_xEntry ); + m_rotEntry->MoveAfterInTabOrder( m_yEntry ); + + // and set up the entries according to the saved options + m_polarCoords->SetValue( m_options.polarCoords ); + m_xEntry->SetValue( wxString::FromDouble( m_options.entry1 ) ); + m_yEntry->SetValue( wxString::FromDouble( m_options.entry2 ) ); + m_rotEntry->SetValue( wxString::FromDouble( m_options.entryRotation ) ); +} + + +DIALOG_MOVE_EXACT::~DIALOG_MOVE_EXACT() +{ +} + + +/*! + * Convert a given Cartesian point into a polar representation. + * + * Linear units are not considered, the answer is in the same units as given + * Angle is returned in degrees + */ +void DIALOG_MOVE_EXACT::ToPolarDeg( double x, double y, double& r, double& q ) +{ + // convert to polar coordinates + r = hypot ( x, y ); + + q = ( r != 0) ? RAD2DEG( atan2( y, x ) ) : 0; +} + + +/*! + * Get the (Cartesian) translation described by the text entries + * @param val output translation vector + * @param polar interpret as polar coords + * @return false if error (though the text conversion functions don't report errors) + */ +bool DIALOG_MOVE_EXACT::GetTranslationInIU ( wxPoint& val, bool polar ) +{ + if( polar ) + { + const int r = ValueFromTextCtrl( *m_xEntry ); + const double q = DoubleValueFromString( DEGREES, m_yEntry->GetValue() ); + + val.x = r * cos( DEG2RAD( q / 10.0 ) ); + val.y = r * sin( DEG2RAD( q / 10.0 ) ); + } + else + { + // direct read + val.x = ValueFromTextCtrl( *m_xEntry ); + val.y = ValueFromTextCtrl( *m_yEntry ); + } + + // no validation to do here, but in future, you could return false here + return true; +} + + +void DIALOG_MOVE_EXACT::OnPolarChanged( wxCommandEvent& event ) +{ + bool newPolar = m_polarCoords->IsChecked(); + wxPoint val; + + // get the value as previously stored + GetTranslationInIU( val, !newPolar ); + + if( newPolar ) + { + // convert to polar coordinates + double r, q; + ToPolarDeg( val.x, val.y, r, q); + + PutValueInLocalUnits( *m_xEntry, round( r / 10.0) * 10 ); + m_xLabel->SetLabelText( wxT( "r:" ) ); + + m_yEntry->SetValue( wxString::FromDouble( q ) ); + m_yLabel->SetLabelText( wxT( "\u03b8:" ) ); // theta + + m_yUnit->SetLabelText( GetAbbreviatedUnitsLabel( DEGREES ) ); + } + else + { + // vector is already in Cartesian, so just render out + + // note - round off the last decimal place (10nm) to prevent + // (some) rounding causing errors when round-tripping + // you can never eliminate entirely, however + PutValueInLocalUnits( *m_xEntry, round( val.x / 10.0) * 10 ); + m_xLabel->SetLabelText( wxT( "x:" ) ); + + PutValueInLocalUnits( *m_yEntry, round( val.y / 10.0) * 10 ); + m_yLabel->SetLabelText( wxT( "y:" ) ); + + m_yUnit->SetLabelText( GetAbbreviatedUnitsLabel( g_UserUnit ) ); + } +} + + +void DIALOG_MOVE_EXACT::OnClear( wxCommandEvent& event ) +{ + wxObject* obj = event.GetEventObject(); + wxTextCtrl* entry = NULL; + + if( obj == m_clearX ) + { + entry = m_xEntry; + } + else if( obj == m_clearY ) + { + entry = m_yEntry; + } + else if( obj == m_clearRot ) + { + entry = m_rotEntry; + } + + if( entry ) + entry->SetValue( "0" ); +} + + +void DIALOG_MOVE_EXACT::OnCancelClick( wxCommandEvent& event ) +{ + EndModal( MOVE_ABORT ); +} + + +void DIALOG_MOVE_EXACT::OnOkClick( wxCommandEvent& event ) +{ + m_rotation = DoubleValueFromString( DEGREES, m_rotEntry->GetValue() ); + + // for the output, we only deliver a Cartesian vector + bool ok = GetTranslationInIU( m_translation, m_polarCoords->IsChecked() ); + + if ( ok ) + { + // save the settings + m_options.polarCoords = m_polarCoords->GetValue(); + m_xEntry->GetValue().ToDouble( &m_options.entry1 ); + m_yEntry->GetValue().ToDouble( &m_options.entry2 ); + m_rotEntry->GetValue().ToDouble( &m_options.entryRotation ); + + EndModal( MOVE_OK ); + } +} + + +/*! + * Reset a text field to be 0 if it was exited while blank + */ +void DIALOG_MOVE_EXACT::OnTextFocusLost( wxFocusEvent& event ) +{ + wxTextCtrl* obj = static_cast( event.GetEventObject() ); + + if( obj->GetValue().IsEmpty() ) + { + obj->SetValue("0"); + } +} diff --git a/pcbnew/dialogs/dialog_move_exact.fbp b/pcbnew/dialogs/dialog_move_exact.fbp new file mode 100644 index 0000000000..db4223f270 --- /dev/null +++ b/pcbnew/dialogs/dialog_move_exact.fbp @@ -0,0 +1,1281 @@ + + + + + + C++ + 1 + source_name + 0 + 0 + res + UTF-8 + connect + dialog_move_exact_base + 1000 + none + 1 + DIALOG_MOVE_EXACT_BASE + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + -1,-1 + DIALOG_MOVE_EXACT_BASE + + -1,-1 + wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER + DIALOG_SHIM; dialog_shim.h + Move item + + + + + + + + + + + + + + OnClose + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bMainSizer + wxVERTICAL + none + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Polar coordinates + + 0 + + + 0 + + 1 + m_polarCoords + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + OnPolarChanged + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + 4 + wxBOTH + 1 + + 0 + + fgSizer2 + wxFLEX_GROWMODE_SPECIFIED + none + 0 + 0 + + 5 + wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + x: + + 0 + + + 0 + + 1 + m_xLabel + 1 + + + protected + 1 + + Resizable + 1 + -1,-1 + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_xEntry + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + 0 + + + + + + + + + OnTextFocusLost + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + mm + + 0 + + + 0 + + 1 + m_xUnit + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + Load From Art Provider; wxART_DELETE; wxART_BUTTON + + 1 + 0 + 1 + + 1 + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + + wxID_CLEAR + Clear + + 0 + + + 0 + + 1 + m_clearX + 1 + + + protected + 1 + + Resizable + + 1 + + wxBU_AUTODRAW + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + OnClear + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + y: + + 0 + + + 0 + + 1 + m_yLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_yEntry + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + 0 + + + + + + + + + OnTextFocusLost + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + mm + + 0 + + + 0 + + 1 + m_yUnit + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + Load From Art Provider; wxART_DELETE; wxART_BUTTON + + 1 + 0 + 1 + + 1 + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + + wxID_CLEAR + Clear + + 0 + + + 0 + + 1 + m_clearY + 1 + + + protected + 1 + + Resizable + + 1 + + wxBU_AUTODRAW + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + OnClear + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Rotate: + + 0 + + + 0 + + 1 + m_rotLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_rotEntry + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + 0 + + + + + + + + + OnTextFocusLost + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + deg + + 0 + + + 0 + + 1 + m_rotUnit + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + Load From Art Provider; wxART_DELETE; wxART_BUTTON + + 1 + 0 + 1 + + 1 + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + + wxID_CLEAR + Clear + + 0 + + + 0 + + 1 + m_clearRot + 1 + + + protected + 1 + + Resizable + + 1 + + wxBU_AUTODRAW + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + OnClear + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + + m_stdButtons + protected + + OnCancelClick + + + + OnOkClick + + + + + + + + diff --git a/pcbnew/dialogs/dialog_move_exact.h b/pcbnew/dialogs/dialog_move_exact.h new file mode 100644 index 0000000000..b71f871551 --- /dev/null +++ b/pcbnew/dialogs/dialog_move_exact.h @@ -0,0 +1,87 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 John Beard, john.j.beard@gmail.com + * Copyright (C) 1992-2014 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 + */ + +#ifndef __DIALOG_MOVE_EXACT__ +#define __DIALOG_MOVE_EXACT__ + +// Include the wxFormBuider header base: +#include +#include + +class DIALOG_MOVE_EXACT : public DIALOG_MOVE_EXACT_BASE +{ +private: + + wxPoint& m_translation; + double& m_rotation; + +public: + + enum MOVE_EDIT_T + { + MOVE_ABORT, ///< if not changed or error + MOVE_OK, ///< if successfully changed + }; + + // Constructor and destructor + DIALOG_MOVE_EXACT( PCB_BASE_FRAME* aParent, wxPoint& translation, + double& rotation ); + ~DIALOG_MOVE_EXACT(); + +private: + + void OnTextFocusLost( wxFocusEvent& event ); + void OnPolarChanged( wxCommandEvent& event ); + void OnClear( wxCommandEvent& event ); + + void OnCancelClick( wxCommandEvent& event ); + void OnOkClick( wxCommandEvent& event ); + + void ToPolarDeg( double x, double y, double& r, double& q ); + bool GetTranslationInIU ( wxPoint& val, bool polar ); + + /** + * Persistent dialog options + */ + struct MOVE_EXACT_OPTIONS + { + bool polarCoords; + double entry1; + double entry2; + double entryRotation; + + MOVE_EXACT_OPTIONS(): + polarCoords(false), + entry1(0), + entry2(0), + entryRotation(0) + { + } + }; + + // persistent settings + static MOVE_EXACT_OPTIONS m_options; +}; + +#endif // __DIALOG_MOVE_EXACT__ diff --git a/pcbnew/dialogs/dialog_move_exact_base.cpp b/pcbnew/dialogs/dialog_move_exact_base.cpp new file mode 100644 index 0000000000..4f2644ac27 --- /dev/null +++ b/pcbnew/dialogs/dialog_move_exact_base.cpp @@ -0,0 +1,114 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 6 2014) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "dialog_move_exact_base.h" + +/////////////////////////////////////////////////////////////////////////// + +DIALOG_MOVE_EXACT_BASE::DIALOG_MOVE_EXACT_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + + wxBoxSizer* bMainSizer; + bMainSizer = new wxBoxSizer( wxVERTICAL ); + + m_polarCoords = new wxCheckBox( this, wxID_ANY, _("Polar coordinates"), wxDefaultPosition, wxDefaultSize, 0 ); + bMainSizer->Add( m_polarCoords, 0, wxALL|wxEXPAND, 5 ); + + wxFlexGridSizer* fgSizer2; + fgSizer2 = new wxFlexGridSizer( 0, 4, 0, 0 ); + fgSizer2->AddGrowableCol( 1 ); + fgSizer2->SetFlexibleDirection( wxBOTH ); + fgSizer2->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_xLabel = new wxStaticText( this, wxID_ANY, _("x:"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_xLabel->Wrap( -1 ); + fgSizer2->Add( m_xLabel, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5 ); + + m_xEntry = new wxTextCtrl( this, wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer2->Add( m_xEntry, 0, wxALL|wxEXPAND, 5 ); + + m_xUnit = new wxStaticText( this, wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 ); + m_xUnit->Wrap( -1 ); + fgSizer2->Add( m_xUnit, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT|wxALL, 5 ); + + m_clearX = new wxBitmapButton( this, wxID_CLEAR, wxArtProvider::GetBitmap( wxART_DELETE, wxART_BUTTON ), wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW ); + fgSizer2->Add( m_clearX, 0, wxALL, 5 ); + + m_yLabel = new wxStaticText( this, wxID_ANY, _("y:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_yLabel->Wrap( -1 ); + fgSizer2->Add( m_yLabel, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5 ); + + m_yEntry = new wxTextCtrl( this, wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer2->Add( m_yEntry, 0, wxALL|wxEXPAND, 5 ); + + m_yUnit = new wxStaticText( this, wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 ); + m_yUnit->Wrap( -1 ); + fgSizer2->Add( m_yUnit, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_clearY = new wxBitmapButton( this, wxID_CLEAR, wxArtProvider::GetBitmap( wxART_DELETE, wxART_BUTTON ), wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW ); + fgSizer2->Add( m_clearY, 0, wxALL, 5 ); + + m_rotLabel = new wxStaticText( this, wxID_ANY, _("Rotate:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_rotLabel->Wrap( -1 ); + fgSizer2->Add( m_rotLabel, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5 ); + + m_rotEntry = new wxTextCtrl( this, wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer2->Add( m_rotEntry, 0, wxALL|wxEXPAND, 5 ); + + m_rotUnit = new wxStaticText( this, wxID_ANY, _("deg"), wxDefaultPosition, wxDefaultSize, 0 ); + m_rotUnit->Wrap( -1 ); + fgSizer2->Add( m_rotUnit, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_clearRot = new wxBitmapButton( this, wxID_CLEAR, wxArtProvider::GetBitmap( wxART_DELETE, wxART_BUTTON ), wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW ); + fgSizer2->Add( m_clearRot, 0, wxALL, 5 ); + + + bMainSizer->Add( fgSizer2, 1, wxEXPAND, 5 ); + + m_stdButtons = new wxStdDialogButtonSizer(); + m_stdButtonsOK = new wxButton( this, wxID_OK ); + m_stdButtons->AddButton( m_stdButtonsOK ); + m_stdButtonsCancel = new wxButton( this, wxID_CANCEL ); + m_stdButtons->AddButton( m_stdButtonsCancel ); + m_stdButtons->Realize(); + + bMainSizer->Add( m_stdButtons, 0, wxALL|wxEXPAND, 5 ); + + + this->SetSizer( bMainSizer ); + this->Layout(); + bMainSizer->Fit( this ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_MOVE_EXACT_BASE::OnClose ) ); + m_polarCoords->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnPolarChanged ), NULL, this ); + m_xEntry->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextFocusLost ), NULL, this ); + m_clearX->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnClear ), NULL, this ); + m_yEntry->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextFocusLost ), NULL, this ); + m_clearY->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnClear ), NULL, this ); + m_rotEntry->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextFocusLost ), NULL, this ); + m_clearRot->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnClear ), NULL, this ); + m_stdButtonsCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnCancelClick ), NULL, this ); + m_stdButtonsOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnOkClick ), NULL, this ); +} + +DIALOG_MOVE_EXACT_BASE::~DIALOG_MOVE_EXACT_BASE() +{ + // Disconnect Events + this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_MOVE_EXACT_BASE::OnClose ) ); + m_polarCoords->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnPolarChanged ), NULL, this ); + m_xEntry->Disconnect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextFocusLost ), NULL, this ); + m_clearX->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnClear ), NULL, this ); + m_yEntry->Disconnect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextFocusLost ), NULL, this ); + m_clearY->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnClear ), NULL, this ); + m_rotEntry->Disconnect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextFocusLost ), NULL, this ); + m_clearRot->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnClear ), NULL, this ); + m_stdButtonsCancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnCancelClick ), NULL, this ); + m_stdButtonsOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnOkClick ), NULL, this ); + +} diff --git a/pcbnew/dialogs/dialog_move_exact_base.h b/pcbnew/dialogs/dialog_move_exact_base.h new file mode 100644 index 0000000000..e8072f8848 --- /dev/null +++ b/pcbnew/dialogs/dialog_move_exact_base.h @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 6 2014) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __DIALOG_MOVE_EXACT_BASE_H__ +#define __DIALOG_MOVE_EXACT_BASE_H__ + +#include +#include +#include +class DIALOG_SHIM; + +#include "dialog_shim.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class DIALOG_MOVE_EXACT_BASE +/////////////////////////////////////////////////////////////////////////////// +class DIALOG_MOVE_EXACT_BASE : public DIALOG_SHIM +{ + private: + + protected: + wxCheckBox* m_polarCoords; + wxStaticText* m_xLabel; + wxTextCtrl* m_xEntry; + wxStaticText* m_xUnit; + wxBitmapButton* m_clearX; + wxStaticText* m_yLabel; + wxTextCtrl* m_yEntry; + wxStaticText* m_yUnit; + wxBitmapButton* m_clearY; + wxStaticText* m_rotLabel; + wxTextCtrl* m_rotEntry; + wxStaticText* m_rotUnit; + wxBitmapButton* m_clearRot; + wxStdDialogButtonSizer* m_stdButtons; + wxButton* m_stdButtonsOK; + wxButton* m_stdButtonsCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnPolarChanged( wxCommandEvent& event ) { event.Skip(); } + virtual void OnTextFocusLost( wxFocusEvent& event ) { event.Skip(); } + virtual void OnClear( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancelClick( wxCommandEvent& event ) { event.Skip(); } + virtual void OnOkClick( wxCommandEvent& event ) { event.Skip(); } + + + public: + + DIALOG_MOVE_EXACT_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Move item"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~DIALOG_MOVE_EXACT_BASE(); + +}; + +#endif //__DIALOG_MOVE_EXACT_BASE_H__ diff --git a/pcbnew/hotkeys.cpp b/pcbnew/hotkeys.cpp index 006c0b2651..fc730fae98 100644 --- a/pcbnew/hotkeys.cpp +++ b/pcbnew/hotkeys.cpp @@ -108,6 +108,10 @@ static EDA_HOTKEY HkEditBoardItem( wxT( "Edit Item" ), HK_EDIT_ITEM, 'E' ); static EDA_HOTKEY HkFlipItem( wxT( "Flip Item" ), HK_FLIP_ITEM, 'F' ); static EDA_HOTKEY HkRotateItem( wxT( "Rotate Item" ), HK_ROTATE_ITEM, 'R' ); static EDA_HOTKEY HkMoveItem( wxT( "Move Item" ), HK_MOVE_ITEM, 'M' ); +static EDA_HOTKEY HkMoveItemExact( wxT( "Move Item Exactly" ), HK_MOVE_ITEM_EXACT, 'M' + GR_KB_CTRL ); +static EDA_HOTKEY HkDuplicateItem( wxT( "Duplicate Item" ), HK_DUPLICATE_ITEM, 'D' + GR_KB_CTRL ); +static EDA_HOTKEY HkDuplicateItemAndIncrement( wxT( "Duplicate Item and Increment" ), + HK_DUPLICATE_ITEM_AND_INCREMENT, 'D' + GR_KB_SHIFTCTRL ); static EDA_HOTKEY HkCopyItem( wxT( "Copy Item" ), HK_COPY_ITEM, 'C' ); static EDA_HOTKEY HkDragFootprint( wxT( "Drag Item" ), HK_DRAG_ITEM, 'G' ); static EDA_HOTKEY HkGetAndMoveFootprint( wxT( "Get and Move Footprint" ), HK_GET_AND_MOVE_FOOTPRINT, 'T' ); @@ -300,6 +304,7 @@ EDA_HOTKEY* board_edit_Hotkey_List[] = // List of hotkey descriptors for the module editor EDA_HOTKEY* module_edit_Hotkey_List[] = { &HkMoveItem, &HkRotateItem, &HkEditBoardItem, + &HkMoveItemExact, &HkDuplicateItem, &HkDuplicateItemAndIncrement, &HkDelete, &HkSaveModule, NULL diff --git a/pcbnew/hotkeys.h b/pcbnew/hotkeys.h index ca58a7fddd..7f6b5a0a8a 100644 --- a/pcbnew/hotkeys.h +++ b/pcbnew/hotkeys.h @@ -41,6 +41,7 @@ enum hotkey_id_commnand { HK_FLIP_ITEM, HK_COPY_ITEM, HK_MOVE_ITEM, + HK_MOVE_ITEM_EXACT, HK_DRAG_ITEM, HK_GET_AND_MOVE_FOOTPRINT, HK_LOCK_UNLOCK_FOOTPRINT, @@ -60,6 +61,8 @@ enum hotkey_id_commnand { HK_SWITCH_TRACK_DISPLAY_MODE, HK_FIND_ITEM, HK_EDIT_ITEM, + HK_DUPLICATE_ITEM, + HK_DUPLICATE_ITEM_AND_INCREMENT, HK_PLACE_ITEM, HK_SWITCH_TRACK_WIDTH_TO_NEXT, HK_SWITCH_TRACK_WIDTH_TO_PREVIOUS, diff --git a/pcbnew/hotkeys_board_editor.cpp b/pcbnew/hotkeys_board_editor.cpp index 02ecf4ff58..2281cb06db 100644 --- a/pcbnew/hotkeys_board_editor.cpp +++ b/pcbnew/hotkeys_board_editor.cpp @@ -896,7 +896,6 @@ bool PCB_EDIT_FRAME::OnHotkeyMoveItem( int aIdCommand ) return false; } - bool PCB_EDIT_FRAME::OnHotkeyPlaceItem( wxDC* aDC ) { BOARD_ITEM* item = GetCurItem(); diff --git a/pcbnew/hotkeys_module_editor.cpp b/pcbnew/hotkeys_module_editor.cpp index 918d8a493c..bc6a41f8c9 100644 --- a/pcbnew/hotkeys_module_editor.cpp +++ b/pcbnew/hotkeys_module_editor.cpp @@ -152,31 +152,84 @@ bool FOOTPRINT_EDIT_FRAME::OnHotKey( wxDC* aDC, int aHotKey, const wxPoint& aPos OnHotkeyMoveItem( HK_MOVE_ITEM ); break; + case HK_MOVE_ITEM_EXACT: + if( blockActive ) + { + cmd.SetId( ID_POPUP_MOVE_BLOCK_EXACT ); + GetEventHandler()->ProcessEvent( cmd ); + } + else + { + OnHotkeyMoveItemExact(); + } + break; + case HK_ROTATE_ITEM: OnHotkeyRotateItem( HK_ROTATE_ITEM ); break; + + case HK_DUPLICATE_ITEM: + case HK_DUPLICATE_ITEM_AND_INCREMENT: + OnHotkeyDuplicateItem( HK_Descr->m_Idcommand ); + break; } return true; } -bool FOOTPRINT_EDIT_FRAME::OnHotkeyEditItem( int aIdCommand ) +BOARD_ITEM* FOOTPRINT_EDIT_FRAME::PrepareItemForHotkey( bool failIfCurrentlyEdited ) { BOARD_ITEM* item = GetCurItem(); bool itemCurrentlyEdited = item && item->GetFlags(); bool blockActive = GetScreen()->m_BlockLocate.GetCommand() != BLOCK_IDLE; - if( itemCurrentlyEdited || blockActive ) - return false; + if( failIfCurrentlyEdited ) + { + if( itemCurrentlyEdited || blockActive ) + return NULL; - item = ModeditLocateAndDisplay(); + item = ModeditLocateAndDisplay(); + } + else + { + if( blockActive ) + return NULL; + + if( !itemCurrentlyEdited ) + item = ModeditLocateAndDisplay(); + } + + // set item if we can, but don't clear if not + if( item ) + SetCurItem( item ); + + return item; +} + + +bool FOOTPRINT_EDIT_FRAME::PostCommandMenuEvent( int evt_type ) +{ + if( evt_type != 0 ) + { + wxCommandEvent evt( wxEVT_COMMAND_MENU_SELECTED ); + evt.SetEventObject( this ); + evt.SetId( evt_type ); + wxPostEvent( this, evt ); + return true; + } + + return false; +} + + +bool FOOTPRINT_EDIT_FRAME::OnHotkeyEditItem( int aIdCommand ) +{ + BOARD_ITEM* item = PrepareItemForHotkey( true ); if( item == NULL ) return false; - SetCurItem( item ); - int evt_type = 0; // Used to post a wxCommandEvent on demand switch( item->Type() ) @@ -209,35 +262,17 @@ bool FOOTPRINT_EDIT_FRAME::OnHotkeyEditItem( int aIdCommand ) break; } - if( evt_type != 0 ) - { - wxCommandEvent evt( wxEVT_COMMAND_MENU_SELECTED ); - evt.SetEventObject( this ); - evt.SetId( evt_type ); - wxPostEvent( this, evt ); - return true; - } - - return false; + return PostCommandMenuEvent( evt_type ); } bool FOOTPRINT_EDIT_FRAME::OnHotkeyDeleteItem( int aIdCommand ) { - BOARD_ITEM* item = GetCurItem(); - bool itemCurrentlyEdited = item && item->GetFlags(); - bool blockActive = GetScreen()->m_BlockLocate.GetCommand() != BLOCK_IDLE; - - if( itemCurrentlyEdited || blockActive ) - return false; - - item = ModeditLocateAndDisplay(); + BOARD_ITEM* item = PrepareItemForHotkey( true ); if( item == NULL ) return false; - SetCurItem( item ); - int evt_type = 0; // Used to post a wxCommandEvent on demand switch( item->Type() ) @@ -264,35 +299,17 @@ bool FOOTPRINT_EDIT_FRAME::OnHotkeyDeleteItem( int aIdCommand ) break; } - if( evt_type != 0 ) - { - wxCommandEvent evt( wxEVT_COMMAND_MENU_SELECTED ); - evt.SetEventObject( this ); - evt.SetId( evt_type ); - wxPostEvent( this, evt ); - return true; - } - - return false; + return PostCommandMenuEvent( evt_type ); } bool FOOTPRINT_EDIT_FRAME::OnHotkeyMoveItem( int aIdCommand ) { - BOARD_ITEM* item = GetCurItem(); - bool itemCurrentlyEdited = item && item->GetFlags(); - bool blockActive = GetScreen()->m_BlockLocate.GetCommand() != BLOCK_IDLE; - - if( itemCurrentlyEdited || blockActive ) - return false; - - item = ModeditLocateAndDisplay(); + BOARD_ITEM* item = PrepareItemForHotkey( true ); if( item == NULL ) return false; - SetCurItem( item ); - int evt_type = 0; // Used to post a wxCommandEvent on demand switch( item->Type() ) @@ -319,36 +336,70 @@ bool FOOTPRINT_EDIT_FRAME::OnHotkeyMoveItem( int aIdCommand ) break; } - if( evt_type != 0 ) + return PostCommandMenuEvent( evt_type ); +} + + +bool FOOTPRINT_EDIT_FRAME::OnHotkeyMoveItemExact() +{ + BOARD_ITEM* item = PrepareItemForHotkey( false ); + + if( item == NULL ) + return false; + + int evt_type = 0; // Used to post a wxCommandEvent on demand + + switch( item->Type() ) { - wxCommandEvent evt( wxEVT_COMMAND_MENU_SELECTED ); - evt.SetEventObject( this ); - evt.SetId( evt_type ); - wxPostEvent( this, evt ); - return true; + case PCB_PAD_T: + case PCB_MODULE_EDGE_T: + case PCB_MODULE_TEXT_T: + evt_type = ID_POPUP_PCB_MOVE_EXACT; + break; + default: + break; } - return false; + return PostCommandMenuEvent( evt_type ); +} + +bool FOOTPRINT_EDIT_FRAME::OnHotkeyDuplicateItem( int aIdCommand ) +{ + BOARD_ITEM* item = PrepareItemForHotkey( true ); + + if( item == NULL ) + return false; + + int evt_type = 0; // Used to post a wxCommandEvent on demand + + switch( item->Type() ) + { + case PCB_PAD_T: + case PCB_MODULE_EDGE_T: + case PCB_MODULE_TEXT_T: + if( aIdCommand == HK_DUPLICATE_ITEM ) + evt_type = ID_POPUP_PCB_DUPLICATE_ITEM; + else + evt_type = ID_POPUP_PCB_DUPLICATE_ITEM_AND_INCREMENT; + + break; + + default: + break; + } + + return PostCommandMenuEvent( evt_type ); } bool FOOTPRINT_EDIT_FRAME::OnHotkeyRotateItem( int aIdCommand ) { - BOARD_ITEM* item = GetCurItem(); - bool itemCurrentlyEdited = item && item->GetFlags(); - int evt_type = 0; // Used to post a wxCommandEvent on demand - bool blockActive = GetScreen()->m_BlockLocate.GetCommand() != BLOCK_IDLE; - - if( blockActive ) - return false; - - if( !itemCurrentlyEdited ) - item = ModeditLocateAndDisplay(); + BOARD_ITEM* item = PrepareItemForHotkey( false ); if( item == NULL ) return false; - SetCurItem( item ); + int evt_type = 0; // Used to post a wxCommandEvent on demand switch( item->Type() ) { @@ -362,14 +413,5 @@ bool FOOTPRINT_EDIT_FRAME::OnHotkeyRotateItem( int aIdCommand ) break; } - if( evt_type != 0 ) - { - wxCommandEvent evt( wxEVT_COMMAND_MENU_SELECTED ); - evt.SetEventObject( this ); - evt.SetId( evt_type ); - wxPostEvent( this, evt ); - return true; - } - - return false; + return PostCommandMenuEvent( evt_type ); } diff --git a/pcbnew/modedit.cpp b/pcbnew/modedit.cpp index 61951c2795..eb778dfaa1 100644 --- a/pcbnew/modedit.cpp +++ b/pcbnew/modedit.cpp @@ -55,6 +55,7 @@ #include #include +#include #include #include #include @@ -64,10 +65,13 @@ // Functions defined in block_module_editor, but used here -// These 2 functions are used in modedit to rotate or mirror the whole footprint -// so they are called with force_all = true +// These 3 functions are used in modedit to rotate, mirror or move the +// whole footprint so they are called with force_all = true void MirrorMarkedItems( MODULE* module, wxPoint offset, bool force_all = false ); void RotateMarkedItems( MODULE* module, wxPoint offset, bool force_all = false ); +void MoveMarkedItemsExactly( MODULE* module, const wxPoint& centre, + const wxPoint& translation, double rotation, + bool force_all = false ); BOARD_ITEM* FOOTPRINT_EDIT_FRAME::ModeditLocateAndDisplay( int aHotKeyCode ) @@ -635,6 +639,38 @@ void FOOTPRINT_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event ) m_canvas->MoveCursorToCrossHair(); break; + case ID_POPUP_PCB_DUPLICATE_ITEM: + duplicateItems( false ); + break; + + case ID_POPUP_PCB_DUPLICATE_ITEM_AND_INCREMENT: + duplicateItems( true ); + break; + + case ID_POPUP_PCB_MOVE_EXACT: + { + wxPoint translation; + double rotation = 0; + + DIALOG_MOVE_EXACT dialog( this, translation, rotation ); + int ret = dialog.ShowModal(); + + if( ret == DIALOG_MOVE_EXACT::MOVE_OK ) + { + SaveCopyInUndoList( GetBoard()->m_Modules, UR_MODEDIT ); + + BOARD_ITEM* item = GetScreen()->GetCurItem(); + + item->Move( translation ); + item->Rotate( item->GetPosition(), rotation ); + m_canvas->Refresh(); + } + + m_canvas->MoveCursorToCrossHair(); + + break; + } + case ID_POPUP_PCB_IMPORT_PAD_SETTINGS: SaveCopyInUndoList( GetBoard()->m_Modules, UR_MODEDIT ); m_canvas->MoveCursorToCrossHair(); @@ -735,6 +771,7 @@ void FOOTPRINT_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event ) case ID_MODEDIT_MODULE_ROTATE: case ID_MODEDIT_MODULE_MIRROR: + case ID_MODEDIT_MODULE_MOVE_EXACT: SaveCopyInUndoList( GetBoard()->m_Modules, UR_MODEDIT ); Transform( (MODULE*) GetScreen()->GetCurItem(), id ); m_canvas->Refresh(); @@ -799,6 +836,12 @@ void FOOTPRINT_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event ) HandleBlockEnd( &dc ); break; + case ID_POPUP_MOVE_BLOCK_EXACT: + GetScreen()->m_BlockLocate.SetCommand( BLOCK_MOVE_EXACT ); + GetScreen()->m_BlockLocate.SetMessageBlock( this ); + HandleBlockEnd( &dc ); + break; + case ID_GEN_IMPORT_DXF_FILE: InvokeDXFDialogModuleImport( this, GetBoard()->m_Modules ); m_canvas->Refresh(); @@ -812,6 +855,49 @@ void FOOTPRINT_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event ) } +void FOOTPRINT_EDIT_FRAME::DuplicateItems( bool aIncrement ) +{ + SaveCopyInUndoList( GetBoard()->m_Modules, UR_MODEDIT ); + + BOARD_ITEM* item = GetScreen()->GetCurItem(); + MODULE* module = static_cast( item->GetParent() ); + + int move_cmd = 0; + + BOARD_ITEM* new_item = module->DuplicateAndAddItem( + item, aIncrement ); + + if( new_item ) + { + switch( new_item->Type() ) + { + case PCB_PAD_T: + move_cmd = ID_POPUP_PCB_MOVE_PAD_REQUEST; + break; + case PCB_MODULE_TEXT_T: + move_cmd = ID_POPUP_PCB_MOVE_TEXTMODULE_REQUEST; + break; + case PCB_MODULE_EDGE_T: + move_cmd = ID_POPUP_PCB_MOVE_EDGE; + break; + default: + break; + } + + if( move_cmd ) + { + SetMsgPanel( new_item ); + SetCurItem( new_item ); + + m_canvas->MoveCursorToCrossHair(); + + // pick up the item and start moving + PostCommandMenuEvent( move_cmd ); + } + } +} + + void FOOTPRINT_EDIT_FRAME::Transform( MODULE* module, int transform ) { switch( transform ) @@ -824,6 +910,23 @@ void FOOTPRINT_EDIT_FRAME::Transform( MODULE* module, int transform ) MirrorMarkedItems( module, wxPoint(0,0), true ); break; + case ID_MODEDIT_MODULE_MOVE_EXACT: + { + wxPoint translation; + double rotation = 0; + + DIALOG_MOVE_EXACT dialog( this, translation, rotation ); + int ret = dialog.ShowModal(); + + if( ret == DIALOG_MOVE_EXACT::MOVE_OK ) + { + MoveMarkedItemsExactly( module, wxPoint(0, 0), + translation, rotation, true ); + } + + break; + } + default: DisplayInfoMessage( this, wxT( "Not available" ) ); break; diff --git a/pcbnew/modedit_onclick.cpp b/pcbnew/modedit_onclick.cpp index edb29d005d..745f5b0dd0 100644 --- a/pcbnew/modedit_onclick.cpp +++ b/pcbnew/modedit_onclick.cpp @@ -173,7 +173,7 @@ void FOOTPRINT_EDIT_FRAME::OnLeftClick( wxDC* DC, const wxPoint& MousePos ) // so deselect the active tool SetToolID( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor(), wxEmptyString ); SetCurItem( NULL ); - m_canvas->Refresh(); + m_canvas->Refresh(); } break; @@ -260,6 +260,11 @@ bool FOOTPRINT_EDIT_FRAME::OnRightClick( const wxPoint& MousePos, wxMenu* PopMen AddMenuItem( PopMenu, ID_POPUP_DELETE_BLOCK, _( "Delete Block (shift+ctrl + drag mouse)" ), KiBitmap( delete_xpm ) ); + + msg = AddHotkeyName( _("Move Block Exactly" ), + g_Module_Editor_Hokeys_Descr, HK_MOVE_ITEM_EXACT ); + AddMenuItem( PopMenu, ID_POPUP_MOVE_BLOCK_EXACT, + msg, KiBitmap( move_xpm ) ); } else { @@ -286,10 +291,14 @@ bool FOOTPRINT_EDIT_FRAME::OnRightClick( const wxPoint& MousePos, wxMenu* PopMen KiBitmap( rotate_module_ccw_xpm ) ); AddMenuItem( transform_choice, ID_MODEDIT_MODULE_MIRROR, _( "Mirror" ), KiBitmap( mirror_footprint_axisY_xpm ) ); + AddMenuItem( transform_choice, ID_MODEDIT_MODULE_MOVE_EXACT, _( "Move Exactly" ), + KiBitmap( move_module_xpm ) ); + msg = AddHotkeyName( _( "Edit Footprint" ), g_Module_Editor_Hokeys_Descr, HK_EDIT_ITEM ); AddMenuItem( PopMenu, ID_POPUP_PCB_EDIT_MODULE_PRMS, msg, KiBitmap( edit_module_xpm ) ); AddMenuItem( PopMenu, transform_choice, ID_MODEDIT_TRANSFORM_MODULE, _( "Transform Footprint" ), KiBitmap( edit_xpm ) ); + break; } @@ -309,6 +318,12 @@ bool FOOTPRINT_EDIT_FRAME::OnRightClick( const wxPoint& MousePos, wxMenu* PopMen msg = AddHotkeyName( _("Delete Pad" ), g_Module_Editor_Hokeys_Descr, HK_DELETE ); AddMenuItem( PopMenu, ID_POPUP_PCB_DELETE_PAD, msg, KiBitmap( delete_pad_xpm ) ); + msg = AddHotkeyName( _( "Duplicate Pad" ), g_Module_Editor_Hokeys_Descr, HK_DUPLICATE_ITEM ); + AddMenuItem( PopMenu, ID_POPUP_PCB_DUPLICATE_ITEM, msg, KiBitmap( duplicate_pad_xpm ) ); + + msg = AddHotkeyName( _("Move Pad Exactly" ), g_Module_Editor_Hokeys_Descr, HK_MOVE_ITEM_EXACT ); + AddMenuItem( PopMenu, ID_POPUP_PCB_MOVE_EXACT, msg, KiBitmap( move_pad_xpm ) ); + if( !flags ) { PopMenu->AppendSeparator(); @@ -331,6 +346,25 @@ bool FOOTPRINT_EDIT_FRAME::OnRightClick( const wxPoint& MousePos, wxMenu* PopMen HK_ROTATE_ITEM ); AddMenuItem( PopMenu, ID_POPUP_PCB_ROTATE_TEXTMODULE, msg, KiBitmap( rotate_field_xpm ) ); + { + // Do not show option to duplicate value or reference fields + // (there can only be one of each) + + const MODULE* module = static_cast( item->GetParent() ); + const TEXTE_MODULE* text = static_cast( item ); + + if( &module->Reference() != text && &module->Value() != text ) + { + msg = AddHotkeyName( _( "Duplicate Text" ), + g_Module_Editor_Hokeys_Descr, HK_DUPLICATE_ITEM ); + AddMenuItem( PopMenu, ID_POPUP_PCB_DUPLICATE_ITEM, + msg, KiBitmap( duplicate_text_xpm ) ); + } + } + + msg = AddHotkeyName( _("Move Text Exactly" ), g_Module_Editor_Hokeys_Descr, HK_MOVE_ITEM_EXACT ); + AddMenuItem( PopMenu, ID_POPUP_PCB_MOVE_EXACT, msg, KiBitmap( move_field_xpm ) ); + if( !flags ) { msg = AddHotkeyName( _("Edit Text" ), g_Module_Editor_Hokeys_Descr, @@ -359,6 +393,12 @@ bool FOOTPRINT_EDIT_FRAME::OnRightClick( const wxPoint& MousePos, wxMenu* PopMen AddMenuItem( PopMenu, ID_POPUP_PCB_MOVE_EDGE, msg, KiBitmap( move_line_xpm ) ); } + msg = AddHotkeyName( _( "Duplicate Edge" ), g_Module_Editor_Hokeys_Descr, HK_DUPLICATE_ITEM ); + AddMenuItem( PopMenu, ID_POPUP_PCB_DUPLICATE_ITEM, msg, KiBitmap( duplicate_line_xpm ) ); + + msg = AddHotkeyName( _("Move Edge Exactly" ), g_Module_Editor_Hokeys_Descr, HK_MOVE_ITEM_EXACT ); + AddMenuItem( PopMenu, ID_POPUP_PCB_MOVE_EXACT, msg, KiBitmap( move_line_xpm ) ); + if( ( flags & (IS_NEW | IS_MOVED) ) == IS_MOVED ) AddMenuItem( PopMenu, ID_POPUP_PCB_PLACE_EDGE, _( "Place edge" ), KiBitmap( checked_ok_xpm ) ); diff --git a/pcbnew/module_editor_frame.h b/pcbnew/module_editor_frame.h index 1bd3c040d1..9988b823c5 100644 --- a/pcbnew/module_editor_frame.h +++ b/pcbnew/module_editor_frame.h @@ -145,10 +145,15 @@ public: */ bool OnHotKey( wxDC* aDC, int aHotKey, const wxPoint& aPosition, EDA_ITEM* aItem = NULL ); + BOARD_ITEM* PrepareItemForHotkey( bool failIfCurrentlyEdited ); + bool PostCommandMenuEvent( int evt_type ); + bool OnHotkeyEditItem( int aIdCommand ); bool OnHotkeyDeleteItem( int aIdCommand ); bool OnHotkeyMoveItem( int aIdCommand ); + bool OnHotkeyMoveItemExact(); bool OnHotkeyRotateItem( int aIdCommand ); + bool OnHotkeyDuplicateItem( int aIdCommand ); /** * Function Show3D_Frame @@ -530,6 +535,14 @@ protected: * @return a pointer to the new text, or NULL if aborted */ TEXTE_MODULE* CreateTextModule( MODULE* aModule, wxDC* aDC ); + +private: + + /** + * Duplicate the item under the cursor + * @param aIncrement increment the number of pad (if that is what is selected) + */ + void duplicateItems( bool aIncrement ); }; #endif // MODULE_EDITOR_FRAME_H_ diff --git a/pcbnew/moduleframe.cpp b/pcbnew/moduleframe.cpp index 55e18b4666..c2b3405c48 100644 --- a/pcbnew/moduleframe.cpp +++ b/pcbnew/moduleframe.cpp @@ -148,6 +148,7 @@ BEGIN_EVENT_TABLE( FOOTPRINT_EDIT_FRAME, PCB_BASE_FRAME ) // Module transformations EVT_MENU( ID_MODEDIT_MODULE_ROTATE, FOOTPRINT_EDIT_FRAME::Process_Special_Functions ) EVT_MENU( ID_MODEDIT_MODULE_MIRROR, FOOTPRINT_EDIT_FRAME::Process_Special_Functions ) + EVT_MENU( ID_MODEDIT_MODULE_MOVE_EXACT, FOOTPRINT_EDIT_FRAME::Process_Special_Functions ) EVT_MENU( ID_PCB_DRAWINGS_WIDTHS_SETUP, FOOTPRINT_EDIT_FRAME::Process_Special_Functions ) EVT_MENU( ID_PCB_PAD_SETUP, FOOTPRINT_EDIT_FRAME::Process_Special_Functions ) diff --git a/pcbnew/pcbnew_id.h b/pcbnew/pcbnew_id.h index 1aceb0dce9..f783ae8b12 100644 --- a/pcbnew/pcbnew_id.h +++ b/pcbnew/pcbnew_id.h @@ -61,6 +61,9 @@ enum pcbnew_ids ID_POPUP_PCB_ROTATE_PAD, ID_POPUP_PCB_MOVE_PAD_REQUEST, ID_POPUP_PCB_DRAG_PAD_REQUEST, + ID_POPUP_PCB_DUPLICATE_ITEM, + ID_POPUP_PCB_DUPLICATE_ITEM_AND_INCREMENT, + ID_POPUP_PCB_MOVE_EXACT, ID_POPUP_PCB_MOVE_TEXTMODULE_REQUEST, ID_POPUP_PCB_ROTATE_TEXTMODULE, @@ -346,6 +349,7 @@ enum pcbnew_ids ID_MODEDIT_TRANSFORM_MODULE, ID_MODEDIT_MODULE_ROTATE, ID_MODEDIT_MODULE_MIRROR, + ID_MODEDIT_MODULE_MOVE_EXACT, ID_MODEDIT_IMPORT_PART, ID_MODEDIT_EXPORT_PART, ID_MODEDIT_CREATE_NEW_LIB_AND_SAVE_CURRENT_PART, diff --git a/pcbnew/tools/common_actions.cpp b/pcbnew/tools/common_actions.cpp index c35372ea27..1e7eb7b7cc 100644 --- a/pcbnew/tools/common_actions.cpp +++ b/pcbnew/tools/common_actions.cpp @@ -64,6 +64,18 @@ TOOL_ACTION COMMON_ACTIONS::editActivate( "pcbnew.InteractiveEdit", AS_GLOBAL, 'M', "Move", "Moves the selected item(s)", AF_ACTIVATE ); +TOOL_ACTION COMMON_ACTIONS::duplicate( "pcbnew.InteractiveEdit.duplicate", + AS_GLOBAL, MD_CTRL + int( 'D' ), + "Duplicate", "Duplicates the selected item(s)" ); + +TOOL_ACTION COMMON_ACTIONS::duplicateIncrement( "pcbnew.InteractiveEdit.duplicateIncrementPads", + AS_GLOBAL, MD_CTRL + MD_SHIFT + int( 'D' ), + "Duplicate", "Duplicates the selected item(s), incrementing pad numbers" ); + +TOOL_ACTION COMMON_ACTIONS::moveExact( "pcbnew.InteractiveEdit.moveExact", + AS_GLOBAL, MD_CTRL + int( 'M' ), + "Move Exactly...", "Moves the selected item(s) by an exact amount" ); + TOOL_ACTION COMMON_ACTIONS::rotate( "pcbnew.InteractiveEdit.rotate", AS_GLOBAL, 'R', "Rotate", "Rotates selected item(s)" ); diff --git a/pcbnew/tools/common_actions.h b/pcbnew/tools/common_actions.h index 73849a755e..3c0c85e991 100644 --- a/pcbnew/tools/common_actions.h +++ b/pcbnew/tools/common_actions.h @@ -65,6 +65,15 @@ public: /// Activation of the edit tool static TOOL_ACTION properties; + /// Activation of the exact move tool + static TOOL_ACTION moveExact; + + /// Activation of the duplication tool + static TOOL_ACTION duplicate; + + /// Activation of the duplication tool with incrementing (e.g. pad number) + static TOOL_ACTION duplicateIncrement; + /// Deleting a BOARD_ITEM static TOOL_ACTION remove; diff --git a/pcbnew/tools/edit_tool.cpp b/pcbnew/tools/edit_tool.cpp index be76326bf2..344c89e0e2 100644 --- a/pcbnew/tools/edit_tool.cpp +++ b/pcbnew/tools/edit_tool.cpp @@ -42,6 +42,8 @@ #include "selection_tool.h" #include "edit_tool.h" +#include + EDIT_TOOL::EDIT_TOOL() : TOOL_INTERACTIVE( "pcbnew.InteractiveEdit" ), m_selectionTool( NULL ), m_editModules( false ) { @@ -71,6 +73,7 @@ bool EDIT_TOOL::Init() m_selectionTool->AddMenuItem( COMMON_ACTIONS::flip, SELECTION_CONDITIONS::NotEmpty ); m_selectionTool->AddMenuItem( COMMON_ACTIONS::remove, SELECTION_CONDITIONS::NotEmpty ); m_selectionTool->AddMenuItem( COMMON_ACTIONS::properties, SELECTION_CONDITIONS::NotEmpty ); + m_selectionTool->AddMenuItem( COMMON_ACTIONS::moveExact, SELECTION_CONDITIONS::NotEmpty ); m_offset.x = 0; m_offset.y = 0; @@ -110,6 +113,12 @@ int EDIT_TOOL::Main( TOOL_EVENT& aEvent ) controls->SetSnapping( true ); controls->ForceCursorPosition( false ); + // cumulative translation + wxPoint totalMovement( 0, 0 ); + + // make sure nothing is inhibiting undo points + bool inhibitUndo = m_toolMgr->IsUndoInhibited(); + // Main loop: keep receiving events while( OPT_TOOL_EVENT evt = Wait() ) { @@ -145,6 +154,37 @@ int EDIT_TOOL::Main( TOOL_EVENT& aEvent ) break; // exit the loop, as there is no further processing for removed items } + else if( evt->IsAction( &COMMON_ACTIONS::duplicate ) ) + { + // On duplicate, stop moving this item + // The duplicate tool should then select the new item and start + // a new move procedure + break; + } + else if( evt->IsAction( &COMMON_ACTIONS::moveExact ) ) + { + // Can't do this, because the selection will then contain + // stale pointers and it will all go horribly wrong... + //editFrame->RestoreCopyFromUndoList( dummy ); + // + // So, instead, reset the position manually + for( unsigned int i = 0; i < selection.items.GetCount(); ++i ) + { + BOARD_ITEM* item = selection.Item( i ); + item->SetPosition( item->GetPosition() - totalMovement ); + + // And what about flipping and rotation? + // for now, they won't be undone, but maybe that is how + // it should be, so you can flip and move exact in the + // same action? + } + + // This causes a double event, so we will get the dialogue + // correctly, somehow - why does Rotate not? + //MoveExact( aEvent ); + break; // exit the loop - we move exactly, so we have + // finished moving + } } else if( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) @@ -156,6 +196,8 @@ int EDIT_TOOL::Main( TOOL_EVENT& aEvent ) wxPoint movement = wxPoint( m_cursor.x, m_cursor.y ) - selection.Item( 0 )->GetPosition(); + totalMovement += movement; + // Drag items to the current cursor position for( unsigned int i = 0; i < selection.items.GetCount(); ++i ) selection.Item( i )->Move( movement + m_offset ); @@ -168,8 +210,11 @@ int EDIT_TOOL::Main( TOOL_EVENT& aEvent ) break; // Save items, so changes can be undone - editFrame->OnModify(); - editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED ); + if( !inhibitUndo ) + { + editFrame->OnModify(); + editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED ); + } if( selection.Size() == 1 ) { @@ -195,6 +240,7 @@ int EDIT_TOOL::Main( TOOL_EVENT& aEvent ) controls->SetAutoPan( true ); m_dragging = true; + m_toolMgr->IncUndoInhibit(); } selection.group->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ); @@ -203,9 +249,14 @@ int EDIT_TOOL::Main( TOOL_EVENT& aEvent ) else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) ) break; // Finish + } + if( m_dragging ) + m_toolMgr->DecUndoInhibit(); + m_dragging = false; + m_offset.x = 0; m_offset.y = 0; @@ -324,7 +375,8 @@ int EDIT_TOOL::Rotate( TOOL_EVENT& aEvent ) wxPoint rotatePoint = getModificationPoint( selection ); - if( !m_dragging ) // If it is being dragged, then it is already saved with UR_CHANGED flag + // If it is being dragged, then it is already saved with UR_CHANGED flag + if( !m_toolMgr->IsUndoInhibited() ) { editFrame->OnModify(); editFrame->SaveCopyInUndoList( selection.items, UR_ROTATED, rotatePoint ); @@ -378,7 +430,7 @@ int EDIT_TOOL::Flip( TOOL_EVENT& aEvent ) wxPoint flipPoint = getModificationPoint( selection ); - if( !m_dragging ) // If it is being dragged, then it is already saved with UR_CHANGED flag + if( !m_toolMgr->IsUndoInhibited() ) // If it is being dragged, then it is already saved with UR_CHANGED flag { editFrame->OnModify(); editFrame->SaveCopyInUndoList( selection.items, UR_FLIPPED, flipPoint ); @@ -532,6 +584,69 @@ void EDIT_TOOL::remove( BOARD_ITEM* aItem ) } +int EDIT_TOOL::MoveExact( TOOL_EVENT& aEvent ) +{ + const SELECTION& selection = m_selectionTool->GetSelection(); + + // Shall the selection be cleared at the end? + bool unselect = selection.Empty(); + + if( !makeSelection( selection ) || m_selectionTool->CheckLock() ) + { + setTransitions(); + + return 0; + } + + wxPoint translation; + double rotation = 0; + + PCB_BASE_FRAME* editFrame = getEditFrame(); + + DIALOG_MOVE_EXACT dialog( editFrame, translation, rotation ); + int ret = dialog.ShowModal(); + + if( ret == DIALOG_MOVE_EXACT::MOVE_OK ) + { + if( !m_toolMgr->IsUndoInhibited() ) + { + editFrame->OnModify(); + // Record an action of move and rotate + editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED ); + } + + wxPoint rotPoint = selection.GetCenter(); + + for( unsigned int i = 0; i < selection.items.GetCount(); ++i ) + { + BOARD_ITEM* item = selection.Item( i ); + + item->Move( translation ); + item->Rotate( rotPoint, rotation ); + + if( !m_dragging ) + item->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ); + } + + updateRatsnest( m_dragging ); + + if( m_dragging ) + selection.group->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ); + else + getModel()->GetRatsnest()->Recalculate(); + + if( unselect ) + m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); + + m_toolMgr->RunAction( COMMON_ACTIONS::pointEditorUpdate, true ); + } + + setTransitions(); + + return 0; +} + + void EDIT_TOOL::setTransitions() { Go( &EDIT_TOOL::Main, COMMON_ACTIONS::editActivate.MakeEvent() ); @@ -539,6 +654,7 @@ void EDIT_TOOL::setTransitions() Go( &EDIT_TOOL::Flip, COMMON_ACTIONS::flip.MakeEvent() ); Go( &EDIT_TOOL::Remove, COMMON_ACTIONS::remove.MakeEvent() ); Go( &EDIT_TOOL::Properties, COMMON_ACTIONS::properties.MakeEvent() ); + Go( &EDIT_TOOL::MoveExact, COMMON_ACTIONS::moveExact.MakeEvent() ); } diff --git a/pcbnew/tools/edit_tool.h b/pcbnew/tools/edit_tool.h index a3386b3823..125ec1bd5f 100644 --- a/pcbnew/tools/edit_tool.h +++ b/pcbnew/tools/edit_tool.h @@ -91,6 +91,20 @@ public: */ int Remove( TOOL_EVENT& aEvent ); + /** + * Function Duplicate() + * + * Duplicates a component and starts a move action + */ + int Duplicate( TOOL_EVENT& aEvent ); + + /** + * Function MoveExact() + * + * Invokes a dialog box to allow moving of the item by an exact amount. + */ + int MoveExact( TOOL_EVENT& aEvent ); + /** * Function EditModules() * diff --git a/pcbnew/tools/module_tools.cpp b/pcbnew/tools/module_tools.cpp index 54b9f52db7..e0c3443f00 100644 --- a/pcbnew/tools/module_tools.cpp +++ b/pcbnew/tools/module_tools.cpp @@ -74,6 +74,7 @@ bool MODULE_TOOLS::Init() } selectionTool->AddMenuItem( COMMON_ACTIONS::enumeratePads ); + selectionTool->AddMenuItem( COMMON_ACTIONS::duplicate ); setTransitions(); @@ -81,44 +82,6 @@ bool MODULE_TOOLS::Init() } -static wxString getNextPadName( MODULE* aModule ) -{ - std::set usedNumbers; - - // Create a set of used pad numbers - for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() ) - { - wxString padName = pad->GetPadName(); - int padNumber = 0; - int base = 1; - - // Trim and extract the trailing numeric part - while( padName.Len() && padName.Last() >= '0' && padName.Last() <= '9' ) - { - padNumber += ( padName.Last() - '0' ) * base; - padName.RemoveLast(); - base *= 10; - } - - usedNumbers.insert( padNumber ); - } - - int candidate = *usedNumbers.begin(); - - // Look for a gap in pad numbering - for( std::set::iterator it = usedNumbers.begin(), - itEnd = usedNumbers.end(); it != itEnd; ++it ) - { - if( *it - candidate > 1 ) - break; - - candidate = *it; - } - - return wxString::Format( wxT( "%i" ), ++candidate ); -} - - int MODULE_TOOLS::PlacePad( TOOL_EVENT& aEvent ) { m_frame->SetToolID( ID_MODEDIT_PAD_TOOL, wxCURSOR_PENCIL, _( "Add pads" ) ); @@ -189,14 +152,8 @@ int MODULE_TOOLS::PlacePad( TOOL_EVENT& aEvent ) // ( pad position for module orient, 0, and relative to the module position) pad->SetLocalCoord(); - /* NPTH pads take empty pad number (since they can't be connected), - * other pads get incremented from the last one edited */ - wxString padName; - - if( pad->GetAttribute() != PAD_HOLE_NOT_PLATED ) - padName = getNextPadName( module ); - - pad->SetPadName( padName ); + // Take the next available pad number + pad->IncrementPadName( true, true ); // Handle the view aspect preview.Remove( pad ); @@ -532,6 +489,85 @@ int MODULE_TOOLS::PasteItems( TOOL_EVENT& aEvent ) return 0; } +int MODULE_TOOLS::DuplicateItems( TOOL_EVENT& aEvent ) +{ + bool increment = aEvent.IsAction( &COMMON_ACTIONS::duplicateIncrement ); + + MODULE* module = m_board->m_Modules; + assert( module ); + + // first, check if we have a selection, or try to get one + SELECTION_TOOL* selTool = m_toolMgr->GetTool(); + + if( selTool->GetSelection().Empty() ) + { + m_toolMgr->RunAction( COMMON_ACTIONS::selectionCursor, true ); + } + + const SELECTION& selection = selTool->GetSelection(); + + // if we don't have a selection by now, this tool can't do anything + if( selection.Empty() || selTool->CheckLock() ) + { + setTransitions(); + return 0; + } + + // we have a selection to work on now, so start the tool process + + m_frame->OnModify(); + m_frame->SaveCopyInUndoList( module, UR_MODEDIT ); + + // prevent other tools making undo points while the duplicate is going on + // so that if you cancel, you don't get a duplicate object hiding over + // the original + m_toolMgr->IncUndoInhibit(); + + std::vector old_items; + + for( int i = 0; i < selection.Size(); ++i ) + { + BOARD_ITEM* item = selection.Item( i ); + + if( item ) + old_items.push_back( item ); + } + + for( unsigned i = 0; i < old_items.size(); ++i ) + { + BOARD_ITEM* item = old_items[i]; + + // Unselect the item, so we won't pick it up again + // Do this first, so a single-item duplicate will correctly call + // SetCurItem and show the item properties + m_toolMgr->RunAction( COMMON_ACTIONS::unselectItem, true, item ); + + BOARD_ITEM* new_item = module->DuplicateAndAddItem( item, increment ); + + if( new_item ) + { + m_view->Add( new_item ); + + // Select the new item, so we can pick it up + m_toolMgr->RunAction( COMMON_ACTIONS::selectItem, true, new_item ); + } + } + + m_frame->DisplayToolMsg( wxString::Format( _( "Duplicated %d item(s)" ), + (int) old_items.size() ) ); + + // pick up the selected item(s) and start moving + // this works well for "dropping" copies around + m_toolMgr->RunAction( COMMON_ACTIONS::editActivate, true ); + + // and re-enable undos + m_toolMgr->DecUndoInhibit(); + + setTransitions(); + + return 0; +} + int MODULE_TOOLS::ModuleTextOutlines( TOOL_EVENT& aEvent ) { @@ -608,6 +644,8 @@ void MODULE_TOOLS::setTransitions() Go( &MODULE_TOOLS::EnumeratePads, COMMON_ACTIONS::enumeratePads.MakeEvent() ); Go( &MODULE_TOOLS::CopyItems, COMMON_ACTIONS::copyItems.MakeEvent() ); Go( &MODULE_TOOLS::PasteItems, COMMON_ACTIONS::pasteItems.MakeEvent() ); + Go( &MODULE_TOOLS::DuplicateItems, COMMON_ACTIONS::duplicate.MakeEvent() ); + Go( &MODULE_TOOLS::DuplicateItems, COMMON_ACTIONS::duplicateIncrement.MakeEvent() ); Go( &MODULE_TOOLS::ModuleTextOutlines, COMMON_ACTIONS::moduleTextOutlines.MakeEvent() ); Go( &MODULE_TOOLS::ModuleEdgeOutlines, COMMON_ACTIONS::moduleEdgeOutlines.MakeEvent() ); } diff --git a/pcbnew/tools/module_tools.h b/pcbnew/tools/module_tools.h index 71d83acc7e..3e9ef1043a 100644 --- a/pcbnew/tools/module_tools.h +++ b/pcbnew/tools/module_tools.h @@ -77,6 +77,9 @@ public: */ int PasteItems( TOOL_EVENT& aEvent ); + + int DuplicateItems ( TOOL_EVENT& aEvent ); + /** * Function ModuleTextOutlines() * diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp index 85ec289e80..4ac34b61ac 100644 --- a/pcbnew/tools/selection_tool.cpp +++ b/pcbnew/tools/selection_tool.cpp @@ -939,3 +939,27 @@ void SELECTION::clear() items.ClearItemsList(); group->Clear(); } + + +wxPoint SELECTION::GetCenter() const +{ + wxPoint centre; + + if( Size() == 1 ) + { + centre = Item( 0 )->GetCenter(); + } + else + { + EDA_RECT bbox = Item( 0 )->GetBoundingBox(); + for( unsigned int i = 1; i < items.GetCount(); ++i ) + { + BOARD_ITEM* item = Item( i ); + bbox.Merge( item->GetBoundingBox() ); + } + + centre = bbox.Centre(); + } + + return centre; +} diff --git a/pcbnew/tools/selection_tool.h b/pcbnew/tools/selection_tool.h index 89cb40b4ec..9772d94acb 100644 --- a/pcbnew/tools/selection_tool.h +++ b/pcbnew/tools/selection_tool.h @@ -70,6 +70,8 @@ struct SELECTION return static_cast( items.GetPickedItem( aIndex ) ); } + wxPoint GetCenter() const; + private: /// Clears both the VIEW_GROUP and set of selected items. Please note that it does not /// change properties of selected items (e.g. selection flag).