diff options
author | saurabhb17 | 2020-02-26 16:20:48 +0530 |
---|---|---|
committer | GitHub | 2020-02-26 16:20:48 +0530 |
commit | b77f5d9d8097c38159c6f60917995d6af13bbe1c (patch) | |
tree | 1392c90227aeea231c1d86371131e04c40382918 /common | |
parent | dadc4d490966a24efe15b5cc533ef8695986048a (diff) | |
parent | 003d02608917e7a69d1a98438837e94ccf68352a (diff) | |
download | KiCad-eSim-b77f5d9d8097c38159c6f60917995d6af13bbe1c.tar.gz KiCad-eSim-b77f5d9d8097c38159c6f60917995d6af13bbe1c.tar.bz2 KiCad-eSim-b77f5d9d8097c38159c6f60917995d6af13bbe1c.zip |
Merge pull request #4 from FOSSEE/develop
merging dev into master
Diffstat (limited to 'common')
182 files changed, 75303 insertions, 0 deletions
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt new file mode 100644 index 0000000..549a2c8 --- /dev/null +++ b/common/CMakeLists.txt @@ -0,0 +1,493 @@ +include_directories( BEFORE ${INC_BEFORE} ) +include_directories( + ./dialogs + ./dialog_about + ${CAIRO_INCLUDE_DIR} + ${GLEW_INCLUDE_DIR} + ${CURL_INCLUDE_DIRS} + ../3d-viewer + ../pcbnew + ../polygon + ${INC_AFTER} + ) + + +if( NOT APPLE ) # windows and linux use openssl under curl + find_package( OpenSSL REQUIRED ) +endif() + + +# Generate header files containing shader programs +# Order of input files is significant +add_custom_command( + OUTPUT gal/opengl/shader_src.h + COMMAND ${CMAKE_COMMAND} + -DinputFiles="${PROJECT_SOURCE_DIR}/common/gal/opengl/shader.vert\\;${PROJECT_SOURCE_DIR}/common/gal/opengl/shader.frag" + -DoutputFile="shader_src.h" + -P ${CMAKE_MODULE_PATH}/Shaders.cmake + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/common/gal/opengl + COMMENT "Generating headers containing GLSL source code" + ) + +add_custom_target( + shader_headers ALL + DEPENDS gal/opengl/shader_src.h + ) + +set( GAL_SRCS + # Common part + draw_panel_gal.cpp + painter.cpp + worksheet_viewitem.cpp + origin_viewitem.cpp + gal/graphics_abstraction_layer.cpp + gal/stroke_font.cpp + gal/color4d.cpp + view/view_controls.cpp + view/wx_view_controls.cpp + geometry/hetriang.cpp + + # OpenGL GAL + gal/opengl/opengl_gal.cpp + gal/opengl/shader.cpp + gal/opengl/vertex_item.cpp + gal/opengl/vertex_container.cpp + gal/opengl/cached_container.cpp + gal/opengl/noncached_container.cpp + gal/opengl/vertex_manager.cpp + gal/opengl/gpu_manager.cpp + gal/opengl/opengl_compositor.cpp + + # Cairo GAL + gal/cairo/cairo_gal.cpp + gal/cairo/cairo_compositor.cpp + ) + +add_library( gal STATIC ${GAL_SRCS} ) +add_dependencies( gal shader_headers ) +add_dependencies( gal lib-dependencies ) + +target_link_libraries( gal + ${GLEW_LIBRARIES} + ${CAIRO_LIBRARIES} + ${PIXMAN_LIBRARY} + ${OPENGL_LIBRARIES} +) + + +# Only for win32 cross compilation using MXE +if( WIN32 AND MSYS ) + add_definitions( -DGLEW_STATIC ) +endif() + + +# A shared library subsetted from common which restricts what can go into +# a single_top link image. By not linking to common, we control what does +# statically go into single_top link images. My current thinking is that only +# wxWidgets should be a shared link from single top, everything else should be +# statically bound into it. Otherwise you will have DSO loading problems. After it +# sets the LIB PATHS however, we want the *.kiface modules to use shared linking. +add_library( singletop STATIC EXCLUDE_FROM_ALL + confirm.cpp + eda_doc.cpp + kiway.cpp + kiway_holder.cpp + ) + + +# A shared library used by multiple *.kiface files and one or two program +# launchers. Object files can migrate into here over time, but only if they are +# surely needed and certainly used from more than one place without recompilation. +# Functions and data all need to use the #include <import_export.h> and be declared +# as APIEXPORT +set( LIB_KICAD_SRCS + colors.cpp + dlist.cpp + string.cpp + ) + +if( future ) +add_library( lib_kicad SHARED + ) +target_link_libraries( lib_kicad + ${wxWidgets_LIBRARIES} + ) +set_target_properties( lib_kicad PROPERTIES + OUTPUT_NAME ki + ) +install( TARGETS lib_kicad + DESTINATION ${KICAD_BIN} + COMPONENT binary + ) +endif() + + +# KiCad build version string defaults to "no-vcs-found" which forces the build version header +# command to look for git to create the version string header when the .git path is found in +# the source path. +set( KICAD_BRANCH_NAME "" CACHE STRING "KiCad repository name." ) +set( KICAD_VERSION_EXTRA "" CACHE STRING + "User defined configuration string to append to KiCad version." ) + +# Generate version header file. +add_custom_target( + version_header ALL + COMMAND ${CMAKE_COMMAND} + -DKICAD_VERSION=${KICAD_VERSION} + -DKICAD_BRANCH_NAME=${KICAD_BRANCH_NAME} + -DKICAD_VERSION_EXTRA=${KICAD_VERSION_EXTRA} + -DOUTPUT_FILE=${CMAKE_BINARY_DIR}/kicad_build_version.h + -DSRC_PATH=${PROJECT_SOURCE_DIR} + -DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH} + -P ${CMAKE_MODULE_PATH}/WriteVersionHeader.cmake + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMENT "Generating version string header" + ) + +set( COMMON_ABOUT_DLG_SRCS + dialog_about/AboutDialog_main.cpp + dialog_about/dialog_about.cpp + dialog_about/dialog_about_base.cpp + dialogs/dialog_display_info_HTML_base.cpp + dialogs/dialog_exit_base.cpp + dialogs/dialog_image_editor.cpp + dialogs/dialog_image_editor_base.cpp + dialogs/dialog_get_component.cpp + dialogs/dialog_get_component_base.cpp + dialogs/dialog_hotkeys_editor.cpp + dialogs/dialog_hotkeys_editor_base.cpp + dialogs/dialog_list_selector_base.cpp + dialogs/dialog_page_settings_base.cpp + dialogs/dialog_env_var_config_base.cpp + dialogs/dialog_env_var_config.cpp + dialogs/wx_html_report_panel_base.cpp + dialogs/wx_html_report_panel.cpp + ) + +set( COMMON_PAGE_LAYOUT_SRCS + page_layout/title_block_shapes.cpp + page_layout/class_worksheet_dataitem.cpp + page_layout/class_worksheet_layout.cpp + page_layout/page_layout_default_description.cpp + page_layout/page_layout_graphic_items.cpp + page_layout/page_layout_reader_keywords.cpp + page_layout/page_layout_reader.cpp + ) + +set( COMMON_SRCS + ${LIB_KICAD_SRCS} + ${COMMON_ABOUT_DLG_SRCS} + ${COMMON_PAGE_LAYOUT_SRCS} + base_struct.cpp + basicframe.cpp + bezier_curves.cpp + bin_mod.cpp + bitmap.cpp + block_commande.cpp + build_version.cpp + class_bitmap_base.cpp + class_colors_design_settings.cpp + class_layer_box_selector.cpp + class_marker_base.cpp + class_plotter.cpp + class_undoredo_container.cpp + colors.cpp + common.cpp + common_plot_functions.cpp + common_plotHPGL_functions.cpp + common_plotPS_functions.cpp + common_plotPDF_functions.cpp + common_plotGERBER_functions.cpp + common_plotDXF_functions.cpp + common_plotSVG_functions.cpp + config_params.cpp + confirm.cpp + copy_to_clipboard.cpp + convert_basic_shapes_to_polygon.cpp + dialog_shim.cpp + displlst.cpp + draw_frame.cpp + draw_panel.cpp + drawtxt.cpp + dsnlexer.cpp + eda_dde.cpp + eda_doc.cpp + filter_reader.cpp +# findkicadhelppath.cpp.notused deprecated, use searchhelpfilefullpath.cpp + gestfich.cpp + getrunningmicrosecs.cpp + grid_tricks.cpp + gr_basic.cpp + hotkeys_basic.cpp + html_messagebox.cpp + kiface_i.cpp + kiway.cpp + kiway_express.cpp + kiway_holder.cpp + kiway_player.cpp + lockfile.cpp + msgpanel.cpp + netlist_keywords.cpp + newstroke_font.cpp + prependpath.cpp + project.cpp + ptree.cpp + reporter.cpp + richio.cpp + searchhelpfilefullpath.cpp + search_stack.cpp + selcolor.cpp + systemdirsappend.cpp + trigo.cpp + utf8.cpp + validators.cpp + wildcards_and_files_ext.cpp + worksheet.cpp + wxwineda.cpp + wx_unit_binder.cpp + wx_status_popup.cpp + xnode.cpp + zoom.cpp + ) + +if( TRUE OR NOT USE_KIWAY_DLLS ) +#if( NOT USE_KIWAY_DLLS ) + # We DO NOT want pgm_base.cpp linked into the KIFACE, only into the KIWAY. + # Check the map files to verify eda_pgm.o not being linked in. + list( APPEND COMMON_SRCS pgm_base.cpp ) +endif() + +if( NOT HAVE_STRTOKR ) + list( APPEND COMMON_SRCS strtok_r.c ) +endif() + + +set( COMMON_SRCS + ${COMMON_SRCS} + kicad_curl/kicad_curl.cpp + kicad_curl/kicad_curl_easy.cpp + + view/view.cpp + view/view_item.cpp + view/view_group.cpp + + math/math_util.cpp + + tool/tool_action.cpp + tool/tool_base.cpp + tool/tool_manager.cpp + tool/tool_dispatcher.cpp + tool/tool_event.cpp + tool/tool_interactive.cpp + tool/action_manager.cpp + tool/context_menu.cpp + + geometry/seg.cpp + geometry/shape.cpp + geometry/shape_line_chain.cpp + geometry/shape_poly_set.cpp + geometry/shape_collisions.cpp + geometry/shape_file_io.cpp + ) +add_library( common STATIC ${COMMON_SRCS} ) +add_dependencies( common lib-dependencies ) +add_dependencies( common version_header ) +target_link_libraries( common + ${Boost_LIBRARIES} + ${CURL_LIBRARIES} + ${OPENSSL_LIBRARIES} # empty on Apple + ) + + +set( PCB_COMMON_SRCS + base_screen.cpp + eda_text.cpp + class_page_info.cpp + pcbcommon.cpp + lset.cpp + footprint_info.cpp + ../pcbnew/basepcbframe.cpp + ../pcbnew/class_board.cpp + ../pcbnew/class_board_connected_item.cpp + ../pcbnew/class_board_design_settings.cpp + ../pcbnew/class_board_item.cpp + ../pcbnew/class_dimension.cpp + ../pcbnew/class_drawsegment.cpp + ../pcbnew/class_drc_item.cpp + ../pcbnew/class_edge_mod.cpp + ../pcbnew/class_netclass.cpp + ../pcbnew/class_netinfo_item.cpp + ../pcbnew/class_netinfolist.cpp + ../pcbnew/class_marker_pcb.cpp + ../pcbnew/class_mire.cpp + ../pcbnew/class_module.cpp + ../pcbnew/class_pad.cpp + ../pcbnew/class_pad_draw_functions.cpp + ../pcbnew/class_pcb_text.cpp + ../pcbnew/class_text_mod.cpp + ../pcbnew/class_track.cpp + ../pcbnew/class_zone.cpp + ../pcbnew/class_zone_settings.cpp + ../pcbnew/classpcb.cpp + ../pcbnew/ratsnest_data.cpp + ../pcbnew/ratsnest_viewitem.cpp + ../pcbnew/collectors.cpp + ../pcbnew/netlist_reader.cpp + ../pcbnew/legacy_netlist_reader.cpp + ../pcbnew/kicad_netlist_reader.cpp + ../pcbnew/sel_layer.cpp + ../pcbnew/pcb_plot_params.cpp + ../pcbnew/io_mgr.cpp + ../pcbnew/plugin.cpp + ../pcbnew/eagle_plugin.cpp + ../pcbnew/legacy_plugin.cpp + ../pcbnew/kicad_plugin.cpp + ../pcbnew/gpcb_plugin.cpp + ../pcbnew/pcb_netlist.cpp + ../pcbnew/specctra.cpp + ../pcbnew/specctra_export.cpp + ../pcbnew/specctra_keywords.cpp + pcb_plot_params_keywords.cpp + pcb_keywords.cpp + ../pcbnew/pcb_parser.cpp + fp_lib_table_keywords.cpp + fpid.cpp + fp_lib_table.cpp +) + +set( PCB_COMMON_SRCS + ${PCB_COMMON_SRCS} + ../pcbnew/pcb_painter.cpp + ) + +# add -DPCBNEW to compilation of these PCBNEW sources +set_source_files_properties( ${PCB_COMMON_SRCS} PROPERTIES + COMPILE_DEFINITIONS "PCBNEW" + ) + +add_library( pcbcommon STATIC ${PCB_COMMON_SRCS} ) +add_dependencies( pcbcommon lib-dependencies ) + +# auto-generate specctra_lexer.h and specctra_keywords.cpp +make_lexer( + ${PROJECT_SOURCE_DIR}/pcbnew/specctra.keywords + ${PROJECT_SOURCE_DIR}/pcbnew/specctra_lexer.h + ${PROJECT_SOURCE_DIR}/pcbnew/specctra_keywords.cpp + DSN + + # Pass header file with dependency on *_lexer.h as extra_arg + specctra.h + ) + +add_custom_target( + specctra_lexer_source_files ALL + DEPENDS + ${PROJECT_SOURCE_DIR}/pcbnew/specctra_lexer.h + ${PROJECT_SOURCE_DIR}/pcbnew/specctra_keywords.cpp + ) + +add_dependencies( pcbcommon specctra_lexer_source_files ) + +# auto-generate netlist_lexer.h and netlist_keywords.cpp +make_lexer( + ${CMAKE_CURRENT_SOURCE_DIR}/netlist.keywords + ${PROJECT_SOURCE_DIR}/include/netlist_lexer.h + ${CMAKE_CURRENT_SOURCE_DIR}/netlist_keywords.cpp + NL_T + + # Pass header file with dependency on *_lexer.h as extra_arg + ${CMAKE_PROJECT_SOURCE_DIR}/pcbnew/netlist_reader.h + ) + +add_custom_target( + netlist_lexer_source_files ALL + DEPENDS + ${PROJECT_SOURCE_DIR}/include/netlist_lexer.h + ${CMAKE_CURRENT_SOURCE_DIR}/netlist_keywords.cpp + ) + +add_dependencies( common netlist_lexer_source_files ) +add_dependencies( pcbcommon netlist_lexer_source_files ) + +# auto-generate pcb_plot_params_lexer.h and pcb_plot_params_keywords.cpp +make_lexer( + ${CMAKE_CURRENT_SOURCE_DIR}/pcb_plot_params.keywords + ${PROJECT_SOURCE_DIR}/include/pcb_plot_params_lexer.h + ${CMAKE_CURRENT_SOURCE_DIR}/pcb_plot_params_keywords.cpp + PCBPLOTPARAMS_T + + # Pass header file with dependencies on *_lexer.h as extra_arg + ${PROJECT_SOURCE_DIR}/pcbnew/pcb_plot_params.h + ) + +add_custom_target( + pcb_plot_lexer_source_files ALL + DEPENDS + ${PROJECT_SOURCE_DIR}/include/pcb_plot_params_lexer.h + ${CMAKE_CURRENT_SOURCE_DIR}/pcb_plot_params_keywords.cpp + ) + +add_dependencies( pcbcommon pcb_plot_lexer_source_files ) + +# auto-generate pcbnew_sexpr.h and pcbnew_sexpr.cpp +make_lexer( + ${CMAKE_CURRENT_SOURCE_DIR}/pcb.keywords + ${PROJECT_SOURCE_DIR}/include/pcb_lexer.h + ${CMAKE_CURRENT_SOURCE_DIR}/pcb_keywords.cpp + PCB_KEYS_T + + # Pass header file with dependency on *_lexer.h as extra_arg + ${PROJECT_SOURCE_DIR}/pcbnew/pcb_parser.h + ) + +add_custom_target( + pcb_lexer_source_files ALL + DEPENDS + ${PROJECT_SOURCE_DIR}/include/pcb_lexer.h + ${CMAKE_CURRENT_SOURCE_DIR}/pcb_keywords.cpp + ) + +add_dependencies( pcbcommon pcb_lexer_source_files ) + +# auto-generate pcbnew s-expression footprint library table code. +make_lexer( + ${CMAKE_CURRENT_SOURCE_DIR}/fp_lib_table.keywords + ${PROJECT_SOURCE_DIR}/include/fp_lib_table_lexer.h + ${CMAKE_CURRENT_SOURCE_DIR}/fp_lib_table_keywords.cpp + FP_LIB_TABLE_T + ) + +add_custom_target( + fp_lib_table_lexer_source_files ALL + DEPENDS + ${PROJECT_SOURCE_DIR}/include/fp_lib_table_lexer.h + ${CMAKE_CURRENT_SOURCE_DIR}/fp_lib_table_keywords.cpp + ) + +add_dependencies( pcbcommon fp_lib_table_lexer_source_files ) + +# auto-generate page layout reader s-expression page_layout_reader_lexer.h +# and title_block_reader_keywords.cpp. +make_lexer( + ${CMAKE_CURRENT_SOURCE_DIR}/page_layout/page_layout_reader.keywords + ${PROJECT_SOURCE_DIR}/include/page_layout_reader_lexer.h + ${CMAKE_CURRENT_SOURCE_DIR}/page_layout/page_layout_reader_keywords.cpp + TB_READER_T + ) + +add_custom_target( + page_layout_lexer_source_files ALL + DEPENDS + ${PROJECT_SOURCE_DIR}/include/page_layout_reader_lexer.h + ${CMAKE_CURRENT_SOURCE_DIR}/page_layout/page_layout_reader_keywords.cpp + ) + +add_dependencies( common page_layout_lexer_source_files ) + +# This one gets made only when testing. +# to build it, first enable #define STAND_ALONE at top of dsnlexer.cpp +add_executable( dsntest EXCLUDE_FROM_ALL dsnlexer.cpp ) +target_link_libraries( dsntest common ${wxWidgets_LIBRARIES} rt ) + +add_dependencies( dsntest lib-dependencies ) + diff --git a/common/base_screen.cpp b/common/base_screen.cpp new file mode 100644 index 0000000..8fa0b7d --- /dev/null +++ b/common/base_screen.cpp @@ -0,0 +1,471 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr + * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2012 Wayne Stambaugh <stambaughw@verizon.net> + * Copyright (C) 1992-2012 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 + */ + +/** + * @file base_screen.cpp + * @brief BASE_SCREEN object implementation. + */ + +#include <fctsys.h> +#include <macros.h> +#include <common.h> +#include <base_struct.h> +#include <class_base_screen.h> +#include <id.h> +#include <base_units.h> + +wxString BASE_SCREEN::m_PageLayoutDescrFileName; // the name of the page layout descr file. + +BASE_SCREEN::BASE_SCREEN( KICAD_T aType ) : + EDA_ITEM( aType ) +{ + m_UndoRedoCountMax = DEFAULT_MAX_UNDO_ITEMS; + m_FirstRedraw = true; + m_ScreenNumber = 1; + m_NumberOfScreens = 1; // Hierarchy: Root: ScreenNumber = 1 + m_Zoom = 32.0; + m_Grid.m_Size = wxRealPoint( 50, 50 ); // Default grid size + m_Grid.m_CmdId = ID_POPUP_GRID_LEVEL_50; + m_Center = true; + m_IsPrinting = false; + m_ScrollPixelsPerUnitX = 1; + m_ScrollPixelsPerUnitY = 1; + + m_FlagModified = false; // Set when any change is made on board. + m_FlagSave = false; // Used in auto save set when an auto save is required. + + SetCurItem( NULL ); +} + + +BASE_SCREEN::~BASE_SCREEN() +{ +} + + +void BASE_SCREEN::InitDataPoints( const wxSize& aPageSizeIU ) +{ + if( m_Center ) + { + m_crossHairPosition.x = 0; + m_crossHairPosition.y = 0; + + m_DrawOrg.x = -aPageSizeIU.x / 2; + m_DrawOrg.y = -aPageSizeIU.y / 2; + } + else + { + m_crossHairPosition.x = aPageSizeIU.x / 2; + m_crossHairPosition.y = aPageSizeIU.y / 2; + + m_DrawOrg.x = 0; + m_DrawOrg.y = 0; + } + + m_O_Curseur.x = m_O_Curseur.y = 0; +} + + +double BASE_SCREEN::GetScalingFactor() const +{ + double scale = 1.0 / GetZoom(); + return scale; +} + + +void BASE_SCREEN::SetScalingFactor( double aScale ) +{ + // Limit zoom to max and min allowed values: + double zoom = Clamp( GetMinAllowedZoom(), aScale, GetMaxAllowedZoom() ); + + SetZoom( zoom ); +} + + +bool BASE_SCREEN::SetFirstZoom() +{ + return SetZoom( GetMinAllowedZoom() ); +} + + +bool BASE_SCREEN::SetLastZoom() +{ + return SetZoom( GetMaxAllowedZoom() ); +} + + +bool BASE_SCREEN::SetZoom( double iu_per_du ) +{ + if( iu_per_du == m_Zoom ) + return false; + + //wxLogDebug( "Zoom:%.16g 1/Zoom:%.16g", iu_per_du, 1/iu_per_du ); + + if( iu_per_du < GetMinAllowedZoom() ) + return false; + + if( iu_per_du > GetMaxAllowedZoom() ) + return false; + + m_Zoom = iu_per_du; + + return true; +} + + +bool BASE_SCREEN::SetNextZoom() +{ + for( unsigned i=0; i < m_ZoomList.size(); ++i ) + { + if( m_Zoom < m_ZoomList[i] ) + { + SetZoom( m_ZoomList[i] ); + return true; + } + } + + return false; +} + + +bool BASE_SCREEN::SetPreviousZoom() +{ + for( unsigned i = m_ZoomList.size(); i != 0; --i ) + { + if( m_Zoom > m_ZoomList[i - 1] ) + { + SetZoom( m_ZoomList[i - 1] ); + return true; + } + } + + return false; +} + +/* Build the list of human readable grid list. + * The list shows the grid size both in mils or mm. + * aMmFirst = true to have mm first and mils after + * false to have mils first and mm after + */ +int BASE_SCREEN::BuildGridsChoiceList( wxArrayString& aGridsList, bool aMmFirst) const +{ + wxString msg; + wxRealPoint curr_grid_size = GetGridSize(); + int idx = -1; + int idx_usergrid = -1; + + for( size_t i = 0; i < GetGridCount(); i++ ) + { + const GRID_TYPE& grid = m_grids[i]; + double gridValueMils = To_User_Unit( INCHES, grid.m_Size.x ) * 1000; + double gridValue_mm = To_User_Unit( MILLIMETRES, grid.m_Size.x ); + + if( grid.m_CmdId == ID_POPUP_GRID_USER ) + { + msg = _( "User Grid" ); + idx_usergrid = i; + } + else + { + if( aMmFirst ) + msg.Printf( _( "Grid: %.4f mm (%.2f mils)" ), + gridValue_mm, gridValueMils ); + else + msg.Printf( _( "Grid: %.2f mils (%.4f mm)" ), + gridValueMils, gridValue_mm ); + } + + aGridsList.Add( msg ); + + if( curr_grid_size == grid.m_Size ) + idx = i; + } + + if( idx < 0 ) + idx = idx_usergrid; + + return idx; +} + + +void BASE_SCREEN::SetGridList( GRIDS& gridlist ) +{ + if( !m_grids.empty() ) + m_grids.clear(); + + m_grids = gridlist; +} + + +int BASE_SCREEN::SetGrid( const wxRealPoint& size ) +{ + wxASSERT( !m_grids.empty() ); + + GRID_TYPE nearest_grid = m_grids[0]; + int gridIdx = 0; + + for( unsigned i = 0; i < m_grids.size(); i++ ) + { + if( m_grids[i].m_Size == size ) + { + m_Grid = m_grids[i]; + return m_grids[i].m_CmdId - ID_POPUP_GRID_LEVEL_1000; + } + + // keep track of the nearest larger grid size, if the exact size is not found + if ( size.x < m_grids[i].m_Size.x ) + { + gridIdx = m_grids[i].m_CmdId - ID_POPUP_GRID_LEVEL_1000; + nearest_grid = m_grids[i]; + } + } + + m_Grid = nearest_grid; + + wxLogWarning( wxT( "Grid size( %f, %f ) not in grid list, falling back " ) \ + wxT( "to grid size( %f, %f )." ), + size.x, size.y, m_Grid.m_Size.x, m_Grid.m_Size.y ); + + return gridIdx; +} + + +int BASE_SCREEN::SetGrid( int aCommandId ) +{ + wxASSERT( !m_grids.empty() ); + + for( unsigned i = 0; i < m_grids.size(); i++ ) + { + if( m_grids[i].m_CmdId == aCommandId ) + { + m_Grid = m_grids[i]; + return m_grids[i].m_CmdId - ID_POPUP_GRID_LEVEL_1000; + } + } + + m_Grid = m_grids[0]; + + wxLogWarning( wxT( "Grid ID %d not in grid list, falling back to " ) \ + wxT( "grid size( %g, %g )." ), aCommandId, + m_Grid.m_Size.x, m_Grid.m_Size.y ); + + return m_grids[0].m_CmdId - ID_POPUP_GRID_LEVEL_1000; +} + + + +void BASE_SCREEN::AddGrid( const GRID_TYPE& grid ) +{ + for( unsigned i = 0; i < m_grids.size(); i++ ) + { + if( m_grids[i].m_Size == grid.m_Size && grid.m_CmdId != ID_POPUP_GRID_USER ) + { + wxLogDebug( wxT( "Discarding duplicate grid size( %g, %g )." ), + grid.m_Size.x, grid.m_Size.y ); + return; + } + + if( m_grids[i].m_CmdId == grid.m_CmdId ) + { + wxLogDebug( wxT( "Changing grid ID %d from size( %g, %g ) to " ) \ + wxT( "size( %g, %g )." ), + grid.m_CmdId, m_grids[i].m_Size.x, + m_grids[i].m_Size.y, grid.m_Size.x, grid.m_Size.y ); + m_grids[i].m_Size = grid.m_Size; + return; + } + } + + m_grids.push_back( grid ); +} + + +void BASE_SCREEN::AddGrid( const wxRealPoint& size, int id ) +{ + GRID_TYPE grid; + + grid.m_Size = size; + grid.m_CmdId = id; + AddGrid( grid ); +} + + +void BASE_SCREEN::AddGrid( const wxRealPoint& size, EDA_UNITS_T aUnit, int id ) +{ + wxRealPoint new_size; + GRID_TYPE new_grid; + + new_size.x = From_User_Unit( aUnit, size.x ); + new_size.y = From_User_Unit( aUnit, size.y ); + new_grid.m_CmdId = id; + new_grid.m_Size = new_size; + + AddGrid( new_grid ); +} + + +GRID_TYPE& BASE_SCREEN::GetGrid( size_t aIndex ) +{ + wxCHECK_MSG( !m_grids.empty() && aIndex < m_grids.size(), m_Grid, + wxT( "Cannot get grid object outside the bounds of the grid list." ) ); + + return m_grids[ aIndex ]; +} + + +bool BASE_SCREEN::GridExists( int aCommandId ) +{ + // tests for grid command ID (not an index in grid list, but a wxID) exists in grid list. + for( unsigned i = 0; i < m_grids.size(); i++ ) + { + if( m_grids[i].m_CmdId == aCommandId ) + return true; + } + + return false; +} + + +wxPoint BASE_SCREEN::getNearestGridPosition( const wxPoint& aPosition, + const wxPoint& aGridOrigin, wxRealPoint* aGridSize ) const +{ + wxPoint pt; + wxRealPoint gridSize; + + if( aGridSize ) + gridSize = *aGridSize; + else + gridSize = GetGridSize(); + + double offset = fmod( aGridOrigin.x, gridSize.x ); + int x = KiROUND( (aPosition.x - offset) / gridSize.x ); + + pt.x = KiROUND( x * gridSize.x + offset ); + + offset = fmod( aGridOrigin.y, gridSize.y ); + + int y = KiROUND( (aPosition.y - offset) / gridSize.y ); + pt.y = KiROUND ( y * gridSize.y + offset ); + + return pt; +} + + +wxPoint BASE_SCREEN::getCursorPosition( bool aOnGrid, const wxPoint& aGridOrigin, wxRealPoint* aGridSize ) const +{ + if( aOnGrid ) + return getNearestGridPosition( m_crossHairPosition, aGridOrigin, aGridSize ); + + return m_crossHairPosition; +} + + +wxPoint BASE_SCREEN::getCrossHairScreenPosition() const +{ + wxPoint pos = m_crossHairPosition - m_DrawOrg; + double scalar = GetScalingFactor(); + + pos.x = KiROUND( (double) pos.x * scalar ); + pos.y = KiROUND( (double) pos.y * scalar ); + + return pos; +} + + +void BASE_SCREEN::setCrossHairPosition( const wxPoint& aPosition, const wxPoint& aGridOrigin, bool aSnapToGrid ) +{ + if( aSnapToGrid ) + m_crossHairPosition = getNearestGridPosition( aPosition, aGridOrigin, NULL ); + else + m_crossHairPosition = aPosition; +} + + +void BASE_SCREEN::ClearUndoRedoList() +{ + ClearUndoORRedoList( m_UndoList ); + ClearUndoORRedoList( m_RedoList ); +} + + +void BASE_SCREEN::PushCommandToUndoList( PICKED_ITEMS_LIST* aNewitem ) +{ + m_UndoList.PushCommand( aNewitem ); + + // Delete the extra items, if count max reached + if( m_UndoRedoCountMax > 0 ) + { + int extraitems = GetUndoCommandCount() - m_UndoRedoCountMax; + if( extraitems > 0 ) + ClearUndoORRedoList( m_UndoList, extraitems ); + } +} + + +void BASE_SCREEN::PushCommandToRedoList( PICKED_ITEMS_LIST* aNewitem ) +{ + m_RedoList.PushCommand( aNewitem ); + + // Delete the extra items, if count max reached + if( m_UndoRedoCountMax > 0 ) + { + int extraitems = GetRedoCommandCount() - m_UndoRedoCountMax; + if( extraitems > 0 ) + ClearUndoORRedoList( m_RedoList, extraitems ); + } +} + + +PICKED_ITEMS_LIST* BASE_SCREEN::PopCommandFromUndoList( ) +{ + return m_UndoList.PopCommand( ); +} + + +PICKED_ITEMS_LIST* BASE_SCREEN::PopCommandFromRedoList( ) +{ + return m_RedoList.PopCommand( ); +} + + +#if defined(DEBUG) + +void BASE_SCREEN::Show( int nestLevel, std::ostream& os ) const +{ + // for now, make it look like XML, expand on this later. + NestedSpace( nestLevel, os ) << '<' << GetClass().Lower().mb_str() << ">\n"; + + /* this class will eventually go away, but here's a place holder until then. + for( EDA_ITEM* item = m_drawList; item; item = item->Next() ) + { + item->Show( nestLevel+1, os ); + } + */ + + NestedSpace( nestLevel, os ) << "</" << GetClass().Lower().mb_str() << ">\n"; +} + +#endif diff --git a/common/base_struct.cpp b/common/base_struct.cpp new file mode 100644 index 0000000..9b6273d --- /dev/null +++ b/common/base_struct.cpp @@ -0,0 +1,592 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com + * Copyright (C) 1992-2015 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 + */ + +/** + * @file base_struct.cpp + * @brief Implementation of EDA_ITEM base class for KiCad. + */ + +#include <fctsys.h> +#include <trigo.h> +#include <common.h> +#include <macros.h> +#include <kicad_string.h> +#include <wxstruct.h> +#include <class_drawpanel.h> +#include <class_base_screen.h> + +#include "../eeschema/dialogs/dialog_schematic_find.h" + + +const wxString traceFindReplace( wxT( "KicadFindReplace" ) ); + + +enum textbox { + ID_TEXTBOX_LIST = 8010 +}; + + +EDA_ITEM::EDA_ITEM( EDA_ITEM* parent, KICAD_T idType ) +{ + initVars(); + m_StructType = idType; + m_Parent = parent; +} + + +EDA_ITEM::EDA_ITEM( KICAD_T idType ) +{ + initVars(); + m_StructType = idType; +} + + +EDA_ITEM::EDA_ITEM( const EDA_ITEM& base ) +{ + initVars(); + m_StructType = base.m_StructType; + m_Parent = base.m_Parent; + m_Flags = base.m_Flags; + + // A copy of an item cannot have the same time stamp as the original item. + SetTimeStamp( GetNewTimeStamp() ); + m_Status = base.m_Status; +} + + +void EDA_ITEM::initVars() +{ + m_StructType = TYPE_NOT_INIT; + Pnext = NULL; // Linked list: Link (next struct) + Pback = NULL; // Linked list: Link (previous struct) + m_Parent = NULL; // Linked list: Link (parent struct) + m_List = NULL; // I am not on any list yet + m_Image = NULL; // Link to an image copy for undelete or abort command + m_Flags = 0; // flags for editions and other + SetTimeStamp( 0 ); // Time stamp used for logical links + m_Status = 0; + m_forceVisible = false; // true to override the visibility setting of the item. +} + + +void EDA_ITEM::SetModified() +{ + SetFlags( IS_CHANGED ); + + // If this a child object, then the parent modification state also needs to be set. + if( m_Parent ) + m_Parent->SetModified(); +} + + +EDA_ITEM* EDA_ITEM::Clone() const +{ + wxCHECK_MSG( false, NULL, wxT( "Clone not implemented in derived class " ) + GetClass() + + wxT( ". Bad programmer!" ) ); +} + + +SEARCH_RESULT EDA_ITEM::IterateForward( EDA_ITEM* listStart, + INSPECTOR* inspector, + const void* testData, + const KICAD_T scanTypes[] ) +{ + EDA_ITEM* p = listStart; + + for( ; p; p = p->Pnext ) + { + if( SEARCH_QUIT == p->Visit( inspector, testData, scanTypes ) ) + return SEARCH_QUIT; + } + + return SEARCH_CONTINUE; +} + + +// see base_struct.h +// many classes inherit this method, be careful: +SEARCH_RESULT EDA_ITEM::Visit( INSPECTOR* inspector, const void* testData, + const KICAD_T scanTypes[] ) +{ + KICAD_T stype; + +#if 0 && defined(DEBUG) + std::cout << GetClass().mb_str() << ' '; +#endif + + for( const KICAD_T* p = scanTypes; (stype = *p) != EOT; ++p ) + { + // If caller wants to inspect my type + if( stype == Type() ) + { + if( SEARCH_QUIT == inspector->Inspect( this, testData ) ) + return SEARCH_QUIT; + + break; + } + } + + return SEARCH_CONTINUE; +} + + +wxString EDA_ITEM::GetSelectMenuText() const +{ + wxFAIL_MSG( wxT( "GetSelectMenuText() was not overridden for schematic item type " ) + + GetClass() ); + + return wxString( wxT( "Undefined menu text for " ) + GetClass() ); +} + + +bool EDA_ITEM::Matches( const wxString& aText, wxFindReplaceData& aSearchData ) +{ + wxString text = aText; + wxString searchText = aSearchData.GetFindString(); + + // Don't match if searching for replaceable item and the item doesn't support text replace. + if( (aSearchData.GetFlags() & FR_SEARCH_REPLACE) && !IsReplaceable() ) + return false; + + if( aSearchData.GetFlags() & wxFR_WHOLEWORD ) + return aText.IsSameAs( searchText, aSearchData.GetFlags() & wxFR_MATCHCASE ); + + if( aSearchData.GetFlags() & FR_MATCH_WILDCARD ) + { + if( aSearchData.GetFlags() & wxFR_MATCHCASE ) + return text.Matches( searchText ); + + return text.MakeUpper().Matches( searchText.MakeUpper() ); + } + + if( aSearchData.GetFlags() & wxFR_MATCHCASE ) + return aText.Find( searchText ) != wxNOT_FOUND; + + return text.MakeUpper().Find( searchText.MakeUpper() ) != wxNOT_FOUND; +} + + +bool EDA_ITEM::Replace( wxFindReplaceData& aSearchData, wxString& aText ) +{ + wxCHECK_MSG( IsReplaceable(), false, + wxT( "Attempt to replace text in <" ) + GetClass() + wxT( "> item." ) ); + + wxString searchString = (aSearchData.GetFlags() & wxFR_MATCHCASE) ? aText : aText.Upper(); + + int result = searchString.Find( (aSearchData.GetFlags() & wxFR_MATCHCASE) ? + aSearchData.GetFindString() : + aSearchData.GetFindString().Upper() ); + + if( result == wxNOT_FOUND ) + return false; + + wxString prefix = aText.Left( result ); + wxString suffix; + + if( aSearchData.GetFindString().length() + result < aText.length() ) + suffix = aText.Right( aText.length() - ( aSearchData.GetFindString().length() + result ) ); + + wxLogTrace( traceFindReplace, wxT( "Replacing '%s', prefix '%s', replace '%s', suffix '%s'." ), + GetChars( aText ), GetChars( prefix ), GetChars( aSearchData.GetReplaceString() ), + GetChars( suffix ) ); + + aText = prefix + aSearchData.GetReplaceString() + suffix; + + return true; +} + + +bool EDA_ITEM::operator<( const EDA_ITEM& aItem ) const +{ + wxFAIL_MSG( wxString::Format( wxT( "Less than operator not defined for item type %s." ), + GetChars( GetClass() ) ) ); + + return false; +} + +#ifdef USE_EDA_ITEM_OP_EQ // see base_struct.h for explanations +EDA_ITEM& EDA_ITEM::operator=( const EDA_ITEM& aItem ) +{ + if( &aItem != this ) + { + m_Image = aItem.m_Image; + m_StructType = aItem.m_StructType; + m_Parent = aItem.m_Parent; + m_Flags = aItem.m_Flags; + m_TimeStamp = aItem.m_TimeStamp; + m_Status = aItem.m_Status; + m_forceVisible = aItem.m_forceVisible; + } + + return *this; +} +#endif + +const BOX2I EDA_ITEM::ViewBBox() const +{ + // Basic fallback + return BOX2I( VECTOR2I( GetBoundingBox().GetOrigin() ), + VECTOR2I( GetBoundingBox().GetSize() ) ); +} + + +void EDA_ITEM::ViewGetLayers( int aLayers[], int& aCount ) const +{ + // Basic fallback + aCount = 1; + aLayers[0] = 0; +} + + +#if defined(DEBUG) + +// A function that should have been in wxWidgets +std::ostream& operator<<( std::ostream& out, const wxSize& size ) +{ + out << " width=\"" << size.GetWidth() << "\" height=\"" << size.GetHeight() << "\""; + return out; +} + + +// A function that should have been in wxWidgets +std::ostream& operator<<( std::ostream& out, const wxPoint& pt ) +{ + out << " x=\"" << pt.x << "\" y=\"" << pt.y << "\""; + return out; +} + + +void EDA_ITEM::ShowDummy( std::ostream& os ) const +{ + // XML output: + wxString s = GetClass(); + + os << '<' << s.Lower().mb_str() << ">" + << " Need ::Show() override for this class " + << "</" << s.Lower().mb_str() << ">\n"; +} + + +std::ostream& EDA_ITEM::NestedSpace( int nestLevel, std::ostream& os ) +{ + for( int i = 0; i<nestLevel; ++i ) + os << " "; + + // number of spaces here controls indent per nest level + + return os; +} + +#endif + + +/******************/ +/* Class EDA_RECT */ +/******************/ + +void EDA_RECT::Normalize() +{ + if( m_Size.y < 0 ) + { + m_Size.y = -m_Size.y; + m_Pos.y -= m_Size.y; + } + + if( m_Size.x < 0 ) + { + m_Size.x = -m_Size.x; + m_Pos.x -= m_Size.x; + } +} + + +void EDA_RECT::Move( const wxPoint& aMoveVector ) +{ + m_Pos += aMoveVector; +} + + +bool EDA_RECT::Contains( const wxPoint& aPoint ) const +{ + wxPoint rel_pos = aPoint - m_Pos; + wxSize size = m_Size; + + if( size.x < 0 ) + { + size.x = -size.x; + rel_pos.x += size.x; + } + + if( size.y < 0 ) + { + size.y = -size.y; + rel_pos.y += size.y; + } + + return (rel_pos.x >= 0) && (rel_pos.y >= 0) && ( rel_pos.y <= size.y) && ( rel_pos.x <= size.x); +} + + +/* + * return true if aRect is inside me (or on boundaries) + */ +bool EDA_RECT::Contains( const EDA_RECT& aRect ) const +{ + return Contains( aRect.GetOrigin() ) && Contains( aRect.GetEnd() ); +} + + +/* Intersects + * test for a common area between segment and rect. + * return true if at least a common point is found + */ +bool EDA_RECT::Intersects( const wxPoint& aPoint1, const wxPoint& aPoint2 ) const +{ + wxPoint point2, point4; + + if( Contains( aPoint1 ) || Contains( aPoint2 ) ) + return true; + + point2.x = GetEnd().x; + point2.y = GetOrigin().y; + point4.x = GetOrigin().x; + point4.y = GetEnd().y; + + //Only need to test 3 sides since a straight line cant enter and exit on same side + if( SegmentIntersectsSegment( aPoint1, aPoint2, GetOrigin() , point2 ) ) + return true; + + if( SegmentIntersectsSegment( aPoint1, aPoint2, point2 , GetEnd() ) ) + return true; + + if( SegmentIntersectsSegment( aPoint1, aPoint2, GetEnd() , point4 ) ) + return true; + + return false; +} + + +/* Intersects + * test for a common area between 2 rect. + * return true if at least a common point is found + */ +bool EDA_RECT::Intersects( const EDA_RECT& aRect ) const +{ + // this logic taken from wxWidgets' geometry.cpp file: + bool rc; + EDA_RECT me(*this); + EDA_RECT rect(aRect); + me.Normalize(); // ensure size is >= 0 + rect.Normalize(); // ensure size is >= 0 + + // calculate the left common area coordinate: + int left = std::max( me.m_Pos.x, rect.m_Pos.x ); + // calculate the right common area coordinate: + int right = std::min( me.m_Pos.x + me.m_Size.x, rect.m_Pos.x + rect.m_Size.x ); + // calculate the upper common area coordinate: + int top = std::max( me.m_Pos.y, aRect.m_Pos.y ); + // calculate the lower common area coordinate: + int bottom = std::min( me.m_Pos.y + me.m_Size.y, rect.m_Pos.y + rect.m_Size.y ); + + // if a common area exists, it must have a positive (null accepted) size + if( left <= right && top <= bottom ) + rc = true; + else + rc = false; + + return rc; +} + + +EDA_RECT& EDA_RECT::Inflate( int aDelta ) +{ + Inflate( aDelta, aDelta ); + return *this; +} + + +EDA_RECT& EDA_RECT::Inflate( wxCoord dx, wxCoord dy ) +{ + if( m_Size.x >= 0 ) + { + if( m_Size.x < -2 * dx ) + { + // Don't allow deflate to eat more width than we have, + m_Pos.x += m_Size.x / 2; + m_Size.x = 0; + } + else + { + // The inflate is valid. + m_Pos.x -= dx; + m_Size.x += 2 * dx; + } + } + else // size.x < 0: + { + if( m_Size.x > -2 * dx ) + { + // Don't allow deflate to eat more width than we have, + m_Pos.x -= m_Size.x / 2; + m_Size.x = 0; + } + else + { + // The inflate is valid. + m_Pos.x += dx; + m_Size.x -= 2 * dx; // m_Size.x <0: inflate when dx > 0 + } + } + + if( m_Size.y >= 0 ) + { + if( m_Size.y < -2 * dy ) + { + // Don't allow deflate to eat more height than we have, + m_Pos.y += m_Size.y / 2; + m_Size.y = 0; + } + else + { + // The inflate is valid. + m_Pos.y -= dy; + m_Size.y += 2 * dy; + } + } + else // size.y < 0: + { + if( m_Size.y > 2 * dy ) + { + // Don't allow deflate to eat more height than we have, + m_Pos.y -= m_Size.y / 2; + m_Size.y = 0; + } + else + { + // The inflate is valid. + m_Pos.y += dy; + m_Size.y -= 2 * dy; // m_Size.y <0: inflate when dy > 0 + } + } + + return *this; +} + + +void EDA_RECT::Merge( const EDA_RECT& aRect ) +{ + Normalize(); // ensure width and height >= 0 + EDA_RECT rect = aRect; + rect.Normalize(); // ensure width and height >= 0 + wxPoint end = GetEnd(); + wxPoint rect_end = rect.GetEnd(); + + // Change origin and size in order to contain the given rect + m_Pos.x = std::min( m_Pos.x, rect.m_Pos.x ); + m_Pos.y = std::min( m_Pos.y, rect.m_Pos.y ); + end.x = std::max( end.x, rect_end.x ); + end.y = std::max( end.y, rect_end.y ); + SetEnd( end ); +} + + +void EDA_RECT::Merge( const wxPoint& aPoint ) +{ + Normalize(); // ensure width and height >= 0 + + wxPoint end = GetEnd(); + // Change origin and size in order to contain the given rect + m_Pos.x = std::min( m_Pos.x, aPoint.x ); + m_Pos.y = std::min( m_Pos.y, aPoint.y ); + end.x = std::max( end.x, aPoint.x ); + end.y = std::max( end.y, aPoint.y ); + SetEnd( end ); +} + + +double EDA_RECT::GetArea() const +{ + return (double) GetWidth() * (double) GetHeight(); +} + + +EDA_RECT EDA_RECT::Common( const EDA_RECT& aRect ) const +{ + EDA_RECT r; + + if( Intersects( aRect ) ) + { + wxPoint originA( std::min( GetOrigin().x, GetEnd().x ), + std::min( GetOrigin().y, GetEnd().y ) ); + wxPoint originB( std::min( aRect.GetOrigin().x, aRect.GetEnd().x ), + std::min( aRect.GetOrigin().y, aRect.GetEnd().y ) ); + wxPoint endA( std::max( GetOrigin().x, GetEnd().x ), + std::max( GetOrigin().y, GetEnd().y ) ); + wxPoint endB( std::max( aRect.GetOrigin().x, aRect.GetEnd().x ), + std::max( aRect.GetOrigin().y, aRect.GetEnd().y ) ); + + r.SetOrigin( wxPoint( std::max( originA.x, originB.x ), std::max( originA.y, originB.y ) ) ); + r.SetEnd ( wxPoint( std::min( endA.x, endB.x ), std::min( endA.y, endB.y ) ) ); + } + + return r; +} + + +/* Calculate the bounding box of this, when rotated + */ +const EDA_RECT EDA_RECT::GetBoundingBoxRotated( wxPoint aRotCenter, double aAngle ) +{ + wxPoint corners[4]; + + // Build the corners list + corners[0] = GetOrigin(); + corners[2] = GetEnd(); + corners[1].x = corners[0].x; + corners[1].y = corners[2].y; + corners[3].x = corners[2].x; + corners[3].y = corners[0].y; + + // Rotate all corners, to find the bounding box + for( int ii = 0; ii < 4; ii ++ ) + RotatePoint( &corners[ii], aRotCenter, aAngle ); + + // Find the corners bounding box + wxPoint start = corners[0]; + wxPoint end = corners[0]; + + for( int ii = 1; ii < 4; ii ++ ) + { + start.x = std::min( start.x, corners[ii].x); + start.y = std::min( start.y, corners[ii].y); + end.x = std::max( end.x, corners[ii].x); + end.y = std::max( end.y, corners[ii].y); + } + + EDA_RECT bbox; + bbox.SetOrigin( start ); + bbox.SetEnd( end ); + + return bbox; +} diff --git a/common/base_units.cpp b/common/base_units.cpp new file mode 100644 index 0000000..7cf035c --- /dev/null +++ b/common/base_units.cpp @@ -0,0 +1,423 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 CERN + * Copyright (C) 1992-2011 KiCad Developers, see change_log.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 + */ + +/** + * @author Wayne Stambaugh <stambaughw@verizon.net> + * @file base_units.cpp + * @brief Code to handle objects that require both schematic and board internal units. + * @note This file is an ugly hack to solve the problem of formatting the base units + * for either schematics or boards in objects that are include in both domains. + * At some point in the future. This code should be rolled back into the + * appropriate object and build with the correct internal unit formatting + * depending on the application. + */ + +#include <macros.h> +#include <base_struct.h> +#include <class_title_block.h> +#include <common.h> +#include <base_units.h> + + +#if defined( PCBNEW ) || defined( CVPCB ) || defined( EESCHEMA ) || defined( GERBVIEW ) || defined( PL_EDITOR ) +#define IU_TO_MM( x ) ( x / IU_PER_MM ) +#define IU_TO_IN( x ) ( x / IU_PER_MILS / 1000 ) +#define MM_TO_IU( x ) ( x * IU_PER_MM ) +#define IN_TO_IU( x ) ( x * IU_PER_MILS * 1000 ) +#else +#error "Cannot resolve internal units due to no definition of EESCHEMA, CVPCB or PCBNEW." +#endif + + +// Helper function to print a float number without using scientific notation +// and no trailing 0 +// So we cannot always just use the %g or the %f format to print a fp number +// this helper function uses the %f format when needed, or %g when %f is +// not well working and then removes trailing 0 + +std::string Double2Str( double aValue ) +{ + char buf[50]; + int len; + + if( aValue != 0.0 && fabs( aValue ) <= 0.0001 ) + { + // For these small values, %f works fine, + // and %g gives an exponent + len = sprintf( buf, "%.16f", aValue ); + + while( --len > 0 && buf[len] == '0' ) + buf[len] = '\0'; + + if( buf[len] == '.' ) + buf[len] = '\0'; + else + ++len; + } + else + { + // For these values, %g works fine, and sometimes %f + // gives a bad value (try aValue = 1.222222222222, with %.16f format!) + len = sprintf( buf, "%.16g", aValue ); + } + + return std::string( buf, len ); +} + + +double To_User_Unit( EDA_UNITS_T aUnit, double aValue ) +{ + switch( aUnit ) + { + case MILLIMETRES: + return IU_TO_MM( aValue ); + + case INCHES: + return IU_TO_IN( aValue ); + + case DEGREES: + return aValue / 10.0f; + + default: + return aValue; + } +} + +/* Convert a value to a string using double notation. + * For readability, the mantissa has 0, 1, 3 or 4 digits, depending on units + * for unit = inch the mantissa has 3 digits (Eeschema) or 4 digits + * for unit = mil the mantissa has 0 digits (Eeschema) or 1 digits + * for unit = mm the mantissa has 3 digits (Eeschema) or 4 digits + * Should be used only to display info in status, + * but not in dialogs, because 4 digits only + * could truncate the actual value + */ +wxString CoordinateToString( int aValue, bool aConvertToMils ) +{ + return LengthDoubleToString( (double) aValue, aConvertToMils ); +} + +wxString LengthDoubleToString( double aValue, bool aConvertToMils ) +{ + wxString text; + const wxChar* format; + double value = To_User_Unit( g_UserUnit, aValue ); + + if( g_UserUnit == INCHES ) + { + if( aConvertToMils ) + { +#if defined( EESCHEMA ) + format = wxT( "%.0f" ); +#else + format = wxT( "%.1f" ); +#endif + value *= 1000; + } + else + { +#if defined( EESCHEMA ) + format = wxT( "%.3f" ); +#else + format = wxT( "%.4f" ); +#endif + } + } + else + { +#if defined( EESCHEMA ) + format = wxT( "%.2f" ); +#else + format = wxT( "%.3f" ); +#endif + } + + text.Printf( format, value ); + + if( g_UserUnit == INCHES ) + text += ( aConvertToMils ) ? _( " mils" ) : _( " in" ); + else + text += _( " mm" ); + + return text; +} + +/* Remove trailing 0 from a string containing a converted float number. + * the trailing 0 are removed if the mantissa has more + * than aTrailingZeroAllowed digits and some trailing 0 + */ +void StripTrailingZeros( wxString& aStringValue, unsigned aTrailingZeroAllowed ) +{ + struct lconv * lc = localeconv(); + char sep = lc->decimal_point[0]; + unsigned sep_pos = aStringValue.Find( sep ); + + if( sep_pos > 0 ) + { + // We want to keep at least aTrailingZeroAllowed digits after the separator + unsigned min_len = sep_pos + aTrailingZeroAllowed + 1; + + while( aStringValue.Len() > min_len ) + { + if( aStringValue.Last() == '0' ) + aStringValue.RemoveLast(); + else + break; + } + } +} + + +/* Convert a value to a string using double notation. + * For readability, the mantissa has 3 or more digits, + * the trailing 0 are removed if the mantissa has more than 3 digits + * and some trailing 0 + * This function should be used to display values in dialogs because a value + * entered in mm (for instance 2.0 mm) could need up to 8 digits mantissa + * if displayed in inch to avoid truncation or rounding made just by the printf function. + * otherwise the actual value is rounded when read from dialog and converted + * in internal units, and therefore modified. + */ +wxString StringFromValue( EDA_UNITS_T aUnit, int aValue, bool aAddUnitSymbol ) +{ + double value_to_print = To_User_Unit( aUnit, aValue ); + +#if defined( EESCHEMA ) + wxString stringValue = wxString::Format( wxT( "%.3f" ), value_to_print ); + + // Strip trailing zeros. However, keep at least 3 digits in mantissa + // For readability + StripTrailingZeros( stringValue, 3 ); + +#else + + char buf[50]; + int len; + + if( value_to_print != 0.0 && fabs( value_to_print ) <= 0.0001 ) + { + len = sprintf( buf, "%.10f", value_to_print ); + + while( --len > 0 && buf[len] == '0' ) + buf[len] = '\0'; + + if( buf[len]=='.' || buf[len]==',' ) + buf[len] = '\0'; + else + ++len; + } + else + { + len = sprintf( buf, "%.10g", value_to_print ); + } + + wxString stringValue( buf, wxConvUTF8 ); + +#endif + + if( aAddUnitSymbol ) + { + switch( aUnit ) + { + case INCHES: + stringValue += _( " \"" ); + break; + + case MILLIMETRES: + stringValue += _( " mm" ); + break; + + case DEGREES: + stringValue += _( " deg" ); + break; + + case UNSCALED_UNITS: + break; + } + } + + return stringValue; +} + + +void PutValueInLocalUnits( wxTextCtrl& aTextCtr, int aValue ) +{ + wxString msg = StringFromValue( g_UserUnit, aValue ); + + aTextCtr.SetValue( msg ); +} + + +double From_User_Unit( EDA_UNITS_T aUnit, double aValue ) +{ + double value; + + switch( aUnit ) + { + case MILLIMETRES: + value = MM_TO_IU( aValue ); + break; + + case INCHES: + value = IN_TO_IU( aValue ); + break; + + case DEGREES: + // Convert to "decidegrees" + value = aValue * 10; + break; + + default: + case UNSCALED_UNITS: + value = aValue; + } + + return value; +} + + +double DoubleValueFromString( EDA_UNITS_T aUnits, const wxString& aTextValue ) +{ + double value; + double dtmp = 0; + + // Acquire the 'right' decimal point separator + const struct lconv* lc = localeconv(); + + wxChar decimal_point = lc->decimal_point[0]; + wxString buf( aTextValue.Strip( wxString::both ) ); + + // Convert the period in decimal point + buf.Replace( wxT( "." ), wxString( decimal_point, 1 ) ); + + // An ugly fix needed by WxWidgets 2.9.1 that sometimes + // back to a point as separator, although the separator is the comma + // TODO: remove this line if WxWidgets 2.9.2 fixes this issue + buf.Replace( wxT( "," ), wxString( decimal_point, 1 ) ); + + // Find the end of the numeric part + unsigned brk_point = 0; + + while( brk_point < buf.Len() ) + { + wxChar ch = buf[brk_point]; + + if( !( (ch >= '0' && ch <='9') || (ch == decimal_point) || (ch == '-') || (ch == '+') ) ) + { + break; + } + + ++brk_point; + } + + // Extract the numeric part + buf.Left( brk_point ); + + buf.ToDouble( &dtmp ); + + // Check the optional unit designator (2 ch significant) + wxString unit( buf.Mid( brk_point ).Strip( wxString::leading ).Left( 2 ).Lower() ); + + if( aUnits == INCHES || aUnits == MILLIMETRES ) + { + 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( aUnits == DEGREES ) + { + 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 ); +} + + +int ValueFromString( const wxString& aTextValue ) +{ + int value; + + value = ValueFromString( g_UserUnit, aTextValue); + + return value; +} + +int ValueFromTextCtrl( const wxTextCtrl& aTextCtr ) +{ + int value; + wxString msg = aTextCtr.GetValue(); + + value = ValueFromString( g_UserUnit, msg ); + + return value; +} + + +wxString& operator <<( wxString& aString, const wxPoint& aPos ) +{ + aString << wxT( "@ (" ) << CoordinateToString( aPos.x ); + aString << wxT( "," ) << CoordinateToString( aPos.y ); + aString << wxT( ")" ); + + return aString; +} + +/** + * Function AngleToStringDegrees + * is a helper to convert the \a double \a aAngle (in internal unit) + * to a string in degrees + */ +wxString AngleToStringDegrees( double aAngle ) +{ + wxString text; + + text.Printf( wxT( "%.3f" ), aAngle/10.0 ); + StripTrailingZeros( text, 1 ); + + return text; +} + diff --git a/common/basicframe.cpp b/common/basicframe.cpp new file mode 100644 index 0000000..fad5e64 --- /dev/null +++ b/common/basicframe.cpp @@ -0,0 +1,758 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2013-2015 Wayne Stambaugh <stambaughw@verizon.net> + * Copyright (C) 1992-2015 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 + */ + +/** + * @file basicframe.cpp + * @brief EDA_BASE_FRAME class implementation. + */ +#include <config.h> + +// kicad_curl.h must be included before wx headers, to avoid +// conflicts for some defines, at least on Windows +#ifdef BUILD_GITHUB_PLUGIN +#include <curl/curlver.h> +#include <kicad_curl/kicad_curl.h> +#endif + +#include <wx/aboutdlg.h> +#include <wx/fontdlg.h> +#include <wx/clipbrd.h> +#include <wx/statline.h> +#include <wx/platinfo.h> +#include <wx/stdpaths.h> + +#include <build_version.h> +#include <fctsys.h> +#include <pgm_base.h> +#include <kiface_i.h> +#include <online_help.h> +#include <id.h> +#include <eda_doc.h> +#include <wxstruct.h> +#include <macros.h> +#include <menus_helpers.h> +#include <dialog_shim.h> + +#include <boost/version.hpp> +#include <typeinfo> +#include <wx/display.h> + +/// The default auto save interval is 10 minutes. +#define DEFAULT_AUTO_SAVE_INTERVAL 600 + + +const wxChar traceAutoSave[] = wxT( "KicadAutoSave" ); + +/// Configuration file entry name for auto save interval. +static const wxChar entryAutoSaveInterval[] = wxT( "AutoSaveInterval" ); + +/// Configuration file entry for wxAuiManger perspective. +static const wxChar entryPerspective[] = wxT( "Perspective" ); + +/// Configuration file entry for most recently used path. +static const wxChar entryMruPath[] = wxT( "MostRecentlyUsedPath" ); + + +EDA_BASE_FRAME::EDA_BASE_FRAME( wxWindow* aParent, FRAME_T aFrameType, + const wxString& aTitle, const wxPoint& aPos, const wxSize& aSize, + long aStyle, const wxString& aFrameName ) : + wxFrame( aParent, wxID_ANY, aTitle, aPos, aSize, aStyle, aFrameName ) +{ + wxSize minsize; + + m_Ident = aFrameType; + m_mainToolBar = NULL; + m_hasAutoSave = false; + m_autoSaveState = false; + m_autoSaveInterval = -1; + m_autoSaveTimer = new wxTimer( this, ID_AUTO_SAVE_TIMER ); + m_mruPath = wxStandardPaths::Get().GetDocumentsDir(); + minsize.x = 470; + minsize.y = 350; + + SetSizeHints( minsize.x, minsize.y, -1, -1, -1, -1 ); + + if( ( aSize.x < minsize.x ) || ( aSize.y < minsize.y ) ) + SetSize( 0, 0, minsize.x, minsize.y ); + + // Create child subwindows. + + // Dimensions of the user area of the main window. + GetClientSize( &m_FrameSize.x, &m_FrameSize.y ); + + m_FramePos.x = m_FramePos.y = 0; + + Connect( ID_HELP_COPY_VERSION_STRING, + wxEVT_COMMAND_MENU_SELECTED, + wxCommandEventHandler( EDA_BASE_FRAME::CopyVersionInfoToClipboard ) ); + + Connect( ID_AUTO_SAVE_TIMER, wxEVT_TIMER, + wxTimerEventHandler( EDA_BASE_FRAME::onAutoSaveTimer ) ); + + // hook wxEVT_CLOSE_WINDOW so we can call SaveSettings(). This function seems + // to be called before any other hook for wxCloseEvent, which is necessary. + Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( EDA_BASE_FRAME::windowClosing ) ); +} + + +void EDA_BASE_FRAME::windowClosing( wxCloseEvent& event ) +{ + DIALOG_SHIM* dlg = NULL; + wxWindowList list = GetChildren(); + + // Quasi modal dialogs create issues (crashes) when closing Kicad. + // I am guessing they are delete too late, when deleting main frames. + // AFAIK, only these DIALOG_SHIM dialogs create such issues. + // The policy is do not allow closing Kicad if a Quasi modal dialog is open. + // (Anyway, closing without prompting the user is certainly bad, + // because an edit is in preogress) + // Therefore, iterate through the child list to find at least + // a DIALOG_SHIM opened in quasi modal mode + for( wxWindowList::iterator iter = list.begin(); iter != list.end(); ++iter ) + { + if( (dlg = dynamic_cast<DIALOG_SHIM*> (*iter) ) != NULL ) + { + if( dlg->IsQuasiModal() ) + break; + else + dlg = NULL; + } + } + + if( dlg ) + { + // Happens when a quasi modal dialog is currently open. + // For example: if the Kicad manager try to close Kicad. + wxMessageBox( _( + "The program cannot be closed\n" + "A quasi-modal dialog window is currently open, please close it first." ) ); + event.Veto(); + return; + } + + wxConfigBase* cfg = config(); + + if( cfg ) + SaveSettings( cfg ); // virtual, wxFrame specific + + event.Skip(); // we did not "handle" the event, only eavesdropped on it. +} + + +EDA_BASE_FRAME::~EDA_BASE_FRAME() +{ + delete m_autoSaveTimer; + + // This is needed for OSX: avoids further OnDraw processing after this + // destructor and before the native window is destroyed + this->Freeze(); +} + + +bool EDA_BASE_FRAME::ProcessEvent( wxEvent& aEvent ) +{ + if( !wxFrame::ProcessEvent( aEvent ) ) + return false; + + if( IsShown() && m_hasAutoSave && + (m_autoSaveState != isAutoSaveRequired()) && (m_autoSaveInterval > 0) ) + { + if( !m_autoSaveState ) + { + wxLogTrace( traceAutoSave, wxT( "Starting auto save timer." ) ); + m_autoSaveTimer->Start( m_autoSaveInterval * 1000, wxTIMER_ONE_SHOT ); + m_autoSaveState = true; + } + else if( m_autoSaveTimer->IsRunning() ) + { + wxLogTrace( traceAutoSave, wxT( "Stopping auto save timer." ) ); + m_autoSaveTimer->Stop(); + m_autoSaveState = false; + } + } + + return true; +} + + +bool EDA_BASE_FRAME::Enable( bool enable ) +{ + // so we can do logging of this state change: + +#if defined(DEBUG) + const char* type_id = typeid( *this ).name(); + printf( "wxFrame %-28s: %s\n", type_id, enable ? "enabled" : "disabled" ); +#endif + + return wxFrame::Enable( enable ); +} + + +void EDA_BASE_FRAME::onAutoSaveTimer( wxTimerEvent& aEvent ) +{ + if( !doAutoSave() ) + m_autoSaveTimer->Start( m_autoSaveInterval * 1000, wxTIMER_ONE_SHOT ); +} + + +bool EDA_BASE_FRAME::doAutoSave() +{ + wxCHECK_MSG( false, true, wxT( "Auto save timer function not overridden. Bad programmer!" ) ); +} + + +void EDA_BASE_FRAME::ReCreateMenuBar() +{ +} + + +void EDA_BASE_FRAME::ShowChangedLanguage() +{ + ReCreateMenuBar(); + GetMenuBar()->Refresh(); +} + + +void EDA_BASE_FRAME::LoadSettings( wxConfigBase* aCfg ) +{ + int maximized = 0; + + wxString baseCfgName = ConfigBaseName(); + + wxString text = baseCfgName + wxT( "Pos_x" ); + aCfg->Read( text, &m_FramePos.x ); + + text = baseCfgName + wxT( "Pos_y" ); + aCfg->Read( text, &m_FramePos.y ); + + text = baseCfgName + wxT( "Size_x" ); + aCfg->Read( text, &m_FrameSize.x, 600 ); + + text = baseCfgName + wxT( "Size_y" ); + aCfg->Read( text, &m_FrameSize.y, 400 ); + + text = baseCfgName + wxT( "Maximized" ); + aCfg->Read( text, &maximized, 0 ); + + if( m_hasAutoSave ) + { + text = baseCfgName + entryAutoSaveInterval; + aCfg->Read( text, &m_autoSaveInterval, DEFAULT_AUTO_SAVE_INTERVAL ); + } + + // Ensure the window is on a connected display, and is visible. + // (at least a corner of the frame must be visible on screen) + // Sometimes, if a window was moved on an auxiliary display, and when this + // display is no more available, it is not the case. + wxRect rect( m_FramePos, m_FrameSize ); + + if( wxDisplay::GetFromPoint( rect.GetTopLeft() ) == wxNOT_FOUND && + wxDisplay::GetFromPoint( rect.GetTopRight() ) == wxNOT_FOUND && + wxDisplay::GetFromPoint( rect.GetBottomLeft() ) == wxNOT_FOUND && + wxDisplay::GetFromPoint( rect.GetBottomRight() ) == wxNOT_FOUND ) + { + m_FramePos = wxDefaultPosition; + } + + // Ensure Window title bar is visible +#if defined( __WXMAC__ ) + // for macOSX, the window must be below system (macOSX) toolbar + // Ypos_min = GetMBarHeight(); seems no more exist in new API (subject to change) + int Ypos_min = 20; +#else + int Ypos_min = 0; +#endif + if( m_FramePos.y < Ypos_min ) + m_FramePos.y = Ypos_min; + + if( maximized ) + Maximize(); + + aCfg->Read( baseCfgName + entryPerspective, &m_perspective ); + aCfg->Read( baseCfgName + entryMruPath, &m_mruPath ); +} + + +void EDA_BASE_FRAME::SaveSettings( wxConfigBase* aCfg ) +{ + wxString text; + + if( IsIconized() ) + return; + + wxString baseCfgName = ConfigBaseName(); + + m_FrameSize = GetSize(); + m_FramePos = GetPosition(); + + text = baseCfgName + wxT( "Pos_x" ); + aCfg->Write( text, (long) m_FramePos.x ); + + text = baseCfgName + wxT( "Pos_y" ); + aCfg->Write( text, (long) m_FramePos.y ); + + text = baseCfgName + wxT( "Size_x" ); + aCfg->Write( text, (long) m_FrameSize.x ); + + text = baseCfgName + wxT( "Size_y" ); + aCfg->Write( text, (long) m_FrameSize.y ); + + text = baseCfgName + wxT( "Maximized" ); + aCfg->Write( text, IsMaximized() ); + + if( m_hasAutoSave ) + { + text = baseCfgName + entryAutoSaveInterval; + aCfg->Write( text, m_autoSaveInterval ); + } + + // Once this is fully implemented, wxAuiManager will be used to maintain + // the persistance of the main frame and all it's managed windows and + // all of the legacy frame persistence position code can be removed. + wxString perspective = m_auimgr.SavePerspective(); + + // printf( "perspective(%s): %s\n", + // TO_UTF8( m_FrameName + entryPerspective ), TO_UTF8( perspective ) ); + aCfg->Write( baseCfgName + entryPerspective, perspective ); + aCfg->Write( baseCfgName + entryMruPath, m_mruPath ); +} + + +wxConfigBase* EDA_BASE_FRAME::config() +{ + // KICAD_MANAGER_FRAME overrides this + wxConfigBase* ret = Kiface().KifaceSettings(); + //wxASSERT( ret ); + return ret; +} + + +const SEARCH_STACK& EDA_BASE_FRAME::sys_search() +{ + return Kiface().KifaceSearch(); +} + + +wxString EDA_BASE_FRAME::help_name() +{ + return Kiface().GetHelpFileName(); +} + + +void EDA_BASE_FRAME::PrintMsg( const wxString& text ) +{ + SetStatusText( text ); +} + + +void EDA_BASE_FRAME::UpdateFileHistory( const wxString& FullFileName, + wxFileHistory* aFileHistory ) +{ + wxFileHistory* fileHistory = aFileHistory; + + if( !fileHistory ) + fileHistory = &Kiface().GetFileHistory(); + + fileHistory->AddFileToHistory( FullFileName ); +} + + +wxString EDA_BASE_FRAME::GetFileFromHistory( int cmdId, const wxString& type, + wxFileHistory* aFileHistory ) +{ + wxFileHistory* fileHistory = aFileHistory; + + if( !fileHistory ) + fileHistory = &Kiface().GetFileHistory(); + + int baseId = fileHistory->GetBaseId(); + + wxASSERT( cmdId >= baseId && cmdId < baseId + (int) fileHistory->GetCount() ); + + unsigned i = cmdId - baseId; + + if( i < fileHistory->GetCount() ) + { + wxString fn = fileHistory->GetHistoryFile( i ); + + if( wxFileName::FileExists( fn ) ) + return fn; + else + { + wxString msg = wxString::Format( + wxT( "file '%s' was not found." ), + GetChars( fn ) ); + + wxMessageBox( msg ); + + fileHistory->RemoveFileFromHistory( i ); + } + } + + return wxEmptyString; +} + + +void EDA_BASE_FRAME::GetKicadHelp( wxCommandEvent& event ) +{ + const SEARCH_STACK& search = sys_search(); + + /* We have to get document for beginners, + * or the full specific doc + * if event id is wxID_INDEX, we want the document for beginners. + * else the specific doc file (its name is in Kiface().GetHelpFileName()) + * The document for beginners is the same for all KiCad utilities + */ + if( event.GetId() == wxID_INDEX ) + { + // List of possible names for Getting Started in KiCad + const wxChar* names[2] = { + wxT( "getting_started_in_kicad" ), + wxT( "Getting_Started_in_KiCad" ) + }; + + wxString helpFile; + // Search for "getting_started_in_kicad.html" or "getting_started_in_kicad.pdf" + // or "Getting_Started_in_KiCad.html" or "Getting_Started_in_KiCad.pdf" + for( unsigned ii = 0; ii < DIM( names ); ii++ ) + { + helpFile = SearchHelpFileFullPath( search, names[ii] ); + + if( !helpFile.IsEmpty() ) + break; + } + + if( !helpFile ) + { + wxString msg = wxString::Format( _( + "Html or pdf help file \n'%s'\n or\n'%s' could not be found." ), names[0], names[1] ); + wxMessageBox( msg ); + } + else + { + GetAssociatedDocument( this, helpFile ); + } + + return; + } + + wxString base_name = help_name(); + wxString helpFile = SearchHelpFileFullPath( search, base_name ); + + if( !helpFile ) + { + wxString msg = wxString::Format( _( + "Help file '%s' could not be found." ), + GetChars( base_name ) + ); + wxMessageBox( msg ); + } + else + { + GetAssociatedDocument( this, helpFile ); + } +} + + +void EDA_BASE_FRAME::OnSelectPreferredEditor( wxCommandEvent& event ) +{ + // Ask for the current editor and instruct GetEditorName() to not show + // unless we pass false as argument. + wxString editorname = Pgm().GetEditorName( false ); + + // Ask the user to select a new editor, but suggest the current one as the default. + editorname = Pgm().AskUserForPreferredEditor( editorname ); + + // If we have a new editor name request it to be copied to m_editor_name and saved + // to the preferences file. If the user cancelled the dialog then the previous + // value will be retained. + if( !editorname.IsEmpty() ) + Pgm().SetEditorName( editorname ); +} + + +void EDA_BASE_FRAME::GetKicadAbout( wxCommandEvent& event ) +{ + bool ShowAboutDialog(wxWindow * parent); + ShowAboutDialog( this ); +} + + +void EDA_BASE_FRAME::AddHelpVersionInfoMenuEntry( wxMenu* aMenu ) +{ + wxASSERT( aMenu != NULL ); + + // Copy version string to clipboard for bug report purposes. + AddMenuItem( aMenu, ID_HELP_COPY_VERSION_STRING, + _( "Copy &Version Information" ), + _( "Copy the version string to clipboard to send with bug reports" ), + KiBitmap( copy_button_xpm ) ); +} + + +// This is an enhanced version of the compiler build macro provided by wxWidgets +// in <wx/build.h>. Please do not make any of these strings translatable. They +// are used for conveying troubleshooting information to developers. + +#if defined(__GXX_ABI_VERSION) + #define __ABI_VERSION ",compiler with C++ ABI " __WX_BO_STRINGIZE(__GXX_ABI_VERSION) +#else + #define __ABI_VERSION ",compiler without C++ ABI " +#endif + +#if defined(__INTEL_COMPILER) + #define __BO_COMPILER ",Intel C++" +#elif defined(__GNUG__) + #define __BO_COMPILER ",GCC " \ + __WX_BO_STRINGIZE(__GNUC__) "." \ + __WX_BO_STRINGIZE(__GNUC_MINOR__) "." \ + __WX_BO_STRINGIZE(__GNUC_PATCHLEVEL__) +#elif defined(__VISUALC__) + #define __BO_COMPILER ",Visual C++" +#elif defined(__BORLANDC__) + #define __BO_COMPILER ",Borland C++" +#elif defined(__DIGITALMARS__) + #define __BO_COMPILER ",DigitalMars" +#elif defined(__WATCOMC__) + #define __BO_COMPILER ",Watcom C++" +#else + #define __BO_COMPILER ",unknown" +#endif + + +static inline const char* KICAD_BUILD_OPTIONS_SIGNATURE() +{ + return +#ifdef __WXDEBUG__ + " (debug," +#else + " (release," +#endif + __WX_BO_UNICODE __ABI_VERSION __BO_COMPILER __WX_BO_STL + __WX_BO_WXWIN_COMPAT_2_8 ")" + ; +} + + +void EDA_BASE_FRAME::CopyVersionInfoToClipboard( wxCommandEvent& event ) +{ + if( !wxTheClipboard->Open() ) + { + wxMessageBox( _( "Could not open clipboard to write version information." ), + _( "Clipboard Error" ), wxOK | wxICON_EXCLAMATION, this ); + return; + } + + wxString msg_version; + wxPlatformInfo info; + + msg_version = wxT( "Application: " ) + Pgm().App().GetAppName() + wxT( "\n" ); + msg_version << wxT( "Version: " ) << GetBuildVersion() +#ifdef DEBUG + << wxT( " debug" ) +#else + << wxT( " release" ) +#endif + << wxT( " build\n" ); + msg_version << wxT( "wxWidgets: Version " ) << FROM_UTF8( wxVERSION_NUM_DOT_STRING ) + << FROM_UTF8( KICAD_BUILD_OPTIONS_SIGNATURE() ) << wxT( "\n" ) + << wxT( "Platform: " ) << wxGetOsDescription() << wxT( ", " ) + << info.GetArchName() << wxT( ", " ) << info.GetEndiannessName() + << wxT( ", " ) << info.GetPortIdName() << wxT( "\n" ); + + // Just in case someone builds KiCad with the platform native of Boost instead of + // the version included with the KiCad source. + msg_version << wxT( "Boost version: " ) << ( BOOST_VERSION / 100000 ) << wxT( "." ) + << ( BOOST_VERSION / 100 % 1000 ) << wxT( "." ) + << ( BOOST_VERSION % 100 ) << wxT( "\n" ); + +#ifdef BUILD_GITHUB_PLUGIN + // Shows the Curl library version in use: + msg_version << "Curl version: " << KICAD_CURL::GetVersion() << "\n"; +#endif + + msg_version << wxT( " USE_WX_GRAPHICS_CONTEXT=" ); +#ifdef USE_WX_GRAPHICS_CONTEXT + msg_version << wxT( "ON\n" ); +#else + msg_version << wxT( "OFF\n" ); +#endif + + msg_version << wxT( " USE_WX_OVERLAY=" ); +#ifdef USE_WX_OVERLAY + msg_version << wxT( "ON\n" ); +#else + msg_version << wxT( "OFF\n" ); +#endif + + msg_version << wxT( " KICAD_SCRIPTING=" ); +#ifdef KICAD_SCRIPTING + msg_version << wxT( "ON\n" ); +#else + msg_version << wxT( "OFF\n" ); +#endif + + msg_version << wxT( " KICAD_SCRIPTING_MODULES=" ); +#ifdef KICAD_SCRIPTING_MODULES + msg_version << wxT( "ON\n" ); +#else + msg_version << wxT( "OFF\n" ); +#endif + + msg_version << wxT( " KICAD_SCRIPTING_WXPYTHON=" ); +#ifdef KICAD_SCRIPTING_WXPYTHON + msg_version << wxT( "ON\n" ); +#else + msg_version << wxT( "OFF\n" ); +#endif + + msg_version << wxT( " USE_FP_LIB_TABLE=HARD_CODED_ON\n" ); + + msg_version << wxT( " BUILD_GITHUB_PLUGIN=" ); +#ifdef BUILD_GITHUB_PLUGIN + msg_version << wxT( "ON\n" ); +#else + msg_version << wxT( "OFF\n" ); +#endif + + wxTheClipboard->SetData( new wxTextDataObject( msg_version ) ); + wxTheClipboard->Close(); + + wxMessageBox( msg_version, _( "Version Information (copied to the clipboard)" ) ); +} + + +bool EDA_BASE_FRAME::IsWritable( const wxFileName& aFileName ) +{ + wxString msg; + wxFileName fn = aFileName; + + // Check for absence of a file path with a file name. Unfortunately KiCad + // uses paths relative to the current project path without the ./ part which + // confuses wxFileName. Making the file name path absolute may be less than + // elegant but it solves the problem. + if( fn.GetPath().IsEmpty() && fn.HasName() ) + fn.MakeAbsolute(); + + wxCHECK_MSG( fn.IsOk(), false, + wxT( "File name object is invalid. Bad programmer!" ) ); + wxCHECK_MSG( !fn.GetPath().IsEmpty(), false, + wxT( "File name object path <" ) + fn.GetFullPath() + + wxT( "> is not set. Bad programmer!" ) ); + + if( fn.IsDir() && !fn.IsDirWritable() ) + { + msg.Printf( _( "You do not have write permissions to folder <%s>." ), + GetChars( fn.GetPath() ) ); + } + else if( !fn.FileExists() && !fn.IsDirWritable() ) + { + msg.Printf( _( "You do not have write permissions to save file <%s> to folder <%s>." ), + GetChars( fn.GetFullName() ), GetChars( fn.GetPath() ) ); + } + else if( fn.FileExists() && !fn.IsFileWritable() ) + { + msg.Printf( _( "You do not have write permissions to save file <%s>." ), + GetChars( fn.GetFullPath() ) ); + } + + if( !msg.IsEmpty() ) + { + wxMessageBox( msg ); + return false; + } + + return true; +} + + +void EDA_BASE_FRAME::CheckForAutoSaveFile( const wxFileName& aFileName, + const wxString& aBackupFileExtension ) +{ + wxCHECK_RET( aFileName.IsOk(), wxT( "Invalid file name!" ) ); + wxCHECK_RET( !aBackupFileExtension.IsEmpty(), wxT( "Invalid backup file extension!" ) ); + + wxFileName autoSaveFileName = aFileName; + + // Check for auto save file. + autoSaveFileName.SetName( AUTOSAVE_PREFIX_FILENAME + aFileName.GetName() ); + + wxLogTrace( traceAutoSave, + wxT( "Checking for auto save file " ) + autoSaveFileName.GetFullPath() ); + + if( !autoSaveFileName.FileExists() ) + return; + + wxString msg = wxString::Format( _( + "Well this is potentially embarrassing!\n" + "It appears that the last time you were editing the file\n" + "'%s'\n" + "it was not saved properly. Do you wish to restore the last saved edits you made?" ), + GetChars( aFileName.GetFullName() ) + ); + + int response = wxMessageBox( msg, Pgm().App().GetAppName(), wxYES_NO | wxICON_QUESTION, this ); + + // Make a backup of the current file, delete the file, and rename the auto save file to + // the file name. + if( response == wxYES ) + { + // Get the backup file name. + wxFileName backupFileName = aFileName; + backupFileName.SetExt( aBackupFileExtension ); + + // If an old backup file exists, delete it. If an old copy of the file exists, rename + // it to the backup file name + if( aFileName.FileExists() ) + { + // Remove the old file backup file. + if( backupFileName.FileExists() ) + wxRemoveFile( backupFileName.GetFullPath() ); + + // Rename the old file to the backup file name. + if( !wxRenameFile( aFileName.GetFullPath(), backupFileName.GetFullPath() ) ) + { + msg.Printf( _( "Could not create backup file <%s>" ), + GetChars( backupFileName.GetFullPath() ) ); + wxMessageBox( msg ); + } + } + + if( !wxRenameFile( autoSaveFileName.GetFullPath(), aFileName.GetFullPath() ) ) + { + wxMessageBox( _( "The auto save file could not be renamed to the board file name." ), + Pgm().App().GetAppName(), wxOK | wxICON_EXCLAMATION, this ); + } + } + else + { + wxLogTrace( traceAutoSave, + wxT( "Removing auto save file " ) + autoSaveFileName.GetFullPath() ); + + // Remove the auto save file when using the previous file as is. + wxRemoveFile( autoSaveFileName.GetFullPath() ); + } +} + diff --git a/common/bezier_curves.cpp b/common/bezier_curves.cpp new file mode 100644 index 0000000..9dc67b6 --- /dev/null +++ b/common/bezier_curves.cpp @@ -0,0 +1,435 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.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 + */ + +/************************************/ +/* routines to handle bezier curves */ +/************************************/ + +#include <fctsys.h> +#include <bezier_curves.h> + + +#define add_segment(segment) if(s_bezier_Points_Buffer[s_bezier_Points_Buffer.size()-1] != segment) s_bezier_Points_Buffer.push_back(segment); + + +// Local variables: +static std::vector<wxPoint> s_bezier_Points_Buffer; + +static int bezier_recursion_limit = 12; +static double bezier_approximation_scale = 0.5; // 1 + +static double bezier_curve_collinearity_epsilon = 1e-30; +static double bezier_curve_angle_tolerance_epsilon = 0.0001; +static double bezier_distance_tolerance_square; // derived by approximation_scale +static double bezier_angle_tolerance = 0.0; +static double bezier_cusp_limit = 0.0; + +// Local functions: +static void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int level ); +static void recursive_bezier( int x1, + int y1, + int x2, + int y2, + int x3, + int y3, + int x4, + int y4, + int level ); + +/***********************************************************************************/ + + +std::vector<wxPoint> Bezier2Poly( wxPoint c1, wxPoint c2, wxPoint c3, wxPoint c4 ) +{ + return Bezier2Poly( c1.x, c1.y, c2.x, c2.y, c3.x, c3.y, c4.x, c4.y ); +} + + +std::vector<wxPoint> Bezier2Poly( wxPoint c1, wxPoint c2, wxPoint c3 ) +{ + return Bezier2Poly( c1.x, c1.y, c2.x, c2.y, c3.x, c3.y ); +} + + +inline double calc_sq_distance( int x1, int y1, int x2, int y2 ) +{ + int dx = x2 - x1; + int dy = y2 - y1; + + return (double)dx * dx + (double)dy * dy; +} + +inline double sqrt_len( int dx, int dy ) +{ + return ((double)dx * dx) + ((double)dy * dy); +} + + +std::vector<wxPoint> Bezier2Poly( int x1, int y1, int x2, int y2, int x3, int y3 ) +{ + s_bezier_Points_Buffer.clear(); + + bezier_distance_tolerance_square = 0.5 / bezier_approximation_scale; + bezier_distance_tolerance_square *= bezier_distance_tolerance_square; + s_bezier_Points_Buffer.push_back( wxPoint( x1, y1 ) ); + recursive_bezier( x1, y1, x2, y2, x3, y3, 0 ); + s_bezier_Points_Buffer.push_back( wxPoint( x3, y3 ) ); + + wxLogDebug( wxT( "Bezier Conversion - End (%d vertex)" ), s_bezier_Points_Buffer.size() ); + return s_bezier_Points_Buffer; +} + + +std::vector<wxPoint> Bezier2Poly( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 ) +{ + s_bezier_Points_Buffer.clear(); + bezier_distance_tolerance_square = 0.5 / bezier_approximation_scale; + bezier_distance_tolerance_square *= bezier_distance_tolerance_square; + + s_bezier_Points_Buffer.push_back( wxPoint( x1, y1 ) ); + recursive_bezier( x1, y1, x2, y2, x3, y3, x4, y4, 0 ); + s_bezier_Points_Buffer.push_back( wxPoint( x4, y4 ) ); + wxLogDebug( wxT( "Bezier Conversion - End (%d vertex)" ), s_bezier_Points_Buffer.size() ); + return s_bezier_Points_Buffer; +} + + +void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int level ) +{ + if( abs( level ) > bezier_recursion_limit ) + { + return; + } + + // Calculate all the mid-points of the line segments + //---------------------- + int x12 = (x1 + x2) / 2; + int y12 = (y1 + y2) / 2; + int x23 = (x2 + x3) / 2; + int y23 = (y2 + y3) / 2; + int x123 = (x12 + x23) / 2; + int y123 = (y12 + y23) / 2; + + int dx = x3 - x1; + int dy = y3 - y1; + double d = fabs( ((double) (x2 - x3) * dy) - ((double) (y2 - y3) * dx ) ); + double da; + + if( d > bezier_curve_collinearity_epsilon ) + { + // Regular case + //----------------- + if( d * d <= bezier_distance_tolerance_square * (dx * dx + dy * dy) ) + { + // If the curvature doesn't exceed the distance_tolerance value + // we tend to finish subdivisions. + //---------------------- + if( bezier_angle_tolerance < bezier_curve_angle_tolerance_epsilon ) + { + add_segment( wxPoint( x123, y123 ) ); + return; + } + + // Angle & Cusp Condition + //---------------------- + da = fabs( atan2( (double) ( y3 - y2 ), (double) ( x3 - x2 ) ) - + atan2( (double) ( y2 - y1 ), (double) ( x2 - x1 ) ) ); + if( da >=M_PI ) + da = 2 * M_PI - da; + + if( da < bezier_angle_tolerance ) + { + // Finally we can stop the recursion + //---------------------- + add_segment( wxPoint( x123, y123 ) ); + return; + } + } + } + else + { + // Collinear case + //------------------ + da = sqrt_len(dx, dy); + if( da == 0 ) + { + d = calc_sq_distance( x1, y1, x2, y2 ); + } + else + { + d = ( (double)(x2 - x1) * dx + (double)(y2 - y1) * dy ) / da; + if( d > 0 && d < 1 ) + { + // Simple collinear case, 1---2---3 + // We can leave just two endpoints + return; + } + if( d <= 0 ) + d = calc_sq_distance( x2, y2, x1, y1 ); + else if( d >= 1 ) + d = calc_sq_distance( x2, y2, x3, y3 ); + else + d = calc_sq_distance( x2, y2, x1 + (int) d * dx, + y1 + (int) d * dy ); + } + if( d < bezier_distance_tolerance_square ) + { + add_segment( wxPoint( x2, y2 ) ); + return; + } + } + + // Continue subdivision + //---------------------- + recursive_bezier( x1, y1, x12, y12, x123, y123, level + 1 ); + recursive_bezier( x123, y123, x23, y23, x3, y3, -(level + 1) ); +} + + +void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int level ) +{ + if( abs( level ) > bezier_recursion_limit ) + { + return; + } + + // Calculate all the mid-points of the line segments + //---------------------- + int x12 = (x1 + x2) / 2; + int y12 = (y1 + y2) / 2; + int x23 = (x2 + x3) / 2; + int y23 = (y2 + y3) / 2; + int x34 = (x3 + x4) / 2; + int y34 = (y3 + y4) / 2; + int x123 = (x12 + x23) / 2; + int y123 = (y12 + y23) / 2; + int x234 = (x23 + x34) / 2; + int y234 = (y23 + y34) / 2; + int x1234 = (x123 + x234) / 2; + int y1234 = (y123 + y234) / 2; + + + // Try to approximate the full cubic curve by a single straight line + //------------------ + int dx = x4 - x1; + int dy = y4 - y1; + + double d2 = fabs( (double) ( (x2 - x4) * dy - (y2 - y4) * dx ) ); + double d3 = fabs( (double) ( (x3 - x4) * dy - (y3 - y4) * dx ) ); + double da1, da2, k; + + switch( (int(d2 > bezier_curve_collinearity_epsilon) << 1) + + int(d3 > bezier_curve_collinearity_epsilon) ) + { + case 0: + + // All collinear OR p1==p4 + //---------------------- + k = dx * dx + dy * dy; + if( k == 0 ) + { + d2 = calc_sq_distance( x1, y1, x2, y2 ); + d3 = calc_sq_distance( x4, y4, x3, y3 ); + } + else + { + k = 1 / k; + da1 = x2 - x1; + da2 = y2 - y1; + d2 = k * (da1 * dx + da2 * dy); + da1 = x3 - x1; + da2 = y3 - y1; + d3 = k * (da1 * dx + da2 * dy); + if( d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1 ) + { + // Simple collinear case, 1---2---3---4 + // We can leave just two endpoints + return; + } + if( d2 <= 0 ) + d2 = calc_sq_distance( x2, y2, x1, y1 ); + else if( d2 >= 1 ) + d2 = calc_sq_distance( x2, y2, x4, y4 ); + else + d2 = calc_sq_distance( x2, y2, x1 + (int) d2 * dx, + y1 + (int) d2 * dy ); + + if( d3 <= 0 ) + d3 = calc_sq_distance( x3, y3, x1, y1 ); + else if( d3 >= 1 ) + d3 = calc_sq_distance( x3, y3, x4, y4 ); + else + d3 = calc_sq_distance( x3, y3, x1 + (int) d3 * dx, + y1 + (int) d3 * dy ); + } + if( d2 > d3 ) + { + if( d2 < bezier_distance_tolerance_square ) + { + add_segment( wxPoint( x2, y2 ) ); + return; + } + } + else + { + if( d3 < bezier_distance_tolerance_square ) + { + add_segment( wxPoint( x3, y3 ) ); + return; + } + } + break; + + case 1: + + // p1,p2,p4 are collinear, p3 is significant + //---------------------- + if( d3 * d3 <= bezier_distance_tolerance_square * sqrt_len(dx, dy) ) + { + if( bezier_angle_tolerance < bezier_curve_angle_tolerance_epsilon ) + { + add_segment( wxPoint( x23, y23 ) ); + return; + } + + // Angle Condition + //---------------------- + da1 = fabs( atan2( (double) ( y4 - y3 ), (double) ( x4 - x3 ) ) - + atan2( (double) ( y3 - y2 ), (double) ( x3 - x2 ) ) ); + if( da1 >= M_PI ) + da1 = 2 * M_PI - da1; + + if( da1 < bezier_angle_tolerance ) + { + add_segment( wxPoint( x2, y2 ) ); + add_segment( wxPoint( x3, y3 ) ); + return; + } + + if( bezier_cusp_limit != 0.0 ) + { + if( da1 > bezier_cusp_limit ) + { + add_segment( wxPoint( x3, y3 ) ); + return; + } + } + } + break; + + case 2: + + // p1,p3,p4 are collinear, p2 is significant + //---------------------- + if( d2 * d2 <= bezier_distance_tolerance_square * sqrt_len(dx, dy) ) + { + if( bezier_angle_tolerance < bezier_curve_angle_tolerance_epsilon ) + { + add_segment( wxPoint( x23, y23 ) ); + return; + } + + // Angle Condition + //---------------------- + da1 = fabs( atan2( (double) ( y3 - y2 ), (double) ( x3 - x2 ) ) - + atan2( (double) ( y2 - y1 ), (double) ( x2 - x1 ) ) ); + if( da1 >= M_PI ) + da1 = 2 * M_PI - da1; + + if( da1 < bezier_angle_tolerance ) + { + add_segment( wxPoint( x2, y2 ) ); + add_segment( wxPoint( x3, y3 ) ); + return; + } + + if( bezier_cusp_limit != 0.0 ) + { + if( da1 > bezier_cusp_limit ) + { + add_segment( wxPoint( x2, y2 ) ); + return; + } + } + } + break; + + case 3: + + // Regular case + //----------------- + if( (d2 + d3) * (d2 + d3) <= bezier_distance_tolerance_square * sqrt_len(dx, dy) ) + { + // If the curvature doesn't exceed the distance_tolerance value + // we tend to finish subdivisions. + //---------------------- + if( bezier_angle_tolerance < bezier_curve_angle_tolerance_epsilon ) + { + add_segment( wxPoint( x23, y23 ) ); + return; + } + + // Angle & Cusp Condition + //---------------------- + k = atan2( (double) ( y3 - y2 ), (double) ( x3 - x2 ) ); + da1 = fabs( k - atan2( (double) ( y2 - y1 ), + (double) ( x2 - x1 ) ) ); + da2 = fabs( atan2( (double) ( y4 - y3 ), + (double) ( x4 - x3 ) ) - k ); + if( da1 >= M_PI ) + da1 = 2 * M_PI - da1; + if( da2 >= M_PI ) + da2 = 2 * M_PI - da2; + + if( da1 + da2 < bezier_angle_tolerance ) + { + // Finally we can stop the recursion + //---------------------- + add_segment( wxPoint( x23, y23 ) ); + return; + } + + if( bezier_cusp_limit != 0.0 ) + { + if( da1 > bezier_cusp_limit ) + { + add_segment( wxPoint( x2, y2 ) ); + return; + } + + if( da2 > bezier_cusp_limit ) + { + add_segment( wxPoint( x3, y3 ) ); + return; + } + } + } + break; + } + + // Continue subdivision + //---------------------- + recursive_bezier( x1, y1, x12, y12, x123, y123, x1234, y1234, level + 1 ); + recursive_bezier( x1234, y1234, x234, y234, x34, y34, x4, y4, level + 1 ); +} diff --git a/common/bin_mod.cpp b/common/bin_mod.cpp new file mode 100644 index 0000000..af3370d --- /dev/null +++ b/common/bin_mod.cpp @@ -0,0 +1,72 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 CERN + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.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 <bin_mod.h> +#include <online_help.h> +#include <common.h> + + +BIN_MOD::BIN_MOD( const char* aName ) : + m_name( aName ), + m_config( 0 ) +{ +} + + +void BIN_MOD::Init() +{ + // do an OS specific wxConfig instantiation, using the bin_mod (EXE/DLL/DSO) name. + m_config = GetNewConfig( wxString::FromUTF8( m_name ) ); + + m_history.Load( *m_config ); + + // Prepare On Line Help. Use only lower case for help file names, in order to + // avoid problems with upper/lower case file names under windows and unix. + // Help files are now using html format. + // Old help files used pdf format. + // so when searching a help file, the .html file will be searched, + // and if not found, the .pdf file will be searched. + m_help_file = wxString::FromUTF8( m_name ); // no ext given. can be .html or .pdf +} + + +void BIN_MOD::End() +{ + if( m_config ) + { + m_history.Save( *m_config ); + + // Deleting a wxConfigBase writes its contents to disk if changed. + // Might be NULL if called twice, in which case nothing happens. + delete m_config; + m_config = 0; + } +} + + +BIN_MOD::~BIN_MOD() +{ + End(); +} + diff --git a/common/bitmap.cpp b/common/bitmap.cpp new file mode 100644 index 0000000..25cb8ad --- /dev/null +++ b/common/bitmap.cpp @@ -0,0 +1,45 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2011 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2011 KiCad Developers, see change_log.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 <wx/image.h> +#include <wx/bitmap.h> +#include <wx/mstream.h> + +#include <bitmaps.h> + + +wxBitmap KiBitmap( BITMAP_DEF aBitmap ) +{ + wxMemoryInputStream is( aBitmap->png, aBitmap->byteCount ); + + return wxBitmap( wxImage( is, wxBITMAP_TYPE_PNG, -1 ), -1 ); +} + +wxBitmap* KiBitmapNew( BITMAP_DEF aBitmap ) +{ + wxMemoryInputStream is( aBitmap->png, aBitmap->byteCount ); + + return new wxBitmap( wxImage( is, wxBITMAP_TYPE_PNG, -1 ), -1 ); +} diff --git a/common/block_commande.cpp b/common/block_commande.cpp new file mode 100644 index 0000000..e313eb4 --- /dev/null +++ b/common/block_commande.cpp @@ -0,0 +1,233 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com + * Copyright (C) 2004-2011 KiCad Developers, see change_log.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 + */ + +/** + * @file block_commande.cpp + * @brief Common routines for managing on block commands. + */ + +#include <fctsys.h> +#include <gr_basic.h> +#include <draw_frame.h> +#include <common.h> +#include <macros.h> +#include <base_struct.h> +#include <class_base_screen.h> +#include <class_drawpanel.h> +#include <confirm.h> +#include <block_commande.h> + + +BLOCK_SELECTOR::BLOCK_SELECTOR() : + EDA_RECT() +{ + m_state = STATE_NO_BLOCK; // State (enum BLOCK_STATE_T) of block. + m_command = BLOCK_IDLE; // Type (enum BLOCK_COMMAND_T) of operation. + m_color = BROWN; +} + + +BLOCK_SELECTOR::~BLOCK_SELECTOR() +{ +} + + +void BLOCK_SELECTOR::SetMessageBlock( EDA_DRAW_FRAME* frame ) +{ + wxString msg; + + switch( m_command ) + { + case BLOCK_IDLE: + break; + + case BLOCK_MOVE: // Move + case BLOCK_PRESELECT_MOVE: // Move with preselection list + case BLOCK_MOVE_EXACT: + msg = _( "Block Move" ); + break; + + case BLOCK_DRAG: // Drag + msg = _( "Block Drag" ); + break; + + case BLOCK_DRAG_ITEM: // Drag + msg = _( "Drag item" ); + break; + + case BLOCK_COPY: // Copy + msg = _( "Block Copy" ); + break; + + case BLOCK_DELETE: // Delete + msg = _( "Block Delete" ); + break; + + case BLOCK_SAVE: // Save + msg = _( "Block Save" ); + break; + + case BLOCK_PASTE: + msg = _( "Block Paste" ); + break; + + case BLOCK_ZOOM: // Window Zoom + msg = _( "Win Zoom" ); + break; + + case BLOCK_ROTATE: // Rotate 90 deg + msg = _( "Block Rotate" ); + break; + + case BLOCK_FLIP: // Flip + msg = _( "Block Flip" ); + break; + + case BLOCK_MIRROR_X: + case BLOCK_MIRROR_Y: // mirror + msg = _( "Block Mirror" ); + break; + + case BLOCK_ABORT: + break; + + default: + msg = wxT( "???" ); + break; + } + + frame->DisplayToolMsg( msg ); +} + + +void BLOCK_SELECTOR::Draw( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aOffset, + GR_DRAWMODE aDrawMode, EDA_COLOR_T aColor ) +{ + if( !aDC ) + return; + + int w = GetWidth(); + int h = GetHeight(); + + GRSetDrawMode( aDC, aDrawMode ); + + if( w == 0 || h == 0 ) + GRLine( aPanel->GetClipBox(), aDC, GetX() + aOffset.x, GetY() + aOffset.y, + GetRight() + aOffset.x, GetBottom() + aOffset.y, 0, aColor ); + else + GRRect( aPanel->GetClipBox(), aDC, GetX() + aOffset.x, GetY() + aOffset.y, + GetRight() + aOffset.x, GetBottom() + aOffset.y, 0, aColor ); +} + + +void BLOCK_SELECTOR::InitData( EDA_DRAW_PANEL* aPanel, const wxPoint& startpos ) +{ + m_state = STATE_BLOCK_INIT; + SetOrigin( startpos ); + SetSize( wxSize( 0, 0 ) ); + m_items.ClearItemsList(); + aPanel->SetMouseCapture( DrawAndSizingBlockOutlines, AbortBlockCurrentCommand ); +} + + +void BLOCK_SELECTOR::ClearItemsList() +{ + m_items.ClearItemsList(); +} + + +void BLOCK_SELECTOR::ClearListAndDeleteItems() +{ + m_items.ClearListAndDeleteItems(); +} + + +void BLOCK_SELECTOR::PushItem( ITEM_PICKER& aItem ) +{ + m_items.PushItem( aItem ); +} + + +void BLOCK_SELECTOR::Clear() +{ + if( m_command != BLOCK_IDLE ) + { + m_command = BLOCK_IDLE; + m_state = STATE_NO_BLOCK; + ClearItemsList(); + } +} + + +void DrawAndSizingBlockOutlines( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aPosition, + bool aErase ) +{ + BLOCK_SELECTOR* block; + + block = &aPanel->GetScreen()->m_BlockLocate; + + block->SetMoveVector( wxPoint( 0, 0 ) ); + + if( aErase && aDC ) + block->Draw( aPanel, aDC, wxPoint( 0, 0 ), g_XorMode, block->GetColor() ); + + block->SetLastCursorPosition( aPanel->GetParent()->GetCrossHairPosition() ); + block->SetEnd( aPanel->GetParent()->GetCrossHairPosition() ); + + if( aDC ) + block->Draw( aPanel, aDC, wxPoint( 0, 0 ), g_XorMode, block->GetColor() ); + + if( block->GetState() == STATE_BLOCK_INIT ) + { + if( block->GetWidth() || block->GetHeight() ) + // 2nd point exists: the rectangle is not surface anywhere + block->SetState( STATE_BLOCK_END ); + } +} + + +void AbortBlockCurrentCommand( EDA_DRAW_PANEL* aPanel, wxDC* aDC ) +{ + BASE_SCREEN* screen = aPanel->GetScreen(); + + if( aPanel->IsMouseCaptured() ) // Erase current drawing on screen + { + // Clear block outline. + aPanel->CallMouseCapture( aDC, wxDefaultPosition, false ); + aPanel->SetMouseCapture( NULL, NULL ); + screen->SetCurItem( NULL ); + + // Delete the picked wrapper if this is a picked list. + if( screen->m_BlockLocate.GetCommand() != BLOCK_PASTE ) + screen->m_BlockLocate.ClearItemsList(); + } + + screen->m_BlockLocate.SetState( STATE_NO_BLOCK ); + screen->m_BlockLocate.SetCommand( BLOCK_ABORT ); + aPanel->GetParent()->HandleBlockEnd( aDC ); + + screen->m_BlockLocate.SetCommand( BLOCK_IDLE ); + aPanel->GetParent()->DisplayToolMsg( wxEmptyString ); + aPanel->SetCursor( (wxStockCursor) aPanel->GetCurrentCursor() ); +} diff --git a/common/build_version.cpp b/common/build_version.cpp new file mode 100644 index 0000000..bb30861 --- /dev/null +++ b/common/build_version.cpp @@ -0,0 +1,45 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2015-2016 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 + */ + +// Date for KiCad build version +#include <fctsys.h> + +// The include file version.h is always created even if the repo version cannot be +// determined. In this case KICAD_BUILD_VERSION will default to "no-bzr". +#include <kicad_build_version.h> + + +/** + * Function GetBuildVersion + * Return the build version string. + */ +wxString GetBuildVersion() +{ + wxString msg = wxString::Format( + wxT( "%s" ), + wxT( KICAD_VERSION_FULL ) + ); + + return msg; +} diff --git a/common/class_bitmap_base.cpp b/common/class_bitmap_base.cpp new file mode 100644 index 0000000..9c118a1 --- /dev/null +++ b/common/class_bitmap_base.cpp @@ -0,0 +1,300 @@ +/** + * @file class_bitmap_base.cpp + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2011 jean-pierre.charras + * Copyright (C) 2011-2016 KiCad Developers, see change_log.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 "fctsys.h" +#include "gr_basic.h" +#include "macros.h" +#include "class_drawpanel.h" +#include "trigo.h" +#include "common.h" +#include "richio.h" +#include "plot_common.h" + +#include "class_bitmap_base.h" + +#include <wx/mstream.h> + + +/**********************/ +/* class BITMAP_BASE */ +/**********************/ + +BITMAP_BASE::BITMAP_BASE( const wxPoint& pos ) +{ + m_Scale = 1.0; // 1.0 = original bitmap size + m_bitmap = NULL; + m_image = NULL; + m_ppi = 300; // the bitmap definition. the default is 300PPI + m_pixelScaleFactor = 1000.0 / m_ppi; // a value OK for bitmaps using 300 PPI + // for Eeschema which uses currently 1000PPI +} + + +BITMAP_BASE::BITMAP_BASE( const BITMAP_BASE& aSchBitmap ) +{ + m_Scale = aSchBitmap.m_Scale; + m_ppi = aSchBitmap.m_ppi; + m_pixelScaleFactor = aSchBitmap.m_pixelScaleFactor; + m_image = new wxImage( *aSchBitmap.m_image ); + m_bitmap = new wxBitmap( *m_image ); +} + + +/** + * Function ImportData + * Copy aItem image to me and update m_bitmap + */ +void BITMAP_BASE::ImportData( BITMAP_BASE* aItem ) +{ + *m_image = *aItem->m_image; + *m_bitmap = *aItem->m_bitmap; + m_Scale = aItem->m_Scale; + m_ppi = aItem->m_ppi; + m_pixelScaleFactor = aItem->m_pixelScaleFactor; +} + + +bool BITMAP_BASE::ReadImageFile( const wxString& aFullFilename ) +{ + wxImage* new_image = new wxImage(); + + if( !new_image->LoadFile( aFullFilename ) ) + { + delete new_image; + return false; + } + + delete m_image; + m_image = new_image; + m_bitmap = new wxBitmap( *m_image ); + + return true; +} + + +bool BITMAP_BASE::SaveData( FILE* aFile ) const +{ + if( m_image ) + { + wxMemoryOutputStream stream; + m_image->SaveFile( stream, wxBITMAP_TYPE_PNG ); + + // Write binary data in hexadecimal form (ASCII) + wxStreamBuffer* buffer = stream.GetOutputStreamBuffer(); + char* begin = (char*) buffer->GetBufferStart(); + + for( int ii = 0; begin < buffer->GetBufferEnd(); begin++, ii++ ) + { + if( ii >= 32 ) + { + ii = 0; + + if( fprintf( aFile, "\n" ) == EOF ) + return false; + } + + if( fprintf( aFile, "%2.2X ", *begin & 0xFF ) == EOF ) + return false; + } + } + + return true; +} + + +void BITMAP_BASE::SaveData( wxArrayString& aPngStrings ) const +{ + if( m_image ) + { + wxMemoryOutputStream stream; + m_image->SaveFile( stream, wxBITMAP_TYPE_PNG ); + + // Write binary data in hexadecimal form (ASCII) + wxStreamBuffer* buffer = stream.GetOutputStreamBuffer(); + char* begin = (char*) buffer->GetBufferStart(); + wxString line; + + for( int ii = 0; begin < buffer->GetBufferEnd(); begin++, ii++ ) + { + if( ii >= 32 ) + { + ii = 0; + aPngStrings.Add( line ); + line.Empty(); + } + + line << wxString::Format( wxT( "%2.2X " ), *begin & 0xFF ); + } + + // Add last line: + if( !line.IsEmpty() ) + aPngStrings.Add( line ); + } +} + + +bool BITMAP_BASE::LoadData( LINE_READER& aLine, wxString& aErrorMsg ) +{ + wxMemoryOutputStream stream; + char* line; + + while( true ) + { + if( !aLine.ReadLine() ) + { + aErrorMsg = wxT("Unexpected end of data"); + return false; + } + + line = aLine.Line(); + + if( strnicmp( line, "EndData", 4 ) == 0 ) + { + // all the PNG date is read. + // We expect here m_image and m_bitmap are void + m_image = new wxImage(); + wxMemoryInputStream istream( stream ); + m_image->LoadFile( istream, wxBITMAP_TYPE_PNG ); + m_bitmap = new wxBitmap( *m_image ); + break; + } + + // Read PNG data, stored in hexadecimal, + // each byte = 2 hexadecimal digits and a space between 2 bytes + // and put it in memory stream buffer + int len = strlen( line ); + for( ; len > 0; len -= 3, line += 3 ) + { + int value = 0; + if( sscanf( line, "%X", &value ) == 1 ) + stream.PutC( (char) value ); + else + break; + } + } + + return true; +} + + +const EDA_RECT BITMAP_BASE::GetBoundingBox() const +{ + EDA_RECT rect; + + wxSize size = GetSize(); + + rect.Inflate( size.x / 2, size.y / 2 ); + + return rect; +} + + +void BITMAP_BASE::DrawBitmap( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aPos ) +{ + if( m_bitmap == NULL ) + return; + + wxPoint pos = aPos; + wxSize size = GetSize(); + + // This fixes a bug in OSX that should be fixed in the 3.0.3 version or later. + // See: http://trac.wxwidgets.org/ticket/16329 for more information. + if( ( size.x == 0 ) || ( size.y == 0 ) ) + return; + + // To draw the bitmap, pos is the upper left corner position + pos.x -= size.x / 2; + pos.y -= size.y / 2; + + double scale; + int logicalOriginX, logicalOriginY; + aDC->GetUserScale( &scale, &scale ); + aDC->GetLogicalOrigin( &logicalOriginX, &logicalOriginY ); + aDC->SetUserScale( scale * GetScalingFactor(), scale * GetScalingFactor() ); + aDC->SetLogicalOrigin( logicalOriginX / GetScalingFactor(), + logicalOriginY / GetScalingFactor() ); + aDC->DrawBitmap( *m_bitmap, + KiROUND( pos.x / GetScalingFactor() ), + KiROUND( pos.y / GetScalingFactor() ), + true ); + aDC->SetUserScale( scale, scale ); + aDC->SetLogicalOrigin( logicalOriginX, logicalOriginY ); +} + + +wxSize BITMAP_BASE::GetSize() const +{ + wxSize size; + + if( m_bitmap ) + { + size.x = m_bitmap->GetWidth(); + size.y = m_bitmap->GetHeight(); + + size.x = KiROUND( size.x * GetScalingFactor() ); + size.y = KiROUND( size.y * GetScalingFactor() ); + } + + return size; +} + + +void BITMAP_BASE::Mirror( bool aVertically ) +{ + if( m_image ) + { + *m_image = m_image->Mirror( not aVertically ); + RebuildBitmap(); + } +} + + +void BITMAP_BASE::Rotate( bool aRotateCCW ) +{ + if( m_image ) + { + *m_image = m_image->Rotate90( aRotateCCW ); + RebuildBitmap(); + } +} + + +void BITMAP_BASE::PlotImage( PLOTTER* aPlotter, + const wxPoint& aPos, + EDA_COLOR_T aDefaultColor, + int aDefaultPensize ) +{ + if( m_image == NULL ) + return; + + // These 2 lines are useful only for plotters that cannot plot a bitmap + // and plot a rectangle instead of. + aPlotter->SetColor( aDefaultColor ); + aPlotter->SetCurrentLineWidth( aDefaultPensize ); + aPlotter->PlotImage( *m_image, aPos, GetScalingFactor() ); +} diff --git a/common/class_colors_design_settings.cpp b/common/class_colors_design_settings.cpp new file mode 100644 index 0000000..dbdd6ac --- /dev/null +++ b/common/class_colors_design_settings.cpp @@ -0,0 +1,156 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.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 + */ + +/** + * @file class_colors_design_settings.cpp + * @brief Handle colors used to draw all items or layers. + */ +#include <fctsys.h> +#include <colors.h> +#include <macros.h> + +#include <class_colors_design_settings.h> + +/* Class for handle list of color settings for designs + * in Eeschema, Pcbnew and GerbView + */ + +/* Initial colors values: optimized for Pcbnew 64 layers. + * The table is not actually used by Eeschema. + * these values are superseded by config reading + */ +static const EDA_COLOR_T default_layer_color[] = { + RED, YELLOW, LIGHTMAGENTA, LIGHTRED, + CYAN, GREEN, BLUE, DARKGRAY, + MAGENTA, LIGHTGRAY, MAGENTA, RED, + BROWN, LIGHTGRAY, BLUE, GREEN, + + RED, YELLOW, LIGHTMAGENTA, LIGHTRED, + CYAN, GREEN, BLUE, DARKGRAY, + MAGENTA, LIGHTGRAY, MAGENTA, RED, + BROWN, LIGHTGRAY, BLUE, GREEN, + + BLUE, MAGENTA, + LIGHTCYAN, RED, + MAGENTA, CYAN, + BROWN, MAGENTA, + LIGHTGRAY, + BLUE, + GREEN, YELLOW, + YELLOW, + LIGHTMAGENTA, + YELLOW, + DARKGRAY +}; + + +static const EDA_COLOR_T default_items_color[] = { + LIGHTGRAY, // unused + CYAN, // VIA_MICROVIA_VISIBLE + BROWN, // VIA_BBLIND_VISIBLE + LIGHTGRAY, // VIA_THROUGH_VISIBLE + YELLOW, // NON_PLATED_VISIBLE + LIGHTGRAY, // MOD_TEXT_FR_VISIBLE + BLUE, // MOD_TEXT_BK_VISIBLE + DARKGRAY, // MOD_TEXT_INVISIBLE + BLUE, // ANCHOR_VISIBLE + RED, // PAD_FR_VISIBLE + GREEN, // PAD_BK_VISIBLE + LIGHTGRAY, // RATSNEST_VISIBLE + DARKGRAY, // GRID_VISIBLE + LIGHTRED, LIGHTGRAY, LIGHTGRAY, LIGHTGRAY, + LIGHTGRAY, LIGHTGRAY, LIGHTGRAY, LIGHTGRAY, + LIGHTGRAY, LIGHTGRAY, LIGHTGRAY, LIGHTGRAY, + LIGHTGRAY, LIGHTGRAY, LIGHTGRAY, LIGHTGRAY, + LIGHTGRAY, LIGHTGRAY, LIGHTGRAY +}; + + +COLORS_DESIGN_SETTINGS::COLORS_DESIGN_SETTINGS() +{ + for( unsigned src = 0, dst = 0; dst < DIM(m_LayersColors); ++dst ) + { + m_LayersColors[dst] = default_layer_color[src++]; + + if( src >= DIM( default_layer_color ) ) + src = 0; // wrap the source. + } + + for( unsigned src = 0, dst = 0; dst < DIM(m_ItemsColors); ++dst ) + { + m_ItemsColors[dst] = default_items_color[src++]; + + if( src >= DIM( default_items_color ) ) + src = 0; + } +} + + +EDA_COLOR_T COLORS_DESIGN_SETTINGS::GetLayerColor( LAYER_NUM aLayer ) const +{ + if( (unsigned) aLayer < DIM(m_LayersColors) ) + { + return m_LayersColors[aLayer]; + } + return UNSPECIFIED_COLOR; +} + + +void COLORS_DESIGN_SETTINGS::SetLayerColor( LAYER_NUM aLayer, EDA_COLOR_T aColor ) +{ + if( (unsigned) aLayer < DIM(m_LayersColors) ) + { + m_LayersColors[aLayer] = aColor; + } +} + + +EDA_COLOR_T COLORS_DESIGN_SETTINGS::GetItemColor( int aItemIdx ) const +{ + if( (unsigned) aItemIdx < DIM( m_ItemsColors ) ) + { + return m_ItemsColors[aItemIdx]; + } + + return UNSPECIFIED_COLOR; +} + + +void COLORS_DESIGN_SETTINGS::SetItemColor( int aItemIdx, EDA_COLOR_T aColor ) +{ + if( (unsigned) aItemIdx < DIM(m_ItemsColors) ) + { + m_ItemsColors[aItemIdx] = aColor; + } +} + + +void COLORS_DESIGN_SETTINGS::SetAllColorsAs( EDA_COLOR_T aColor ) +{ + for( unsigned ii = 0; ii < DIM(m_LayersColors); ii++ ) + m_LayersColors[ii] = aColor; + + for( unsigned ii = 0; ii < DIM(m_ItemsColors); ii++ ) + m_ItemsColors[ii] = aColor; +} diff --git a/common/class_layer_box_selector.cpp b/common/class_layer_box_selector.cpp new file mode 100644 index 0000000..ad2df99 --- /dev/null +++ b/common/class_layer_box_selector.cpp @@ -0,0 +1,149 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.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 <common.h> +#include <colors_selection.h> +#include <layers_id_colors_and_visibility.h> +#include <bitmaps.h> +#include <colors.h> + +#include <wx/wx.h> +#include <wx/ownerdrw.h> +#include <wx/menuitem.h> + +#include <class_layer_box_selector.h> + + +LAYER_SELECTOR::LAYER_SELECTOR() +{ + m_layerhotkeys = true; + m_hotkeys = NULL; +} + + +bool LAYER_SELECTOR::SetLayersHotkeys( bool value ) +{ + m_layerhotkeys = value; + return m_layerhotkeys; +} + + +void LAYER_SELECTOR::SetBitmapLayer( wxBitmap& aLayerbmp, LAYER_NUM aLayer ) +{ + wxMemoryDC bmpDC; + wxBrush brush; + + // Prepare Bitmap + bmpDC.SelectObject( aLayerbmp ); + brush.SetColour( MakeColour( GetLayerColor( aLayer ) ) ); + brush.SetStyle( wxBRUSHSTYLE_SOLID ); + + bmpDC.SetBrush( brush ); + bmpDC.DrawRectangle( 0, 0, aLayerbmp.GetWidth(), aLayerbmp.GetHeight() ); + bmpDC.SetBrush( *wxTRANSPARENT_BRUSH ); + bmpDC.SetPen( *wxBLACK_PEN ); + bmpDC.DrawRectangle( 0, 0, aLayerbmp.GetWidth(), aLayerbmp.GetHeight() ); +} + +/* class to display a layer list in a wxBitmapComboBox. + */ +LAYER_BOX_SELECTOR::LAYER_BOX_SELECTOR( wxWindow* parent, wxWindowID id, + const wxPoint& pos, const wxSize& size, + int n, const wxString choices[] ) : + wxBitmapComboBox( parent, id, wxEmptyString, pos, size, n, choices, wxCB_READONLY ), + LAYER_SELECTOR() +{ + m_hotkeys = NULL; + + if( choices != NULL ) + ResyncBitmapOnly(); +} + + +LAYER_BOX_SELECTOR::LAYER_BOX_SELECTOR( wxWindow* parent, wxWindowID id, + const wxPoint& pos, const wxSize& size, + const wxArrayString& choices ) : + wxBitmapComboBox( parent, id, wxEmptyString, pos, size, choices, wxCB_READONLY ), + LAYER_SELECTOR() +{ + m_hotkeys = NULL; + + if( !choices.IsEmpty() ) + ResyncBitmapOnly(); +} + + +// Get Current Item # +int LAYER_BOX_SELECTOR::GetChoice() +{ + return GetSelection(); +} + + +// Get Current Layer +LAYER_NUM LAYER_BOX_SELECTOR::GetLayerSelection() const +{ + if( GetSelection() < 0 ) + return UNDEFINED_LAYER; + + return (LAYER_NUM)(intptr_t) GetClientData( GetSelection() ); +} + + +// Set Layer # +int LAYER_BOX_SELECTOR::SetLayerSelection( LAYER_NUM layer ) +{ + int elements = GetCount(); + + for( int i = 0; i < elements; i++ ) + { + if( GetClientData( i ) == (void*)(intptr_t) layer ) + { + if( GetSelection() != i ) // Element (i) is not selected + { + SetSelection( i ); + return i; + } + else + return i; //If element already selected; do nothing + } + } + + // Not Found + SetSelection( -1 ); + return -1; +} + + +void LAYER_BOX_SELECTOR::ResyncBitmapOnly() +{ + int elements = GetCount(); + + for( LAYER_NUM i = 0; i < elements; ++i ) + { + wxBitmap layerbmp( 14, 14 ); + SetBitmapLayer( layerbmp, i ); + } +} + diff --git a/common/class_marker_base.cpp b/common/class_marker_base.cpp new file mode 100644 index 0000000..91e05a2 --- /dev/null +++ b/common/class_marker_base.cpp @@ -0,0 +1,208 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.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 + */ + +/** + * @file class_marker_base.cpp + * @brief Implementation of MARKER_BASE class. + * Markers are used to show something (usually a drc/erc problem). + * Markers in Pcbnew and Eeschema are derived from this base class. + */ + +/* file class_marker_base.cpp + */ + +#include "fctsys.h" +#include "gr_basic.h" +#include "class_base_screen.h" +#include "common.h" +#include "macros.h" +#include "class_drawpanel.h" +#include "class_marker_base.h" +#include "dialog_display_info_HTML_base.h" + + +// Default marquer shape: +const int M_SHAPE_SCALE = 6; // default scaling factor for MarkerShapeCorners coordinates +/* The graphic shape of markers is a polygon. + * MarkerShapeCorners contains the coordinates of corners of the polygonal default shape + * actual coordinates are these values * .m_ScalingFactor +*/ +static const wxPoint MarkerShapeCorners[] = +{ + wxPoint( 0, 0 ), + wxPoint( 8, 1 ), + wxPoint( 4, 3 ), + wxPoint( 13, 8 ), + wxPoint( 9, 9 ), + wxPoint( 8, 13 ), + wxPoint( 3, 4 ), + wxPoint( 1, 8 ) +}; +const unsigned CORNERS_COUNT = DIM( MarkerShapeCorners ); + +/*******************/ +/* Classe MARKER_BASE */ +/*******************/ + +void MARKER_BASE::init() +{ + m_MarkerType = MARKER_UNSPEC; + m_ErrorLevel = MARKER_SEVERITY_UNSPEC; + m_Color = RED; + wxPoint start = MarkerShapeCorners[0]; + wxPoint end = MarkerShapeCorners[0]; + + for( unsigned ii = 0; ii < CORNERS_COUNT; ii++ ) + { + wxPoint corner = MarkerShapeCorners[ii]; + start.x = std::min( start.x, corner.x); + start.y = std::min( start.y, corner.y); + end.x = std::max( end.x, corner.x); + end.y = std::max( end.y, corner.y); + } + + m_ShapeBoundingBox.SetOrigin(start); + m_ShapeBoundingBox.SetEnd(end); +} + + +MARKER_BASE::MARKER_BASE( const MARKER_BASE& aMarker ) +{ + m_Pos = aMarker.m_Pos; + m_ErrorLevel = aMarker.m_ErrorLevel; + m_MarkerType = aMarker.m_MarkerType; + m_Color = aMarker.m_Color; + m_ShapeBoundingBox = aMarker.m_ShapeBoundingBox; + m_ScalingFactor = aMarker.m_ScalingFactor; +} + + +MARKER_BASE::MARKER_BASE() +{ + m_ScalingFactor = M_SHAPE_SCALE; + init(); +} + + +MARKER_BASE::MARKER_BASE( int aErrorCode, const wxPoint& aMarkerPos, + const wxString& aText, const wxPoint& aPos, + const wxString& bText, const wxPoint& bPos ) +{ + m_ScalingFactor = M_SHAPE_SCALE; + init(); + + SetData( aErrorCode, aMarkerPos, + aText, aPos, + bText, bPos ); +} + + +MARKER_BASE::MARKER_BASE( int aErrorCode, const wxPoint& aMarkerPos, + const wxString& aText, const wxPoint& aPos ) +{ + m_ScalingFactor = M_SHAPE_SCALE; + init(); + SetData( aErrorCode, aMarkerPos, aText, aPos ); +} + + +MARKER_BASE::~MARKER_BASE() +{ +} + + +void MARKER_BASE::SetData( int aErrorCode, const wxPoint& aMarkerPos, + const wxString& aText, const wxPoint& aPos, + const wxString& bText, const wxPoint& bPos ) +{ + m_Pos = aMarkerPos; + m_drc.SetData( aErrorCode, + aText, bText, aPos, bPos ); +} + + +void MARKER_BASE::SetData( int aErrorCode, const wxPoint& aMarkerPos, + const wxString& aText, const wxPoint& aPos ) +{ + m_Pos = aMarkerPos; + m_drc.SetData( aErrorCode, + aText, aPos ); +} + + +bool MARKER_BASE::HitTestMarker( const wxPoint& refPos ) const +{ + wxPoint rel_pos = refPos - m_Pos; + rel_pos.x /= m_ScalingFactor; + rel_pos.y /= m_ScalingFactor; + + return m_ShapeBoundingBox.Contains( rel_pos ); +} + + +EDA_RECT MARKER_BASE::GetBoundingBoxMarker() const +{ + wxSize realsize = m_ShapeBoundingBox.GetSize(); + wxPoint realposition = m_ShapeBoundingBox.GetPosition(); + realsize.x *= m_ScalingFactor; + realsize.y *= m_ScalingFactor; + realposition.x *= m_ScalingFactor; + realposition.y *= m_ScalingFactor; + realposition += m_Pos; + return EDA_RECT( m_Pos, realsize ); +} + +void MARKER_BASE::DrawMarker( EDA_DRAW_PANEL* aPanel, wxDC* aDC, GR_DRAWMODE aDrawMode, + const wxPoint& aOffset ) +{ + wxPoint corners[CORNERS_COUNT]; + + GRSetDrawMode( aDC, aDrawMode ); + + for( unsigned ii = 0; ii < CORNERS_COUNT; ii++ ) + { + corners[ii] = MarkerShapeCorners[ii]; + corners[ii].x *= m_ScalingFactor; + corners[ii].y *= m_ScalingFactor; + corners[ii] += m_Pos + aOffset; + } + + GRClosedPoly( aPanel->GetClipBox(), aDC, CORNERS_COUNT, corners, + true, // = Filled + 0, // outline width + m_Color, // outline color + m_Color // fill collor + ); +} + + +void MARKER_BASE::DisplayMarkerInfo( EDA_DRAW_FRAME* aFrame ) +{ + wxString msg = m_drc.ShowHtml(); + DIALOG_DISPLAY_HTML_TEXT_BASE infodisplay( (wxWindow*)aFrame, wxID_ANY, _( "Marker Info" ), + wxGetMousePosition(), wxSize( 550, 140 ) ); + + infodisplay.m_htmlWindow->SetPage( msg ); + infodisplay.ShowModal(); +} diff --git a/common/class_page_info.cpp b/common/class_page_info.cpp new file mode 100644 index 0000000..2cf94d4 --- /dev/null +++ b/common/class_page_info.cpp @@ -0,0 +1,283 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2012 KiCad Developers, see CHANGELOG.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 <common.h> +#include <class_page_info.h> +#include <macros.h> + + +// late arriving wxPAPER_A0, wxPAPER_A1 +#if wxABI_VERSION >= 20999 + #define PAPER_A0 wxPAPER_A0 + #define PAPER_A1 wxPAPER_A1 +#else + #define PAPER_A0 wxPAPER_A2 + #define PAPER_A1 wxPAPER_A2 +#endif + + +// Standard paper sizes nicknames. +const wxChar PAGE_INFO::A4[] = wxT( "A4" ); +const wxChar PAGE_INFO::A3[] = wxT( "A3" ); +const wxChar PAGE_INFO::A2[] = wxT( "A2" ); +const wxChar PAGE_INFO::A1[] = wxT( "A1" ); +const wxChar PAGE_INFO::A0[] = wxT( "A0" ); +const wxChar PAGE_INFO::A[] = wxT( "A" ); +const wxChar PAGE_INFO::B[] = wxT( "B" ) ; +const wxChar PAGE_INFO::C[] = wxT( "C" ); +const wxChar PAGE_INFO::D[] = wxT( "D" ); +const wxChar PAGE_INFO::E[] = wxT( "E" ); + +const wxChar PAGE_INFO::GERBER[] = wxT( "GERBER" ); +const wxChar PAGE_INFO::USLetter[] = wxT( "USLetter" ); +const wxChar PAGE_INFO::USLegal[] = wxT( "USLegal" ); +const wxChar PAGE_INFO::USLedger[] = wxT( "USLedger" ); +const wxChar PAGE_INFO::Custom[] = wxT( "User" ); + + +// Standard page sizes in mils, all constants +// see: https://lists.launchpad.net/kicad-developers/msg07389.html +// also see: wx/defs.h + +// local readability macro for millimeter wxSize +#define MMsize( x, y ) wxSize( Mm2mils( x ), Mm2mils( y ) ) + +// All MUST be defined as landscape. +const PAGE_INFO PAGE_INFO::pageA4( MMsize( 297, 210 ), wxT( "A4" ), wxPAPER_A4 ); +const PAGE_INFO PAGE_INFO::pageA3( MMsize( 420, 297 ), wxT( "A3" ), wxPAPER_A3 ); +const PAGE_INFO PAGE_INFO::pageA2( MMsize( 594, 420 ), wxT( "A2" ), wxPAPER_A2 ); +const PAGE_INFO PAGE_INFO::pageA1( MMsize( 841, 594 ), wxT( "A1" ), PAPER_A1 ); +const PAGE_INFO PAGE_INFO::pageA0( MMsize( 1189, 841 ), wxT( "A0" ), PAPER_A0 ); + +const PAGE_INFO PAGE_INFO::pageA( wxSize( 11000, 8500 ), wxT( "A" ), wxPAPER_LETTER ); +const PAGE_INFO PAGE_INFO::pageB( wxSize( 17000, 11000 ), wxT( "B" ), wxPAPER_TABLOID ); +const PAGE_INFO PAGE_INFO::pageC( wxSize( 22000, 17000 ), wxT( "C" ), wxPAPER_CSHEET ); +const PAGE_INFO PAGE_INFO::pageD( wxSize( 34000, 22000 ), wxT( "D" ), wxPAPER_DSHEET ); +const PAGE_INFO PAGE_INFO::pageE( wxSize( 44000, 34000 ), wxT( "E" ), wxPAPER_ESHEET ); + +const PAGE_INFO PAGE_INFO::pageGERBER( wxSize( 32000, 32000 ), wxT( "GERBER" ), wxPAPER_NONE ); +const PAGE_INFO PAGE_INFO::pageUser( wxSize( 17000, 11000 ), Custom, wxPAPER_NONE ); + +// US paper sizes +const PAGE_INFO PAGE_INFO::pageUSLetter( wxSize( 11000, 8500 ), wxT( "USLetter" ), wxPAPER_LETTER ); +const PAGE_INFO PAGE_INFO::pageUSLegal( wxSize( 14000, 8500 ), wxT( "USLegal" ), wxPAPER_LEGAL ); +const PAGE_INFO PAGE_INFO::pageUSLedger( wxSize( 17000, 11000 ), wxT( "USLedger" ), wxPAPER_TABLOID ); + +// Custom paper size for next instantiation of type "User" +int PAGE_INFO::s_user_width = 17000; +int PAGE_INFO::s_user_height = 11000; + + +inline void PAGE_INFO::updatePortrait() +{ + // update m_portrait based on orientation of m_size.x and m_size.y + m_portrait = ( m_size.y > m_size.x ); +} + + +PAGE_INFO::PAGE_INFO( const wxSize& aSizeMils, const wxString& aType, wxPaperSize aPaperId ) : + m_type( aType ), m_size( aSizeMils ), m_paper_id( aPaperId ) +{ + updatePortrait(); + + // This constructor is protected, and only used by const PAGE_INFO's known + // only to class implementation, so no further changes to "this" object are + // expected. +} + + +PAGE_INFO::PAGE_INFO( const wxString& aType, bool IsPortrait ) +{ + SetType( aType, IsPortrait ); +} + + +bool PAGE_INFO::SetType( const wxString& aType, bool IsPortrait ) +{ + bool rc = true; + + // all are landscape initially + if( aType == pageA4.GetType() ) + *this = pageA4; + else if( aType == pageA3.GetType() ) + *this = pageA3; + else if( aType == pageA2.GetType() ) + *this = pageA2; + else if( aType == pageA1.GetType() ) + *this = pageA1; + else if( aType == pageA0.GetType() ) + *this = pageA0; + else if( aType == pageA.GetType() ) + *this = pageA; + else if( aType == pageB.GetType() ) + *this = pageB; + else if( aType == pageC.GetType() ) + *this = pageC; + else if( aType == pageD.GetType() ) + *this = pageD; + else if( aType == pageE.GetType() ) + *this = pageE; + else if( aType == pageGERBER.GetType() ) + *this = pageGERBER; + else if( aType == pageUSLetter.GetType() ) + *this = pageUSLetter; + else if( aType == pageUSLegal.GetType() ) + *this = pageUSLegal; + else if( aType == pageUSLedger.GetType() ) + *this = pageUSLedger; + else if( aType == pageUser.GetType() ) + { + // pageUser is const, and may not and does not hold the custom size, + // so customize *this later + *this = pageUser; + + // customize: + m_size.x = s_user_width; + m_size.y = s_user_height; + + updatePortrait(); + } + else + rc = false; + + if( IsPortrait ) + { + // all private PAGE_INFOs are landscape, must swap x and y + m_size = wxSize( m_size.y, m_size.x ); + updatePortrait(); + } + + return rc; +} + + +bool PAGE_INFO::IsCustom() const +{ + return m_type == Custom; +} + + +void PAGE_INFO::SetPortrait( bool isPortrait ) +{ + if( m_portrait != isPortrait ) + { + // swap x and y in m_size + m_size = wxSize( m_size.y, m_size.x ); + + m_portrait = isPortrait; + + // margins are not touched, do that if you want + } +} + + +static int clampWidth( int aWidthInMils ) +{ +/* was giving EESCHEMA single component SVG plotter grief + However a minimal test is made to avoid values that crashes Kicad + if( aWidthInMils < 4000 ) // 4" is about a baseball card + aWidthInMils = 4000; + else if( aWidthInMils > 44000 ) //44" is plotter size + aWidthInMils = 44000; +*/ + if( aWidthInMils < 10 ) + aWidthInMils = 10; + return aWidthInMils; +} + + +static int clampHeight( int aHeightInMils ) +{ +/* was giving EESCHEMA single component SVG plotter grief + clamping is best done at the UI, i.e. dialog, levels + However a minimal test is made to avoid values that crashes Kicad + if( aHeightInMils < 4000 ) + aHeightInMils = 4000; + else if( aHeightInMils > 44000 ) + aHeightInMils = 44000; +*/ + if( aHeightInMils < 10 ) + aHeightInMils = 10; + return aHeightInMils; +} + + +void PAGE_INFO::SetCustomWidthMils( int aWidthInMils ) +{ + s_user_width = clampWidth( aWidthInMils ); +} + + +void PAGE_INFO::SetCustomHeightMils( int aHeightInMils ) +{ + s_user_height = clampHeight( aHeightInMils ); +} + + +void PAGE_INFO::SetWidthMils( int aWidthInMils ) +{ + if( m_size.x != aWidthInMils ) + { + m_size.x = clampWidth( aWidthInMils ); + + m_type = Custom; + m_paper_id = wxPAPER_NONE; + + updatePortrait(); + } +} + + +void PAGE_INFO::SetHeightMils( int aHeightInMils ) +{ + if( m_size.y != aHeightInMils ) + { + m_size.y = clampHeight( aHeightInMils ); + + m_type = Custom; + m_paper_id = wxPAPER_NONE; + + updatePortrait(); + } +} + + +void PAGE_INFO::Format( OUTPUTFORMATTER* aFormatter, int aNestLevel, int aControlBits ) const + throw( IO_ERROR ) +{ + aFormatter->Print( aNestLevel, "(page %s", aFormatter->Quotew( GetType() ).c_str() ); + + // The page dimensions are only required for user defined page sizes. + // Internally, the page size is in mils + if( GetType() == PAGE_INFO::Custom ) + aFormatter->Print( 0, " %g %g", + GetWidthMils() * 25.4 / 1000.0, + GetHeightMils() * 25.4 / 1000.0 ); + + if( !IsCustom() && IsPortrait() ) + aFormatter->Print( 0, " portrait" ); + + aFormatter->Print( 0, ")\n" ); +} diff --git a/common/class_plotter.cpp b/common/class_plotter.cpp new file mode 100644 index 0000000..9c1e9c0 --- /dev/null +++ b/common/class_plotter.cpp @@ -0,0 +1,519 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.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 + */ + +/** + * @file class_plotter.cpp + * @brief KiCad: Base of all the plot routines + * the class PLOTTER handle basic functions to plot schematic and boards + * with different plot formats. + * + * There are currently engines for: + * HPGL + * POSTSCRIPT + * GERBER + * DXF + * an SVG 'plot' is also provided along with the 'print' function by wx, but + * is not handled here. + */ + +#include <fctsys.h> + +#include <trigo.h> +#include <wxstruct.h> +#include <base_struct.h> +#include <common.h> +#include <plot_common.h> +#include <macros.h> +#include <class_base_screen.h> +#include <drawtxt.h> + +PLOTTER::PLOTTER( ) +{ + plotScale = 1; + defaultPenWidth = 0; + currentPenWidth = -1; // To-be-set marker + penState = 'Z'; // End-of-path idle + m_plotMirror = false; // Plot mirror option flag + m_mirrorIsHorizontal = true; + m_yaxisReversed = false; + outputFile = 0; + colorMode = false; // Starts as a BW plot + negativeMode = false; + // Temporary init to avoid not initialized vars, will be set later + m_IUsPerDecimil = 1; // will be set later to the actual value + iuPerDeviceUnit = 1; // will be set later to the actual value + m_dashMarkLength_mm = 0.5; // Dashed line parameter in mm: segment + m_dashGapLength_mm = 0.25; // Dashed line parameter in mm: gap +} + +PLOTTER::~PLOTTER() +{ + // Emergency cleanup, but closing the file is + // usually made in EndPlot(). + if( outputFile ) + fclose( outputFile ); +} + + +bool PLOTTER::OpenFile( const wxString& aFullFilename ) +{ + filename = aFullFilename; + + wxASSERT( !outputFile ); + + // Open the file in text mode (not suitable for all plotters + // but only for most of them + outputFile = wxFopen( filename, wxT( "wt" ) ); + + if( outputFile == NULL ) + return false ; + + return true; +} + + +DPOINT PLOTTER::userToDeviceCoordinates( const wxPoint& aCoordinate ) +{ + wxPoint pos = aCoordinate - plotOffset; + + double x = pos.x * plotScale; + double y = ( paperSize.y - pos.y * plotScale ); + + if( m_plotMirror ) + { + if( m_mirrorIsHorizontal ) + x = ( paperSize.x - pos.x * plotScale ); + else + y = pos.y * plotScale; + } + + if( m_yaxisReversed ) + y = paperSize.y - y; + + x *= iuPerDeviceUnit; + y *= iuPerDeviceUnit; + + return DPOINT( x, y ); +} + + +DPOINT PLOTTER::userToDeviceSize( const wxSize& size ) +{ + return DPOINT( size.x * plotScale * iuPerDeviceUnit, + size.y * plotScale * iuPerDeviceUnit ); +} + + +double PLOTTER::userToDeviceSize( double size ) const +{ + return size * plotScale * iuPerDeviceUnit; +} + + +double PLOTTER::GetDashMarkLenIU() const +{ + double mark = userToDeviceSize( m_dashMarkLength_mm*10000/25.4*m_IUsPerDecimil - GetCurrentLineWidth() ); + return ( mark < 0.0 ) ? 0.0 : mark; +} + + +double PLOTTER::GetDashGapLenIU() const +{ + return userToDeviceSize( m_dashGapLength_mm*10000/25.4*m_IUsPerDecimil + GetCurrentLineWidth() ); +} + +void PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius, + FILL_T fill, int width ) +{ + wxPoint start, end; + const int delta = 50; // increment (in 0.1 degrees) to draw circles + + if( StAngle > EndAngle ) + std::swap( StAngle, EndAngle ); + + SetCurrentLineWidth( width ); + /* Please NOTE the different sign due to Y-axis flip */ + start.x = centre.x + KiROUND( cosdecideg( radius, -StAngle ) ); + start.y = centre.y + KiROUND( sindecideg( radius, -StAngle ) ); + MoveTo( start ); + for( int ii = StAngle + delta; ii < EndAngle; ii += delta ) + { + end.x = centre.x + KiROUND( cosdecideg( radius, -ii ) ); + end.y = centre.y + KiROUND( sindecideg( radius, -ii ) ); + LineTo( end ); + } + + end.x = centre.x + KiROUND( cosdecideg( radius, -EndAngle ) ); + end.y = centre.y + KiROUND( sindecideg( radius, -EndAngle ) ); + FinishTo( end ); +} + + +void PLOTTER::PlotImage(const wxImage & aImage, const wxPoint& aPos, double aScaleFactor ) +{ + wxSize size( aImage.GetWidth() * aScaleFactor, + aImage.GetHeight() * aScaleFactor ); + + wxPoint start = aPos; + start.x -= size.x / 2; + start.y -= size.y / 2; + + wxPoint end = start; + end.x += size.x; + end.y += size.y; + + Rect( start, end, NO_FILL ); +} + + +void PLOTTER::markerSquare( const wxPoint& position, int radius ) +{ + double r = KiROUND( radius / 1.4142 ); + std::vector< wxPoint > corner_list; + wxPoint corner; + corner.x = position.x + r; + corner.y = position.y + r; + corner_list.push_back( corner ); + corner.x = position.x + r; + corner.y = position.y - r; + corner_list.push_back( corner ); + corner.x = position.x - r; + corner.y = position.y - r; + corner_list.push_back( corner ); + corner.x = position.x - r; + corner.y = position.y + r; + corner_list.push_back( corner ); + corner.x = position.x + r; + corner.y = position.y + r; + corner_list.push_back( corner ); + + PlotPoly( corner_list, NO_FILL, GetCurrentLineWidth() ); +} + + +void PLOTTER::markerCircle( const wxPoint& position, int radius ) +{ + Circle( position, radius * 2, NO_FILL, GetCurrentLineWidth() ); +} + + +void PLOTTER::markerLozenge( const wxPoint& position, int radius ) +{ + std::vector< wxPoint > corner_list; + wxPoint corner; + corner.x = position.x; + corner.y = position.y + radius; + corner_list.push_back( corner ); + corner.x = position.x + radius; + corner.y = position.y, + corner_list.push_back( corner ); + corner.x = position.x; + corner.y = position.y - radius; + corner_list.push_back( corner ); + corner.x = position.x - radius; + corner.y = position.y; + corner_list.push_back( corner ); + corner.x = position.x; + corner.y = position.y + radius; + corner_list.push_back( corner ); + + PlotPoly( corner_list, NO_FILL, GetCurrentLineWidth() ); +} + + +void PLOTTER::markerHBar( const wxPoint& pos, int radius ) +{ + MoveTo( wxPoint( pos.x - radius, pos.y ) ); + FinishTo( wxPoint( pos.x + radius, pos.y ) ); +} + + +void PLOTTER::markerSlash( const wxPoint& pos, int radius ) +{ + MoveTo( wxPoint( pos.x - radius, pos.y - radius ) ); + FinishTo( wxPoint( pos.x + radius, pos.y + radius ) ); +} + + +void PLOTTER::markerBackSlash( const wxPoint& pos, int radius ) +{ + MoveTo( wxPoint( pos.x + radius, pos.y - radius ) ); + FinishTo( wxPoint( pos.x - radius, pos.y + radius ) ); +} + + +void PLOTTER::markerVBar( const wxPoint& pos, int radius ) +{ + MoveTo( wxPoint( pos.x, pos.y - radius ) ); + FinishTo( wxPoint( pos.x, pos.y + radius ) ); +} + + +void PLOTTER::Marker( const wxPoint& position, int diametre, unsigned aShapeId ) +{ + int radius = diametre / 2; + /* Marker are composed by a series of 'parts' superimposed; not every + combination make sense, obviously. Since they are used in order I + tried to keep the uglier/more complex constructions at the end. + Also I avoided the |/ |\ -/ -\ construction because they're *very* + ugly... if needed they could be added anyway... I'd like to see + a board with more than 58 drilling/slotting tools! + If Visual C++ supported the 0b literals they would be optimally + and easily encoded as an integer array. We have to do with octal */ + static const unsigned char marker_patterns[MARKER_COUNT] = { + // Bit order: O Square Lozenge - | \ / + // First choice: simple shapes + 0003, // X + 0100, // O + 0014, // + + 0040, // Sq + 0020, // Lz + // Two simple shapes + 0103, // X O + 0017, // X + + 0043, // X Sq + 0023, // X Lz + 0114, // O + + 0140, // O Sq + 0120, // O Lz + 0054, // + Sq + 0034, // + Lz + 0060, // Sq Lz + // Three simple shapes + 0117, // X O + + 0143, // X O Sq + 0123, // X O Lz + 0057, // X + Sq + 0037, // X + Lz + 0063, // X Sq Lz + 0154, // O + Sq + 0134, // O + Lz + 0074, // + Sq Lz + // Four simple shapes + 0174, // O Sq Lz + + 0163, // X O Sq Lz + 0157, // X O Sq + + 0137, // X O Lz + + 0077, // X Sq Lz + + // This draws *everything * + 0177, // X O Sq Lz + + // Here we use the single bars... so the cross is forbidden + 0110, // O - + 0104, // O | + 0101, // O / + 0050, // Sq - + 0044, // Sq | + 0041, // Sq / + 0030, // Lz - + 0024, // Lz | + 0021, // Lz / + 0150, // O Sq - + 0144, // O Sq | + 0141, // O Sq / + 0130, // O Lz - + 0124, // O Lz | + 0121, // O Lz / + 0070, // Sq Lz - + 0064, // Sq Lz | + 0061, // Sq Lz / + 0170, // O Sq Lz - + 0164, // O Sq Lz | + 0161, // O Sq Lz / + // Last resort: the backlash component (easy to confound) + 0102, // \ O + 0042, // \ Sq + 0022, // \ Lz + 0142, // \ O Sq + 0122, // \ O Lz + 0062, // \ Sq Lz + 0162 // \ O Sq Lz + }; + if( aShapeId >= MARKER_COUNT ) + { + // Fallback shape + markerCircle( position, radius ); + } + else + { + // Decode the pattern and draw the corresponding parts + unsigned char pat = marker_patterns[aShapeId]; + if( pat & 0001 ) + markerSlash( position, radius ); + if( pat & 0002 ) + markerBackSlash( position, radius ); + if( pat & 0004 ) + markerVBar( position, radius ); + if( pat & 0010 ) + markerHBar( position, radius ); + if( pat & 0020 ) + markerLozenge( position, radius ); + if( pat & 0040 ) + markerSquare( position, radius ); + if( pat & 0100 ) + markerCircle( position, radius ); + } +} + + +void PLOTTER::segmentAsOval( const wxPoint& start, const wxPoint& end, int width, + EDA_DRAW_MODE_T tracemode ) +{ + wxPoint center( (start.x + end.x) / 2, (start.y + end.y) / 2 ); + wxSize size( end.x - start.x, end.y - start.y ); + double orient; + + if( size.y == 0 ) + orient = 0; + else if( size.x == 0 ) + orient = 900; + else + orient = -ArcTangente( size.y, size.x ); + + size.x = KiROUND( EuclideanNorm( size ) ) + width; + size.y = width; + + FlashPadOval( center, size, orient, tracemode ); +} + + +void PLOTTER::sketchOval( const wxPoint& pos, const wxSize& aSize, double orient, int width ) +{ + SetCurrentLineWidth( width ); + width = currentPenWidth; + int radius, deltaxy, cx, cy; + wxSize size( aSize ); + + if( size.x > size.y ) + { + std::swap( size.x, size.y ); + orient = AddAngles( orient, 900 ); + } + + deltaxy = size.y - size.x; /* distance between centers of the oval */ + radius = ( size.x - width ) / 2; + cx = -radius; + cy = -deltaxy / 2; + RotatePoint( &cx, &cy, orient ); + MoveTo( wxPoint( cx + pos.x, cy + pos.y ) ); + cx = -radius; + cy = deltaxy / 2; + RotatePoint( &cx, &cy, orient ); + FinishTo( wxPoint( cx + pos.x, cy + pos.y ) ); + + cx = radius; + cy = -deltaxy / 2; + RotatePoint( &cx, &cy, orient ); + MoveTo( wxPoint( cx + pos.x, cy + pos.y ) ); + cx = radius; + cy = deltaxy / 2; + RotatePoint( &cx, &cy, orient ); + FinishTo( wxPoint( cx + pos.x, cy + pos.y ) ); + + cx = 0; + cy = deltaxy / 2; + RotatePoint( &cx, &cy, orient ); + Arc( wxPoint( cx + pos.x, cy + pos.y ), + orient + 1800, orient + 3600, + radius, NO_FILL ); + cx = 0; + cy = -deltaxy / 2; + RotatePoint( &cx, &cy, orient ); + Arc( wxPoint( cx + pos.x, cy + pos.y ), + orient, orient + 1800, + radius, NO_FILL ); +} + + +void PLOTTER::ThickSegment( const wxPoint& start, const wxPoint& end, int width, + EDA_DRAW_MODE_T tracemode ) +{ + if( tracemode == FILLED ) + { + SetCurrentLineWidth( width ); + MoveTo( start ); + FinishTo( end ); + } + else + { + SetCurrentLineWidth( -1 ); + segmentAsOval( start, end, width, tracemode ); + } +} + + +void PLOTTER::ThickArc( const wxPoint& centre, double StAngle, double EndAngle, + int radius, int width, EDA_DRAW_MODE_T tracemode ) +{ + if( tracemode == FILLED ) + Arc( centre, StAngle, EndAngle, radius, NO_FILL, width ); + else + { + SetCurrentLineWidth( -1 ); + Arc( centre, StAngle, EndAngle, + radius - ( width - currentPenWidth ) / 2, NO_FILL, -1 ); + Arc( centre, StAngle, EndAngle, + radius + ( width - currentPenWidth ) / 2, NO_FILL, -1 ); + } +} + + +void PLOTTER::ThickRect( const wxPoint& p1, const wxPoint& p2, int width, + EDA_DRAW_MODE_T tracemode ) +{ + if( tracemode == FILLED ) + Rect( p1, p2, NO_FILL, width ); + else + { + SetCurrentLineWidth( -1 ); + wxPoint offsetp1( p1.x - (width - currentPenWidth) / 2, + p1.y - (width - currentPenWidth) / 2 ); + wxPoint offsetp2( p2.x + (width - currentPenWidth) / 2, + p2.y + (width - currentPenWidth) / 2 ); + Rect( offsetp1, offsetp2, NO_FILL, -1 ); + offsetp1.x += (width - currentPenWidth); + offsetp1.y += (width - currentPenWidth); + offsetp2.x -= (width - currentPenWidth); + offsetp2.y -= (width - currentPenWidth); + Rect( offsetp1, offsetp2, NO_FILL, -1 ); + } +} + + +void PLOTTER::ThickCircle( const wxPoint& pos, int diametre, int width, EDA_DRAW_MODE_T tracemode ) +{ + if( tracemode == FILLED ) + Circle( pos, diametre, NO_FILL, width ); + else + { + SetCurrentLineWidth( -1 ); + Circle( pos, diametre - width + currentPenWidth, NO_FILL, -1 ); + Circle( pos, diametre + width - currentPenWidth, NO_FILL, -1 ); + } +} + + +void PLOTTER::SetPageSettings( const PAGE_INFO& aPageSettings ) +{ + pageInfo = aPageSettings; +} diff --git a/common/class_undoredo_container.cpp b/common/class_undoredo_container.cpp new file mode 100644 index 0000000..9c71d1a --- /dev/null +++ b/common/class_undoredo_container.cpp @@ -0,0 +1,348 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2009 jean-pierre.charras@gipsa-lab.inpg.fr + * Copyright (C) 2011 Wayne Stambaugh <stambaughw@verizon.net> + * Copyright (C) 2009 KiCad Developers, see change_log.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 <fctsys.h> +#include <common.h> +#include <base_struct.h> + +#include <base_struct.h> +#include <class_undoredo_container.h> + + +ITEM_PICKER::ITEM_PICKER( EDA_ITEM* aItem, UNDO_REDO_T aUndoRedoStatus ) +{ + m_undoRedoStatus = aUndoRedoStatus; + SetItem( aItem ); + m_pickerFlags = 0; + m_link = NULL; +} + + +PICKED_ITEMS_LIST::PICKED_ITEMS_LIST() +{ + m_Status = UR_UNSPECIFIED; +} + +PICKED_ITEMS_LIST::~PICKED_ITEMS_LIST() +{ +} + + +void PICKED_ITEMS_LIST::PushItem( const ITEM_PICKER& aItem ) +{ + m_ItemsList.push_back( aItem ); +} + + +ITEM_PICKER PICKED_ITEMS_LIST::PopItem() +{ + ITEM_PICKER item; + + if( m_ItemsList.size() != 0 ) + { + item = m_ItemsList.back(); + m_ItemsList.pop_back(); + } + + return item; +} + + +bool PICKED_ITEMS_LIST::ContainsItem( const EDA_ITEM* aItem ) const +{ + for( size_t i = 0; i < m_ItemsList.size(); i++ ) + { + if( m_ItemsList[ i ].GetItem() == aItem ) + return true; + } + + return false; +} + + +int PICKED_ITEMS_LIST::FindItem( const EDA_ITEM* aItem ) const +{ + for( size_t i = 0; i < m_ItemsList.size(); i++ ) + { + if( m_ItemsList[i].GetItem() == aItem ) + return i; + } + + return -1; +} + + +void PICKED_ITEMS_LIST::ClearItemsList() +{ + m_ItemsList.clear(); +} + + +void PICKED_ITEMS_LIST::ClearListAndDeleteItems() +{ + bool show_error_message = true; + + // Delete items is they are not flagged UR_NEW, or if this is a block operation + while( GetCount() > 0 ) + { + ITEM_PICKER wrapper = PopItem(); + if( wrapper.GetItem() == NULL ) // No more item in list. + break; + switch( wrapper.GetStatus() ) + { + case UR_UNSPECIFIED: + if( show_error_message ) + wxMessageBox( wxT( "ClearListAndDeleteItems() error: UR_UNSPECIFIED command type" ) ); + + show_error_message = false; + break; + + case UR_WIRE_IMAGE: + { + // Specific to eeschema: a linked list of wires is stored. The wrapper picks only + // the first item (head of list), and is owner of all picked items. + EDA_ITEM* item = wrapper.GetItem(); + + while( item ) + { + // Delete old copy of wires + EDA_ITEM* nextitem = item->Next(); + delete item; + item = nextitem; + } + } + break; + + case UR_MOVED: + case UR_FLIPPED: + case UR_MIRRORED_X: + case UR_MIRRORED_Y: + case UR_ROTATED: + case UR_ROTATED_CLOCKWISE: + case UR_NEW: // Do nothing, items are in use, the picker is not owner of items + break; + + case UR_CHANGED: + case UR_EXCHANGE_T: + delete wrapper.GetLink(); // the picker is owner of this item + break; + + case UR_DELETED: // the picker is owner of this item + case UR_LIBEDIT: /* Libedit save always a copy of the current item + * So, the picker is always owner of the picked item + */ + case UR_MODEDIT: /* Specific to the module editor (modedit creates a full + * copy of the current module when changed), + * and the picker is owner of this item + */ + delete wrapper.GetItem(); + break; + + default: + wxFAIL_MSG( wxString::Format( wxT( "Cannot clear unknown undo/redo command %d" ), + wrapper.GetStatus() ) ); + break; + } + } +} + + +ITEM_PICKER PICKED_ITEMS_LIST::GetItemWrapper( unsigned int aIdx ) const +{ + ITEM_PICKER picker; + + if( aIdx < m_ItemsList.size() ) + picker = m_ItemsList[aIdx]; + + return picker; +} + + +EDA_ITEM* PICKED_ITEMS_LIST::GetPickedItem( unsigned int aIdx ) const +{ + if( aIdx < m_ItemsList.size() ) + return m_ItemsList[aIdx].GetItem(); + else + return NULL; +} + + +EDA_ITEM* PICKED_ITEMS_LIST::GetPickedItemLink( unsigned int aIdx ) const +{ + if( aIdx < m_ItemsList.size() ) + return m_ItemsList[aIdx].GetLink(); + else + return NULL; +} + + +UNDO_REDO_T PICKED_ITEMS_LIST::GetPickedItemStatus( unsigned int aIdx ) const +{ + if( aIdx < m_ItemsList.size() ) + return m_ItemsList[aIdx].GetStatus(); + else + return UR_UNSPECIFIED; +} + + +STATUS_FLAGS PICKED_ITEMS_LIST::GetPickerFlags( unsigned aIdx ) const +{ + if( aIdx < m_ItemsList.size() ) + return m_ItemsList[aIdx].GetFlags(); + else + return 0; +} + + +bool PICKED_ITEMS_LIST::SetPickedItem( EDA_ITEM* aItem, unsigned aIdx ) +{ + if( aIdx < m_ItemsList.size() ) + { + m_ItemsList[aIdx].SetItem( aItem ); + return true; + } + else + return false; +} + + +bool PICKED_ITEMS_LIST::SetPickedItemLink( EDA_ITEM* aLink, unsigned aIdx ) +{ + if( aIdx < m_ItemsList.size() ) + { + m_ItemsList[aIdx].SetLink( aLink ); + return true; + } + else + return false; +} + + +bool PICKED_ITEMS_LIST::SetPickedItem( EDA_ITEM* aItem, UNDO_REDO_T aStatus, unsigned aIdx ) +{ + if( aIdx < m_ItemsList.size() ) + { + m_ItemsList[aIdx].SetItem( aItem ); + m_ItemsList[aIdx].SetStatus( aStatus ); + return true; + } + else + return false; +} + + +bool PICKED_ITEMS_LIST::SetPickedItemStatus( UNDO_REDO_T aStatus, unsigned aIdx ) +{ + if( aIdx < m_ItemsList.size() ) + { + m_ItemsList[aIdx].SetStatus( aStatus ); + return true; + } + else + return false; +} + + +bool PICKED_ITEMS_LIST::SetPickerFlags( STATUS_FLAGS aFlags, unsigned aIdx ) +{ + if( aIdx < m_ItemsList.size() ) + { + m_ItemsList[aIdx].SetFlags( aFlags ); + return true; + } + else + return false; +} + + +bool PICKED_ITEMS_LIST::RemovePicker( unsigned aIdx ) +{ + if( aIdx >= m_ItemsList.size() ) + return false; + m_ItemsList.erase( m_ItemsList.begin() + aIdx ); + return true; +} + + +void PICKED_ITEMS_LIST::CopyList( const PICKED_ITEMS_LIST& aSource ) +{ + m_ItemsList = aSource.m_ItemsList; // Vector's copy +} + + +void PICKED_ITEMS_LIST::ReversePickersListOrder() +{ + std::vector <ITEM_PICKER> tmp; + while( !m_ItemsList.empty() ) + { + tmp.push_back( m_ItemsList.back() ); + m_ItemsList.pop_back(); + } + + m_ItemsList.swap( tmp ); +} + + +/**********************************************/ +/********** UNDO_REDO_CONTAINER ***************/ +/**********************************************/ + +UNDO_REDO_CONTAINER::UNDO_REDO_CONTAINER() +{ +} + + +UNDO_REDO_CONTAINER::~UNDO_REDO_CONTAINER() +{ + ClearCommandList(); +} + + +void UNDO_REDO_CONTAINER::ClearCommandList() +{ + for( unsigned ii = 0; ii < m_CommandsList.size(); ii++ ) + delete m_CommandsList[ii]; + + m_CommandsList.clear(); +} + + +void UNDO_REDO_CONTAINER::PushCommand( PICKED_ITEMS_LIST* aItem ) +{ + m_CommandsList.push_back( aItem ); +} + + +PICKED_ITEMS_LIST* UNDO_REDO_CONTAINER::PopCommand() +{ + if( m_CommandsList.size() != 0 ) + { + PICKED_ITEMS_LIST* item = m_CommandsList.back(); + m_CommandsList.pop_back(); + return item; + } + + return NULL; +} diff --git a/common/colors.cpp b/common/colors.cpp new file mode 100644 index 0000000..0cc6042 --- /dev/null +++ b/common/colors.cpp @@ -0,0 +1,184 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.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 <colors.h> +#include <i18n_utility.h> + + +/** + * The predefined colors used in KiCad. + * Please: if you change a value, remember these values are carefully chosen + * to have good results in Pcbnew, that uses the ORed value of basic colors + * when displaying superimposed objects + * This list must have exactly NBCOLORS items + */ + +const StructColors g_ColorRefs[NBCOLORS] = +{ + { 0, 0, 0, BLACK, _HKI( "Black" ), DARKDARKGRAY }, + { 72, 72, 72, DARKDARKGRAY, _HKI( "Gray 1" ), DARKGRAY }, + { 132, 132, 132, DARKGRAY, _HKI( "Gray 2" ), LIGHTGRAY }, + { 194, 194, 194, LIGHTGRAY, _HKI( "Gray 3" ), WHITE }, + { 255, 255, 255, WHITE, _HKI( "White" ), WHITE }, + { 194, 255, 255, LIGHTYELLOW, _HKI( "L.Yellow" ), WHITE }, + { 72, 0, 0, DARKBLUE, _HKI( "Blue 1" ), BLUE }, + { 0, 72, 0, DARKGREEN, _HKI( "Green 1" ), GREEN }, + { 72, 72, 0, DARKCYAN, _HKI( "Cyan 1" ), CYAN }, + { 0, 0, 72, DARKRED, _HKI( "Red 1" ), RED }, + { 72, 0, 72, DARKMAGENTA, _HKI( "Magenta 1" ), MAGENTA }, + { 0, 72, 72, DARKBROWN, _HKI( "Brown 1" ), BROWN }, + { 132, 0, 0, BLUE, _HKI( "Blue 2" ), LIGHTBLUE }, + { 0, 132, 0, GREEN, _HKI( "Green 2" ), LIGHTGREEN }, + { 132, 132, 0, CYAN, _HKI( "Cyan 2" ), LIGHTCYAN }, + { 0, 0, 132, RED, _HKI( "Red 2" ), LIGHTRED }, + { 132, 0, 132, MAGENTA, _HKI( "Magenta 2" ), LIGHTMAGENTA }, + { 0, 132, 132, BROWN, _HKI( "Brown 2" ), YELLOW }, + { 194, 0, 0, LIGHTBLUE, _HKI( "Blue 3" ), PUREBLUE, }, + { 0, 194, 0, LIGHTGREEN, _HKI( "Green 3" ), PUREGREEN }, + { 194, 194, 0, LIGHTCYAN, _HKI( "Cyan 3" ), PURECYAN }, + { 0, 0, 194, LIGHTRED, _HKI( "Red 3" ), PURERED }, + { 194, 0, 194, LIGHTMAGENTA, _HKI( "Magenta 3" ), PUREMAGENTA }, + { 0, 194, 194, YELLOW, _HKI( "Yellow 3" ), PUREYELLOW }, + { 255, 0, 0, PUREBLUE, _HKI( "Blue 4" ), WHITE }, + { 0, 255, 0, PUREGREEN, _HKI( "Green 4" ), WHITE }, + { 255, 255, 0, PURECYAN, _HKI( "Cyan 4" ), WHITE }, + { 0, 0, 255, PURERED, _HKI( "Red 4" ), WHITE }, + { 255, 0, 255, PUREMAGENTA, _HKI( "Magenta 4" ), WHITE }, + { 0, 255, 255, PUREYELLOW, _HKI( "Yellow 4" ), WHITE }, +}; + + +EDA_COLOR_T ColorByName( const wxString& aName ) +{ + // look for a match in the palette itself + for( EDA_COLOR_T trying = BLACK; trying < NBCOLORS; trying = NextColor(trying) ) + { + if( 0 == aName.CmpNoCase( ColorGetName( trying ) ) ) + return trying; + } + + // Not found, no idea... + return UNSPECIFIED_COLOR; +} + + +bool ColorIsLight( EDA_COLOR_T aColor ) +{ + const StructColors &c = g_ColorRefs[ColorGetBase( aColor )]; + int r = c.m_Red; + int g = c.m_Green; + int b = c.m_Blue; + return ((r * r) + (g * g) + (b * b)) > (128 * 128 * 3); +} + + +EDA_COLOR_T ColorFindNearest( const wxColour &aColor ) +{ + return ColorFindNearest( aColor.Red(), aColor.Green(), aColor.Blue() ); +} + + +EDA_COLOR_T ColorFindNearest( int aR, int aG, int aB ) +{ + EDA_COLOR_T candidate = BLACK; + + /* Find the 'nearest' color in the palette. This is fun. There is + a gazilion of metrics for the color space and no one of the + useful one is in the RGB color space. Who cares, this is a CAD, + not a photosomething... + + I hereby declare that the distance is the sum of the square of the + component difference. Think about the RGB color cube. Now get the + euclidean distance, but without the square root... for ordering + purposes it's the same, obviously. Also each component can't be + less of the target one, since I found this currently work better... + */ + int nearest_distance = 255 * 255 * 3 + 1; // Can't beat this + + for( EDA_COLOR_T trying = BLACK; trying < NBCOLORS; trying = NextColor(trying) ) + { + const StructColors &c = g_ColorRefs[trying]; + int distance = (aR - c.m_Red) * (aR - c.m_Red) + + (aG - c.m_Green) * (aG - c.m_Green) + + (aB - c.m_Blue) * (aB - c.m_Blue); + + if( distance < nearest_distance && c.m_Red >= aR && + c.m_Green >= aG && c.m_Blue >= aB ) + { + nearest_distance = distance; + candidate = trying; + } + } + + return candidate; +} + + +EDA_COLOR_T ColorMix( EDA_COLOR_T aColor1, EDA_COLOR_T aColor2 ) +{ + /* Memoization storage. This could be potentially called for each + * color merge so a cache is useful (there are few colours anyway) */ + static EDA_COLOR_T mix_cache[NBCOLORS][NBCOLORS]; + + // TODO how is alpha used? it's a mac only thing, I have no idea + aColor1 = ColorGetBase( aColor1 ); + aColor2 = ColorGetBase( aColor2 ); + + // First easy thing: a black gives always the other colour + if( aColor1 == BLACK ) + return aColor2; + + if( aColor2 == BLACK) + return aColor1; + + /* Now we are sure that black can't occur, so the rule is: + * BLACK means not computed yet. If we're lucky we already have + * an answer */ + EDA_COLOR_T candidate = mix_cache[aColor1][aColor2]; + + if( candidate != BLACK ) + return candidate; + + // Blend the two colors (i.e. OR the RGB values) + const StructColors &c1 = g_ColorRefs[aColor1]; + const StructColors &c2 = g_ColorRefs[aColor2]; + + // Ask the palette for the nearest color to the mix + wxColour mixed( c1.m_Red | c2.m_Red, + c1.m_Green | c2.m_Green, + c1.m_Blue | c2.m_Blue ); + candidate = ColorFindNearest( mixed ); + + /* Here, BLACK is *not* a good answer, since it would recompute the next time. + * Even theorically its not possible (with the current rules), but + * maybe the metric will change in the future */ + if( candidate == BLACK ) + candidate = DARKDARKGRAY; + + // Store the result in the cache. The operation is commutative, too + mix_cache[aColor1][aColor2] = candidate; + mix_cache[aColor2][aColor1] = candidate; + return candidate; +} + diff --git a/common/common.cpp b/common/common.cpp new file mode 100644 index 0000000..911d581 --- /dev/null +++ b/common/common.cpp @@ -0,0 +1,511 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2008-2015 Wayne Stambaugh <stambaughw@verizon.net> + * Copyright (C) 1992-2015 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 + */ + +/** + * @file common.cpp + */ + +#include <fctsys.h> +#include <gr_basic.h> +#include <trigo.h> +#include <wxstruct.h> +#include <base_struct.h> +#include <common.h> +#include <macros.h> +#include <build_version.h> +#include <confirm.h> +#include <base_units.h> +#include <reporter.h> + +#include <wx/process.h> +#include <wx/config.h> +#include <wx/utils.h> +#include <wx/stdpaths.h> + +#include <pgm_base.h> + + +/** + * Global variables definitions. + * + * TODO: All of these variables should be moved into the class were they + * are defined and used. Most of them probably belong in the + * application class. + */ + +bool g_ShowPageLimits = true; +EDA_UNITS_T g_UserUnit; +EDA_COLOR_T g_GhostColor; + + +/* Class LOCALE_IO + * is a class that can be instantiated within a scope in which you are expecting + * exceptions to be thrown. Its constructor sets a "C" locale, to read/print files + * with fp numbers. + * Its destructor insures that the default locale is restored if an exception + * is thrown, or not. + */ + +int LOCALE_IO::m_c_count = 0; + +LOCALE_IO::LOCALE_IO() +{ + wxASSERT_MSG( m_c_count >= 0, wxT( "LOCALE_IO::m_c_count mismanaged." ) ); + + // use thread safe, atomic operation + if( __sync_fetch_and_add( &m_c_count, 1 ) == 0 ) + { + // Store the user locale name, to restore this locale later, in dtor + m_user_locale = setlocale( LC_ALL, 0 ); + // Switch the locale to C locale, to read/write files with fp numbers + setlocale( LC_ALL, "C" ); + } +} + +LOCALE_IO::~LOCALE_IO() +{ + // use thread safe, atomic operation + if( __sync_sub_and_fetch( &m_c_count, 1 ) == 0 ) + { + // revert to the user locale + setlocale( LC_ALL, m_user_locale.c_str() ); + } + + wxASSERT_MSG( m_c_count >= 0, wxT( "LOCALE_IO::m_c_count mismanaged." ) ); +} + + +wxSize GetTextSize( const wxString& aSingleLine, wxWindow* aWindow ) +{ + wxCoord width; + wxCoord height; + + { + wxClientDC dc( aWindow ); + dc.SetFont( aWindow->GetFont() ); + dc.GetTextExtent( aSingleLine, &width, &height ); + } + + return wxSize( width, height ); +} + + +bool EnsureTextCtrlWidth( wxTextCtrl* aCtrl, const wxString* aString ) +{ + wxWindow* window = aCtrl->GetParent(); + + if( !window ) + window = aCtrl; + + wxString ctrlText; + + if( !aString ) + { + ctrlText = aCtrl->GetValue(); + aString = &ctrlText; + } + + wxSize textz = GetTextSize( *aString, window ); + wxSize ctrlz = aCtrl->GetSize(); + + if( ctrlz.GetWidth() < textz.GetWidth() + 10 ) + { + ctrlz.SetWidth( textz.GetWidth() + 10 ); + aCtrl->SetSizeHints( ctrlz ); + return true; + } + + return false; +} + + +wxString ReturnUnitSymbol( EDA_UNITS_T aUnit, const wxString& formatString ) +{ + wxString tmp; + wxString label; + + switch( aUnit ) + { + case INCHES: + tmp = _( "\"" ); + break; + + case MILLIMETRES: + tmp = _( "mm" ); + break; + + case UNSCALED_UNITS: + break; + + case DEGREES: + wxASSERT( false ); + break; + } + + if( formatString.IsEmpty() ) + return tmp; + + label.Printf( formatString, GetChars( tmp ) ); + + return label; +} + + +wxString GetUnitsLabel( EDA_UNITS_T aUnit ) +{ + wxString label; + + switch( aUnit ) + { + case INCHES: + label = _( "inches" ); + break; + + case MILLIMETRES: + label = _( "millimeters" ); + break; + + case UNSCALED_UNITS: + label = _( "units" ); + break; + + case DEGREES: + wxASSERT( false ); + break; + } + + return label; +} + + +wxString GetAbbreviatedUnitsLabel( EDA_UNITS_T aUnit ) +{ + wxString label; + + switch( aUnit ) + { + case INCHES: + label = _( "in" ); + break; + + case MILLIMETRES: + label = _( "mm" ); + break; + + case UNSCALED_UNITS: + break; + + case DEGREES: + label = _( "deg" ); + break; + + default: + label = wxT( "??" ); + break; + } + + return label; +} + + +void AddUnitSymbol( wxStaticText& Stext, EDA_UNITS_T aUnit ) +{ + wxString msg = Stext.GetLabel(); + + msg += ReturnUnitSymbol( aUnit ); + + Stext.SetLabel( msg ); +} + + +void wxStringSplit( const wxString& aText, wxArrayString& aStrings, wxChar aSplitter ) +{ + wxString tmp; + + for( unsigned ii = 0; ii < aText.Length(); ii++ ) + { + if( aText[ii] == aSplitter ) + { + aStrings.Add( tmp ); + tmp.Clear(); + } + + else + tmp << aText[ii]; + } + + if( !tmp.IsEmpty() ) + { + aStrings.Add( tmp ); + } +} + + +int ProcessExecute( const wxString& aCommandLine, int aFlags, wxProcess *callback ) +{ + return wxExecute( aCommandLine, aFlags, callback ); +} + + +time_t GetNewTimeStamp() +{ + static time_t oldTimeStamp; + time_t newTimeStamp; + + newTimeStamp = time( NULL ); + + if( newTimeStamp <= oldTimeStamp ) + newTimeStamp = oldTimeStamp + 1; + + oldTimeStamp = newTimeStamp; + + return newTimeStamp; +} + + +double RoundTo0( double x, double precision ) +{ + assert( precision != 0 ); + + long long ix = KiROUND( x * precision ); + + if ( x < 0.0 ) + ix = -ix; + + int remainder = ix % 10; // remainder is in precision mm + + if( remainder <= 2 ) + ix -= remainder; // truncate to the near number + else if( remainder >= 8 ) + ix += 10 - remainder; // round to near number + + if ( x < 0 ) + ix = -ix; + + return (double) ix / precision; +} + + +wxConfigBase* GetNewConfig( const wxString& aProgName ) +{ + wxConfigBase* cfg = 0; + wxFileName configname; + configname.AssignDir( GetKicadConfigPath() ); + configname.SetFullName( aProgName ); + + cfg = new wxFileConfig( wxT( "" ), wxT( "" ), configname.GetFullPath() ); + return cfg; +} + +wxString GetKicadLockFilePath() +{ + wxFileName lockpath; + lockpath.AssignDir( wxGetHomeDir() ); // Default wx behavior + +#if defined( __WXMAC__ ) + // In OSX use the standard per user cache directory + lockpath.AppendDir( wxT( "Library" ) ); + lockpath.AppendDir( wxT( "Caches" ) ); + lockpath.AppendDir( wxT( "kicad" ) ); +#elif defined( __UNIX__ ) + wxString envstr; + // Try first the standard XDG_RUNTIME_DIR, falling back to XDG_CACHE_HOME + if( wxGetEnv( wxT( "XDG_RUNTIME_DIR" ), &envstr ) && !envstr.IsEmpty() ) + { + lockpath.AssignDir( envstr ); + } + else if( wxGetEnv( wxT( "XDG_CACHE_HOME" ), &envstr ) && !envstr.IsEmpty() ) + { + lockpath.AssignDir( envstr ); + } + else + { + // If all fails, just use ~/.cache + lockpath.AppendDir( wxT( ".cache" ) ); + } + + lockpath.AppendDir( wxT( "kicad" ) ); +#endif + +#if defined( __WXMAC__ ) || defined( __UNIX__ ) + if( !lockpath.DirExists() ) + { + // Lockfiles should be only readable by the user + lockpath.Mkdir( 0700, wxPATH_MKDIR_FULL ); + } +#endif + return lockpath.GetPath(); +} + + +wxString GetKicadConfigPath() +{ + wxFileName cfgpath; + + // From the wxWidgets wxStandardPaths::GetUserConfigDir() help: + // Unix: ~ (the home directory) + // Windows: "C:\Documents and Settings\username\Application Data" + // Mac: ~/Library/Preferences + cfgpath.AssignDir( wxStandardPaths::Get().GetUserConfigDir() ); + +#if !defined( __WINDOWS__ ) && !defined( __WXMAC__ ) + wxString envstr; + + if( !wxGetEnv( wxT( "XDG_CONFIG_HOME" ), &envstr ) || envstr.IsEmpty() ) + { + // XDG_CONFIG_HOME is not set, so use the fallback + cfgpath.AppendDir( wxT( ".config" ) ); + } + else + { + // Override the assignment above with XDG_CONFIG_HOME + cfgpath.AssignDir( envstr ); + } +#endif + + cfgpath.AppendDir( wxT( "kicad" ) ); + + if( !cfgpath.DirExists() ) + { + cfgpath.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ); + } + + return cfgpath.GetPath(); +} + + +#include <ki_mutex.h> +const wxString ExpandEnvVarSubstitutions( const wxString& aString ) +{ + // wxGetenv( wchar_t* ) is not re-entrant on linux. + // Put a lock on multithreaded use of wxGetenv( wchar_t* ), called from wxEpandEnvVars(), + static MUTEX getenv_mutex; + + MUTLOCK lock( getenv_mutex ); + + // We reserve the right to do this another way, by providing our own member + // function. + return wxExpandEnvVars( aString ); +} + +bool EnsureFileDirectoryExists( wxFileName* aTargetFullFileName, + const wxString& aBaseFilename, + REPORTER* aReporter ) +{ + wxString msg; + wxString baseFilePath = wxFileName( aBaseFilename ).GetPath(); + + // make aTargetFullFileName path, which is relative to aBaseFilename path (if it is not + // already an absolute path) absolute: + if( !aTargetFullFileName->MakeAbsolute( baseFilePath ) ) + { + if( aReporter ) + { + msg.Printf( _( "Cannot make path '%s' absolute with respect to '%s'." ), + GetChars( aTargetFullFileName->GetPath() ), + GetChars( baseFilePath ) ); + aReporter->Report( msg, REPORTER::RPT_ERROR ); + } + + return false; + } + + // Ensure the path of aTargetFullFileName exists, and create it if needed: + wxString outputPath( aTargetFullFileName->GetPath() ); + + if( !wxFileName::DirExists( outputPath ) ) + { + if( wxMkdir( outputPath ) ) + { + if( aReporter ) + { + msg.Printf( _( "Output directory '%s' created.\n" ), GetChars( outputPath ) ); + aReporter->Report( msg, REPORTER::RPT_INFO ); + return true; + } + } + else + { + if( aReporter ) + { + msg.Printf( _( "Cannot create output directory '%s'.\n" ), + GetChars( outputPath ) ); + aReporter->Report( msg, REPORTER::RPT_ERROR ); + } + + return false; + } + } + + return true; +} + + +#ifdef __WXMAC__ +wxString GetOSXKicadUserDataDir() +{ + // According to wxWidgets documentation for GetUserDataDir: + // Mac: ~/Library/Application Support/appname + wxFileName udir( wxStandardPaths::Get().GetUserDataDir(), wxEmptyString ); + + // Since appname is different if started via launcher or standalone binary + // map all to "kicad" here + udir.RemoveLastDir(); + udir.AppendDir( wxT( "kicad" ) ); + + return udir.GetPath(); +} + + +wxString GetOSXKicadMachineDataDir() +{ + return wxT( "/Library/Application Support/kicad" ); +} + + +wxString GetOSXKicadDataDir() +{ + // According to wxWidgets documentation for GetDataDir: + // Mac: appname.app/Contents/SharedSupport bundle subdirectory + wxFileName ddir( wxStandardPaths::Get().GetDataDir(), wxEmptyString ); + + // This must be mapped to main bundle for everything but kicad.app + const wxArrayString dirs = ddir.GetDirs(); + if( dirs[dirs.GetCount() - 3] != wxT( "kicad.app" ) ) + { + // Bundle structure resp. current path is + // kicad.app/Contents/Applications/<standalone>.app/Contents/SharedSupport + // and will be mapped to + // kicad.app/Contents/SharedSupprt + ddir.RemoveLastDir(); + ddir.RemoveLastDir(); + ddir.RemoveLastDir(); + ddir.RemoveLastDir(); + ddir.AppendDir( wxT( "SharedSupport" ) ); + } + + return ddir.GetPath(); +} +#endif diff --git a/common/common_plotDXF_functions.cpp b/common/common_plotDXF_functions.cpp new file mode 100644 index 0000000..5544ab1 --- /dev/null +++ b/common/common_plotDXF_functions.cpp @@ -0,0 +1,815 @@ +/** + * @file common_plotDXF_functions.cpp + * @brief KiCad: Common plot DXF Routines. + */ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 KiCad Developers, see CHANGELOG.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 <fctsys.h> +#include <gr_basic.h> +#include <trigo.h> +#include <wxstruct.h> +#include <base_struct.h> +#include <plot_common.h> +#include <macros.h> +#include <kicad_string.h> +#include <convert_basic_shapes_to_polygon.h> + +/** + * Oblique angle for DXF native text + * (I don't remember if 15 degrees is the ISO value... it looks nice anyway) + */ +static const double DXF_OBLIQUE_ANGLE = 15; + +/** + * Set the scale/position for the DXF plot + * The DXF engine doesn't support line widths and mirroring. The output + * coordinate system is in the first quadrant (in mm) + */ +void DXF_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil, + double aScale, bool aMirror ) +{ + plotOffset = aOffset; + plotScale = aScale; + + /* DXF paper is 'virtual' so there is no need of a paper size. + Also this way we can handle the aux origin which can be useful + (for example when aligning to a mechanical drawing) */ + paperSize.x = 0; + paperSize.y = 0; + + /* Like paper size DXF units are abstract too. Anyway there is a + * system variable (MEASUREMENT) which will be set to 1 to indicate + * metric units */ + m_IUsPerDecimil = aIusPerDecimil; + iuPerDeviceUnit = 1.0 / aIusPerDecimil; // Gives a DXF in decimils + iuPerDeviceUnit *= 0.00254; // ... now in mm + + SetDefaultLineWidth( 0 ); // No line width on DXF + m_plotMirror = false; // No mirroring on DXF + m_currentColor = BLACK; +} + +/** + * Opens the DXF plot with a skeleton header + */ +bool DXF_PLOTTER::StartPlot() +{ + wxASSERT( outputFile ); + + // DXF HEADER - Boilerplate + // Defines the minimum for drawing i.e. the angle system and the + // continuous linetype + fputs( " 0\n" + "SECTION\n" + " 2\n" + "HEADER\n" + " 9\n" + "$ANGBASE\n" + " 50\n" + "0.0\n" + " 9\n" + "$ANGDIR\n" + " 70\n" + " 1\n" + " 9\n" + "$MEASUREMENT\n" + " 70\n" + "0\n" + " 0\n" // This means 'metric units' + "ENDSEC\n" + " 0\n" + "SECTION\n" + " 2\n" + "TABLES\n" + " 0\n" + "TABLE\n" + " 2\n" + "LTYPE\n" + " 70\n" + "1\n" + " 0\n" + "LTYPE\n" + " 2\n" + "CONTINUOUS\n" + " 70\n" + "0\n" + " 3\n" + "Solid line\n" + " 72\n" + "65\n" + " 73\n" + "0\n" + " 40\n" + "0.0\n" + " 0\n" + "ENDTAB\n", + outputFile ); + + // Text styles table + // Defines 4 text styles, one for each bold/italic combination + fputs( " 0\n" + "TABLE\n" + " 2\n" + "STYLE\n" + " 70\n" + "4\n", outputFile ); + + static const char *style_name[4] = {"KICAD", "KICADB", "KICADI", "KICADBI"}; + for(int i = 0; i < 4; i++ ) + { + fprintf( outputFile, + " 0\n" + "STYLE\n" + " 2\n" + "%s\n" // Style name + " 70\n" + "0\n" // Standard flags + " 40\n" + "0\n" // Non-fixed height text + " 41\n" + "1\n" // Width factor (base) + " 42\n" + "1\n" // Last height (mandatory) + " 50\n" + "%g\n" // Oblique angle + " 71\n" + "0\n" // Generation flags (default) + " 3\n" + // The standard ISO font (when kicad is build with it + // the dxf text in acad matches *perfectly*) + "isocp.shx\n", // Font name (when not bigfont) + // Apply a 15 degree angle to italic text + style_name[i], i < 2 ? 0 : DXF_OBLIQUE_ANGLE ); + } + + + // Layer table - one layer per color + fprintf( outputFile, + " 0\n" + "ENDTAB\n" + " 0\n" + "TABLE\n" + " 2\n" + "LAYER\n" + " 70\n" + "%d\n", NBCOLORS ); + + /* The layer/colors palette. The acad/DXF palette is divided in 3 zones: + + - The primary colors (1 - 9) + - An HSV zone (10-250, 5 values x 2 saturations x 10 hues + - Greys (251 - 255) + + There is *no* black... the white does it on paper, usually, and + anyway it depends on the plotter configuration, since DXF colors + are meant to be logical only (they represent *both* line color and + width); later version with plot styles only complicate the matter! + + As usual, brown and magenta/purple are difficult to place since + they are actually variations of other colors. + */ + static const struct { + const char *name; + int color; + } dxf_layer[NBCOLORS] = { + { "BLACK", 7 }, // In DXF, color 7 is *both* white and black! + { "GRAY1", 251 }, + { "GRAY2", 8 }, + { "GRAY3", 9 }, + { "WHITE", 7 }, + { "LYELLOW", 51 }, + { "BLUE1", 178 }, + { "GREEN1", 98 }, + { "CYAN1", 138 }, + { "RED1", 18 }, + { "MAGENTA1", 228 }, + { "BROWN1", 58 }, + { "BLUE2", 5 }, + { "GREEN2", 3 }, + { "CYAN2", 4 }, + { "RED2", 1 }, + { "MAGENTA2", 6 }, + { "BROWN2", 54 }, + { "BLUE3", 171 }, + { "GREEN3", 91 }, + { "CYAN3", 131 }, + { "RED3", 11 }, + { "MAGENTA3", 221 }, + { "YELLOW3", 2 }, + { "BLUE4", 5 }, + { "GREEN4", 3 }, + { "CYAN4", 4 }, + { "RED4", 1 }, + { "MAGENTA4", 6 }, + { "YELLOW4", 2 } + }; + + for( EDA_COLOR_T i = BLACK; i < NBCOLORS; i = NextColor(i) ) + { + fprintf( outputFile, + " 0\n" + "LAYER\n" + " 2\n" + "%s\n" // Layer name + " 70\n" + "0\n" // Standard flags + " 62\n" + "%d\n" // Color number + " 6\n" + "CONTINUOUS\n",// Linetype name + dxf_layer[i].name, dxf_layer[i].color ); + } + + // End of layer table, begin entities + fputs( " 0\n" + "ENDTAB\n" + " 0\n" + "ENDSEC\n" + " 0\n" + "SECTION\n" + " 2\n" + "ENTITIES\n", outputFile ); + + return true; +} + + +bool DXF_PLOTTER::EndPlot() +{ + wxASSERT( outputFile ); + + // DXF FOOTER + fputs( " 0\n" + "ENDSEC\n" + " 0\n" + "EOF\n", outputFile ); + fclose( outputFile ); + outputFile = NULL; + + return true; +} + + +/** + * The DXF exporter handles 'colors' as layers... + */ +void DXF_PLOTTER::SetColor( EDA_COLOR_T color ) +{ + if( ( color >= 0 && colorMode ) + || ( color == BLACK ) + || ( color == WHITE ) ) + { + m_currentColor = color; + } + else + m_currentColor = BLACK; +} + +/** + * DXF rectangle: fill not supported + */ +void DXF_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width ) +{ + MoveTo( p1 ); + LineTo( wxPoint( p1.x, p2.y ) ); + LineTo( wxPoint( p2.x, p2.y ) ); + LineTo( wxPoint( p2.x, p1.y ) ); + FinishTo( wxPoint( p1.x, p1.y ) ); +} + + +/** + * DXF circle: full functionality; it even does 'fills' drawing a + * circle with a dual-arc polyline wide as the radius. + * + * I could use this trick to do other filled primitives + */ +void DXF_PLOTTER::Circle( const wxPoint& centre, int diameter, FILL_T fill, int width ) +{ + wxASSERT( outputFile ); + double radius = userToDeviceSize( diameter / 2 ); + DPOINT centre_dev = userToDeviceCoordinates( centre ); + if( radius > 0 ) + { + wxString cname( ColorGetName( m_currentColor ) ); + if (!fill) + { + fprintf( outputFile, "0\nCIRCLE\n8\n%s\n10\n%g\n20\n%g\n40\n%g\n", + TO_UTF8( cname ), + centre_dev.x, centre_dev.y, radius ); + } + if (fill == FILLED_SHAPE) + { + double r = radius*0.5; + fprintf( outputFile, "0\nPOLYLINE\n"); + fprintf( outputFile, "8\n%s\n66\n1\n70\n1\n", TO_UTF8( cname )); + fprintf( outputFile, "40\n%g\n41\n%g\n", radius, radius); + fprintf( outputFile, "0\nVERTEX\n8\n%s\n", TO_UTF8( cname )); + fprintf( outputFile, "10\n%g\n 20\n%g\n42\n1.0\n", + centre_dev.x-r, centre_dev.y ); + fprintf( outputFile, "0\nVERTEX\n8\n%s\n", TO_UTF8( cname )); + fprintf( outputFile, "10\n%g\n 20\n%g\n42\n1.0\n", + centre_dev.x+r, centre_dev.y ); + fprintf( outputFile, "0\nSEQEND\n"); + } + } +} + + +/** + * DXF polygon: doesn't fill it but at least it close the filled ones + * DXF does not know thick outline. + * It does not know thhick segments, therefore filled polygons with thick outline + * are converted to inflated polygon by aWidth/2 + */ +void DXF_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList, + FILL_T aFill, int aWidth) +{ + if( aCornerList.size() <= 1 ) + return; + + unsigned last = aCornerList.size() - 1; + + // Plot outlines with lines (thickness = 0) to define the polygon + if( aWidth <= 0 ) + { + MoveTo( aCornerList[0] ); + + for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) + LineTo( aCornerList[ii] ); + + // Close polygon if 'fill' requested + if( aFill ) + { + if( aCornerList[last] != aCornerList[0] ) + LineTo( aCornerList[0] ); + } + + PenFinish(); + + return; + } + + + // if the polygon outline has thickness, and is not filled + // (i.e. is a polyline) plot outlines with thick segments + if( aWidth > 0 && !aFill ) + { + MoveTo( aCornerList[0] ); + + for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) + ThickSegment( aCornerList[ii-1], aCornerList[ii], + aWidth, FILLED ); + + return; + } + + // The polygon outline has thickness, and is filled + // Build and plot the polygon which contains the initial + // polygon and its thick outline + SHAPE_POLY_SET bufferOutline; + SHAPE_POLY_SET bufferPolybase; + const int circleToSegmentsCount = 16; + + bufferPolybase.NewOutline(); + + // enter outline as polygon: + for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) + { + TransformRoundedEndsSegmentToPolygon( bufferOutline, + aCornerList[ii-1], aCornerList[ii], circleToSegmentsCount, aWidth ); + } + + // enter the initial polygon: + for( unsigned ii = 0; ii < aCornerList.size(); ii++ ) + { + bufferPolybase.Append( aCornerList[ii] ); + } + + // Merge polygons to build the polygon which contains the initial + // polygon and its thick outline + + bufferPolybase.BooleanAdd( bufferOutline ); // create the outline which contains thick outline + bufferPolybase.Fracture(); + + + if( bufferPolybase.OutlineCount() < 1 ) // should not happen + return; + + const SHAPE_LINE_CHAIN& path = bufferPolybase.COutline( 0 ); + + if( path.PointCount() < 2 ) // should not happen + return; + + // Now, output the final polygon to DXF file: + last = path.PointCount() - 1; + VECTOR2I point = path.CPoint( 0 ); + + wxPoint startPoint( point.x, point.y ); + MoveTo( startPoint ); + + for( int ii = 1; ii < path.PointCount(); ii++ ) + { + point = path.CPoint( ii ); + LineTo( wxPoint( point.x, point.y ) ); + } + + // Close polygon, if needed + point = path.CPoint( last ); + wxPoint endPoint( point.x, point.y ); + + if( endPoint != startPoint ) + LineTo( startPoint ); + + PenFinish(); +} + + +void DXF_PLOTTER::PenTo( const wxPoint& pos, char plume ) +{ + wxASSERT( outputFile ); + if( plume == 'Z' ) + { + return; + } + DPOINT pos_dev = userToDeviceCoordinates( pos ); + DPOINT pen_lastpos_dev = userToDeviceCoordinates( penLastpos ); + + if( penLastpos != pos && plume == 'D' ) + { + // DXF LINE + wxString cname( ColorGetName( m_currentColor ) ); + fprintf( outputFile, "0\nLINE\n8\n%s\n10\n%g\n20\n%g\n11\n%g\n21\n%g\n", + TO_UTF8( cname ), + pen_lastpos_dev.x, pen_lastpos_dev.y, pos_dev.x, pos_dev.y ); + } + penLastpos = pos; +} + + +/** + * Dashed lines are not (yet) supported by DXF_PLOTTER + */ +void DXF_PLOTTER::SetDash( bool dashed ) +{ + // NOP for now +} + + +void DXF_PLOTTER::ThickSegment( const wxPoint& aStart, const wxPoint& aEnd, int aWidth, + EDA_DRAW_MODE_T aPlotMode ) +{ + MoveTo( aStart ); + FinishTo( aEnd ); +} + +/* Plot an arc in DXF format + * Filling is not supported + */ +void DXF_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius, + FILL_T fill, int width ) +{ + wxASSERT( outputFile ); + + if( radius <= 0 ) + return; + + // In DXF, arcs are drawn CCW. + // In Kicad, arcs are CW or CCW + // If StAngle > EndAngle, it is CW. So transform it to CCW + if( StAngle > EndAngle ) + { + std::swap( StAngle, EndAngle ); + } + + DPOINT centre_dev = userToDeviceCoordinates( centre ); + double radius_dev = userToDeviceSize( radius ); + + // Emit a DXF ARC entity + wxString cname( ColorGetName( m_currentColor ) ); + fprintf( outputFile, + "0\nARC\n8\n%s\n10\n%g\n20\n%g\n40\n%g\n50\n%g\n51\n%g\n", + TO_UTF8( cname ), + centre_dev.x, centre_dev.y, radius_dev, + StAngle / 10.0, EndAngle / 10.0 ); +} + +/** + * DXF oval pad: always done in sketch mode + */ +void DXF_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, double orient, + EDA_DRAW_MODE_T trace_mode ) +{ + wxSize size( aSize ); + + /* The chip is reduced to an oval tablet with size.y > size.x + * (Oval vertical orientation 0) */ + if( size.x > size.y ) + { + std::swap( size.x, size.y ); + orient = AddAngles( orient, 900 ); + } + + sketchOval( pos, size, orient, -1 ); +} + + +/** + * DXF round pad: always done in sketch mode; it could be filled but it isn't + * pretty if other kinds of pad aren't... + */ +void DXF_PLOTTER::FlashPadCircle( const wxPoint& pos, int diametre, + EDA_DRAW_MODE_T trace_mode ) +{ + Circle( pos, diametre, NO_FILL ); +} + + +/** + * DXF rectangular pad: alwayd done in sketch mode + */ +void DXF_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& padsize, + double orient, EDA_DRAW_MODE_T trace_mode ) +{ + wxASSERT( outputFile ); + wxSize size; + int ox, oy, fx, fy; + + size.x = padsize.x / 2; + size.y = padsize.y / 2; + + if( size.x < 0 ) + size.x = 0; + if( size.y < 0 ) + size.y = 0; + + // If a dimension is zero, the trace is reduced to 1 line + if( size.x == 0 ) + { + ox = pos.x; + oy = pos.y - size.y; + RotatePoint( &ox, &oy, pos.x, pos.y, orient ); + fx = pos.x; + fy = pos.y + size.y; + RotatePoint( &fx, &fy, pos.x, pos.y, orient ); + MoveTo( wxPoint( ox, oy ) ); + FinishTo( wxPoint( fx, fy ) ); + return; + } + if( size.y == 0 ) + { + ox = pos.x - size.x; + oy = pos.y; + RotatePoint( &ox, &oy, pos.x, pos.y, orient ); + fx = pos.x + size.x; + fy = pos.y; + RotatePoint( &fx, &fy, pos.x, pos.y, orient ); + MoveTo( wxPoint( ox, oy ) ); + FinishTo( wxPoint( fx, fy ) ); + return; + } + + ox = pos.x - size.x; + oy = pos.y - size.y; + RotatePoint( &ox, &oy, pos.x, pos.y, orient ); + MoveTo( wxPoint( ox, oy ) ); + + fx = pos.x - size.x; + fy = pos.y + size.y; + RotatePoint( &fx, &fy, pos.x, pos.y, orient ); + LineTo( wxPoint( fx, fy ) ); + + fx = pos.x + size.x; + fy = pos.y + size.y; + RotatePoint( &fx, &fy, pos.x, pos.y, orient ); + LineTo( wxPoint( fx, fy ) ); + + fx = pos.x + size.x; + fy = pos.y - size.y; + RotatePoint( &fx, &fy, pos.x, pos.y, orient ); + LineTo( wxPoint( fx, fy ) ); + + FinishTo( wxPoint( ox, oy ) ); +} + + +/** + * DXF trapezoidal pad: only sketch mode is supported + */ +void DXF_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint *aCorners, + double aPadOrient, EDA_DRAW_MODE_T aTrace_Mode ) +{ + wxPoint coord[4]; /* coord actual corners of a trapezoidal trace */ + + for( int ii = 0; ii < 4; ii++ ) + { + coord[ii] = aCorners[ii]; + RotatePoint( &coord[ii], aPadOrient ); + coord[ii] += aPadPos; + } + + // Plot edge: + MoveTo( coord[0] ); + LineTo( coord[1] ); + LineTo( coord[2] ); + LineTo( coord[3] ); + FinishTo( coord[0] ); +} + +/** + * Checks if a given string contains non-ASCII characters. + * FIXME: the performance of this code is really poor, but in this case it can be + * acceptable because the plot operation is not called very often. + * @param string String to check + * @return true if it contains some non-ASCII character, false if all characters are + * inside ASCII range (<=255). + */ +bool containsNonAsciiChars( const wxString& string ) +{ + for( unsigned i = 0; i < string.length(); i++ ) + { + wchar_t ch = string[i]; + if( ch > 255 ) + return true; + } + return false; +} + +void DXF_PLOTTER::Text( const wxPoint& aPos, + enum EDA_COLOR_T aColor, + const wxString& aText, + double aOrient, + const wxSize& aSize, + enum EDA_TEXT_HJUSTIFY_T aH_justify, + enum EDA_TEXT_VJUSTIFY_T aV_justify, + int aWidth, + bool aItalic, + bool aBold, + bool aMultilineAllowed ) +{ + // Fix me: see how to use DXF text mode for multiline texts + if( aMultilineAllowed && !aText.Contains( wxT( "\n" ) ) ) + aMultilineAllowed = false; // the text has only one line. + + if( textAsLines || containsNonAsciiChars( aText ) || aMultilineAllowed ) + { + // output text as graphics. + // Perhaps multiline texts could be handled as DXF text entity + // but I do not want spend time about this (JPC) + PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, + aWidth, aItalic, aBold, aMultilineAllowed ); + } + else + { + /* Emit text as a text entity. This loses formatting and shape but it's + more useful as a CAD object */ + DPOINT origin_dev = userToDeviceCoordinates( aPos ); + SetColor( aColor ); + wxString cname( ColorGetName( m_currentColor ) ); + DPOINT size_dev = userToDeviceSize( aSize ); + int h_code = 0, v_code = 0; + switch( aH_justify ) + { + case GR_TEXT_HJUSTIFY_LEFT: + h_code = 0; + break; + case GR_TEXT_HJUSTIFY_CENTER: + h_code = 1; + break; + case GR_TEXT_HJUSTIFY_RIGHT: + h_code = 2; + break; + } + switch( aV_justify ) + { + case GR_TEXT_VJUSTIFY_TOP: + v_code = 3; + break; + case GR_TEXT_VJUSTIFY_CENTER: + v_code = 2; + break; + case GR_TEXT_VJUSTIFY_BOTTOM: + v_code = 1; + break; + } + + // Position, size, rotation and alignment + // The two alignment point usages is somewhat idiot (see the DXF ref) + // Anyway since we don't use the fit/aligned options, they're the same + fprintf( outputFile, + " 0\n" + "TEXT\n" + " 7\n" + "%s\n" // Text style + " 8\n" + "%s\n" // Layer name + " 10\n" + "%g\n" // First point X + " 11\n" + "%g\n" // Second point X + " 20\n" + "%g\n" // First point Y + " 21\n" + "%g\n" // Second point Y + " 40\n" + "%g\n" // Text height + " 41\n" + "%g\n" // Width factor + " 50\n" + "%g\n" // Rotation + " 51\n" + "%g\n" // Oblique angle + " 71\n" + "%d\n" // Mirror flags + " 72\n" + "%d\n" // H alignment + " 73\n" + "%d\n", // V alignment + aBold ? (aItalic ? "KICADBI" : "KICADB") + : (aItalic ? "KICADI" : "KICAD"), + TO_UTF8( cname ), + origin_dev.x, origin_dev.x, + origin_dev.y, origin_dev.y, + size_dev.y, fabs( size_dev.x / size_dev.y ), + aOrient / 10.0, + aItalic ? DXF_OBLIQUE_ANGLE : 0, + size_dev.x < 0 ? 2 : 0, // X mirror flag + h_code, v_code ); + + /* There are two issue in emitting the text: + - Our overline character (~) must be converted to the appropriate + control sequence %%O or %%o + - Text encoding in DXF is more or less unspecified since depends on + the DXF declared version, the acad version reading it *and* some + system variables to be put in the header handled only by newer acads + Also before R15 unicode simply is not supported (you need to use + bigfonts which are a massive PITA). Common denominator solution: + use Latin1 (and however someone could choke on it, anyway). Sorry + for the extended latin people. If somewant want to try fixing this + recent version seems to use UTF-8 (and not UCS2 like the rest of + Windows) + + XXX Actually there is a *third* issue: older DXF formats are limited + to 255 bytes records (it was later raised to 2048); since I'm lazy + and text so long is not probable I just don't implement this rule. + If someone is interested in fixing this, you have to emit the first + partial lines with group code 3 (max 250 bytes each) and then finish + with a group code 1 (less than 250 bytes). The DXF refs explains it + in no more details... + */ + + bool overlining = false; + fputs( " 1\n", outputFile ); + for( unsigned i = 0; i < aText.length(); i++ ) + { + /* Here I do a bad thing: writing the output one byte at a time! + but today I'm lazy and I have no idea on how to coerce a Unicode + wxString to spit out latin1 encoded text ... + + Atleast stdio is *supposed* to do output buffering, so there is + hope is not too slow */ + wchar_t ch = aText[i]; + if( ch > 255 ) + { + // I can't encode this... + putc( '?', outputFile ); + } + else + { + if( ch == '~' ) + { + // Handle the overline toggle + fputs( overlining ? "%%o" : "%%O", outputFile ); + overlining = !overlining; + } + else + { + putc( ch, outputFile ); + } + } + } + putc( '\n', outputFile ); + } +} + diff --git a/common/common_plotGERBER_functions.cpp b/common/common_plotGERBER_functions.cpp new file mode 100644 index 0000000..914388d --- /dev/null +++ b/common/common_plotGERBER_functions.cpp @@ -0,0 +1,594 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.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 + */ + +/** + * @file common_plotGERBER_functions.cpp + * @brief Common GERBER plot routines. + */ + +#include <fctsys.h> +#include <gr_basic.h> +#include <trigo.h> +#include <wxstruct.h> +#include <base_struct.h> +#include <common.h> +#include <plot_common.h> +#include <macros.h> +#include <kicad_string.h> + +#include <build_version.h> + + +GERBER_PLOTTER::GERBER_PLOTTER() +{ + workFile = 0; + finalFile = 0; + currentAperture = apertures.end(); + + // number of digits after the point (number of digits of the mantissa + // Be carefull: the Gerber coordinates are stored in an integer + // so 6 digits (inches) or 5 digits (mm) is a good value + // To avoid overflow, 7 digits (inches) or 6 digits is a max. + // with lower values than 6 digits (inches) or 5 digits (mm), + // Creating self-intersecting polygons from non-intersecting polygons + // happen easily. + m_gerberUnitInch = false; + m_gerberUnitFmt = 6; +} + + +void GERBER_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil, + double aScale, bool aMirror ) +{ + wxASSERT( aMirror == false ); + m_plotMirror = false; + plotOffset = aOffset; + wxASSERT( aScale == 1 ); // aScale parameter is not used in Gerber + plotScale = 1; // Plot scale is *always* 1.0 + + m_IUsPerDecimil = aIusPerDecimil; + // gives now a default value to iuPerDeviceUnit (because the units of the caller is now known) + // which could be modified later by calling SetGerberCoordinatesFormat() + iuPerDeviceUnit = pow( 10.0, m_gerberUnitFmt ) / ( m_IUsPerDecimil * 10000.0 ); + + // We don't handle the filmbox, and it's more useful to keep the + // origin at the origin + paperSize.x = 0; + paperSize.y = 0; + SetDefaultLineWidth( 100 * aIusPerDecimil ); // Arbitrary default +} + + +void GERBER_PLOTTER::SetGerberCoordinatesFormat( int aResolution, bool aUseInches ) +{ + m_gerberUnitInch = aUseInches; + m_gerberUnitFmt = aResolution; + + iuPerDeviceUnit = pow( 10.0, m_gerberUnitFmt ) / ( m_IUsPerDecimil * 10000.0 ); + + if( ! m_gerberUnitInch ) + iuPerDeviceUnit *= 25.4; // gerber output in mm +} + + +void GERBER_PLOTTER::emitDcode( const DPOINT& pt, int dcode ) +{ + + fprintf( outputFile, "X%dY%dD%02d*\n", + KiROUND( pt.x ), KiROUND( pt.y ), dcode ); +} + + +bool GERBER_PLOTTER::StartPlot() +{ + wxASSERT( outputFile ); + + finalFile = outputFile; // the actual gerber file will be created later + + // Create a temporary filename to store gerber file + // note tmpfile() does not work under Vista and W7 in user mode + m_workFilename = filename + wxT(".tmp"); + workFile = wxFopen( m_workFilename, wxT( "wt" )); + outputFile = workFile; + wxASSERT( outputFile ); + + if( outputFile == NULL ) + return false; + + for( unsigned ii = 0; ii < m_headerExtraLines.GetCount(); ii++ ) + { + if( ! m_headerExtraLines[ii].IsEmpty() ) + fprintf( outputFile, "%s\n", TO_UTF8( m_headerExtraLines[ii] ) ); + } + + // Set coordinate format to 3.6 or 4.5 absolute, leading zero omitted + // the number of digits for the integer part of coordintes is needed + // in gerber format, but is not very important when omitting leading zeros + // It is fixed here to 3 (inch) or 4 (mm), but is not actually used + int leadingDigitCount = m_gerberUnitInch ? 3 : 4; + + fprintf( outputFile, "%%FSLAX%d%dY%d%d*%%\n", + leadingDigitCount, m_gerberUnitFmt, + leadingDigitCount, m_gerberUnitFmt ); + fprintf( outputFile, + "G04 Gerber Fmt %d.%d, Leading zero omitted, Abs format (unit %s)*\n", + leadingDigitCount, m_gerberUnitFmt, + m_gerberUnitInch ? "inch" : "mm" ); + + wxString Title = creator + wxT( " " ) + GetBuildVersion(); + fprintf( outputFile, "G04 Created by KiCad (%s) date %s*\n", + TO_UTF8( Title ), TO_UTF8( DateAndTime() ) ); + + /* Mass parameter: unit = INCHES/MM */ + if( m_gerberUnitInch ) + fputs( "%MOIN*%\n", outputFile ); + else + fputs( "%MOMM*%\n", outputFile ); + + // Be sure the usual dark polarity is selected: + fputs( "%LPD*%\n", outputFile ); + + // Specify linear interpol (G01): + fputs( "G01*\n", outputFile ); + + fputs( "G04 APERTURE LIST*\n", outputFile ); + /* Select the default aperture */ + SetCurrentLineWidth( -1 ); + + return true; +} + + +bool GERBER_PLOTTER::EndPlot() +{ + char line[1024]; + wxString msg; + + wxASSERT( outputFile ); + + /* Outfile is actually a temporary file i.e. workFile */ + fputs( "M02*\n", outputFile ); + fflush( outputFile ); + + fclose( workFile ); + workFile = wxFopen( m_workFilename, wxT( "rt" )); + wxASSERT( workFile ); + outputFile = finalFile; + + // Placement of apertures in RS274X + while( fgets( line, 1024, workFile ) ) + { + fputs( line, outputFile ); + + if( strcmp( strtok( line, "\n\r" ), "G04 APERTURE LIST*" ) == 0 ) + { + writeApertureList(); + fputs( "G04 APERTURE END LIST*\n", outputFile ); + } + } + + fclose( workFile ); + fclose( finalFile ); + ::wxRemoveFile( m_workFilename ); + outputFile = 0; + + return true; +} + + +void GERBER_PLOTTER::SetDefaultLineWidth( int width ) +{ + defaultPenWidth = width; + currentAperture = apertures.end(); +} + + +void GERBER_PLOTTER::SetCurrentLineWidth( int width ) +{ + int pen_width; + + if( width > 0 ) + pen_width = width; + else + pen_width = defaultPenWidth; + + selectAperture( wxSize( pen_width, pen_width ), APERTURE::Plotting ); + currentPenWidth = pen_width; +} + + +std::vector<APERTURE>::iterator GERBER_PLOTTER::getAperture( const wxSize& size, + APERTURE::APERTURE_TYPE type ) +{ + int last_D_code = 9; + + // Search an existing aperture + std::vector<APERTURE>::iterator tool = apertures.begin(); + + while( tool != apertures.end() ) + { + last_D_code = tool->DCode; + + if( (tool->Type == type) && (tool->Size == size) ) + return tool; + + tool++; + } + + // Allocate a new aperture + APERTURE new_tool; + new_tool.Size = size; + new_tool.Type = type; + new_tool.DCode = last_D_code + 1; + apertures.push_back( new_tool ); + return apertures.end() - 1; +} + + +void GERBER_PLOTTER::selectAperture( const wxSize& size, + APERTURE::APERTURE_TYPE type ) +{ + wxASSERT( outputFile ); + + if( ( currentAperture == apertures.end() ) + || ( currentAperture->Type != type ) + || ( currentAperture->Size != size ) ) + { + // Pick an existing aperture or create a new one + currentAperture = getAperture( size, type ); + fprintf( outputFile, "D%d*\n", currentAperture->DCode ); + } +} + + +void GERBER_PLOTTER::writeApertureList() +{ + wxASSERT( outputFile ); + char cbuf[1024]; + + // Init + for( std::vector<APERTURE>::iterator tool = apertures.begin(); + tool != apertures.end(); tool++ ) + { + // apertude sizes are in inch or mm, regardless the + // coordinates format + double fscale = 0.0001 * plotScale / m_IUsPerDecimil; // inches + + if(! m_gerberUnitInch ) + fscale *= 25.4; // size in mm + + char* text = cbuf + sprintf( cbuf, "%%ADD%d", tool->DCode ); + + /* Please note: the Gerber specs for mass parameters say that + exponential syntax is *not* allowed and the decimal point should + also be always inserted. So the %g format is ruled out, but %f is fine + (the # modifier forces the decimal point). Sadly the %f formatter + can't remove trailing zeros but thats not a problem, since nothing + forbid it (the file is only slightly longer) */ + + switch( tool->Type ) + { + case APERTURE::Circle: + sprintf( text, "C,%#f*%%\n", tool->Size.x * fscale ); + break; + + case APERTURE::Rect: + sprintf( text, "R,%#fX%#f*%%\n", + tool->Size.x * fscale, + tool->Size.y * fscale ); + break; + + case APERTURE::Plotting: + sprintf( text, "C,%#f*%%\n", tool->Size.x * fscale ); + break; + + case APERTURE::Oval: + sprintf( text, "O,%#fX%#f*%%\n", + tool->Size.x * fscale, + tool->Size.y * fscale ); + break; + } + + fputs( cbuf, outputFile ); + } +} + + +void GERBER_PLOTTER::PenTo( const wxPoint& aPos, char plume ) +{ + DPOINT pos_dev = userToDeviceCoordinates( aPos ); + + switch( plume ) + { + case 'Z': + break; + + case 'U': + emitDcode( pos_dev, 2 ); + break; + + case 'D': + emitDcode( pos_dev, 1 ); + } + + penState = plume; +} + + +void GERBER_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width ) +{ + std::vector< wxPoint > cornerList; + + // Build corners list + cornerList.push_back( p1 ); + wxPoint corner(p1.x, p2.y); + cornerList.push_back( corner ); + cornerList.push_back( p2 ); + corner.x = p2.x; + corner.y = p1.y; + cornerList.push_back( corner ); + cornerList.push_back( p1 ); + + PlotPoly( cornerList, fill, width ); +} + + +void GERBER_PLOTTER::Circle( const wxPoint& aCenter, int aDiameter, FILL_T aFill, int aWidth ) +{ + Arc( aCenter, 0, 3600, aDiameter / 2, aFill, aWidth ); +} + + +void GERBER_PLOTTER::Arc( const wxPoint& aCenter, double aStAngle, double aEndAngle, + int aRadius, FILL_T aFill, int aWidth ) +{ + wxASSERT( outputFile ); + wxPoint start, end; + start.x = aCenter.x + KiROUND( cosdecideg( aRadius, aStAngle ) ); + start.y = aCenter.y - KiROUND( sindecideg( aRadius, aStAngle ) ); + SetCurrentLineWidth( aWidth ); + MoveTo( start ); + end.x = aCenter.x + KiROUND( cosdecideg( aRadius, aEndAngle ) ); + end.y = aCenter.y - KiROUND( sindecideg( aRadius, aEndAngle ) ); + DPOINT devEnd = userToDeviceCoordinates( end ); + DPOINT devCenter = userToDeviceCoordinates( aCenter ) - userToDeviceCoordinates( start ); + + fprintf( outputFile, "G75*\n" ); // Multiquadrant mode + + if( aStAngle < aEndAngle ) + fprintf( outputFile, "G03" ); + else + fprintf( outputFile, "G02" ); + + fprintf( outputFile, "X%dY%dI%dJ%dD01*\n", + KiROUND( devEnd.x ), KiROUND( devEnd.y ), + KiROUND( devCenter.x ), KiROUND( devCenter.y ) ); + fprintf( outputFile, "G01*\n" ); // Back to linear interp. +} + + +void GERBER_PLOTTER:: PlotPoly( const std::vector< wxPoint >& aCornerList, + FILL_T aFill, int aWidth ) +{ + if( aCornerList.size() <= 1 ) + return; + + // Gerber format does not know filled polygons with thick outline + // Therefore, to plot a filled polygon with outline having a thickness, + // one should plot outline as thick segments + + SetCurrentLineWidth( aWidth ); + + if( aFill ) + { + fputs( "G36*\n", outputFile ); + + MoveTo( aCornerList[0] ); + + for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) + LineTo( aCornerList[ii] ); + + FinishTo( aCornerList[0] ); + fputs( "G37*\n", outputFile ); + } + + if( aWidth > 0 ) + { + MoveTo( aCornerList[0] ); + + for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) + LineTo( aCornerList[ii] ); + + // Ensure the thick outline is closed for filled polygons + // (if not filled, could be only a polyline) + if( aFill && ( aCornerList[aCornerList.size()-1] != aCornerList[0] ) ) + LineTo( aCornerList[0] ); + + PenFinish(); + } +} + + +void GERBER_PLOTTER::FlashPadCircle( const wxPoint& pos, int diametre, EDA_DRAW_MODE_T trace_mode ) +{ + wxSize size( diametre, diametre ); + + if( trace_mode == SKETCH ) + { + SetCurrentLineWidth( -1 ); + Circle( pos, diametre - currentPenWidth, NO_FILL ); + } + else + { + DPOINT pos_dev = userToDeviceCoordinates( pos ); + selectAperture( size, APERTURE::Circle ); + emitDcode( pos_dev, 3 ); + } +} + + +void GERBER_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, double orient, + EDA_DRAW_MODE_T trace_mode ) +{ + int x0, y0, x1, y1, delta; + wxSize size( aSize ); + + /* Plot a flashed shape. */ + if( ( orient == 0 || orient == 900 || orient == 1800 || orient == 2700 ) + && trace_mode == FILLED ) + { + if( orient == 900 || orient == 2700 ) /* orientation turned 90 deg. */ + std::swap( size.x, size.y ); + + DPOINT pos_dev = userToDeviceCoordinates( pos ); + selectAperture( size, APERTURE::Oval ); + emitDcode( pos_dev, 3 ); + } + else /* Plot pad as a segment. */ + { + if( size.x > size.y ) + { + std::swap( size.x, size.y ); + + if( orient < 2700 ) + orient += 900; + else + orient -= 2700; + } + + if( trace_mode == FILLED ) + { + /* XXX to do: use an aperture macro to declare the rotated pad */ + /* The pad is reduced to an oval with dy > dx */ + delta = size.y - size.x; + x0 = 0; + y0 = -delta / 2; + x1 = 0; + y1 = delta / 2; + RotatePoint( &x0, &y0, orient ); + RotatePoint( &x1, &y1, orient ); + ThickSegment( wxPoint( pos.x + x0, pos.y + y0 ), + wxPoint( pos.x + x1, pos.y + y1 ), + size.x, trace_mode ); + } + else + { + sketchOval( pos, size, orient, -1 ); + } + } +} + + +void GERBER_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& aSize, + double orient, EDA_DRAW_MODE_T trace_mode ) + +{ + wxSize size( aSize ); + + // Plot as an aperture flash + switch( int( orient ) ) + { + case 900: + case 2700: // rotation of 90 degrees or 270 swaps sizes + std::swap( size.x, size.y ); + + // Pass through + case 0: + case 1800: + if( trace_mode == SKETCH ) + { + SetCurrentLineWidth( -1 ); + Rect( wxPoint( pos.x - (size.x - currentPenWidth) / 2, + pos.y - (size.y - currentPenWidth) / 2 ), + wxPoint( pos.x + (size.x - currentPenWidth) / 2, + pos.y + (size.y - currentPenWidth) / 2 ), + NO_FILL ); + } + else + { + DPOINT pos_dev = userToDeviceCoordinates( pos ); + selectAperture( size, APERTURE::Rect ); + emitDcode( pos_dev, 3 ); + } + break; + + default: // plot pad shape as polygon + { + // XXX to do: use an aperture macro to declare the rotated pad + wxPoint coord[4]; + // coord[0] is assumed the lower left + // coord[1] is assumed the upper left + // coord[2] is assumed the upper right + // coord[3] is assumed the lower right + + /* Trace the outline. */ + coord[0].x = -size.x/2; // lower left + coord[0].y = size.y/2; + coord[1].x = -size.x/2; // upper left + coord[1].y = -size.y/2; + coord[2].x = size.x/2; // upper right + coord[2].y = -size.y/2; + coord[3].x = size.x/2; // lower right + coord[3].y = size.y/2; + + FlashPadTrapez( pos, coord, orient, trace_mode ); + } + break; + } +} + + +void GERBER_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCorners, + double aPadOrient, EDA_DRAW_MODE_T aTrace_Mode ) + +{ + // XXX to do: use an aperture macro to declare the pad + // polygon corners list + std::vector< wxPoint > cornerList; + + for( int ii = 0; ii < 4; ii++ ) + cornerList.push_back( aCorners[ii] ); + + // Draw the polygon and fill the interior as required + for( unsigned ii = 0; ii < 4; ii++ ) + { + RotatePoint( &cornerList[ii], aPadOrient ); + cornerList[ii] += aPadPos; + } + + // Close the polygon + cornerList.push_back( cornerList[0] ); + + SetCurrentLineWidth( -1 ); + PlotPoly( cornerList, aTrace_Mode==FILLED ? FILLED_SHAPE : NO_FILL ); +} + + +void GERBER_PLOTTER::SetLayerPolarity( bool aPositive ) +{ + if( aPositive ) + fprintf( outputFile, "%%LPD*%%\n" ); + else + fprintf( outputFile, "%%LPC*%%\n" ); +} diff --git a/common/common_plotHPGL_functions.cpp b/common/common_plotHPGL_functions.cpp new file mode 100644 index 0000000..ba99b67 --- /dev/null +++ b/common/common_plotHPGL_functions.cpp @@ -0,0 +1,740 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.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 + */ + +/** + * @file common_plotHPGL_functions.cpp + * @brief KiCad: Common plot HPGL Routines + * Filled primitive are not supported, but some could be using HPGL/2 + * Since this plot engine is mostly intended for import in external programs, + * sadly HPGL/2 isn't supported a lot... some of the primitives use overlapped + * strokes to fill the shape + */ + +/* Some HPGL commands: + * Note: the HPGL unit is 25 micrometers + * All commands MUST be terminated by a semi-colon or a linefeed. + * Spaces can NOT be substituted for required commas in the syntax of a command. + * + * + * AA (Arc Absolute): Angle is a floating point # (requires non integer value) + * Draws an arc with the center at (X,Y). + * A positive angle creates a counter-clockwise arc. + * If the chord angle is specified, + * this will be the number of degrees used for stepping around the arc. + * If no value is given then a default value of five degrees is used. + * AA x, y, a {,b}; + * + * AR (Arc Relative): + * AR Dx, Dy, a {, b}; + * + * CA (Alternate Character Set): + * CA {n}; + * + * CI (Circle): + * CI r {,b}; + * + * CP (Character Plot): + * CP {h, v}; + * h [-127.9999 .. 127.9999] Anzahl der Zeichen horizontal + * v [-127.9999 .. 127.9999] Anzahl der Zeichen vertikal + * + * CS (Standard Character Set): + * CS {n}; + * + * DR (Relative Direction for Label Text): + * DR s, a; + * + * DI (Absolute Direction for Label Text): + * DI {s, a}; + * + * DT (Define Terminator - this character becomes unavailable except to terminate a label string. + * Default is ^C control-C): + * DT t; + * + * EA (rEctangle Absolute - Unfilled, from current position to diagonal x,y): + * EA x, y; + * + * ER (rEctangle Relative - Unfilled, from current position to diagonal x,y): + * ER x,y; + * + * FT (Fill Type): + * FT {s {,l {a}}}; + * + * IM (Input Mask): + * IM {f}; + * + * IN (Initialize): This command instructs the controller to begin processing the HPGL plot file. + * Without this, the commands in the file are received but never executed. + * If multiple IN s are found during execution of the file, + * the controller performs a Pause/Cancel operation. + * All motion from the previous job, yet to be executed, is lost, + * and the new information is executed. + * IN; + * + * IP Input P1 and P2: + * IP {P1x, P1y {, P2x, P2y}}; + * + * IW (Input Window): + * IW {XUL, YUL, XOR, YOR}; + * + * LB (Label): + * LB c1 .. cn t; + * + * PA (Plot Absolute): Moves to an absolute HPGL position and sets absolute mode for + * future PU and PD commands. If no arguments follow the command, + * only absolute mode is set. + * PA {x1, y1 {{PU|PD|,} ..., ..., xn, yn}}; + * P1x, P1y, P2x, P2y [Integer in ASCII] + * + * PD (Pen Down): Executes <current pen> pen then moves to the requested position + * if one is specified. This position is dependent on whether absolute + * or relative mode is set. This command performs no motion in 3-D mode, + * but the outputs and feedrates are affected. + * PD {x, y}; + * + * PR (Plot Relative): Moves to the relative position specified and sets relative mode + * for future PU and PD commands. + * If no arguments follow the command, only relative mode is set. + * PR {Dx1, Dy1 {{PU|PD|,} ..., ..., Dxn, Dyn}}; + * + * PS (Paper Size): + * PS {n}; + * + * PT (Pen Thickness): + * PT {l}; + * + * PU (Pen Up): Executes <current pen> pen then moves to the requested position + * if one is specified. This position is dependent on whether absolute + * or relative mode is set. + * This command performs no motion in 3-D mode, but the outputs + * and feedrates are affected. + * PU {x, y}; + * + * RA (Rectangle Absolute - Filled, from current position to diagonal x,y): + * RA x, y; + * + * RO (Rotate Coordinate System): + * RO; + * + * RR (Rectangle Relative - Filled, from current position to diagonal x,y): + * RR x, y; + * + * SA (Select Alternate Set): + * SA; + * + * SC (Scale): + * SC {Xmin, Xmax, Ymin, Ymax}; + * + * SI (Absolute Character Size): + * SI b, h; + * b [-127.9999 .. 127.9999, keine 0] + * h [-127.9999 .. 127.9999, keine 0] + * + * SL (Character Slant): + * SL {a}; + * a [-3.5 .. -0.5, 0.5 .. 3.5] +* + * SP (Select Pen): Selects a new pen or tool for use. + * If no pen number or a value of zero is given, + * the controller performs an EOF (end of file command). + * Once an EOF is performed, no motion is executed, + * until a new IN command is received. + * SP n; + * + * SR (Relative Character Size): + * SR {b, h}; + * b [-127.9999 .. 127.9999, keine 0] + * h [-127.9999 .. 127.9999, keine 0] + * + * SS (Select Standard Set): + * SS; + * + * TL (Tick Length): + * TL {tp {, tm}}; + * + * UC (User Defined Character): + * UC {i,} x1, y1, {i,} x2, y2, ... {i,} xn, yn; + * + * VS (Velocity Select): + * VS {v {, n}}; + * v [1 .. 40] + * n [1 .. 8, je nach Ausstattung] + * + * XT (X Tick): + * XT; + * + * YT (Y Tick): + * YT; + */ + + +#include <fctsys.h> +#include <gr_basic.h> +#include <trigo.h> +#include <wxstruct.h> +#include <base_struct.h> +#include <plot_common.h> +#include <macros.h> +#include <kicad_string.h> + +// HPGL scale factor (1 PLU = 1/40mm = 25 micrometers) +static const double PLUsPERDECIMIL = 0.102041; + +HPGL_PLOTTER::HPGL_PLOTTER() +{ + SetPenSpeed( 40 ); // Default pen speed = 40 cm/s; Pen speed is *always* in cm + SetPenNumber( 1 ); // Default pen num = 1 + SetPenDiameter( 0.0 ); + SetPenOverlap( 0.0 ); +} + +void HPGL_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil, + double aScale, bool aMirror ) +{ + plotOffset = aOffset; + plotScale = aScale; + m_IUsPerDecimil = aIusPerDecimil; + iuPerDeviceUnit = PLUsPERDECIMIL / aIusPerDecimil; + /* Compute the paper size in IUs */ + paperSize = pageInfo.GetSizeMils(); + paperSize.x *= 10.0 * aIusPerDecimil; + paperSize.y *= 10.0 * aIusPerDecimil; + SetDefaultLineWidth( 0 ); // HPGL has pen sizes instead + m_plotMirror = aMirror; +} + + +/** + * At the start of the HPGL plot pen speed and number are requested + */ +bool HPGL_PLOTTER::StartPlot() +{ + wxASSERT( outputFile ); + fprintf( outputFile, "IN;VS%d;PU;PA;SP%d;\n", penSpeed, penNumber ); + return true; +} + + +/** + * HPGL end of plot: pen return and release + */ +bool HPGL_PLOTTER::EndPlot() +{ + wxASSERT( outputFile ); + fputs( "PU;PA;SP0;\n", outputFile ); + fclose( outputFile ); + outputFile = NULL; + return true; +} + + +/** + * HPGL rectangle: fill not supported + */ +void HPGL_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width ) +{ + wxASSERT( outputFile ); + DPOINT p2dev = userToDeviceCoordinates( p2 ); + MoveTo( p1 ); + fprintf( outputFile, "EA %.0f,%.0f;\n", p2dev.x, p2dev.y ); + PenFinish(); +} + + +/** + * HPGL circle: fill not supported + */ +void HPGL_PLOTTER::Circle( const wxPoint& centre, int diameter, FILL_T fill, + int width ) +{ + wxASSERT( outputFile ); + double radius = userToDeviceSize( diameter / 2 ); + + if( radius > 0 ) + { + MoveTo( centre ); + fprintf( outputFile, "CI %g;\n", radius ); + PenFinish(); + } +} + + +/** + * HPGL polygon: fill not supported (but closed, at least) + */ +void HPGL_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList, + FILL_T aFill, int aWidth ) +{ + if( aCornerList.size() <= 1 ) + return; + + SetCurrentLineWidth( aWidth ); + + MoveTo( aCornerList[0] ); + + for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) + LineTo( aCornerList[ii] ); + + // Close polygon if filled. + if( aFill ) + { + int ii = aCornerList.size() - 1; + + if( aCornerList[ii] != aCornerList[0] ) + LineTo( aCornerList[0] ); + } + + PenFinish(); +} + + +/** + * Pen control logic (remove redundant pen activations) + */ +void HPGL_PLOTTER::penControl( char plume ) +{ + wxASSERT( outputFile ); + + switch( plume ) + { + case 'U': + + if( penState != 'U' ) + { + fputs( "PU;", outputFile ); + penState = 'U'; + } + + break; + + case 'D': + + if( penState != 'D' ) + { + fputs( "PD;", outputFile ); + penState = 'D'; + } + + break; + + case 'Z': + fputs( "PU;", outputFile ); + penState = 'U'; + penLastpos.x = -1; + penLastpos.y = -1; + break; + } +} + + +void HPGL_PLOTTER::PenTo( const wxPoint& pos, char plume ) +{ + wxASSERT( outputFile ); + + if( plume == 'Z' ) + { + penControl( 'Z' ); + return; + } + + penControl( plume ); + DPOINT pos_dev = userToDeviceCoordinates( pos ); + + if( penLastpos != pos ) + fprintf( outputFile, "PA %.0f,%.0f;\n", pos_dev.x, pos_dev.y ); + + penLastpos = pos; +} + + +/** + * HPGL supports dashed lines + */ +void HPGL_PLOTTER::SetDash( bool dashed ) +{ + wxASSERT( outputFile ); + + if( dashed ) + fputs( "LI 2;\n", outputFile ); + else + fputs( "LI;\n", outputFile ); +} + + +void HPGL_PLOTTER::ThickSegment( const wxPoint& start, const wxPoint& end, + int width, EDA_DRAW_MODE_T tracemode ) +{ + wxPoint center; + wxSize size; + + // Suppress overlap if pen is too big + if( penDiameter >= width ) + { + MoveTo( start ); + FinishTo( end ); + } + else + segmentAsOval( start, end, width, tracemode ); +} + + +/* Plot an arc: + * Center = center coord + * Stangl, endAngle = angle of beginning and end + * Radius = radius of the arc + * Command + * PU PY x, y; PD start_arc_X AA, start_arc_Y, angle, NbSegm; PU; + * Or PU PY x, y; PD start_arc_X AA, start_arc_Y, angle, PU; + */ +void HPGL_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius, + FILL_T fill, int width ) +{ + wxASSERT( outputFile ); + double angle; + + if( radius <= 0 ) + return; + + DPOINT centre_dev = userToDeviceCoordinates( centre ); + + if( m_plotMirror ) + angle = StAngle - EndAngle; + else + angle = EndAngle - StAngle; + + NORMALIZE_ANGLE_180( angle ); + angle /= 10; + + // Calculate arc start point: + wxPoint cmap; + cmap.x = centre.x + KiROUND( cosdecideg( radius, StAngle ) ); + cmap.y = centre.y - KiROUND( sindecideg( radius, StAngle ) ); + DPOINT cmap_dev = userToDeviceCoordinates( cmap ); + + fprintf( outputFile, + "PU;PA %.0f,%.0f;PD;AA %.0f,%.0f,", + cmap_dev.x, cmap_dev.y, + centre_dev.x, centre_dev.y ); + fprintf( outputFile, "%.0f", angle ); + fprintf( outputFile, ";PU;\n" ); + PenFinish(); +} + + +/* Plot oval pad. + */ +void HPGL_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, double orient, + EDA_DRAW_MODE_T trace_mode ) +{ + int deltaxy, cx, cy; + wxSize size( aSize ); + + /* The pad is reduced to an oval with size.y > size.x + * (Oval vertical orientation 0) + */ + if( size.x > size.y ) + { + std::swap( size.x, size.y ); + orient = AddAngles( orient, 900 ); + } + + deltaxy = size.y - size.x; // distance between centers of the oval + + if( trace_mode == FILLED ) + { + FlashPadRect( pos, wxSize( size.x, deltaxy + KiROUND( penDiameter ) ), + orient, trace_mode ); + cx = 0; cy = deltaxy / 2; + RotatePoint( &cx, &cy, orient ); + FlashPadCircle( wxPoint( cx + pos.x, cy + pos.y ), size.x, trace_mode ); + cx = 0; cy = -deltaxy / 2; + RotatePoint( &cx, &cy, orient ); + FlashPadCircle( wxPoint( cx + pos.x, cy + pos.y ), size.x, trace_mode ); + } + else // Plot in SKETCH mode. + { + sketchOval( pos, size, orient, KiROUND( penDiameter ) ); + } +} + + +/* Plot round pad or via. + */ +void HPGL_PLOTTER::FlashPadCircle( const wxPoint& pos, int diametre, + EDA_DRAW_MODE_T trace_mode ) +{ + wxASSERT( outputFile ); + DPOINT pos_dev = userToDeviceCoordinates( pos ); + + int delta = KiROUND( penDiameter - penOverlap ); + int radius = ( diametre - KiROUND( penDiameter ) ) / 2; + + if( radius < 0 ) + radius = 0; + + double rsize = userToDeviceSize( radius ); + + fprintf( outputFile, "PA %.0f,%.0f;CI %.0f;\n", + pos_dev.x, pos_dev.y, rsize ); + + if( trace_mode == FILLED ) // Plot in filled mode. + { + if( delta > 0 ) + { + while( (radius -= delta ) >= 0 ) + { + rsize = userToDeviceSize( radius ); + fprintf( outputFile, "PA %.0f,%.0f;CI %.0f;\n", + pos_dev.x, pos_dev.y, rsize ); + } + } + } + + PenFinish(); +} + + +void HPGL_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& padsize, + double orient, EDA_DRAW_MODE_T trace_mode ) +{ + wxASSERT( outputFile ); + wxSize size; + int delta; + int ox, oy, fx, fy; + + size.x = padsize.x / 2; + size.y = padsize.y / 2; + + size.x = (padsize.x - (int) penDiameter) / 2; + size.y = (padsize.y - (int) penDiameter) / 2; + + if( size.x < 0 ) + size.x = 0; + + if( size.y < 0 ) + size.y = 0; + + // If a dimension is zero, the trace is reduced to 1 line. + if( size.x == 0 ) + { + ox = pos.x; + oy = pos.y - size.y; + RotatePoint( &ox, &oy, pos.x, pos.y, orient ); + fx = pos.x; + fy = pos.y + size.y; + RotatePoint( &fx, &fy, pos.x, pos.y, orient ); + MoveTo( wxPoint( ox, oy ) ); + FinishTo( wxPoint( fx, fy ) ); + return; + } + + if( size.y == 0 ) + { + ox = pos.x - size.x; + oy = pos.y; + RotatePoint( &ox, &oy, pos.x, pos.y, orient ); + fx = pos.x + size.x; + fy = pos.y; + RotatePoint( &fx, &fy, pos.x, pos.y, orient ); + MoveTo( wxPoint( ox, oy ) ); + FinishTo( wxPoint( fx, fy ) ); + return; + } + + ox = pos.x - size.x; + oy = pos.y - size.y; + RotatePoint( &ox, &oy, pos.x, pos.y, orient ); + MoveTo( wxPoint( ox, oy ) ); + + fx = pos.x - size.x; + fy = pos.y + size.y; + RotatePoint( &fx, &fy, pos.x, pos.y, orient ); + LineTo( wxPoint( fx, fy ) ); + + fx = pos.x + size.x; + fy = pos.y + size.y; + RotatePoint( &fx, &fy, pos.x, pos.y, orient ); + LineTo( wxPoint( fx, fy ) ); + + fx = pos.x + size.x; + fy = pos.y - size.y; + RotatePoint( &fx, &fy, pos.x, pos.y, orient ); + LineTo( wxPoint( fx, fy ) ); + + FinishTo( wxPoint( ox, oy ) ); + + if( trace_mode == FILLED ) + { + // Plot in filled mode. + delta = (int) (penDiameter - penOverlap); + + if( delta > 0 ) + while( (size.x > 0) && (size.y > 0) ) + { + size.x -= delta; + size.y -= delta; + + if( size.x < 0 ) + size.x = 0; + + if( size.y < 0 ) + size.y = 0; + + ox = pos.x - size.x; + oy = pos.y - size.y; + RotatePoint( &ox, &oy, pos.x, pos.y, orient ); + MoveTo( wxPoint( ox, oy ) ); + + fx = pos.x - size.x; + fy = pos.y + size.y; + RotatePoint( &fx, &fy, pos.x, pos.y, orient ); + LineTo( wxPoint( fx, fy ) ); + + fx = pos.x + size.x; + fy = pos.y + size.y; + RotatePoint( &fx, &fy, pos.x, pos.y, orient ); + LineTo( wxPoint( fx, fy ) ); + + fx = pos.x + size.x; + fy = pos.y - size.y; + RotatePoint( &fx, &fy, pos.x, pos.y, orient ); + LineTo( wxPoint( fx, fy ) ); + + FinishTo( wxPoint( ox, oy ) ); + } + + + } +} + + +void HPGL_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCorners, + double aPadOrient, EDA_DRAW_MODE_T aTrace_Mode ) +{ + wxPoint polygone[4]; // coordinates of corners relatives to the pad + wxPoint coord[4]; // absolute coordinates of corners (coordinates in plotter space) + int move; + + move = KiROUND( penDiameter ); + + for( int ii = 0; ii < 4; ii++ ) + polygone[ii] = aCorners[ii]; + + // polygone[0] is assumed the lower left + // polygone[1] is assumed the upper left + // polygone[2] is assumed the upper right + // polygone[3] is assumed the lower right + + // Plot the outline: + for( int ii = 0; ii < 4; ii++ ) + { + coord[ii] = polygone[ii]; + RotatePoint( &coord[ii], aPadOrient ); + coord[ii] += aPadPos; + } + + MoveTo( coord[0] ); + LineTo( coord[1] ); + LineTo( coord[2] ); + LineTo( coord[3] ); + FinishTo( coord[0] ); + + // Fill shape: + if( aTrace_Mode == FILLED ) + { + // TODO: replace this par the HPGL plot polygon. + int jj; + // Fill the shape + move = KiROUND( penDiameter - penOverlap ); + // Calculate fill height. + + if( polygone[0].y == polygone[3].y ) // Horizontal + { + jj = polygone[3].y - (int) ( penDiameter + ( 2 * penOverlap ) ); + } + else // vertical + { + jj = polygone[3].x - (int) ( penDiameter + ( 2 * penOverlap ) ); + } + + // Calculation of dd = number of segments was traced to fill. + int delta = (int) ( penDiameter - penOverlap ); + + if( delta ) + jj = jj / delta; + else + jj = 0; + + // Trace the outline. + for( ; jj > 0; jj-- ) + { + polygone[0].x += move; + polygone[0].y -= move; + polygone[1].x += move; + polygone[1].y += move; + polygone[2].x -= move; + polygone[2].y += move; + polygone[3].x -= move; + polygone[3].y -= move; + + // Test for crossed vertexes. + if( polygone[0].x > polygone[3].x ) /* X axis intersection on + * vertexes 0 and 3 */ + { + polygone[0].x = polygone[3].x = 0; + } + + if( polygone[1].x > polygone[2].x ) /* X axis intersection on + * vertexes 1 and 2 */ + { + polygone[1].x = polygone[2].x = 0; + } + + if( polygone[1].y > polygone[0].y ) /* Y axis intersection on + * vertexes 0 and 1 */ + { + polygone[0].y = polygone[1].y = 0; + } + + if( polygone[2].y > polygone[3].y ) /* Y axis intersection on + * vertexes 2 and 3 */ + { + polygone[2].y = polygone[3].y = 0; + } + + for( int ii = 0; ii < 4; ii++ ) + { + coord[ii] = polygone[ii]; + RotatePoint( &coord[ii], aPadOrient ); + coord[ii] += aPadPos; + } + + MoveTo( coord[0] ); + LineTo( coord[1] ); + LineTo( coord[2] ); + LineTo( coord[3] ); + FinishTo( coord[0] ); + } + } +} diff --git a/common/common_plotPDF_functions.cpp b/common/common_plotPDF_functions.cpp new file mode 100644 index 0000000..a95e7de --- /dev/null +++ b/common/common_plotPDF_functions.cpp @@ -0,0 +1,834 @@ +/** + * @file common_plotPDF_functions.cpp + * @brief Kicad: Common plot PDF Routines + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 1992-2012 Lorenzo Marcantonio, l.marcantonio@logossrl.com + * Copyright (C) 1992-2017 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 <fctsys.h> +#include <pgm_base.h> +#include <trigo.h> +#include <wxstruct.h> +#include <base_struct.h> +#include <common.h> +#include <plot_common.h> +#include <macros.h> +#include <kicad_string.h> +#include <wx/zstream.h> +#include <wx/mstream.h> + + +/* + * Open or create the plot file aFullFilename + * return true if success, false if the file cannot be created/opened + * + * Opens the PDF file in binary mode + */ +bool PDF_PLOTTER::OpenFile( const wxString& aFullFilename ) +{ + filename = aFullFilename; + + wxASSERT( !outputFile ); + + // Open the PDF file in binary mode + outputFile = wxFopen( filename, wxT( "wb" ) ); + + if( outputFile == NULL ) + return false ; + + return true; +} + +void PDF_PLOTTER::SetPageSettings( const PAGE_INFO& aPageSettings ) +{ + wxASSERT( !workFile ); + pageInfo = aPageSettings; +} + +void PDF_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil, + double aScale, bool aMirror ) +{ + m_plotMirror = aMirror; + plotOffset = aOffset; + plotScale = aScale; + m_IUsPerDecimil = aIusPerDecimil; + + // The CTM is set to 1 user unit per decimil + iuPerDeviceUnit = 1.0 / aIusPerDecimil; + + SetDefaultLineWidth( 100 / iuPerDeviceUnit ); // arbitrary default + + /* The paper size in this engined is handled page by page + Look in the StartPage function */ +} + + +/** + * Pen width setting for PDF. Since the specs *explicitly* says that a 0 + * width is a bad thing to use (since it results in 1 pixel traces), we + * convert such requests to the minimal width (like 1) + * Note pen width = 0 is used in plot polygons to plot filled polygons with + * no outline thickness + * use in this case pen width = 1 does not actally change the polygon + */ +void PDF_PLOTTER::SetCurrentLineWidth( int width ) +{ + wxASSERT( workFile ); + int pen_width; + + if( width > 0 ) + pen_width = width; + else if( width == 0 ) + pen_width = 1; + else + pen_width = defaultPenWidth; + + if( pen_width != currentPenWidth ) + fprintf( workFile, "%g w\n", + userToDeviceSize( pen_width ) ); + + currentPenWidth = pen_width; +} + + +/** + * PDF supports colors fully. It actually has distinct fill and pen colors, + * but we set both at the same time. + * + * XXX Keeping them divided could result in a minor optimization in + * eeschema filled shapes, but would propagate to all the other plot + * engines. Also arcs are filled as pies but only the arc is stroked so + * it would be difficult to handle anyway. + */ +void PDF_PLOTTER::emitSetRGBColor( double r, double g, double b ) +{ + wxASSERT( workFile ); + fprintf( workFile, "%g %g %g rg %g %g %g RG\n", + r, g, b, r, g, b ); +} + +/** + * PDF supports dashed lines + */ +void PDF_PLOTTER::SetDash( bool dashed ) +{ + wxASSERT( workFile ); + if( dashed ) + fprintf( workFile, "[%d %d] 0 d\n", + (int) GetDashMarkLenIU(), (int) GetDashGapLenIU() ); + else + fputs( "[] 0 d\n", workFile ); +} + + +/** + * Rectangles in PDF. Supported by the native operator + */ +void PDF_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width ) +{ + wxASSERT( workFile ); + DPOINT p1_dev = userToDeviceCoordinates( p1 ); + DPOINT p2_dev = userToDeviceCoordinates( p2 ); + + SetCurrentLineWidth( width ); + fprintf( workFile, "%g %g %g %g re %c\n", p1_dev.x, p1_dev.y, + p2_dev.x - p1_dev.x, p2_dev.y - p1_dev.y, + fill == NO_FILL ? 'S' : 'B' ); +} + + +/** + * Circle drawing for PDF. They're approximated by curves, but fill is supported + */ +void PDF_PLOTTER::Circle( const wxPoint& pos, int diametre, FILL_T aFill, int width ) +{ + wxASSERT( workFile ); + DPOINT pos_dev = userToDeviceCoordinates( pos ); + double radius = userToDeviceSize( diametre / 2.0 ); + + /* OK. Here's a trick. PDF doesn't support circles or circular angles, that's + a fact. You'll have to do with cubic beziers. These *can't* represent + circular arcs (NURBS can, beziers don't). But there is a widely known + approximation which is really good + */ + + SetCurrentLineWidth( width ); + double magic = radius * 0.551784; // You don't want to know where this come from + + // This is the convex hull for the bezier approximated circle + fprintf( workFile, "%g %g m " + "%g %g %g %g %g %g c " + "%g %g %g %g %g %g c " + "%g %g %g %g %g %g c " + "%g %g %g %g %g %g c %c\n", + pos_dev.x - radius, pos_dev.y, + + pos_dev.x - radius, pos_dev.y + magic, + pos_dev.x - magic, pos_dev.y + radius, + pos_dev.x, pos_dev.y + radius, + + pos_dev.x + magic, pos_dev.y + radius, + pos_dev.x + radius, pos_dev.y + magic, + pos_dev.x + radius, pos_dev.y, + + pos_dev.x + radius, pos_dev.y - magic, + pos_dev.x + magic, pos_dev.y - radius, + pos_dev.x, pos_dev.y - radius, + + pos_dev.x - magic, pos_dev.y - radius, + pos_dev.x - radius, pos_dev.y - magic, + pos_dev.x - radius, pos_dev.y, + + aFill == NO_FILL ? 's' : 'b' ); +} + + +/** + * The PDF engine can't directly plot arcs, it uses the base emulation. + * So no filled arcs (not a great loss... ) + */ +void PDF_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius, + FILL_T fill, int width ) +{ + wxASSERT( workFile ); + if( radius <= 0 ) + return; + + /* Arcs are not so easily approximated by beziers (in the general case), + so we approximate them in the old way */ + wxPoint start, end; + const int delta = 50; // increment (in 0.1 degrees) to draw circles + + if( StAngle > EndAngle ) + std::swap( StAngle, EndAngle ); + + SetCurrentLineWidth( width ); + + // Usual trig arc plotting routine... + start.x = centre.x + KiROUND( cosdecideg( radius, -StAngle ) ); + start.y = centre.y + KiROUND( sindecideg( radius, -StAngle ) ); + DPOINT pos_dev = userToDeviceCoordinates( start ); + fprintf( workFile, "%g %g m ", pos_dev.x, pos_dev.y ); + for( int ii = StAngle + delta; ii < EndAngle; ii += delta ) + { + end.x = centre.x + KiROUND( cosdecideg( radius, -ii ) ); + end.y = centre.y + KiROUND( sindecideg( radius, -ii ) ); + pos_dev = userToDeviceCoordinates( end ); + fprintf( workFile, "%g %g l ", pos_dev.x, pos_dev.y ); + } + + end.x = centre.x + KiROUND( cosdecideg( radius, -EndAngle ) ); + end.y = centre.y + KiROUND( sindecideg( radius, -EndAngle ) ); + pos_dev = userToDeviceCoordinates( end ); + fprintf( workFile, "%g %g l ", pos_dev.x, pos_dev.y ); + + // The arc is drawn... if not filled we stroke it, otherwise we finish + // closing the pie at the center + if( fill == NO_FILL ) + { + fputs( "S\n", workFile ); + } + else + { + pos_dev = userToDeviceCoordinates( centre ); + fprintf( workFile, "%g %g l b\n", pos_dev.x, pos_dev.y ); + } +} + + +/** + * Polygon plotting for PDF. Everything is supported + */ +void PDF_PLOTTER::PlotPoly( const std::vector< wxPoint >& aCornerList, + FILL_T aFill, int aWidth ) +{ + wxASSERT( workFile ); + if( aCornerList.size() <= 1 ) + return; + + SetCurrentLineWidth( aWidth ); + + DPOINT pos = userToDeviceCoordinates( aCornerList[0] ); + fprintf( workFile, "%g %g m\n", pos.x, pos.y ); + + for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) + { + pos = userToDeviceCoordinates( aCornerList[ii] ); + fprintf( workFile, "%g %g l\n", pos.x, pos.y ); + } + + // Close path and stroke(/fill) + fprintf( workFile, "%c\n", aFill == NO_FILL ? 'S' : 'b' ); +} + + +void PDF_PLOTTER::PenTo( const wxPoint& pos, char plume ) +{ + wxASSERT( workFile ); + if( plume == 'Z' ) + { + if( penState != 'Z' ) + { + fputs( "S\n", workFile ); + penState = 'Z'; + penLastpos.x = -1; + penLastpos.y = -1; + } + return; + } + + if( penState != plume || pos != penLastpos ) + { + DPOINT pos_dev = userToDeviceCoordinates( pos ); + fprintf( workFile, "%g %g %c\n", + pos_dev.x, pos_dev.y, + ( plume=='D' ) ? 'l' : 'm' ); + } + penState = plume; + penLastpos = pos; +} + +/** + * PDF images are handles as inline, not XObject streams... + */ +void PDF_PLOTTER::PlotImage( const wxImage & aImage, const wxPoint& aPos, + double aScaleFactor ) +{ + wxASSERT( workFile ); + wxSize pix_size( aImage.GetWidth(), aImage.GetHeight() ); + + // Requested size (in IUs) + DPOINT drawsize( aScaleFactor * pix_size.x, + aScaleFactor * pix_size.y ); + + // calculate the bitmap start position + wxPoint start( aPos.x - drawsize.x / 2, + aPos.y + drawsize.y / 2); + + DPOINT dev_start = userToDeviceCoordinates( start ); + + /* PDF has an uhm... simplified coordinate system handling. There is + *one* operator to do everything (the PS concat equivalent). At least + they kept the matrix stack to save restore environments. Also images + are always emitted at the origin with a size of 1x1 user units. + What we need to do is: + 1) save the CTM end estabilish the new one + 2) plot the image + 3) restore the CTM + 4) profit + */ + fprintf( workFile, "q %g 0 0 %g %g %g cm\n", // Step 1 + userToDeviceSize( drawsize.x ), + userToDeviceSize( drawsize.y ), + dev_start.x, dev_start.y ); + + /* An inline image is a cross between a dictionary and a stream. + A real ugly construct (compared with the elegance of the PDF + format). Also it accepts some 'abbreviations', which is stupid + since the content stream is usually compressed anyway... */ + fprintf( workFile, + "BI\n" + " /BPC 8\n" + " /CS %s\n" + " /W %d\n" + " /H %d\n" + "ID\n", colorMode ? "/RGB" : "/G", pix_size.x, pix_size.y ); + + /* Here comes the stream (in binary!). I *could* have hex or ascii84 + encoded it, but who cares? I'll go through zlib anyway */ + for( int y = 0; y < pix_size.y; y++ ) + { + for( int x = 0; x < pix_size.x; x++ ) + { + unsigned char r = aImage.GetRed( x, y ) & 0xFF; + unsigned char g = aImage.GetGreen( x, y ) & 0xFF; + unsigned char b = aImage.GetBlue( x, y ) & 0xFF; + // As usual these days, stdio buffering has to suffeeeeerrrr + if( colorMode ) + { + putc( r, workFile ); + putc( g, workFile ); + putc( b, workFile ); + } + else + { + // Grayscale conversion + putc( (r + g + b) / 3, workFile ); + } + } + } + + fputs( "EI Q\n", workFile ); // Finish step 2 and do step 3 +} + + +/** + * Allocate a new handle in the table of the PDF object. The + * handle must be completed using startPdfObject. It's an in-RAM operation + * only, no output is done. + */ +int PDF_PLOTTER::allocPdfObject() +{ + xrefTable.push_back( 0 ); + return xrefTable.size() - 1; +} + + +/** + * Open a new PDF object and returns the handle if the parameter is -1. + * Otherwise fill in the xref entry for the passed object + */ +int PDF_PLOTTER::startPdfObject(int handle) +{ + wxASSERT( outputFile ); + wxASSERT( !workFile ); + if( handle < 0) + handle = allocPdfObject(); + + xrefTable[handle] = ftell( outputFile ); + fprintf( outputFile, "%d 0 obj\n", handle ); + return handle; +} + + +/** + * Close the current PDF object + */ +void PDF_PLOTTER::closePdfObject() +{ + wxASSERT( outputFile ); + wxASSERT( !workFile ); + fputs( "endobj\n", outputFile ); +} + + +/** + * Starts a PDF stream (for the page). Returns the object handle opened + * Pass -1 (default) for a fresh object. Especially from PDF 1.5 streams + * can contain a lot of things, but for the moment we only handle page + * content. + */ +int PDF_PLOTTER::startPdfStream(int handle) +{ + wxASSERT( outputFile ); + wxASSERT( !workFile ); + handle = startPdfObject( handle ); + + // This is guaranteed to be handle+1 but needs to be allocated since + // you could allocate more object during stream preparation + streamLengthHandle = allocPdfObject(); + fprintf( outputFile, + "<< /Length %d 0 R /Filter /FlateDecode >>\n" // Length is deferred + "stream\n", handle + 1 ); + + // Open a temporary file to accumulate the stream + workFilename = filename + wxT(".tmp"); + workFile = wxFopen( workFilename, wxT( "w+b" )); + wxASSERT( workFile ); + return handle; +} + + +/** + * Finish the current PDF stream (writes the deferred length, too) + */ +void PDF_PLOTTER::closePdfStream() +{ + wxASSERT( workFile ); + + long stream_len = ftell( workFile ); + + if( stream_len < 0 ) + { + wxASSERT( false ); + return; + } + + // Rewind the file, read in the page stream and DEFLATE it + fseek( workFile, 0, SEEK_SET ); + unsigned char *inbuf = new unsigned char[stream_len]; + + int rc = fread( inbuf, 1, stream_len, workFile ); + wxASSERT( rc == stream_len ); + (void) rc; + + // We are done with the temporary file, junk it + fclose( workFile ); + workFile = 0; + ::wxRemoveFile( workFilename ); + + // NULL means memos owns the memory, but provide a hint on optimum size needed. + wxMemoryOutputStream memos( NULL, std::max( 2000l, stream_len ) ) ; + + { + /* Somewhat standard parameters to compress in DEFLATE. The PDF spec is + * misleading, it says it wants a DEFLATE stream but it really want a ZLIB + * stream! (a DEFLATE stream would be generated with -15 instead of 15) + * rc = deflateInit2( &zstrm, Z_BEST_COMPRESSION, Z_DEFLATED, 15, + * 8, Z_DEFAULT_STRATEGY ); + */ + + wxZlibOutputStream zos( memos, wxZ_BEST_COMPRESSION, wxZLIB_ZLIB ); + + zos.Write( inbuf, stream_len ); + + delete[] inbuf; + + } // flush the zip stream using zos destructor + + wxStreamBuffer* sb = memos.GetOutputStreamBuffer(); + + unsigned out_count = sb->Tell(); + + fwrite( sb->GetBufferStart(), 1, out_count, outputFile ); + + fputs( "endstream\n", outputFile ); + closePdfObject(); + + // Writing the deferred length as an indirect object + startPdfObject( streamLengthHandle ); + fprintf( outputFile, "%u\n", out_count ); + closePdfObject(); +} + +/** + * Starts a new page in the PDF document + */ +void PDF_PLOTTER::StartPage() +{ + wxASSERT( outputFile ); + wxASSERT( !workFile ); + + // Compute the paper size in IUs + paperSize = pageInfo.GetSizeMils(); + paperSize.x *= 10.0 / iuPerDeviceUnit; + paperSize.y *= 10.0 / iuPerDeviceUnit; + + // Open the content stream; the page object will go later + pageStreamHandle = startPdfStream(); + + /* Now, until ClosePage *everything* must be wrote in workFile, to be + compressed later in closePdfStream */ + + // Default graphic settings (coordinate system, default color and line style) + fprintf( workFile, + "%g 0 0 %g 0 0 cm 1 J 1 j 0 0 0 rg 0 0 0 RG %g w\n", + 0.0072 * plotScaleAdjX, 0.0072 * plotScaleAdjY, + userToDeviceSize( defaultPenWidth ) ); +} + +/** + * Close the current page in the PDF document (and emit its compressed stream) + */ +void PDF_PLOTTER::ClosePage() +{ + wxASSERT( workFile ); + + // Close the page stream (and compress it) + closePdfStream(); + + // Emit the page object and put it in the page list for later + pageHandles.push_back( startPdfObject() ); + + /* Page size is in 1/72 of inch (default user space units) + Works like the bbox in postscript but there is no need for + swapping the sizes, since PDF doesn't require a portrait page. + We use the MediaBox but PDF has lots of other less used boxes + to use */ + + const double BIGPTsPERMIL = 0.072; + wxSize psPaperSize = pageInfo.GetSizeMils(); + + fprintf( outputFile, + "<<\n" + "/Type /Page\n" + "/Parent %d 0 R\n" + "/Resources <<\n" + " /ProcSet [/PDF /Text /ImageC /ImageB]\n" + " /Font %d 0 R >>\n" + "/MediaBox [0 0 %d %d]\n" + "/Contents %d 0 R\n" + ">>\n", + pageTreeHandle, + fontResDictHandle, + int( ceil( psPaperSize.x * BIGPTsPERMIL ) ), + int( ceil( psPaperSize.y * BIGPTsPERMIL ) ), + pageStreamHandle ); + closePdfObject(); + + // Mark the page stream as idle + pageStreamHandle = 0; +} + +/** + * The PDF engine supports multiple pages; the first one is opened + * 'for free' the following are to be closed and reopened. Between + * each page parameters can be set + */ +bool PDF_PLOTTER::StartPlot() +{ + wxASSERT( outputFile ); + + // First things first: the customary null object + xrefTable.clear(); + xrefTable.push_back( 0 ); + + /* The header (that's easy!). The second line is binary junk required + to make the file binary from the beginning (the important thing is + that they must have the bit 7 set) */ + fputs( "%PDF-1.5\n%\200\201\202\203\n", outputFile ); + + /* Allocate an entry for the page tree root, it will go in every page + parent entry */ + pageTreeHandle = allocPdfObject(); + + /* In the same way, the font resource dictionary is used by every page + (it *could* be inherited via the Pages tree */ + fontResDictHandle = allocPdfObject(); + + /* Now, the PDF is read from the end, (more or less)... so we start + with the page stream for page 1. Other more important stuff is written + at the end */ + StartPage(); + return true; +} + + +bool PDF_PLOTTER::EndPlot() +{ + wxASSERT( outputFile ); + + // Close the current page (often the only one) + ClosePage(); + + /* We need to declare the resources we're using (fonts in particular) + The useful standard one is the Helvetica family. Adding external fonts + is *very* involved! */ + struct { + const char *psname; + const char *rsname; + int font_handle; + } fontdefs[4] = { + { "/Helvetica", "/KicadFont", 0 }, + { "/Helvetica-Oblique", "/KicadFontI", 0 }, + { "/Helvetica-Bold", "/KicadFontB", 0 }, + { "/Helvetica-BoldOblique", "/KicadFontBI", 0 } + }; + + /* Declare the font resources. Since they're builtin fonts, no descriptors (yay!) + We'll need metrics anyway to do any aligment (these are in the shared with + the postscript engine) */ + for( int i = 0; i < 4; i++ ) + { + fontdefs[i].font_handle = startPdfObject(); + fprintf( outputFile, + "<< /BaseFont %s\n" + " /Type /Font\n" + " /Subtype /Type1\n" + + /* Adobe is so Mac-based that the nearest thing to Latin1 is + the Windows ANSI encoding! */ + " /Encoding /WinAnsiEncoding\n" + ">>\n", + fontdefs[i].psname ); + closePdfObject(); + } + + // Named font dictionary (was allocated, now we emit it) + startPdfObject( fontResDictHandle ); + fputs( "<<\n", outputFile ); + for( int i = 0; i < 4; i++ ) + { + fprintf( outputFile, " %s %d 0 R\n", + fontdefs[i].rsname, fontdefs[i].font_handle ); + } + fputs( ">>\n", outputFile ); + closePdfObject(); + + /* The page tree: it's a B-tree but luckily we only have few pages! + So we use just an array... The handle was allocated at the beginning, + now we instantiate the corresponding object */ + startPdfObject( pageTreeHandle ); + fputs( "<<\n" + "/Type /Pages\n" + "/Kids [\n", outputFile ); + + for( unsigned i = 0; i < pageHandles.size(); i++ ) + fprintf( outputFile, "%d 0 R\n", pageHandles[i] ); + + fprintf( outputFile, + "]\n" + "/Count %ld\n" + ">>\n", (long) pageHandles.size() ); + closePdfObject(); + + + // The info dictionary + int infoDictHandle = startPdfObject(); + char date_buf[250]; + time_t ltime = time( NULL ); + strftime( date_buf, 250, "D:%Y%m%d%H%M%S", + localtime( <ime ) ); + fprintf( outputFile, + "<<\n" + "/Producer (KiCAD PDF)\n" + "/CreationDate (%s)\n" + "/Creator (%s)\n" + "/Title (%s)\n" + "/Trapped false\n", + date_buf, + TO_UTF8( creator ), + TO_UTF8( filename ) ); + + fputs( ">>\n", outputFile ); + closePdfObject(); + + // The catalog, at last + int catalogHandle = startPdfObject(); + fprintf( outputFile, + "<<\n" + "/Type /Catalog\n" + "/Pages %d 0 R\n" + "/Version /1.5\n" + "/PageMode /UseNone\n" + "/PageLayout /SinglePage\n" + ">>\n", pageTreeHandle ); + closePdfObject(); + + /* Emit the xref table (format is crucial to the byte, each entry must + be 20 bytes long, and object zero must be done in that way). Also + the offset must be kept along for the trailer */ + long xref_start = ftell( outputFile ); + fprintf( outputFile, + "xref\n" + "0 %ld\n" + "0000000000 65535 f \n", (long) xrefTable.size() ); + for( unsigned i = 1; i < xrefTable.size(); i++ ) + { + fprintf( outputFile, "%010ld 00000 n \n", xrefTable[i] ); + } + + // Done the xref, go for the trailer + fprintf( outputFile, + "trailer\n" + "<< /Size %lu /Root %d 0 R /Info %d 0 R >>\n" + "startxref\n" + "%ld\n" // The offset we saved before + "%%%%EOF\n", + (unsigned long) xrefTable.size(), catalogHandle, infoDictHandle, xref_start ); + + fclose( outputFile ); + outputFile = NULL; + + return true; +} + +void PDF_PLOTTER::Text( const wxPoint& aPos, + enum EDA_COLOR_T aColor, + const wxString& aText, + double aOrient, + const wxSize& aSize, + enum EDA_TEXT_HJUSTIFY_T aH_justify, + enum EDA_TEXT_VJUSTIFY_T aV_justify, + int aWidth, + bool aItalic, + bool aBold, + bool aMultilineAllowed ) +{ + // PDF files do not like 0 sized texts which create broken files. + if( aSize.x == 0 || aSize.y == 0 ) + return; + + // Fix me: see how to use PDF text mode for multiline texts + if( aMultilineAllowed && !aText.Contains( wxT( "\n" ) ) ) + aMultilineAllowed = false; // the text has only one line. + + // Emit native PDF text (if requested) + // Currently: does not work: disable it + bool use_native = false; // = m_textMode != PLOTTEXTMODE_STROKE && !aMultilineAllowed; + + if( use_native ) + { + const char *fontname = aItalic ? (aBold ? "/KicadFontBI" : "/KicadFontI") + : (aBold ? "/KicadFontB" : "/KicadFont"); + + // Compute the copious tranformation parameters + double ctm_a, ctm_b, ctm_c, ctm_d, ctm_e, ctm_f; + double wideningFactor, heightFactor; + computeTextParameters( aPos, aText, aOrient, aSize, aH_justify, + aV_justify, aWidth, aItalic, aBold, + &wideningFactor, &ctm_a, &ctm_b, &ctm_c, + &ctm_d, &ctm_e, &ctm_f, &heightFactor ); + + SetColor( aColor ); + SetCurrentLineWidth( aWidth ); + + /* We use the full CTM instead of the text matrix because the same + coordinate system will be used for the overlining. Also the %f + for the trig part of the matrix to avoid %g going in exponential + format (which is not supported) + Rendermode 0 shows the text, rendermode 3 is invisible */ + fprintf( workFile, "q %f %f %f %f %g %g cm BT %s %g Tf %d Tr %g Tz ", + ctm_a, ctm_b, ctm_c, ctm_d, ctm_e, ctm_f, + fontname, heightFactor, + (m_textMode == PLOTTEXTMODE_NATIVE) ? 0 : 3, + wideningFactor * 100 ); + + // The text must be escaped correctly + fputsPostscriptString( workFile, aText ); + fputs( " Tj ET\n", workFile ); + + /* We are still in text coordinates, plot the overbars (if we're + * not doing phantom text) */ + if( m_textMode == PLOTTEXTMODE_NATIVE ) + { + std::vector<int> pos_pairs; + postscriptOverlinePositions( aText, aSize.x, aItalic, aBold, &pos_pairs ); + int overbar_y = KiROUND( aSize.y * 1.1 ); + for( unsigned i = 0; i < pos_pairs.size(); i += 2) + { + /* This is a nontrivial situation: we are *not* in the user + coordinate system, so the userToDeviceCoordinates function + can't be used! Strange as it may seem, the userToDeviceSize + is the right function to use here... */ + DPOINT dev_from = userToDeviceSize( wxSize( pos_pairs[i], overbar_y ) ); + DPOINT dev_to = userToDeviceSize( wxSize( pos_pairs[i + 1], overbar_y ) ); + fprintf( workFile, "%g %g m %g %g l ", + dev_from.x, dev_from.y, dev_to.x, dev_to.y ); + } + } + + // Stroke and restore the CTM + fputs( "S Q\n", workFile ); + } + + // Plot the stroked text (if requested) + if( !use_native ) + { + PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, + aWidth, aItalic, aBold, aMultilineAllowed ); + } +} + diff --git a/common/common_plotPS_functions.cpp b/common/common_plotPS_functions.cpp new file mode 100644 index 0000000..bbf9ede --- /dev/null +++ b/common/common_plotPS_functions.cpp @@ -0,0 +1,1090 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2017 KiCad Developers, see CHANGELOG.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 + */ + +/** + * @file common_plotPS_functions.cpp + * @brief Kicad: Common plot Postscript Routines + */ + +#include <fctsys.h> +#include <trigo.h> +#include <wxstruct.h> +#include <base_struct.h> +#include <common.h> +#include <plot_common.h> +#include <macros.h> +#include <kicad_string.h> + +/* Forward declaration of the font width metrics + (yes extern! this is the way to forward declare variables */ +extern const double hv_widths[256]; +extern const double hvb_widths[256]; +extern const double hvo_widths[256]; +extern const double hvbo_widths[256]; + +const double PSLIKE_PLOTTER::postscriptTextAscent = 0.718; + + +// Common routines for Postscript-like plotting engines + +void PSLIKE_PLOTTER::SetDefaultLineWidth( int width ) +{ + defaultPenWidth = width; + currentPenWidth = -1; +} + + +void PSLIKE_PLOTTER::SetColor( EDA_COLOR_T color ) +{ + // Return at invalid color index + if( color < 0 ) + return; + + if( colorMode ) + { + double r = g_ColorRefs[color].m_Red / 255.0; + double g = g_ColorRefs[color].m_Green / 255.0; + double b = g_ColorRefs[color].m_Blue / 255.0; + if( negativeMode ) + emitSetRGBColor( 1 - r, 1 - g, 1 - b ); + else + emitSetRGBColor( r, g, b ); + } + else + { + /* B/W Mode - Use BLACK or WHITE for all items + * note the 2 colors are used in B&W mode, mainly by Pcbnew to draw + * holes in white on pads in black + */ + double k = 1; // White + if( color != WHITE ) + k = 0; + if( negativeMode ) + emitSetRGBColor( 1 - k, 1 - k, 1 - k ); + else + emitSetRGBColor( k, k, k ); + } +} + + +void PSLIKE_PLOTTER::FlashPadOval( const wxPoint& aPadPos, const wxSize& aSize, + double aPadOrient, EDA_DRAW_MODE_T aTraceMode ) +{ + int x0, y0, x1, y1, delta; + wxSize size( aSize ); + + // The pad is reduced to an oval by dy > dx + if( size.x > size.y ) + { + std::swap( size.x, size.y ); + aPadOrient = AddAngles( aPadOrient, 900 ); + } + + delta = size.y - size.x; + x0 = 0; + y0 = -delta / 2; + x1 = 0; + y1 = delta / 2; + RotatePoint( &x0, &y0, aPadOrient ); + RotatePoint( &x1, &y1, aPadOrient ); + + if( aTraceMode == FILLED ) + ThickSegment( wxPoint( aPadPos.x + x0, aPadPos.y + y0 ), + wxPoint( aPadPos.x + x1, aPadPos.y + y1 ), size.x, aTraceMode ); + else + sketchOval( aPadPos, size, aPadOrient, -1 ); +} + + +void PSLIKE_PLOTTER::FlashPadCircle( const wxPoint& aPadPos, int aDiameter, + EDA_DRAW_MODE_T aTraceMode ) +{ + if( aTraceMode == FILLED ) + Circle( aPadPos, aDiameter, FILLED_SHAPE, 0 ); + else // Plot a ring: + { + SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH ); + int linewidth = GetCurrentLineWidth(); + + // avoid aDiameter <= 1 ) + if( linewidth > aDiameter-2 ) + linewidth = aDiameter-2; + + Circle( aPadPos, aDiameter - linewidth, NO_FILL, linewidth ); + } + + SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH ); +} + + +void PSLIKE_PLOTTER::FlashPadRect( const wxPoint& aPadPos, const wxSize& aSize, + double aPadOrient, EDA_DRAW_MODE_T aTraceMode ) +{ + static std::vector< wxPoint > cornerList; + wxSize size( aSize ); + cornerList.clear(); + + if( aTraceMode == FILLED ) + SetCurrentLineWidth( 0 ); + else + SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH ); + + size.x -= GetCurrentLineWidth(); + size.y -= GetCurrentLineWidth(); + + if( size.x < 1 ) + size.x = 1; + + if( size.y < 1 ) + size.y = 1; + + int dx = size.x / 2; + int dy = size.y / 2; + + wxPoint corner; + corner.x = aPadPos.x - dx; + corner.y = aPadPos.y + dy; + cornerList.push_back( corner ); + corner.x = aPadPos.x - dx; + corner.y = aPadPos.y - dy; + cornerList.push_back( corner ); + corner.x = aPadPos.x + dx; + corner.y = aPadPos.y - dy; + cornerList.push_back( corner ); + corner.x = aPadPos.x + dx; + corner.y = aPadPos.y + dy, + cornerList.push_back( corner ); + + for( unsigned ii = 0; ii < cornerList.size(); ii++ ) + { + RotatePoint( &cornerList[ii], aPadPos, aPadOrient ); + } + + cornerList.push_back( cornerList[0] ); + + PlotPoly( cornerList, ( aTraceMode == FILLED ) ? FILLED_SHAPE : NO_FILL, + GetCurrentLineWidth() ); +} + + +void PSLIKE_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint *aCorners, + double aPadOrient, EDA_DRAW_MODE_T aTraceMode ) +{ + static std::vector< wxPoint > cornerList; + cornerList.clear(); + + for( int ii = 0; ii < 4; ii++ ) + cornerList.push_back( aCorners[ii] ); + + if( aTraceMode == FILLED ) + { + SetCurrentLineWidth( 0 ); + } + else + { + SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH ); + int w = GetCurrentLineWidth(); + // offset polygon by w + // coord[0] is assumed the lower left + // coord[1] is assumed the upper left + // coord[2] is assumed the upper right + // coord[3] is assumed the lower right + + /* Trace the outline. */ + cornerList[0].x += w; + cornerList[0].y -= w; + cornerList[1].x += w; + cornerList[1].y += w; + cornerList[2].x -= w; + cornerList[2].y += w; + cornerList[3].x -= w; + cornerList[3].y -= w; + } + + for( int ii = 0; ii < 4; ii++ ) + { + RotatePoint( &cornerList[ii], aPadOrient ); + cornerList[ii] += aPadPos; + } + + cornerList.push_back( cornerList[0] ); + PlotPoly( cornerList, ( aTraceMode == FILLED ) ? FILLED_SHAPE : NO_FILL, + GetCurrentLineWidth() ); +} + + +/** + * Write on a stream a string escaped for postscript/PDF + */ +void PSLIKE_PLOTTER::fputsPostscriptString(FILE *fout, const wxString& txt) +{ + putc( '(', fout ); + for( unsigned i = 0; i < txt.length(); i++ ) + { + // Lazyness made me use stdio buffering yet another time... + wchar_t ch = txt[i]; + + if( ch < 256 ) + { + switch (ch) + { + // The ~ shouldn't reach the outside + case '~': + break; + // These characters must be escaped + case '(': + case ')': + case '\\': + putc( '\\', fout ); + + // FALLTHRU + default: + putc( ch, fout ); + break; + } + } + } + + putc( ')', fout ); +} + + +/** + * Sister function for the GraphicTextWidth in drawtxt.cpp + * Does the same processing (i.e. calculates a text string width) but + * using postscript metrics for the Helvetica font (optionally used for + * PS and PDF plotting + */ +int PSLIKE_PLOTTER::returnPostscriptTextWidth( const wxString& aText, int aXSize, + bool aItalic, bool aBold ) +{ + const double *width_table = aBold ? ( aItalic ? hvbo_widths : hvb_widths ) + : ( aItalic ? hvo_widths : hv_widths ); + double tally = 0; + + for( unsigned i = 0; i < aText.length(); i++ ) + { + wchar_t AsciiCode = aText[i]; + // Skip the negation marks and untabled points + if( AsciiCode != '~' && AsciiCode < 256 ) + { + tally += width_table[AsciiCode]; + } + } + + // Widths are proportional to height, but height is enlarged by a + // scaling factor + return KiROUND( aXSize * tally / postscriptTextAscent ); +} + + +/** + * Computes the x coordinates for the overlining in a string of text. + * Fills the passed vector with couples of (start, stop) values to be + * used in the text coordinate system (use computeTextParameters to + * obtain the parameters to estabilish such a system) + */ +void PSLIKE_PLOTTER::postscriptOverlinePositions( const wxString& aText, int aXSize, + bool aItalic, bool aBold, + std::vector<int> *pos_pairs ) +{ + /* XXX This function is *too* similar to returnPostscriptTextWidth. + Consider merging them... */ + const double *width_table = aBold ? ( aItalic ? hvbo_widths : hvb_widths ) + : ( aItalic ? hvo_widths : hv_widths ); + double tally = 0; + + for( unsigned i = 0; i < aText.length(); i++ ) + { + wchar_t AsciiCode = aText[i]; + // Skip the negation marks and untabled points + if( AsciiCode != '~' && AsciiCode < 256 ) + { + tally += width_table[AsciiCode]; + } + else + { + if( AsciiCode == '~' ) + pos_pairs->push_back( KiROUND( aXSize * tally / postscriptTextAscent ) ); + } + } + + // Special rule: we have to complete the last bar if the ~ aren't matched + if( pos_pairs->size() % 2 == 1 ) + pos_pairs->push_back( KiROUND( aXSize * tally / postscriptTextAscent ) ); +} + +void PS_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil, + double aScale, bool aMirror ) +{ + m_plotMirror = aMirror; + plotOffset = aOffset; + plotScale = aScale; + m_IUsPerDecimil = aIusPerDecimil; + iuPerDeviceUnit = 1.0 / aIusPerDecimil; + /* Compute the paper size in IUs */ + paperSize = pageInfo.GetSizeMils(); + paperSize.x *= 10.0 * aIusPerDecimil; + paperSize.y *= 10.0 * aIusPerDecimil; + SetDefaultLineWidth( 100 * aIusPerDecimil ); // arbitrary default +} + + +/** This is the core for postscript/PDF text alignment + * It computes the transformation matrix to generate a user space + * system aligned with the text. Even the PS uses the concat + * operator to simplify PDF generation (concat is everything PDF + * has to modify the CTM. Lots of parameters, both in and out. + */ +void PSLIKE_PLOTTER::computeTextParameters( const wxPoint& aPos, + const wxString& aText, + int aOrient, + const wxSize& aSize, + enum EDA_TEXT_HJUSTIFY_T aH_justify, + enum EDA_TEXT_VJUSTIFY_T aV_justify, + int aWidth, + bool aItalic, + bool aBold, + double *wideningFactor, + double *ctm_a, + double *ctm_b, + double *ctm_c, + double *ctm_d, + double *ctm_e, + double *ctm_f, + double *heightFactor ) +{ + // Compute the starting position (compensated for alignment) + wxPoint start_pos = aPos; + + // This is an approximation of the text bounds (in IUs) + int tw = returnPostscriptTextWidth( aText, aSize.x, aItalic, aWidth ); + int th = aSize.y; + int dx, dy; + + switch( aH_justify ) + { + case GR_TEXT_HJUSTIFY_CENTER: + dx = -tw / 2; + break; + + case GR_TEXT_HJUSTIFY_RIGHT: + dx = -tw; + break; + + case GR_TEXT_HJUSTIFY_LEFT: + dx = 0; + break; + } + + switch( aV_justify ) + { + case GR_TEXT_VJUSTIFY_CENTER: + dy = th / 2; + break; + + case GR_TEXT_VJUSTIFY_TOP: + dy = th; + break; + + case GR_TEXT_VJUSTIFY_BOTTOM: + dy = 0; + break; + } + + RotatePoint( &dx, &dy, aOrient ); + RotatePoint( &tw, &th, aOrient ); + start_pos.x += dx; + start_pos.y += dy; + DPOINT pos_dev = userToDeviceCoordinates( start_pos ); + DPOINT sz_dev = userToDeviceSize( aSize ); + + // Now returns the final values... the widening factor + *wideningFactor = sz_dev.y / sz_dev.x; + + // The CTM transformation matrix + double alpha = DECIDEG2RAD( aOrient ); + double sinalpha = sin( alpha ); + double cosalpha = cos( alpha ); + + *ctm_a = cosalpha; + *ctm_b = sinalpha; + *ctm_c = -sinalpha; + *ctm_d = cosalpha; + *ctm_e = pos_dev.x; + *ctm_f = pos_dev.y; + + // This is because the letters are less than 1 unit high + *heightFactor = sz_dev.y / postscriptTextAscent; +} + + +/* Set the current line width (in IUs) for the next plot + */ +void PS_PLOTTER::SetCurrentLineWidth( int width ) +{ + wxASSERT( outputFile ); + int pen_width; + + if( width >= 0 ) + pen_width = width; + else + pen_width = defaultPenWidth; + + if( pen_width != GetCurrentLineWidth() ) + fprintf( outputFile, "%g setlinewidth\n", userToDeviceSize( pen_width ) ); + + currentPenWidth = pen_width; +} + + +void PS_PLOTTER::emitSetRGBColor( double r, double g, double b ) +{ + wxASSERT( outputFile ); + + // XXX why %.3g ? shouldn't %g suffice? who cares... + fprintf( outputFile, "%.3g %.3g %.3g setrgbcolor\n", r, g, b ); +} + + +/** + * Postscript supports dashed lines + */ +void PS_PLOTTER::SetDash( bool dashed ) +{ + wxASSERT( outputFile ); + if( dashed ) + fprintf( outputFile, "[%d %d] 0 setdash\n", + (int) GetDashMarkLenIU(), (int) GetDashGapLenIU() ); + else + fputs( "solidline\n", outputFile ); +} + + +void PS_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width ) +{ + DPOINT p1_dev = userToDeviceCoordinates( p1 ); + DPOINT p2_dev = userToDeviceCoordinates( p2 ); + + SetCurrentLineWidth( width ); + fprintf( outputFile, "%g %g %g %g rect%d\n", p1_dev.x, p1_dev.y, + p2_dev.x - p1_dev.x, p2_dev.y - p1_dev.y, fill ); +} + + +void PS_PLOTTER::Circle( const wxPoint& pos, int diametre, FILL_T fill, int width ) +{ + wxASSERT( outputFile ); + DPOINT pos_dev = userToDeviceCoordinates( pos ); + double radius = userToDeviceSize( diametre / 2.0 ); + + SetCurrentLineWidth( width ); + fprintf( outputFile, "%g %g %g cir%d\n", pos_dev.x, pos_dev.y, radius, fill ); +} + + +void PS_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, + int radius, FILL_T fill, int width ) +{ + wxASSERT( outputFile ); + if( radius <= 0 ) + return; + + if( StAngle > EndAngle ) + std::swap( StAngle, EndAngle ); + + SetCurrentLineWidth( width ); + + // Calculate start point. + DPOINT centre_dev = userToDeviceCoordinates( centre ); + double radius_dev = userToDeviceSize( radius ); + + if( m_plotMirror ) + { + if( m_mirrorIsHorizontal ) + { + StAngle = 1800.0 -StAngle; + EndAngle = 1800.0 -EndAngle; + std::swap( StAngle, EndAngle ); + } + else + { + StAngle = -StAngle; + EndAngle = -EndAngle; + } + } + + fprintf( outputFile, "%g %g %g %g %g arc%d\n", centre_dev.x, centre_dev.y, + radius_dev, StAngle / 10.0, EndAngle / 10.0, fill ); +} + + +void PS_PLOTTER::PlotPoly( const std::vector< wxPoint >& aCornerList, + FILL_T aFill, int aWidth ) +{ + if( aCornerList.size() <= 1 ) + return; + + SetCurrentLineWidth( aWidth ); + + DPOINT pos = userToDeviceCoordinates( aCornerList[0] ); + fprintf( outputFile, "newpath\n%g %g moveto\n", pos.x, pos.y ); + + for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) + { + pos = userToDeviceCoordinates( aCornerList[ii] ); + fprintf( outputFile, "%g %g lineto\n", pos.x, pos.y ); + } + + // Close/(fill) the path + fprintf( outputFile, "poly%d\n", aFill ); +} + + +/** + * Postscript-likes at the moment are the only plot engines supporting bitmaps... + */ +void PS_PLOTTER::PlotImage( const wxImage & aImage, const wxPoint& aPos, + double aScaleFactor ) +{ + wxSize pix_size; // size of the bitmap in pixels + pix_size.x = aImage.GetWidth(); + pix_size.y = aImage.GetHeight(); + DPOINT drawsize( aScaleFactor * pix_size.x, + aScaleFactor * pix_size.y ); // requested size of image + + // calculate the bottom left corner position of bitmap + wxPoint start = aPos; + start.x -= drawsize.x / 2; // left + start.y += drawsize.y / 2; // bottom (Y axis reversed) + + // calculate the top right corner position of bitmap + wxPoint end; + end.x = start.x + drawsize.x; + end.y = start.y - drawsize.y; + + fprintf( outputFile, "/origstate save def\n" ); + fprintf( outputFile, "/pix %d string def\n", pix_size.x ); + + // Locate lower-left corner of image + DPOINT start_dev = userToDeviceCoordinates( start ); + fprintf( outputFile, "%g %g translate\n", start_dev.x, start_dev.y ); + // Map image size to device + DPOINT end_dev = userToDeviceCoordinates( end ); + fprintf( outputFile, "%g %g scale\n", + std::abs(end_dev.x - start_dev.x), std::abs(end_dev.y - start_dev.y)); + + // Dimensions of source image (in pixels + fprintf( outputFile, "%d %d 8", pix_size.x, pix_size.y ); + // Map unit square to source + fprintf( outputFile, " [%d 0 0 %d 0 %d]\n", pix_size.x, -pix_size.y , pix_size.y); + // include image data in ps file + fprintf( outputFile, "{currentfile pix readhexstring pop}\n" ); + + if( colorMode ) + fputs( "false 3 colorimage\n", outputFile ); + else + fputs( "image\n", outputFile ); + // Single data source, 3 colors, Output RGB data (hexadecimal) + // (or the same downscaled to gray) + int jj = 0; + + for( int yy = 0; yy < pix_size.y; yy ++ ) + { + for( int xx = 0; xx < pix_size.x; xx++, jj++ ) + { + if( jj >= 16 ) + { + jj = 0; + fprintf( outputFile, "\n"); + } + + int red, green, blue; + red = aImage.GetRed( xx, yy) & 0xFF; + green = aImage.GetGreen( xx, yy) & 0xFF; + blue = aImage.GetBlue( xx, yy) & 0xFF; + + if( colorMode ) + fprintf( outputFile, "%2.2X%2.2X%2.2X", red, green, blue ); + else + fprintf( outputFile, "%2.2X", (red + green + blue) / 3 ); + } + } + + fprintf( outputFile, "\n"); + fprintf( outputFile, "origstate restore\n" ); +} + + +void PS_PLOTTER::PenTo( const wxPoint& pos, char plume ) +{ + wxASSERT( outputFile ); + + if( plume == 'Z' ) + { + if( penState != 'Z' ) + { + fputs( "stroke\n", outputFile ); + penState = 'Z'; + penLastpos.x = -1; + penLastpos.y = -1; + } + + return; + } + + if( penState == 'Z' ) + { + fputs( "newpath\n", outputFile ); + } + + if( penState != plume || pos != penLastpos ) + { + DPOINT pos_dev = userToDeviceCoordinates( pos ); + fprintf( outputFile, "%g %g %sto\n", + pos_dev.x, pos_dev.y, + ( plume=='D' ) ? "line" : "move" ); + } + + penState = plume; + penLastpos = pos; +} + + +/** + * The code within this function (and the CloseFilePS function) + * creates postscript files whose contents comply with Adobe's + * Document Structuring Convention, as documented by assorted + * details described within the following URLs: + * + * http://en.wikipedia.org/wiki/Document_Structuring_Conventions + * http://partners.adobe.com/public/developer/en/ps/5001.DSC_Spec.pdf + * + * + * BBox is the boundary box (position and size of the "client rectangle" + * for drawings (page - margins) in mils (0.001 inch) + */ +bool PS_PLOTTER::StartPlot() +{ + wxASSERT( outputFile ); + wxString msg; + + static const char* PSMacro[] = + { + "%%BeginProlog\n", + "/line { newpath moveto lineto stroke } bind def\n", + "/cir0 { newpath 0 360 arc stroke } bind def\n", + "/cir1 { newpath 0 360 arc gsave fill grestore stroke } bind def\n", + "/cir2 { newpath 0 360 arc gsave fill grestore stroke } bind def\n", + "/arc0 { newpath arc stroke } bind def\n", + "/arc1 { newpath 4 index 4 index moveto arc closepath gsave fill\n", + " grestore stroke } bind def\n", + "/arc2 { newpath 4 index 4 index moveto arc closepath gsave fill\n", + " grestore stroke } bind def\n", + "/poly0 { stroke } bind def\n", + "/poly1 { closepath gsave fill grestore stroke } bind def\n", + "/poly2 { closepath gsave fill grestore stroke } bind def\n", + "/rect0 { rectstroke } bind def\n", + "/rect1 { rectfill } bind def\n", + "/rect2 { rectfill } bind def\n", + "/linemode0 { 0 setlinecap 0 setlinejoin 0 setlinewidth } bind def\n", + "/linemode1 { 1 setlinecap 1 setlinejoin } bind def\n", + "/dashedline { [200] 100 setdash } bind def\n", + "/solidline { [] 0 setdash } bind def\n", + + // This is for 'hidden' text (search anchors for PDF) + "/phantomshow { moveto\n", + " /KicadFont findfont 0.000001 scalefont setfont\n", + " show } bind def\n", + + // This is for regular postscript text + "/textshow { gsave\n", + " findfont exch scalefont setfont concat 1 scale 0 0 moveto show\n", + " } bind def\n", + + // Utility for getting Latin1 encoded fonts + "/reencodefont {\n", + " findfont dup length dict begin\n", + " { 1 index /FID ne\n", + " { def }\n", + " { pop pop } ifelse\n", + " } forall\n", + " /Encoding ISOLatin1Encoding def\n", + " currentdict\n", + " end } bind def\n" + + // Remap AdobeStandard fonts to Latin1 + "/KicadFont /Helvetica reencodefont definefont pop\n", + "/KicadFont-Bold /Helvetica-Bold reencodefont definefont pop\n", + "/KicadFont-Oblique /Helvetica-Oblique reencodefont definefont pop\n", + "/KicadFont-BoldOblique /Helvetica-BoldOblique reencodefont definefont pop\n", + "%%EndProlog\n", + NULL + }; + + time_t time1970 = time( NULL ); + + fputs( "%!PS-Adobe-3.0\n", outputFile ); // Print header + + fprintf( outputFile, "%%%%Creator: %s\n", TO_UTF8( creator ) ); + + /* A "newline" character ("\n") is not included in the following string, + because it is provided by the ctime() function. */ + fprintf( outputFile, "%%%%CreationDate: %s", ctime( &time1970 ) ); + fprintf( outputFile, "%%%%Title: %s\n", TO_UTF8( filename ) ); + fprintf( outputFile, "%%%%Pages: 1\n" ); + fprintf( outputFile, "%%%%PageOrder: Ascend\n" ); + + // Print boundary box in 1/72 pixels per inch, box is in mils + const double BIGPTsPERMIL = 0.072; + + /* The coordinates of the lower left corner of the boundary + box need to be "rounded down", but the coordinates of its + upper right corner need to be "rounded up" instead. */ + wxSize psPaperSize = pageInfo.GetSizeMils(); + + if( !pageInfo.IsPortrait() ) + psPaperSize.Set( pageInfo.GetHeightMils(), pageInfo.GetWidthMils() ); + + fprintf( outputFile, "%%%%BoundingBox: 0 0 %d %d\n", + (int) ceil( psPaperSize.x * BIGPTsPERMIL ), + (int) ceil( psPaperSize.y * BIGPTsPERMIL ) ); + + // Specify the size of the sheet and the name associated with that size. + // (If the "User size" option has been selected for the sheet size, + // identify the sheet size as "Custom" (rather than as "User"), but + // otherwise use the name assigned by KiCad for each sheet size.) + // + // (The Document Structuring Convention also supports sheet weight, + // sheet color, and sheet type properties being specified within a + // %%DocumentMedia comment, but they are not being specified here; + // a zero and two null strings are subsequently provided instead.) + // + // (NOTE: m_Size.y is *supposed* to be listed before m_Size.x; + // the order in which they are specified is not wrong!) + // Also note pageSize is given in mils, not in internal units and must be + // converted to internal units. + + if( pageInfo.IsCustom() ) + fprintf( outputFile, "%%%%DocumentMedia: Custom %d %d 0 () ()\n", + KiROUND( psPaperSize.x * BIGPTsPERMIL ), + KiROUND( psPaperSize.y * BIGPTsPERMIL ) ); + + else // a standard paper size + fprintf( outputFile, "%%%%DocumentMedia: %s %d %d 0 () ()\n", + TO_UTF8( pageInfo.GetType() ), + KiROUND( psPaperSize.x * BIGPTsPERMIL ), + KiROUND( psPaperSize.y * BIGPTsPERMIL ) ); + + if( pageInfo.IsPortrait() ) + fprintf( outputFile, "%%%%Orientation: Portrait\n" ); + else + fprintf( outputFile, "%%%%Orientation: Landscape\n" ); + + fprintf( outputFile, "%%%%EndComments\n" ); + + // Now specify various other details. + + for( int ii = 0; PSMacro[ii] != NULL; ii++ ) + { + fputs( PSMacro[ii], outputFile ); + } + + // The following string has been specified here (rather than within + // PSMacro[]) to highlight that it has been provided to ensure that the + // contents of the postscript file comply with the details specified + // within the Document Structuring Convention. + fputs( "%%Page: 1 1\n" + "%%BeginPageSetup\n" + "gsave\n" + "0.0072 0.0072 scale\n" // Configure postscript for decimils coordinates + "linemode1\n", outputFile ); + + + // Rototranslate the coordinate to achieve the landscape layout + if( !pageInfo.IsPortrait() ) + fprintf( outputFile, "%d 0 translate 90 rotate\n", 10 * psPaperSize.x ); + + // Apply the user fine scale adjustments + if( plotScaleAdjX != 1.0 || plotScaleAdjY != 1.0 ) + fprintf( outputFile, "%g %g scale\n", + plotScaleAdjX, plotScaleAdjY ); + + // Set default line width + fprintf( outputFile, "%g setlinewidth\n", userToDeviceSize( defaultPenWidth ) ); + fputs( "%%EndPageSetup\n", outputFile ); + + return true; +} + + +bool PS_PLOTTER::EndPlot() +{ + wxASSERT( outputFile ); + fputs( "showpage\n" + "grestore\n" + "%%EOF\n", outputFile ); + fclose( outputFile ); + outputFile = NULL; + + return true; +} + + + +void PS_PLOTTER::Text( const wxPoint& aPos, + enum EDA_COLOR_T aColor, + const wxString& aText, + double aOrient, + const wxSize& aSize, + enum EDA_TEXT_HJUSTIFY_T aH_justify, + enum EDA_TEXT_VJUSTIFY_T aV_justify, + int aWidth, + bool aItalic, + bool aBold, + bool aMultilineAllowed ) +{ + SetCurrentLineWidth( aWidth ); + SetColor( aColor ); + + // Fix me: see how to use PS text mode for multiline texts + if( aMultilineAllowed && !aText.Contains( wxT( "\n" ) ) ) + aMultilineAllowed = false; // the text has only one line. + + // Draw the native postscript text (if requested) + // Currently: does not work: disable it + bool use_native = false; // = m_textMode == PLOTTEXTMODE_NATIVE && !aMultilineAllowed; + + if( use_native ) + { + const char *fontname = aItalic ? (aBold ? "/KicadFont-BoldOblique" + : "/KicadFont-Oblique") + : (aBold ? "/KicadFont-Bold" + : "/KicadFont"); + + // Compute the copious tranformation parameters + double ctm_a, ctm_b, ctm_c, ctm_d, ctm_e, ctm_f; + double wideningFactor, heightFactor; + computeTextParameters( aPos, aText, aOrient, aSize, aH_justify, + aV_justify, aWidth, aItalic, aBold, + &wideningFactor, &ctm_a, &ctm_b, &ctm_c, + &ctm_d, &ctm_e, &ctm_f, &heightFactor ); + + + // The text must be escaped correctly, the others are the various + // parameters. The CTM is formatted with %f since sin/cos tends + // to make %g use exponential notation (which is not supported) + fputsPostscriptString( outputFile, aText ); + fprintf( outputFile, " %g [%f %f %f %f %f %f] %g %s textshow\n", + wideningFactor, ctm_a, ctm_b, ctm_c, ctm_d, ctm_e, ctm_f, + heightFactor, fontname ); + + /* The textshow operator retained the coordinate system, we use it + * to plot the overbars. See the PDF sister function for more + * details */ + + std::vector<int> pos_pairs; + postscriptOverlinePositions( aText, aSize.x, aItalic, aBold, &pos_pairs ); + int overbar_y = KiROUND( aSize.y * 1.1 ); + + for( unsigned i = 0; i < pos_pairs.size(); i += 2) + { + DPOINT dev_from = userToDeviceSize( wxSize( pos_pairs[i], overbar_y ) ); + DPOINT dev_to = userToDeviceSize( wxSize( pos_pairs[i + 1], overbar_y ) ); + fprintf( outputFile, "%g %g %g %g line ", + dev_from.x, dev_from.y, dev_to.x, dev_to.y ); + } + + // Restore the CTM + fputs( "grestore\n", outputFile ); + } + + // Draw the hidden postscript text (if requested) + if( m_textMode == PLOTTEXTMODE_PHANTOM ) + { + fputsPostscriptString( outputFile, aText ); + DPOINT pos_dev = userToDeviceCoordinates( aPos ); + fprintf( outputFile, " %g %g phantomshow\n", pos_dev.x, pos_dev.y ); + } + + // Draw the stroked text (if requested) + if( !use_native ) + { + PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, + aWidth, aItalic, aBold, aMultilineAllowed ); + } +} + + +/** + * Character widths for Helvetica + */ +const double hv_widths[256] = { + 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, + 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, + 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, + 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, + 0.278, 0.278, 0.355, 0.556, 0.556, 0.889, 0.667, 0.191, + 0.333, 0.333, 0.389, 0.584, 0.278, 0.333, 0.278, 0.278, + 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, + 0.556, 0.556, 0.278, 0.278, 0.584, 0.584, 0.584, 0.556, + 1.015, 0.667, 0.667, 0.722, 0.722, 0.667, 0.611, 0.778, + 0.722, 0.278, 0.500, 0.667, 0.556, 0.833, 0.722, 0.778, + 0.667, 0.778, 0.722, 0.667, 0.611, 0.722, 0.667, 0.944, + 0.667, 0.667, 0.611, 0.278, 0.278, 0.278, 0.469, 0.556, + 0.333, 0.556, 0.556, 0.500, 0.556, 0.556, 0.278, 0.556, + 0.556, 0.222, 0.222, 0.500, 0.222, 0.833, 0.556, 0.556, + 0.556, 0.556, 0.333, 0.500, 0.278, 0.556, 0.500, 0.722, + 0.500, 0.500, 0.500, 0.334, 0.260, 0.334, 0.584, 0.278, + 0.278, 0.278, 0.222, 0.556, 0.333, 1.000, 0.556, 0.556, + 0.333, 1.000, 0.667, 0.333, 1.000, 0.278, 0.278, 0.278, + 0.278, 0.222, 0.222, 0.333, 0.333, 0.350, 0.556, 1.000, + 0.333, 1.000, 0.500, 0.333, 0.944, 0.278, 0.278, 0.667, + 0.278, 0.333, 0.556, 0.556, 0.556, 0.556, 0.260, 0.556, + 0.333, 0.737, 0.370, 0.556, 0.584, 0.333, 0.737, 0.333, + 0.400, 0.584, 0.333, 0.333, 0.333, 0.556, 0.537, 0.278, + 0.333, 0.333, 0.365, 0.556, 0.834, 0.834, 0.834, 0.611, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 1.000, 0.722, + 0.667, 0.667, 0.667, 0.667, 0.278, 0.278, 0.278, 0.278, + 0.722, 0.722, 0.778, 0.778, 0.778, 0.778, 0.778, 0.584, + 0.778, 0.722, 0.722, 0.722, 0.722, 0.667, 0.667, 0.611, + 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.889, 0.500, + 0.556, 0.556, 0.556, 0.556, 0.278, 0.278, 0.278, 0.278, + 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.584, + 0.611, 0.556, 0.556, 0.556, 0.556, 0.500, 0.556, 0.500 +}; + +/** + * Character widths for Helvetica-Bold + */ +const double hvb_widths[256] = { + 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, + 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, + 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, + 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, + 0.278, 0.333, 0.474, 0.556, 0.556, 0.889, 0.722, 0.238, + 0.333, 0.333, 0.389, 0.584, 0.278, 0.333, 0.278, 0.278, + 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, + 0.556, 0.556, 0.333, 0.333, 0.584, 0.584, 0.584, 0.611, + 0.975, 0.722, 0.722, 0.722, 0.722, 0.667, 0.611, 0.778, + 0.722, 0.278, 0.556, 0.722, 0.611, 0.833, 0.722, 0.778, + 0.667, 0.778, 0.722, 0.667, 0.611, 0.722, 0.667, 0.944, + 0.667, 0.667, 0.611, 0.333, 0.278, 0.333, 0.584, 0.556, + 0.333, 0.556, 0.611, 0.556, 0.611, 0.556, 0.333, 0.611, + 0.611, 0.278, 0.278, 0.556, 0.278, 0.889, 0.611, 0.611, + 0.611, 0.611, 0.389, 0.556, 0.333, 0.611, 0.556, 0.778, + 0.556, 0.556, 0.500, 0.389, 0.280, 0.389, 0.584, 0.278, + 0.278, 0.278, 0.278, 0.556, 0.500, 1.000, 0.556, 0.556, + 0.333, 1.000, 0.667, 0.333, 1.000, 0.278, 0.278, 0.278, + 0.278, 0.278, 0.278, 0.500, 0.500, 0.350, 0.556, 1.000, + 0.333, 1.000, 0.556, 0.333, 0.944, 0.278, 0.278, 0.667, + 0.278, 0.333, 0.556, 0.556, 0.556, 0.556, 0.280, 0.556, + 0.333, 0.737, 0.370, 0.556, 0.584, 0.333, 0.737, 0.333, + 0.400, 0.584, 0.333, 0.333, 0.333, 0.611, 0.556, 0.278, + 0.333, 0.333, 0.365, 0.556, 0.834, 0.834, 0.834, 0.611, + 0.722, 0.722, 0.722, 0.722, 0.722, 0.722, 1.000, 0.722, + 0.667, 0.667, 0.667, 0.667, 0.278, 0.278, 0.278, 0.278, + 0.722, 0.722, 0.778, 0.778, 0.778, 0.778, 0.778, 0.584, + 0.778, 0.722, 0.722, 0.722, 0.722, 0.667, 0.667, 0.611, + 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.889, 0.556, + 0.556, 0.556, 0.556, 0.556, 0.278, 0.278, 0.278, 0.278, + 0.611, 0.611, 0.611, 0.611, 0.611, 0.611, 0.611, 0.584, + 0.611, 0.611, 0.611, 0.611, 0.611, 0.556, 0.611, 0.556 +}; + +/** + * Character widths for Helvetica-Oblique + */ +const double hvo_widths[256] = { + 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, + 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, + 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, + 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, + 0.278, 0.278, 0.355, 0.556, 0.556, 0.889, 0.667, 0.191, + 0.333, 0.333, 0.389, 0.584, 0.278, 0.333, 0.278, 0.278, + 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, + 0.556, 0.556, 0.278, 0.278, 0.584, 0.584, 0.584, 0.556, + 1.015, 0.667, 0.667, 0.722, 0.722, 0.667, 0.611, 0.778, + 0.722, 0.278, 0.500, 0.667, 0.556, 0.833, 0.722, 0.778, + 0.667, 0.778, 0.722, 0.667, 0.611, 0.722, 0.667, 0.944, + 0.667, 0.667, 0.611, 0.278, 0.278, 0.278, 0.469, 0.556, + 0.333, 0.556, 0.556, 0.500, 0.556, 0.556, 0.278, 0.556, + 0.556, 0.222, 0.222, 0.500, 0.222, 0.833, 0.556, 0.556, + 0.556, 0.556, 0.333, 0.500, 0.278, 0.556, 0.500, 0.722, + 0.500, 0.500, 0.500, 0.334, 0.260, 0.334, 0.584, 0.278, + 0.278, 0.278, 0.222, 0.556, 0.333, 1.000, 0.556, 0.556, + 0.333, 1.000, 0.667, 0.333, 1.000, 0.278, 0.278, 0.278, + 0.278, 0.222, 0.222, 0.333, 0.333, 0.350, 0.556, 1.000, + 0.333, 1.000, 0.500, 0.333, 0.944, 0.278, 0.278, 0.667, + 0.278, 0.333, 0.556, 0.556, 0.556, 0.556, 0.260, 0.556, + 0.333, 0.737, 0.370, 0.556, 0.584, 0.333, 0.737, 0.333, + 0.400, 0.584, 0.333, 0.333, 0.333, 0.556, 0.537, 0.278, + 0.333, 0.333, 0.365, 0.556, 0.834, 0.834, 0.834, 0.611, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 1.000, 0.722, + 0.667, 0.667, 0.667, 0.667, 0.278, 0.278, 0.278, 0.278, + 0.722, 0.722, 0.778, 0.778, 0.778, 0.778, 0.778, 0.584, + 0.778, 0.722, 0.722, 0.722, 0.722, 0.667, 0.667, 0.611, + 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.889, 0.500, + 0.556, 0.556, 0.556, 0.556, 0.278, 0.278, 0.278, 0.278, + 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.584, + 0.611, 0.556, 0.556, 0.556, 0.556, 0.500, 0.556, 0.500 +}; + +/** + * Character widths for Helvetica-BoldOblique + */ +const double hvbo_widths[256] = { + 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, + 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, + 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, + 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, 0.278, + 0.278, 0.333, 0.474, 0.556, 0.556, 0.889, 0.722, 0.238, + 0.333, 0.333, 0.389, 0.584, 0.278, 0.333, 0.278, 0.278, + 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, + 0.556, 0.556, 0.333, 0.333, 0.584, 0.584, 0.584, 0.611, + 0.975, 0.722, 0.722, 0.722, 0.722, 0.667, 0.611, 0.778, + 0.722, 0.278, 0.556, 0.722, 0.611, 0.833, 0.722, 0.778, + 0.667, 0.778, 0.722, 0.667, 0.611, 0.722, 0.667, 0.944, + 0.667, 0.667, 0.611, 0.333, 0.278, 0.333, 0.584, 0.556, + 0.333, 0.556, 0.611, 0.556, 0.611, 0.556, 0.333, 0.611, + 0.611, 0.278, 0.278, 0.556, 0.278, 0.889, 0.611, 0.611, + 0.611, 0.611, 0.389, 0.556, 0.333, 0.611, 0.556, 0.778, + 0.556, 0.556, 0.500, 0.389, 0.280, 0.389, 0.584, 0.278, + 0.278, 0.278, 0.278, 0.556, 0.500, 1.000, 0.556, 0.556, + 0.333, 1.000, 0.667, 0.333, 1.000, 0.278, 0.278, 0.278, + 0.278, 0.278, 0.278, 0.500, 0.500, 0.350, 0.556, 1.000, + 0.333, 1.000, 0.556, 0.333, 0.944, 0.278, 0.278, 0.667, + 0.278, 0.333, 0.556, 0.556, 0.556, 0.556, 0.280, 0.556, + 0.333, 0.737, 0.370, 0.556, 0.584, 0.333, 0.737, 0.333, + 0.400, 0.584, 0.333, 0.333, 0.333, 0.611, 0.556, 0.278, + 0.333, 0.333, 0.365, 0.556, 0.834, 0.834, 0.834, 0.611, + 0.722, 0.722, 0.722, 0.722, 0.722, 0.722, 1.000, 0.722, + 0.667, 0.667, 0.667, 0.667, 0.278, 0.278, 0.278, 0.278, + 0.722, 0.722, 0.778, 0.778, 0.778, 0.778, 0.778, 0.584, + 0.778, 0.722, 0.722, 0.722, 0.722, 0.667, 0.667, 0.611, + 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.889, 0.556, + 0.556, 0.556, 0.556, 0.556, 0.278, 0.278, 0.278, 0.278, + 0.611, 0.611, 0.611, 0.611, 0.611, 0.611, 0.611, 0.584, + 0.611, 0.611, 0.611, 0.611, 0.611, 0.556, 0.611, 0.556 +}; diff --git a/common/common_plotSVG_functions.cpp b/common/common_plotSVG_functions.cpp new file mode 100644 index 0000000..09b69d1 --- /dev/null +++ b/common/common_plotSVG_functions.cpp @@ -0,0 +1,623 @@ +/** + * @file common_plotPS_functions.cpp + * @brief Kicad: Common plot SVG functions + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 1992-2015 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 + */ + +/* Some info on basic items SVG format, used here: + * The root element of all SVG files is the <svg> element. + * + * The <g> element is used to group SVG shapes together. + * Once grouped you can transform the whole group of shapes as if it was a single shape. + * This is an advantage compared to a nested <svg> element + * which cannot be the target of transformation by itself. + * + * The <rect> element represents a rectangle. + * Using this element you can draw rectangles of various width, height, + * with different stroke (outline) and fill colors, with sharp or rounded corners etc. + * + * <svg xmlns="http://www.w3.org/2000/svg" + * xmlns:xlink="http://www.w3.org/1999/xlink"> + * + * <rect x="10" y="10" height="100" width="100" + * style="stroke:#006600; fill: #00cc00"/> + * + * </svg> + * + * The <circle> element is used to draw circles. + * <circle cx="40" cy="40" r="24" style="stroke:#006600; fill:#00cc00"/> + * + * The <ellipse> element is used to draw ellipses. + * An ellipse is a circle that does not have equal height and width. + * Its radius in the x and y directions are different, in other words. + * <ellipse cx="40" cy="40" rx="30" ry="15" + * style="stroke:#006600; fill:#00cc00"/> + * + * The <line> element is used to draw lines. + * + * <line x1="0" y1="10" x2="0" y2="100" style="stroke:#006600;"/> + * <line x1="10" y1="10" x2="100" y2="100" style="stroke:#006600;"/> + * + * The <polyline> element is used to draw multiple connected lines + * Here is a simple example: + * + * <polyline points="0,0 30,0 15,30" style="stroke:#006600;"/> + * + * The <polygon> element is used to draw with multiple (3 or more) sides / edges. + * Here is a simple example: + * + * <polygon points="0,0 50,0 25,50" style="stroke:#660000; fill:#cc3333;"/> + * + * The <path> element is used to draw advanced shapes combined from lines and arcs, + * with or without fill. + * It is probably the most advanced and versatile SVG shape of them all. + * It is probably also the hardest element to master. + * <path d="M50,50 + * A30,30 0 0,1 35,20 + * L100,100 + * M110,110 + * L100,0" + * style="stroke:#660000; fill:none;"/> + * + * Draw an elliptic arc: it is one of basic path command: + * <path d="M(startx,starty) A(radiusx,radiusy) + * rotation-axe-x + * flag_arc_large,flag_sweep endx,endy"> + * flag_arc_large: 0 = small arc > 180 deg, 1 = large arc > 180 deg + * flag_sweep : 0 = CCW, 1 = CW + * The center of ellipse is automatically calculated. + */ +#include <fctsys.h> +#include <trigo.h> +#include <wxstruct.h> +#include <base_struct.h> +#include <common.h> +#include <plot_common.h> +#include <macros.h> +#include <kicad_string.h> + + + +/** + * Function XmlEsc + * translates '<' to "<", '>' to ">" and so on, according to the spec: + * http://www.w3.org/TR/2000/WD-xml-c14n-20000119.html#charescaping + * May be moved to a library if needed generally, but not expecting that. + */ +static wxString XmlEsc( const wxString& aStr, bool isAttribute = false ) +{ + wxString escaped; + + escaped.reserve( aStr.length() ); + + for( wxString::const_iterator it = aStr.begin(); it != aStr.end(); ++it ) + { + const wxChar c = *it; + + switch( c ) + { + case wxS( '<' ): + escaped.append( wxS( "<" ) ); + break; + case wxS( '>' ): + escaped.append( wxS( ">" ) ); + break; + case wxS( '&' ): + escaped.append( wxS( "&" ) ); + break; + case wxS( '\r' ): + escaped.append( wxS( "
" ) ); + break; + default: + if( isAttribute ) + { + switch( c ) + { + case wxS( '"' ): + escaped.append( wxS( """ ) ); + break; + case wxS( '\t' ): + escaped.append( wxS( "	" ) ); + break; + case wxS( '\n' ): + escaped.append( wxS( "
" )); + break; + default: + escaped.append(c); + } + } + else + escaped.append(c); + } + } + + return escaped; +} + + +SVG_PLOTTER::SVG_PLOTTER() +{ + m_graphics_changed = true; + SetTextMode( PLOTTEXTMODE_STROKE ); + m_fillMode = NO_FILL; // or FILLED_SHAPE or FILLED_WITH_BG_BODYCOLOR + m_pen_rgb_color = 0; // current color value (black) + m_brush_rgb_color = 0; // current color value (black) + m_dashed = false; +} + + +void SVG_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil, + double aScale, bool aMirror ) +{ + m_plotMirror = aMirror; + m_yaxisReversed = true; // unlike other plotters, SVG has Y axis reversed + plotOffset = aOffset; + plotScale = aScale; + m_IUsPerDecimil = aIusPerDecimil; + iuPerDeviceUnit = 1.0 / aIusPerDecimil; + /* Compute the paper size in IUs */ + paperSize = pageInfo.GetSizeMils(); + paperSize.x *= 10.0 * aIusPerDecimil; + paperSize.y *= 10.0 * aIusPerDecimil; + SetDefaultLineWidth( 100 * aIusPerDecimil ); // arbitrary default +} + + +void SVG_PLOTTER::SetColor( EDA_COLOR_T color ) +{ + PSLIKE_PLOTTER::SetColor( color ); + + if( m_graphics_changed ) + setSVGPlotStyle(); +} + + +void SVG_PLOTTER::setFillMode( FILL_T fill ) +{ + if( m_fillMode != fill ) + { + m_graphics_changed = true; + m_fillMode = fill; + } +} + + +void SVG_PLOTTER::setSVGPlotStyle() +{ + fputs( "</g>\n<g style=\"", outputFile ); + fputs( "fill:#", outputFile ); + // output the background fill color + fprintf( outputFile, "%6.6lX; ", m_brush_rgb_color ); + + switch( m_fillMode ) + { + case NO_FILL: + fputs( "fill-opacity:0.0; ", outputFile ); + break; + + case FILLED_SHAPE: + fputs( "fill-opacity:1.0; ", outputFile ); + break; + + case FILLED_WITH_BG_BODYCOLOR: + fputs( "fill-opacity:0.6; ", outputFile ); + break; + } + + double pen_w = userToDeviceSize( GetCurrentLineWidth() ); + fprintf( outputFile, "\nstroke:#%6.6lX; stroke-width:%g; stroke-opacity:1; \n", + m_pen_rgb_color, pen_w ); + fputs( "stroke-linecap:round; stroke-linejoin:round;", outputFile ); + + if( m_dashed ) + fprintf( outputFile, "stroke-dasharray:%g,%g;", + GetDashMarkLenIU(), GetDashGapLenIU() ); + + fputs( "\">\n", outputFile ); + + m_graphics_changed = false; +} + +/* Set the current line width (in IUs) for the next plot + */ +void SVG_PLOTTER::SetCurrentLineWidth( int width ) +{ + int pen_width; + + if( width >= 0 ) + pen_width = width; + else + pen_width = defaultPenWidth; + + if( pen_width != currentPenWidth ) + { + m_graphics_changed = true; + currentPenWidth = pen_width; + } + + if( m_graphics_changed ) + setSVGPlotStyle(); +} + + +/* initialize m_red, m_green, m_blue ( 0 ... 255) + * from reduced values r, g ,b ( 0.0 to 1.0 ) + */ +void SVG_PLOTTER::emitSetRGBColor( double r, double g, double b ) +{ + int red = (int) ( 255.0 * r ); + int green = (int) ( 255.0 * g ); + int blue = (int) ( 255.0 * b ); + long rgb_color = (red << 16) | (green << 8) | blue; + + if( m_pen_rgb_color != rgb_color ) + { + m_graphics_changed = true; + m_pen_rgb_color = rgb_color; + + // Currently, use the same color for brush and pen + // (i.e. to draw and fill a contour) + m_brush_rgb_color = rgb_color; + } +} + + +/** + * SVG supports dashed lines + */ +void SVG_PLOTTER::SetDash( bool dashed ) +{ + if( m_dashed != dashed ) + { + m_graphics_changed = true; + m_dashed = dashed; + } + + if( m_graphics_changed ) + setSVGPlotStyle(); +} + + +void SVG_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width ) +{ + EDA_RECT rect( p1, wxSize( p2.x -p1.x, p2.y -p1.y ) ); + rect.Normalize(); + DPOINT org_dev = userToDeviceCoordinates( rect.GetOrigin() ); + DPOINT end_dev = userToDeviceCoordinates( rect.GetEnd() ); + DSIZE size_dev = end_dev - org_dev; + // Ensure size of rect in device coordinates is > 0 + // I don't know if this is a SVG issue or a Inkscape issue, but + // Inkscape has problems with negative or null values for width and/or height, so avoid them + DBOX rect_dev( org_dev, size_dev); + rect_dev.Normalize(); + + setFillMode( fill ); + SetCurrentLineWidth( width ); + + // Rectangles having a 0 size value for height or width are just not drawn on Inscape, + // so use a line when happens. + if( rect_dev.GetSize().x == 0.0 || rect_dev.GetSize().y == 0.0 ) // Draw a line + fprintf( outputFile, + "<line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\" />\n", + rect_dev.GetPosition().x, rect_dev.GetPosition().y, + rect_dev.GetEnd().x, rect_dev.GetEnd().y + ); + + else + fprintf( outputFile, + "<rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" />\n", + rect_dev.GetPosition().x, rect_dev.GetPosition().y, + rect_dev.GetSize().x, rect_dev.GetSize().y, + 0.0 // radius of rounded corners + ); +} + + +void SVG_PLOTTER::Circle( const wxPoint& pos, int diametre, FILL_T fill, int width ) +{ + DPOINT pos_dev = userToDeviceCoordinates( pos ); + double radius = userToDeviceSize( diametre / 2.0 ); + + setFillMode( fill ); + SetCurrentLineWidth( width ); + + fprintf( outputFile, + "<circle cx=\"%g\" cy=\"%g\" r=\"%g\" /> \n", + pos_dev.x, pos_dev.y, radius ); +} + + +void SVG_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius, + FILL_T fill, int width ) +{ + /* Draws an arc of a circle, centred on (xc,yc), with starting point + * (x1, y1) and ending at (x2, y2). The current pen is used for the outline + * and the current brush for filling the shape. + * + * The arc is drawn in an anticlockwise direction from the start point to + * the end point + */ + + if( radius <= 0 ) + return; + + if( StAngle > EndAngle ) + std::swap( StAngle, EndAngle ); + + setFillMode( fill ); + SetCurrentLineWidth( width ); + + // Calculate start point. + DPOINT centre_dev = userToDeviceCoordinates( centre ); + double radius_dev = userToDeviceSize( radius ); + + if( !m_yaxisReversed ) // Should be never the case + { + double tmp = StAngle; + StAngle = -EndAngle; + EndAngle = -tmp; + } + + if( m_plotMirror ) + { + if( m_mirrorIsHorizontal ) + { + StAngle = 1800.0 -StAngle; + EndAngle = 1800.0 -EndAngle; + std::swap( StAngle, EndAngle ); + } + else + { + StAngle = -StAngle; + EndAngle = -EndAngle; + } + } + + DPOINT start; + start.x = radius_dev; + RotatePoint( &start.x, &start.y, StAngle ); + DPOINT end; + end.x = radius_dev; + RotatePoint( &end.x, &end.y, EndAngle ); + start += centre_dev; + end += centre_dev; + + double theta1 = DECIDEG2RAD( StAngle ); + + if( theta1 < 0 ) + theta1 = theta1 + M_PI * 2; + + double theta2 = DECIDEG2RAD( EndAngle ); + + if( theta2 < 0 ) + theta2 = theta2 + M_PI * 2; + + if( theta2 < theta1 ) + theta2 = theta2 + M_PI * 2; + + int flg_arc = 0; // flag for large or small arc. 0 means less than 180 degrees + + if( fabs( theta2 - theta1 ) > M_PI ) + flg_arc = 1; + + int flg_sweep = 0; // flag for sweep always 0 + + // Draw a single arc: an arc is one of 3 curve commands (2 other are 2 bezier curves) + // params are start point, radius1, radius2, X axe rotation, + // flag arc size (0 = small arc > 180 deg, 1 = large arc > 180 deg), + // sweep arc ( 0 = CCW, 1 = CW), + // end point + fprintf( outputFile, "<path d=\"M%g %g A%g %g 0.0 %d %d %g %g \" />\n", + start.x, start.y, radius_dev, radius_dev, + flg_arc, flg_sweep, + end.x, end.y ); +} + + +void SVG_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList, + FILL_T aFill, int aWidth ) +{ + if( aCornerList.size() <= 1 ) + return; + + setFillMode( aFill ); + SetCurrentLineWidth( aWidth ); + + switch( aFill ) + { + case NO_FILL: + fprintf( outputFile, "<polyline fill=\"none;\"\n" ); + break; + + case FILLED_WITH_BG_BODYCOLOR: + case FILLED_SHAPE: + fprintf( outputFile, "<polyline style=\"fill-rule:evenodd;\"\n" ); + break; + } + + DPOINT pos = userToDeviceCoordinates( aCornerList[0] ); + fprintf( outputFile, "points=\"%d,%d\n", (int) pos.x, (int) pos.y ); + + for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) + { + pos = userToDeviceCoordinates( aCornerList[ii] ); + fprintf( outputFile, "%d,%d\n", (int) pos.x, (int) pos.y ); + } + + // Close/(fill) the path + fprintf( outputFile, "\" /> \n" ); +} + + +/** + * Postscript-likes at the moment are the only plot engines supporting bitmaps... + */ +void SVG_PLOTTER::PlotImage( const wxImage& aImage, const wxPoint& aPos, + double aScaleFactor ) +{ + // in svg file we must insert a link to a png image file to plot an image + // the image itself is not included in the svg file. + // So we prefer skip the image, and just draw a rectangle, + // like other plotters which do not support images + + PLOTTER::PlotImage( aImage, aPos, aScaleFactor ); + +} + + +void SVG_PLOTTER::PenTo( const wxPoint& pos, char plume ) +{ + if( plume == 'Z' ) + { + if( penState != 'Z' ) + { + fputs( "\" />\n", outputFile ); + penState = 'Z'; + penLastpos.x = -1; + penLastpos.y = -1; + } + + return; + } + + if( penState == 'Z' ) // here plume = 'D' or 'U' + { + DPOINT pos_dev = userToDeviceCoordinates( pos ); + + // Ensure we do not use a fill mode when moving tne pen, + // in SVG mode (i;e. we are plotting only basic lines, not a filled area + if( m_fillMode != NO_FILL ) + { + setFillMode( NO_FILL ); + setSVGPlotStyle(); + } + + fprintf( outputFile, "<path d=\"M%d %d\n", + (int) pos_dev.x, (int) pos_dev.y ); + } + else if( penState != plume || pos != penLastpos ) + { + DPOINT pos_dev = userToDeviceCoordinates( pos ); + fprintf( outputFile, "L%d %d\n", + (int) pos_dev.x, (int) pos_dev.y ); + } + + penState = plume; + penLastpos = pos; +} + + +/** + * The code within this function + * creates SVG files header + */ +bool SVG_PLOTTER::StartPlot() +{ + wxASSERT( outputFile ); + wxString msg; + + static const char* header[] = + { + "<?xml version=\"1.0\" standalone=\"no\"?>\n", + " <!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \n", + " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\"> \n", + "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" \n", + NULL + }; + + // Write header. + for( int ii = 0; header[ii] != NULL; ii++ ) + { + fputs( header[ii], outputFile ); + } + + // Write viewport pos and size + wxPoint origin; // TODO set to actual value + fprintf( outputFile, + " width=\"%gcm\" height=\"%gcm\" viewBox=\"%d %d %d %d \">\n", + (double) paperSize.x / m_IUsPerDecimil * 2.54 / 10000, + (double) paperSize.y / m_IUsPerDecimil * 2.54 / 10000, + origin.x, origin.y, + (int) ( paperSize.x / m_IUsPerDecimil ), + (int) ( paperSize.y / m_IUsPerDecimil) ); + + // Write title + char date_buf[250]; + time_t ltime = time( NULL ); + strftime( date_buf, 250, "%Y/%m/%d %H:%M:%S", + localtime( <ime ) ); + + fprintf( outputFile, + "<title>SVG Picture created as %s date %s </title>\n", + TO_UTF8( XmlEsc( wxFileName( filename ).GetFullName() ) ), date_buf ); + // End of header + fprintf( outputFile, " <desc>Picture generated by %s </desc>\n", + TO_UTF8( XmlEsc( creator ) ) ); + + // output the pen and brush color (RVB values in hex) and opacity + double opacity = 1.0; // 0.0 (transparent to 1.0 (solid) + fprintf( outputFile, + "<g style=\"fill:#%6.6lX; fill-opacity:%g;stroke:#%6.6lX; stroke-opacity:%g;\n", + m_brush_rgb_color, opacity, m_pen_rgb_color, opacity ); + + // output the pen cap and line joint + fputs( "stroke-linecap:round; stroke-linejoin:round; \"\n", outputFile ); + fputs( " transform=\"translate(0 0) scale(1 1)\">\n", outputFile ); + return true; +} + + +bool SVG_PLOTTER::EndPlot() +{ + fputs( "</g> \n</svg>\n", outputFile ); + fclose( outputFile ); + outputFile = NULL; + + return true; +} + + +void SVG_PLOTTER::Text( const wxPoint& aPos, + enum EDA_COLOR_T aColor, + const wxString& aText, + double aOrient, + const wxSize& aSize, + enum EDA_TEXT_HJUSTIFY_T aH_justify, + enum EDA_TEXT_VJUSTIFY_T aV_justify, + int aWidth, + bool aItalic, + bool aBold, + bool aMultilineAllowed ) +{ + setFillMode( NO_FILL ); + SetColor( aColor ); + SetCurrentLineWidth( aWidth ); + + // TODO: see if the postscript native text code can be used in SVG plotter + + PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, + aWidth, aItalic, aBold, aMultilineAllowed ); +} diff --git a/common/common_plot_functions.cpp b/common/common_plot_functions.cpp new file mode 100644 index 0000000..cda3788 --- /dev/null +++ b/common/common_plot_functions.cpp @@ -0,0 +1,165 @@ +/** + * @file common_plot_functions.cpp + * @brief Kicad: Common plotting functions + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 1992-2013 KiCad Developers, see change_log.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 <fctsys.h> +#include <base_struct.h> +#include <plot_common.h> +#include <worksheet.h> +#include <class_base_screen.h> +#include <drawtxt.h> +#include <class_title_block.h> +#include "worksheet_shape_builder.h" +#include "class_worksheet_dataitem.h" +#include <wx/filename.h> + + + +wxString GetDefaultPlotExtension( PlotFormat aFormat ) +{ + switch( aFormat ) + { + case PLOT_FORMAT_DXF: + return DXF_PLOTTER::GetDefaultFileExtension(); + + case PLOT_FORMAT_POST: + return PS_PLOTTER::GetDefaultFileExtension(); + + case PLOT_FORMAT_PDF: + return PDF_PLOTTER::GetDefaultFileExtension(); + + case PLOT_FORMAT_HPGL: + return HPGL_PLOTTER::GetDefaultFileExtension(); + + case PLOT_FORMAT_GERBER: + return GERBER_PLOTTER::GetDefaultFileExtension(); + + case PLOT_FORMAT_SVG: + return SVG_PLOTTER::GetDefaultFileExtension(); + + default: + wxASSERT( false ); + return wxEmptyString; + } +} + + + +void PlotWorkSheet( PLOTTER* plotter, const TITLE_BLOCK& aTitleBlock, + const PAGE_INFO& aPageInfo, + int aSheetNumber, int aNumberOfSheets, + const wxString &aSheetDesc, const wxString &aFilename ) +{ + /* Note: Page sizes values are given in mils + */ + double iusPerMil = plotter->GetIUsPerDecimil() * 10.0; + + EDA_COLOR_T plotColor = plotter->GetColorMode() ? RED : BLACK; + plotter->SetColor( plotColor ); + WS_DRAW_ITEM_LIST drawList; + + // Print only a short filename, if aFilename is the full filename + wxFileName fn( aFilename ); + + // Prepare plot parameters + drawList.SetPenSize(PLOTTER::USE_DEFAULT_LINE_WIDTH ); + drawList.SetMilsToIUfactor( iusPerMil ); + drawList.SetSheetNumber( aSheetNumber ); + drawList.SetSheetCount( aNumberOfSheets ); + drawList.SetFileName( fn.GetFullName() ); // Print only the short filename + drawList.SetSheetName( aSheetDesc ); + + + drawList.BuildWorkSheetGraphicList( aPageInfo, + aTitleBlock, plotColor, plotColor ); + + // Draw item list + for( WS_DRAW_ITEM_BASE* item = drawList.GetFirst(); item; + item = drawList.GetNext() ) + { + plotter->SetCurrentLineWidth( PLOTTER::USE_DEFAULT_LINE_WIDTH ); + + switch( item->GetType() ) + { + case WS_DRAW_ITEM_BASE::wsg_line: + { + WS_DRAW_ITEM_LINE* line = (WS_DRAW_ITEM_LINE*) item; + plotter->SetCurrentLineWidth( line->GetPenWidth() ); + plotter->MoveTo( line->GetStart() ); + plotter->FinishTo( line->GetEnd() ); + } + break; + + case WS_DRAW_ITEM_BASE::wsg_rect: + { + WS_DRAW_ITEM_RECT* rect = (WS_DRAW_ITEM_RECT*) item; + plotter->Rect( rect->GetStart(), + rect->GetEnd(), + NO_FILL, + rect->GetPenWidth() ); + } + break; + + case WS_DRAW_ITEM_BASE::wsg_text: + { + WS_DRAW_ITEM_TEXT* text = (WS_DRAW_ITEM_TEXT*) item; + plotter->Text( text->GetTextPosition(), text->GetColor(), + text->GetShownText(), text->GetOrientation(), + text->GetSize(), + text->GetHorizJustify(), text->GetVertJustify(), + text->GetPenWidth(), + text->IsItalic(), text->IsBold(), + text->IsMultilineAllowed() ); + } + break; + + case WS_DRAW_ITEM_BASE::wsg_poly: + { + WS_DRAW_ITEM_POLYGON* poly = (WS_DRAW_ITEM_POLYGON*) item; + plotter->PlotPoly( poly->m_Corners, + poly->IsFilled() ? FILLED_SHAPE : NO_FILL, + poly->GetPenWidth() ); + } + break; + + case WS_DRAW_ITEM_BASE::wsg_bitmap: + { + WS_DRAW_ITEM_BITMAP* bm = (WS_DRAW_ITEM_BITMAP*) item; + + WORKSHEET_DATAITEM_BITMAP* parent = (WORKSHEET_DATAITEM_BITMAP*)bm->GetParent(); + + if( parent->m_ImageBitmap == NULL ) + break; + + parent->m_ImageBitmap->PlotImage( plotter, bm->GetPosition(), + plotColor, PLOTTER::USE_DEFAULT_LINE_WIDTH ); + } + break; + } + } +} diff --git a/common/config_params.cpp b/common/config_params.cpp new file mode 100644 index 0000000..1882ac1 --- /dev/null +++ b/common/config_params.cpp @@ -0,0 +1,529 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com + * Copyright (C) 2008-2011 Wayne Stambaugh <stambaughw@verizon.net> + * Copyright (C) 1992-2011 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 <fctsys.h> +#include <gr_basic.h> +#include <pgm_base.h> +#include <common.h> +#include <kicad_string.h> +#include <gestfich.h> +#include <wxstruct.h> +#include <config_params.h> + +#include <wx/apptrait.h> +#include <wx/stdpaths.h> + +#include <wildcards_and_files_ext.h> + +#include <boost/foreach.hpp> + + +void wxConfigLoadParams( wxConfigBase* aCfg, + const PARAM_CFG_ARRAY& aList, const wxString& aGroup ) +{ + wxASSERT( aCfg ); + + BOOST_FOREACH( const PARAM_CFG_BASE& param, aList ) + { + if( !!param.m_Group ) + aCfg->SetPath( param.m_Group ); + else + aCfg->SetPath( aGroup ); + + if( param.m_Setup ) + continue; + + param.ReadParam( aCfg ); + } +} + + +void wxConfigLoadSetups( wxConfigBase* aCfg, const PARAM_CFG_ARRAY& aList ) +{ + wxASSERT( aCfg ); + + BOOST_FOREACH( const PARAM_CFG_BASE& param, aList ) + { + if( !param.m_Setup ) + continue; + + param.ReadParam( aCfg ); + } +} + + +void wxConfigSaveParams( wxConfigBase* aCfg, + const PARAM_CFG_ARRAY& aList, const wxString& aGroup ) +{ + wxASSERT( aCfg ); + + BOOST_FOREACH( const PARAM_CFG_BASE& param, aList ) + { + if( !!param.m_Group ) + aCfg->SetPath( param.m_Group ); + else + aCfg->SetPath( aGroup ); + + if( param.m_Setup ) + continue; + + if( param.m_Type == PARAM_COMMAND_ERASE ) // Erase all data + { + if( !!param.m_Ident ) + aCfg->DeleteGroup( param.m_Ident ); + } + else + { + param.SaveParam( aCfg ); + } + } +} + + +void wxConfigSaveSetups( wxConfigBase* aCfg, const PARAM_CFG_ARRAY& aList ) +{ + wxASSERT( aCfg ); + + BOOST_FOREACH( const PARAM_CFG_BASE& param, aList ) + { + if( !param.m_Setup ) + continue; + + if( param.m_Type == PARAM_COMMAND_ERASE ) // Erase all data + { + if( !!param.m_Ident ) + aCfg->DeleteGroup( param.m_Ident ); + } + else + { + param.SaveParam( aCfg ); + } + } +} + + +void ConfigBaseWriteDouble( wxConfigBase* aConfig, const wxString& aKey, double aValue ) +{ + // Use a single strategy, regardless of wx version. + // Want C locale float string. + + LOCALE_IO toggle; + wxString tnumber = wxString::Format( wxT( "%.16g" ), aValue ); + + aConfig->Write( aKey, tnumber ); +} + + +PARAM_CFG_BASE::PARAM_CFG_BASE( const wxString& ident, const paramcfg_id type, + const wxChar* group ) +{ + m_Ident = ident; + m_Type = type; + m_Group = group; + m_Setup = false; +} + + +PARAM_CFG_INT::PARAM_CFG_INT( const wxString& ident, int* ptparam, + int default_val, int min, int max, + const wxChar* group ) : + PARAM_CFG_BASE( ident, PARAM_INT, group ) +{ + m_Pt_param = ptparam; + m_Default = default_val; + m_Min = min; + m_Max = max; +} + + +PARAM_CFG_INT::PARAM_CFG_INT( bool Insetup, const wxString& ident, int* ptparam, + int default_val, int min, int max, + const wxChar* group ) : + PARAM_CFG_BASE( ident, PARAM_INT, group ) +{ + m_Pt_param = ptparam; + m_Default = default_val; + m_Min = min; + m_Max = max; + m_Setup = Insetup; +} + + +void PARAM_CFG_INT::ReadParam( wxConfigBase* aConfig ) const +{ + if( !m_Pt_param || !aConfig ) + return; + + int itmp = aConfig->Read( m_Ident, m_Default ); + + if( (itmp < m_Min) || (itmp > m_Max) ) + itmp = m_Default; + + *m_Pt_param = itmp; +} + + +void PARAM_CFG_INT::SaveParam( wxConfigBase* aConfig ) const +{ + if( !m_Pt_param || !aConfig ) + return; + + aConfig->Write( m_Ident, *m_Pt_param ); +} + + +PARAM_CFG_INT_WITH_SCALE::PARAM_CFG_INT_WITH_SCALE( const wxString& ident, int* ptparam, + int default_val, int min, int max, + const wxChar* group, double aBiu2cfgunit ) : + PARAM_CFG_INT( ident, ptparam, default_val, min, max, group ) +{ + m_Type = PARAM_INT_WITH_SCALE; + m_BIU_to_cfgunit = aBiu2cfgunit; +} + + +PARAM_CFG_INT_WITH_SCALE::PARAM_CFG_INT_WITH_SCALE( bool Insetup, + const wxString& ident, int* ptparam, + int default_val, int min, int max, + const wxChar* group, double aBiu2cfgunit ) : + PARAM_CFG_INT( Insetup, ident, ptparam, default_val, min, max, group ) +{ + m_Type = PARAM_INT_WITH_SCALE; + m_BIU_to_cfgunit = aBiu2cfgunit; +} + + +void PARAM_CFG_INT_WITH_SCALE::ReadParam( wxConfigBase* aConfig ) const +{ + if( !m_Pt_param || !aConfig ) + return; + + double dtmp = (double) m_Default * m_BIU_to_cfgunit; + aConfig->Read( m_Ident, &dtmp ); + + int itmp = KiROUND( dtmp / m_BIU_to_cfgunit ); + + if( (itmp < m_Min) || (itmp > m_Max) ) + itmp = m_Default; + + *m_Pt_param = itmp; +} + + +void PARAM_CFG_INT_WITH_SCALE::SaveParam( wxConfigBase* aConfig ) const +{ + if( !m_Pt_param || !aConfig ) + return; + + // We cannot use aConfig->Write for a double, because + // this function uses a format with very few digits in mantissa, + // and truncature issues are frequent. + // We uses our function. + ConfigBaseWriteDouble( aConfig, m_Ident, *m_Pt_param * m_BIU_to_cfgunit ); +} + + +PARAM_CFG_SETCOLOR::PARAM_CFG_SETCOLOR( const wxString& ident, EDA_COLOR_T* ptparam, + EDA_COLOR_T default_val, + const wxChar* group ) : + PARAM_CFG_BASE( ident, PARAM_SETCOLOR, group ) +{ + m_Pt_param = ptparam; + m_Default = default_val; +} + + +PARAM_CFG_SETCOLOR::PARAM_CFG_SETCOLOR( bool Insetup, + const wxString& ident, + EDA_COLOR_T* ptparam, + EDA_COLOR_T default_val, + const wxChar* group ) : + PARAM_CFG_BASE( ident, PARAM_SETCOLOR, group ) +{ + m_Pt_param = ptparam; + m_Default = default_val; + m_Setup = Insetup; +} + + +void PARAM_CFG_SETCOLOR::ReadParam( wxConfigBase* aConfig ) const +{ + if( !m_Pt_param || !aConfig ) + return; + + EDA_COLOR_T itmp = ColorByName( aConfig->Read( m_Ident, wxT("NONE") ) ); + + if( itmp == UNSPECIFIED_COLOR ) + itmp = m_Default; + *m_Pt_param = itmp; +} + + +void PARAM_CFG_SETCOLOR::SaveParam( wxConfigBase* aConfig ) const +{ + if( !m_Pt_param || !aConfig ) + return; + + aConfig->Write( m_Ident, ColorGetName( *m_Pt_param ) ); +} + + +PARAM_CFG_DOUBLE::PARAM_CFG_DOUBLE( const wxString& ident, double* ptparam, + double default_val, double min, double max, + const wxChar* group ) : + PARAM_CFG_BASE( ident, PARAM_DOUBLE, group ) +{ + m_Pt_param = ptparam; + m_Default = default_val; + m_Min = min; + m_Max = max; +} + + +PARAM_CFG_DOUBLE::PARAM_CFG_DOUBLE( bool Insetup, + const wxString& ident, + double* ptparam, + double default_val, + double min, + double max, + const wxChar* group ) : + PARAM_CFG_BASE( ident, PARAM_DOUBLE, group ) +{ + m_Pt_param = ptparam; + m_Default = default_val; + m_Min = min; + m_Max = max; + m_Setup = Insetup; +} + + +void PARAM_CFG_DOUBLE::ReadParam( wxConfigBase* aConfig ) const +{ + if( !m_Pt_param || !aConfig ) + return; + + double dtmp = m_Default; + aConfig->Read( m_Ident, &dtmp ); + + if( (dtmp < m_Min) || (dtmp > m_Max) ) + dtmp = m_Default; + + *m_Pt_param = dtmp; +} + + +void PARAM_CFG_DOUBLE::SaveParam( wxConfigBase* aConfig ) const +{ + if( !m_Pt_param || !aConfig ) + return; + + // We cannot use aConfig->Write for a double, because + // this function uses a format with very few digits in mantissa, + // and truncature issues are frequent. + // We uses our function. + ConfigBaseWriteDouble( aConfig, m_Ident, *m_Pt_param ); +} + + +PARAM_CFG_BOOL::PARAM_CFG_BOOL( const wxString& ident, bool* ptparam, + int default_val, const wxChar* group ) : + PARAM_CFG_BASE( ident, PARAM_BOOL, group ) +{ + m_Pt_param = ptparam; + m_Default = default_val ? true : false; +} + + +PARAM_CFG_BOOL::PARAM_CFG_BOOL( bool Insetup, + const wxString& ident, + bool* ptparam, + int default_val, + const wxChar* group ) : + PARAM_CFG_BASE( ident, PARAM_BOOL, group ) +{ + m_Pt_param = ptparam; + m_Default = default_val ? true : false; + m_Setup = Insetup; +} + + +void PARAM_CFG_BOOL::ReadParam( wxConfigBase* aConfig ) const +{ + if( !m_Pt_param || !aConfig ) + return; + + int itmp = aConfig->Read( m_Ident, (int) m_Default ); + + *m_Pt_param = itmp ? true : false; +} + + +void PARAM_CFG_BOOL::SaveParam( wxConfigBase* aConfig ) const +{ + if( !m_Pt_param || !aConfig ) + return; + + aConfig->Write( m_Ident, *m_Pt_param ); +} + + +PARAM_CFG_WXSTRING::PARAM_CFG_WXSTRING( const wxString& ident, + wxString* ptparam, + const wxChar* group ) : + PARAM_CFG_BASE( ident, PARAM_WXSTRING, group ) +{ + m_Pt_param = ptparam; +} + + +PARAM_CFG_WXSTRING::PARAM_CFG_WXSTRING( bool Insetup, const wxString& ident, + wxString* ptparam, + const wxString& default_val, + const wxChar* group ) : + PARAM_CFG_BASE( ident, PARAM_WXSTRING, group ) +{ + m_Pt_param = ptparam; + m_Setup = Insetup; + m_default = default_val; +} + + +void PARAM_CFG_WXSTRING::ReadParam( wxConfigBase* aConfig ) const +{ + if( !m_Pt_param || !aConfig ) + return; + + *m_Pt_param = aConfig->Read( m_Ident, m_default ); +} + + +void PARAM_CFG_WXSTRING::SaveParam( wxConfigBase* aConfig ) const +{ + if( !m_Pt_param || !aConfig ) + return; + + aConfig->Write( m_Ident, *m_Pt_param ); +} + + +PARAM_CFG_FILENAME::PARAM_CFG_FILENAME( const wxString& ident, + wxString* ptparam, + const wxChar* group ) : + PARAM_CFG_BASE( ident, PARAM_FILENAME, group ) +{ + m_Pt_param = ptparam; +} + + +void PARAM_CFG_FILENAME::ReadParam( wxConfigBase* aConfig ) const +{ + if( !m_Pt_param || !aConfig ) + return; + + wxString prm = aConfig->Read( m_Ident ); + // file names are stored using Unix notation + // under Window we must use \ instead of / + // mainly if there is a server name in path (something like \\server\kicad) +#ifdef __WINDOWS__ + prm.Replace(wxT("/"), wxT("\\")); +#endif + *m_Pt_param = prm; +} + + +void PARAM_CFG_FILENAME::SaveParam( wxConfigBase* aConfig ) const +{ + if( !m_Pt_param || !aConfig ) + return; + + wxString prm = *m_Pt_param; + // filenames are stored using Unix notation + prm.Replace(wxT("\\"), wxT("/") ); + aConfig->Write( m_Ident, prm ); +} + + +PARAM_CFG_LIBNAME_LIST::PARAM_CFG_LIBNAME_LIST( const wxChar* ident, + wxArrayString* ptparam, + const wxChar* group ) : + PARAM_CFG_BASE( ident, PARAM_LIBNAME_LIST, group ) +{ + m_Pt_param = ptparam; +} + + +void PARAM_CFG_LIBNAME_LIST::ReadParam( wxConfigBase* aConfig ) const +{ + if( !m_Pt_param || !aConfig ) + return; + + int indexlib = 1; // We start indexlib to 1 because first + // lib name is LibName1 + wxString libname, id_lib; + wxArrayString* libname_list = m_Pt_param; + + while( 1 ) + { + id_lib = m_Ident; + id_lib << indexlib; + indexlib++; + libname = aConfig->Read( id_lib, wxT( "" ) ); + + if( libname.IsEmpty() ) + break; + // file names are stored using Unix notation + // under Window we must use \ instead of / + // mainly if there is a server name in path (something like \\server\kicad) +#ifdef __WINDOWS__ + libname.Replace(wxT("/"), wxT("\\")); +#endif + libname_list->Add( libname ); + } +} + + +void PARAM_CFG_LIBNAME_LIST::SaveParam( wxConfigBase* aConfig ) const +{ + if( !m_Pt_param || !aConfig ) + return; + + wxArrayString* libname_list = m_Pt_param; + + wxString configkey; + wxString libname; + + for( unsigned indexlib = 0; indexlib < libname_list->GetCount(); indexlib++ ) + { + configkey = m_Ident; + + // We use indexlib+1 because first lib name is LibName1 + configkey << (indexlib + 1); + libname = libname_list->Item( indexlib ); + + // filenames are stored using Unix notation + libname.Replace(wxT("\\"), wxT("/") ); + aConfig->Write( configkey, libname ); + } +} diff --git a/common/confirm.cpp b/common/confirm.cpp new file mode 100644 index 0000000..7aeb85f --- /dev/null +++ b/common/confirm.cpp @@ -0,0 +1,160 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2007 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 1992-2013 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 + */ + +/** + * @file confirm.cpp + * @brief utilities to display some error, warning and info short messges + */ + +#include <fctsys.h> +#include <common.h> +#include <wx/wx.h> +#include <wx/html/htmlwin.h> +#include <wx/stockitem.h> +#include <html_messagebox.h> +#include <dialog_exit_base.h> +#include <bitmaps.h> + +class DIALOG_EXIT: public DIALOG_EXIT_BASE +{ +public: + DIALOG_EXIT( wxWindow *aParent, const wxString& aMessage ) : + DIALOG_EXIT_BASE( aParent ) + { + m_bitmap->SetBitmap( KiBitmap( dialog_warning_xpm ) ); + + if( !aMessage.IsEmpty() ) + m_TextInfo->SetLabel( aMessage ); + + GetSizer()->Fit( this ); + GetSizer()->SetSizeHints( this ); + }; + +private: + void OnSaveAndExit( wxCommandEvent& event ) { EndModal( wxID_YES ); } + void OnCancel( wxCommandEvent& event ) { EndModal( wxID_CANCEL ); } + void OnExitNoSave( wxCommandEvent& event ) { EndModal( wxID_NO ); } +}; + + +int DisplayExitDialog( wxWindow* parent, const wxString& aMessage ) +{ + DIALOG_EXIT dlg( parent, aMessage ); + + int ret = dlg.ShowModal(); + return ret; +} + + +void DisplayError( wxWindow* parent, const wxString& text, int displaytime ) +{ + wxMessageDialog* dialog; + + if( displaytime > 0 ) + dialog = new wxMessageDialog( parent, text, _( "Warning" ), + wxOK | wxCENTRE | wxICON_INFORMATION + | wxRESIZE_BORDER + ); + else + dialog = new wxMessageDialog( parent, text, _( "Error" ), + wxOK | wxCENTRE | wxICON_ERROR + | wxRESIZE_BORDER + ); + + dialog->ShowModal(); + dialog->Destroy(); +} + + +void DisplayInfoMessage( wxWindow* parent, const wxString& text, int displaytime ) +{ + wxMessageDialog* dialog; + + dialog = new wxMessageDialog( parent, text, _( "Info" ), + wxOK | wxCENTRE | wxICON_INFORMATION ); + + dialog->ShowModal(); + dialog->Destroy(); +} + + +void DisplayHtmlInfoMessage( wxWindow* parent, const wxString& title, + const wxString& text, const wxSize& size ) +{ + HTML_MESSAGE_BOX dlg( parent, title, wxDefaultPosition, size ); + + dlg.AddHTML_Text( text ); + dlg.ShowModal(); +} + + +bool IsOK( wxWindow* aParent, const wxString& aMessage ) +{ + wxMessageDialog dlg( aParent, aMessage, _( "Confirmation" ), + wxYES_NO | wxCENTRE | wxICON_QUESTION ); + + return dlg.ShowModal() == wxID_YES; +} + + +class DIALOG_YES_NO_CANCEL : public DIALOG_EXIT +{ +public: + DIALOG_YES_NO_CANCEL( wxWindow *aParent, + const wxString& aPrimaryMessage, + const wxString& aSecondaryMessage = wxEmptyString, + const wxString& aYesButtonText = wxEmptyString, + const wxString& aNoButtonText = wxEmptyString, + const wxString& aCancelButtonText = wxEmptyString ) : + DIALOG_EXIT( aParent, aSecondaryMessage ) + { + m_TextInfo->SetLabel( aPrimaryMessage ); + + if( aSecondaryMessage.IsEmpty() ) + m_staticText2->Hide(); + + m_buttonSaveAndExit->SetLabel( aYesButtonText.IsEmpty() ? wxGetStockLabel( wxID_YES ) : + aYesButtonText ); + m_buttonExitNoSave->SetLabel( aNoButtonText.IsEmpty() ? wxGetStockLabel( wxID_NO ) : + aNoButtonText ); + m_buttonCancel->SetLabel( aCancelButtonText.IsEmpty() ? wxGetStockLabel( wxID_CANCEL ) : + aCancelButtonText ); + GetSizer()->Fit( this ); + GetSizer()->SetSizeHints( this ); + }; +}; + + +int YesNoCancelDialog( wxWindow* aParent, + const wxString& aPrimaryMessage, + const wxString& aSecondaryMessage, + const wxString& aYesButtonText, + const wxString& aNoButtonText, + const wxString& aCancelButtonText ) +{ + DIALOG_YES_NO_CANCEL dlg( aParent, aPrimaryMessage, aSecondaryMessage, + aYesButtonText, aNoButtonText, aCancelButtonText ); + + return dlg.ShowModal(); +} diff --git a/common/convert_basic_shapes_to_polygon.cpp b/common/convert_basic_shapes_to_polygon.cpp new file mode 100644 index 0000000..fbbd3c1 --- /dev/null +++ b/common/convert_basic_shapes_to_polygon.cpp @@ -0,0 +1,249 @@ +/** + * @file convert_basic_shapes_to_polygon.cpp + */ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 1992-2012 KiCad Developers, see change_log.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 <vector> + +#include <fctsys.h> +#include <trigo.h> +#include <macros.h> +#include <common.h> +#include <convert_basic_shapes_to_polygon.h> + + +/** + * Function TransformCircleToPolygon + * convert a circle to a polygon, using multiple straight lines + * @param aCornerBuffer = a buffer to store the polygon + * @param aCenter = the center of the circle + * @param aRadius = the radius of the circle + * @param aCircleToSegmentsCount = the number of segments to approximate a circle + * Note: the polygon is inside the circle, so if you want to have the polygon + * outside the circle, you should give aRadius calculated with a corrrection factor + */ +void TransformCircleToPolygon( SHAPE_POLY_SET& aCornerBuffer, + wxPoint aCenter, int aRadius, + int aCircleToSegmentsCount ) +{ + wxPoint corner_position; + int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree + int halfstep = 1800 / aCircleToSegmentsCount; // the starting value for rot angles + + aCornerBuffer.NewOutline(); + + for( int ii = 0; ii < aCircleToSegmentsCount; ii++ ) + { + corner_position.x = aRadius; + corner_position.y = 0; + int angle = (ii * delta) + halfstep; + RotatePoint( &corner_position.x, &corner_position.y, angle ); + corner_position += aCenter; + aCornerBuffer.Append( corner_position.x, corner_position.y ); + } +} + + +/** + * Function TransformRoundedEndsSegmentToPolygon + * convert a segment with rounded ends to a polygon + * Convert arcs to multiple straight lines + * @param aCornerBuffer = a buffer to store the polygon + * @param aStart = the segment start point coordinate + * @param aEnd = the segment end point coordinate + * @param aCircleToSegmentsCount = the number of segments to approximate a circle + * @param aWidth = the segment width + * Note: the polygon is inside the arc ends, so if you want to have the polygon + * outside the circle, you should give aStart and aEnd calculated with a correction factor + */ +void TransformRoundedEndsSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer, + wxPoint aStart, wxPoint aEnd, + int aCircleToSegmentsCount, + int aWidth ) +{ + int radius = aWidth / 2; + wxPoint endp = aEnd - aStart; // end point coordinate for the same segment starting at (0,0) + wxPoint startp = aStart; + wxPoint corner; + VECTOR2I polypoint; + + aCornerBuffer.NewOutline(); + + // normalize the position in order to have endp.x >= 0; + if( endp.x < 0 ) + { + endp = aStart - aEnd; + startp = aEnd; + } + + double delta_angle = ArcTangente( endp.y, endp.x ); // delta_angle is in 0.1 degrees + int seg_len = KiROUND( EuclideanNorm( endp ) ); + + int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree + + // Compute the outlines of the segment, and creates a polygon + // add right rounded end: + for( int ii = 0; ii < 1800; ii += delta ) + { + corner = wxPoint( 0, radius ); + RotatePoint( &corner, ii ); + corner.x += seg_len; + RotatePoint( &corner, -delta_angle ); + corner += startp; + polypoint.x = corner.x; + polypoint.y = corner.y; + aCornerBuffer.Append( polypoint.x, polypoint.y ); + } + + // Finish arc: + corner = wxPoint( seg_len, -radius ); + RotatePoint( &corner, -delta_angle ); + corner += startp; + polypoint.x = corner.x; + polypoint.y = corner.y; + aCornerBuffer.Append( polypoint.x, polypoint.y ); + + // add left rounded end: + for( int ii = 0; ii < 1800; ii += delta ) + { + corner = wxPoint( 0, -radius ); + RotatePoint( &corner, ii ); + RotatePoint( &corner, -delta_angle ); + corner += startp; + polypoint.x = corner.x; + polypoint.y = corner.y; + aCornerBuffer.Append( polypoint.x, polypoint.y ); + } + + // Finish arc: + corner = wxPoint( 0, radius ); + RotatePoint( &corner, -delta_angle ); + corner += startp; + polypoint.x = corner.x; + polypoint.y = corner.y; + aCornerBuffer.Append( polypoint.x, polypoint.y ); +} + + +/** + * Function TransformArcToPolygon + * Creates a polygon from an Arc + * Convert arcs to multiple straight segments + * @param aCornerBuffer = a buffer to store the polygon + * @param aCentre = centre of the arc or circle + * @param aStart = start point of the arc, or a point on the circle + * @param aArcAngle = arc angle in 0.1 degrees. For a circle, aArcAngle = 3600 + * @param aCircleToSegmentsCount = the number of segments to approximate a circle + * @param aWidth = width (thickness) of the line + */ +void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer, + wxPoint aCentre, wxPoint aStart, double aArcAngle, + int aCircleToSegmentsCount, int aWidth ) +{ + wxPoint arc_start, arc_end; + int delta = 3600 / aCircleToSegmentsCount; // rotate angle in 0.1 degree + + arc_end = arc_start = aStart; + + if( aArcAngle != 3600 ) + { + RotatePoint( &arc_end, aCentre, -aArcAngle ); + } + + if( aArcAngle < 0 ) + { + std::swap( arc_start, arc_end ); + aArcAngle = -aArcAngle; + } + + // Compute the ends of segments and creates poly + wxPoint curr_end = arc_start; + wxPoint curr_start = arc_start; + + for( int ii = delta; ii < aArcAngle; ii += delta ) + { + curr_end = arc_start; + RotatePoint( &curr_end, aCentre, -ii ); + TransformRoundedEndsSegmentToPolygon( aCornerBuffer, curr_start, curr_end, + aCircleToSegmentsCount, aWidth ); + curr_start = curr_end; + } + + if( curr_end != arc_end ) + TransformRoundedEndsSegmentToPolygon( aCornerBuffer, + curr_end, arc_end, + aCircleToSegmentsCount, aWidth ); +} + + +/** + * Function TransformRingToPolygon + * Creates a polygon from a ring + * Convert arcs to multiple straight segments + * @param aCornerBuffer = a buffer to store the polygon + * @param aCentre = centre of the arc or circle + * @param aRadius = radius of the circle + * @param aCircleToSegmentsCount = the number of segments to approximate a circle + * @param aWidth = width (thickness) of the ring + */ +void TransformRingToPolygon( SHAPE_POLY_SET& aCornerBuffer, + wxPoint aCentre, int aRadius, + int aCircleToSegmentsCount, int aWidth ) +{ + int delta = 3600 / aCircleToSegmentsCount; // rotate angle in 0.1 degree + + // Compute the corners posituions and creates poly + wxPoint curr_point; + int inner_radius = aRadius - ( aWidth / 2 ); + int outer_radius = inner_radius + aWidth; + + aCornerBuffer.NewOutline(); + + // Draw the inner circle of the ring + for( int ii = 0; ii < 3600; ii += delta ) + { + curr_point.x = inner_radius; + curr_point.y = 0; + RotatePoint( &curr_point, ii ); + curr_point += aCentre; + aCornerBuffer.Append( curr_point.x, curr_point.y ); + } + + // Draw the last point of inner circle + aCornerBuffer.Append( aCentre.x + inner_radius, aCentre.y ); + + // Draw the outer circle of the ring + for( int ii = 0; ii < 3600; ii += delta ) + { + curr_point.x = outer_radius; + curr_point.y = 0; + RotatePoint( &curr_point, -ii ); + curr_point += aCentre; + aCornerBuffer.Append( curr_point.x, curr_point.y ); + } + + // Draw the last point of outer circle + aCornerBuffer.Append( aCentre.x + outer_radius, aCentre.y ); + aCornerBuffer.Append( aCentre.x + inner_radius, aCentre.y ); +} diff --git a/common/copy_to_clipboard.cpp b/common/copy_to_clipboard.cpp new file mode 100644 index 0000000..1c58546 --- /dev/null +++ b/common/copy_to_clipboard.cpp @@ -0,0 +1,159 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2006 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com + * Copyright (C) 1992-2011 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 + */ + +/** + * @file copy_to_clipboard.cpp + */ + +#include <wx/clipbrd.h> +//#include <wx/metafile.h> +#include <fctsys.h> +#include <gr_basic.h> +#include <common.h> +#include <id.h> +#include <class_drawpanel.h> +#include <class_base_screen.h> +#include <confirm.h> +#include <draw_frame.h> + +static bool DrawPageOnClipboard( EDA_DRAW_FRAME* aFrame ); + + +void EDA_DRAW_FRAME::CopyToClipboard( wxCommandEvent& event ) +{ + DrawPageOnClipboard( this ); + + if( event.GetId() == ID_GEN_COPY_BLOCK_TO_CLIPBOARD ) + { + if( GetScreen()->IsBlockActive() ) + m_canvas->SetCursor( wxCursor( (wxStockCursor) m_canvas->GetDefaultCursor() ) ); + + m_canvas->EndMouseCapture(); + } +} + + +/* copy the current page or block to the clipboard , + * to export drawings to other applications (word processing ...) + * This is not suitable for copy command within Eeschema or Pcbnew + */ +bool DrawPageOnClipboard( EDA_DRAW_FRAME* aFrame ) +{ + bool DrawBlock = false; + wxRect DrawArea; + BASE_SCREEN* screen = aFrame->GetCanvas()->GetScreen(); + + if( screen->IsBlockActive() ) + { + DrawBlock = true; + DrawArea.SetX( screen->m_BlockLocate.GetX() ); + DrawArea.SetY( screen->m_BlockLocate.GetY() ); + DrawArea.SetWidth( screen->m_BlockLocate.GetWidth() ); + DrawArea.SetHeight( screen->m_BlockLocate.GetHeight() ); + } + else + DrawArea.SetSize( aFrame->GetPageSizeIU() ); + + // Calculate a reasonable dc size, in pixels, and the dc scale to fit + // the drawings into the dc size + // scale is the ratio resolution (in PPI) / internal units + double ppi = 300; // Use 300 pixels per inch to create bitmap images on start + double inch2Iu = 1000.0 * (double) screen->MilsToIuScalar(); + double scale = ppi / inch2Iu; + + wxSize dcsize = DrawArea.GetSize(); + + int maxdim = std::max( dcsize.x, dcsize.y ); + // the max size in pixels of the bitmap used to byuild the sheet copy + const int maxbitmapsize = 3000; + + while( int( maxdim * scale ) > maxbitmapsize ) + { + ppi = ppi / 1.5; + scale = ppi / inch2Iu; + } + + dcsize.x *= scale; + dcsize.y *= scale; + + // Set draw offset, zoom... to values needed to draw in the memory DC + // after saving initial values: + wxPoint tmp_startvisu = screen->m_StartVisu; + double tmpzoom = screen->GetZoom(); + wxPoint old_org = screen->m_DrawOrg; + screen->m_DrawOrg.x = screen->m_DrawOrg.y = 0; + screen->m_StartVisu.x = screen->m_StartVisu.y = 0; + + screen->SetZoom( 1 ); // we use zoom = 1 in draw functions. + + wxMemoryDC dc; + wxBitmap image( dcsize ); + dc.SelectObject( image ); + + EDA_RECT tmp = *aFrame->GetCanvas()->GetClipBox(); + GRResetPenAndBrush( &dc ); + GRForceBlackPen( false ); + screen->m_IsPrinting = true; + dc.SetUserScale( scale, scale ); + + aFrame->GetCanvas()->SetClipBox( EDA_RECT( wxPoint( 0, 0 ), + wxSize( 0x7FFFFF0, 0x7FFFFF0 ) ) ); + + if( DrawBlock ) + { + dc.SetClippingRegion( DrawArea ); + } + + dc.Clear(); + aFrame->GetCanvas()->EraseScreen( &dc ); + const LSET allLayersMask = LSET().set(); + aFrame->PrintPage( &dc, allLayersMask, false ); + screen->m_IsPrinting = false; + aFrame->GetCanvas()->SetClipBox( tmp ); + + bool success = true; + + if( wxTheClipboard->Open() ) + { + // This data objects are held by the clipboard, + // so do not delete them in the app. + wxBitmapDataObject* clipbrd_data = new wxBitmapDataObject( image ); + wxTheClipboard->SetData( clipbrd_data ); + wxTheClipboard->Close(); + } + else + success = false; + + // Deselect Bitmap from DC in order to delete the MemoryDC safely + // without deleting the bitmap + dc.SelectObject( wxNullBitmap ); + + GRForceBlackPen( false ); + + screen->m_StartVisu = tmp_startvisu; + screen->m_DrawOrg = old_org; + screen->SetZoom( tmpzoom ); + + return success; +} diff --git a/common/dialog_about/AboutDialog_main.cpp b/common/dialog_about/AboutDialog_main.cpp new file mode 100644 index 0000000..662229a --- /dev/null +++ b/common/dialog_about/AboutDialog_main.cpp @@ -0,0 +1,501 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2010 Rafael Sokolowski <Rafael.Sokolowski@web.de> + * Copyright (C) 2010-2015 KiCad Developers, see CHANGELOG.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 <dialog_about.h> +#include <aboutinfo.h> +#include <wx/aboutdlg.h> +#include <wx/textctrl.h> +#include <boost/version.hpp> + + +/* Used icons: + * lang_xx_xpm[]; // Icons of various national flags + * show_3d_xpm[]; // 3D icon + * edit_module_xpm[]; + * icon_kicad_xpm[]; // Icon of the application + */ +#include <bitmaps.h> +#include <wxstruct.h> +#include <common.h> +#include <pgm_base.h> +#include <build_version.h> + + +#include <wx/arrimpl.cpp> +WX_DEFINE_OBJARRAY( Contributors ) + +// Helper functions: +static wxString HtmlHyperlink( const wxString& url, const wxString& description = wxEmptyString ); +static wxString HtmlNewline( const unsigned int amount = 1 ); + + +/** + * Initializes the <code>AboutAppInfo</code> object with application specific information. + * + * This the object which holds all information about the application + */ +static void InitKiCadAboutNew( AboutAppInfo& info ) +{ + // Set application specific icon + const wxTopLevelWindow* const tlw = wxDynamicCast( Pgm().App().GetTopWindow(), + wxTopLevelWindow ); + + if( tlw ) + info.SetIcon( tlw->GetIcon() ); + else + { + wxBitmap bitmap = KiBitmap( icon_kicad_xpm ); + wxIcon icon; + + icon.CopyFromBitmap( bitmap ); + + info.SetIcon( icon ); + } + + /* Set title */ + info.SetAppName( wxT( ".: " ) + Pgm().App().GetAppName() + wxT( " :." ) ); + + /* Copyright information */ + info.SetCopyright( wxT( "(C) 1992-2015 KiCad Developers Team" ) ); + + /* KiCad build version */ + wxString version; + version << wxT( "Version: " ) << GetBuildVersion() +#ifdef DEBUG + << wxT( ", debug" ) +#else + << wxT( ", release" ) +#endif + << wxT( " build" ); + + info.SetBuildVersion( version ); + + /* wxWidgets version */ + wxString libVersion; + libVersion << wxT( "wxWidgets " ) + << wxMAJOR_VERSION << wxT( "." ) + << wxMINOR_VERSION << wxT( "." ) + << wxRELEASE_NUMBER + + /* Unicode or ANSI version */ +#if wxUSE_UNICODE + << wxT( " Unicode " ); +#else + << wxT( " ANSI " ); +#endif + + // Just in case someone builds KiCad with the platform native of Boost instead of + // the version included with the KiCad source. + libVersion << wxT( "and Boost " ) << ( BOOST_VERSION / 100000 ) << wxT( "." ) + << ( BOOST_VERSION / 100 % 1000 ) << wxT( "." ) << ( BOOST_VERSION % 100 ) + << wxT( "\n" ); + + // Operating System Information + + wxPlatformInfo platformInfo; + + libVersion << wxT( "Platform: " ) << wxGetOsDescription() << wxT( ", " ) + << platformInfo.GetArchName(); + + info.SetLibVersion( libVersion ); + + + /* info/description part HTML formatted */ + + wxString description; + + /* short description */ + description << wxT( "<p>" ); + description << wxT( "<b><u>" ) + << _( "Description" ) + << wxT( "</u></b>" ); // bold & underlined font for caption + + description << wxT( "<p>" ) + << _( "The KiCad EDA Suite is a set of open source applications for the " + "creation of electronic schematics and to design printed circuit boards." ) + << wxT( "</p>" ); + + description << wxT( "</p>" ); + + /* websites */ + description << wxT( "<p>" ); + description << wxT( "<b><u>" ) + << _( "KiCad on the web" ) + << wxT( "</u></b>" ); // bold & underlined font for caption + + // bullet-ed list with some http links + description << wxT( "<ul>" ); + description << wxT( "<li>" ) + << HtmlHyperlink( wxT( "http://www.kicad-pcb.org" ), + _( "The official KiCad website" ) ) + << wxT( "</li>" ); + description << wxT( "<li>" ) + << HtmlHyperlink( wxT( "https://launchpad.net/kicad" ), + _( "Developer's website on Launchpad" ) ) + << wxT("</li>" ); + description << wxT( "<li>" ) + << HtmlHyperlink( wxT( "https://github.com/KiCad/" ), + _( "Official repository for component and footprint libraries" ) ) + << wxT( "</li>" ); + description << wxT( "</ul>" ); + description << wxT( "</p>" ); + + description << wxT( "<p><b><u>" ) + << _( "Bug tracker" ) + << wxT( "</u></b>" ); // bold & underlined font caption + + // bullet-ed list with some http links + description << wxT( "<ul>" ); + description << wxT( "<li>" ) + << HtmlHyperlink( wxT( "https://bugs.launchpad.net/kicad/+bugs?orderby=-id&start=0" ), + _( "Report or examine bugs" ) ) + << wxT( "</li>" ); + description << wxT( "</ul></p>" ); + + description << wxT( "<p><b><u>" ) + << _( "KiCad user's groups and community" ) + << wxT( "</u></b>" ); // bold & underlined font caption + + description << wxT( "<ul>" ); + description << wxT( "<li>" ) + << HtmlHyperlink( wxT( "https://groups.yahoo.com/neo/groups/kicad-users/info" ), + _( "KiCad user's group" ) ) + << wxT( "</li>" ); + + description << wxT( "<li>" ) + << HtmlHyperlink( wxT( "https://forum.kicad.info" ), + _( "KiCad forum" ) ) + << wxT( "</li>" ); + + description << wxT( "</ul></p>" ); + + info.SetDescription( description ); + + + /* License information also HTML formatted */ + wxString license; + license + << wxT( "<div align='center'>" ) + << HtmlNewline( 4 ) + << _( "The complete KiCad EDA Suite is released under the" ) << HtmlNewline( 2 ) + << HtmlHyperlink( wxT( "http://www.gnu.org/licenses" ), + _( "GNU General Public License (GPL) version 3 or any later version" ) ) + << wxT( "</div>" ); + + info.SetLicense( license ); + + + /* A contributor consists of the following information: + * Mandatory: + * - Name + * - EMail address + * Optional: + * - Category + * - Category specific icon + * + * All contributors of the same category will be enumerated under this category + * which should be represented by the same icon. + */ + + // The core developers + info.AddDeveloper( new Contributor( wxT( "Jean-Pierre Charras" ), + wxT( "jp.charras@wanadoo.fr" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Dick Hollenbeck" ), + wxT( "dick@softplc.com" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Wayne Stambaugh" ), + wxT( "stambaughw@gmail.com" ) ) ); + + // alphabetically by last name after main 3 above: + info.AddDeveloper( new Contributor( wxT( "Frank Bennett" ), + wxT( "bennett78@lpbroadband.net" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Cirilo Bernardo" ), + wxT( "cirilo_bernardo@yahoo.com" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Jonas Diemer" ), + wxT( "diemer@gmx.de" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Torsten Hüter" ), + wxT( "torstenhtr@gmx.de" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Jerry Jacobs" ), + wxT( "xor.gate.engineering@gmail.com" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Mario Luzeiro" ), + wxT( "mrluzeiro@ua.pt" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Daniel Majewski" ), + wxT( "lordblick@gmail.com" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Lorenzo Marcantonio" ), + wxT( "lomarcan@tin.it" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Marco Mattila" ), + wxT( "marcom99@gmail.com" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Chris Pavlina" ), + wxT( "pavlina.chris@gmail.com" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Miguel Angel Ajo Pelayo" ), + wxT( "miguelangel@nbee.es" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Jacobo Aragunde Perez" ), + wxT( "jaragunde@igalia.com" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Simon Richter" ), + wxT( "Simon.Richter@hogyros.de" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Mark Roszko" ), + wxT( "mark.roszko@gmail.com" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Marco Serantoni" ), + wxT( "marco.serantoni@gmail.com" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Brian Sidebotham" ), + wxT( "brian.sidebotham@gmail.com" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Mateusz Skowroński" ), + wxT( "skowri@gmail.com" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Rafael Sokolowski" ), + wxT( "rafael.sokolowski@web.de" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Vesa Solonen" ), + wxT( "vesa.solonen@hut.fi" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Bernhard Stegmaier" ), + wxT( "stegmaier@sw-systems.de" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Orson (Maciej Sumiński)" ), + wxT( "maciej.suminski@cern.ch" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Tomasz Wlostowski" ), + wxT( "tomasz.wlostowski@cern.ch" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Adam Wolf" ), + wxT( "adamwolf@feelslikeburning.com" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Alexander Zakamaldin" ), + wxT( "zaka62@mail.ru" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Henner Zeller" ), + wxT( "h.zeller@acm.org" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Andrew Zonenberg" ), + wxT( "azonenberg@drawersteak.com" ) ) ); + info.AddDeveloper( new Contributor( wxT( "Nick Østergaard" ), + wxT( "oe.nick@gmail.com" ) ) ); + + // The document writers + info.AddDocWriter( new Contributor( wxT( "Jean-Pierre Charras" ), + wxT( "jp.charras@wanadoo.fr" ) ) ); + info.AddDocWriter( new Contributor( wxT( "Marco Ciampa" ), + wxT( "ciampix@libero.it" ) ) ); + info.AddDocWriter( new Contributor( wxT( "Dick Hollenbeck" ), + wxT( "dick@softplc.com" ) ) ); + info.AddDocWriter( new Contributor( wxT( "Igor Plyatov" ), + wxT( "plyatov@gmail.com" ) ) ); + info.AddDocWriter( new Contributor( wxT( "Wayne Stambaugh" ), + wxT( "stambaughw@gmail.com" ) ) ); + info.AddDocWriter( new Contributor( wxT( "Fabrizio Tappero" ), + wxT( "fabrizio.tappero@gmail.com" ) ) ); + + /* The translators + * As category the language to which the translation was done is used + * and as icon the national flag of the corresponding country. + */ + info.AddTranslator( new Contributor( wxT( "Martin Kratoška" ), + wxT( "martin@ok1rr.com" ), + wxT( "Czech (CZ)" ), + KiBitmapNew( lang_cs_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Jerry Jacobs" ), + wxT( "xor.gate.engineering@gmail.com" ), + wxT( "Dutch (NL)" ), + KiBitmapNew( lang_nl_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Vesa Solonen" ), + wxT( "vesa.solonen@hut.fi" ), + wxT( "Finnish (FI)" ), + KiBitmapNew( lang_fi_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Jean-Pierre Charras" ), + wxT( "jp.charras@wanadoo.fr" ), + wxT( "French (FR)" ), + KiBitmapNew( lang_fr_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Mateusz Skowroński" ), + wxT( "skowri@gmail.com" ), + wxT( "Polish (PL)" ), + KiBitmapNew( lang_pl_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Kerusey Karyu" ), + wxT( "keruseykaryu@o2.pl" ), + wxT( "Polish (PL)" ), + KiBitmapNew( lang_pl_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Renie Marquet" ), + wxT( "reniemarquet@uol.com.br" ), + wxT( "Portuguese (PT)" ), + KiBitmapNew( lang_pt_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Igor Plyatov" ), + wxT( "plyatov@gmail.com" ), + wxT( "Russian (RU)" ), + KiBitmapNew( lang_ru_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Andrey Fedorushkov" ), + wxT( "andrf@mail.ru" ), + wxT( "Russian (RU)" ), + KiBitmapNew( lang_ru_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Eldar Khayrullin" ), + wxT( "eldar.khayrullin@mail.ru" ), + wxT( "Russian (RU)" ), + KiBitmapNew( lang_ru_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Pedro Martin del Valle" ), + wxT( "pkicad@yahoo.es" ), + wxT( "Spanish (ES)" ), + KiBitmapNew( lang_es_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Iñigo Zuluaga" ), + wxT( "inigo_zuluaga@yahoo.es" ), + wxT( "Spanish (ES)" ), + KiBitmapNew( lang_es_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Iñigo Figuero" ), + wxT( "ifs@elektroquark.com" ), + wxT( "Spanish (ES)" ), + KiBitmapNew( lang_es_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Rafael Sokolowski" ), + wxT( "rafael.sokolowski@web.de" ), + wxT( "German (DE)" ), + KiBitmapNew( lang_de_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Kenta Yonekura" ), + wxT( "yoneken@kicad.jp" ), + wxT( "Japanese (JA)" ), + KiBitmapNew( lang_jp_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Manolis Stefanis" ), + wxT( "" ), + wxT( "Greek (el_GR)" ), + KiBitmapNew( lang_gr_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Athanasios Vlastos" ), + wxT( "" ), + wxT( "Greek (el_GR)" ), + KiBitmapNew( lang_gr_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Milonas Kostas" ), + wxT( "milonas.ko@gmail.com" ), + wxT( "Greek (el_GR)" ), + KiBitmapNew( lang_gr_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Michail Misirlis" ), + wxT( "mmisirlis@gmail.com" ), + wxT( "Greek (el_GR)" ), + KiBitmapNew( lang_gr_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Massimo Cioce" ), + wxT( "ciocemax@alice.it" ), + wxT( "Italian (IT)" ), + KiBitmapNew( lang_it_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Marco Ciampa" ), + wxT( "ciampix@libero.it" ), + wxT( "Italian (IT)" ), + KiBitmapNew( lang_it_xpm ) ) ); + info.AddTranslator( new Contributor( wxT( "Evgeniy Ivanov" ), + wxT( "evgeniy_p_ivanov@yahoo.ca" ), + wxT( "Bulgarian (BG)" ), + KiBitmapNew( lang_bg_xpm ) ) ); + + // TODO: are these all russian translators, + // placed them here now, + // or else align them below other language maintainer with mail adress + info.AddTranslator( new Contributor( wxT( "Remy Halvick" ), + wxEmptyString, + wxT( "Others" ) ) ); + info.AddTranslator( new Contributor( wxT( "David Briscoe" ), + wxEmptyString, + wxT( "Others" ) ) ); + info.AddTranslator( new Contributor( wxT( "Dominique Laigle" ), + wxEmptyString, + wxT( "Others" ) ) ); + info.AddTranslator( new Contributor( wxT( "Paul Burke" ), + wxEmptyString, + wxT( "Others" ) ) ); + + // Programm credits for icons + info.AddArtist( new Contributor( wxT( "Iñigo Zuluaga" ), + wxT( "inigo_zuluaga@yahoo.es" ), + wxT( "Icons by" ), + KiBitmapNew( edit_module_xpm ) ) ); + info.AddArtist( new Contributor( wxT( "Konstantin Baranovskiy" ), + wxT( "baranovskiykonstantin@gmail.com" ), + wxT( "New icons by" ), + KiBitmapNew( edit_module_xpm ) ) ); + info.AddArtist( new Contributor( wxT( "Fabrizio Tappero" ), + wxT( "fabrizio.tappero@gmail.com" ), + wxT( "New icons by" ), + KiBitmapNew( edit_module_xpm ) ) ); + info.AddArtist( new Contributor( wxT( "Christophe Boschat" ), + wxT( "nox454@hotmail.fr" ), + wxT( "3D models by" ), + KiBitmapNew( three_d_xpm ) ) ); + info.AddArtist( new Contributor( wxT( "Renie Marquet" ), + wxT( "reniemarquet@uol.com.br" ), + wxT( "3D models by" ), + KiBitmapNew( three_d_xpm ) ) ); + + // Programm credits for package developers. + info.AddPackager( new Contributor( wxT( "Jean-Samuel Reynaud" ), + wxT( "js.reynaud@gmail.com" ) ) ); + info.AddPackager( new Contributor( wxT( "Bernhard Stegmaier" ), + wxT( "stegmaier@sw-systems.de" ) ) ); + info.AddPackager( new Contributor( wxT( "Adam Wolf" ), + wxT( "adamwolf@feelslikeburning.com" ) ) ); + info.AddPackager( new Contributor( wxT( "Nick Østergaard" ), + wxT( "oe.nick@gmail.com" ) ) ); +} + + +bool ShowAboutDialog( wxWindow* parent ) +{ + AboutAppInfo info; + + InitKiCadAboutNew( info ); + + dialog_about* dlg = new dialog_about( parent, info ); + dlg->SetIcon( info.GetIcon() ); + dlg->Show(); + + return true; +} + + +/////////////////////////////////////////////////////////////////////////////// +/// Helper functions +/////////////////////////////////////////////////////////////////////////////// + +/** + * Function HtmlHyperlink + * + * wraps \a aUrl with a HTML anchor tag containing a hyperlink text reference + * to form a HTML hyperlink. + * + * @param aUrl the url that will be embedded in an anchor tag containing a hyperlink reference + * @param aDescription the optional describing text that will be represented as a hyperlink. + * If not specified the url will be used as hyperlink. + * @return a HTML conform hyperlink like <a href='url'>description</a> + */ +static wxString HtmlHyperlink( const wxString& aUrl, const wxString& aDescription ) +{ + wxString hyperlink = wxEmptyString; + + if( aDescription.IsEmpty() ) + hyperlink << wxT( "<a href='" ) << aUrl << wxT( "'>" ) << aUrl << wxT( "</a>" ); + else + hyperlink << wxT( "<a href='" ) << aUrl << wxT( "'>" ) << aDescription << wxT( "</a>" ); + + return hyperlink; +} + + +/** + * Function HtmlNewline + * + * creates an HTML newline character sequence of \a aCount. + * + * @param aCount the number of HTML newline tags to concatenate, default is to return just + * one <br> tag. + * @return the concatenated amount of HTML newline tag(s) <br> + */ +static wxString HtmlNewline( const unsigned int aCount ) +{ + wxString newlineTags = wxEmptyString; + + for( size_t i = 0; i<aCount; ++i ) + newlineTags << wxT( "<br>" ); + + return newlineTags; +} diff --git a/common/dialog_about/aboutinfo.h b/common/dialog_about/aboutinfo.h new file mode 100644 index 0000000..8e19c59 --- /dev/null +++ b/common/dialog_about/aboutinfo.h @@ -0,0 +1,174 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 Rafael Sokolowski <Rafael.Sokolowski@web.de> + * Copyright (C) 2014-2015 KiCad Developers, see CHANGELOG.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 ABOUTAPPINFO_H +#define ABOUTAPPINFO_H + +#include <wx/aboutdlg.h> +#include <wx/bitmap.h> +#include <wx/dynarray.h> + +class Contributor; + +WX_DECLARE_OBJARRAY( Contributor, Contributors ); + + +/** + * An object of this class is meant to be used to store application specific information + * like who has contributed in which area of the application, the license, copyright + * and other descriptive information. + */ +class AboutAppInfo +{ +public: + AboutAppInfo() {}; + virtual ~AboutAppInfo() {}; + + void AddDeveloper( const Contributor* developer ) + { + if( developer != NULL ) + developers.Add( developer ); + } + + void AddDocWriter( const Contributor* docwriter ) + { + if( docwriter != NULL ) + docwriters.Add( docwriter ); + } + + void AddArtist( const Contributor* artist ) + { + if( artist != NULL ) + artists.Add( artist ); + } + + void AddTranslator( const Contributor* translator ) + { + if( translator != NULL ) + translators.Add( translator ); + } + + void AddPackager( const Contributor* packager ) + { + if( packager != NULL ) + packagers.Add( packager ); + } + + Contributors GetDevelopers() { return developers; } + Contributors GetDocWriters() { return docwriters; } + Contributors GetArtists() { return artists; } + Contributors GetTranslators() { return translators; } + Contributors GetPackagers() { return packagers; } + + void SetDescription( const wxString& text ) { description = text; } + wxString& GetDescription() { return description; } + + void SetLicense( const wxString& text ) { license = text; } + wxString& GetLicense() { return license; } + + void SetCopyright( const wxString& text ) { copyright = text; } + wxString GetCopyright() + { + wxString copyrightText = copyright; + +#if wxUSE_UNICODE + const wxString utf8_copyrightSign = wxString::FromUTF8( "\xc2\xa9" ); + copyrightText.Replace( _T( "(c)" ), utf8_copyrightSign ); + copyrightText.Replace( _T( "(C)" ), utf8_copyrightSign ); +#endif // wxUSE_UNICODE + + return copyrightText; + } + + void SetAppName( const wxString& name ) { appName = name; } + wxString& GetAppName() { return appName; } + + void SetBuildVersion( const wxString& version ) { buildVersion = version; } + wxString& GetBuildVersion() { return buildVersion; } + + void SetLibVersion( const wxString& version ) { libVersion = version; } + wxString& GetLibVersion() { return libVersion; } + + void SetIcon( const wxIcon& icon ) { appIcon = icon; } + wxIcon& GetIcon() { return appIcon; } + +private: + Contributors developers; + Contributors docwriters; + Contributors artists; + Contributors translators; + Contributors packagers; + + wxString description; + wxString license; + + wxString copyright; // Todo: copyright sign in unicode + wxString appName; + wxString buildVersion; + wxString libVersion; + + wxIcon appIcon; +}; + + +/** + * A contributor, a person which was involved in the development of the application + * or which has contributed in any kind somehow to the project. + * + * A contributor consists of the following mandatory information: + * - Name + * - EMail address + * + * Each contributor can have optional information assigned like: + * - A category + * - A category specific icon + */ +class Contributor +{ +public: + Contributor( const wxString& name, + const wxString& email, + const wxString& category = wxEmptyString, + wxBitmap* icon = NULL ) : + m_checked( false ) + { m_name = name; m_email = email; m_category = category; m_icon = icon; } + + virtual ~Contributor() {} + + wxString& GetName() { return m_name; } + wxString& GetEMail() { return m_email; } + wxString& GetCategory() { return m_category; } + wxBitmap* GetIcon() { return m_icon; } + void SetChecked( bool status ) { m_checked = status; } + bool IsChecked() { return m_checked; } + +private: + wxString m_name; + wxString m_email; + wxString m_category; + wxBitmap* m_icon; + bool m_checked; +}; + +#endif // ABOUTAPPINFO_H diff --git a/common/dialog_about/dialog_about.cpp b/common/dialog_about/dialog_about.cpp new file mode 100644 index 0000000..e947c87 --- /dev/null +++ b/common/dialog_about/dialog_about.cpp @@ -0,0 +1,399 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2010 Rafael Sokolowski <Rafael.Sokolowski@web.de> + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.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 <dialog_about.h> + +/////////////////////////////////////////////////////////////////////////////// +/// Class dialog_about methods +/////////////////////////////////////////////////////////////////////////////// + +dialog_about::dialog_about(wxWindow *parent, AboutAppInfo& appInfo) + : dialog_about_base(parent), info(appInfo) +{ + picInformation = KiBitmap( info_xpm ); + picDevelopers = KiBitmap( preference_xpm ); + picDocWriters = KiBitmap( editor_xpm ); + picArtists = KiBitmap( palette_xpm ); + picTranslators = KiBitmap( language_xpm ); + picLicense = KiBitmap( tools_xpm ); + picPackagers = KiBitmap( zip_xpm ); + + m_bitmapApp->SetBitmap( info.GetIcon() ); + + m_staticTextAppTitle->SetLabel( info.GetAppName() ); + m_staticTextCopyright->SetLabel( info.GetCopyright() ); + m_staticTextBuildVersion->SetLabel( info.GetBuildVersion() ); + m_staticTextLibVersion->SetLabel( info.GetLibVersion() ); + + DeleteNotebooks(); + CreateNotebooks(); + + GetSizer()->SetSizeHints( this ); + m_auiNotebook->Update(); + SetFocus(); + Centre(); +} + + +dialog_about::~dialog_about() +{ +} + + +wxFlexGridSizer* dialog_about::CreateFlexGridSizer() +{ + // three columns with vertical and horizontal extra space of two pixels + wxFlexGridSizer* fgSizer1 = new wxFlexGridSizer( 3, 2, 2 ); + fgSizer1->SetFlexibleDirection( wxHORIZONTAL ); + fgSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + return fgSizer1; +} + + +void dialog_about::DeleteNotebooks() +{ + for( size_t i=0; i<m_auiNotebook->GetPageCount(); ++i ) + m_auiNotebook->DeletePage( i ); +} + + +void dialog_about::CreateNotebooks() +{ + CreateNotebookHtmlPage( m_auiNotebook, _( "Information" ), picInformation, + info.GetDescription() ); + + CreateNotebookPage( m_auiNotebook, _( "Developers" ) , picDevelopers, info.GetDevelopers() ); + CreateNotebookPage( m_auiNotebook, _( "Doc Writers" ), picDocWriters, info.GetDocWriters() ); + + CreateNotebookPageByCategory( m_auiNotebook, _( "Artists" ), picArtists, info.GetArtists() ); + CreateNotebookPageByCategory( m_auiNotebook, _( "Translators" ), picTranslators, + info.GetTranslators() ); + CreateNotebookPageByCategory( m_auiNotebook, _( "Packagers" ), picPackagers, + info.GetPackagers() ); + + CreateNotebookHtmlPage( m_auiNotebook, _( "License" ), picLicense, info.GetLicense() ); +} + +void dialog_about::CreateNotebookPage( wxAuiNotebook* parent, const wxString& caption, + const wxBitmap& icon, const Contributors& contributors ) +{ + wxBoxSizer* bSizer = new wxBoxSizer( wxHORIZONTAL ); + + wxScrolledWindow* m_scrolledWindow1 = new wxScrolledWindow( parent, wxID_ANY, + wxDefaultPosition, + wxDefaultSize, + wxHSCROLL|wxVSCROLL ); + m_scrolledWindow1->SetScrollRate( 5, 5 ); + + /* Panel for additional space at the left, + * but can also be used to show an additional bitmap. + */ + wxPanel* panel1 = new wxPanel( m_scrolledWindow1 ); + + wxFlexGridSizer* fgSizer1 = CreateFlexGridSizer(); + + for( size_t i=0; i<contributors.GetCount(); ++i ) + { + Contributor* contributor = &contributors.Item( i ); + + // Icon at first column + wxStaticBitmap* m_bitmap1 = CreateStaticBitmap( m_scrolledWindow1, contributor->GetIcon() ); + fgSizer1->Add( m_bitmap1, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, 5 ); + + // Name of contributor at second column + if ( contributor->GetName() != wxEmptyString ) + { + wxStaticText* m_staticText1 = new wxStaticText( m_scrolledWindow1, wxID_ANY, + contributor->GetName(), + wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText1->Wrap( -1 ); + fgSizer1->Add( m_staticText1, 0, wxALIGN_LEFT|wxBOTTOM, 2 ); + } + else + { + fgSizer1->AddSpacer( 5 ); + } + + // Email address of contributor at third column + if ( contributor->GetEMail() != wxEmptyString ) + { + wxHyperlinkCtrl* hyperlink = CreateHyperlink( m_scrolledWindow1, + contributor->GetEMail() ); + fgSizer1->Add( hyperlink, 0, wxALIGN_LEFT|wxBOTTOM, 2 ); + } + else + { + fgSizer1->AddSpacer( 5 ); + } + } + + bSizer->Add( panel1, 1, wxEXPAND|wxALL, 10 ); + bSizer->Add( fgSizer1, 7, wxEXPAND|wxALL, 10 ); // adjust width of panel with first int value + m_scrolledWindow1->SetSizer( bSizer ); + m_scrolledWindow1->Layout(); + bSizer->Fit( m_scrolledWindow1 ); + parent->AddPage( m_scrolledWindow1, caption, false, icon ); +} + + +void dialog_about::CreateNotebookPageByCategory(wxAuiNotebook* parent, const wxString& caption, + const wxBitmap& icon, + const Contributors& contributors) +{ + wxBoxSizer* bSizer = new wxBoxSizer( wxHORIZONTAL ); + + wxScrolledWindow* m_scrolledWindow1 = new wxScrolledWindow( parent, wxID_ANY, + wxDefaultPosition, + wxDefaultSize, + wxHSCROLL|wxVSCROLL ); + m_scrolledWindow1->SetScrollRate( 5, 5 ); + + /* Panel for additional space at the left, + * but can also be used to show an additional bitmap. + */ + wxPanel* panel1 = new wxPanel( m_scrolledWindow1 ); + + wxFlexGridSizer* fgSizer1 = CreateFlexGridSizer(); + + for( size_t i=0; i<contributors.GetCount(); ++i ) + { + Contributor* contributor = &contributors.Item( i ); + + wxBitmap* icon = contributor->GetIcon(); + wxString category = contributor->GetCategory(); + + /* to construct the next row we expect to have + * a category and a contributor that was not considered up to now + */ + if( ( category != wxEmptyString ) && !( contributor->IsChecked() ) ) + { + // Icon at first column + wxStaticBitmap* m_bitmap1 = CreateStaticBitmap( m_scrolledWindow1, icon ); + fgSizer1->Add( m_bitmap1, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, 5 ); + + // Category name at second column + wxStaticText* m_staticText1 = new wxStaticText( m_scrolledWindow1, wxID_ANY, + contributor->GetCategory() + wxT( ":" ), + wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText1->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, + wxEmptyString ) ); // bold font + m_staticText1->Wrap( -1 ); + fgSizer1->Add( m_staticText1, 0, wxALIGN_LEFT|wxBOTTOM, 2 ); + + // Nothing at third column + fgSizer1->AddSpacer( 5 ); + + // Now, all contributors of the same category will follow + for( size_t j=0; j<contributors.GetCount(); ++j ) + { + Contributor* contributor = &contributors.Item( j ); + + if ( contributor->GetCategory() == category ) + { + // First column is empty + fgSizer1->AddSpacer(5); + + // Name of contributor at second column + wxStaticText* m_staticText2 = new wxStaticText( m_scrolledWindow1, wxID_ANY, + wxT(" • ") + contributor->GetName(), + wxDefaultPosition, + wxDefaultSize, 0 ); + m_staticText1->Wrap( -1 ); + fgSizer1->Add( m_staticText2, 0, wxALIGN_LEFT|wxBOTTOM, 2 ); + + // Email address of contributor at third column + if( contributor->GetEMail() != wxEmptyString ) + { + wxHyperlinkCtrl* hyperlink = CreateHyperlink( m_scrolledWindow1, + contributor->GetEMail() ); + fgSizer1->Add( hyperlink, 0, wxALIGN_LEFT|wxBOTTOM, 2 ); + } + else + { + fgSizer1->AddSpacer( 5 ); + } + + /* this contributor was added to the GUI, + * thus can be ignored next time + */ + contributor->SetChecked( true ); + } + } + } + else + { + continue; + } + } + + /* Now, lets list the remaining contributors that have not been considered + * because they were not assigned to any category. + */ + for ( size_t k=0; k<contributors.GetCount(); ++k ) + { + Contributor* contributor = &contributors.Item( k ); + + if ( contributor->IsChecked() ) + continue; + + // Icon at first column + wxStaticBitmap* m_bitmap1 = CreateStaticBitmap( m_scrolledWindow1, contributor->GetIcon() ); + fgSizer1->Add( m_bitmap1, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, 5 ); + + // Name of contributor at second column + if( contributor->GetName() != wxEmptyString ) + { + wxStaticText* m_staticText1 = new wxStaticText( m_scrolledWindow1, wxID_ANY, + contributor->GetName(), + wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText1->Wrap( -1 ); + fgSizer1->Add( m_staticText1, 0, wxALIGN_LEFT|wxBOTTOM, 2 ); + } + else + { + fgSizer1->AddSpacer( 5 ); + } + + // Email address of contributor at third column + if ( contributor->GetEMail() != wxEmptyString ) + { + wxHyperlinkCtrl* hyperlink = CreateHyperlink( m_scrolledWindow1, + contributor->GetEMail() ); + fgSizer1->Add( hyperlink, 0, wxALIGN_LEFT|wxBOTTOM, 2 ); + } + else + { + fgSizer1->AddSpacer( 5 ); + } + } + + bSizer->Add( panel1, 1, wxEXPAND|wxALL, 10 ); + bSizer->Add( fgSizer1, 7, wxEXPAND|wxALL, 10 ); // adjust width of panel with first int value + m_scrolledWindow1->SetSizer( bSizer ); + m_scrolledWindow1->Layout(); + bSizer->Fit( m_scrolledWindow1 ); + parent->AddPage( m_scrolledWindow1, caption, false, icon ); +} + + +void dialog_about::CreateNotebookHtmlPage( wxAuiNotebook* parent, const wxString& caption, + const wxBitmap& icon, const wxString& html ) +{ + wxPanel* panel = new wxPanel( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, + wxTAB_TRAVERSAL ); + + wxBoxSizer* bSizer = new wxBoxSizer( wxVERTICAL ); + + wxString htmlPage = wxEmptyString, htmlContent = html; + + // to have a unique look background color for HTML pages is set to the default as it is + // used for all the other widgets + wxString htmlColor = ( this->GetBackgroundColour() ).GetAsString( wxC2S_HTML_SYNTAX ); + + // beginning of HTML structure + htmlPage.Append( wxT( "<html><body bgcolor='" ) + htmlColor + wxT( "'>" ) ); + + htmlPage.Append( htmlContent ); + + // end of HTML structure indicated by closing tags + htmlPage.Append( wxT( "</body></html>" ) ); + + // the HTML page is going to be created with previously created HTML content + wxHtmlWindow* htmlWindow = new wxHtmlWindow( panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, + wxHW_SCROLLBAR_AUTO|wxHW_NO_SELECTION ); + + // HTML font set to font properties as they are used for widgets to have an unique look + // under different platforms with HTML + wxFont font = this->GetFont(); + htmlWindow->SetStandardFonts( font.GetPointSize(), font.GetFaceName(), font.GetFaceName() ); + htmlWindow->SetPage( htmlPage ); + + // the HTML window shall not be used to open external links, thus this task is delegated + // to users default browser + htmlWindow->Connect( wxEVT_COMMAND_HTML_LINK_CLICKED, + wxHtmlLinkEventHandler( dialog_about::OnHtmlLinkClicked ), NULL, this ); + + // no additional space around the HTML window as it is also the case by the other notebook pages + bSizer->Add( htmlWindow, 1, wxALL|wxEXPAND, 0 ); + panel->SetSizer( bSizer ); + panel->Layout(); + bSizer->Fit( panel ); + parent->AddPage( panel, caption, false, icon ); +} + + +wxHyperlinkCtrl* dialog_about::CreateHyperlink(wxScrolledWindow* parent, const wxString& email) +{ + wxHyperlinkCtrl* hyperlink = new wxHyperlinkCtrl( + parent, wxID_ANY, + wxT( "<" ) + email + wxT( ">" ), /* the label */ + wxT( "mailto:" ) + email + + wxT( "?subject=KiCad - " ) + + info.GetBuildVersion() + + wxT( " , " ) + info.GetLibVersion() + ); /* the url */ + + return hyperlink; +} + + +wxStaticBitmap* dialog_about::CreateStaticBitmap(wxScrolledWindow* parent, wxBitmap* icon) +{ + wxStaticBitmap* bitmap = new wxStaticBitmap( parent, wxID_ANY, wxNullBitmap, + wxDefaultPosition, wxDefaultSize, 0 ); + + if( icon ) + { + bitmap->SetBitmap( *icon ); + } + else + { + bitmap->SetBitmap( KiBitmap( right_xpm ) ); + } + + return bitmap; +} + + +/////////////////////////////////////////////////////////////////////////////// +/// Event handlers +/////////////////////////////////////////////////////////////////////////////// + +void dialog_about::OnClose( wxCloseEvent &event ) +{ + Destroy(); +} + + +void dialog_about::OnOkClick( wxCommandEvent &event ) +{ + Destroy(); +} + + +void dialog_about::OnHtmlLinkClicked( wxHtmlLinkEvent& event ) +{ + ::wxLaunchDefaultBrowser( event.GetLinkInfo().GetHref() ); +} diff --git a/common/dialog_about/dialog_about.h b/common/dialog_about/dialog_about.h new file mode 100644 index 0000000..067ee63 --- /dev/null +++ b/common/dialog_about/dialog_about.h @@ -0,0 +1,99 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2010 Rafael Sokolowski <Rafael.Sokolowski@web.de> + * Copyright (C) 2010-2015 KiCad Developers, see CHANGELOG.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_ABOUT_H +#define DIALOG_ABOUT_H + +#include <wx/html/htmlwin.h> +#include <wx/statbmp.h> +#include <wx/stattext.h> +#include <wx/hyperlink.h> + +#include <aboutinfo.h> +#include <dialog_about_base.h> + +/* Pixel information of icons in XPM format. + * All KiCad icons are linked into shared library 'libbitmaps.a'. + * Icons: + * preference_xpm[]; // Icon for 'Developers' tab + * editor_xpm[]; // Icon for 'Doc Writers' tab + * palette_xpm[]; // Icon for 'Artists' tab + * language_xpm[]; // Icon for 'Translators' tab + * right_xpm[]; // Right arrow icon for list items + * info_xpm[]; // Bulb for description tab + * tools_xpm[]; // Sheet of paper icon for license info tab + */ +#include <bitmaps.h> + +/** + * About dialog to show application specific information. + * Needs an <code>AboutAppInfo</code> object that contains the data to be displayed. + */ +class dialog_about : public dialog_about_base +{ +private: + + // Icons for the various tabs of wxAuiNotebook + wxBitmap picInformation; + wxBitmap picDevelopers; + wxBitmap picDocWriters; + wxBitmap picArtists; + wxBitmap picTranslators; + wxBitmap picPackagers; + wxBitmap picLicense; + + AboutAppInfo info; + +public: + dialog_about( wxWindow* dlg, AboutAppInfo& appInfo ); + ~dialog_about(); + +private: + void initDialog(); + virtual void OnClose( wxCloseEvent& event ); + virtual void OnOkClick( wxCommandEvent& event ); + virtual void OnHtmlLinkClicked( wxHtmlLinkEvent& event ); + + // Notebook pages + wxFlexGridSizer* CreateFlexGridSizer(); + void DeleteNotebooks(); + void CreateNotebooks(); + void CreateNotebookPage( wxAuiNotebook* parent, + const wxString& caption, + const wxBitmap& icon, + const Contributors& contributors ); + void CreateNotebookPageByCategory( wxAuiNotebook* parent, + const wxString& caption, + const wxBitmap& icon, + const Contributors& contributors ); + void CreateNotebookHtmlPage( wxAuiNotebook* parent, + const wxString& caption, + const wxBitmap& icon, + const wxString& html ); + + wxHyperlinkCtrl* CreateHyperlink( wxScrolledWindow* parent, const wxString& email ); + wxStaticBitmap* CreateStaticBitmap( wxScrolledWindow* parent, wxBitmap* icon ); +}; + +#endif // DIALOG_ABOUT_H diff --git a/common/dialog_about/dialog_about_base.cpp b/common/dialog_about/dialog_about_base.cpp new file mode 100644 index 0000000..19b9db7 --- /dev/null +++ b/common/dialog_about/dialog_about_base.cpp @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 5 2014) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "dialog_about_base.h" + +/////////////////////////////////////////////////////////////////////////// + +dialog_about_base::dialog_about_base( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + + wxBoxSizer* bSizer1; + bSizer1 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer3; + bSizer3 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer3->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_bitmapApp = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizer3->Add( m_bitmapApp, 1, wxALIGN_CENTER|wxALL, 5 ); + + wxBoxSizer* b_apptitleSizer; + b_apptitleSizer = new wxBoxSizer( wxVERTICAL ); + + m_staticTextAppTitle = new wxStaticText( this, wxID_ANY, _("App Title"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE ); + m_staticTextAppTitle->Wrap( -1 ); + m_staticTextAppTitle->SetFont( wxFont( 14, 70, 90, 92, false, wxEmptyString ) ); + + b_apptitleSizer->Add( m_staticTextAppTitle, 0, wxALIGN_CENTER|wxALL, 5 ); + + m_staticTextCopyright = new wxStaticText( this, wxID_ANY, _("Copyright Info"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE ); + m_staticTextCopyright->Wrap( -1 ); + b_apptitleSizer->Add( m_staticTextCopyright, 0, wxALIGN_CENTER|wxALL, 1 ); + + m_staticTextBuildVersion = new wxStaticText( this, wxID_ANY, _("Build Version Info"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE ); + m_staticTextBuildVersion->Wrap( -1 ); + b_apptitleSizer->Add( m_staticTextBuildVersion, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT|wxTOP, 5 ); + + m_staticTextLibVersion = new wxStaticText( this, wxID_ANY, _("Lib Version Info"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE ); + m_staticTextLibVersion->Wrap( -1 ); + b_apptitleSizer->Add( m_staticTextLibVersion, 0, wxALIGN_CENTER|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + + bSizer3->Add( b_apptitleSizer, 10, wxEXPAND, 5 ); + + + bSizer3->Add( 0, 0, 2, wxEXPAND, 5 ); + + + bSizer1->Add( bSizer3, 0, wxEXPAND, 5 ); + + wxStaticLine* m_staticline1; + m_staticline1 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer1->Add( m_staticline1, 0, wxEXPAND | wxALL, 5 ); + + m_auiNotebook = new wxAuiNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxAUI_NB_SCROLL_BUTTONS|wxAUI_NB_TAB_FIXED_WIDTH ); + m_auiNotebook->SetMinSize( wxSize( 750,350 ) ); + + + bSizer1->Add( m_auiNotebook, 2, wxEXPAND | wxALL, 5 ); + + m_sdbSizer = new wxStdDialogButtonSizer(); + m_sdbSizerOK = new wxButton( this, wxID_OK ); + m_sdbSizer->AddButton( m_sdbSizerOK ); + m_sdbSizer->Realize(); + + bSizer1->Add( m_sdbSizer, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + this->SetSizer( bSizer1 ); + this->Layout(); +} + +dialog_about_base::~dialog_about_base() +{ +} diff --git a/common/dialog_about/dialog_about_base.fbp b/common/dialog_about/dialog_about_base.fbp new file mode 100644 index 0000000..e776d48 --- /dev/null +++ b/common/dialog_about/dialog_about_base.fbp @@ -0,0 +1,752 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<wxFormBuilder_Project> + <FileVersion major="1" minor="13" /> + <object class="Project" expanded="1"> + <property name="class_decoration"></property> + <property name="code_generation">C++</property> + <property name="disconnect_events">1</property> + <property name="disconnect_mode">source_name</property> + <property name="disconnect_php_events">0</property> + <property name="disconnect_python_events">0</property> + <property name="embedded_files_path">res</property> + <property name="encoding">UTF-8</property> + <property name="event_generation">connect</property> + <property name="file">dialog_about_base</property> + <property name="first_id">1000</property> + <property name="help_provider">none</property> + <property name="internationalize">1</property> + <property name="name">MyProject</property> + <property name="namespace"></property> + <property name="path">.</property> + <property name="precompiled_header"></property> + <property name="relative_path">1</property> + <property name="skip_lua_events">1</property> + <property name="skip_php_events">1</property> + <property name="skip_python_events">1</property> + <property name="ui_table">UI</property> + <property name="use_enum">1</property> + <property name="use_microsoft_bom">0</property> + <object class="Dialog" expanded="1"> + <property name="aui_managed">0</property> + <property name="aui_manager_style">wxAUI_MGR_DEFAULT</property> + <property name="bg"></property> + <property name="center"></property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="enabled">1</property> + <property name="event_handler">impl_virtual</property> + <property name="extra_style"></property> + <property name="fg"></property> + <property name="font"></property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="maximum_size"></property> + <property name="minimum_size">-1,-1</property> + <property name="name">dialog_about_base</property> + <property name="pos"></property> + <property name="size">750,437</property> + <property name="style">wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER|wxSTAY_ON_TOP</property> + <property name="subclass"></property> + <property name="title">About...</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnActivate"></event> + <event name="OnActivateApp"></event> + <event name="OnAuiFindManager"></event> + <event name="OnAuiPaneButton"></event> + <event name="OnAuiPaneClose"></event> + <event name="OnAuiPaneMaximize"></event> + <event name="OnAuiPaneRestore"></event> + <event name="OnAuiRender"></event> + <event name="OnChar"></event> + <event name="OnClose"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnHibernate"></event> + <event name="OnIconize"></event> + <event name="OnIdle"></event> + <event name="OnInitDialog"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizer1</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">0</property> + <object class="wxBoxSizer" expanded="0"> + <property name="minimum_size"></property> + <property name="name">bSizer3</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="spacer" expanded="0"> + <property name="height">0</property> + <property name="permission">protected</property> + <property name="width">0</property> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER|wxALL</property> + <property name="proportion">1</property> + <object class="wxStaticBitmap" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="bitmap"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_bitmapApp</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">10</property> + <object class="wxBoxSizer" expanded="0"> + <property name="minimum_size"></property> + <property name="name">b_apptitleSizer</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER|wxALL</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font">,90,92,14,70,0</property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">App Title</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextAppTitle</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style">wxALIGN_CENTRE</property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">1</property> + <property name="flag">wxALIGN_CENTER|wxALL</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Copyright Info</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextCopyright</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style">wxALIGN_CENTRE</property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER|wxLEFT|wxRIGHT|wxTOP</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Build Version Info</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextBuildVersion</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style">wxALIGN_CENTRE</property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER|wxBOTTOM|wxLEFT|wxRIGHT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Lib Version Info</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextLibVersion</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style">wxALIGN_CENTRE</property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">2</property> + <object class="spacer" expanded="0"> + <property name="height">0</property> + <property name="permission">protected</property> + <property name="width">0</property> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxEXPAND | wxALL</property> + <property name="proportion">0</property> + <object class="wxStaticLine" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticline1</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">none</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style">wxLI_HORIZONTAL</property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxEXPAND | wxALL</property> + <property name="proportion">2</property> + <object class="wxAuiNotebook" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size">750,350</property> + <property name="moveable">1</property> + <property name="name">m_auiNotebook</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style">wxAUI_NB_SCROLL_BUTTONS|wxAUI_NB_TAB_FIXED_WIDTH</property> + <property name="subclass"></property> + <property name="tab_ctrl_height">-1</property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="uniform_bitmap_size"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnAuiNotebookAllowDND"></event> + <event name="OnAuiNotebookBeginDrag"></event> + <event name="OnAuiNotebookButton"></event> + <event name="OnAuiNotebookDragMotion"></event> + <event name="OnAuiNotebookEndDrag"></event> + <event name="OnAuiNotebookPageChanged"></event> + <event name="OnAuiNotebookPageChanging"></event> + <event name="OnAuiNotebookPageClose"></event> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxALIGN_CENTER_HORIZONTAL</property> + <property name="proportion">0</property> + <object class="wxStdDialogButtonSizer" expanded="1"> + <property name="Apply">0</property> + <property name="Cancel">0</property> + <property name="ContextHelp">0</property> + <property name="Help">0</property> + <property name="No">0</property> + <property name="OK">1</property> + <property name="Save">0</property> + <property name="Yes">0</property> + <property name="minimum_size"></property> + <property name="name">m_sdbSizer</property> + <property name="permission">protected</property> + <event name="OnApplyButtonClick"></event> + <event name="OnCancelButtonClick"></event> + <event name="OnContextHelpButtonClick"></event> + <event name="OnHelpButtonClick"></event> + <event name="OnNoButtonClick"></event> + <event name="OnOKButtonClick"></event> + <event name="OnSaveButtonClick"></event> + <event name="OnYesButtonClick"></event> + </object> + </object> + </object> + </object> + </object> +</wxFormBuilder_Project> diff --git a/common/dialog_about/dialog_about_base.h b/common/dialog_about/dialog_about_base.h new file mode 100644 index 0000000..9098984 --- /dev/null +++ b/common/dialog_about/dialog_about_base.h @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 5 2014) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __DIALOG_ABOUT_BASE_H__ +#define __DIALOG_ABOUT_BASE_H__ + +#include <wx/artprov.h> +#include <wx/xrc/xmlres.h> +#include <wx/intl.h> +#include <wx/bitmap.h> +#include <wx/image.h> +#include <wx/icon.h> +#include <wx/statbmp.h> +#include <wx/gdicmn.h> +#include <wx/font.h> +#include <wx/colour.h> +#include <wx/settings.h> +#include <wx/string.h> +#include <wx/stattext.h> +#include <wx/sizer.h> +#include <wx/statline.h> +#include <wx/aui/auibook.h> +#include <wx/button.h> +#include <wx/dialog.h> + +/////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +/// Class dialog_about_base +/////////////////////////////////////////////////////////////////////////////// +class dialog_about_base : public wxDialog +{ + private: + + protected: + wxStaticBitmap* m_bitmapApp; + wxStaticText* m_staticTextAppTitle; + wxStaticText* m_staticTextCopyright; + wxStaticText* m_staticTextBuildVersion; + wxStaticText* m_staticTextLibVersion; + wxAuiNotebook* m_auiNotebook; + wxStdDialogButtonSizer* m_sdbSizer; + wxButton* m_sdbSizerOK; + + public: + + dialog_about_base( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("About..."), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 750,437 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER|wxSTAY_ON_TOP ); + ~dialog_about_base(); + +}; + +#endif //__DIALOG_ABOUT_BASE_H__ diff --git a/common/dialog_shim.cpp b/common/dialog_shim.cpp new file mode 100644 index 0000000..a05a5b6 --- /dev/null +++ b/common/dialog_shim.cpp @@ -0,0 +1,619 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2012-2016 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 <dialog_shim.h> +#include <kiway_player.h> +#include <wx/evtloop.h> +#include <pgm_base.h> + + +/// Toggle a window's "enable" status to disabled, then enabled on destruction. +class WDO_ENABLE_DISABLE +{ + wxWindow* m_win; + +public: + + WDO_ENABLE_DISABLE( wxWindow* aWindow ) : + m_win( aWindow ) + { + if( m_win ) + m_win->Disable(); + } + + ~WDO_ENABLE_DISABLE() + { + if( m_win ) + { + m_win->Enable(); + m_win->SetFocus(); // let's focus back on the parent window + } + } +}; + + +DIALOG_SHIM::DIALOG_SHIM( wxWindow* aParent, wxWindowID id, const wxString& title, + const wxPoint& pos, const wxSize& size, long style, const wxString& name ) : + wxDialog( aParent, id, title, pos, size, style, name ), + KIWAY_HOLDER( 0 ), + m_qmodal_loop( 0 ), + m_qmodal_showing( false ), + m_qmodal_parent_disabler( 0 ) +{ + // pray that aParent is either a KIWAY_PLAYER or DIALOG_SHIM derivation. + KIWAY_HOLDER* h = dynamic_cast<KIWAY_HOLDER*>( aParent ); + + // wxASSERT_MSG( h, wxT( "DIALOG_SHIM's parent is NULL or not derived from KIWAY_PLAYER nor DIALOG_SHIM" ) ); + + if( h ) + SetKiway( this, &h->Kiway() ); + + Bind( wxEVT_CLOSE_WINDOW, &DIALOG_SHIM::OnCloseWindow, this ); + Bind( wxEVT_BUTTON, &DIALOG_SHIM::OnButton, this ); + +#ifdef __WINDOWS__ + // On Windows, the app top windows can be brought to the foreground + // (at least temporary) in certain circumstances, + // for instance when calling an external tool in Eeschema boom generation. + // So set the parent KIWAY_PLAYER kicad frame (if exists) to top window + // to avoid this annoying behavior + KIWAY_PLAYER* parent_kiwayplayer = dynamic_cast<KIWAY_PLAYER*>( aParent ); + + if( parent_kiwayplayer ) + Pgm().App().SetTopWindow( parent_kiwayplayer ); +#endif + +#if DLGSHIM_USE_SETFOCUS + Connect( wxEVT_INIT_DIALOG, wxInitDialogEventHandler( DIALOG_SHIM::onInit ) ); +#endif +} + + +DIALOG_SHIM::~DIALOG_SHIM() +{ + // if the dialog is quasi-modal, this will end its event loop + if( IsQuasiModal() ) + EndQuasiModal( wxID_CANCEL ); + + delete m_qmodal_parent_disabler; // usually NULL by now +} + +void DIALOG_SHIM::FinishDialogSettings() +{ + // must be called from the constructor of derived classes, + // when all widgets are initialized, and therefore their size fixed + + // SetSizeHints fixes the minimal size of sizers in the dialog + // (SetSizeHints calls Fit(), so no need to call it) + GetSizer()->SetSizeHints( this ); + + // the default position, when calling the first time the dlg + Center(); +} + +void DIALOG_SHIM::FixOSXCancelButtonIssue() +{ +#ifdef __WXMAC__ + // A ugly hack to fix an issue on OSX: ctrl+c closes the dialog instead of + // copying a text if a button with wxID_CANCEL is used in a wxStdDialogButtonSizer + // created by wxFormBuilder: the label is &Cancel, and this accelerator key has priority + // to copy text standard accelerator, and the dlg is closed when trying to copy text + wxButton* button = dynamic_cast< wxButton* > ( wxWindow::FindWindowById( wxID_CANCEL, this ) ); + + if( button ) + button->SetLabel( _( "Cancel" ) ); +#endif +} + + +// our hashtable is an implementation secret, don't need or want it in a header file +#include <hashtables.h> +#include <base_struct.h> // EDA_RECT +#include <typeinfo> + +static RECT_MAP class_map; + +bool DIALOG_SHIM::Show( bool show ) +{ + bool ret; + const char* hash_key; + + if( m_hash_key.size() ) + { + // a special case like EDA_LIST_DIALOG, which has multiple uses. + hash_key = m_hash_key.c_str(); + } + else + { + hash_key = typeid(*this).name(); + } + + // Show or hide the window. If hiding, save current position and size. + // If showing, use previous position and size. + if( show ) + { + wxDialog::Raise(); // Needed on OS X and some other window managers (i.e. Unity) + ret = wxDialog::Show( show ); + + // classname is key, returns a zeroed out default EDA_RECT if none existed before. + EDA_RECT r = class_map[ hash_key ]; + + if( r.GetSize().x != 0 && r.GetSize().y != 0 ) + SetSize( r.GetPosition().x, r.GetPosition().y, r.GetSize().x, r.GetSize().y, 0 ); + } + else + { + // Save the dialog's position & size before hiding, using classname as key + EDA_RECT r( wxDialog::GetPosition(), wxDialog::GetSize() ); + class_map[ hash_key ] = r; + + ret = wxDialog::Show( show ); + } + return ret; +} + + +bool DIALOG_SHIM::Enable( bool enable ) +{ + // so we can do logging of this state change: + +#if defined(DEBUG) + const char* type_id = typeid( *this ).name(); + printf( "wxDialog %s: %s\n", type_id, enable ? "enabled" : "disabled" ); +#endif + + return wxDialog::Enable( enable ); +} + + +#if DLGSHIM_USE_SETFOCUS + +static bool findWindowRecursively( const wxWindowList& children, const wxWindow* wanted ) +{ + for( wxWindowList::const_iterator it = children.begin(); it != children.end(); ++it ) + { + const wxWindow* child = *it; + + if( wanted == child ) + return true; + else + { + if( findWindowRecursively( child->GetChildren(), wanted ) ) + return true; + } + } + + return false; +} + + +static bool findWindowRecursively( const wxWindow* topmost, const wxWindow* wanted ) +{ + // wanted may be NULL and that is ok. + + if( wanted == topmost ) + return true; + + return findWindowRecursively( topmost->GetChildren(), wanted ); +} + + +/// Set the focus if it is not already set in a derived constructor to a specific control. +void DIALOG_SHIM::onInit( wxInitDialogEvent& aEvent ) +{ + wxWindow* focusWnd = wxWindow::FindFocus(); + + // If focusWnd is not already this window or a child of it, then SetFocus(). + // Otherwise the derived class's constructor SetFocus() already to a specific + // child control. + + if( !findWindowRecursively( this, focusWnd ) ) + { + // Linux wxGTK needs this to allow the ESCAPE key to close a wxDialog window. + SetFocus(); + } + + aEvent.Skip(); // derived class's handler should be called too +} +#endif + + +/* + Quasi-Modal Mode Explained: + + The gtk calls in wxDialog::ShowModal() cause event routing problems if that + modal dialog then tries to use KIWAY_PLAYER::ShowModal(). The latter shows up + and mostly works but does not respond to the window decoration close button. + There is no way to get around this without reversing the gtk calls temporarily. + + Quasi-Modal mode is our own almost modal mode which disables only the parent + of the DIALOG_SHIM, leaving other frames operable and while staying captured in the + nested event loop. This avoids the gtk calls and leaves event routing pure + and sufficient to operate the KIWAY_PLAYER::ShowModal() properly. When using + ShowQuasiModal() you have to use EndQuasiModal() in your dialogs and not + EndModal(). There is also IsQuasiModal() but its value can only be true + when the nested event loop is active. Do not mix the modal and quasi-modal + functions. Use one set or the other. + + You might find this behavior preferable over a pure modal mode, and it was said + that only the Mac has this natively, but now other platforms have something + similar. You CAN use it anywhere for any dialog. But you MUST use it when + you want to use KIWAY_PLAYER::ShowModal() from a dialog event. +*/ + + + +/* +/// wxEventLoopActivator but with a friend so it +/// has access to m_evtLoopOld, and it does not SetActive() as that is +/// done inside base class Run(). +class ELOOP_ACTIVATOR +{ + friend class EVENT_LOOP; + +public: + ELOOP_ACTIVATOR( WX_EVENT_LOOP* evtLoop ) + { + m_evtLoopOld = wxEventLoopBase::GetActive(); + // wxEventLoopBase::SetActive( evtLoop ); + } + + ~ELOOP_ACTIVATOR() + { + // restore the previously active event loop + wxEventLoopBase::SetActive( m_evtLoopOld ); + } + +private: + WX_EVENT_LOOP* m_evtLoopOld; +}; +*/ + + +class EVENT_LOOP : public WX_EVENT_LOOP +{ +public: + + EVENT_LOOP() + { + ; + } + + ~EVENT_LOOP() + { + } + +#if 0 // does not work any better than inherited wxGuiEventLoop functions: + + // sets the "should exit" flag and wakes up the loop so that it terminates + // soon + void ScheduleExit( int rc = 0 ) + { + wxCHECK_RET( IsInsideRun(), wxT("can't call ScheduleExit() if not running") ); + + m_exitcode = rc; + m_shouldExit = true; + + OnExit(); + + // all we have to do to exit from the loop is to (maybe) wake it up so that + // it can notice that Exit() had been called + // + // in particular, do *not* use here calls such as PostQuitMessage() (under + // MSW) which terminate the current event loop here because we're not sure + // that it is going to be processed by the correct event loop: it would be + // possible that another one is started and terminated by mistake if we do + // this + WakeUp(); + } + + int Run() + { + // event loops are not recursive, you need to create another loop! + //wxCHECK_MSG( !IsInsideRun(), -1, wxT("can't reenter a message loop") ); + + // ProcessIdle() and ProcessEvents() below may throw so the code here should + // be exception-safe, hence we must use local objects for all actions we + // should undo + wxEventLoopActivator activate(this); + + // We might be called again, after a previous call to ScheduleExit(), so + // reset this flag. + m_shouldExit = false; + + // Set this variable to true for the duration of this method. + setInsideRun( true ); + + struct SET_FALSE + { + EVENT_LOOP* m_loop; + SET_FALSE( EVENT_LOOP* aLoop ) : m_loop( aLoop ) {} + ~SET_FALSE() { m_loop->setInsideRun( false ); } + } t( this ); + + // Finally really run the loop. + return DoRun(); + } + + bool ProcessEvents() + { + // process pending wx events first as they correspond to low-level events + // which happened before, i.e. typically pending events were queued by a + // previous call to Dispatch() and if we didn't process them now the next + // call to it might enqueue them again (as happens with e.g. socket events + // which would be generated as long as there is input available on socket + // and this input is only removed from it when pending event handlers are + // executed) + if( wxTheApp ) + { + wxTheApp->ProcessPendingEvents(); + + // One of the pending event handlers could have decided to exit the + // loop so check for the flag before trying to dispatch more events + // (which could block indefinitely if no more are coming). + if( m_shouldExit ) + return false; + } + + return Dispatch(); + } + + + int DoRun() + { + // we must ensure that OnExit() is called even if an exception is thrown + // from inside ProcessEvents() but we must call it from Exit() in normal + // situations because it is supposed to be called synchronously, + // wxModalEventLoop depends on this (so we can't just use ON_BLOCK_EXIT or + // something similar here) + #if wxUSE_EXCEPTIONS + for( ; ; ) + { + try + { + #endif // wxUSE_EXCEPTIONS + + // this is the event loop itself + for( ; ; ) + { + // generate and process idle events for as long as we don't + // have anything else to do + while ( !m_shouldExit && !Pending() && ProcessIdle() ) + ; + + if ( m_shouldExit ) + break; + + // a message came or no more idle processing to do, dispatch + // all the pending events and call Dispatch() to wait for the + // next message + if ( !ProcessEvents() ) + { + // we got WM_QUIT + break; + } + } + + // Process the remaining queued messages, both at the level of the + // underlying toolkit level (Pending/Dispatch()) and wx level + // (Has/ProcessPendingEvents()). + // + // We do run the risk of never exiting this loop if pending event + // handlers endlessly generate new events but they shouldn't do + // this in a well-behaved program and we shouldn't just discard the + // events we already have, they might be important. + for( ; ; ) + { + bool hasMoreEvents = false; + if ( wxTheApp && wxTheApp->HasPendingEvents() ) + { + wxTheApp->ProcessPendingEvents(); + hasMoreEvents = true; + } + + if ( Pending() ) + { + Dispatch(); + hasMoreEvents = true; + } + + if ( !hasMoreEvents ) + break; + } + + #if wxUSE_EXCEPTIONS + // exit the outer loop as well + break; + } + catch ( ... ) + { + try + { + if ( !wxTheApp || !wxTheApp->OnExceptionInMainLoop() ) + { + OnExit(); + break; + } + //else: continue running the event loop + } + catch ( ... ) + { + // OnException() throwed, possibly rethrowing the same + // exception again: very good, but we still need OnExit() to + // be called + OnExit(); + throw; + } + } + } + #endif // wxUSE_EXCEPTIONS + + return m_exitcode; + } + +protected: + int m_exitcode; + + /* this only works if you add + friend class EVENT_LOOP + to EventLoopBase + */ + void setInsideRun( bool aValue ) + { + m_isInsideRun = aValue; + } +#endif +}; + + +int DIALOG_SHIM::ShowQuasiModal() +{ + // This is an exception safe way to zero a pointer before returning. + // Yes, even though DismissModal() clears this first normally, this is + // here in case there's an exception before the dialog is dismissed. + struct NULLER + { + void*& m_what; + NULLER( void*& aPtr ) : m_what( aPtr ) {} + ~NULLER() { m_what = 0; } // indeed, set it to NULL on destruction + } clear_this( (void*&) m_qmodal_loop ); + + // release the mouse if it's currently captured as the window having it + // will be disabled when this dialog is shown -- but will still keep the + // capture making it impossible to do anything in the modal dialog itself + wxWindow* win = wxWindow::GetCapture(); + if( win ) + win->ReleaseMouse(); + + // Get the optimal parent + wxWindow* parent = GetParentForModalDialog( GetParent(), GetWindowStyle() ); + + // Show the optimal parent + DBG( if( parent ) printf( "%s: optimal parent: %s\n", __func__, typeid(*parent).name() );) + + wxASSERT_MSG( !m_qmodal_parent_disabler, + wxT( "Caller using ShowQuasiModal() twice on same window?" ) ); + + // quasi-modal: disable only my "optimal" parent + m_qmodal_parent_disabler = new WDO_ENABLE_DISABLE( parent ); + + Show( true ); + + m_qmodal_showing = true; + + EVENT_LOOP event_loop; + + m_qmodal_loop = &event_loop; + + event_loop.Run(); + + return GetReturnCode(); +} + + +void DIALOG_SHIM::EndQuasiModal( int retCode ) +{ + // Hook up validator and transfer data from controls handling so quasi-modal dialogs + // handle validation in the same way as other dialogs. + if( ( retCode == wxID_OK ) && ( !Validate() || !TransferDataFromWindow() ) ) + return; + + SetReturnCode( retCode ); + + if( !IsQuasiModal() ) + { + wxFAIL_MSG( wxT( "either DIALOG_SHIM::EndQuasiModal called twice or ShowQuasiModal wasn't called" ) ); + return; + } + + m_qmodal_showing = false; + + if( m_qmodal_loop ) + { + if( m_qmodal_loop->IsRunning() ) + m_qmodal_loop->Exit( 0 ); + else + m_qmodal_loop->ScheduleExit( 0 ); + + m_qmodal_loop = NULL; + } + + delete m_qmodal_parent_disabler; + m_qmodal_parent_disabler = 0; + + Show( false ); +} + + +void DIALOG_SHIM::OnCloseWindow( wxCloseEvent& aEvent ) +{ + if( IsQuasiModal() ) + { + EndQuasiModal( wxID_CANCEL ); + return; + } + + // This is mandatory to allow wxDialogBase::OnCloseWindow() to be called. + aEvent.Skip(); +} + + +void DIALOG_SHIM::OnButton( wxCommandEvent& aEvent ) +{ + if( IsQuasiModal() ) + { + const int id = aEvent.GetId(); + + if( id == GetAffirmativeId() ) + { + EndQuasiModal( id ); + } + else if( id == wxID_APPLY ) + { + // Dialogs that provide Apply buttons should make sure data is valid before + // allowing a transfer, as there is no other way to indicate failure + // (i.e. the dialog can't refuse to close as it might with OK, because it + // isn't closing anyway) + if( Validate() ) + { + bool success = TransferDataFromWindow(); + (void) success; + } + } + else if( id == GetEscapeId() || + (id == wxID_CANCEL && GetEscapeId() == wxID_ANY) ) + { + EndQuasiModal( wxID_CANCEL ); + } + else // not a standard button + { + aEvent.Skip(); + } + + return; + } + + // This is mandatory to allow wxDialogBase::OnButton() to be called. + aEvent.Skip(); +} diff --git a/common/dialogs/dialog_display_info_HTML_base.cpp b/common/dialogs/dialog_display_info_HTML_base.cpp new file mode 100644 index 0000000..5a693c8 --- /dev/null +++ b/common/dialogs/dialog_display_info_HTML_base.cpp @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Nov 5 2013) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "dialog_display_info_HTML_base.h" + +/////////////////////////////////////////////////////////////////////////// + +DIALOG_DISPLAY_HTML_TEXT_BASE::DIALOG_DISPLAY_HTML_TEXT_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( 400,120 ), wxDefaultSize ); + + wxBoxSizer* bMainSizer; + bMainSizer = new wxBoxSizer( wxVERTICAL ); + + m_htmlWindow = new wxHtmlWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO|wxSUNKEN_BORDER ); + bMainSizer->Add( m_htmlWindow, 1, wxEXPAND, 5 ); + + m_buttonClose = new wxButton( this, wxID_CANCEL, _("Close"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT ); + m_buttonClose->SetDefault(); + bMainSizer->Add( m_buttonClose, 0, wxALIGN_RIGHT|wxALL, 10 ); + + + this->SetSizer( bMainSizer ); + this->Layout(); + + // Connect Events + m_htmlWindow->Connect( wxEVT_COMMAND_HTML_LINK_CLICKED, wxHtmlLinkEventHandler( DIALOG_DISPLAY_HTML_TEXT_BASE::OnHTMLLinkClicked ), NULL, this ); + m_buttonClose->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_DISPLAY_HTML_TEXT_BASE::OnCloseButtonClick ), NULL, this ); +} + +DIALOG_DISPLAY_HTML_TEXT_BASE::~DIALOG_DISPLAY_HTML_TEXT_BASE() +{ + // Disconnect Events + m_htmlWindow->Disconnect( wxEVT_COMMAND_HTML_LINK_CLICKED, wxHtmlLinkEventHandler( DIALOG_DISPLAY_HTML_TEXT_BASE::OnHTMLLinkClicked ), NULL, this ); + m_buttonClose->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_DISPLAY_HTML_TEXT_BASE::OnCloseButtonClick ), NULL, this ); + +} diff --git a/common/dialogs/dialog_display_info_HTML_base.fbp b/common/dialogs/dialog_display_info_HTML_base.fbp new file mode 100644 index 0000000..2147dcd --- /dev/null +++ b/common/dialogs/dialog_display_info_HTML_base.fbp @@ -0,0 +1,271 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<wxFormBuilder_Project> + <FileVersion major="1" minor="13" /> + <object class="Project" expanded="1"> + <property name="class_decoration" /> + <property name="code_generation">C++</property> + <property name="disconnect_events">1</property> + <property name="disconnect_mode">source_name</property> + <property name="disconnect_php_events">0</property> + <property name="disconnect_python_events">0</property> + <property name="embedded_files_path">res</property> + <property name="encoding">UTF-8</property> + <property name="event_generation">connect</property> + <property name="file">dialog_display_info_HTML_base</property> + <property name="first_id">1000</property> + <property name="help_provider">none</property> + <property name="internationalize">1</property> + <property name="name">dialog_display_info_HTML</property> + <property name="namespace" /> + <property name="path">.</property> + <property name="precompiled_header" /> + <property name="relative_path">1</property> + <property name="skip_lua_events">1</property> + <property name="skip_php_events">1</property> + <property name="skip_python_events">1</property> + <property name="ui_table">UI</property> + <property name="use_enum">0</property> + <property name="use_microsoft_bom">0</property> + <object class="Dialog" expanded="1"> + <property name="aui_managed">0</property> + <property name="aui_manager_style">wxAUI_MGR_DEFAULT</property> + <property name="bg" /> + <property name="center" /> + <property name="context_help" /> + <property name="context_menu">1</property> + <property name="enabled">1</property> + <property name="event_handler">impl_virtual</property> + <property name="extra_style" /> + <property name="fg" /> + <property name="font" /> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="maximum_size" /> + <property name="minimum_size">400,120</property> + <property name="name">DIALOG_DISPLAY_HTML_TEXT_BASE</property> + <property name="pos" /> + <property name="size">465,202</property> + <property name="style">wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER</property> + <property name="subclass">DIALOG_SHIM; dialog_shim.h</property> + <property name="title" /> + <property name="tooltip" /> + <property name="window_extra_style" /> + <property name="window_name" /> + <property name="window_style" /> + <event name="OnActivate" /> + <event name="OnActivateApp" /> + <event name="OnAuiFindManager" /> + <event name="OnAuiPaneButton" /> + <event name="OnAuiPaneClose" /> + <event name="OnAuiPaneMaximize" /> + <event name="OnAuiPaneRestore" /> + <event name="OnAuiRender" /> + <event name="OnChar" /> + <event name="OnClose" /> + <event name="OnEnterWindow" /> + <event name="OnEraseBackground" /> + <event name="OnHibernate" /> + <event name="OnIconize" /> + <event name="OnIdle" /> + <event name="OnInitDialog" /> + <event name="OnKeyDown" /> + <event name="OnKeyUp" /> + <event name="OnKillFocus" /> + <event name="OnLeaveWindow" /> + <event name="OnLeftDClick" /> + <event name="OnLeftDown" /> + <event name="OnLeftUp" /> + <event name="OnMiddleDClick" /> + <event name="OnMiddleDown" /> + <event name="OnMiddleUp" /> + <event name="OnMotion" /> + <event name="OnMouseEvents" /> + <event name="OnMouseWheel" /> + <event name="OnPaint" /> + <event name="OnRightDClick" /> + <event name="OnRightDown" /> + <event name="OnRightUp" /> + <event name="OnSetFocus" /> + <event name="OnSize" /> + <event name="OnUpdateUI" /> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size">-1,-1</property> + <property name="name">bMainSizer</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxHtmlWindow" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer" /> + <property name="aui_name" /> + <property name="aui_position" /> + <property name="aui_row" /> + <property name="best_size" /> + <property name="bg" /> + <property name="caption" /> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help" /> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg" /> + <property name="floatable">1</property> + <property name="font" /> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size" /> + <property name="maximize_button">0</property> + <property name="maximum_size" /> + <property name="min_size" /> + <property name="minimize_button">0</property> + <property name="minimum_size">-1,-1</property> + <property name="moveable">1</property> + <property name="name">m_htmlWindow</property> + <property name="pane_border">1</property> + <property name="pane_position" /> + <property name="pane_size" /> + <property name="permission">public</property> + <property name="pin_button">1</property> + <property name="pos" /> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size" /> + <property name="style">wxHW_SCROLLBAR_AUTO</property> + <property name="subclass" /> + <property name="toolbar_pane">0</property> + <property name="tooltip" /> + <property name="window_extra_style" /> + <property name="window_name" /> + <property name="window_style">wxSUNKEN_BORDER</property> + <event name="OnChar" /> + <event name="OnEnterWindow" /> + <event name="OnEraseBackground" /> + <event name="OnHtmlCellClicked" /> + <event name="OnHtmlCellHover" /> + <event name="OnHtmlLinkClicked">OnHTMLLinkClicked</event> + <event name="OnKeyDown" /> + <event name="OnKeyUp" /> + <event name="OnKillFocus" /> + <event name="OnLeaveWindow" /> + <event name="OnLeftDClick" /> + <event name="OnLeftDown" /> + <event name="OnLeftUp" /> + <event name="OnMiddleDClick" /> + <event name="OnMiddleDown" /> + <event name="OnMiddleUp" /> + <event name="OnMotion" /> + <event name="OnMouseEvents" /> + <event name="OnMouseWheel" /> + <event name="OnPaint" /> + <event name="OnRightDClick" /> + <event name="OnRightDown" /> + <event name="OnRightUp" /> + <event name="OnSetFocus" /> + <event name="OnSize" /> + <event name="OnUpdateUI" /> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">10</property> + <property name="flag">wxALIGN_RIGHT|wxALL</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer" /> + <property name="aui_name" /> + <property name="aui_position" /> + <property name="aui_row" /> + <property name="best_size" /> + <property name="bg" /> + <property name="caption" /> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help" /> + <property name="context_menu">1</property> + <property name="default">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg" /> + <property name="floatable">1</property> + <property name="font" /> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_CANCEL</property> + <property name="label">Close</property> + <property name="max_size" /> + <property name="maximize_button">0</property> + <property name="maximum_size" /> + <property name="min_size" /> + <property name="minimize_button">0</property> + <property name="minimum_size" /> + <property name="moveable">1</property> + <property name="name">m_buttonClose</property> + <property name="pane_border">1</property> + <property name="pane_position" /> + <property name="pane_size" /> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos" /> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size" /> + <property name="style">wxBU_EXACTFIT</property> + <property name="subclass" /> + <property name="toolbar_pane">0</property> + <property name="tooltip" /> + <property name="validator_data_type" /> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable" /> + <property name="window_extra_style" /> + <property name="window_name" /> + <property name="window_style" /> + <event name="OnButtonClick">OnCloseButtonClick</event> + <event name="OnChar" /> + <event name="OnEnterWindow" /> + <event name="OnEraseBackground" /> + <event name="OnKeyDown" /> + <event name="OnKeyUp" /> + <event name="OnKillFocus" /> + <event name="OnLeaveWindow" /> + <event name="OnLeftDClick" /> + <event name="OnLeftDown" /> + <event name="OnLeftUp" /> + <event name="OnMiddleDClick" /> + <event name="OnMiddleDown" /> + <event name="OnMiddleUp" /> + <event name="OnMotion" /> + <event name="OnMouseEvents" /> + <event name="OnMouseWheel" /> + <event name="OnPaint" /> + <event name="OnRightDClick" /> + <event name="OnRightDown" /> + <event name="OnRightUp" /> + <event name="OnSetFocus" /> + <event name="OnSize" /> + <event name="OnUpdateUI" /> + </object> + </object> + </object> + </object> + </object> +</wxFormBuilder_Project> diff --git a/common/dialogs/dialog_display_info_HTML_base.h b/common/dialogs/dialog_display_info_HTML_base.h new file mode 100644 index 0000000..4cfa053 --- /dev/null +++ b/common/dialogs/dialog_display_info_HTML_base.h @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Nov 5 2013) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __DIALOG_DISPLAY_INFO_HTML_BASE_H__ +#define __DIALOG_DISPLAY_INFO_HTML_BASE_H__ + +#include <wx/artprov.h> +#include <wx/xrc/xmlres.h> +#include <wx/intl.h> +class DIALOG_SHIM; + +#include "dialog_shim.h" +#include <wx/html/htmlwin.h> +#include <wx/gdicmn.h> +#include <wx/font.h> +#include <wx/colour.h> +#include <wx/settings.h> +#include <wx/string.h> +#include <wx/button.h> +#include <wx/sizer.h> +#include <wx/dialog.h> + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class DIALOG_DISPLAY_HTML_TEXT_BASE +/////////////////////////////////////////////////////////////////////////////// +class DIALOG_DISPLAY_HTML_TEXT_BASE : public DIALOG_SHIM +{ + private: + + protected: + wxButton* m_buttonClose; + + // Virtual event handlers, overide them in your derived class + virtual void OnHTMLLinkClicked( wxHtmlLinkEvent& event ) { event.Skip(); } + virtual void OnCloseButtonClick( wxCommandEvent& event ) { event.Skip(); } + + + public: + wxHtmlWindow* m_htmlWindow; + + DIALOG_DISPLAY_HTML_TEXT_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 465,202 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~DIALOG_DISPLAY_HTML_TEXT_BASE(); + +}; + +#endif //__DIALOG_DISPLAY_INFO_HTML_BASE_H__ diff --git a/common/dialogs/dialog_env_var_config.cpp b/common/dialogs/dialog_env_var_config.cpp new file mode 100644 index 0000000..79ef7dd --- /dev/null +++ b/common/dialogs/dialog_env_var_config.cpp @@ -0,0 +1,285 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2015 Wayne Stambaugh <stambaughw@gmail.com> + * Copyright (C) 2015 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 + */ + +/** + * @file dialg_env_var_config.cpp + */ + +#include <dialog_env_var_config.h> + +#include <validators.h> +#include <html_messagebox.h> + +#include <wx/regex.h> + + +DIALOG_ENV_VAR_CONFIG::DIALOG_ENV_VAR_CONFIG( wxWindow* aParent, const ENV_VAR_MAP& aEnvVarMap ) : + DIALOG_ENV_VAR_CONFIG_BASE( aParent ) +{ + m_extDefsChanged = false; + m_envVarMap = aEnvVarMap; + + m_grid->AppendRows( (int) m_envVarMap.size() ); + + for( size_t row = 0; row < m_envVarMap.size(); row++ ) + { + wxGridCellTextEditor* editor = new wxGridCellTextEditor; + ENVIRONMENT_VARIABLE_CHAR_VALIDATOR envVarValidator; + editor->SetValidator( envVarValidator ); + m_grid->SetCellEditor( (int) row, 0, editor ); + + editor = new wxGridCellTextEditor; + FILE_NAME_WITH_PATH_CHAR_VALIDATOR pathValidator; + editor->SetValidator( pathValidator ); + m_grid->SetCellEditor( (int) row, 1, editor ); + } +} + + +bool DIALOG_ENV_VAR_CONFIG::TransferDataToWindow() +{ + wxLogDebug( wxT( "In DIALOG_ENV_VAR_CONFIG::TransferDataToWindow()." ) ); + + if( !wxDialog::TransferDataToWindow() ) + return false; + + long row = 0L; + + for( ENV_VAR_MAP_ITER it = m_envVarMap.begin(); it != m_envVarMap.end(); ++it ) + { + m_grid->SetCellValue( row, 0, it->first ); + m_grid->SetCellValue( row, 1, it->second.GetValue() ); + + // Highlight environment variables that are externally defined. + if( it->second.GetDefinedExternally() ) + { + wxGridCellAttr* attr = m_grid->GetOrCreateCellAttr( row, 0 ); + attr->SetBackgroundColour( *wxLIGHT_GREY ); + m_grid->SetRowAttr( row, attr ); + } + + row++; + } + + m_grid->AutoSizeColumns(); + m_grid->AutoSizeRows(); + GetSizer()->Layout(); + GetSizer()->Fit( this ); + GetSizer()->SetSizeHints( this ); + + return true; +} + + +bool DIALOG_ENV_VAR_CONFIG::TransferDataFromWindow() +{ + if( !wxDialog::TransferDataFromWindow() ) + return false; + + int row; + wxArrayString envVarNames; + + for( row = 0; row < m_grid->GetNumberRows(); row++ ) + { + wxString caption = _( "Invalid Input" ); + wxString name = m_grid->GetCellValue( row, 0 ); + wxString value = m_grid->GetCellValue( row, 1 ); + + // Ignore completely empty rows. + if( name.IsEmpty() && value.IsEmpty() ) + continue; + + wxLogDebug( wxT( "Row %d, name: %s, value %s." ), row, + GetChars( name ), GetChars( value ) ); + + // Name cannot be empty. + if( name.IsEmpty() ) + { + wxMessageBox( _( "Environment variable name cannot be empty." ), + caption, wxOK | wxICON_ERROR, this ); + m_grid->GoToCell( row, 0 ); + m_grid->SetGridCursor( row, 0 ); + return false; + } + + // Value cannot be empty. + if( value.IsEmpty() ) + { + wxMessageBox( _( "Environment variable value cannot be empty." ), caption, + wxOK | wxICON_ERROR, this ); + m_grid->GoToCell( row, 1 ); + m_grid->SetGridCursor( row, 1 ); + m_grid->SetFocus(); + return false; + } + + // First character of the environment variable name cannot be a digit (0-9). + if( name.Left( 1 ).IsNumber() ) + { + wxMessageBox( _( "The first character of an environment variable name cannot be " + "a digit (0-9)." ), caption, wxOK | wxICON_ERROR, this ); + m_grid->GoToCell( row, 0 ); + m_grid->SetGridCursor( row, 0 ); + m_grid->SelectBlock( row, 0, row, 0 ); + m_grid->SetFocus(); + return false; + } + + // Check for duplicate environment variable names. + if( envVarNames.Index( name ) != wxNOT_FOUND ) + { + wxMessageBox( _( "Cannot have duplicate environment variable names." ), caption, + wxOK | wxICON_ERROR, this ); + m_grid->GoToCell( row, 0 ); + m_grid->SetGridCursor( row, 0 ); + m_grid->SelectRow( row ); + m_grid->SetFocus(); + return false; + } + + envVarNames.Add( name ); + } + + // Add new entries and update any modified entries. + for( row = 0; row < m_grid->GetNumberRows(); row++ ) + { + wxString name = m_grid->GetCellValue( row, 0 ); + wxString value = m_grid->GetCellValue( row, 1 ); + ENV_VAR_MAP_ITER it = m_envVarMap.find( name ); + + if( it == m_envVarMap.end() ) + { + ENV_VAR_ITEM item( value, wxGetEnv( name, NULL ) ); + + // Add new environment variable. + m_envVarMap[ name ] = item; + } + else if( it->second.GetValue() != value ) + { + // Environment variable already defined but it's value changed. + it->second.SetValue( value ); + + // Externally defined variable has been changed. + if( it->second.GetDefinedExternally() ) + m_extDefsChanged = true; + } + } + + std::vector< wxString > removeFromMap; + + // Remove deleted entries from the map. + for( ENV_VAR_MAP_ITER it = m_envVarMap.begin(); it != m_envVarMap.end(); ++it ) + { + bool found = false; + + for( row = 0; row < m_grid->GetNumberRows(); row++ ) + { + if( m_grid->GetCellValue( row, 0 ) == it->first ) + { + found = true; + break; + } + } + + if( !found ) + removeFromMap.push_back( it->first ); + } + + for( size_t i = 0; i < removeFromMap.size(); i++ ) + m_envVarMap.erase( removeFromMap[i] ); + + return true; +} + + +void DIALOG_ENV_VAR_CONFIG::OnAddRow( wxCommandEvent& aEvent ) +{ + m_grid->AppendRows(); + + int row = m_grid->GetNumberRows() - 1; + wxGridCellTextEditor* editor = new wxGridCellTextEditor; + ENVIRONMENT_VARIABLE_CHAR_VALIDATOR envVarNameValidator; + editor->SetValidator( envVarNameValidator ); + m_grid->SetCellEditor( row, 0, editor ); + + editor = new wxGridCellTextEditor; + FILE_NAME_WITH_PATH_CHAR_VALIDATOR pathValidator; + editor->SetValidator( pathValidator ); + m_grid->SetCellEditor( row, 1, editor ); + m_grid->GoToCell( row, 0 ); + m_grid->SetGridCursor( row, 0 ); + m_grid->SetFocus(); +} + + +void DIALOG_ENV_VAR_CONFIG::OnDeleteSelectedRows( wxCommandEvent& aEvent ) +{ + if( !m_grid->IsSelection() ) + return; + + wxGridUpdateLocker locker( m_grid ); + + for( int n = 0; n < m_grid->GetNumberRows(); ) + { + if( m_grid->IsInSelection( n , 0 ) ) + m_grid->DeleteRows( n, 1 ); + else + n++; + } +} + + +void DIALOG_ENV_VAR_CONFIG::OnHelpRequest( wxCommandEvent& aEvent ) +{ + wxString msg = _( "Enter the name and path for each environment variable. Grey entries " + "are names that have been defined externally at the system or user " + "level. Environment variables defined at the system or user level " + "take precedence over the ones defined in this table. This means the " + "values in this table are ignored." ); + msg << wxT( "<br><br><b>" ); + msg << _( "To ensure environment variable names are valid on all platforms, the name field " + "will only accept upper case letters, digits, and the underscore characters." ); + msg << wxT( "</b><br><br>" ); + msg << _( "<b>KIGITHUB</b> is used by KiCad to define the URL of the repository " + "of the official KiCad libraries." ); + msg << wxT( "<br><br>" ); + msg << _( "<b>KISYS3DMOD</b> is the base path of system footprint 3D " + "shapes (.3Dshapes folders)." ); + msg << wxT( "<br><br>" ); + msg << _( "<b>KISYSMOD</b> is the base path of locally installed system " + "footprint libraries (.pretty folders)." ); + msg << wxT( "<br><br>" ); + msg << _( "<b>KIPRJMOD</b> is internally defined by KiCad (cannot be edited) and is set " + "to the absolute path of the currently loaded project file. This environment " + "variable can be used to define files and paths relative to the currently loaded " + "project. For instance, ${KIPRJMOD}/libs/footprints.pretty can be defined as a " + "folder containing a project specific footprint library named footprints.pretty." ); + msg << wxT( "<br><br>" ); + msg << _( "<b>KICAD_PTEMPLATES</b> is optional and can be defined if you want to " + "create your own project templates folder." ); + + HTML_MESSAGE_BOX dlg( GetParent(), _( "Environment Variable Help" ) ); + dlg.AddHTML_Text( msg ); + dlg.ShowModal(); +} diff --git a/common/dialogs/dialog_env_var_config_base.cpp b/common/dialogs/dialog_env_var_config_base.cpp new file mode 100644 index 0000000..8f8590a --- /dev/null +++ b/common/dialogs/dialog_env_var_config_base.cpp @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 17 2015) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "dialog_env_var_config_base.h" + +/////////////////////////////////////////////////////////////////////////// + +DIALOG_ENV_VAR_CONFIG_BASE::DIALOG_ENV_VAR_CONFIG_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( wxDefaultSize, wxDefaultSize ); + + wxBoxSizer* mainSizer; + mainSizer = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bupperSizer; + bupperSizer = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bleftSizer; + bleftSizer = new wxBoxSizer( wxVERTICAL ); + + m_grid = new wxGrid( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + + // Grid + m_grid->CreateGrid( 0, 2 ); + m_grid->EnableEditing( true ); + m_grid->EnableGridLines( true ); + m_grid->EnableDragGridSize( true ); + m_grid->SetMargins( 0, 0 ); + + // Columns + m_grid->EnableDragColMove( false ); + m_grid->EnableDragColSize( true ); + m_grid->SetColLabelSize( 30 ); + m_grid->SetColLabelValue( 0, _("Name") ); + m_grid->SetColLabelValue( 1, _("Path") ); + m_grid->SetColLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE ); + + // Rows + m_grid->EnableDragRowSize( true ); + m_grid->SetRowLabelSize( 40 ); + m_grid->SetRowLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE ); + + // Label Appearance + + // Cell Defaults + m_grid->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_TOP ); + bleftSizer->Add( m_grid, 1, wxALL|wxEXPAND, 5 ); + + + bupperSizer->Add( bleftSizer, 1, wxEXPAND, 5 ); + + wxBoxSizer* brightSizer; + brightSizer = new wxBoxSizer( wxVERTICAL ); + + m_buttonAdd = new wxButton( this, wxID_ANY, _("Add"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonAdd->SetToolTip( _("Add a new entry to the table.") ); + + brightSizer->Add( m_buttonAdd, 0, wxALL|wxEXPAND, 5 ); + + m_buttonDelete = new wxButton( this, wxID_ANY, _("Delete"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonDelete->SetToolTip( _("Remove the selected entry from the table.") ); + + brightSizer->Add( m_buttonDelete, 0, wxBOTTOM|wxLEFT|wxRIGHT|wxEXPAND, 5 ); + + + bupperSizer->Add( brightSizer, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + mainSizer->Add( bupperSizer, 1, wxEXPAND, 5 ); + + m_staticline1 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + mainSizer->Add( m_staticline1, 0, wxEXPAND | wxALL, 5 ); + + m_sdbSizer = new wxStdDialogButtonSizer(); + m_sdbSizerOK = new wxButton( this, wxID_OK ); + m_sdbSizer->AddButton( m_sdbSizerOK ); + m_sdbSizerCancel = new wxButton( this, wxID_CANCEL ); + m_sdbSizer->AddButton( m_sdbSizerCancel ); + m_sdbSizerHelp = new wxButton( this, wxID_HELP ); + m_sdbSizer->AddButton( m_sdbSizerHelp ); + m_sdbSizer->Realize(); + + mainSizer->Add( m_sdbSizer, 0, wxALL|wxALIGN_RIGHT, 5 ); + + + this->SetSizer( mainSizer ); + this->Layout(); + + this->Centre( wxBOTH ); + + // Connect Events + m_buttonAdd->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_ENV_VAR_CONFIG_BASE::OnAddRow ), NULL, this ); + m_buttonDelete->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_ENV_VAR_CONFIG_BASE::OnDeleteSelectedRows ), NULL, this ); + m_sdbSizerHelp->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_ENV_VAR_CONFIG_BASE::OnHelpRequest ), NULL, this ); +} + +DIALOG_ENV_VAR_CONFIG_BASE::~DIALOG_ENV_VAR_CONFIG_BASE() +{ + // Disconnect Events + m_buttonAdd->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_ENV_VAR_CONFIG_BASE::OnAddRow ), NULL, this ); + m_buttonDelete->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_ENV_VAR_CONFIG_BASE::OnDeleteSelectedRows ), NULL, this ); + m_sdbSizerHelp->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_ENV_VAR_CONFIG_BASE::OnHelpRequest ), NULL, this ); + +} diff --git a/common/dialogs/dialog_env_var_config_base.fbp b/common/dialogs/dialog_env_var_config_base.fbp new file mode 100644 index 0000000..8c86db4 --- /dev/null +++ b/common/dialogs/dialog_env_var_config_base.fbp @@ -0,0 +1,558 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<wxFormBuilder_Project> + <FileVersion major="1" minor="13" /> + <object class="Project" expanded="1"> + <property name="class_decoration"></property> + <property name="code_generation">C++</property> + <property name="disconnect_events">1</property> + <property name="disconnect_mode">source_name</property> + <property name="disconnect_php_events">0</property> + <property name="disconnect_python_events">0</property> + <property name="embedded_files_path">res</property> + <property name="encoding">UTF-8</property> + <property name="event_generation">connect</property> + <property name="file">dialog_env_var_config_base</property> + <property name="first_id">1000</property> + <property name="help_provider">none</property> + <property name="internationalize">1</property> + <property name="name">dialog_env_var_editor_base</property> + <property name="namespace"></property> + <property name="path">.</property> + <property name="precompiled_header"></property> + <property name="relative_path">1</property> + <property name="skip_lua_events">1</property> + <property name="skip_php_events">1</property> + <property name="skip_python_events">1</property> + <property name="ui_table">UI</property> + <property name="use_enum">0</property> + <property name="use_microsoft_bom">0</property> + <object class="Dialog" expanded="1"> + <property name="aui_managed">0</property> + <property name="aui_manager_style">wxAUI_MGR_DEFAULT</property> + <property name="bg"></property> + <property name="center">wxBOTH</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="enabled">1</property> + <property name="event_handler">impl_virtual</property> + <property name="extra_style"></property> + <property name="fg"></property> + <property name="font"></property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="maximum_size"></property> + <property name="minimum_size"></property> + <property name="name">DIALOG_ENV_VAR_CONFIG_BASE</property> + <property name="pos"></property> + <property name="size">363,177</property> + <property name="style">wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER</property> + <property name="subclass">DIALOG_SHIM; dialog_shim.h</property> + <property name="title">Path Configuration</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnActivate"></event> + <event name="OnActivateApp"></event> + <event name="OnAuiFindManager"></event> + <event name="OnAuiPaneButton"></event> + <event name="OnAuiPaneClose"></event> + <event name="OnAuiPaneMaximize"></event> + <event name="OnAuiPaneRestore"></event> + <event name="OnAuiRender"></event> + <event name="OnChar"></event> + <event name="OnClose"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnHibernate"></event> + <event name="OnIconize"></event> + <event name="OnIdle"></event> + <event name="OnInitDialog"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">mainSizer</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bupperSizer</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bleftSizer</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxGrid" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="autosize_cols">0</property> + <property name="autosize_rows">0</property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="cell_bg"></property> + <property name="cell_font"></property> + <property name="cell_horiz_alignment">wxALIGN_LEFT</property> + <property name="cell_text"></property> + <property name="cell_vert_alignment">wxALIGN_TOP</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="col_label_horiz_alignment">wxALIGN_CENTRE</property> + <property name="col_label_size">30</property> + <property name="col_label_values">"Name" "Path"</property> + <property name="col_label_vert_alignment">wxALIGN_CENTRE</property> + <property name="cols">2</property> + <property name="column_sizes"></property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="drag_col_move">0</property> + <property name="drag_col_size">1</property> + <property name="drag_grid_size">1</property> + <property name="drag_row_size">1</property> + <property name="editing">1</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="grid_line_color"></property> + <property name="grid_lines">1</property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label_bg"></property> + <property name="label_font"></property> + <property name="label_text"></property> + <property name="margin_height">0</property> + <property name="margin_width">0</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_grid</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="row_label_horiz_alignment">wxALIGN_CENTRE</property> + <property name="row_label_size">40</property> + <property name="row_label_values"></property> + <property name="row_label_vert_alignment">wxALIGN_CENTRE</property> + <property name="row_sizes"></property> + <property name="rows">0</property> + <property name="show">1</property> + <property name="size"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnGridCellChange"></event> + <event name="OnGridCellLeftClick"></event> + <event name="OnGridCellLeftDClick"></event> + <event name="OnGridCellRightClick"></event> + <event name="OnGridCellRightDClick"></event> + <event name="OnGridCmdCellChange"></event> + <event name="OnGridCmdCellLeftClick"></event> + <event name="OnGridCmdCellLeftDClick"></event> + <event name="OnGridCmdCellRightClick"></event> + <event name="OnGridCmdCellRightDClick"></event> + <event name="OnGridCmdColSize"></event> + <event name="OnGridCmdEditorCreated"></event> + <event name="OnGridCmdEditorHidden"></event> + <event name="OnGridCmdEditorShown"></event> + <event name="OnGridCmdLabelLeftClick"></event> + <event name="OnGridCmdLabelLeftDClick"></event> + <event name="OnGridCmdLabelRightClick"></event> + <event name="OnGridCmdLabelRightDClick"></event> + <event name="OnGridCmdRangeSelect"></event> + <event name="OnGridCmdRowSize"></event> + <event name="OnGridCmdSelectCell"></event> + <event name="OnGridColSize"></event> + <event name="OnGridEditorCreated"></event> + <event name="OnGridEditorHidden"></event> + <event name="OnGridEditorShown"></event> + <event name="OnGridLabelLeftClick"></event> + <event name="OnGridLabelLeftDClick"></event> + <event name="OnGridLabelRightClick"></event> + <event name="OnGridLabelRightDClick"></event> + <event name="OnGridRangeSelect"></event> + <event name="OnGridRowSize"></event> + <event name="OnGridSelectCell"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL</property> + <property name="proportion">0</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">brightSizer</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxEXPAND</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default">0</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Add</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_buttonAdd</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip">Add a new entry to the table.</property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnButtonClick">OnAddRow</event> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxBOTTOM|wxLEFT|wxRIGHT|wxEXPAND</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default">0</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Delete</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_buttonDelete</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip">Remove the selected entry from the table.</property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnButtonClick">OnDeleteSelectedRows</event> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND | wxALL</property> + <property name="proportion">0</property> + <object class="wxStaticLine" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticline1</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style">wxLI_HORIZONTAL</property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxALIGN_RIGHT</property> + <property name="proportion">0</property> + <object class="wxStdDialogButtonSizer" expanded="1"> + <property name="Apply">0</property> + <property name="Cancel">1</property> + <property name="ContextHelp">0</property> + <property name="Help">1</property> + <property name="No">0</property> + <property name="OK">1</property> + <property name="Save">0</property> + <property name="Yes">0</property> + <property name="minimum_size"></property> + <property name="name">m_sdbSizer</property> + <property name="permission">protected</property> + <event name="OnApplyButtonClick"></event> + <event name="OnCancelButtonClick"></event> + <event name="OnContextHelpButtonClick"></event> + <event name="OnHelpButtonClick">OnHelpRequest</event> + <event name="OnNoButtonClick"></event> + <event name="OnOKButtonClick"></event> + <event name="OnSaveButtonClick"></event> + <event name="OnYesButtonClick"></event> + </object> + </object> + </object> + </object> + </object> +</wxFormBuilder_Project> diff --git a/common/dialogs/dialog_env_var_config_base.h b/common/dialogs/dialog_env_var_config_base.h new file mode 100644 index 0000000..69b74f2 --- /dev/null +++ b/common/dialogs/dialog_env_var_config_base.h @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 17 2015) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __DIALOG_ENV_VAR_CONFIG_BASE_H__ +#define __DIALOG_ENV_VAR_CONFIG_BASE_H__ + +#include <wx/artprov.h> +#include <wx/xrc/xmlres.h> +#include <wx/intl.h> +class DIALOG_SHIM; + +#include "dialog_shim.h" +#include <wx/colour.h> +#include <wx/settings.h> +#include <wx/string.h> +#include <wx/font.h> +#include <wx/grid.h> +#include <wx/gdicmn.h> +#include <wx/sizer.h> +#include <wx/button.h> +#include <wx/statline.h> +#include <wx/dialog.h> + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class DIALOG_ENV_VAR_CONFIG_BASE +/////////////////////////////////////////////////////////////////////////////// +class DIALOG_ENV_VAR_CONFIG_BASE : public DIALOG_SHIM +{ + private: + + protected: + wxGrid* m_grid; + wxButton* m_buttonAdd; + wxButton* m_buttonDelete; + wxStaticLine* m_staticline1; + wxStdDialogButtonSizer* m_sdbSizer; + wxButton* m_sdbSizerOK; + wxButton* m_sdbSizerCancel; + wxButton* m_sdbSizerHelp; + + // Virtual event handlers, overide them in your derived class + virtual void OnAddRow( wxCommandEvent& event ) { event.Skip(); } + virtual void OnDeleteSelectedRows( wxCommandEvent& event ) { event.Skip(); } + virtual void OnHelpRequest( wxCommandEvent& event ) { event.Skip(); } + + + public: + + DIALOG_ENV_VAR_CONFIG_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Path Configuration"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 363,177 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~DIALOG_ENV_VAR_CONFIG_BASE(); + +}; + +#endif //__DIALOG_ENV_VAR_CONFIG_BASE_H__ diff --git a/common/dialogs/dialog_exit_base.cpp b/common/dialogs/dialog_exit_base.cpp new file mode 100644 index 0000000..0e1d422 --- /dev/null +++ b/common/dialogs/dialog_exit_base.cpp @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Oct 8 2012) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "dialog_exit_base.h" + +/////////////////////////////////////////////////////////////////////////// + +DIALOG_EXIT_BASE::DIALOG_EXIT_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( wxDefaultSize, wxDefaultSize ); + + wxBoxSizer* bSizerMain; + bSizerMain = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizerUpper; + bSizerUpper = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizerBitmap; + bSizerBitmap = new wxBoxSizer( wxVERTICAL ); + + m_bitmap = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizerBitmap->Add( m_bitmap, 0, wxALL, 5 ); + + + bSizerUpper->Add( bSizerBitmap, 0, 0, 5 ); + + wxBoxSizer* bSizerMessages; + bSizerMessages = new wxBoxSizer( wxVERTICAL ); + + m_TextInfo = new wxStaticText( this, wxID_ANY, _("Save the changes before closing?"), wxDefaultPosition, wxDefaultSize, 0 ); + m_TextInfo->Wrap( -1 ); + m_TextInfo->SetFont( wxFont( 8, 74, 90, 92, false, wxT("MS Shell Dlg 2") ) ); + + bSizerMessages->Add( m_TextInfo, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizerMessages->Add( 10, 10, 0, 0, 5 ); + + m_staticText2 = new wxStaticText( this, wxID_ANY, _("If you don't save, all your changes will be permanently lost."), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText2->Wrap( -1 ); + bSizerMessages->Add( m_staticText2, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizerUpper->Add( bSizerMessages, 1, wxEXPAND, 5 ); + + + bSizerMain->Add( bSizerUpper, 1, wxEXPAND, 5 ); + + wxBoxSizer* bSizerLower; + bSizerLower = new wxBoxSizer( wxVERTICAL ); + + m_staticline = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizerLower->Add( m_staticline, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizerButtons; + bSizerButtons = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonSaveAndExit = new wxButton( this, wxID_ANY, _("Save and Exit"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonSaveAndExit->SetDefault(); + bSizerButtons->Add( m_buttonSaveAndExit, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_buttonExitNoSave = new wxButton( this, wxID_ANY, _("Exit without Save"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizerButtons->Add( m_buttonExitNoSave, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizerButtons->Add( m_buttonCancel, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerLower->Add( bSizerButtons, 0, wxALIGN_RIGHT, 5 ); + + + bSizerMain->Add( bSizerLower, 0, wxEXPAND, 5 ); + + + this->SetSizer( bSizerMain ); + this->Layout(); + bSizerMain->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + m_buttonSaveAndExit->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_EXIT_BASE::OnSaveAndExit ), NULL, this ); + m_buttonExitNoSave->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_EXIT_BASE::OnExitNoSave ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_EXIT_BASE::OnCancel ), NULL, this ); +} + +DIALOG_EXIT_BASE::~DIALOG_EXIT_BASE() +{ + // Disconnect Events + m_buttonSaveAndExit->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_EXIT_BASE::OnSaveAndExit ), NULL, this ); + m_buttonExitNoSave->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_EXIT_BASE::OnExitNoSave ), NULL, this ); + m_buttonCancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_EXIT_BASE::OnCancel ), NULL, this ); + +} diff --git a/common/dialogs/dialog_exit_base.fbp b/common/dialogs/dialog_exit_base.fbp new file mode 100644 index 0000000..a02b755 --- /dev/null +++ b/common/dialogs/dialog_exit_base.fbp @@ -0,0 +1,754 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<wxFormBuilder_Project> + <FileVersion major="1" minor="11" /> + <object class="Project" expanded="1"> + <property name="class_decoration"></property> + <property name="code_generation">C++</property> + <property name="disconnect_events">1</property> + <property name="disconnect_mode">source_name</property> + <property name="disconnect_php_events">0</property> + <property name="disconnect_python_events">0</property> + <property name="embedded_files_path">res</property> + <property name="encoding">UTF-8</property> + <property name="event_generation">connect</property> + <property name="file">dialog_exit_base</property> + <property name="first_id">1000</property> + <property name="help_provider">none</property> + <property name="internationalize">1</property> + <property name="name">dialog_exit_base</property> + <property name="namespace"></property> + <property name="path">.</property> + <property name="precompiled_header"></property> + <property name="relative_path">1</property> + <property name="skip_php_events">1</property> + <property name="skip_python_events">1</property> + <property name="use_enum">0</property> + <property name="use_microsoft_bom">0</property> + <object class="Dialog" expanded="1"> + <property name="aui_managed">0</property> + <property name="aui_manager_style">wxAUI_MGR_DEFAULT</property> + <property name="bg"></property> + <property name="center">wxBOTH</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="enabled">1</property> + <property name="event_handler">impl_virtual</property> + <property name="extra_style"></property> + <property name="fg"></property> + <property name="font"></property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="maximum_size"></property> + <property name="minimum_size"></property> + <property name="name">DIALOG_EXIT_BASE</property> + <property name="pos"></property> + <property name="size">-1,-1</property> + <property name="style">wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER</property> + <property name="subclass">DIALOG_SHIM; dialog_shim.h</property> + <property name="title"></property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnActivate"></event> + <event name="OnActivateApp"></event> + <event name="OnAuiFindManager"></event> + <event name="OnAuiPaneButton"></event> + <event name="OnAuiPaneClose"></event> + <event name="OnAuiPaneMaximize"></event> + <event name="OnAuiPaneRestore"></event> + <event name="OnAuiRender"></event> + <event name="OnChar"></event> + <event name="OnClose"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnHibernate"></event> + <event name="OnIconize"></event> + <event name="OnIdle"></event> + <event name="OnInitDialog"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerMain</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerUpper</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag"></property> + <property name="proportion">0</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerBitmap</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL</property> + <property name="proportion">0</property> + <object class="wxStaticBitmap" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="bitmap"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_bitmap</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerMessages</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxALIGN_CENTER_HORIZONTAL</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font">MS Shell Dlg 2,90,92,8,74,0</property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Save the changes before closing?</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_TextInfo</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag"></property> + <property name="proportion">0</property> + <object class="spacer" expanded="1"> + <property name="height">10</property> + <property name="permission">protected</property> + <property name="width">10</property> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxALIGN_CENTER_HORIZONTAL</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">If you don't save, all your changes will be permanently lost.</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticText2</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">0</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerLower</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxTOP|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticLine" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticline</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style">wxLI_HORIZONTAL</property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALIGN_RIGHT</property> + <property name="proportion">0</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerButtons</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxALIGN_CENTER_VERTICAL</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Save and Exit</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_buttonSaveAndExit</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnButtonClick">OnSaveAndExit</event> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxALIGN_CENTER_VERTICAL</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default">0</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Exit without Save</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_buttonExitNoSave</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnButtonClick">OnExitNoSave</event> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxALIGN_CENTER_VERTICAL</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default">0</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_CANCEL</property> + <property name="label">Cancel</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_buttonCancel</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnButtonClick">OnCancel</event> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + </object> + </object> + </object> + </object> + </object> +</wxFormBuilder_Project> diff --git a/common/dialogs/dialog_exit_base.h b/common/dialogs/dialog_exit_base.h new file mode 100644 index 0000000..7330435 --- /dev/null +++ b/common/dialogs/dialog_exit_base.h @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Oct 8 2012) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __DIALOG_EXIT_BASE_H__ +#define __DIALOG_EXIT_BASE_H__ + +#include <wx/artprov.h> +#include <wx/xrc/xmlres.h> +#include <wx/intl.h> +class DIALOG_SHIM; + +#include "dialog_shim.h" +#include <wx/bitmap.h> +#include <wx/image.h> +#include <wx/icon.h> +#include <wx/statbmp.h> +#include <wx/gdicmn.h> +#include <wx/font.h> +#include <wx/colour.h> +#include <wx/settings.h> +#include <wx/string.h> +#include <wx/sizer.h> +#include <wx/stattext.h> +#include <wx/statline.h> +#include <wx/button.h> +#include <wx/dialog.h> + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class DIALOG_EXIT_BASE +/////////////////////////////////////////////////////////////////////////////// +class DIALOG_EXIT_BASE : public DIALOG_SHIM +{ + private: + + protected: + wxStaticBitmap* m_bitmap; + wxStaticText* m_TextInfo; + wxStaticText* m_staticText2; + wxStaticLine* m_staticline; + wxButton* m_buttonSaveAndExit; + wxButton* m_buttonExitNoSave; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnSaveAndExit( wxCommandEvent& event ) { event.Skip(); } + virtual void OnExitNoSave( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } + + + public: + + DIALOG_EXIT_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~DIALOG_EXIT_BASE(); + +}; + +#endif //__DIALOG_EXIT_BASE_H__ diff --git a/common/dialogs/dialog_get_component.cpp b/common/dialogs/dialog_get_component.cpp new file mode 100644 index 0000000..c0516bb --- /dev/null +++ b/common/dialogs/dialog_get_component.cpp @@ -0,0 +1,144 @@ +/*********************************/ +/* dialog_get_component.cpp */ +/*********************************/ + +#include <fctsys.h> +#include <common.h> +#include <macros.h> +#include <draw_frame.h> +#include <dialog_get_component.h> + + +/****************************************************************************/ +/* Show a dialog frame to choose a name from an history list, or a new name */ +/* to select a component or a module */ +/****************************************************************************/ + +static unsigned s_HistoryMaxCount = 8; // Max number of items displayed in history list + + +/* + * Dialog frame to choose a component or a footprint + * This dialog shows an history of last selected items + */ +DIALOG_GET_COMPONENT::DIALOG_GET_COMPONENT( EDA_DRAW_FRAME* parent, + wxArrayString& HistoryList, + const wxString& Title, + bool show_extra_tool ) : + DIALOG_GET_COMPONENT_BASE( parent, -1, Title ) +{ + +#ifdef __WXMAC__ + m_auxToolSelector = false; +#else + m_auxToolSelector = show_extra_tool; +#endif + initDialog( HistoryList ); + + m_textCmpNameCtrl->SetFocus(); + GetSizer()->Fit( this ); + GetSizer()->SetSizeHints( this ); +} + +void DIALOG_GET_COMPONENT::initDialog( wxArrayString& aHistoryList ) +{ + SetFocus(); + m_GetExtraFunction = false; + m_selectionIsKeyword = false; + m_historyList->Append( aHistoryList ); + if( !m_auxToolSelector ) + { + m_buttonBrowse->Show( false ); + m_buttonBrowse->Enable( false ); + } +} + + +void DIALOG_GET_COMPONENT::OnCancel( wxCommandEvent& event ) +{ + m_Text = wxEmptyString; + EndModal( wxID_CANCEL ); +} + +void DIALOG_GET_COMPONENT::Accept( wxCommandEvent& event ) +{ + m_selectionIsKeyword = false; + switch( event.GetId() ) + { + case ID_SEL_BY_LISTBOX: + m_Text = m_historyList->GetStringSelection(); + break; + + case wxID_OK: + m_Text = m_textCmpNameCtrl->GetValue(); + break; + + case ID_ACCEPT_KEYWORD: + m_selectionIsKeyword = true; + m_Text = m_textCmpNameCtrl->GetValue(); + break; + + case ID_LIST_ALL: + m_Text = wxT( "*" ); + break; + } + + m_Text.Trim( false ); // Remove blanks at beginning + m_Text.Trim( true ); // Remove blanks at end + + EndModal( wxID_OK ); +} + + +/* Get the component name by the extra function */ +void DIALOG_GET_COMPONENT::GetExtraSelection( wxCommandEvent& event ) +{ + m_GetExtraFunction = true; + EndModal( wxID_OK ); +} + + +// Return the component name selected by the dialog +wxString DIALOG_GET_COMPONENT::GetComponentName( void ) +{ + return m_Text; +} + + +/* Initialize the default component name default choice +*/ +void DIALOG_GET_COMPONENT::SetComponentName( const wxString& name ) +{ + if( m_textCmpNameCtrl ) + { + m_textCmpNameCtrl->SetValue( name ); + m_textCmpNameCtrl->SetSelection(-1, -1); + } +} + + +/* + * Add the string "aName" to the history list aHistoryList + */ +void AddHistoryComponentName( wxArrayString& aHistoryList, const wxString& aName ) +{ + if( ( aHistoryList.GetCount() > 0 ) && ( aName == aHistoryList[0] ) ) + return; + + /* remove an old identical name if exists */ + for( unsigned ii = 1; ii < aHistoryList.GetCount(); ii++ ) + { + if( aName == aHistoryList[ii] ) + { + aHistoryList.RemoveAt( ii ); + ii--; + } + } + + // Add the new name at the beginning of the history list + aHistoryList.Insert(aName, 0); + + // Remove extra names + while( aHistoryList.GetCount() >= s_HistoryMaxCount ) + aHistoryList.RemoveAt( aHistoryList.GetCount()-1 ); +} diff --git a/common/dialogs/dialog_get_component_base.cpp b/common/dialogs/dialog_get_component_base.cpp new file mode 100644 index 0000000..8e8810c --- /dev/null +++ b/common/dialogs/dialog_get_component_base.cpp @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Nov 5 2013) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "dialog_get_component_base.h" + +/////////////////////////////////////////////////////////////////////////// + +DIALOG_GET_COMPONENT_BASE::DIALOG_GET_COMPONENT_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( wxDefaultSize, wxDefaultSize ); + + wxBoxSizer* bSizerMain; + bSizerMain = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizerLeft; + bSizerLeft = new wxBoxSizer( wxVERTICAL ); + + m_staticTextName = new wxStaticText( this, wxID_ANY, _("Name:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextName->Wrap( -1 ); + bSizerLeft->Add( m_staticTextName, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); + + m_textCmpNameCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_textCmpNameCtrl->SetMaxLength( 0 ); + bSizerLeft->Add( m_textCmpNameCtrl, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + m_staticTextHistory = new wxStaticText( this, wxID_ANY, _("History list:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextHistory->Wrap( -1 ); + bSizerLeft->Add( m_staticTextHistory, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); + + m_historyList = new wxListBox( this, ID_SEL_BY_LISTBOX, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + m_historyList->SetMinSize( wxSize( 200,100 ) ); + + bSizerLeft->Add( m_historyList, 1, wxALL|wxEXPAND, 5 ); + + + bSizerMain->Add( bSizerLeft, 1, wxEXPAND, 5 ); + + wxBoxSizer* bSizerRight; + bSizerRight = new wxBoxSizer( wxVERTICAL ); + + m_buttonOK = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonOK->SetDefault(); + bSizerRight->Add( m_buttonOK, 0, wxALL|wxEXPAND, 5 ); + + m_buttonKW = new wxButton( this, ID_ACCEPT_KEYWORD, _("Search by Keyword"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizerRight->Add( m_buttonKW, 0, wxALL|wxEXPAND, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizerRight->Add( m_buttonCancel, 0, wxALL|wxEXPAND, 5 ); + + m_buttonList = new wxButton( this, ID_LIST_ALL, _("List All"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizerRight->Add( m_buttonList, 0, wxALL|wxEXPAND, 5 ); + + m_buttonBrowse = new wxButton( this, ID_EXTRA_TOOL, _("Select by Browser"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizerRight->Add( m_buttonBrowse, 0, wxALL|wxEXPAND, 5 ); + + + bSizerMain->Add( bSizerRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + this->SetSizer( bSizerMain ); + this->Layout(); + bSizerMain->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + m_historyList->Connect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( DIALOG_GET_COMPONENT_BASE::Accept ), NULL, this ); + m_buttonOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GET_COMPONENT_BASE::Accept ), NULL, this ); + m_buttonKW->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GET_COMPONENT_BASE::Accept ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GET_COMPONENT_BASE::OnCancel ), NULL, this ); + m_buttonList->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GET_COMPONENT_BASE::Accept ), NULL, this ); + m_buttonBrowse->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GET_COMPONENT_BASE::GetExtraSelection ), NULL, this ); +} + +DIALOG_GET_COMPONENT_BASE::~DIALOG_GET_COMPONENT_BASE() +{ + // Disconnect Events + m_historyList->Disconnect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( DIALOG_GET_COMPONENT_BASE::Accept ), NULL, this ); + m_buttonOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GET_COMPONENT_BASE::Accept ), NULL, this ); + m_buttonKW->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GET_COMPONENT_BASE::Accept ), NULL, this ); + m_buttonCancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GET_COMPONENT_BASE::OnCancel ), NULL, this ); + m_buttonList->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GET_COMPONENT_BASE::Accept ), NULL, this ); + m_buttonBrowse->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GET_COMPONENT_BASE::GetExtraSelection ), NULL, this ); + +} diff --git a/common/dialogs/dialog_get_component_base.fbp b/common/dialogs/dialog_get_component_base.fbp new file mode 100644 index 0000000..10a950c --- /dev/null +++ b/common/dialogs/dialog_get_component_base.fbp @@ -0,0 +1,906 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<wxFormBuilder_Project> + <FileVersion major="1" minor="11" /> + <object class="Project" expanded="1"> + <property name="class_decoration"></property> + <property name="code_generation">C++</property> + <property name="disconnect_events">1</property> + <property name="disconnect_mode">source_name</property> + <property name="disconnect_php_events">0</property> + <property name="disconnect_python_events">0</property> + <property name="embedded_files_path">res</property> + <property name="encoding">UTF-8</property> + <property name="event_generation">connect</property> + <property name="file">dialog_get_component_base</property> + <property name="first_id">1000</property> + <property name="help_provider">none</property> + <property name="internationalize">1</property> + <property name="name">dialog_get_component_base</property> + <property name="namespace"></property> + <property name="path">.</property> + <property name="precompiled_header"></property> + <property name="relative_path">1</property> + <property name="skip_lua_events">1</property> + <property name="skip_php_events">1</property> + <property name="skip_python_events">1</property> + <property name="ui_table">UI</property> + <property name="use_enum">0</property> + <property name="use_microsoft_bom">0</property> + <object class="Dialog" expanded="1"> + <property name="aui_managed">0</property> + <property name="aui_manager_style">wxAUI_MGR_DEFAULT</property> + <property name="bg"></property> + <property name="center">wxBOTH</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="enabled">1</property> + <property name="event_handler">impl_virtual</property> + <property name="extra_style"></property> + <property name="fg"></property> + <property name="font"></property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="maximum_size"></property> + <property name="minimum_size"></property> + <property name="name">DIALOG_GET_COMPONENT_BASE</property> + <property name="pos"></property> + <property name="size">-1,-1</property> + <property name="style">wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER</property> + <property name="subclass">DIALOG_SHIM; dialog_shim.h</property> + <property name="title"></property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnActivate"></event> + <event name="OnActivateApp"></event> + <event name="OnAuiFindManager"></event> + <event name="OnAuiPaneButton"></event> + <event name="OnAuiPaneClose"></event> + <event name="OnAuiPaneMaximize"></event> + <event name="OnAuiPaneRestore"></event> + <event name="OnAuiRender"></event> + <event name="OnChar"></event> + <event name="OnClose"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnHibernate"></event> + <event name="OnIconize"></event> + <event name="OnIdle"></event> + <event name="OnInitDialog"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerMain</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerLeft</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxTOP|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Name:</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextName</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxTextCtrl" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="maxlength">0</property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_textCmpNameCtrl</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="value"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnText"></event> + <event name="OnTextEnter"></event> + <event name="OnTextMaxLen"></event> + <event name="OnTextURL"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxTOP|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">History list:</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextHistory</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxListBox" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="choices"></property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">ID_SEL_BY_LISTBOX</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size">200,100</property> + <property name="moveable">1</property> + <property name="name">m_historyList</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnListBox">Accept</event> + <event name="OnListBoxDClick"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL</property> + <property name="proportion">0</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerRight</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxEXPAND</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_OK</property> + <property name="label">OK</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_buttonOK</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnButtonClick">Accept</event> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxEXPAND</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default">0</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">ID_ACCEPT_KEYWORD</property> + <property name="label">Search by Keyword</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_buttonKW</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnButtonClick">Accept</event> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxEXPAND</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default">0</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_CANCEL</property> + <property name="label">Cancel</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_buttonCancel</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnButtonClick">OnCancel</event> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxEXPAND</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default">0</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">ID_LIST_ALL</property> + <property name="label">List All</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_buttonList</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnButtonClick">Accept</event> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxEXPAND</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default">0</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">ID_EXTRA_TOOL</property> + <property name="label">Select by Browser</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_buttonBrowse</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnButtonClick">GetExtraSelection</event> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + </object> + </object> + </object> +</wxFormBuilder_Project> diff --git a/common/dialogs/dialog_get_component_base.h b/common/dialogs/dialog_get_component_base.h new file mode 100644 index 0000000..a0a39d5 --- /dev/null +++ b/common/dialogs/dialog_get_component_base.h @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Nov 5 2013) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __DIALOG_GET_COMPONENT_BASE_H__ +#define __DIALOG_GET_COMPONENT_BASE_H__ + +#include <wx/artprov.h> +#include <wx/xrc/xmlres.h> +#include <wx/intl.h> +class DIALOG_SHIM; + +#include "dialog_shim.h" +#include <wx/string.h> +#include <wx/stattext.h> +#include <wx/gdicmn.h> +#include <wx/font.h> +#include <wx/colour.h> +#include <wx/settings.h> +#include <wx/textctrl.h> +#include <wx/listbox.h> +#include <wx/sizer.h> +#include <wx/button.h> +#include <wx/dialog.h> + +/////////////////////////////////////////////////////////////////////////// + +#define ID_SEL_BY_LISTBOX 1000 +#define ID_ACCEPT_KEYWORD 1001 +#define ID_LIST_ALL 1002 +#define ID_EXTRA_TOOL 1003 + +/////////////////////////////////////////////////////////////////////////////// +/// Class DIALOG_GET_COMPONENT_BASE +/////////////////////////////////////////////////////////////////////////////// +class DIALOG_GET_COMPONENT_BASE : public DIALOG_SHIM +{ + private: + + protected: + wxStaticText* m_staticTextName; + wxTextCtrl* m_textCmpNameCtrl; + wxStaticText* m_staticTextHistory; + wxListBox* m_historyList; + wxButton* m_buttonOK; + wxButton* m_buttonKW; + wxButton* m_buttonCancel; + wxButton* m_buttonList; + wxButton* m_buttonBrowse; + + // Virtual event handlers, overide them in your derived class + virtual void Accept( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } + virtual void GetExtraSelection( wxCommandEvent& event ) { event.Skip(); } + + + public: + + DIALOG_GET_COMPONENT_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~DIALOG_GET_COMPONENT_BASE(); + +}; + +#endif //__DIALOG_GET_COMPONENT_BASE_H__ diff --git a/common/dialogs/dialog_hotkeys_editor.cpp b/common/dialogs/dialog_hotkeys_editor.cpp new file mode 100644 index 0000000..0dfedf2 --- /dev/null +++ b/common/dialogs/dialog_hotkeys_editor.cpp @@ -0,0 +1,389 @@ +/** + * @file dialog_hotkeys_editor.cpp + */ + +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 1992-2014 Kicad Developers, see CHANGELOG.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 <algorithm> + +#include <fctsys.h> +#include <pgm_base.h> +#include <common.h> +#include <confirm.h> + +#include <dialog_hotkeys_editor.h> + + +HOTKEY_LIST_CTRL::HOTKEY_LIST_CTRL( wxWindow *aParent, struct EDA_HOTKEY_CONFIG* aSection ) : + wxListCtrl( aParent, wxID_ANY, wxDefaultPosition, + wxDefaultSize, wxLC_HRULES|wxLC_REPORT|wxLC_SINGLE_SEL|wxLC_VIRTUAL ) +{ + m_sectionTag = aSection->m_SectionTag; + m_curEditingRow = -1; + + InsertColumn( 0, _( "Command" ) ); + InsertColumn( 1, _( "Hotkey" ) ); + + // Add a dummy hotkey_spec which is a header before each hotkey list + EDA_HOTKEY** hotkey_descr_list; + + // Add a copy of hotkeys to our list + for( hotkey_descr_list = aSection->m_HK_InfoList; *hotkey_descr_list; hotkey_descr_list++ ) + { + EDA_HOTKEY* hotkey_descr = *hotkey_descr_list; + m_hotkeys.push_back( new EDA_HOTKEY( hotkey_descr ) ); + } + + // Set item count to hotkey size, this gets it to autoload the entries + SetItemCount( m_hotkeys.size() ); + + SetColumnWidth( 0, wxLIST_AUTOSIZE ); + SetColumnWidth( 1, wxLIST_AUTOSIZE ); + + Bind( wxEVT_CHAR, &HOTKEY_LIST_CTRL::OnChar, this ); + Bind( wxEVT_LIST_ITEM_SELECTED, &HOTKEY_LIST_CTRL::OnListItemSelected, this ); + Bind( wxEVT_SIZE, &HOTKEY_LIST_CTRL::OnSize, this ); +} + + +void HOTKEY_LIST_CTRL::OnSize( wxSizeEvent& aEvent ) +{ + recalculateColumns(); + aEvent.Skip(); +} + + +void HOTKEY_LIST_CTRL::recalculateColumns() +{ + float totalLength = 0; + float scale = 0; + + // Find max character length of first column + int maxInfoMsgLength = 0; + + for( int i = 0; i < GetItemCount(); i++ ) + { + int length = GetItemText( i, 0 ).Length(); + + if( length > maxInfoMsgLength ) + maxInfoMsgLength = length; + } + + // Find max character length of second column + int maxKeyCodeLength = 0; + + for( int i = 0; i < GetItemCount(); i++ ) + { + int length = GetItemText( i, 1 ).Length(); + if( length > maxKeyCodeLength ) + maxKeyCodeLength = length; + } + + // Use the lengths of column texts to create a scale of the max list width + // to set the column widths + totalLength = maxInfoMsgLength + maxKeyCodeLength; + + scale = (double) GetClientSize().x / totalLength; + + SetColumnWidth( 0, int( maxInfoMsgLength*scale ) - 2 ); + SetColumnWidth( 1, int( maxKeyCodeLength*scale ) ); +} + + +void HOTKEY_LIST_CTRL::OnListItemSelected( wxListEvent& aEvent ) +{ + m_curEditingRow = aEvent.GetIndex(); +} + + +void HOTKEY_LIST_CTRL::DeselectRow( int aRow ) +{ + SetItemState( aRow, 0, wxLIST_STATE_SELECTED ); +} + + +wxString HOTKEY_LIST_CTRL::OnGetItemText( long aRow, long aColumn ) const +{ + EDA_HOTKEY* hotkey_descr = m_hotkeys[aRow]; + + if( aColumn == 0 ) + { + return wxGetTranslation( hotkey_descr->m_InfoMsg ); + } + else + { + return KeyNameFromKeyCode( hotkey_descr->m_KeyCode ); + } +} + + +void HOTKEY_LIST_CTRL::OnChar( wxKeyEvent& aEvent ) +{ + if( m_curEditingRow != -1 ) + { + long key = aEvent.GetKeyCode(); + + switch( key ) + { + case WXK_ESCAPE: + // Remove selection + DeselectRow( m_curEditingRow ); + m_curEditingRow = -1; + break; + + default: + if( key >= 'a' && key <= 'z' ) // convert to uppercase + key = key + ('A' - 'a'); + + // Remap Ctrl A (=1+GR_KB_CTRL) to Ctrl Z(=26+GR_KB_CTRL) + // to GR_KB_CTRL+'A' .. GR_KB_CTRL+'Z' + if( aEvent.ControlDown() && key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z ) + key += 'A' - 1; + + /* Disallow shift for keys that have two keycodes on them (e.g. number and + * punctuation keys) leaving only the "letter keys" of A-Z. + * Then, you can have, e.g. Ctrl-5 and Ctrl-% (GB layout) + * and Ctrl-( and Ctrl-5 (FR layout). + * Otherwise, you'd have to have to say Ctrl-Shift-5 on a FR layout + */ + bool keyIsLetter = key >= 'A' && key <= 'Z'; + + if( aEvent.ShiftDown() && ( keyIsLetter || key > 256 ) ) + key |= GR_KB_SHIFT; + + if( aEvent.ControlDown() ) + key |= GR_KB_CTRL; + + if( aEvent.AltDown() ) + key |= GR_KB_ALT; + + // See if this key code is handled in hotkeys names list + bool exists; + KeyNameFromKeyCode( key, &exists ); + + if( exists && m_hotkeys[m_curEditingRow]->m_KeyCode != key ) + { + bool canUpdate = ((HOTKEY_SECTION_PAGE *)m_parent)->GetDialog()->CanSetKey( key, m_sectionTag ); + + if( canUpdate ) + { + m_hotkeys[m_curEditingRow]->m_KeyCode = key; + recalculateColumns(); + } + + // Remove selection + DeselectRow( m_curEditingRow ); + m_curEditingRow = -1; + } + } + } + RefreshItems(0,m_hotkeys.size()-1); +} + + +void HOTKEY_LIST_CTRL::RestoreFrom( struct EDA_HOTKEY_CONFIG* aSection ) +{ + int row = 0; + + EDA_HOTKEY** info_ptr; + + for( info_ptr = aSection->m_HK_InfoList; *info_ptr; info_ptr++ ) + { + EDA_HOTKEY* info = *info_ptr; + m_hotkeys[row++]->m_KeyCode = info->m_KeyCode; + } + + // Remove selection + DeselectRow( m_curEditingRow ); + m_curEditingRow = -1; + + RefreshItems( 0, m_hotkeys.size()-1 ); +} + + +HOTKEY_SECTION_PAGE::HOTKEY_SECTION_PAGE( HOTKEYS_EDITOR_DIALOG* aDialog, + wxNotebook* aParent, + const wxString& aTitle, + EDA_HOTKEY_CONFIG* aSection ) : + wxPanel( aParent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxNO_BORDER ), + m_hotkeySection( aSection ), + m_dialog( aDialog ) +{ + aParent->AddPage( this, aTitle ); + + wxBoxSizer* bMainSizer = new wxBoxSizer( wxVERTICAL ); + + SetSizer( bMainSizer ); + Layout(); + bMainSizer->Fit( this ); + + m_hotkeyList = new HOTKEY_LIST_CTRL( this, aSection ); + bMainSizer->Add( m_hotkeyList, 1, wxALL|wxEXPAND, 5 ); +} + + +void HOTKEY_SECTION_PAGE::Restore() +{ + m_hotkeyList->RestoreFrom( m_hotkeySection ); + + Update(); +} + + +void InstallHotkeyFrame( EDA_BASE_FRAME* aParent, EDA_HOTKEY_CONFIG* aHotkeys ) +{ + HOTKEYS_EDITOR_DIALOG dialog( aParent, aHotkeys ); + + int diag = dialog.ShowModal(); + if( diag == wxID_OK ) + { + aParent->ReCreateMenuBar(); + aParent->Refresh(); + } +} + + +HOTKEYS_EDITOR_DIALOG::HOTKEYS_EDITOR_DIALOG( EDA_BASE_FRAME* aParent, + EDA_HOTKEY_CONFIG* aHotkeys ) : + HOTKEYS_EDITOR_DIALOG_BASE( aParent ), + m_parent( aParent ), + m_hotkeys( aHotkeys ) +{ + EDA_HOTKEY_CONFIG* section; + + for( section = m_hotkeys; section->m_HK_InfoList; section++ ) + { + m_hotkeySectionPages.push_back( new HOTKEY_SECTION_PAGE( this, m_hotkeySections, + wxGetTranslation( *section->m_Title ), + section ) ); + } + + m_sdbSizerOK->SetDefault(); + Center(); +} + + +void HOTKEYS_EDITOR_DIALOG::OnOKClicked( wxCommandEvent& event ) +{ + std::vector<HOTKEY_SECTION_PAGE*>::iterator i; + + for( i = m_hotkeySectionPages.begin(); i != m_hotkeySectionPages.end(); ++i ) + { + std::vector<EDA_HOTKEY*>& hotkey_vec = (*i)->GetHotkeys(); + EDA_HOTKEY_CONFIG* section = (*i)->GetHotkeySection(); + + EDA_HOTKEY** info_ptr; + + for( info_ptr = section->m_HK_InfoList; *info_ptr; info_ptr++ ) + { + EDA_HOTKEY* info = *info_ptr; + + /* find the corresponding hotkey */ + std::vector<EDA_HOTKEY*>::iterator j; + + for( j = hotkey_vec.begin(); j != hotkey_vec.end(); ++j ) + { + if( (*j) && (*j)->m_Idcommand == info->m_Idcommand ) + { + info->m_KeyCode = (*j)->m_KeyCode; + break; + } + } + } + } + + /* save the hotkeys */ + m_parent->WriteHotkeyConfig( m_hotkeys ); + + EndModal( wxID_OK ); +} + + + +void HOTKEYS_EDITOR_DIALOG::UndoClicked( wxCommandEvent& aEvent ) +{ + std::vector<HOTKEY_SECTION_PAGE*>::iterator i; + + for( i = m_hotkeySectionPages.begin(); i != m_hotkeySectionPages.end(); ++i ) + { + (*i)->Restore(); + } +} + + +bool HOTKEYS_EDITOR_DIALOG::CanSetKey( long aKey, const wxString* sectionTag ) +{ + std::vector<HOTKEY_SECTION_PAGE*>::iterator i; + + EDA_HOTKEY* conflictingKey = NULL; + HOTKEY_SECTION_PAGE* conflictingSection = NULL; + + for( i = m_hotkeySectionPages.begin(); i != m_hotkeySectionPages.end(); ++i ) + { + // Any non Common section can only conflict with itself and Common + if( *sectionTag != g_CommonSectionTag + && *((*i)->GetHotkeySection()->m_SectionTag) != g_CommonSectionTag + && *((*i)->GetHotkeySection()->m_SectionTag) != *sectionTag ) + continue; + + std::vector<EDA_HOTKEY*>& hotkey_vec = (*i)->GetHotkeys(); + /* find the corresponding hotkey */ + std::vector<EDA_HOTKEY*>::iterator j; + + for( j = hotkey_vec.begin(); j != hotkey_vec.end(); ++j ) + { + if( aKey == (*j)->m_KeyCode ) + { + conflictingKey = (*j); + conflictingSection = (*i); + + break; + } + } + } + + if( conflictingKey != NULL ) + { + wxString info = wxGetTranslation( conflictingKey->m_InfoMsg ); + wxString msg = wxString::Format( + _( "<%s> is already assigned to \"%s\" in section \"%s\". Are you sure you want " + "to change its assignment?" ), + KeyNameFromKeyCode( aKey ), GetChars( info ), + *(conflictingSection->GetHotkeySection()->m_Title) ); + + wxMessageDialog dlg( this, msg, _( "Confirm change" ), wxYES_NO | wxNO_DEFAULT ); + + if( dlg.ShowModal() == wxID_YES ) + { + conflictingKey->m_KeyCode = 0; + return true; + } + else + { + return false; + } + } + + return true; +} diff --git a/common/dialogs/dialog_hotkeys_editor_base.cpp b/common/dialogs/dialog_hotkeys_editor_base.cpp new file mode 100644 index 0000000..ef2f7c9 --- /dev/null +++ b/common/dialogs/dialog_hotkeys_editor_base.cpp @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 17 2015) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "dialog_hotkeys_editor_base.h" + +/////////////////////////////////////////////////////////////////////////// + +HOTKEYS_EDITOR_DIALOG_BASE::HOTKEYS_EDITOR_DIALOG_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( wxDefaultSize, wxDefaultSize ); + + wxBoxSizer* bMainSizer; + bMainSizer = new wxBoxSizer( wxVERTICAL ); + + m_staticText1 = new wxStaticText( this, wxID_ANY, _("Select a row and press a new key combination to alter the binding."), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText1->Wrap( 400 ); + bMainSizer->Add( m_staticText1, 0, wxALL|wxEXPAND, 5 ); + + m_hotkeySections = new wxNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + + bMainSizer->Add( m_hotkeySections, 1, wxEXPAND | wxALL, 5 ); + + wxBoxSizer* b_buttonsSizer; + b_buttonsSizer = new wxBoxSizer( wxHORIZONTAL ); + + m_sdbSizer = new wxStdDialogButtonSizer(); + m_sdbSizerOK = new wxButton( this, wxID_OK ); + m_sdbSizer->AddButton( m_sdbSizerOK ); + m_sdbSizerCancel = new wxButton( this, wxID_CANCEL ); + m_sdbSizer->AddButton( m_sdbSizerCancel ); + m_sdbSizer->Realize(); + + b_buttonsSizer->Add( m_sdbSizer, 0, wxEXPAND|wxTOP|wxBOTTOM, 5 ); + + m_undoButton = new wxButton( this, wxID_UNDO, _("Undo"), wxDefaultPosition, wxDefaultSize, 0 ); + b_buttonsSizer->Add( m_undoButton, 0, wxALL|wxEXPAND, 5 ); + + + bMainSizer->Add( b_buttonsSizer, 0, wxALIGN_RIGHT, 5 ); + + + this->SetSizer( bMainSizer ); + this->Layout(); + + // Connect Events + m_sdbSizerOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( HOTKEYS_EDITOR_DIALOG_BASE::OnOKClicked ), NULL, this ); + m_undoButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( HOTKEYS_EDITOR_DIALOG_BASE::UndoClicked ), NULL, this ); +} + +HOTKEYS_EDITOR_DIALOG_BASE::~HOTKEYS_EDITOR_DIALOG_BASE() +{ + // Disconnect Events + m_sdbSizerOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( HOTKEYS_EDITOR_DIALOG_BASE::OnOKClicked ), NULL, this ); + m_undoButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( HOTKEYS_EDITOR_DIALOG_BASE::UndoClicked ), NULL, this ); + +} diff --git a/common/dialogs/dialog_hotkeys_editor_base.fbp b/common/dialogs/dialog_hotkeys_editor_base.fbp new file mode 100644 index 0000000..7b12ee4 --- /dev/null +++ b/common/dialogs/dialog_hotkeys_editor_base.fbp @@ -0,0 +1,391 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<wxFormBuilder_Project> + <FileVersion major="1" minor="13" /> + <object class="Project" expanded="1"> + <property name="class_decoration"></property> + <property name="code_generation">C++</property> + <property name="disconnect_events">1</property> + <property name="disconnect_mode">source_name</property> + <property name="disconnect_php_events">0</property> + <property name="disconnect_python_events">0</property> + <property name="embedded_files_path">res</property> + <property name="encoding">UTF-8</property> + <property name="event_generation">connect</property> + <property name="file">dialog_hotkeys_editor_base</property> + <property name="first_id">1000</property> + <property name="help_provider">none</property> + <property name="internationalize">1</property> + <property name="name">dialog_hotkeys_editor_base</property> + <property name="namespace"></property> + <property name="path">.</property> + <property name="precompiled_header"></property> + <property name="relative_path">1</property> + <property name="skip_lua_events">1</property> + <property name="skip_php_events">1</property> + <property name="skip_python_events">1</property> + <property name="ui_table">UI</property> + <property name="use_enum">0</property> + <property name="use_microsoft_bom">0</property> + <object class="Dialog" expanded="1"> + <property name="aui_managed">0</property> + <property name="aui_manager_style">wxAUI_MGR_DEFAULT</property> + <property name="bg"></property> + <property name="center"></property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="enabled">1</property> + <property name="event_handler">impl_virtual</property> + <property name="extra_style"></property> + <property name="fg"></property> + <property name="font"></property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="maximum_size"></property> + <property name="minimum_size"></property> + <property name="name">HOTKEYS_EDITOR_DIALOG_BASE</property> + <property name="pos"></property> + <property name="size">450,500</property> + <property name="style">wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER</property> + <property name="subclass">DIALOG_SHIM; dialog_shim.h</property> + <property name="title">Hotkeys Editor</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnActivate"></event> + <event name="OnActivateApp"></event> + <event name="OnAuiFindManager"></event> + <event name="OnAuiPaneButton"></event> + <event name="OnAuiPaneClose"></event> + <event name="OnAuiPaneMaximize"></event> + <event name="OnAuiPaneRestore"></event> + <event name="OnAuiRender"></event> + <event name="OnChar"></event> + <event name="OnClose"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnHibernate"></event> + <event name="OnIconize"></event> + <event name="OnIdle"></event> + <event name="OnInitDialog"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bMainSizer</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxEXPAND</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Select a row and press a new key combination to alter the binding.</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticText1</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">400</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND | wxALL</property> + <property name="proportion">1</property> + <object class="wxNotebook" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="bitmapsize"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_hotkeySections</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnNotebookPageChanged"></event> + <event name="OnNotebookPageChanging"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALIGN_RIGHT</property> + <property name="proportion">0</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">b_buttonsSizer</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxTOP|wxBOTTOM</property> + <property name="proportion">0</property> + <object class="wxStdDialogButtonSizer" expanded="1"> + <property name="Apply">0</property> + <property name="Cancel">1</property> + <property name="ContextHelp">0</property> + <property name="Help">0</property> + <property name="No">0</property> + <property name="OK">1</property> + <property name="Save">0</property> + <property name="Yes">0</property> + <property name="minimum_size"></property> + <property name="name">m_sdbSizer</property> + <property name="permission">protected</property> + <event name="OnApplyButtonClick"></event> + <event name="OnCancelButtonClick"></event> + <event name="OnContextHelpButtonClick"></event> + <event name="OnHelpButtonClick"></event> + <event name="OnNoButtonClick"></event> + <event name="OnOKButtonClick">OnOKClicked</event> + <event name="OnSaveButtonClick"></event> + <event name="OnYesButtonClick"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALL|wxEXPAND</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default">0</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_UNDO</property> + <property name="label">Undo</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_undoButton</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnButtonClick">UndoClicked</event> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + </object> + </object> + </object> +</wxFormBuilder_Project> diff --git a/common/dialogs/dialog_hotkeys_editor_base.h b/common/dialogs/dialog_hotkeys_editor_base.h new file mode 100644 index 0000000..f0d4d9e --- /dev/null +++ b/common/dialogs/dialog_hotkeys_editor_base.h @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 17 2015) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __DIALOG_HOTKEYS_EDITOR_BASE_H__ +#define __DIALOG_HOTKEYS_EDITOR_BASE_H__ + +#include <wx/artprov.h> +#include <wx/xrc/xmlres.h> +#include <wx/intl.h> +class DIALOG_SHIM; + +#include "dialog_shim.h" +#include <wx/string.h> +#include <wx/stattext.h> +#include <wx/gdicmn.h> +#include <wx/font.h> +#include <wx/colour.h> +#include <wx/settings.h> +#include <wx/notebook.h> +#include <wx/sizer.h> +#include <wx/button.h> +#include <wx/dialog.h> + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class HOTKEYS_EDITOR_DIALOG_BASE +/////////////////////////////////////////////////////////////////////////////// +class HOTKEYS_EDITOR_DIALOG_BASE : public DIALOG_SHIM +{ + private: + + protected: + wxStaticText* m_staticText1; + wxNotebook* m_hotkeySections; + wxStdDialogButtonSizer* m_sdbSizer; + wxButton* m_sdbSizerOK; + wxButton* m_sdbSizerCancel; + wxButton* m_undoButton; + + // Virtual event handlers, overide them in your derived class + virtual void OnOKClicked( wxCommandEvent& event ) { event.Skip(); } + virtual void UndoClicked( wxCommandEvent& event ) { event.Skip(); } + + + public: + + HOTKEYS_EDITOR_DIALOG_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Hotkeys Editor"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 450,500 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~HOTKEYS_EDITOR_DIALOG_BASE(); + +}; + +#endif //__DIALOG_HOTKEYS_EDITOR_BASE_H__ diff --git a/common/dialogs/dialog_image_editor.cpp b/common/dialogs/dialog_image_editor.cpp new file mode 100644 index 0000000..9d0edd2 --- /dev/null +++ b/common/dialogs/dialog_image_editor.cpp @@ -0,0 +1,179 @@ +/** + * @file dialog_image_editor.cpp + */ + +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2011 jean-pierre.charras + * Copyright (C) 2011 Kicad Developers, see change_log.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 <fctsys.h> +#include <gr_basic.h> +#include <common.h> +#include <class_bitmap_base.h> + +#include <dialog_image_editor.h> + +DIALOG_IMAGE_EDITOR::DIALOG_IMAGE_EDITOR( wxWindow* aParent, BITMAP_BASE* aItem ) + : DIALOG_IMAGE_EDITOR_BASE( aParent ) +{ + m_workingImage = new BITMAP_BASE( * aItem ); + m_lastImage = NULL; + m_buttonUndoLast->Enable( false ); + wxString msg; + msg.Printf( wxT("%f"), m_workingImage->m_Scale ); + m_textCtrlScale->SetValue( msg ); + + GetSizer()->SetSizeHints( this ); + Layout(); + Fit(); + SetMinSize( GetBestSize() ); + + Centre(); + SetFocus(); +} + +void DIALOG_IMAGE_EDITOR::OnUndoLastChange( wxCommandEvent& event ) +{ + BITMAP_BASE * tmp = m_workingImage; + m_workingImage = m_lastImage; + delete tmp; + m_buttonUndoLast->Enable( false ); + m_lastImage = NULL; + m_panelDraw->Refresh(); +} + +void DIALOG_IMAGE_EDITOR::OnMirrorX_click( wxCommandEvent& event ) +{ + delete m_lastImage; + m_lastImage = new BITMAP_BASE( * m_workingImage ); + m_buttonUndoLast->Enable( true ); + m_buttonUndoLast->Enable( true ); + m_workingImage->Mirror( true ); + m_panelDraw->Refresh(); +} + +void DIALOG_IMAGE_EDITOR::OnMirrorY_click( wxCommandEvent& event ) +{ + delete m_lastImage; + m_lastImage = new BITMAP_BASE( * m_workingImage ); + m_buttonUndoLast->Enable( true ); + m_workingImage->Mirror( false ); + m_panelDraw->Refresh(); +} + +void DIALOG_IMAGE_EDITOR::OnRotateClick( wxCommandEvent& event ) +{ + delete m_lastImage; + m_lastImage = new BITMAP_BASE( * m_workingImage ); + m_buttonUndoLast->Enable( true ); + m_workingImage->Rotate( false ); + m_panelDraw->Refresh(); +} + +void DIALOG_IMAGE_EDITOR::OnGreyScaleConvert( wxCommandEvent& event ) +{ + delete m_lastImage; + m_lastImage = new BITMAP_BASE( * m_workingImage ); + m_buttonUndoLast->Enable( true ); + wxImage& image = *m_workingImage->GetImageData(); + image = image.ConvertToGreyscale(); + m_workingImage->RebuildBitmap(); + m_panelDraw->Refresh(); +} + +void DIALOG_IMAGE_EDITOR::OnHalfSize( wxCommandEvent& event ) +{ + delete m_lastImage; + m_lastImage = new BITMAP_BASE( * m_workingImage ); + m_buttonUndoLast->Enable( true ); + wxSize psize = m_workingImage->GetSizePixels(); + wxImage& image = *m_workingImage->GetImageData(); + + image = image.Scale(psize.x/2, psize.y/2, wxIMAGE_QUALITY_HIGH); + m_workingImage->RebuildBitmap(); + m_panelDraw->Refresh(); +} + +/* Test params values correctness + * Currently scale value must give an actual image + * > MIN_SIZE pixels and < MAX_SIZE pixels + */ +bool DIALOG_IMAGE_EDITOR::CheckValues() +{ + #define MIN_SIZE 16 + #define MAX_SIZE 6000 + double tmp; + wxString msg = m_textCtrlScale->GetValue(); + // Test number correctness + if( ! msg.ToDouble( &tmp ) ) + { + wxMessageBox( _("Incorrect scale number" ) ); + return false; + } + + // Test value correctness + wxSize psize = m_workingImage->GetSizePixels(); + if ( (psize.x * tmp) < MIN_SIZE || (psize.y * tmp) < MIN_SIZE ) + { + wxMessageBox( _("Scale is too small for this image" ) ); + return false; + } + if ( (psize.x * tmp) > MAX_SIZE || (psize.y * tmp) > MAX_SIZE ) + { + wxMessageBox( _("Scale is too large for this image" ) ); + return false; + } + + return true; +} + +void DIALOG_IMAGE_EDITOR::OnOK_Button( wxCommandEvent& aEvent ) +{ + if( CheckValues() ) + EndModal( wxID_OK ); + return; +} + +void DIALOG_IMAGE_EDITOR::OnCancel_Button( wxCommandEvent& aEvent ) +{ + EndModal( wxID_CANCEL ); +} + +void DIALOG_IMAGE_EDITOR::OnRedrawPanel( wxPaintEvent& event ) +{ + wxPaintDC dc( m_panelDraw ); + wxSize size = m_panelDraw->GetClientSize(); + dc.SetDeviceOrigin( size.x/2, size.y/2 ); + + double scale = 1.0 / m_workingImage->GetScalingFactor(); + dc.SetUserScale( scale, scale ); + m_workingImage->DrawBitmap( NULL, &dc, wxPoint(0,0) ); +} + +void DIALOG_IMAGE_EDITOR::TransfertToImage(BITMAP_BASE* aItem ) +{ + wxString msg = m_textCtrlScale->GetValue(); + msg.ToDouble( &m_workingImage->m_Scale ); + aItem->ImportData( m_workingImage ); +} + diff --git a/common/dialogs/dialog_image_editor.fbp b/common/dialogs/dialog_image_editor.fbp new file mode 100644 index 0000000..04c5c7e --- /dev/null +++ b/common/dialogs/dialog_image_editor.fbp @@ -0,0 +1,978 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<wxFormBuilder_Project> + <FileVersion major="1" minor="11" /> + <object class="Project" expanded="1"> + <property name="class_decoration" /> + <property name="code_generation">C++</property> + <property name="disconnect_events">1</property> + <property name="disconnect_mode">source_name</property> + <property name="disconnect_python_events">0</property> + <property name="embedded_files_path">res</property> + <property name="encoding">UTF-8</property> + <property name="event_generation">connect</property> + <property name="file">dialog_image_editor_base</property> + <property name="first_id">1000</property> + <property name="help_provider">none</property> + <property name="internationalize">1</property> + <property name="name">dialog_image_editor</property> + <property name="namespace" /> + <property name="path">.</property> + <property name="precompiled_header" /> + <property name="relative_path">1</property> + <property name="skip_python_events">1</property> + <property name="use_enum">0</property> + <property name="use_microsoft_bom">0</property> + <object class="Dialog" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_managed">0</property> + <property name="aui_name" /> + <property name="best_size" /> + <property name="bg" /> + <property name="caption" /> + <property name="caption_visible">1</property> + <property name="center">wxBOTH</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help" /> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="event_handler">impl_virtual</property> + <property name="extra_style" /> + <property name="fg" /> + <property name="floatable">1</property> + <property name="font" /> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="layer" /> + <property name="max_size" /> + <property name="maximize_button">0</property> + <property name="maximum_size" /> + <property name="min_size" /> + <property name="minimize_button">0</property> + <property name="minimum_size" /> + <property name="moveable">1</property> + <property name="name">DIALOG_IMAGE_EDITOR_BASE</property> + <property name="pane_border">1</property> + <property name="pane_position" /> + <property name="pane_size" /> + <property name="pin_button">1</property> + <property name="pos" /> + <property name="position" /> + <property name="resize">Resizable</property> + <property name="row" /> + <property name="show">1</property> + <property name="size">340,299</property> + <property name="style">wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER</property> + <property name="subclass" /> + <property name="title">Image Editor</property> + <property name="toolbar_pane">0</property> + <property name="tooltip" /> + <property name="validator_data_type" /> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable" /> + <property name="window_extra_style" /> + <property name="window_name" /> + <property name="window_style" /> + <event name="OnActivate" /> + <event name="OnActivateApp" /> + <event name="OnAuiFindManager" /> + <event name="OnAuiPaneButton" /> + <event name="OnAuiPaneClose" /> + <event name="OnAuiPaneMaximize" /> + <event name="OnAuiPaneRestore" /> + <event name="OnAuiRender" /> + <event name="OnChar" /> + <event name="OnClose" /> + <event name="OnEnterWindow" /> + <event name="OnEraseBackground" /> + <event name="OnHibernate" /> + <event name="OnIconize" /> + <event name="OnIdle" /> + <event name="OnInitDialog" /> + <event name="OnKeyDown" /> + <event name="OnKeyUp" /> + <event name="OnKillFocus" /> + <event name="OnLeaveWindow" /> + <event name="OnLeftDClick" /> + <event name="OnLeftDown" /> + <event name="OnLeftUp" /> + <event name="OnMiddleDClick" /> + <event name="OnMiddleDown" /> + <event name="OnMiddleUp" /> + <event name="OnMotion" /> + <event name="OnMouseEvents" /> + <event name="OnMouseWheel" /> + <event name="OnPaint" /> + <event name="OnRightDClick" /> + <event name="OnRightDown" /> + <event name="OnRightUp" /> + <event name="OnSetFocus" /> + <event name="OnSize" /> + <event name="OnUpdateUI" /> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size" /> + <property name="name">bSizerMain</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size" /> + <property name="name">bUpperSizer</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size" /> + <property name="name">bSizerLeft</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND | wxALL</property> + <property name="proportion">1</property> + <object class="wxPanel" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_name" /> + <property name="best_size" /> + <property name="bg" /> + <property name="caption" /> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help" /> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg" /> + <property name="floatable">1</property> + <property name="font" /> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="layer" /> + <property name="max_size" /> + <property name="maximize_button">0</property> + <property name="maximum_size" /> + <property name="min_size" /> + <property name="minimize_button">0</property> + <property name="minimum_size">256,256</property> + <property name="moveable">1</property> + <property name="name">m_panelDraw</property> + <property name="pane_border">1</property> + <property name="pane_position" /> + <property name="pane_size" /> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos" /> + <property name="position" /> + <property name="resize">Resizable</property> + <property name="row" /> + <property name="show">1</property> + <property name="size" /> + <property name="subclass" /> + <property name="toolbar_pane">0</property> + <property name="tooltip" /> + <property name="validator_data_type" /> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable" /> + <property name="window_extra_style" /> + <property name="window_name" /> + <property name="window_style">wxFULL_REPAINT_ON_RESIZE|wxSIMPLE_BORDER|wxTAB_TRAVERSAL</property> + <event name="OnChar" /> + <event name="OnEnterWindow" /> + <event name="OnEraseBackground" /> + <event name="OnKeyDown" /> + <event name="OnKeyUp" /> + <event name="OnKillFocus" /> + <event name="OnLeaveWindow" /> + <event name="OnLeftDClick" /> + <event name="OnLeftDown" /> + <event name="OnLeftUp" /> + <event name="OnMiddleDClick" /> + <event name="OnMiddleDown" /> + <event name="OnMiddleUp" /> + <event name="OnMotion" /> + <event name="OnMouseEvents" /> + <event name="OnMouseWheel" /> + <event name="OnPaint">OnRedrawPanel</event> + <event name="OnRightDClick" /> + <event name="OnRightDown" /> + <event name="OnRightUp" /> + <event name="OnSetFocus" /> + <event name="OnSize" /> + <event name="OnUpdateUI" /> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">0</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size" /> + <property name="name">bSizerRight</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxTOP|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_name" /> + <property name="best_size" /> + <property name="bg" /> + <property name="caption" /> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help" /> + <property name="context_menu">1</property> + <property name="default">0</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg" /> + <property name="floatable">1</property> + <property name="font" /> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Mirror X</property> + <property name="layer" /> + <property name="max_size" /> + <property name="maximize_button">0</property> + <property name="maximum_size" /> + <property name="min_size" /> + <property name="minimize_button">0</property> + <property name="minimum_size" /> + <property name="moveable">1</property> + <property name="name">m_buttonMirrorX</property> + <property name="pane_border">1</property> + <property name="pane_position" /> + <property name="pane_size" /> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos" /> + <property name="position" /> + <property name="resize">Resizable</property> + <property name="row" /> + <property name="show">1</property> + <property name="size" /> + <property name="style" /> + <property name="subclass" /> + <property name="toolbar_pane">0</property> + <property name="tooltip" /> + <property name="validator_data_type" /> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable" /> + <property name="window_extra_style" /> + <property name="window_name" /> + <property name="window_style" /> + <event name="OnButtonClick">OnMirrorX_click</event> + <event name="OnChar" /> + <event name="OnEnterWindow" /> + <event name="OnEraseBackground" /> + <event name="OnKeyDown" /> + <event name="OnKeyUp" /> + <event name="OnKillFocus" /> + <event name="OnLeaveWindow" /> + <event name="OnLeftDClick" /> + <event name="OnLeftDown" /> + <event name="OnLeftUp" /> + <event name="OnMiddleDClick" /> + <event name="OnMiddleDown" /> + <event name="OnMiddleUp" /> + <event name="OnMotion" /> + <event name="OnMouseEvents" /> + <event name="OnMouseWheel" /> + <event name="OnPaint" /> + <event name="OnRightDClick" /> + <event name="OnRightDown" /> + <event name="OnRightUp" /> + <event name="OnSetFocus" /> + <event name="OnSize" /> + <event name="OnUpdateUI" /> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxTOP|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_name" /> + <property name="best_size" /> + <property name="bg" /> + <property name="caption" /> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help" /> + <property name="context_menu">1</property> + <property name="default">0</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg" /> + <property name="floatable">1</property> + <property name="font" /> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Mirror Y</property> + <property name="layer" /> + <property name="max_size" /> + <property name="maximize_button">0</property> + <property name="maximum_size" /> + <property name="min_size" /> + <property name="minimize_button">0</property> + <property name="minimum_size" /> + <property name="moveable">1</property> + <property name="name">m_buttonMirrorY</property> + <property name="pane_border">1</property> + <property name="pane_position" /> + <property name="pane_size" /> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos" /> + <property name="position" /> + <property name="resize">Resizable</property> + <property name="row" /> + <property name="show">1</property> + <property name="size" /> + <property name="style" /> + <property name="subclass" /> + <property name="toolbar_pane">0</property> + <property name="tooltip" /> + <property name="validator_data_type" /> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable" /> + <property name="window_extra_style" /> + <property name="window_name" /> + <property name="window_style" /> + <event name="OnButtonClick">OnMirrorY_click</event> + <event name="OnChar" /> + <event name="OnEnterWindow" /> + <event name="OnEraseBackground" /> + <event name="OnKeyDown" /> + <event name="OnKeyUp" /> + <event name="OnKillFocus" /> + <event name="OnLeaveWindow" /> + <event name="OnLeftDClick" /> + <event name="OnLeftDown" /> + <event name="OnLeftUp" /> + <event name="OnMiddleDClick" /> + <event name="OnMiddleDown" /> + <event name="OnMiddleUp" /> + <event name="OnMotion" /> + <event name="OnMouseEvents" /> + <event name="OnMouseWheel" /> + <event name="OnPaint" /> + <event name="OnRightDClick" /> + <event name="OnRightDown" /> + <event name="OnRightUp" /> + <event name="OnSetFocus" /> + <event name="OnSize" /> + <event name="OnUpdateUI" /> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxALL</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_name" /> + <property name="best_size" /> + <property name="bg" /> + <property name="caption" /> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help" /> + <property name="context_menu">1</property> + <property name="default">0</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg" /> + <property name="floatable">1</property> + <property name="font" /> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Rotate</property> + <property name="layer" /> + <property name="max_size" /> + <property name="maximize_button">0</property> + <property name="maximum_size" /> + <property name="min_size" /> + <property name="minimize_button">0</property> + <property name="minimum_size" /> + <property name="moveable">1</property> + <property name="name">m_buttonRotate</property> + <property name="pane_border">1</property> + <property name="pane_position" /> + <property name="pane_size" /> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos" /> + <property name="position" /> + <property name="resize">Resizable</property> + <property name="row" /> + <property name="show">1</property> + <property name="size" /> + <property name="style" /> + <property name="subclass" /> + <property name="toolbar_pane">0</property> + <property name="tooltip" /> + <property name="validator_data_type" /> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable" /> + <property name="window_extra_style" /> + <property name="window_name" /> + <property name="window_style" /> + <event name="OnButtonClick">OnRotateClick</event> + <event name="OnChar" /> + <event name="OnEnterWindow" /> + <event name="OnEraseBackground" /> + <event name="OnKeyDown" /> + <event name="OnKeyUp" /> + <event name="OnKillFocus" /> + <event name="OnLeaveWindow" /> + <event name="OnLeftDClick" /> + <event name="OnLeftDown" /> + <event name="OnLeftUp" /> + <event name="OnMiddleDClick" /> + <event name="OnMiddleDown" /> + <event name="OnMiddleUp" /> + <event name="OnMotion" /> + <event name="OnMouseEvents" /> + <event name="OnMouseWheel" /> + <event name="OnPaint" /> + <event name="OnRightDClick" /> + <event name="OnRightDown" /> + <event name="OnRightUp" /> + <event name="OnSetFocus" /> + <event name="OnSize" /> + <event name="OnUpdateUI" /> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxTOP|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_name" /> + <property name="best_size" /> + <property name="bg" /> + <property name="caption" /> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help" /> + <property name="context_menu">1</property> + <property name="default">0</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg" /> + <property name="floatable">1</property> + <property name="font" /> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Grey</property> + <property name="layer" /> + <property name="max_size" /> + <property name="maximize_button">0</property> + <property name="maximum_size" /> + <property name="min_size" /> + <property name="minimize_button">0</property> + <property name="minimum_size" /> + <property name="moveable">1</property> + <property name="name">m_buttonGrey</property> + <property name="pane_border">1</property> + <property name="pane_position" /> + <property name="pane_size" /> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos" /> + <property name="position" /> + <property name="resize">Resizable</property> + <property name="row" /> + <property name="show">1</property> + <property name="size" /> + <property name="style" /> + <property name="subclass" /> + <property name="toolbar_pane">0</property> + <property name="tooltip" /> + <property name="validator_data_type" /> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable" /> + <property name="window_extra_style" /> + <property name="window_name" /> + <property name="window_style" /> + <event name="OnButtonClick">OnGreyScaleConvert</event> + <event name="OnChar" /> + <event name="OnEnterWindow" /> + <event name="OnEraseBackground" /> + <event name="OnKeyDown" /> + <event name="OnKeyUp" /> + <event name="OnKillFocus" /> + <event name="OnLeaveWindow" /> + <event name="OnLeftDClick" /> + <event name="OnLeftDown" /> + <event name="OnLeftUp" /> + <event name="OnMiddleDClick" /> + <event name="OnMiddleDown" /> + <event name="OnMiddleUp" /> + <event name="OnMotion" /> + <event name="OnMouseEvents" /> + <event name="OnMouseWheel" /> + <event name="OnPaint" /> + <event name="OnRightDClick" /> + <event name="OnRightDown" /> + <event name="OnRightUp" /> + <event name="OnSetFocus" /> + <event name="OnSize" /> + <event name="OnUpdateUI" /> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxEXPAND</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_name" /> + <property name="best_size" /> + <property name="bg" /> + <property name="caption" /> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help" /> + <property name="context_menu">1</property> + <property name="default">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg" /> + <property name="floatable">1</property> + <property name="font" /> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Half Size</property> + <property name="layer" /> + <property name="max_size" /> + <property name="maximize_button">0</property> + <property name="maximum_size" /> + <property name="min_size" /> + <property name="minimize_button">0</property> + <property name="minimum_size" /> + <property name="moveable">1</property> + <property name="name">m_buttonHalfSize</property> + <property name="pane_border">1</property> + <property name="pane_position" /> + <property name="pane_size" /> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos" /> + <property name="position" /> + <property name="resize">Resizable</property> + <property name="row" /> + <property name="show">1</property> + <property name="size" /> + <property name="style" /> + <property name="subclass" /> + <property name="toolbar_pane">0</property> + <property name="tooltip" /> + <property name="validator_data_type" /> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable" /> + <property name="window_extra_style" /> + <property name="window_name" /> + <property name="window_style" /> + <event name="OnButtonClick">OnHalfSize</event> + <event name="OnChar" /> + <event name="OnEnterWindow" /> + <event name="OnEraseBackground" /> + <event name="OnKeyDown" /> + <event name="OnKeyUp" /> + <event name="OnKillFocus" /> + <event name="OnLeaveWindow" /> + <event name="OnLeftDClick" /> + <event name="OnLeftDown" /> + <event name="OnLeftUp" /> + <event name="OnMiddleDClick" /> + <event name="OnMiddleDown" /> + <event name="OnMiddleUp" /> + <event name="OnMotion" /> + <event name="OnMouseEvents" /> + <event name="OnMouseWheel" /> + <event name="OnPaint" /> + <event name="OnRightDClick" /> + <event name="OnRightDown" /> + <event name="OnRightUp" /> + <event name="OnSetFocus" /> + <event name="OnSize" /> + <event name="OnUpdateUI" /> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxEXPAND</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_name" /> + <property name="best_size" /> + <property name="bg" /> + <property name="caption" /> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help" /> + <property name="context_menu">1</property> + <property name="default">0</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg" /> + <property name="floatable">1</property> + <property name="font" /> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Undo Last</property> + <property name="layer" /> + <property name="max_size" /> + <property name="maximize_button">0</property> + <property name="maximum_size" /> + <property name="min_size" /> + <property name="minimize_button">0</property> + <property name="minimum_size" /> + <property name="moveable">1</property> + <property name="name">m_buttonUndoLast</property> + <property name="pane_border">1</property> + <property name="pane_position" /> + <property name="pane_size" /> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos" /> + <property name="position" /> + <property name="resize">Resizable</property> + <property name="row" /> + <property name="show">1</property> + <property name="size" /> + <property name="style" /> + <property name="subclass" /> + <property name="toolbar_pane">0</property> + <property name="tooltip" /> + <property name="validator_data_type" /> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable" /> + <property name="window_extra_style" /> + <property name="window_name" /> + <property name="window_style" /> + <event name="OnButtonClick">OnUndoLastChange</event> + <event name="OnChar" /> + <event name="OnEnterWindow" /> + <event name="OnEraseBackground" /> + <event name="OnKeyDown" /> + <event name="OnKeyUp" /> + <event name="OnKillFocus" /> + <event name="OnLeaveWindow" /> + <event name="OnLeftDClick" /> + <event name="OnLeftDown" /> + <event name="OnLeftUp" /> + <event name="OnMiddleDClick" /> + <event name="OnMiddleDown" /> + <event name="OnMiddleUp" /> + <event name="OnMotion" /> + <event name="OnMouseEvents" /> + <event name="OnMouseWheel" /> + <event name="OnPaint" /> + <event name="OnRightDClick" /> + <event name="OnRightDown" /> + <event name="OnRightUp" /> + <event name="OnSetFocus" /> + <event name="OnSize" /> + <event name="OnUpdateUI" /> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxTOP|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_name" /> + <property name="best_size" /> + <property name="bg" /> + <property name="caption" /> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help" /> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg" /> + <property name="floatable">1</property> + <property name="font" /> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Image Scale:</property> + <property name="layer" /> + <property name="max_size" /> + <property name="maximize_button">0</property> + <property name="maximum_size" /> + <property name="min_size" /> + <property name="minimize_button">0</property> + <property name="minimum_size" /> + <property name="moveable">1</property> + <property name="name">m_staticTextScale</property> + <property name="pane_border">1</property> + <property name="pane_position" /> + <property name="pane_size" /> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos" /> + <property name="position" /> + <property name="resize">Resizable</property> + <property name="row" /> + <property name="show">1</property> + <property name="size" /> + <property name="style" /> + <property name="subclass" /> + <property name="toolbar_pane">0</property> + <property name="tooltip" /> + <property name="validator_data_type" /> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable" /> + <property name="window_extra_style" /> + <property name="window_name" /> + <property name="window_style" /> + <property name="wrap">-1</property> + <event name="OnChar" /> + <event name="OnEnterWindow" /> + <event name="OnEraseBackground" /> + <event name="OnKeyDown" /> + <event name="OnKeyUp" /> + <event name="OnKillFocus" /> + <event name="OnLeaveWindow" /> + <event name="OnLeftDClick" /> + <event name="OnLeftDown" /> + <event name="OnLeftUp" /> + <event name="OnMiddleDClick" /> + <event name="OnMiddleDown" /> + <event name="OnMiddleUp" /> + <event name="OnMotion" /> + <event name="OnMouseEvents" /> + <event name="OnMouseWheel" /> + <event name="OnPaint" /> + <event name="OnRightDClick" /> + <event name="OnRightDown" /> + <event name="OnRightUp" /> + <event name="OnSetFocus" /> + <event name="OnSize" /> + <event name="OnUpdateUI" /> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND</property> + <property name="proportion">0</property> + <object class="wxTextCtrl" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_name" /> + <property name="best_size" /> + <property name="bg" /> + <property name="caption" /> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help" /> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg" /> + <property name="floatable">1</property> + <property name="font" /> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="layer" /> + <property name="max_size" /> + <property name="maximize_button">0</property> + <property name="maximum_size" /> + <property name="maxlength">0</property> + <property name="min_size" /> + <property name="minimize_button">0</property> + <property name="minimum_size" /> + <property name="moveable">1</property> + <property name="name">m_textCtrlScale</property> + <property name="pane_border">1</property> + <property name="pane_position" /> + <property name="pane_size" /> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos" /> + <property name="position" /> + <property name="resize">Resizable</property> + <property name="row" /> + <property name="show">1</property> + <property name="size" /> + <property name="style" /> + <property name="subclass" /> + <property name="toolbar_pane">0</property> + <property name="tooltip" /> + <property name="validator_data_type" /> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable" /> + <property name="value" /> + <property name="window_extra_style" /> + <property name="window_name" /> + <property name="window_style" /> + <event name="OnChar" /> + <event name="OnEnterWindow" /> + <event name="OnEraseBackground" /> + <event name="OnKeyDown" /> + <event name="OnKeyUp" /> + <event name="OnKillFocus" /> + <event name="OnLeaveWindow" /> + <event name="OnLeftDClick" /> + <event name="OnLeftDown" /> + <event name="OnLeftUp" /> + <event name="OnMiddleDClick" /> + <event name="OnMiddleDown" /> + <event name="OnMiddleUp" /> + <event name="OnMotion" /> + <event name="OnMouseEvents" /> + <event name="OnMouseWheel" /> + <event name="OnPaint" /> + <event name="OnRightDClick" /> + <event name="OnRightDown" /> + <event name="OnRightUp" /> + <event name="OnSetFocus" /> + <event name="OnSize" /> + <event name="OnText" /> + <event name="OnTextEnter" /> + <event name="OnTextMaxLen" /> + <event name="OnTextURL" /> + <event name="OnUpdateUI" /> + </object> + </object> + </object> + </object> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALIGN_RIGHT</property> + <property name="proportion">0</property> + <object class="wxStdDialogButtonSizer" expanded="1"> + <property name="Apply">0</property> + <property name="Cancel">1</property> + <property name="ContextHelp">0</property> + <property name="Help">0</property> + <property name="No">0</property> + <property name="OK">1</property> + <property name="Save">0</property> + <property name="Yes">0</property> + <property name="minimum_size" /> + <property name="name">m_sdbSizer1</property> + <property name="permission">protected</property> + <event name="OnApplyButtonClick" /> + <event name="OnCancelButtonClick">OnCancel_Button</event> + <event name="OnContextHelpButtonClick" /> + <event name="OnHelpButtonClick" /> + <event name="OnNoButtonClick" /> + <event name="OnOKButtonClick">OnOK_Button</event> + <event name="OnSaveButtonClick" /> + <event name="OnYesButtonClick" /> + </object> + </object> + </object> + </object> + </object> +</wxFormBuilder_Project> diff --git a/common/dialogs/dialog_image_editor.h b/common/dialogs/dialog_image_editor.h new file mode 100644 index 0000000..226221c --- /dev/null +++ b/common/dialogs/dialog_image_editor.h @@ -0,0 +1,69 @@ +/** + * @file dialog_image_editor.h + */ + +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2011 jean-pierre.charras + * Copyright (C) 2011 Kicad Developers, see change_log.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_IMAGE_EDITOR_H_ +#define _DIALOG_IMAGE_EDITOR_H_ + +#include <dialog_image_editor_base.h> + + +class DIALOG_IMAGE_EDITOR : public DIALOG_IMAGE_EDITOR_BASE +{ +private: + BITMAP_BASE* m_workingImage; // The copy of BITMAP_BASE to be edited + BITMAP_BASE* m_lastImage; // the saved BITMAP_BASE before a new change. + // Used to undo the last change + +public: + DIALOG_IMAGE_EDITOR( wxWindow* aParent, BITMAP_BASE* aItem ); + ~DIALOG_IMAGE_EDITOR(){ delete m_workingImage; } + + +public: + /** + * Function TransfertToImage + * copy edited image to aItem + * @param aItem = the target + */ + void TransfertToImage( BITMAP_BASE* aItem ); + +private: + void OnUndoLastChange( wxCommandEvent& event ); + void OnGreyScaleConvert( wxCommandEvent& event ); + void OnHalfSize( wxCommandEvent& event ); + void OnMirrorX_click( wxCommandEvent& event ); + void OnMirrorY_click( wxCommandEvent& event ); + void OnRotateClick( wxCommandEvent& event ); + void OnOK_Button( wxCommandEvent& aEvent ); + void OnCancel_Button( wxCommandEvent& aEvent ); + void OnRedrawPanel( wxPaintEvent& event ); + bool CheckValues(); +}; + + +#endif // _DIALOG_IMAGE_EDITOR_H_ diff --git a/common/dialogs/dialog_image_editor_base.cpp b/common/dialogs/dialog_image_editor_base.cpp new file mode 100644 index 0000000..e11c764 --- /dev/null +++ b/common/dialogs/dialog_image_editor_base.cpp @@ -0,0 +1,103 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 30 2011) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "dialog_image_editor_base.h" + +/////////////////////////////////////////////////////////////////////////// + +DIALOG_IMAGE_EDITOR_BASE::DIALOG_IMAGE_EDITOR_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxDefaultSize, wxDefaultSize ); + + wxBoxSizer* bSizerMain; + bSizerMain = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bUpperSizer; + bUpperSizer = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizerLeft; + bSizerLeft = new wxBoxSizer( wxHORIZONTAL ); + + m_panelDraw = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE|wxSIMPLE_BORDER|wxTAB_TRAVERSAL ); + m_panelDraw->SetMinSize( wxSize( 256,256 ) ); + + bSizerLeft->Add( m_panelDraw, 1, wxEXPAND | wxALL, 5 ); + + wxBoxSizer* bSizerRight; + bSizerRight = new wxBoxSizer( wxVERTICAL ); + + m_buttonMirrorX = new wxButton( this, wxID_ANY, _("Mirror X"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizerRight->Add( m_buttonMirrorX, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 ); + + m_buttonMirrorY = new wxButton( this, wxID_ANY, _("Mirror Y"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizerRight->Add( m_buttonMirrorY, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 ); + + m_buttonRotate = new wxButton( this, wxID_ANY, _("Rotate"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizerRight->Add( m_buttonRotate, 0, wxEXPAND|wxALL, 5 ); + + m_buttonGrey = new wxButton( this, wxID_ANY, _("Grey"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizerRight->Add( m_buttonGrey, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 ); + + m_buttonHalfSize = new wxButton( this, wxID_ANY, _("Half Size"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonHalfSize->SetDefault(); + bSizerRight->Add( m_buttonHalfSize, 0, wxALL|wxEXPAND, 5 ); + + m_buttonUndoLast = new wxButton( this, wxID_ANY, _("Undo Last"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizerRight->Add( m_buttonUndoLast, 0, wxALL|wxEXPAND, 5 ); + + m_staticTextScale = new wxStaticText( this, wxID_ANY, _("Image Scale:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextScale->Wrap( -1 ); + bSizerRight->Add( m_staticTextScale, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); + + m_textCtrlScale = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + bSizerRight->Add( m_textCtrlScale, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 ); + + bSizerLeft->Add( bSizerRight, 0, wxEXPAND, 5 ); + + bUpperSizer->Add( bSizerLeft, 1, wxEXPAND, 5 ); + + bSizerMain->Add( bUpperSizer, 1, wxEXPAND, 5 ); + + m_sdbSizer1 = new wxStdDialogButtonSizer(); + m_sdbSizer1OK = new wxButton( this, wxID_OK ); + m_sdbSizer1->AddButton( m_sdbSizer1OK ); + m_sdbSizer1Cancel = new wxButton( this, wxID_CANCEL ); + m_sdbSizer1->AddButton( m_sdbSizer1Cancel ); + m_sdbSizer1->Realize(); + bSizerMain->Add( m_sdbSizer1, 0, wxALIGN_RIGHT, 5 ); + + this->SetSizer( bSizerMain ); + this->Layout(); + + this->Centre( wxBOTH ); + + // Connect Events + m_panelDraw->Connect( wxEVT_PAINT, wxPaintEventHandler( DIALOG_IMAGE_EDITOR_BASE::OnRedrawPanel ), NULL, this ); + m_buttonMirrorX->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMAGE_EDITOR_BASE::OnMirrorX_click ), NULL, this ); + m_buttonMirrorY->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMAGE_EDITOR_BASE::OnMirrorY_click ), NULL, this ); + m_buttonRotate->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMAGE_EDITOR_BASE::OnRotateClick ), NULL, this ); + m_buttonGrey->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMAGE_EDITOR_BASE::OnGreyScaleConvert ), NULL, this ); + m_buttonHalfSize->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMAGE_EDITOR_BASE::OnHalfSize ), NULL, this ); + m_buttonUndoLast->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMAGE_EDITOR_BASE::OnUndoLastChange ), NULL, this ); + m_sdbSizer1Cancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMAGE_EDITOR_BASE::OnCancel_Button ), NULL, this ); + m_sdbSizer1OK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMAGE_EDITOR_BASE::OnOK_Button ), NULL, this ); +} + +DIALOG_IMAGE_EDITOR_BASE::~DIALOG_IMAGE_EDITOR_BASE() +{ + // Disconnect Events + m_panelDraw->Disconnect( wxEVT_PAINT, wxPaintEventHandler( DIALOG_IMAGE_EDITOR_BASE::OnRedrawPanel ), NULL, this ); + m_buttonMirrorX->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMAGE_EDITOR_BASE::OnMirrorX_click ), NULL, this ); + m_buttonMirrorY->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMAGE_EDITOR_BASE::OnMirrorY_click ), NULL, this ); + m_buttonRotate->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMAGE_EDITOR_BASE::OnRotateClick ), NULL, this ); + m_buttonGrey->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMAGE_EDITOR_BASE::OnGreyScaleConvert ), NULL, this ); + m_buttonHalfSize->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMAGE_EDITOR_BASE::OnHalfSize ), NULL, this ); + m_buttonUndoLast->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMAGE_EDITOR_BASE::OnUndoLastChange ), NULL, this ); + m_sdbSizer1Cancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMAGE_EDITOR_BASE::OnCancel_Button ), NULL, this ); + m_sdbSizer1OK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMAGE_EDITOR_BASE::OnOK_Button ), NULL, this ); + +} diff --git a/common/dialogs/dialog_image_editor_base.h b/common/dialogs/dialog_image_editor_base.h new file mode 100644 index 0000000..a7522a1 --- /dev/null +++ b/common/dialogs/dialog_image_editor_base.h @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 30 2011) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __DIALOG_IMAGE_EDITOR_BASE_H__ +#define __DIALOG_IMAGE_EDITOR_BASE_H__ + +#include <wx/artprov.h> +#include <wx/xrc/xmlres.h> +#include <wx/intl.h> +#include <wx/panel.h> +#include <wx/string.h> +#include <wx/gdicmn.h> +#include <wx/font.h> +#include <wx/colour.h> +#include <wx/settings.h> +#include <wx/button.h> +#include <wx/stattext.h> +#include <wx/textctrl.h> +#include <wx/sizer.h> +#include <wx/dialog.h> + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class DIALOG_IMAGE_EDITOR_BASE +/////////////////////////////////////////////////////////////////////////////// +class DIALOG_IMAGE_EDITOR_BASE : public wxDialog +{ + private: + + protected: + wxPanel* m_panelDraw; + wxButton* m_buttonMirrorX; + wxButton* m_buttonMirrorY; + wxButton* m_buttonRotate; + wxButton* m_buttonGrey; + wxButton* m_buttonHalfSize; + wxButton* m_buttonUndoLast; + wxStaticText* m_staticTextScale; + wxTextCtrl* m_textCtrlScale; + wxStdDialogButtonSizer* m_sdbSizer1; + wxButton* m_sdbSizer1OK; + wxButton* m_sdbSizer1Cancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnRedrawPanel( wxPaintEvent& event ) { event.Skip(); } + virtual void OnMirrorX_click( wxCommandEvent& event ) { event.Skip(); } + virtual void OnMirrorY_click( wxCommandEvent& event ) { event.Skip(); } + virtual void OnRotateClick( wxCommandEvent& event ) { event.Skip(); } + virtual void OnGreyScaleConvert( wxCommandEvent& event ) { event.Skip(); } + virtual void OnHalfSize( wxCommandEvent& event ) { event.Skip(); } + virtual void OnUndoLastChange( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancel_Button( wxCommandEvent& event ) { event.Skip(); } + virtual void OnOK_Button( wxCommandEvent& event ) { event.Skip(); } + + + public: + + DIALOG_IMAGE_EDITOR_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Image Editor"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 340,299 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~DIALOG_IMAGE_EDITOR_BASE(); + +}; + +#endif //__DIALOG_IMAGE_EDITOR_BASE_H__ diff --git a/common/dialogs/dialog_list_selector_base.cpp b/common/dialogs/dialog_list_selector_base.cpp new file mode 100644 index 0000000..d38d417 --- /dev/null +++ b/common/dialogs/dialog_list_selector_base.cpp @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 5 2014) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "dialog_list_selector_base.h" + +/////////////////////////////////////////////////////////////////////////// + +EDA_LIST_DIALOG_BASE::EDA_LIST_DIALOG_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( 400,400 ), wxDefaultSize ); + + wxBoxSizer* bSizerMain; + bSizerMain = new wxBoxSizer( wxVERTICAL ); + + m_filterLabel = new wxStaticText( this, wxID_ANY, _("Filter:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_filterLabel->Wrap( -1 ); + m_filterLabel->SetToolTip( _("Enter a string to filter items.\nOnly names containing this string will be listed") ); + + bSizerMain->Add( m_filterLabel, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); + + m_filterBox = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + bSizerMain->Add( m_filterBox, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + m_staticText2 = new wxStaticText( this, wxID_ANY, _("Items:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText2->Wrap( -1 ); + bSizerMain->Add( m_staticText2, 0, wxRIGHT|wxLEFT, 5 ); + + m_listBox = new wxListCtrl( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_HRULES|wxLC_REPORT|wxLC_SINGLE_SEL|wxLC_VRULES|wxALWAYS_SHOW_SB|wxVSCROLL ); + m_listBox->SetMinSize( wxSize( -1,200 ) ); + + bSizerMain->Add( m_listBox, 3, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 ); + + m_staticTextMsg = new wxStaticText( this, wxID_ANY, _("Messages:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextMsg->Wrap( -1 ); + bSizerMain->Add( m_staticTextMsg, 0, wxRIGHT|wxLEFT, 5 ); + + m_messages = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY ); + m_messages->SetMinSize( wxSize( -1,80 ) ); + + bSizerMain->Add( m_messages, 1, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + m_sdbSizer = new wxStdDialogButtonSizer(); + m_sdbSizerOK = new wxButton( this, wxID_OK ); + m_sdbSizer->AddButton( m_sdbSizerOK ); + m_sdbSizerCancel = new wxButton( this, wxID_CANCEL ); + m_sdbSizer->AddButton( m_sdbSizerCancel ); + m_sdbSizer->Realize(); + + bSizerMain->Add( m_sdbSizer, 0, wxALL|wxEXPAND, 5 ); + + + this->SetSizer( bSizerMain ); + this->Layout(); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( EDA_LIST_DIALOG_BASE::onClose ) ); + m_filterBox->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( EDA_LIST_DIALOG_BASE::textChangeInFilterBox ), NULL, this ); + m_listBox->Connect( wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler( EDA_LIST_DIALOG_BASE::onListItemActivated ), NULL, this ); + m_listBox->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( EDA_LIST_DIALOG_BASE::onListItemSelected ), NULL, this ); + m_sdbSizerCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( EDA_LIST_DIALOG_BASE::onCancelClick ), NULL, this ); + m_sdbSizerOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( EDA_LIST_DIALOG_BASE::onOkClick ), NULL, this ); +} + +EDA_LIST_DIALOG_BASE::~EDA_LIST_DIALOG_BASE() +{ + // Disconnect Events + this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( EDA_LIST_DIALOG_BASE::onClose ) ); + m_filterBox->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( EDA_LIST_DIALOG_BASE::textChangeInFilterBox ), NULL, this ); + m_listBox->Disconnect( wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler( EDA_LIST_DIALOG_BASE::onListItemActivated ), NULL, this ); + m_listBox->Disconnect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( EDA_LIST_DIALOG_BASE::onListItemSelected ), NULL, this ); + m_sdbSizerCancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( EDA_LIST_DIALOG_BASE::onCancelClick ), NULL, this ); + m_sdbSizerOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( EDA_LIST_DIALOG_BASE::onOkClick ), NULL, this ); + +} diff --git a/common/dialogs/dialog_list_selector_base.fbp b/common/dialogs/dialog_list_selector_base.fbp new file mode 100644 index 0000000..1d1d0e2 --- /dev/null +++ b/common/dialogs/dialog_list_selector_base.fbp @@ -0,0 +1,661 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<wxFormBuilder_Project> + <FileVersion major="1" minor="13" /> + <object class="Project" expanded="1"> + <property name="class_decoration"></property> + <property name="code_generation">C++</property> + <property name="disconnect_events">1</property> + <property name="disconnect_mode">source_name</property> + <property name="disconnect_php_events">0</property> + <property name="disconnect_python_events">0</property> + <property name="embedded_files_path">res</property> + <property name="encoding">UTF-8</property> + <property name="event_generation">connect</property> + <property name="file">dialog_list_selector_base</property> + <property name="first_id">1000</property> + <property name="help_provider">none</property> + <property name="internationalize">1</property> + <property name="name">dialog_list_selector_base</property> + <property name="namespace"></property> + <property name="path">.</property> + <property name="precompiled_header"></property> + <property name="relative_path">1</property> + <property name="skip_lua_events">1</property> + <property name="skip_php_events">1</property> + <property name="skip_python_events">1</property> + <property name="ui_table">UI</property> + <property name="use_enum">0</property> + <property name="use_microsoft_bom">0</property> + <object class="Dialog" expanded="1"> + <property name="aui_managed">0</property> + <property name="aui_manager_style">wxAUI_MGR_DEFAULT</property> + <property name="bg"></property> + <property name="center">wxBOTH</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="enabled">1</property> + <property name="event_handler">decl_pure_virtual</property> + <property name="extra_style"></property> + <property name="fg"></property> + <property name="font"></property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="maximum_size"></property> + <property name="minimum_size">400,400</property> + <property name="name">EDA_LIST_DIALOG_BASE</property> + <property name="pos"></property> + <property name="size">400,400</property> + <property name="style">wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER</property> + <property name="subclass">DIALOG_SHIM; dialog_shim.h</property> + <property name="title"></property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnActivate"></event> + <event name="OnActivateApp"></event> + <event name="OnAuiFindManager"></event> + <event name="OnAuiPaneButton"></event> + <event name="OnAuiPaneClose"></event> + <event name="OnAuiPaneMaximize"></event> + <event name="OnAuiPaneRestore"></event> + <event name="OnAuiRender"></event> + <event name="OnChar"></event> + <event name="OnClose">onClose</event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnHibernate"></event> + <event name="OnIconize"></event> + <event name="OnIdle"></event> + <event name="OnInitDialog"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerMain</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxTOP|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Filter:</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_filterLabel</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip">Enter a string to filter items.
Only names containing this string will be listed</property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxTextCtrl" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="maxlength"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_filterBox</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="value"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnText">textChangeInFilterBox</event> + <event name="OnTextEnter"></event> + <event name="OnTextMaxLen"></event> + <event name="OnTextURL"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Items:</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticText2</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND</property> + <property name="proportion">3</property> + <object class="wxListCtrl" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size">-1,200</property> + <property name="moveable">1</property> + <property name="name">m_listBox</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style">wxLC_HRULES|wxLC_REPORT|wxLC_SINGLE_SEL|wxLC_VRULES</property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style">wxALWAYS_SHOW_SB|wxVSCROLL</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnListBeginDrag"></event> + <event name="OnListBeginLabelEdit"></event> + <event name="OnListBeginRDrag"></event> + <event name="OnListCacheHint"></event> + <event name="OnListColBeginDrag"></event> + <event name="OnListColClick"></event> + <event name="OnListColDragging"></event> + <event name="OnListColEndDrag"></event> + <event name="OnListColRightClick"></event> + <event name="OnListDeleteAllItems"></event> + <event name="OnListDeleteItem"></event> + <event name="OnListEndLabelEdit"></event> + <event name="OnListInsertItem"></event> + <event name="OnListItemActivated">onListItemActivated</event> + <event name="OnListItemDeselected"></event> + <event name="OnListItemFocused"></event> + <event name="OnListItemMiddleClick"></event> + <event name="OnListItemRightClick"></event> + <event name="OnListItemSelected">onListItemSelected</event> + <event name="OnListKeyDown"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Messages:</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextMsg</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT</property> + <property name="proportion">1</property> + <object class="wxTextCtrl" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="maxlength"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size">-1,80</property> + <property name="moveable">1</property> + <property name="name">m_messages</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style">wxTE_MULTILINE|wxTE_READONLY</property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="value"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnText"></event> + <event name="OnTextEnter"></event> + <event name="OnTextMaxLen"></event> + <event name="OnTextURL"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxEXPAND</property> + <property name="proportion">0</property> + <object class="wxStdDialogButtonSizer" expanded="1"> + <property name="Apply">0</property> + <property name="Cancel">1</property> + <property name="ContextHelp">0</property> + <property name="Help">0</property> + <property name="No">0</property> + <property name="OK">1</property> + <property name="Save">0</property> + <property name="Yes">0</property> + <property name="minimum_size"></property> + <property name="name">m_sdbSizer</property> + <property name="permission">protected</property> + <event name="OnApplyButtonClick"></event> + <event name="OnCancelButtonClick">onCancelClick</event> + <event name="OnContextHelpButtonClick"></event> + <event name="OnHelpButtonClick"></event> + <event name="OnNoButtonClick"></event> + <event name="OnOKButtonClick">onOkClick</event> + <event name="OnSaveButtonClick"></event> + <event name="OnYesButtonClick"></event> + </object> + </object> + </object> + </object> + </object> +</wxFormBuilder_Project> diff --git a/common/dialogs/dialog_list_selector_base.h b/common/dialogs/dialog_list_selector_base.h new file mode 100644 index 0000000..c274c2c --- /dev/null +++ b/common/dialogs/dialog_list_selector_base.h @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 5 2014) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __DIALOG_LIST_SELECTOR_BASE_H__ +#define __DIALOG_LIST_SELECTOR_BASE_H__ + +#include <wx/artprov.h> +#include <wx/xrc/xmlres.h> +#include <wx/intl.h> +class DIALOG_SHIM; + +#include "dialog_shim.h" +#include <wx/string.h> +#include <wx/stattext.h> +#include <wx/gdicmn.h> +#include <wx/font.h> +#include <wx/colour.h> +#include <wx/settings.h> +#include <wx/textctrl.h> +#include <wx/listctrl.h> +#include <wx/sizer.h> +#include <wx/button.h> +#include <wx/dialog.h> + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class EDA_LIST_DIALOG_BASE +/////////////////////////////////////////////////////////////////////////////// +class EDA_LIST_DIALOG_BASE : public DIALOG_SHIM +{ + private: + + protected: + wxStaticText* m_filterLabel; + wxTextCtrl* m_filterBox; + wxStaticText* m_staticText2; + wxListCtrl* m_listBox; + wxStaticText* m_staticTextMsg; + wxTextCtrl* m_messages; + wxStdDialogButtonSizer* m_sdbSizer; + wxButton* m_sdbSizerOK; + wxButton* m_sdbSizerCancel; + + // Virtual event handlers, overide them in your derived class + virtual void onClose( wxCloseEvent& event ) = 0; + virtual void textChangeInFilterBox( wxCommandEvent& event ) = 0; + virtual void onListItemActivated( wxListEvent& event ) = 0; + virtual void onListItemSelected( wxListEvent& event ) = 0; + virtual void onCancelClick( wxCommandEvent& event ) = 0; + virtual void onOkClick( wxCommandEvent& event ) = 0; + + + public: + + EDA_LIST_DIALOG_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 400,400 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~EDA_LIST_DIALOG_BASE(); + +}; + +#endif //__DIALOG_LIST_SELECTOR_BASE_H__ diff --git a/common/dialogs/dialog_page_settings.cpp b/common/dialogs/dialog_page_settings.cpp new file mode 100644 index 0000000..ef17651 --- /dev/null +++ b/common/dialogs/dialog_page_settings.cpp @@ -0,0 +1,841 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 1992-2015 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 + */ + +/** + * @file dialog_page_settings.cpp + */ + +#include <fctsys.h> +#include <macros.h> // DIM() +#include <common.h> +#include <project.h> +#include <confirm.h> +#include <gr_basic.h> +#include <base_struct.h> +#include <class_drawpanel.h> +#include <class_title_block.h> +#include <draw_frame.h> +#include <worksheet_shape_builder.h> +#include <class_base_screen.h> +#include <wildcards_and_files_ext.h> + +#include <wx/valgen.h> +#include <wx/tokenzr.h> + +#ifdef EESCHEMA +#include <class_sch_screen.h> +#include <general.h> +#endif + +#include <worksheet.h> +#include <dialog_page_settings.h> + + +// List of page formats. +// they are prefixed by "_HKI" (already in use for hotkeys) instead of "_", +// because we need both the translated and the not translated version. +// when displayed in dialog we should explicitely call wxGetTranslation() +// to show the translated version. +// See hotkeys_basic.h for more info +#define _HKI( x ) wxT( x ) +static const wxString pageFmts[] = +{ + _HKI("A4 210x297mm"), + _HKI("A3 297x420mm"), + _HKI("A2 420x594mm"), + _HKI("A1 594x841mm"), + _HKI("A0 841x1189mm"), + _HKI("A 8.5x11in"), + _HKI("B 11x17in"), + _HKI("C 17x22in"), + _HKI("D 22x34in"), + _HKI("E 34x44in"), + _HKI("USLetter 8.5x11in"), // USLetter without space is correct + _HKI("USLegal 8.5x14in"), // USLegal without space is correct + _HKI("USLedger 11x17in"), // USLedger without space is correct + _HKI("User (Custom)"), // size defined by user. The string must contain "Custom" + // to be reconized in code +}; + +void EDA_DRAW_FRAME::Process_PageSettings( wxCommandEvent& event ) +{ + DIALOG_PAGES_SETTINGS dlg( this ); + dlg.SetWksFileName( BASE_SCREEN::m_PageLayoutDescrFileName ); + + if( dlg.ShowModal() == wxID_OK ) + { + if( m_canvas ) + m_canvas->Refresh(); + } +} + + +DIALOG_PAGES_SETTINGS::DIALOG_PAGES_SETTINGS( EDA_DRAW_FRAME* parent ) : + DIALOG_PAGES_SETTINGS_BASE( parent ), + m_initialized( false ) +{ + m_parent = parent; + m_screen = m_parent->GetScreen(); + m_projectPath = Prj().GetProjectPath(); + m_page_bitmap = NULL; + m_tb = m_parent->GetTitleBlock(); + m_customFmt = false; + m_localPrjConfigChanged = false; + m_pagelayout = NULL; + + initDialog(); + + GetSizer()->SetSizeHints( this ); + Centre(); +} + + +DIALOG_PAGES_SETTINGS::~DIALOG_PAGES_SETTINGS() +{ + delete m_page_bitmap; + delete m_pagelayout; +} + + +void DIALOG_PAGES_SETTINGS::initDialog() +{ + wxString msg; + double customSizeX; + double customSizeY; + + // initalize page format choice box and page format list. + // The first shows translated strings, the second contains not translated strings + m_paperSizeComboBox->Clear(); + + for( unsigned ii = 0; ii < DIM(pageFmts); ii++ ) + { + m_pageFmt.Add( pageFmts[ii] ); + m_paperSizeComboBox->Append( wxGetTranslation( pageFmts[ii] ) ); + } + + // initialize the page layout descr filename + SetWksFileName( BASE_SCREEN::m_PageLayoutDescrFileName ); + + +#ifdef EESCHEMA + // Init display value for schematic sub-sheet number + wxString format = m_TextSheetCount->GetLabel(); + msg.Printf( format, m_screen->m_NumberOfScreens ); + m_TextSheetCount->SetLabel( msg ); + + format = m_TextSheetNumber->GetLabel(); + msg.Printf( format, m_screen->m_ScreenNumber ); + m_TextSheetNumber->SetLabel( msg ); +#else + m_TextSheetCount->Show( false ); + m_TextSheetNumber->Show( false ); +#endif + + m_pageInfo = m_parent->GetPageSettings(); + SetCurrentPageSizeSelection( m_pageInfo.GetType() ); + m_orientationComboBox->SetSelection( m_pageInfo.IsPortrait() ); + + // only a click fires the "selection changed" event, so have to fabricate this check + wxCommandEvent dummy; + OnPaperSizeChoice( dummy ); + + if( m_customFmt) // The custom value is defined by the page size + { + customSizeX = m_pageInfo.GetWidthMils(); + customSizeY = m_pageInfo.GetHeightMils(); + } + else // The custom value is set to a default value, or the last defined value + { + customSizeX = m_pageInfo.GetCustomWidthMils(); + customSizeY = m_pageInfo.GetCustomHeightMils(); + } + + switch( g_UserUnit ) + { + case MILLIMETRES: + customSizeX *= 25.4e-3; + customSizeY *= 25.4e-3; + + msg.Printf( wxT( "%.2f" ), customSizeX ); + m_TextUserSizeX->SetValue( msg ); + + msg.Printf( wxT( "%.2f" ), customSizeY ); + m_TextUserSizeY->SetValue( msg ); + break; + + default: + case INCHES: + customSizeX /= 1000.0; + customSizeY /= 1000.0; + + msg.Printf( wxT( "%.3f" ), customSizeX ); + m_TextUserSizeX->SetValue( msg ); + + msg.Printf( wxT( "%.3f" ), customSizeY ); + m_TextUserSizeY->SetValue( msg ); + break; + } + + m_TextRevision->SetValue( m_tb.GetRevision() ); + m_TextDate->SetValue( m_tb.GetDate() ); + m_TextTitle->SetValue( m_tb.GetTitle() ); + m_TextCompany->SetValue( m_tb.GetCompany() ); + m_TextComment1->SetValue( m_tb.GetComment1() ); + m_TextComment2->SetValue( m_tb.GetComment2() ); + m_TextComment3->SetValue( m_tb.GetComment3() ); + m_TextComment4->SetValue( m_tb.GetComment4() ); + +#ifndef EESCHEMA + // these options have meaning only for Eeschema. + // disable them for other apps + m_RevisionExport->Show( false ); + m_DateExport->Show( false ); + m_TitleExport->Show( false ); + m_CompanyExport->Show( false ); + m_Comment1Export->Show( false ); + m_Comment2Export->Show( false ); + m_Comment3Export->Show( false ); + m_Comment4Export->Show( false ); +#endif + + GetPageLayoutInfoFromDialog(); + UpdatePageLayoutExample(); + + // Make the OK button the default. + m_sdbSizerOK->SetDefault(); + m_initialized = true; +} + + +void DIALOG_PAGES_SETTINGS::OnOkClick( wxCommandEvent& event ) +{ + if( SavePageSettings() ) + { + m_screen->SetModify(); + m_parent->GetCanvas()->Refresh(); + + if( LocalPrjConfigChanged() ) + m_parent->SaveProjectSettings( true ); + } + + event.Skip(); +} + + +void DIALOG_PAGES_SETTINGS::OnPaperSizeChoice( wxCommandEvent& event ) +{ + int idx = m_paperSizeComboBox->GetSelection(); + + if( idx < 0 ) + idx = 0; + + const wxString paperType = m_pageFmt[idx]; + + if( paperType.Contains( PAGE_INFO::Custom ) ) + { + m_orientationComboBox->Enable( false ); + m_TextUserSizeX->Enable( true ); + m_TextUserSizeY->Enable( true ); + m_customFmt = true; + } + else + { + m_orientationComboBox->Enable( true ); + +#if 0 + // ForcePortrait() does not exist, but could be useful. + // so I leave these lines, which could be seen as a todo feature + if( paperType.ForcePortrait() ) + { + m_orientationComboBox->SetStringSelection( _( "Portrait" ) ); + m_orientationComboBox->Enable( false ); + } +#endif + m_TextUserSizeX->Enable( false ); + m_TextUserSizeY->Enable( false ); + m_customFmt = false; + } + + GetPageLayoutInfoFromDialog(); + UpdatePageLayoutExample(); +} + + +void DIALOG_PAGES_SETTINGS::OnUserPageSizeXTextUpdated( wxCommandEvent& event ) +{ + if( m_initialized && m_TextUserSizeX->IsModified() ) + { + GetPageLayoutInfoFromDialog(); + UpdatePageLayoutExample(); + } +} + + +void DIALOG_PAGES_SETTINGS::OnUserPageSizeYTextUpdated( wxCommandEvent& event ) +{ + if( m_initialized && m_TextUserSizeY->IsModified() ) + { + GetPageLayoutInfoFromDialog(); + UpdatePageLayoutExample(); + } +} + + +void DIALOG_PAGES_SETTINGS::OnPageOrientationChoice( wxCommandEvent& event ) +{ + if( m_initialized ) + { + GetPageLayoutInfoFromDialog(); + UpdatePageLayoutExample(); + } +} + + +void DIALOG_PAGES_SETTINGS::OnRevisionTextUpdated( wxCommandEvent& event ) +{ + if( m_initialized && m_TextRevision->IsModified() ) + { + GetPageLayoutInfoFromDialog(); + m_tb.SetRevision( m_TextRevision->GetValue() ); + UpdatePageLayoutExample(); + } +} + + +void DIALOG_PAGES_SETTINGS::OnDateTextUpdated( wxCommandEvent& event ) +{ + if( m_initialized && m_TextDate->IsModified() ) + { + GetPageLayoutInfoFromDialog(); + m_tb.SetDate( m_TextDate->GetValue() ); + UpdatePageLayoutExample(); + } +} + + +void DIALOG_PAGES_SETTINGS::OnTitleTextUpdated( wxCommandEvent& event ) +{ + if( m_initialized && m_TextTitle->IsModified() ) + { + GetPageLayoutInfoFromDialog(); + m_tb.SetTitle( m_TextTitle->GetValue() ); + UpdatePageLayoutExample(); + } +} + + +void DIALOG_PAGES_SETTINGS::OnCompanyTextUpdated( wxCommandEvent& event ) +{ + if( m_initialized && m_TextCompany->IsModified() ) + { + GetPageLayoutInfoFromDialog(); + m_tb.SetCompany( m_TextCompany->GetValue() ); + UpdatePageLayoutExample(); + } +} + + +void DIALOG_PAGES_SETTINGS::OnComment1TextUpdated( wxCommandEvent& event ) +{ + if( m_initialized && m_TextComment1->IsModified() ) + { + GetPageLayoutInfoFromDialog(); + m_tb.SetComment1( m_TextComment1->GetValue() ); + UpdatePageLayoutExample(); + } +} + + +void DIALOG_PAGES_SETTINGS::OnComment2TextUpdated( wxCommandEvent& event ) +{ + if( m_initialized && m_TextComment2->IsModified() ) + { + GetPageLayoutInfoFromDialog(); + m_tb.SetComment2( m_TextComment2->GetValue() ); + UpdatePageLayoutExample(); + } +} + + +void DIALOG_PAGES_SETTINGS::OnComment3TextUpdated( wxCommandEvent& event ) +{ + if( m_initialized && m_TextComment3->IsModified() ) + { + GetPageLayoutInfoFromDialog(); + m_tb.SetComment3( m_TextComment3->GetValue() ); + UpdatePageLayoutExample(); + } +} + + +void DIALOG_PAGES_SETTINGS::OnComment4TextUpdated( wxCommandEvent& event ) +{ + if( m_initialized && m_TextComment4->IsModified() ) + { + GetPageLayoutInfoFromDialog(); + m_tb.SetComment4( m_TextComment4->GetValue() ); + UpdatePageLayoutExample(); + } +} + + +void DIALOG_PAGES_SETTINGS::OnDateApplyClick( wxCommandEvent& event ) +{ + wxDateTime datetime = m_PickDate->GetValue(); + wxString date = + // We can choose different formats. Only one must be uncommented + // + // datetime.Format( wxLocale::GetInfo( wxLOCALE_SHORT_DATE_FMT ) ); + // datetime.Format( wxLocale::GetInfo( wxLOCALE_LONG_DATE_FMT ) ); + // datetime.Format( wxT("%Y-%b-%d") ); + datetime.FormatISODate(); + + m_TextDate->SetValue( date ); +} + + +bool DIALOG_PAGES_SETTINGS::SavePageSettings() +{ + bool retSuccess = false; + + wxString fileName = GetWksFileName(); + + if( fileName != BASE_SCREEN::m_PageLayoutDescrFileName ) + { + wxString fullFileName = + WORKSHEET_LAYOUT::MakeFullFileName( fileName, m_projectPath ); + + if( !fullFileName.IsEmpty() ) + { + + if( !wxFileExists( fullFileName ) ) + { + wxString msg; + msg.Printf( _("Page layout description file <%s> not found. Abort"), + GetChars( fullFileName ) ); + wxMessageBox( msg ); + return false; + } + } + + BASE_SCREEN::m_PageLayoutDescrFileName = fileName; + WORKSHEET_LAYOUT& pglayout = WORKSHEET_LAYOUT::GetTheInstance(); + pglayout.SetPageLayout( fullFileName ); + m_localPrjConfigChanged = true; + } + + int idx = m_paperSizeComboBox->GetSelection(); + + if( idx < 0 ) + idx = 0; + + const wxString paperType = m_pageFmt[idx]; + + if( paperType.Contains( PAGE_INFO::Custom ) ) + { + GetCustomSizeMilsFromDialog(); + + retSuccess = m_pageInfo.SetType( PAGE_INFO::Custom ); + + if( retSuccess ) + { + if( m_layout_size.x < MIN_PAGE_SIZE || m_layout_size.y < MIN_PAGE_SIZE || + m_layout_size.x > MAX_PAGE_SIZE || m_layout_size.y > MAX_PAGE_SIZE ) + { + wxString msg = wxString::Format( _( "Selected custom paper size\nis out of the permissible \ +limits\n%.1f - %.1f %s!\nSelect another custom paper size?" ), + g_UserUnit == INCHES ? MIN_PAGE_SIZE / 1000. : MIN_PAGE_SIZE * 25.4 / 1000, + g_UserUnit == INCHES ? MAX_PAGE_SIZE / 1000. : MAX_PAGE_SIZE * 25.4 / 1000, + g_UserUnit == INCHES ? _( "inches" ) : _( "mm" ) ); + + if( wxMessageBox( msg, _( "Warning!" ), wxYES_NO | wxICON_EXCLAMATION, this ) == wxYES ) + { + return false; + } + + m_layout_size.x = Clamp( MIN_PAGE_SIZE, m_layout_size.x, MAX_PAGE_SIZE ); + m_layout_size.y = Clamp( MIN_PAGE_SIZE, m_layout_size.y, MAX_PAGE_SIZE ); + } + + PAGE_INFO::SetCustomWidthMils( m_layout_size.x ); + PAGE_INFO::SetCustomHeightMils( m_layout_size.y ); + + m_pageInfo.SetWidthMils( m_layout_size.x ); + m_pageInfo.SetHeightMils( m_layout_size.y ); + } + } + else + { + // search for longest common string first, e.g. A4 before A + if( paperType.Contains( PAGE_INFO::USLetter ) ) + retSuccess = m_pageInfo.SetType( PAGE_INFO::USLetter ); + else if( paperType.Contains( PAGE_INFO::USLegal ) ) + retSuccess = m_pageInfo.SetType( PAGE_INFO::USLegal ); + else if( paperType.Contains( PAGE_INFO::USLedger ) ) + retSuccess = m_pageInfo.SetType( PAGE_INFO::USLedger ); + else if( paperType.Contains( PAGE_INFO::GERBER ) ) + retSuccess = m_pageInfo.SetType( PAGE_INFO::GERBER ); + else if( paperType.Contains( PAGE_INFO::A4 ) ) + retSuccess = m_pageInfo.SetType( PAGE_INFO::A4 ); + else if( paperType.Contains( PAGE_INFO::A3 ) ) + retSuccess = m_pageInfo.SetType( PAGE_INFO::A3 ); + else if( paperType.Contains( PAGE_INFO::A2 ) ) + retSuccess = m_pageInfo.SetType( PAGE_INFO::A2 ); + else if( paperType.Contains( PAGE_INFO::A1 ) ) + retSuccess = m_pageInfo.SetType( PAGE_INFO::A1 ); + else if( paperType.Contains( PAGE_INFO::A0 ) ) + retSuccess = m_pageInfo.SetType( PAGE_INFO::A0 ); + else if( paperType.Contains( PAGE_INFO::A ) ) + retSuccess = m_pageInfo.SetType( PAGE_INFO::A ); + else if( paperType.Contains( PAGE_INFO::B ) ) + retSuccess = m_pageInfo.SetType( PAGE_INFO::B ); + else if( paperType.Contains( PAGE_INFO::C ) ) + retSuccess = m_pageInfo.SetType( PAGE_INFO::C ); + else if( paperType.Contains( PAGE_INFO::D ) ) + retSuccess = m_pageInfo.SetType( PAGE_INFO::D ); + else if( paperType.Contains( PAGE_INFO::E ) ) + retSuccess = m_pageInfo.SetType( PAGE_INFO::E ); + + if( retSuccess ) + { + int choice = m_orientationComboBox->GetSelection(); + m_pageInfo.SetPortrait( choice != 0 ); + } + } + + if( !retSuccess ) + { + wxASSERT_MSG( false, wxT( "the translation for paper size must preserve original spellings" ) ); + m_pageInfo.SetType( PAGE_INFO::A4 ); + } + + m_parent->SetPageSettings( m_pageInfo ); + + m_tb.SetRevision( m_TextRevision->GetValue() ); + m_tb.SetDate( m_TextDate->GetValue() ); + m_tb.SetCompany( m_TextCompany->GetValue() ); + m_tb.SetTitle( m_TextTitle->GetValue() ); + m_tb.SetComment1( m_TextComment1->GetValue() ); + m_tb.SetComment2( m_TextComment2->GetValue() ); + m_tb.SetComment3( m_TextComment3->GetValue() ); + m_tb.SetComment4( m_TextComment4->GetValue() ); + + m_parent->SetTitleBlock( m_tb ); + + +#ifdef EESCHEMA + // Exports settings to other sheets if requested: + SCH_SCREEN* screen; + + // Build the screen list + SCH_SCREENS ScreenList; + + // Update title blocks for all screens + for( screen = ScreenList.GetFirst(); screen != NULL; screen = ScreenList.GetNext() ) + { + if( screen == m_screen ) + continue; + + TITLE_BLOCK tb2 = screen->GetTitleBlock(); + + if( m_RevisionExport->IsChecked() ) + tb2.SetRevision( m_tb.GetRevision() ); + + if( m_DateExport->IsChecked() ) + tb2.SetDate( m_tb.GetDate() ); + + if( m_TitleExport->IsChecked() ) + tb2.SetTitle( m_tb.GetTitle() ); + + if( m_CompanyExport->IsChecked() ) + tb2.SetCompany( m_tb.GetCompany() ); + + if( m_Comment1Export->IsChecked() ) + tb2.SetComment1( m_tb.GetComment1() ); + + if( m_Comment2Export->IsChecked() ) + tb2.SetComment2( m_tb.GetComment2() ); + + if( m_Comment3Export->IsChecked() ) + tb2.SetComment3( m_tb.GetComment3() ); + + if( m_Comment4Export->IsChecked() ) + tb2.SetComment4( m_tb.GetComment4() ); + + screen->SetTitleBlock( tb2 ); + } + +#endif + + return true; +} + + +void DIALOG_PAGES_SETTINGS::SetCurrentPageSizeSelection( const wxString& aPaperSize ) +{ + // search all the not translated label list containing our paper type + for( unsigned i = 0; i < m_pageFmt.GetCount(); ++i ) + { + // parse each label looking for aPaperSize within it + wxStringTokenizer st( m_pageFmt[i] ); + + while( st.HasMoreTokens() ) + { + if( st.GetNextToken() == aPaperSize ) + { + m_paperSizeComboBox->SetSelection( i ); + return; + } + } + } +} + + +void DIALOG_PAGES_SETTINGS::UpdatePageLayoutExample() +{ + int lyWidth, lyHeight; + + wxSize clamped_layout_size( Clamp( MIN_PAGE_SIZE, m_layout_size.x, MAX_PAGE_SIZE ), + Clamp( MIN_PAGE_SIZE, m_layout_size.y, MAX_PAGE_SIZE ) ); + + double lyRatio = clamped_layout_size.x < clamped_layout_size.y ? + (double) clamped_layout_size.y / clamped_layout_size.x : + (double) clamped_layout_size.x / clamped_layout_size.y; + + if( clamped_layout_size.x < clamped_layout_size.y ) + { + lyHeight = MAX_PAGE_EXAMPLE_SIZE; + lyWidth = KiROUND( (double) lyHeight / lyRatio ); + } + else + { + lyWidth = MAX_PAGE_EXAMPLE_SIZE; + lyHeight = KiROUND( (double) lyWidth / lyRatio ); + } + + if( m_page_bitmap ) + { + m_PageLayoutExampleBitmap->SetBitmap( wxNullBitmap ); + delete m_page_bitmap; + } + + m_page_bitmap = new wxBitmap( lyWidth + 1, lyHeight + 1 ); + + if( m_page_bitmap->IsOk() ) + { + // Calculate layout preview scale. + int appScale = m_screen->MilsToIuScalar(); + + double scaleW = (double) lyWidth / clamped_layout_size.x / appScale; + double scaleH = (double) lyHeight / clamped_layout_size.y / appScale; + + // Prepare DC. + wxSize example_size( lyWidth + 1, lyHeight + 1 ); + wxMemoryDC memDC; + memDC.SelectObject( *m_page_bitmap ); + memDC.SetClippingRegion( wxPoint( 0, 0 ), example_size ); + memDC.Clear(); + memDC.SetUserScale( scaleW, scaleH ); + + // Get logical page size and margins. + PAGE_INFO pageDUMMY; + + // Get page type + int idx = m_paperSizeComboBox->GetSelection(); + + if( idx < 0 ) + idx = 0; + + wxString pageFmtName = m_pageFmt[idx].BeforeFirst( ' ' ); + bool portrait = clamped_layout_size.x < clamped_layout_size.y; + pageDUMMY.SetType( pageFmtName, portrait ); + if( m_customFmt ) + { + pageDUMMY.SetWidthMils( clamped_layout_size.x ); + pageDUMMY.SetHeightMils( clamped_layout_size.y ); + } + + // Draw layout preview. + wxString emptyString; + GRResetPenAndBrush( &memDC ); + + WORKSHEET_LAYOUT::SetAltInstance( m_pagelayout ); + DrawPageLayout( &memDC, NULL, pageDUMMY, + emptyString, emptyString, + m_tb, m_screen->m_NumberOfScreens, + m_screen->m_ScreenNumber, 1, appScale, DARKGRAY, RED ); + + memDC.SelectObject( wxNullBitmap ); + m_PageLayoutExampleBitmap->SetBitmap( *m_page_bitmap ); + WORKSHEET_LAYOUT::SetAltInstance( NULL ); + + // Refresh the dialog. + Layout(); + Refresh(); + } +} + + +void DIALOG_PAGES_SETTINGS::GetPageLayoutInfoFromDialog() +{ + int idx = m_paperSizeComboBox->GetSelection(); + + if( idx < 0 ) + idx = 0; + + const wxString paperType = m_pageFmt[idx]; + + // here we assume translators will keep original paper size spellings + if( paperType.Contains( PAGE_INFO::Custom ) ) + { + GetCustomSizeMilsFromDialog(); + + if( m_layout_size.x && m_layout_size.y ) + { + if( m_layout_size.x < m_layout_size.y ) + m_orientationComboBox->SetStringSelection( _( "Portrait" ) ); + else + m_orientationComboBox->SetStringSelection( _( "Landscape" ) ); + } + } + else + { + PAGE_INFO pageInfo; // SetType() later to lookup size + + static const wxChar* papers[] = { + // longest common string first, since sequential search below + PAGE_INFO::A4, + PAGE_INFO::A3, + PAGE_INFO::A2, + PAGE_INFO::A1, + PAGE_INFO::A0, + PAGE_INFO::A, + PAGE_INFO::B, + PAGE_INFO::C, + PAGE_INFO::D, + PAGE_INFO::E, + PAGE_INFO::USLetter, + PAGE_INFO::USLegal, + PAGE_INFO::USLedger, + }; + + unsigned i; + + for( i=0; i < DIM( papers ); ++i ) + { + if( paperType.Contains( papers[i] ) ) + { + pageInfo.SetType( papers[i] ); + break; + } + } + + wxASSERT( i != DIM(papers) ); // dialog UI match the above list? + + m_layout_size = pageInfo.GetSizeMils(); + + // swap sizes to match orientation + bool isPortrait = (bool) m_orientationComboBox->GetSelection(); + + if( ( isPortrait && m_layout_size.x >= m_layout_size.y ) || + ( !isPortrait && m_layout_size.x < m_layout_size.y ) ) + { + m_layout_size.Set( m_layout_size.y, m_layout_size.x ); + } + } +} + + +void DIALOG_PAGES_SETTINGS::GetCustomSizeMilsFromDialog() +{ + double customSizeX; + double customSizeY; + wxString msg; + + msg = m_TextUserSizeX->GetValue(); + msg.ToDouble( &customSizeX ); + + msg = m_TextUserSizeY->GetValue(); + msg.ToDouble( &customSizeY ); + + switch( g_UserUnit ) + { + case MILLIMETRES: + customSizeX *= 1000. / 25.4; + customSizeY *= 1000. / 25.4; + break; + + default: + case INCHES: + customSizeX *= 1000.; + customSizeY *= 1000.; + } + + // Prepare to painless double -> int conversion. + customSizeX = Clamp( double( INT_MIN ), customSizeX, double( INT_MAX ) ); + customSizeY = Clamp( double( INT_MIN ), customSizeY, double( INT_MAX ) ); + m_layout_size = wxSize( KiROUND( customSizeX ), KiROUND( customSizeY ) ); +} + +// Called on .kicad_wks file description selection change +void DIALOG_PAGES_SETTINGS::OnWksFileSelection( wxCommandEvent& event ) +{ + // Display a file picker dialog + wxFileDialog fileDialog( this, _( "Select Page Layout Descr File" ), + m_projectPath, GetWksFileName(), + PageLayoutDescrFileWildcard, + wxFD_DEFAULT_STYLE | wxFD_FILE_MUST_EXIST ); + + if( fileDialog.ShowModal() != wxID_OK ) + return; + + wxString fileName = fileDialog.GetPath(); + + // Try to remove the path, if the path is the current working dir, + // or the dir of kicad.pro (template), and use a relative path + wxString shortFileName = WORKSHEET_LAYOUT::MakeShortFileName( fileName, m_projectPath ); + + // For Win/Linux/macOS compatibility, a relative path is a good idea + if( shortFileName != GetWksFileName() && shortFileName != fileName ) + { + wxString msg = wxString::Format( _( + "The page layout descr filename has changed.\n" + "Do you want to use the relative path:\n" + "'%s'\n" + "instead of\n" + "'%s'" ), GetChars( shortFileName ), GetChars( fileName ) ); + + if( !IsOK( this, msg ) ) + shortFileName = fileName; + } + + SetWksFileName( shortFileName ); + + if( m_pagelayout == NULL ) + m_pagelayout = new WORKSHEET_LAYOUT; + + m_pagelayout->SetPageLayout( fileName ); + + GetPageLayoutInfoFromDialog(); + UpdatePageLayoutExample(); +} diff --git a/common/dialogs/dialog_page_settings.h b/common/dialogs/dialog_page_settings.h new file mode 100644 index 0000000..5127886 --- /dev/null +++ b/common/dialogs/dialog_page_settings.h @@ -0,0 +1,120 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 1992-2013 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_PAGES_SETTINGS_H_ +#define _DIALOG_PAGES_SETTINGS_H_ + +#include <dialog_page_settings_base.h> + +#define MAX_PAGE_EXAMPLE_SIZE 200 + +/*! + * DIALOG_PAGES_SETTINGS class declaration + */ + +class DIALOG_PAGES_SETTINGS: public DIALOG_PAGES_SETTINGS_BASE +{ +private: + EDA_DRAW_FRAME* m_parent; + BASE_SCREEN* m_screen; + wxString m_projectPath; // the curr project path + wxArrayString m_pageFmt; /// list of page sizes (not translated) + bool m_initialized; + bool m_localPrjConfigChanged; /// the page layuout filename was changed + wxBitmap* m_page_bitmap; /// Temporary bitmap for the page layout example. + wxSize m_layout_size; /// Logical page layout size. + PAGE_INFO m_pageInfo; /// Temporary page info. + bool m_customFmt; /// true if the page selection is custom + TITLE_BLOCK m_tb; /// Temporary title block (basic inscriptions). + WORKSHEET_LAYOUT *m_pagelayout; // the alternate and temporary page layout shown by the dialog + // when the initial one is replaced by a new one + +public: + DIALOG_PAGES_SETTINGS( EDA_DRAW_FRAME* parent ); + ~DIALOG_PAGES_SETTINGS(); + + const wxString GetWksFileName() + { + return m_textCtrlFilePicker->GetValue(); + } + + void SetWksFileName(const wxString& aFilename ) + { + m_textCtrlFilePicker->SetValue( aFilename ); + } + + void EnableWksFileNamePicker( bool aEnable ) + { + m_textCtrlFilePicker->Enable( aEnable ); + m_buttonBrowse->Enable( aEnable ); + } + +private: + void initDialog(); // Initialisation of member variables + + // event handler for button OK + void OnOkClick( wxCommandEvent& event ); + + // event handlers for page size choice + void OnPaperSizeChoice( wxCommandEvent& event ); + void OnUserPageSizeXTextUpdated( wxCommandEvent& event ); + void OnUserPageSizeYTextUpdated( wxCommandEvent& event ); + void OnPageOrientationChoice( wxCommandEvent& event ); + + // event handler for texts in title block + void OnRevisionTextUpdated( wxCommandEvent& event ); + void OnDateTextUpdated( wxCommandEvent& event ); + void OnTitleTextUpdated( wxCommandEvent& event ); + void OnCompanyTextUpdated( wxCommandEvent& event ); + void OnComment1TextUpdated( wxCommandEvent& event ); + void OnComment2TextUpdated( wxCommandEvent& event ); + void OnComment3TextUpdated( wxCommandEvent& event ); + void OnComment4TextUpdated( wxCommandEvent& event ); + + // Handle button click for setting the date from the picker + void OnDateApplyClick( wxCommandEvent& event ); + + // .kicad_wks file description selection + void OnWksFileSelection( wxCommandEvent& event ); + + // Save in the current title block the new page settings + // return true if changes are made, or false if not + bool SavePageSettings(); + + void SetCurrentPageSizeSelection( const wxString& aPaperSize ); + + // Update page layout example + void UpdatePageLayoutExample(); + + // Get page layout info from selected dialog items + void GetPageLayoutInfoFromDialog(); + + // Get custom page size in mils from dialog + void GetCustomSizeMilsFromDialog(); + + /// @return true if the local prj config is chande + /// i.e. if the page layout descr file has chnaged + bool LocalPrjConfigChanged() { return m_localPrjConfigChanged; } +}; + +#endif // _DIALOG_PAGES_SETTINGS_H_ diff --git a/common/dialogs/dialog_page_settings_base.cpp b/common/dialogs/dialog_page_settings_base.cpp new file mode 100644 index 0000000..197a255 --- /dev/null +++ b/common/dialogs/dialog_page_settings_base.cpp @@ -0,0 +1,423 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Mar 9 2015) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "dialog_page_settings_base.h" + +/////////////////////////////////////////////////////////////////////////// + +DIALOG_PAGES_SETTINGS_BASE::DIALOG_PAGES_SETTINGS_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( wxDefaultSize, wxDefaultSize ); + + wxBoxSizer* bMainSizer; + bMainSizer = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bUpperSizerH; + bUpperSizerH = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bleftSizer; + bleftSizer = new wxBoxSizer( wxVERTICAL ); + + m_staticTextPaper = new wxStaticText( this, wxID_ANY, _("Paper"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextPaper->Wrap( -1 ); + bleftSizer->Add( m_staticTextPaper, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); + + m_staticline2 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bleftSizer->Add( m_staticline2, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 ); + + m_staticTextSize = new wxStaticText( this, wxID_ANY, _("Size:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextSize->Wrap( -1 ); + bleftSizer->Add( m_staticTextSize, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); + + wxString m_paperSizeComboBoxChoices[] = { _("dummy text") }; + int m_paperSizeComboBoxNChoices = sizeof( m_paperSizeComboBoxChoices ) / sizeof( wxString ); + m_paperSizeComboBox = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_paperSizeComboBoxNChoices, m_paperSizeComboBoxChoices, 0 ); + m_paperSizeComboBox->SetSelection( 0 ); + bleftSizer->Add( m_paperSizeComboBox, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + m_staticTextOrient = new wxStaticText( this, wxID_ANY, _("Orientation:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextOrient->Wrap( -1 ); + bleftSizer->Add( m_staticTextOrient, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); + + wxString m_orientationComboBoxChoices[] = { _("Landscape"), _("Portrait") }; + int m_orientationComboBoxNChoices = sizeof( m_orientationComboBoxChoices ) / sizeof( wxString ); + m_orientationComboBox = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_orientationComboBoxNChoices, m_orientationComboBoxChoices, 0 ); + m_orientationComboBox->SetSelection( 0 ); + bleftSizer->Add( m_orientationComboBox, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + m_staticTextCustSize = new wxStaticText( this, wxID_ANY, _("Custom Size:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextCustSize->Wrap( -1 ); + bleftSizer->Add( m_staticTextCustSize, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizerCustSize; + bSizerCustSize = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizercustHeight; + bSizercustHeight = new wxBoxSizer( wxVERTICAL ); + + m_staticTextHeight = new wxStaticText( this, wxID_ANY, _("Height:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextHeight->Wrap( -1 ); + bSizercustHeight->Add( m_staticTextHeight, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); + + m_TextUserSizeY = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_LEFT ); + m_TextUserSizeY->SetMaxLength( 6 ); + m_TextUserSizeY->SetToolTip( _("Custom paper height.") ); + + bSizercustHeight->Add( m_TextUserSizeY, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizerCustSize->Add( bSizercustHeight, 1, wxEXPAND, 5 ); + + wxBoxSizer* bSizercustWidth; + bSizercustWidth = new wxBoxSizer( wxVERTICAL ); + + m_staticTextWidth = new wxStaticText( this, wxID_ANY, _("Width:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextWidth->Wrap( -1 ); + bSizercustWidth->Add( m_staticTextWidth, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); + + m_TextUserSizeX = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_LEFT ); + m_TextUserSizeX->SetMaxLength( 6 ); + m_TextUserSizeX->SetToolTip( _("Custom paper width.") ); + + bSizercustWidth->Add( m_TextUserSizeX, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizerCustSize->Add( bSizercustWidth, 1, wxEXPAND, 5 ); + + + bleftSizer->Add( bSizerCustSize, 0, wxEXPAND, 5 ); + + m_staticTextPreview = new wxStaticText( this, wxID_ANY, _("Layout Preview"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextPreview->Wrap( -1 ); + bleftSizer->Add( m_staticTextPreview, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_PageLayoutExampleBitmap = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxFULL_REPAINT_ON_RESIZE|wxSIMPLE_BORDER ); + m_PageLayoutExampleBitmap->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_PageLayoutExampleBitmap->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + bleftSizer->Add( m_PageLayoutExampleBitmap, 1, wxALL|wxEXPAND, 5 ); + + + bUpperSizerH->Add( bleftSizer, 0, wxEXPAND, 5 ); + + m_staticline1 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bUpperSizerH->Add( m_staticline1, 0, wxEXPAND|wxTOP|wxBOTTOM, 5 ); + + wxBoxSizer* bSizerRight; + bSizerRight = new wxBoxSizer( wxVERTICAL ); + + m_staticTexttbprm = new wxStaticText( this, wxID_ANY, _("Title Block Parameters"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTexttbprm->Wrap( -1 ); + bSizerRight->Add( m_staticTexttbprm, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_staticline3 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizerRight->Add( m_staticline3, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* SheetInfoSizer; + SheetInfoSizer = new wxBoxSizer( wxHORIZONTAL ); + + m_TextSheetCount = new wxStaticText( this, wxID_ANY, _("Number of sheets: %d"), wxDefaultPosition, wxDefaultSize, 0 ); + m_TextSheetCount->Wrap( -1 ); + SheetInfoSizer->Add( m_TextSheetCount, 0, wxALL, 5 ); + + + SheetInfoSizer->Add( 5, 5, 1, wxEXPAND, 5 ); + + m_TextSheetNumber = new wxStaticText( this, wxID_ANY, _("Sheet number: %d"), wxDefaultPosition, wxDefaultSize, 0 ); + m_TextSheetNumber->Wrap( -1 ); + SheetInfoSizer->Add( m_TextSheetNumber, 0, wxALL, 5 ); + + + bSizerRight->Add( SheetInfoSizer, 0, 0, 5 ); + + wxBoxSizer* bSizerDate; + bSizerDate = new wxBoxSizer( wxVERTICAL ); + + m_staticTextDate = new wxStaticText( this, wxID_ANY, _("Issue Date"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextDate->Wrap( -1 ); + bSizerDate->Add( m_staticTextDate, 0, wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizerissuedate; + bSizerissuedate = new wxBoxSizer( wxHORIZONTAL ); + + m_TextDate = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_TextDate->SetMaxLength( 0 ); + m_TextDate->SetMinSize( wxSize( 100,-1 ) ); + + bSizerissuedate->Add( m_TextDate, 3, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + m_ApplyDate = new wxButton( this, wxID_ANY, _("<<<"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT ); + bSizerissuedate->Add( m_ApplyDate, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + m_PickDate = new wxDatePickerCtrl( this, wxID_ANY, wxDefaultDateTime, wxDefaultPosition, wxDefaultSize, wxDP_DEFAULT ); + bSizerissuedate->Add( m_PickDate, 2, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + m_DateExport = new wxCheckBox( this, wxID_ANY, _("Export to other sheets"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizerissuedate->Add( m_DateExport, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + + bSizerDate->Add( bSizerissuedate, 1, wxEXPAND, 5 ); + + + bSizerRight->Add( bSizerDate, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizerRev; + bSizerRev = new wxBoxSizer( wxVERTICAL ); + + m_staticTextRev = new wxStaticText( this, wxID_ANY, _("Revision"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextRev->Wrap( -1 ); + bSizerRev->Add( m_staticTextRev, 0, wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizer9; + bSizer9 = new wxBoxSizer( wxHORIZONTAL ); + + m_TextRevision = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_TextRevision->SetMaxLength( 0 ); + m_TextRevision->SetMinSize( wxSize( 100,-1 ) ); + + bSizer9->Add( m_TextRevision, 1, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + m_RevisionExport = new wxCheckBox( this, wxID_ANY, _("Export to other sheets"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer9->Add( m_RevisionExport, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + + bSizerRev->Add( bSizer9, 1, wxEXPAND, 5 ); + + + bSizerRight->Add( bSizerRev, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizerTitle; + bSizerTitle = new wxBoxSizer( wxVERTICAL ); + + m_staticTextTitle = new wxStaticText( this, wxID_ANY, _("Title"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextTitle->Wrap( -1 ); + bSizerTitle->Add( m_staticTextTitle, 0, wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizer12; + bSizer12 = new wxBoxSizer( wxHORIZONTAL ); + + m_TextTitle = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_TextTitle->SetMaxLength( 0 ); + m_TextTitle->SetMinSize( wxSize( 360,-1 ) ); + + bSizer12->Add( m_TextTitle, 1, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + m_TitleExport = new wxCheckBox( this, wxID_ANY, _("Export to other sheets"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer12->Add( m_TitleExport, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + + bSizerTitle->Add( bSizer12, 1, wxEXPAND, 5 ); + + + bSizerRight->Add( bSizerTitle, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizerCompany; + bSizerCompany = new wxBoxSizer( wxVERTICAL ); + + m_staticText13 = new wxStaticText( this, wxID_ANY, _("Company"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText13->Wrap( -1 ); + bSizerCompany->Add( m_staticText13, 0, wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizer14; + bSizer14 = new wxBoxSizer( wxHORIZONTAL ); + + m_TextCompany = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_TextCompany->SetMaxLength( 0 ); + m_TextCompany->SetMinSize( wxSize( 360,-1 ) ); + + bSizer14->Add( m_TextCompany, 1, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + m_CompanyExport = new wxCheckBox( this, wxID_ANY, _("Export to other sheets"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer14->Add( m_CompanyExport, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + + bSizerCompany->Add( bSizer14, 1, wxEXPAND, 5 ); + + + bSizerRight->Add( bSizerCompany, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizerComment1; + bSizerComment1 = new wxBoxSizer( wxVERTICAL ); + + m_staticTextComment1 = new wxStaticText( this, wxID_ANY, _("Comment1"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextComment1->Wrap( -1 ); + bSizerComment1->Add( m_staticTextComment1, 0, wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizercmt1; + bSizercmt1 = new wxBoxSizer( wxHORIZONTAL ); + + m_TextComment1 = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_TextComment1->SetMaxLength( 0 ); + m_TextComment1->SetMinSize( wxSize( 360,-1 ) ); + + bSizercmt1->Add( m_TextComment1, 1, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + m_Comment1Export = new wxCheckBox( this, wxID_ANY, _("Export to other sheets"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizercmt1->Add( m_Comment1Export, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + + bSizerComment1->Add( bSizercmt1, 1, wxEXPAND, 5 ); + + + bSizerRight->Add( bSizerComment1, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizerComment2; + bSizerComment2 = new wxBoxSizer( wxVERTICAL ); + + m_staticTextComment2 = new wxStaticText( this, wxID_ANY, _("Comment2"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextComment2->Wrap( -1 ); + bSizerComment2->Add( m_staticTextComment2, 0, wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizercmt2; + bSizercmt2 = new wxBoxSizer( wxHORIZONTAL ); + + m_TextComment2 = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_TextComment2->SetMaxLength( 0 ); + m_TextComment2->SetMinSize( wxSize( 360,-1 ) ); + + bSizercmt2->Add( m_TextComment2, 1, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + m_Comment2Export = new wxCheckBox( this, wxID_ANY, _("Export to other sheets"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizercmt2->Add( m_Comment2Export, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + + bSizerComment2->Add( bSizercmt2, 1, wxEXPAND, 5 ); + + + bSizerRight->Add( bSizerComment2, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizerComment12; + bSizerComment12 = new wxBoxSizer( wxVERTICAL ); + + m_staticTextComment3 = new wxStaticText( this, wxID_ANY, _("Comment3"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextComment3->Wrap( -1 ); + bSizerComment12->Add( m_staticTextComment3, 0, wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizercmt3; + bSizercmt3 = new wxBoxSizer( wxHORIZONTAL ); + + m_TextComment3 = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_TextComment3->SetMaxLength( 0 ); + m_TextComment3->SetMinSize( wxSize( 360,-1 ) ); + + bSizercmt3->Add( m_TextComment3, 1, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + m_Comment3Export = new wxCheckBox( this, wxID_ANY, _("Export to other sheets"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizercmt3->Add( m_Comment3Export, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + + bSizerComment12->Add( bSizercmt3, 1, wxEXPAND, 5 ); + + + bSizerRight->Add( bSizerComment12, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizerComment4; + bSizerComment4 = new wxBoxSizer( wxVERTICAL ); + + m_staticTextComment4 = new wxStaticText( this, wxID_ANY, _("Comment4"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextComment4->Wrap( -1 ); + bSizerComment4->Add( m_staticTextComment4, 0, wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizercmt4; + bSizercmt4 = new wxBoxSizer( wxHORIZONTAL ); + + m_TextComment4 = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_TextComment4->SetMaxLength( 0 ); + m_TextComment4->SetMinSize( wxSize( 360,-1 ) ); + + bSizercmt4->Add( m_TextComment4, 1, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + m_Comment4Export = new wxCheckBox( this, wxID_ANY, _("Export to other sheets"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizercmt4->Add( m_Comment4Export, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + + bSizerComment4->Add( bSizercmt4, 1, wxEXPAND, 5 ); + + + bSizerRight->Add( bSizerComment4, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizerFilename; + bSizerFilename = new wxBoxSizer( wxVERTICAL ); + + m_staticTextfilename = new wxStaticText( this, wxID_ANY, _("Page layout description file"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextfilename->Wrap( -1 ); + bSizerFilename->Add( m_staticTextfilename, 0, wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizerfileSelection; + bSizerfileSelection = new wxBoxSizer( wxHORIZONTAL ); + + m_textCtrlFilePicker = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + bSizerfileSelection->Add( m_textCtrlFilePicker, 1, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); + + m_buttonBrowse = new wxButton( this, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT ); + bSizerfileSelection->Add( m_buttonBrowse, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizerFilename->Add( bSizerfileSelection, 1, wxEXPAND, 5 ); + + + bSizerRight->Add( bSizerFilename, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 ); + + + bUpperSizerH->Add( bSizerRight, 1, wxEXPAND, 5 ); + + + bMainSizer->Add( bUpperSizerH, 1, wxEXPAND, 5 ); + + m_sdbSizer = new wxStdDialogButtonSizer(); + m_sdbSizerOK = new wxButton( this, wxID_OK ); + m_sdbSizer->AddButton( m_sdbSizerOK ); + m_sdbSizerCancel = new wxButton( this, wxID_CANCEL ); + m_sdbSizer->AddButton( m_sdbSizerCancel ); + m_sdbSizer->Realize(); + + bMainSizer->Add( m_sdbSizer, 0, wxALIGN_RIGHT|wxALL, 5 ); + + + this->SetSizer( bMainSizer ); + this->Layout(); + + // Connect Events + m_paperSizeComboBox->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnPaperSizeChoice ), NULL, this ); + m_orientationComboBox->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnPageOrientationChoice ), NULL, this ); + m_TextUserSizeY->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnUserPageSizeYTextUpdated ), NULL, this ); + m_TextUserSizeX->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnUserPageSizeXTextUpdated ), NULL, this ); + m_TextDate->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnDateTextUpdated ), NULL, this ); + m_ApplyDate->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnDateApplyClick ), NULL, this ); + m_TextRevision->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnRevisionTextUpdated ), NULL, this ); + m_TextTitle->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnTitleTextUpdated ), NULL, this ); + m_TitleExport->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnCheckboxTitleClick ), NULL, this ); + m_TextCompany->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnCompanyTextUpdated ), NULL, this ); + m_TextComment1->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnComment1TextUpdated ), NULL, this ); + m_TextComment2->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnComment2TextUpdated ), NULL, this ); + m_TextComment3->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnComment3TextUpdated ), NULL, this ); + m_TextComment4->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnComment4TextUpdated ), NULL, this ); + m_buttonBrowse->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnWksFileSelection ), NULL, this ); + m_sdbSizerOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnOkClick ), NULL, this ); +} + +DIALOG_PAGES_SETTINGS_BASE::~DIALOG_PAGES_SETTINGS_BASE() +{ + // Disconnect Events + m_paperSizeComboBox->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnPaperSizeChoice ), NULL, this ); + m_orientationComboBox->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnPageOrientationChoice ), NULL, this ); + m_TextUserSizeY->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnUserPageSizeYTextUpdated ), NULL, this ); + m_TextUserSizeX->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnUserPageSizeXTextUpdated ), NULL, this ); + m_TextDate->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnDateTextUpdated ), NULL, this ); + m_ApplyDate->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnDateApplyClick ), NULL, this ); + m_TextRevision->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnRevisionTextUpdated ), NULL, this ); + m_TextTitle->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnTitleTextUpdated ), NULL, this ); + m_TitleExport->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnCheckboxTitleClick ), NULL, this ); + m_TextCompany->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnCompanyTextUpdated ), NULL, this ); + m_TextComment1->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnComment1TextUpdated ), NULL, this ); + m_TextComment2->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnComment2TextUpdated ), NULL, this ); + m_TextComment3->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnComment3TextUpdated ), NULL, this ); + m_TextComment4->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnComment4TextUpdated ), NULL, this ); + m_buttonBrowse->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnWksFileSelection ), NULL, this ); + m_sdbSizerOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAGES_SETTINGS_BASE::OnOkClick ), NULL, this ); + +} diff --git a/common/dialogs/dialog_page_settings_base.fbp b/common/dialogs/dialog_page_settings_base.fbp new file mode 100644 index 0000000..a456426 --- /dev/null +++ b/common/dialogs/dialog_page_settings_base.fbp @@ -0,0 +1,4454 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<wxFormBuilder_Project> + <FileVersion major="1" minor="13" /> + <object class="Project" expanded="1"> + <property name="class_decoration"></property> + <property name="code_generation">C++</property> + <property name="disconnect_events">1</property> + <property name="disconnect_mode">source_name</property> + <property name="disconnect_php_events">0</property> + <property name="disconnect_python_events">0</property> + <property name="embedded_files_path">res</property> + <property name="encoding">UTF-8</property> + <property name="event_generation">connect</property> + <property name="file">dialog_page_settings_base</property> + <property name="first_id">1000</property> + <property name="help_provider">none</property> + <property name="internationalize">1</property> + <property name="name">dialog_page_settings_base</property> + <property name="namespace"></property> + <property name="path">.</property> + <property name="precompiled_header"></property> + <property name="relative_path">1</property> + <property name="skip_lua_events">1</property> + <property name="skip_php_events">1</property> + <property name="skip_python_events">1</property> + <property name="ui_table">UI</property> + <property name="use_enum">0</property> + <property name="use_microsoft_bom">0</property> + <object class="Dialog" expanded="1"> + <property name="aui_managed">0</property> + <property name="aui_manager_style">wxAUI_MGR_DEFAULT</property> + <property name="bg"></property> + <property name="center"></property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="enabled">1</property> + <property name="event_handler">impl_virtual</property> + <property name="extra_style"></property> + <property name="fg"></property> + <property name="font"></property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="maximum_size"></property> + <property name="minimum_size"></property> + <property name="name">DIALOG_PAGES_SETTINGS_BASE</property> + <property name="pos"></property> + <property name="size">748,470</property> + <property name="style">wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER</property> + <property name="subclass">DIALOG_SHIM; dialog_shim.h</property> + <property name="title">Page Settings</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnActivate"></event> + <event name="OnActivateApp"></event> + <event name="OnAuiFindManager"></event> + <event name="OnAuiPaneButton"></event> + <event name="OnAuiPaneClose"></event> + <event name="OnAuiPaneMaximize"></event> + <event name="OnAuiPaneRestore"></event> + <event name="OnAuiRender"></event> + <event name="OnChar"></event> + <event name="OnClose"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnHibernate"></event> + <event name="OnIconize"></event> + <event name="OnIdle"></event> + <event name="OnInitDialog"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size">-1,-1</property> + <property name="name">bMainSizer</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bUpperSizerH</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">0</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bleftSizer</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_HORIZONTAL|wxALL</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Paper</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextPaper</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticLine" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticline2</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style">wxLI_HORIZONTAL</property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxTOP|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Size:</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextSize</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxChoice" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="choices">"dummy text"</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_paperSizeComboBox</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="selection">0</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnChoice">OnPaperSizeChoice</event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxTOP|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Orientation:</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextOrient</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxChoice" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="choices">"Landscape" "Portrait"</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_orientationComboBox</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="selection">0</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnChoice">OnPageOrientationChoice</event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxTOP|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Custom Size:</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextCustSize</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">0</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerCustSize</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizercustHeight</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxTOP|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Height:</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextHeight</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxTextCtrl" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="maxlength">6</property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_TextUserSizeY</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style">wxTE_LEFT</property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip">Custom paper height.</property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="value"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnText">OnUserPageSizeYTextUpdated</event> + <event name="OnTextEnter"></event> + <event name="OnTextMaxLen"></event> + <event name="OnTextURL"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizercustWidth</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxTOP|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Width:</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextWidth</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxTextCtrl" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="maxlength">6</property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_TextUserSizeX</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style">wxTE_LEFT</property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip">Custom paper width.</property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxTextValidator</property> + <property name="validator_variable"></property> + <property name="value"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnText">OnUserPageSizeXTextUpdated</event> + <event name="OnTextEnter"></event> + <event name="OnTextMaxLen"></event> + <event name="OnTextURL"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxALIGN_CENTER_HORIZONTAL</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Layout Preview</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextPreview</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALL|wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxStaticBitmap" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg">wxSYS_COLOUR_WINDOW</property> + <property name="bitmap">Load From Icon Resource; </property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg">wxSYS_COLOUR_WINDOW</property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size">-1,-1</property> + <property name="moveable">1</property> + <property name="name">m_PageLayoutExampleBitmap</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size">-1,-1</property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style">wxFULL_REPAINT_ON_RESIZE|wxSIMPLE_BORDER</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxTOP|wxBOTTOM</property> + <property name="proportion">0</property> + <object class="wxStaticLine" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticline1</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style">wxLI_VERTICAL</property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerRight</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL|wxALIGN_CENTER_HORIZONTAL</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Title Block Parameters</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTexttbprm</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticLine" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticline3</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style">wxLI_HORIZONTAL</property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag"></property> + <property name="proportion">0</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">SheetInfoSizer</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALL</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Number of sheets: %d</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_TextSheetCount</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="spacer" expanded="0"> + <property name="height">5</property> + <property name="permission">protected</property> + <property name="width">5</property> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALL</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Sheet number: %d</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_TextSheetNumber</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerDate</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Issue Date</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextDate</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerissuedate</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT</property> + <property name="proportion">3</property> + <object class="wxTextCtrl" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="maxlength">0</property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size">100,-1</property> + <property name="moveable">1</property> + <property name="name">m_TextDate</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="value"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnText">OnDateTextUpdated</event> + <event name="OnTextEnter"></event> + <event name="OnTextMaxLen"></event> + <event name="OnTextURL"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default">0</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label"><<<</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_ApplyDate</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style">wxBU_EXACTFIT</property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnButtonClick">OnDateApplyClick</event> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT</property> + <property name="proportion">2</property> + <object class="wxDatePickerCtrl" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_PickDate</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style">wxDP_DEFAULT</property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnDateChanged"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT</property> + <property name="proportion">0</property> + <object class="wxCheckBox" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="checked">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Export to other sheets</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_DateExport</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnCheckBox"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerRev</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Revision</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextRev</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizer9</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT</property> + <property name="proportion">1</property> + <object class="wxTextCtrl" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="maxlength">0</property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size">100,-1</property> + <property name="moveable">1</property> + <property name="name">m_TextRevision</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="value"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnText">OnRevisionTextUpdated</event> + <event name="OnTextEnter"></event> + <event name="OnTextMaxLen"></event> + <event name="OnTextURL"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT</property> + <property name="proportion">0</property> + <object class="wxCheckBox" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="checked">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Export to other sheets</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_RevisionExport</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnCheckBox"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerTitle</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Title</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextTitle</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizer12</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT</property> + <property name="proportion">1</property> + <object class="wxTextCtrl" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="maxlength">0</property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size">360,-1</property> + <property name="moveable">1</property> + <property name="name">m_TextTitle</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="value"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnText">OnTitleTextUpdated</event> + <event name="OnTextEnter"></event> + <event name="OnTextMaxLen"></event> + <event name="OnTextURL"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT</property> + <property name="proportion">0</property> + <object class="wxCheckBox" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="checked">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Export to other sheets</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_TitleExport</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnCheckBox">OnCheckboxTitleClick</event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerCompany</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Company</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticText13</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizer14</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT</property> + <property name="proportion">1</property> + <object class="wxTextCtrl" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="maxlength">0</property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size">360,-1</property> + <property name="moveable">1</property> + <property name="name">m_TextCompany</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="value"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnText">OnCompanyTextUpdated</event> + <event name="OnTextEnter"></event> + <event name="OnTextMaxLen"></event> + <event name="OnTextURL"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT</property> + <property name="proportion">0</property> + <object class="wxCheckBox" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="checked">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Export to other sheets</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_CompanyExport</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnCheckBox"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerComment1</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Comment1</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextComment1</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizercmt1</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT</property> + <property name="proportion">1</property> + <object class="wxTextCtrl" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="maxlength">0</property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size">360,-1</property> + <property name="moveable">1</property> + <property name="name">m_TextComment1</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="value"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnText">OnComment1TextUpdated</event> + <event name="OnTextEnter"></event> + <event name="OnTextMaxLen"></event> + <event name="OnTextURL"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT</property> + <property name="proportion">0</property> + <object class="wxCheckBox" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="checked">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Export to other sheets</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_Comment1Export</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnCheckBox"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerComment2</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Comment2</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextComment2</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizercmt2</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT</property> + <property name="proportion">1</property> + <object class="wxTextCtrl" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="maxlength">0</property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size">360,-1</property> + <property name="moveable">1</property> + <property name="name">m_TextComment2</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="value"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnText">OnComment2TextUpdated</event> + <event name="OnTextEnter"></event> + <event name="OnTextMaxLen"></event> + <event name="OnTextURL"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT</property> + <property name="proportion">0</property> + <object class="wxCheckBox" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="checked">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Export to other sheets</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_Comment2Export</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnCheckBox"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerComment12</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Comment3</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextComment3</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizercmt3</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT</property> + <property name="proportion">1</property> + <object class="wxTextCtrl" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="maxlength">0</property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size">360,-1</property> + <property name="moveable">1</property> + <property name="name">m_TextComment3</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="value"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnText">OnComment3TextUpdated</event> + <event name="OnTextEnter"></event> + <event name="OnTextMaxLen"></event> + <event name="OnTextURL"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT</property> + <property name="proportion">0</property> + <object class="wxCheckBox" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="checked">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Export to other sheets</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_Comment3Export</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnCheckBox"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerComment4</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Comment4</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextComment4</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="0"> + <property name="minimum_size"></property> + <property name="name">bSizercmt4</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT</property> + <property name="proportion">1</property> + <object class="wxTextCtrl" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="maxlength">0</property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size">360,-1</property> + <property name="moveable">1</property> + <property name="name">m_TextComment4</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="value"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnText">OnComment4TextUpdated</event> + <event name="OnTextEnter"></event> + <event name="OnTextMaxLen"></event> + <event name="OnTextURL"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT</property> + <property name="proportion">0</property> + <object class="wxCheckBox" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="checked">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Export to other sheets</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_Comment4Export</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnCheckBox"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerFilename</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Page layout description file</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticTextfilename</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizerfileSelection</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL</property> + <property name="proportion">1</property> + <object class="wxTextCtrl" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="maxlength"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_textCtrlFilePicker</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="value"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnText"></event> + <event name="OnTextEnter"></event> + <event name="OnTextMaxLen"></event> + <event name="OnTextURL"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default">0</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Browse</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_buttonBrowse</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style">wxBU_EXACTFIT</property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnButtonClick">OnWksFileSelection</event> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + </object> + </object> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_RIGHT|wxALL</property> + <property name="proportion">0</property> + <object class="wxStdDialogButtonSizer" expanded="0"> + <property name="Apply">0</property> + <property name="Cancel">1</property> + <property name="ContextHelp">0</property> + <property name="Help">0</property> + <property name="No">0</property> + <property name="OK">1</property> + <property name="Save">0</property> + <property name="Yes">0</property> + <property name="minimum_size"></property> + <property name="name">m_sdbSizer</property> + <property name="permission">protected</property> + <event name="OnApplyButtonClick"></event> + <event name="OnCancelButtonClick"></event> + <event name="OnContextHelpButtonClick"></event> + <event name="OnHelpButtonClick"></event> + <event name="OnNoButtonClick"></event> + <event name="OnOKButtonClick">OnOkClick</event> + <event name="OnSaveButtonClick"></event> + <event name="OnYesButtonClick"></event> + </object> + </object> + </object> + </object> + </object> +</wxFormBuilder_Project> diff --git a/common/dialogs/dialog_page_settings_base.h b/common/dialogs/dialog_page_settings_base.h new file mode 100644 index 0000000..13ccf6f --- /dev/null +++ b/common/dialogs/dialog_page_settings_base.h @@ -0,0 +1,126 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Mar 9 2015) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __DIALOG_PAGE_SETTINGS_BASE_H__ +#define __DIALOG_PAGE_SETTINGS_BASE_H__ + +#include <wx/artprov.h> +#include <wx/xrc/xmlres.h> +#include <wx/intl.h> +class DIALOG_SHIM; + +#include "dialog_shim.h" +#include <wx/string.h> +#include <wx/stattext.h> +#include <wx/gdicmn.h> +#include <wx/font.h> +#include <wx/colour.h> +#include <wx/settings.h> +#include <wx/statline.h> +#include <wx/choice.h> +#include <wx/textctrl.h> +#include <wx/sizer.h> +#include <wx/valtext.h> +#include <wx/bitmap.h> +#include <wx/image.h> +#include <wx/icon.h> +#include <wx/statbmp.h> +#include <wx/button.h> +#include <wx/datectrl.h> +#include <wx/dateevt.h> +#include <wx/checkbox.h> +#include <wx/dialog.h> + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class DIALOG_PAGES_SETTINGS_BASE +/////////////////////////////////////////////////////////////////////////////// +class DIALOG_PAGES_SETTINGS_BASE : public DIALOG_SHIM +{ + private: + + protected: + wxStaticText* m_staticTextPaper; + wxStaticLine* m_staticline2; + wxStaticText* m_staticTextSize; + wxChoice* m_paperSizeComboBox; + wxStaticText* m_staticTextOrient; + wxChoice* m_orientationComboBox; + wxStaticText* m_staticTextCustSize; + wxStaticText* m_staticTextHeight; + wxTextCtrl* m_TextUserSizeY; + wxStaticText* m_staticTextWidth; + wxTextCtrl* m_TextUserSizeX; + wxStaticText* m_staticTextPreview; + wxStaticBitmap* m_PageLayoutExampleBitmap; + wxStaticLine* m_staticline1; + wxStaticText* m_staticTexttbprm; + wxStaticLine* m_staticline3; + wxStaticText* m_TextSheetCount; + wxStaticText* m_TextSheetNumber; + wxStaticText* m_staticTextDate; + wxTextCtrl* m_TextDate; + wxButton* m_ApplyDate; + wxDatePickerCtrl* m_PickDate; + wxCheckBox* m_DateExport; + wxStaticText* m_staticTextRev; + wxTextCtrl* m_TextRevision; + wxCheckBox* m_RevisionExport; + wxStaticText* m_staticTextTitle; + wxTextCtrl* m_TextTitle; + wxCheckBox* m_TitleExport; + wxStaticText* m_staticText13; + wxTextCtrl* m_TextCompany; + wxCheckBox* m_CompanyExport; + wxStaticText* m_staticTextComment1; + wxTextCtrl* m_TextComment1; + wxCheckBox* m_Comment1Export; + wxStaticText* m_staticTextComment2; + wxTextCtrl* m_TextComment2; + wxCheckBox* m_Comment2Export; + wxStaticText* m_staticTextComment3; + wxTextCtrl* m_TextComment3; + wxCheckBox* m_Comment3Export; + wxStaticText* m_staticTextComment4; + wxTextCtrl* m_TextComment4; + wxCheckBox* m_Comment4Export; + wxStaticText* m_staticTextfilename; + wxTextCtrl* m_textCtrlFilePicker; + wxButton* m_buttonBrowse; + wxStdDialogButtonSizer* m_sdbSizer; + wxButton* m_sdbSizerOK; + wxButton* m_sdbSizerCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnPaperSizeChoice( wxCommandEvent& event ) { event.Skip(); } + virtual void OnPageOrientationChoice( wxCommandEvent& event ) { event.Skip(); } + virtual void OnUserPageSizeYTextUpdated( wxCommandEvent& event ) { event.Skip(); } + virtual void OnUserPageSizeXTextUpdated( wxCommandEvent& event ) { event.Skip(); } + virtual void OnDateTextUpdated( wxCommandEvent& event ) { event.Skip(); } + virtual void OnDateApplyClick( wxCommandEvent& event ) { event.Skip(); } + virtual void OnRevisionTextUpdated( wxCommandEvent& event ) { event.Skip(); } + virtual void OnTitleTextUpdated( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCheckboxTitleClick( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCompanyTextUpdated( wxCommandEvent& event ) { event.Skip(); } + virtual void OnComment1TextUpdated( wxCommandEvent& event ) { event.Skip(); } + virtual void OnComment2TextUpdated( wxCommandEvent& event ) { event.Skip(); } + virtual void OnComment3TextUpdated( wxCommandEvent& event ) { event.Skip(); } + virtual void OnComment4TextUpdated( wxCommandEvent& event ) { event.Skip(); } + virtual void OnWksFileSelection( wxCommandEvent& event ) { event.Skip(); } + virtual void OnOkClick( wxCommandEvent& event ) { event.Skip(); } + + + public: + + DIALOG_PAGES_SETTINGS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Page Settings"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 748,470 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~DIALOG_PAGES_SETTINGS_BASE(); + +}; + +#endif //__DIALOG_PAGE_SETTINGS_BASE_H__ diff --git a/common/dialogs/wx_html_report_panel.cpp b/common/dialogs/wx_html_report_panel.cpp new file mode 100644 index 0000000..c9dca09 --- /dev/null +++ b/common/dialogs/wx_html_report_panel.cpp @@ -0,0 +1,302 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 CERN + * Copyright (C) 2015 KiCad Developers, see change_log.txt for contributors. + * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "wx_html_report_panel.h" + +#include <wildcards_and_files_ext.h> +#include <boost/foreach.hpp> + +WX_HTML_REPORT_PANEL::WX_HTML_REPORT_PANEL( wxWindow* parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style ) : + WX_HTML_REPORT_PANEL_BASE( parent, id, pos, size, style ), + m_reporter( this ), + m_severities( -1 ), + m_showAll( true ), + m_lazyUpdate( false ) +{ + syncCheckboxes(); + m_htmlView->SetPage( addHeader( "" ) ); +} + + +WX_HTML_REPORT_PANEL::~WX_HTML_REPORT_PANEL() +{ +} + + +void WX_HTML_REPORT_PANEL::MsgPanelSetMinSize( const wxSize& aMinSize ) +{ + m_htmlView->SetMinSize( aMinSize ); + GetSizer()->SetSizeHints( this ); +} + + +REPORTER& WX_HTML_REPORT_PANEL::Reporter() +{ + return m_reporter; +} + + +void WX_HTML_REPORT_PANEL::Report( const wxString& aText, REPORTER::SEVERITY aSeverity ) +{ + REPORT_LINE line; + line.message = aText; + line.severity = aSeverity; + + m_report.push_back( line ); + + m_html += generateHtml( line ); + + if( !m_lazyUpdate ) + { + m_htmlView->AppendToPage( generateHtml( line ) ); + scrollToBottom(); + } +} + + +void WX_HTML_REPORT_PANEL::SetLazyUpdate( bool aLazyUpdate ) +{ + m_lazyUpdate = aLazyUpdate; +} + + +void WX_HTML_REPORT_PANEL::Flush() +{ + m_htmlView->SetPage( addHeader( m_html ) ); + scrollToBottom(); +} + + +void WX_HTML_REPORT_PANEL::scrollToBottom() +{ + int x, y, xUnit, yUnit; + + m_htmlView->GetVirtualSize( &x, &y ); + m_htmlView->GetScrollPixelsPerUnit( &xUnit, &yUnit ); + m_htmlView->Scroll( 0, y / yUnit ); +} + + +void WX_HTML_REPORT_PANEL::refreshView() +{ + wxString html; + + BOOST_FOREACH( REPORT_LINE l, m_report ) + { + html += generateHtml( l ); + } + + m_htmlView->SetPage( addHeader( html ) ); + scrollToBottom(); +} + + +wxString WX_HTML_REPORT_PANEL::addHeader( const wxString& aBody ) +{ + wxColour bgcolor = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ); + wxColour fgcolor = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT ); + wxString s = "<html><body bgcolor=\"" + bgcolor.GetAsString( wxC2S_HTML_SYNTAX ) + + "\" text=\"" + fgcolor.GetAsString( wxC2S_HTML_SYNTAX ) + "\">"; + s += aBody; + s += "</body></html>"; + + return s; +} + + +wxString WX_HTML_REPORT_PANEL::generateHtml( const REPORT_LINE& aLine ) +{ + if( !m_showAll && ! ( m_severities & aLine.severity ) ) + return wxEmptyString; + + switch( aLine.severity ) + { + case REPORTER::RPT_ERROR: + return wxString( "<font color=\"red\" size=2>" ) + _( "<b>Error: </b></font><font size=2>" ) + aLine.message + wxString( "</font><br>" ); + case REPORTER::RPT_WARNING: + return wxString( "<font color=\"orange\" size=2>" ) + _( "<b>Warning: </b></font><font size=2>" ) + aLine.message + wxString( "</font><br>" ); + case REPORTER::RPT_INFO: + return wxString( "<font color=\"gray\" size=2>" ) + _( "<b>Info: </b>" ) + aLine.message + wxString( "</font><br>" ); + case REPORTER::RPT_ACTION: + return wxString( "<font color=\"darkgreen\" size=2>" ) + aLine.message + wxString( "</font><br>" ); + default: + return wxString( "<font size=2>" ) + aLine.message + wxString( "</font><br>" ); + } +} + + +wxString WX_HTML_REPORT_PANEL::generatePlainText( const REPORT_LINE& aLine ) +{ + switch( aLine.severity ) + { + case REPORTER::RPT_ERROR: + return _( "Error: " ) + aLine.message + wxT( "\n" ); + case REPORTER::RPT_WARNING: + return _( "Warning: " ) + aLine.message + wxT( "\n" ); + case REPORTER::RPT_INFO: + return _( "Info: " ) + aLine.message + wxT( "\n" ); + default: + return aLine.message + wxT( "\n" ); + } +} + + +void WX_HTML_REPORT_PANEL::onCheckBoxShowAll( wxCommandEvent& event ) +{ + if ( event.IsChecked() ) + m_showAll = true; + else + m_showAll = false; + + syncCheckboxes(); + refreshView(); +} + + +void WX_HTML_REPORT_PANEL::syncCheckboxes() +{ + m_checkBoxShowAll->SetValue( m_showAll ); + m_checkBoxShowWarnings->Enable( !m_showAll ); + m_checkBoxShowWarnings->SetValue( m_severities & REPORTER::RPT_WARNING ); + m_checkBoxShowErrors->Enable( !m_showAll ); + m_checkBoxShowErrors->SetValue( m_severities & REPORTER::RPT_ERROR ); + m_checkBoxShowInfos->Enable( !m_showAll ); + m_checkBoxShowInfos->SetValue( m_severities & REPORTER::RPT_INFO ); + m_checkBoxShowActions->Enable( !m_showAll ); + m_checkBoxShowActions->SetValue( m_severities & REPORTER::RPT_ACTION ); +} + + +void WX_HTML_REPORT_PANEL::onCheckBoxShowWarnings( wxCommandEvent& event ) +{ + if ( event.IsChecked() ) + m_severities |= REPORTER::RPT_WARNING; + else + m_severities &= ~REPORTER::RPT_WARNING; + + refreshView(); +} + + +void WX_HTML_REPORT_PANEL::onCheckBoxShowErrors( wxCommandEvent& event ) +{ + if ( event.IsChecked() ) + m_severities |= REPORTER::RPT_ERROR; + else + m_severities &= ~REPORTER::RPT_ERROR; + + refreshView(); +} + + +void WX_HTML_REPORT_PANEL::onCheckBoxShowInfos( wxCommandEvent& event ) +{ + if ( event.IsChecked() ) + m_severities |= REPORTER::RPT_INFO; + else + m_severities &= ~REPORTER::RPT_INFO; + + refreshView(); +} + + +void WX_HTML_REPORT_PANEL::onCheckBoxShowActions( wxCommandEvent& event ) +{ + if ( event.IsChecked() ) + m_severities |= REPORTER::RPT_ACTION; + else + m_severities &= ~REPORTER::RPT_ACTION; + + refreshView(); +} + + +void WX_HTML_REPORT_PANEL::onBtnSaveToFile( wxCommandEvent& event ) +{ + wxFileName fn( "./report.txt" ); + + wxFileDialog dlg( this, _( "Save report to file" ), fn.GetPath(), fn.GetName(), + TextWildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); + + if( dlg.ShowModal() != wxID_OK ) + return; + + fn = dlg.GetPath(); + + if( fn.GetExt().IsEmpty() ) + fn.SetExt( wxT( "txt" ) ); + + wxFile f( fn.GetFullPath(), wxFile::write ); + + if( !f.IsOpened() ) + { + wxString msg; + + msg.Printf( _( "Cannot write report to file '%s'." ), + fn.GetFullPath().GetData() ); + wxMessageBox( msg, _( "File save error" ), wxOK | wxICON_ERROR, this ); + return; + } + + BOOST_FOREACH( REPORT_LINE l, m_report ) + { + f.Write( generatePlainText( l ) ); + } + + f.Close(); +} + + +void WX_HTML_REPORT_PANEL::Clear() +{ + m_html.clear(); + m_report.clear(); +} + + +void WX_HTML_REPORT_PANEL::SetLabel( const wxString& aLabel ) +{ + m_box->GetStaticBox()->SetLabel( aLabel ); +} + + +void WX_HTML_REPORT_PANEL::SetVisibleSeverities( int aSeverities ) +{ + if( aSeverities < 0 ) + m_showAll = true; + else + { + m_showAll = false; + m_severities = aSeverities; + } + + syncCheckboxes(); +} + + +int WX_HTML_REPORT_PANEL::GetVisibleSeverities() +{ + return m_showAll ? m_severities | 0x80000000 : m_severities & ~0x80000000; +} diff --git a/common/dialogs/wx_html_report_panel.h b/common/dialogs/wx_html_report_panel.h new file mode 100644 index 0000000..03e351f --- /dev/null +++ b/common/dialogs/wx_html_report_panel.h @@ -0,0 +1,122 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 CERN + * Copyright (C) 2015 KiCad Developers, see change_log.txt for contributors. + * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __WX_HTML_REPORT_PANEL_H__ +#define __WX_HTML_REPORT_PANEL_H__ + +#include <wx/wx.h> +#include <reporter.h> +#include <vector> + +#include "wx_html_report_panel_base.h" + + +/** + * Class WX_HTML_REPORT_PANEL + * + * A widget for browsing a rich text error/status report. Used in numerous + * dialogs in eeschema and pcbnew. Provides error filtering functionality + * and saving report files. + * + * The messages are reported throuth a REPORTER object + */ +class WX_HTML_REPORT_PANEL : public WX_HTML_REPORT_PANEL_BASE +{ +public: + WX_HTML_REPORT_PANEL( wxWindow* parent, wxWindowID id = wxID_ANY, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxSize( 500,300 ), long style = wxTAB_TRAVERSAL ); + ~WX_HTML_REPORT_PANEL(); + + ///> Set the min size of the area which displays html messages: + void MsgPanelSetMinSize( const wxSize& aMinSize ); + + ///> returns the reporter object that reports to this panel + REPORTER& Reporter(); + + ///> reports a string directly. + void Report( const wxString& aText, REPORTER::SEVERITY aSeverity ); + + ///> clears the report panel + void Clear(); + + ///> sets the frame label + void SetLabel( const wxString& aLabel ); + + ///> Sets the lasy update. If this mode is on, messages are stored but the display + ///> is not updated (Updating display can be very time consumming if there are many messages) + ///> A call to Flush() will be needed after build the report + void SetLazyUpdate( bool aLazyUpdate ); + + ///> Forces updating the HTML page, after the report is built in lazy mode + void Flush(); + + ///> Set the visible severity filter. + ///> if aSeverities < 0 the m_showAll option is set + void SetVisibleSeverities( int aSeverities ); + + ///> @return the visible severity filter. + ///> If the m_showAll option is set, the mask is < 0 + int GetVisibleSeverities(); + +private: + struct REPORT_LINE + { + REPORTER::SEVERITY severity; + wxString message; + }; + + typedef std::vector<REPORT_LINE> REPORT_LINES; + + wxString addHeader( const wxString& aBody ); + wxString generateHtml( const REPORT_LINE& aLine ); + wxString generatePlainText( const REPORT_LINE& aLine ); + + void refreshView(); + void scrollToBottom(); + void syncCheckboxes(); + + void onCheckBoxShowAll( wxCommandEvent& event ); + void onCheckBoxShowWarnings( wxCommandEvent& event ); + void onCheckBoxShowErrors( wxCommandEvent& event ); + void onCheckBoxShowInfos( wxCommandEvent& event ); + void onCheckBoxShowActions( wxCommandEvent& event ); + + void onBtnSaveToFile( wxCommandEvent& event ); + + ///> copy of the report, stored for filtering + REPORT_LINES m_report; + + ///> the reporter + WX_HTML_PANEL_REPORTER m_reporter; + + ///> message severities to display (mask) + int m_severities; + + ///> show all messages flag (overrides m_severities) + bool m_showAll; + + wxString m_html; + + bool m_lazyUpdate; +}; + +#endif //__WX_HTML_REPORT_PANEL_H__ diff --git a/common/dialogs/wx_html_report_panel_base.cpp b/common/dialogs/wx_html_report_panel_base.cpp new file mode 100644 index 0000000..bd33c8c --- /dev/null +++ b/common/dialogs/wx_html_report_panel_base.cpp @@ -0,0 +1,94 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 17 2015) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "wx_html_report_panel_base.h" + +/////////////////////////////////////////////////////////////////////////// + +WX_HTML_REPORT_PANEL_BASE::WX_HTML_REPORT_PANEL_BASE( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) +{ + m_box = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Messages:") ), wxVERTICAL ); + + wxFlexGridSizer* fgSizer4; + fgSizer4 = new wxFlexGridSizer( 2, 1, 0, 0 ); + fgSizer4->AddGrowableCol( 0 ); + fgSizer4->AddGrowableRow( 0 ); + fgSizer4->SetFlexibleDirection( wxBOTH ); + fgSizer4->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_htmlView = new wxHtmlWindow( m_box->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO ); + m_htmlView->SetFont( wxFont( 10, 70, 90, 90, false, wxEmptyString ) ); + + fgSizer4->Add( m_htmlView, 1, wxEXPAND, 5 ); + + wxFlexGridSizer* fgSizer3; + fgSizer3 = new wxFlexGridSizer( 1, 7, 0, 0 ); + fgSizer3->AddGrowableCol( 6 ); + fgSizer3->SetFlexibleDirection( wxBOTH ); + fgSizer3->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_staticText3 = new wxStaticText( m_box->GetStaticBox(), wxID_ANY, _("Filter:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText3->Wrap( -1 ); + fgSizer3->Add( m_staticText3, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT, 5 ); + + m_checkBoxShowAll = new wxCheckBox( m_box->GetStaticBox(), wxID_ANY, _("All"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxShowAll->SetValue(true); + fgSizer3->Add( m_checkBoxShowAll, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT|wxLEFT, 5 ); + + m_checkBoxShowWarnings = new wxCheckBox( m_box->GetStaticBox(), wxID_ANY, _("Warnings"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxShowWarnings->Enable( false ); + + fgSizer3->Add( m_checkBoxShowWarnings, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT|wxLEFT, 5 ); + + m_checkBoxShowErrors = new wxCheckBox( m_box->GetStaticBox(), wxID_ANY, _("Errors"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxShowErrors->Enable( false ); + + fgSizer3->Add( m_checkBoxShowErrors, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT|wxLEFT, 5 ); + + m_checkBoxShowInfos = new wxCheckBox( m_box->GetStaticBox(), wxID_ANY, _("Infos"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxShowInfos->Enable( false ); + + fgSizer3->Add( m_checkBoxShowInfos, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT|wxLEFT, 5 ); + + m_checkBoxShowActions = new wxCheckBox( m_box->GetStaticBox(), wxID_ANY, _("Actions"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxShowActions->Enable( false ); + + fgSizer3->Add( m_checkBoxShowActions, 0, wxTOP|wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); + + m_btnSaveReportToFile = new wxButton( m_box->GetStaticBox(), wxID_ANY, _("Save report to file..."), wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer3->Add( m_btnSaveReportToFile, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxTOP|wxLEFT, 5 ); + + + fgSizer4->Add( fgSizer3, 1, wxEXPAND, 5 ); + + + m_box->Add( fgSizer4, 1, wxEXPAND|wxALL, 5 ); + + + this->SetSizer( m_box ); + this->Layout(); + + // Connect Events + m_checkBoxShowAll->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( WX_HTML_REPORT_PANEL_BASE::onCheckBoxShowAll ), NULL, this ); + m_checkBoxShowWarnings->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( WX_HTML_REPORT_PANEL_BASE::onCheckBoxShowWarnings ), NULL, this ); + m_checkBoxShowErrors->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( WX_HTML_REPORT_PANEL_BASE::onCheckBoxShowErrors ), NULL, this ); + m_checkBoxShowInfos->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( WX_HTML_REPORT_PANEL_BASE::onCheckBoxShowInfos ), NULL, this ); + m_checkBoxShowActions->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( WX_HTML_REPORT_PANEL_BASE::onCheckBoxShowActions ), NULL, this ); + m_btnSaveReportToFile->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( WX_HTML_REPORT_PANEL_BASE::onBtnSaveToFile ), NULL, this ); +} + +WX_HTML_REPORT_PANEL_BASE::~WX_HTML_REPORT_PANEL_BASE() +{ + // Disconnect Events + m_checkBoxShowAll->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( WX_HTML_REPORT_PANEL_BASE::onCheckBoxShowAll ), NULL, this ); + m_checkBoxShowWarnings->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( WX_HTML_REPORT_PANEL_BASE::onCheckBoxShowWarnings ), NULL, this ); + m_checkBoxShowErrors->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( WX_HTML_REPORT_PANEL_BASE::onCheckBoxShowErrors ), NULL, this ); + m_checkBoxShowInfos->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( WX_HTML_REPORT_PANEL_BASE::onCheckBoxShowInfos ), NULL, this ); + m_checkBoxShowActions->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( WX_HTML_REPORT_PANEL_BASE::onCheckBoxShowActions ), NULL, this ); + m_btnSaveReportToFile->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( WX_HTML_REPORT_PANEL_BASE::onBtnSaveToFile ), NULL, this ); + +} diff --git a/common/dialogs/wx_html_report_panel_base.fbp b/common/dialogs/wx_html_report_panel_base.fbp new file mode 100644 index 0000000..8b635e0 --- /dev/null +++ b/common/dialogs/wx_html_report_panel_base.fbp @@ -0,0 +1,823 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<wxFormBuilder_Project> + <FileVersion major="1" minor="13" /> + <object class="Project" expanded="1"> + <property name="class_decoration"></property> + <property name="code_generation">C++</property> + <property name="disconnect_events">1</property> + <property name="disconnect_mode">source_name</property> + <property name="disconnect_php_events">0</property> + <property name="disconnect_python_events">0</property> + <property name="embedded_files_path">res</property> + <property name="encoding">UTF-8</property> + <property name="event_generation">connect</property> + <property name="file">wx_html_report_panel_base</property> + <property name="first_id">1000</property> + <property name="help_provider">none</property> + <property name="internationalize">1</property> + <property name="name">WX_HTML_REPORT_PANEL_BASE</property> + <property name="namespace"></property> + <property name="path">.</property> + <property name="precompiled_header"></property> + <property name="relative_path">1</property> + <property name="skip_lua_events">1</property> + <property name="skip_php_events">1</property> + <property name="skip_python_events">1</property> + <property name="ui_table">UI</property> + <property name="use_enum">0</property> + <property name="use_microsoft_bom">0</property> + <object class="Panel" expanded="1"> + <property name="aui_managed">0</property> + <property name="aui_manager_style">wxAUI_MGR_DEFAULT</property> + <property name="bg"></property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="enabled">1</property> + <property name="event_handler">impl_virtual</property> + <property name="fg"></property> + <property name="font"></property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="maximum_size"></property> + <property name="minimum_size"></property> + <property name="name">WX_HTML_REPORT_PANEL_BASE</property> + <property name="pos"></property> + <property name="size">500,300</property> + <property name="subclass"></property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style">wxTAB_TRAVERSAL</property> + <event name="OnAuiFindManager"></event> + <event name="OnAuiPaneButton"></event> + <event name="OnAuiPaneClose"></event> + <event name="OnAuiPaneMaximize"></event> + <event name="OnAuiPaneRestore"></event> + <event name="OnAuiRender"></event> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnInitDialog"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + <object class="wxStaticBoxSizer" expanded="1"> + <property name="id">wxID_ANY</property> + <property name="label">Messages:</property> + <property name="minimum_size"></property> + <property name="name">m_box</property> + <property name="orient">wxVERTICAL</property> + <property name="permission">protected</property> + <event name="OnUpdateUI"></event> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxALL</property> + <property name="proportion">1</property> + <object class="wxFlexGridSizer" expanded="0"> + <property name="cols">1</property> + <property name="flexible_direction">wxBOTH</property> + <property name="growablecols">0</property> + <property name="growablerows">0</property> + <property name="hgap">0</property> + <property name="minimum_size"></property> + <property name="name">fgSizer4</property> + <property name="non_flexible_grow_mode">wxFLEX_GROWMODE_SPECIFIED</property> + <property name="permission">none</property> + <property name="rows">2</property> + <property name="vgap">0</property> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxHtmlWindow" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">0</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font">,90,90,10,70,0</property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_htmlView</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style">wxHW_SCROLLBAR_AUTO</property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnHtmlCellClicked"></event> + <event name="OnHtmlCellHover"></event> + <event name="OnHtmlLinkClicked"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxFlexGridSizer" expanded="0"> + <property name="cols">7</property> + <property name="flexible_direction">wxBOTH</property> + <property name="growablecols">6</property> + <property name="growablerows"></property> + <property name="hgap">0</property> + <property name="minimum_size"></property> + <property name="name">fgSizer3</property> + <property name="non_flexible_grow_mode">wxFLEX_GROWMODE_SPECIFIED</property> + <property name="permission">none</property> + <property name="rows">1</property> + <property name="vgap">0</property> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Filter:</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticText3</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxCheckBox" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="checked">1</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">All</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_checkBoxShowAll</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnCheckBox">onCheckBoxShowAll</event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxCheckBox" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="checked">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">0</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Warnings</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_checkBoxShowWarnings</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnCheckBox">onCheckBoxShowWarnings</event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxCheckBox" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="checked">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">0</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Errors</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_checkBoxShowErrors</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnCheckBox">onCheckBoxShowErrors</event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxCheckBox" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="checked">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">0</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Infos</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_checkBoxShowInfos</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnCheckBox">onCheckBoxShowInfos</event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxTOP|wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL</property> + <property name="proportion">0</property> + <object class="wxCheckBox" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="checked">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">0</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Actions</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_checkBoxShowActions</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnChar"></event> + <event name="OnCheckBox">onCheckBoxShowActions</event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + <object class="sizeritem" expanded="0"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxTOP|wxLEFT</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="0"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default">0</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Save report to file...</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size">-1,-1</property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_btnSaveReportToFile</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass"></property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <event name="OnButtonClick">onBtnSaveToFile</event> + <event name="OnChar"></event> + <event name="OnEnterWindow"></event> + <event name="OnEraseBackground"></event> + <event name="OnKeyDown"></event> + <event name="OnKeyUp"></event> + <event name="OnKillFocus"></event> + <event name="OnLeaveWindow"></event> + <event name="OnLeftDClick"></event> + <event name="OnLeftDown"></event> + <event name="OnLeftUp"></event> + <event name="OnMiddleDClick"></event> + <event name="OnMiddleDown"></event> + <event name="OnMiddleUp"></event> + <event name="OnMotion"></event> + <event name="OnMouseEvents"></event> + <event name="OnMouseWheel"></event> + <event name="OnPaint"></event> + <event name="OnRightDClick"></event> + <event name="OnRightDown"></event> + <event name="OnRightUp"></event> + <event name="OnSetFocus"></event> + <event name="OnSize"></event> + <event name="OnUpdateUI"></event> + </object> + </object> + </object> + </object> + </object> + </object> + </object> + </object> + </object> +</wxFormBuilder_Project> diff --git a/common/dialogs/wx_html_report_panel_base.h b/common/dialogs/wx_html_report_panel_base.h new file mode 100644 index 0000000..b65449e --- /dev/null +++ b/common/dialogs/wx_html_report_panel_base.h @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 17 2015) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __WX_HTML_REPORT_PANEL_BASE_H__ +#define __WX_HTML_REPORT_PANEL_BASE_H__ + +#include <wx/artprov.h> +#include <wx/xrc/xmlres.h> +#include <wx/intl.h> +#include <wx/html/htmlwin.h> +#include <wx/gdicmn.h> +#include <wx/font.h> +#include <wx/colour.h> +#include <wx/settings.h> +#include <wx/string.h> +#include <wx/stattext.h> +#include <wx/checkbox.h> +#include <wx/button.h> +#include <wx/sizer.h> +#include <wx/statbox.h> +#include <wx/panel.h> + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class WX_HTML_REPORT_PANEL_BASE +/////////////////////////////////////////////////////////////////////////////// +class WX_HTML_REPORT_PANEL_BASE : public wxPanel +{ + private: + + protected: + wxStaticBoxSizer* m_box; + wxHtmlWindow* m_htmlView; + wxStaticText* m_staticText3; + wxCheckBox* m_checkBoxShowAll; + wxCheckBox* m_checkBoxShowWarnings; + wxCheckBox* m_checkBoxShowErrors; + wxCheckBox* m_checkBoxShowInfos; + wxCheckBox* m_checkBoxShowActions; + wxButton* m_btnSaveReportToFile; + + // Virtual event handlers, overide them in your derived class + virtual void onCheckBoxShowAll( wxCommandEvent& event ) { event.Skip(); } + virtual void onCheckBoxShowWarnings( wxCommandEvent& event ) { event.Skip(); } + virtual void onCheckBoxShowErrors( wxCommandEvent& event ) { event.Skip(); } + virtual void onCheckBoxShowInfos( wxCommandEvent& event ) { event.Skip(); } + virtual void onCheckBoxShowActions( wxCommandEvent& event ) { event.Skip(); } + virtual void onBtnSaveToFile( wxCommandEvent& event ) { event.Skip(); } + + + public: + + WX_HTML_REPORT_PANEL_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 500,300 ), long style = wxTAB_TRAVERSAL ); + ~WX_HTML_REPORT_PANEL_BASE(); + +}; + +#endif //__WX_HTML_REPORT_PANEL_BASE_H__ diff --git a/common/displlst.cpp b/common/displlst.cpp new file mode 100644 index 0000000..b8961f6 --- /dev/null +++ b/common/displlst.cpp @@ -0,0 +1,252 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2007 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 1992-2013 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 + */ + +/** + * @file displlst.cpp + */ + +#include <fctsys.h> +#include <macros.h> +#include <draw_frame.h> +#include <kicad_string.h> +#include <dialog_helpers.h> + +EDA_LIST_DIALOG::EDA_LIST_DIALOG( EDA_DRAW_FRAME* aParent, const wxString& aTitle, + const wxArrayString& aItemHeaders, + const std::vector<wxArrayString>& aItemList, + const wxString& aSelection, + void( *aCallBackFunction )( wxString&, void* ), + void* aCallBackFunctionData, + bool aSortList ) : + EDA_LIST_DIALOG_BASE( aParent, wxID_ANY, aTitle ) +{ + m_sortList = aSortList; + m_cb_func = aCallBackFunction; + m_cb_data = aCallBackFunctionData; + m_itemsListCp = &aItemList; + + initDialog( aItemHeaders, aSelection ); + + // DIALOG_SHIM needs a unique hash_key because classname is not sufficient + // because so many dialogs share this same class, with different numbers of + // columns, different column names, and column widths. + m_hash_key = TO_UTF8( aTitle ); + + m_filterBox->SetFocus(); + + // this line fixes an issue on Linux Ubuntu using Unity (dialog not shown), + // and works fine on all systems + GetSizer()->Fit( this ); + + Centre(); +} + + +void EDA_LIST_DIALOG::initDialog( const wxArrayString& aItemHeaders, + const wxString& aSelection) +{ + + for( unsigned i = 0; i < aItemHeaders.Count(); i++ ) + { + wxListItem column; + + column.SetId( i ); + column.SetText( aItemHeaders.Item( i ) ); + + m_listBox->InsertColumn( i, column ); + } + + InsertItems( *m_itemsListCp, 0 ); + + if( m_cb_func == NULL ) + { + m_messages->Show( false ); + m_staticTextMsg->Show( false ); + } + + for( unsigned col = 0; col < aItemHeaders.Count(); ++col ) + { + m_listBox->SetColumnWidth( col, wxLIST_AUTOSIZE ); + int columnwidth = m_listBox->GetColumnWidth( col ); + m_listBox->SetColumnWidth( col, wxLIST_AUTOSIZE_USEHEADER ); + int headerwidth = m_listBox->GetColumnWidth( col ); + m_listBox->SetColumnWidth( col, std::max( columnwidth, headerwidth ) ); + } + + if( !!aSelection ) + { + for( unsigned row = 0; row < m_itemsListCp->size(); ++row ) + { + if( (*m_itemsListCp)[row][0] == aSelection ) + { + m_listBox->SetItemState( row, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED ); + m_listBox->EnsureVisible( row ); + break; + } + } + } +} + + +void EDA_LIST_DIALOG::textChangeInFilterBox( wxCommandEvent& event ) +{ + wxString filter; + wxString itemName; + + filter = wxT( "*" ) + m_filterBox->GetLineText( 0 ).MakeLower() + wxT( "*" ); + + m_listBox->DeleteAllItems(); + + for( unsigned i = 0; i < m_itemsListCp->size(); i++ ) + { + itemName = (*m_itemsListCp)[i].Item( 0 ); + + if( itemName.MakeLower().Matches( filter ) ) + { + Append( (*m_itemsListCp)[i] ); + } + } + + if( m_sortList ) + sortList(); +} + + +wxString EDA_LIST_DIALOG::GetTextSelection( int aColumn ) +{ + wxCHECK_MSG( unsigned( aColumn ) < unsigned( m_listBox->GetColumnCount() ), wxEmptyString, + wxT( "Invalid list control column." ) ); + + long item = m_listBox->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED ); + + if( item >= 0 ) // if something is selected. + { + wxListItem info; + + info.m_mask = wxLIST_MASK_TEXT; + info.m_itemId = item; + info.m_col = aColumn; + + if( m_listBox->GetItem( info ) ) + return info.m_text; + } + + return wxEmptyString; +} + + +void EDA_LIST_DIALOG::Append( const wxArrayString& itemList ) +{ + long itemIndex = m_listBox->InsertItem( m_listBox->GetItemCount(), itemList[0] ); + + m_listBox->SetItemData( itemIndex, (long) &(itemList[0]) ); + + // Adding the next columns content + for( unsigned i = 1; i < itemList.size(); i++ ) + { + m_listBox->SetItem( itemIndex, i, itemList[i] ); + } +} + + +void EDA_LIST_DIALOG::InsertItems( const std::vector< wxArrayString >& itemList, int position ) +{ + for( unsigned row = 0; row < itemList.size(); row++ ) + { + wxASSERT( (int) itemList[row].GetCount() == m_listBox->GetColumnCount() ); + + long itemIndex = 0; + for( unsigned col = 0; col < itemList[row].GetCount(); col++ ) + { + + if( col == 0 ) + { + itemIndex = m_listBox->InsertItem( row+position, itemList[row].Item( col ) ); + m_listBox->SetItemData( itemIndex, (long) &itemList[row].Item( col ) ); + } + else + { + m_listBox->SetItem( itemIndex, col, itemList[row].Item( col ) ); + } + } + } + + if( m_sortList ) + sortList(); +} + + +void EDA_LIST_DIALOG::onCancelClick( wxCommandEvent& event ) +{ + EndModal( wxID_CANCEL ); +} + + +void EDA_LIST_DIALOG::onListItemSelected( wxListEvent& event ) +{ + if( m_cb_func ) + { + m_messages->Clear(); + wxString text = GetTextSelection(); + m_cb_func( text, m_cb_data ); + m_messages->WriteText( text ); + } +} + + +void EDA_LIST_DIALOG::onListItemActivated( wxListEvent& event ) +{ + EndModal( wxID_OK ); +} + + +void EDA_LIST_DIALOG::onOkClick( wxCommandEvent& event ) +{ + EndModal( wxID_OK ); +} + + +void EDA_LIST_DIALOG::onClose( wxCloseEvent& event ) +{ + EndModal( wxID_CANCEL ); +} + + +/* Sort alphabetically, case insensitive. + */ +static int wxCALLBACK myCompareFunction( wxIntPtr aItem1, wxIntPtr aItem2, + wxIntPtr WXUNUSED( aSortData ) ) +{ + wxString* component1Name = (wxString*) aItem1; + wxString* component2Name = (wxString*) aItem2; + + return StrNumCmp( *component1Name, *component2Name, INT_MAX, true ); +} + + +void EDA_LIST_DIALOG::sortList() +{ + m_listBox->SortItems( myCompareFunction, 0 ); +} diff --git a/common/dlist.cpp b/common/dlist.cpp new file mode 100644 index 0000000..512fd1c --- /dev/null +++ b/common/dlist.cpp @@ -0,0 +1,228 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2008 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 1992-2008 KiCad Developers, see change_log.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 <fctsys.h> +#include <dlist.h> +#include <base_struct.h> + + +/* Implement the class DHEAD from dlist.h */ + + +DHEAD::~DHEAD() +{ + if( meOwner ) + DeleteAll(); +} + + +void DHEAD::DeleteAll() +{ + EDA_ITEM* next; + EDA_ITEM* item = first; + + while( item ) + { + next = item->Next(); + delete item; // virtual destructor, class specific + item = next; + } + + first = 0; + last = 0; + count = 0; +} + + +void DHEAD::append( EDA_ITEM* aNewElement ) +{ + wxASSERT( aNewElement != NULL ); + + if( first ) // list is not empty, first is not touched + { + wxASSERT( last != NULL ); + + aNewElement->SetNext( 0 ); + aNewElement->SetBack( last ); + + last->SetNext( aNewElement ); + last = aNewElement; + } + else // list is empty, first and last are changed + { + aNewElement->SetNext( 0 ); + aNewElement->SetBack( 0 ); + + first = aNewElement; + last = aNewElement; + } + + aNewElement->SetList( this ); + + ++count; +} + + +void DHEAD::append( DHEAD& aList ) +{ + if( aList.first ) + { + // Change the item's list to me. + for( EDA_ITEM* item = aList.first; item; item = item->Next() ) + item->SetList( this ); + + if( first ) // this list is not empty, set last item's next to the first item in aList + { + wxCHECK_RET( last != NULL, wxT( "Last list element not set." ) ); + + last->SetNext( aList.first ); + aList.first->SetBack( last ); + last = aList.last; + } + else // this list is empty, first and last are same as aList + { + first = aList.first; + last = aList.last; + } + + count += aList.count; + + aList.count = 0; + aList.first = NULL; + aList.last = NULL; + } +} + + +void DHEAD::insert( EDA_ITEM* aNewElement, EDA_ITEM* aAfterMe ) +{ + wxASSERT( aNewElement != NULL ); + + if( !aAfterMe ) + append( aNewElement ); + else + { + wxASSERT( aAfterMe->GetList() == this ); + + // the list cannot be empty if aAfterMe is supposedly on the list + wxASSERT( first && last ); + + if( first == aAfterMe ) + { + aAfterMe->SetBack( aNewElement ); + + aNewElement->SetBack( 0 ); // first in list does not point back + aNewElement->SetNext( aAfterMe ); + + first = aNewElement; + } + else + { + EDA_ITEM* oldBack = aAfterMe->Back(); + + aAfterMe->SetBack( aNewElement ); + + aNewElement->SetBack( oldBack ); + aNewElement->SetNext( aAfterMe ); + + oldBack->SetNext( aNewElement ); + } + + aNewElement->SetList( this ); + + ++count; + } +} + + +void DHEAD::remove( EDA_ITEM* aElement ) +{ + wxASSERT( aElement ); + wxASSERT( aElement->GetList() == this ); + + if( aElement->Next() ) + { + aElement->Next()->SetBack( aElement->Back() ); + } + else // element being removed is last + { + wxASSERT( last == aElement ); + last = aElement->Back(); + } + + if( aElement->Back() ) + { + aElement->Back()->SetNext( aElement->Next() ); + } + else // element being removed is first + { + wxASSERT( first == aElement ); + first = aElement->Next(); + } + + aElement->SetBack( 0 ); + aElement->SetNext( 0 ); + aElement->SetList( 0 ); + + --count; +} + +#if defined(DEBUG) + +void DHEAD::VerifyListIntegrity() +{ + EDA_ITEM* item; + unsigned i = 0; + + for( item = first; item && i<count; ++i, item = item->Next() ) + { + if( i < count-1 ) + { + wxASSERT( item->Next() ); + } + + wxASSERT( item->GetList() == this ); + } + + wxASSERT( item == NULL ); + wxASSERT( i == count ); + + i = 0; + for( item = last; item && i<count; ++i, item = item->Back() ) + { + if( i < count-1 ) + { + wxASSERT( item->Back() ); + } + } + + wxASSERT( item == NULL ); + wxASSERT( i == count ); + + // printf("list %p has %d items.\n", this, count ); +} + +#endif + diff --git a/common/draw_frame.cpp b/common/draw_frame.cpp new file mode 100644 index 0000000..2a81a17 --- /dev/null +++ b/common/draw_frame.cpp @@ -0,0 +1,1306 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2004-2015 Jean-Pierre Charras, jean-pierre.charras@gipsa-lab.inpg.fr + * Copyright (C) 2008-2015 Wayne Stambaugh <stambaughw@verizon.net> + * Copyright (C) 2004-2016 KiCad Developers, see change_log.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 + */ + +/** + * @file draw_frame.cpp + */ + +#include <fctsys.h> +#include <pgm_base.h> +#include <kiface_i.h> +#include <gr_basic.h> +#include <common.h> +#include <bitmaps.h> +#include <macros.h> +#include <id.h> +#include <class_drawpanel.h> +#include <class_draw_panel_gal.h> +#include <class_base_screen.h> +#include <msgpanel.h> +#include <draw_frame.h> +#include <confirm.h> +#include <kicad_device_context.h> +#include <dialog_helpers.h> +#include <base_units.h> +#include <math/box2.h> + +#include <wx/fontdlg.h> +#include <wx/snglinst.h> +#include <view/view.h> +#include <view/view_controls.h> +#include <gal/graphics_abstraction_layer.h> +#include <tool/tool_manager.h> +#include <tool/tool_dispatcher.h> + +/** + * Definition for enabling and disabling scroll bar setting trace output. See the + * wxWidgets documentation on useing the WXTRACE environment variable. + */ +static const wxString traceScrollSettings( wxT( "KicadScrollSettings" ) ); + + +// Configuration entry names. +static const wxString CursorShapeEntryKeyword( wxT( "CursorShape" ) ); +static const wxString ShowGridEntryKeyword( wxT( "ShowGrid" ) ); +static const wxString GridColorEntryKeyword( wxT( "GridColor" ) ); +static const wxString LastGridSizeIdKeyword( wxT( "_LastGridSize" ) ); +static const wxString MaxUndoItemsEntry(wxT( "MaxUndoItems" ) ); + + +BEGIN_EVENT_TABLE( EDA_DRAW_FRAME, KIWAY_PLAYER ) + EVT_MOUSEWHEEL( EDA_DRAW_FRAME::OnMouseEvent ) + EVT_MENU_OPEN( EDA_DRAW_FRAME::OnMenuOpen ) + EVT_ACTIVATE( EDA_DRAW_FRAME::OnActivate ) + EVT_MENU_RANGE( ID_ZOOM_BEGIN, ID_ZOOM_END, EDA_DRAW_FRAME::OnZoom ) + + EVT_MENU_RANGE( ID_POPUP_ZOOM_START_RANGE, ID_POPUP_ZOOM_END_RANGE, + EDA_DRAW_FRAME::OnZoom ) + + EVT_MENU_RANGE( ID_POPUP_GRID_LEVEL_1000, ID_POPUP_GRID_USER, + EDA_DRAW_FRAME::OnSelectGrid ) + + EVT_TOOL( ID_TB_OPTIONS_SHOW_GRID, EDA_DRAW_FRAME::OnToggleGridState ) + EVT_TOOL_RANGE( ID_TB_OPTIONS_SELECT_UNIT_MM, ID_TB_OPTIONS_SELECT_UNIT_INCH, + EDA_DRAW_FRAME::OnSelectUnits ) + EVT_TOOL( ID_TB_OPTIONS_SELECT_CURSOR, EDA_DRAW_FRAME::OnToggleCrossHairStyle ) + + EVT_UPDATE_UI( wxID_UNDO, EDA_DRAW_FRAME::OnUpdateUndo ) + EVT_UPDATE_UI( wxID_REDO, EDA_DRAW_FRAME::OnUpdateRedo ) + EVT_UPDATE_UI( ID_TB_OPTIONS_SHOW_GRID, EDA_DRAW_FRAME::OnUpdateGrid ) + EVT_UPDATE_UI( ID_TB_OPTIONS_SELECT_CURSOR, EDA_DRAW_FRAME::OnUpdateCrossHairStyle ) + EVT_UPDATE_UI_RANGE( ID_TB_OPTIONS_SELECT_UNIT_MM, ID_TB_OPTIONS_SELECT_UNIT_INCH, + EDA_DRAW_FRAME::OnUpdateUnits ) +END_EVENT_TABLE() + + +EDA_DRAW_FRAME::EDA_DRAW_FRAME( KIWAY* aKiway, wxWindow* aParent, + FRAME_T aFrameType, + const wxString& aTitle, + const wxPoint& aPos, const wxSize& aSize, + long aStyle, const wxString & aFrameName ) : + KIWAY_PLAYER( aKiway, aParent, aFrameType, aTitle, aPos, aSize, aStyle, aFrameName ) +{ + m_file_checker = NULL; + + m_drawToolBar = NULL; + m_optionsToolBar = NULL; + m_gridSelectBox = NULL; + m_zoomSelectBox = NULL; + m_hotkeysDescrList = NULL; + + m_canvas = NULL; + m_galCanvas = NULL; + m_galCanvasActive = false; + m_toolManager = NULL; + m_toolDispatcher = NULL; + m_messagePanel = NULL; + m_currentScreen = NULL; + m_toolId = ID_NO_TOOL_SELECTED; + m_lastDrawToolId = ID_NO_TOOL_SELECTED; + m_showAxis = false; // true to draw axis. + m_showBorderAndTitleBlock = false; // true to display reference sheet. + m_showGridAxis = false; // true to draw the grid axis + m_showOriginAxis = false; // true to draw the grid origin + m_cursorShape = 0; + m_LastGridSizeId = 0; + m_drawGrid = true; // hide/Show grid. default = show + m_gridColor = DARKGRAY; // Default grid color + m_showPageLimits = false; + m_drawBgColor = BLACK; // the background color of the draw canvas: + // BLACK for Pcbnew, BLACK or WHITE for eeschema + m_snapToGrid = true; + m_MsgFrameHeight = EDA_MSG_PANEL::GetRequiredHeight(); + m_movingCursorWithKeyboard = false; + m_zoomLevelCoeff = 1.0; + + m_auimgr.SetFlags(wxAUI_MGR_DEFAULT|wxAUI_MGR_LIVE_RESIZE); + + CreateStatusBar( 6 ); + + // set the size of the status bar subwindows: + + wxWindow* stsbar = GetStatusBar(); + + int dims[] = { + + // remainder of status bar on far left is set to a default or whatever is left over. + -1, + + // When using GetTextSize() remember the width of character '1' is not the same + // as the width of '0' unless the font is fixed width, and it usually won't be. + + // zoom: + GetTextSize( wxT( "Z 762000" ), stsbar ).x + 10, + + // cursor coords + GetTextSize( wxT( "X 0234.567890 Y 0234.567890" ), stsbar ).x + 10, + + // delta distances + GetTextSize( wxT( "dx 0234.567890 dx 0234.567890 d 0234.567890" ), stsbar ).x + 10, + + // units display, Inches is bigger than mm + GetTextSize( _( "Inches" ), stsbar ).x + 10, + + // Size for the panel used as "Current tool in play": will take longest string from + // void PCB_EDIT_FRAME::OnSelectTool( wxCommandEvent& aEvent ) in pcbnew/edit.cpp + GetTextSize( wxT( "Add layer alignment target" ), stsbar ).x + 10, + }; + + SetStatusWidths( DIM( dims ), dims ); + + // Create child subwindows. + GetClientSize( &m_FrameSize.x, &m_FrameSize.y ); + m_FramePos.x = m_FramePos.y = 0; + m_FrameSize.y -= m_MsgFrameHeight; + + m_canvas = new EDA_DRAW_PANEL( this, -1, wxPoint( 0, 0 ), m_FrameSize ); + m_messagePanel = new EDA_MSG_PANEL( this, -1, wxPoint( 0, m_FrameSize.y ), + wxSize( m_FrameSize.x, m_MsgFrameHeight ) ); + + m_messagePanel->SetBackgroundColour( MakeColour( LIGHTGRAY ) ); +} + + +EDA_DRAW_FRAME::~EDA_DRAW_FRAME() +{ + delete m_toolManager; + delete m_toolDispatcher; + delete m_galCanvas; + + delete m_currentScreen; + m_currentScreen = NULL; + + m_auimgr.UnInit(); + + ReleaseFile(); +} + + +void EDA_DRAW_FRAME::ReleaseFile() +{ + delete m_file_checker; + m_file_checker = 0; +} + + +bool EDA_DRAW_FRAME::LockFile( const wxString& aFileName ) +{ + delete m_file_checker; + + m_file_checker = ::LockFile( aFileName ); + + return bool( m_file_checker ); +} + + +void EDA_DRAW_FRAME::unitsChangeRefresh() +{ + UpdateStatusBar(); + UpdateMsgPanel(); +} + + +void EDA_DRAW_FRAME::EraseMsgBox() +{ + if( m_messagePanel ) + m_messagePanel->EraseMsgBox(); +} + + +void EDA_DRAW_FRAME::OnActivate( wxActivateEvent& event ) +{ + if( m_canvas ) + m_canvas->SetCanStartBlock( -1 ); + + event.Skip(); // required under wxMAC +} + + +void EDA_DRAW_FRAME::OnMenuOpen( wxMenuEvent& event ) +{ + if( m_canvas ) + m_canvas->SetCanStartBlock( -1 ); + + event.Skip(); +} + + +void EDA_DRAW_FRAME::SkipNextLeftButtonReleaseEvent() +{ + m_canvas->SetIgnoreLeftButtonReleaseEvent( true ); +} + + +void EDA_DRAW_FRAME::OnToggleGridState( wxCommandEvent& aEvent ) +{ + SetGridVisibility( !IsGridVisible() ); + + if( IsGalCanvasActive() ) + { + GetGalCanvas()->GetGAL()->SetGridVisibility( IsGridVisible() ); + GetGalCanvas()->GetView()->MarkTargetDirty( KIGFX::TARGET_NONCACHED ); + } + + m_canvas->Refresh(); +} + + +void EDA_DRAW_FRAME::OnSelectUnits( wxCommandEvent& aEvent ) +{ + if( aEvent.GetId() == ID_TB_OPTIONS_SELECT_UNIT_MM && g_UserUnit != MILLIMETRES ) + { + g_UserUnit = MILLIMETRES; + unitsChangeRefresh(); + } + else if( aEvent.GetId() == ID_TB_OPTIONS_SELECT_UNIT_INCH && g_UserUnit != INCHES ) + { + g_UserUnit = INCHES; + unitsChangeRefresh(); + } +} + + +void EDA_DRAW_FRAME::OnToggleCrossHairStyle( wxCommandEvent& aEvent ) +{ + INSTALL_UNBUFFERED_DC( dc, m_canvas ); + m_canvas->CrossHairOff( &dc ); + SetCursorShape( !GetCursorShape() ); + m_canvas->CrossHairOn( &dc ); +} + + +void EDA_DRAW_FRAME::OnUpdateUndo( wxUpdateUIEvent& aEvent ) +{ + if( GetScreen() ) + aEvent.Enable( GetScreen()->GetUndoCommandCount() > 0 ); +} + + +void EDA_DRAW_FRAME::OnUpdateRedo( wxUpdateUIEvent& aEvent ) +{ + if( GetScreen() ) + aEvent.Enable( GetScreen()->GetRedoCommandCount() > 0 ); +} + + +void EDA_DRAW_FRAME::OnUpdateUnits( wxUpdateUIEvent& aEvent ) +{ + bool enable; + + enable = ( ((aEvent.GetId() == ID_TB_OPTIONS_SELECT_UNIT_MM) && (g_UserUnit == MILLIMETRES)) + || ((aEvent.GetId() == ID_TB_OPTIONS_SELECT_UNIT_INCH) && (g_UserUnit == INCHES)) ); + + aEvent.Check( enable ); + DisplayUnitsMsg(); +} + + +void EDA_DRAW_FRAME::OnUpdateGrid( wxUpdateUIEvent& aEvent ) +{ + wxString tool_tip = IsGridVisible() ? _( "Hide grid" ) : _( "Show grid" ); + + aEvent.Check( IsGridVisible() ); + m_optionsToolBar->SetToolShortHelp( ID_TB_OPTIONS_SHOW_GRID, tool_tip ); +} + + +void EDA_DRAW_FRAME::OnUpdateCrossHairStyle( wxUpdateUIEvent& aEvent ) +{ + aEvent.Check( m_cursorShape ); +} + + +void EDA_DRAW_FRAME::ReCreateAuxiliaryToolbar() +{ +} + + +void EDA_DRAW_FRAME::ReCreateMenuBar() +{ +} + + +bool EDA_DRAW_FRAME::OnHotKey( wxDC* aDC, int aHotKey, const wxPoint& aPosition, EDA_ITEM* aItem ) +{ + return false; +} + +int EDA_DRAW_FRAME::WriteHotkeyConfig( struct EDA_HOTKEY_CONFIG* aDescList, wxString* aFullFileName ) +{ + int result = EDA_BASE_FRAME::WriteHotkeyConfig( aDescList, aFullFileName ); + + if( IsGalCanvasActive() ) + GetToolManager()->UpdateHotKeys(); + + return result; +} + +void EDA_DRAW_FRAME::ToolOnRightClick( wxCommandEvent& event ) +{ +} + + +void EDA_DRAW_FRAME::PrintPage( wxDC* aDC, LSET aPrintMask, bool aPrintMirrorMode, void* aData ) +{ + wxMessageBox( wxT("EDA_DRAW_FRAME::PrintPage() error") ); +} + + +void EDA_DRAW_FRAME::OnSelectGrid( wxCommandEvent& event ) +{ + int* clientData; + int eventId = ID_POPUP_GRID_LEVEL_100; + + if( event.GetEventType() == wxEVT_CHOICE ) + { + if( m_gridSelectBox == NULL ) // Should not happen + return; + + /* + * Don't use wxCommandEvent::GetClientData() here. It always + * returns NULL in GTK. This solution is not as elegant but + * it works. + */ + int index = m_gridSelectBox->GetSelection(); + wxASSERT( index != wxNOT_FOUND ); + clientData = (int*) m_gridSelectBox->wxItemContainer::GetClientData( index ); + + if( clientData != NULL ) + eventId = *clientData; + } + else + { + eventId = event.GetId(); + } + + int idx = eventId - ID_POPUP_GRID_LEVEL_1000; + + // Notify GAL + TOOL_MANAGER* mgr = GetToolManager(); + + if( mgr && IsGalCanvasActive() ) + { + mgr->RunAction( "common.Control.gridPreset", true, idx ); + } + else + SetPresetGrid( idx ); + + m_canvas->Refresh(); +} + + +void EDA_DRAW_FRAME::OnSelectZoom( wxCommandEvent& event ) +{ + if( m_zoomSelectBox == NULL ) + return; // Should not happen! + + int id = m_zoomSelectBox->GetCurrentSelection(); + + if( id < 0 || !( id < (int)m_zoomSelectBox->GetCount() ) ) + return; + + if( id == 0 ) // Auto zoom (Fit in Page) + { + Zoom_Automatique( true ); + } + else + { + id--; + double selectedZoom = GetScreen()->m_ZoomList[id]; + + if( GetScreen()->GetZoom() == selectedZoom ) + return; + + GetScreen()->SetZoom( selectedZoom ); + RedrawScreen( GetScrollCenterPosition(), false ); + } + + // Notify GAL + TOOL_MANAGER* mgr = GetToolManager(); + + if( mgr && IsGalCanvasActive() ) + { + mgr->RunAction( "common.Control.zoomPreset", true, id ); + UpdateStatusBar(); + } +} + + +double EDA_DRAW_FRAME::GetZoom() +{ + return GetScreen()->GetZoom(); +} + + +void EDA_DRAW_FRAME::OnMouseEvent( wxMouseEvent& event ) +{ + event.Skip(); +} + + +void EDA_DRAW_FRAME::OnLeftDClick( wxDC* DC, const wxPoint& MousePos ) +{ +} + + +void EDA_DRAW_FRAME::DisplayToolMsg( const wxString& msg ) +{ + SetStatusText( msg, 5 ); +} + + +void EDA_DRAW_FRAME::DisplayUnitsMsg() +{ + wxString msg; + + switch( g_UserUnit ) + { + case INCHES: + msg = _( "Inches" ); + break; + + case MILLIMETRES: + msg = _( "mm" ); + break; + + default: + msg = _( "Units" ); + break; + } + + SetStatusText( msg, 4 ); +} + + + +void EDA_DRAW_FRAME::OnSize( wxSizeEvent& SizeEv ) +{ + m_FrameSize = GetClientSize( ); + + SizeEv.Skip(); +} + + +void EDA_DRAW_FRAME::SetToolID( int aId, int aCursor, const wxString& aToolMsg ) +{ + // Keep default cursor in toolbars + SetCursor( wxNullCursor ); + + // Change m_canvas cursor if requested. + if( m_canvas && aCursor >= 0 ) + m_canvas->SetCurrentCursor( aCursor ); + + DisplayToolMsg( aToolMsg ); + + if( aId < 0 ) + return; + + wxCHECK2_MSG( aId >= ID_NO_TOOL_SELECTED, aId = ID_NO_TOOL_SELECTED, + wxString::Format( wxT( "Current tool ID cannot be set to %d." ), aId ) ); + + m_toolId = aId; +} + + +wxPoint EDA_DRAW_FRAME::GetGridPosition( const wxPoint& aPosition ) const +{ + wxPoint pos = aPosition; + + if( m_currentScreen != NULL && m_snapToGrid ) + pos = GetNearestGridPosition( aPosition ); + + return pos; +} + + +void EDA_DRAW_FRAME::SetNextGrid() +{ + BASE_SCREEN * screen = GetScreen(); + + int new_grid_cmd = screen->GetGridCmdId(); + + // if the grid id is the not the last, increment it + if( screen->GridExists( new_grid_cmd + 1 ) ) + new_grid_cmd += 1; + + SetPresetGrid( new_grid_cmd - ID_POPUP_GRID_LEVEL_1000 ); +} + + +void EDA_DRAW_FRAME::SetPrevGrid() +{ + BASE_SCREEN * screen = GetScreen(); + + int new_grid_cmd = screen->GetGridCmdId(); + + // if the grid id is the not the first, increment it + if( screen->GridExists( new_grid_cmd - 1 ) ) + new_grid_cmd -= 1; + + SetPresetGrid( new_grid_cmd - ID_POPUP_GRID_LEVEL_1000 ); +} + + +void EDA_DRAW_FRAME::SetPresetGrid( int aIndex ) +{ + BASE_SCREEN * screen = GetScreen(); + + if( ! screen->GridExists( aIndex + ID_POPUP_GRID_LEVEL_1000 ) ) + aIndex = screen->GetGrids()[0].m_CmdId; + + // aIndex is a Command Id relative to ID_POPUP_GRID_LEVEL_1000 comand id code. + // we need an index in grid list (the cmd id in list is is screen->GetGrids()[0].m_CmdId): + int glistIdx = aIndex + ID_POPUP_GRID_LEVEL_1000 - screen->GetGrids()[0].m_CmdId; + + if( m_gridSelectBox ) + { + if( glistIdx < 0 || glistIdx >= (int) m_gridSelectBox->GetCount() ) + { + wxASSERT_MSG( false, "Invalid grid index" ); + return; + } + + m_gridSelectBox->SetSelection( glistIdx ); + } + + // Be sure m_LastGridSizeId is up to date. + m_LastGridSizeId = aIndex; + GetScreen()->SetGrid( aIndex + ID_POPUP_GRID_LEVEL_1000 ); + + // Put cursor on new grid + SetCrossHairPosition( RefPos( true ) ); +} + + +int EDA_DRAW_FRAME::BlockCommand( int key ) +{ + return 0; +} + + +void EDA_DRAW_FRAME::InitBlockPasteInfos() +{ + GetScreen()->m_BlockLocate.ClearItemsList(); + m_canvas->SetMouseCaptureCallback( NULL ); +} + + +void EDA_DRAW_FRAME::HandleBlockPlace( wxDC* DC ) +{ +} + + +bool EDA_DRAW_FRAME::HandleBlockEnd( wxDC* DC ) +{ + return false; +} + + +void EDA_DRAW_FRAME::UpdateStatusBar() +{ + SetStatusText( GetZoomLevelIndicator(), 1 ); + + // Absolute and relative cursor positions are handled by overloading this function and + // handling the internal to user units conversion at the appropriate level. + + // refresh units display + DisplayUnitsMsg(); +} + +const wxString EDA_DRAW_FRAME::GetZoomLevelIndicator() const +{ + wxString Line; + double level = 0.0; + + if( IsGalCanvasActive() ) + { + KIGFX::GAL* gal = m_galCanvas->GetGAL(); + KIGFX::VIEW* view = m_galCanvas->GetView(); + double zoomFactor = gal->GetWorldScale() / gal->GetZoomFactor(); + level = m_zoomLevelCoeff * zoomFactor * view->GetScale(); + } + else if( BASE_SCREEN* screen = GetScreen() ) + { + level = m_zoomLevelCoeff / (double) screen->GetZoom(); + } + + // returns a human readable value which can be displayed as zoom + // level indicator in dialogs. + Line.Printf( wxT( "Z %.2f" ), level ); + + return Line; +} + + +void EDA_DRAW_FRAME::LoadSettings( wxConfigBase* aCfg ) +{ + EDA_BASE_FRAME::LoadSettings( aCfg ); + + wxString baseCfgName = ConfigBaseName(); + + aCfg->Read( baseCfgName + CursorShapeEntryKeyword, &m_cursorShape, ( long )0 ); + + bool btmp; + if( aCfg->Read( baseCfgName + ShowGridEntryKeyword, &btmp ) ) + SetGridVisibility( btmp ); + + int itmp; + if( aCfg->Read( baseCfgName + GridColorEntryKeyword, &itmp ) ) + SetGridColor( ColorFromInt( itmp ) ); + + aCfg->Read( baseCfgName + LastGridSizeIdKeyword, &m_LastGridSizeId, 0L ); + + // m_LastGridSizeId is an offset, expected to be >= 0 + if( m_LastGridSizeId < 0 ) + m_LastGridSizeId = 0; + + m_UndoRedoCountMax = aCfg->Read( baseCfgName + MaxUndoItemsEntry, + long( DEFAULT_MAX_UNDO_ITEMS ) ); +} + + +void EDA_DRAW_FRAME::SaveSettings( wxConfigBase* aCfg ) +{ + EDA_BASE_FRAME::SaveSettings( aCfg ); + + wxString baseCfgName = ConfigBaseName(); + + aCfg->Write( baseCfgName + CursorShapeEntryKeyword, m_cursorShape ); + aCfg->Write( baseCfgName + ShowGridEntryKeyword, IsGridVisible() ); + aCfg->Write( baseCfgName + GridColorEntryKeyword, ( long ) GetGridColor() ); + aCfg->Write( baseCfgName + LastGridSizeIdKeyword, ( long ) m_LastGridSizeId ); + aCfg->Write( baseCfgName + MaxUndoItemsEntry, long( GetScreen()->GetMaxUndoItems() ) ); +} + + +void EDA_DRAW_FRAME::AppendMsgPanel( const wxString& textUpper, + const wxString& textLower, + EDA_COLOR_T color, int pad ) +{ + if( m_messagePanel == NULL ) + return; + + m_messagePanel->AppendMessage( textUpper, textLower, color, pad ); +} + + +void EDA_DRAW_FRAME::ClearMsgPanel() +{ + if( m_messagePanel == NULL ) + return; + + m_messagePanel->EraseMsgBox(); +} + + +void EDA_DRAW_FRAME::SetMsgPanel( const MSG_PANEL_ITEMS& aList ) +{ + if( m_messagePanel == NULL ) + return; + + ClearMsgPanel(); + + for( unsigned i = 0; i < aList.size(); i++ ) + m_messagePanel->AppendMessage( aList[i] ); +} + + +void EDA_DRAW_FRAME::SetMsgPanel( EDA_ITEM* aItem ) +{ + wxCHECK_RET( aItem != NULL, wxT( "Invalid EDA_ITEM pointer. Bad programmer." ) ); + + MSG_PANEL_ITEMS items; + aItem->GetMsgPanelInfo( items ); + SetMsgPanel( items ); +} + + +void EDA_DRAW_FRAME::UpdateMsgPanel() +{ + EDA_ITEM* item = GetScreen()->GetCurItem(); + + if( item ) + SetMsgPanel( item ); +} + +// FIXME: There needs to be a better way for child windows to load preferences. +// This function pushes four preferences from a parent window to a child window +// i.e. from eeschema to the schematic symbol editor +void EDA_DRAW_FRAME::PushPreferences( const EDA_DRAW_PANEL* aParentCanvas ) +{ + m_canvas->SetEnableZoomNoCenter( aParentCanvas->GetEnableZoomNoCenter() ); + m_canvas->SetEnableMiddleButtonPan( aParentCanvas->GetEnableMiddleButtonPan() ); + m_canvas->SetMiddleButtonPanLimited( aParentCanvas->GetMiddleButtonPanLimited() ); + m_canvas->SetEnableAutoPan( aParentCanvas->GetEnableAutoPan() ); +} + +wxString EDA_DRAW_FRAME::CoordinateToString( int aValue, bool aConvertToMils ) const +{ + return ::CoordinateToString( aValue, aConvertToMils ); +} + +wxString EDA_DRAW_FRAME::LengthDoubleToString( double aValue, bool aConvertToMils ) const +{ + return ::LengthDoubleToString( aValue, aConvertToMils ); +} + + +bool EDA_DRAW_FRAME::HandleBlockBegin( wxDC* aDC, int aKey, const wxPoint& aPosition ) +{ + BLOCK_SELECTOR* block = &GetScreen()->m_BlockLocate; + + if( ( block->GetCommand() != BLOCK_IDLE ) || ( block->GetState() != STATE_NO_BLOCK ) ) + return false; + + block->SetCommand( (BLOCK_COMMAND_T) BlockCommand( aKey ) ); + + if( block->GetCommand() == 0 ) + return false; + + switch( block->GetCommand() ) + { + case BLOCK_IDLE: + break; + + case BLOCK_MOVE: // Move + case BLOCK_DRAG: // Drag (block defined) + case BLOCK_DRAG_ITEM: // Drag from a drag item command + case BLOCK_COPY: // Copy + case BLOCK_COPY_AND_INCREMENT: // Copy and increment relevant references + case BLOCK_DELETE: // Delete + case BLOCK_SAVE: // Save + case BLOCK_ROTATE: // Rotate 90 deg + case BLOCK_FLIP: // Flip + case BLOCK_ZOOM: // Window Zoom + case BLOCK_MIRROR_X: + case BLOCK_MIRROR_Y: // mirror + case BLOCK_PRESELECT_MOVE: // Move with preselection list + block->InitData( m_canvas, aPosition ); + break; + + case BLOCK_PASTE: + block->InitData( m_canvas, aPosition ); + block->SetLastCursorPosition( wxPoint( 0, 0 ) ); + InitBlockPasteInfos(); + + if( block->GetCount() == 0 ) // No data to paste + { + DisplayError( this, wxT( "No block to paste" ), 20 ); + GetScreen()->m_BlockLocate.SetCommand( BLOCK_IDLE ); + m_canvas->SetMouseCaptureCallback( NULL ); + return true; + } + + if( !m_canvas->IsMouseCaptured() ) + { + block->ClearItemsList(); + DisplayError( this, + wxT( "EDA_DRAW_FRAME::HandleBlockBegin() Err: m_mouseCaptureCallback NULL" ) ); + return true; + } + + block->SetState( STATE_BLOCK_MOVE ); + m_canvas->CallMouseCapture( aDC, aPosition, false ); + break; + + default: + { + wxString msg; + msg << wxT( "EDA_DRAW_FRAME::HandleBlockBegin() error: Unknown command " ) << + block->GetCommand(); + DisplayError( this, msg ); + } + break; + } + + block->SetMessageBlock( this ); + return true; +} + + +// I am not seeing a problem with this size yet: +static const double MAX_AXIS = INT_MAX - 100; + +#define VIRT_MIN (-MAX_AXIS/2.0) ///< min X or Y coordinate in virtual space +#define VIRT_MAX (MAX_AXIS/2.0) ///< max X or Y coordinate in virtual space + + +void EDA_DRAW_FRAME::AdjustScrollBars( const wxPoint& aCenterPositionIU ) +{ + BASE_SCREEN* screen = GetScreen(); + + if( !screen || !m_canvas ) + return; + + double scale = screen->GetScalingFactor(); + + wxLogTrace( traceScrollSettings, wxT( "Center Position = ( %d, %d ), scale = %.10g" ), + aCenterPositionIU.x, aCenterPositionIU.y, scale ); + + // Calculate the portion of the drawing that can be displayed in the + // client area at the current zoom level. + + // visible viewport in device units ~ pixels + wxSize clientSizeDU = m_canvas->GetClientSize(); + + // Size of the client window in IU + DSIZE clientSizeIU( clientSizeDU.x / scale, clientSizeDU.y / scale ); + + // Full drawing or "page" rectangle in internal units + DBOX pageRectIU( wxPoint( 0, 0 ), wxSize( GetPageSizeIU().x, GetPageSizeIU().y ) ); + + // The upper left corner of the client rectangle in internal units. + double xIU = aCenterPositionIU.x - clientSizeIU.x / 2.0; + double yIU = aCenterPositionIU.y - clientSizeIU.y / 2.0; + + // If drawn around the center, adjust the client rectangle accordingly. + if( screen->m_Center ) + { + // half page offset. + xIU += pageRectIU.GetWidth() / 2.0; + yIU += pageRectIU.GetHeight() / 2.0; + } + + DBOX clientRectIU( wxPoint( xIU, yIU ), wxSize( clientSizeIU.x, clientSizeIU.y ) ); + wxPoint centerPositionIU; + + // put "int" limits on the clientRect + if( clientRectIU.GetLeft() < VIRT_MIN ) + clientRectIU.MoveLeftTo( VIRT_MIN ); + if( clientRectIU.GetTop() < VIRT_MIN ) + clientRectIU.MoveTopTo( VIRT_MIN ); + if( clientRectIU.GetRight() > VIRT_MAX ) + clientRectIU.MoveRightTo( VIRT_MAX ); + if( clientRectIU.GetBottom() > VIRT_MAX ) + clientRectIU.MoveBottomTo( VIRT_MAX ); + + centerPositionIU.x = KiROUND( clientRectIU.GetX() + clientRectIU.GetWidth() / 2 ); + centerPositionIU.y = KiROUND( clientRectIU.GetY() + clientRectIU.GetHeight() / 2 ); + + if( screen->m_Center ) + { + centerPositionIU.x -= KiROUND( pageRectIU.GetWidth() / 2.0 ); + centerPositionIU.y -= KiROUND( pageRectIU.GetHeight() / 2.0 ); + } + + DSIZE virtualSizeIU; + + if( pageRectIU.GetLeft() < clientRectIU.GetLeft() && pageRectIU.GetRight() > clientRectIU.GetRight() ) + { + virtualSizeIU.x = pageRectIU.GetSize().x; + } + else + { + double pageCenterX = pageRectIU.GetX() + ( pageRectIU.GetWidth() / 2 ); + double clientCenterX = clientRectIU.GetX() + ( clientRectIU.GetWidth() / 2 ); + + if( clientRectIU.GetWidth() > pageRectIU.GetWidth() ) + { + if( pageCenterX > clientCenterX ) + virtualSizeIU.x = ( pageCenterX - clientRectIU.GetLeft() ) * 2; + else if( pageCenterX < clientCenterX ) + virtualSizeIU.x = ( clientRectIU.GetRight() - pageCenterX ) * 2; + else + virtualSizeIU.x = clientRectIU.GetWidth(); + } + else + { + if( pageCenterX > clientCenterX ) + virtualSizeIU.x = pageRectIU.GetWidth() + ( (pageRectIU.GetLeft() - clientRectIU.GetLeft() ) * 2 ); + else if( pageCenterX < clientCenterX ) + virtualSizeIU.x = pageRectIU.GetWidth() + ( (clientRectIU.GetRight() - pageRectIU.GetRight() ) * 2 ); + else + virtualSizeIU.x = pageRectIU.GetWidth(); + } + } + + if( pageRectIU.GetTop() < clientRectIU.GetTop() && pageRectIU.GetBottom() > clientRectIU.GetBottom() ) + { + virtualSizeIU.y = pageRectIU.GetSize().y; + } + else + { + double pageCenterY = pageRectIU.GetY() + ( pageRectIU.GetHeight() / 2 ); + double clientCenterY = clientRectIU.GetY() + ( clientRectIU.GetHeight() / 2 ); + + if( clientRectIU.GetHeight() > pageRectIU.GetHeight() ) + { + if( pageCenterY > clientCenterY ) + virtualSizeIU.y = ( pageCenterY - clientRectIU.GetTop() ) * 2; + else if( pageCenterY < clientCenterY ) + virtualSizeIU.y = ( clientRectIU.GetBottom() - pageCenterY ) * 2; + else + virtualSizeIU.y = clientRectIU.GetHeight(); + } + else + { + if( pageCenterY > clientCenterY ) + virtualSizeIU.y = pageRectIU.GetHeight() + + ( ( pageRectIU.GetTop() - clientRectIU.GetTop() ) * 2 ); + else if( pageCenterY < clientCenterY ) + virtualSizeIU.y = pageRectIU.GetHeight() + + ( ( clientRectIU.GetBottom() - pageRectIU.GetBottom() ) * 2 ); + else + virtualSizeIU.y = pageRectIU.GetHeight(); + } + } + + // put "int" limits on the virtualSizeIU + virtualSizeIU.x = std::min( virtualSizeIU.x, MAX_AXIS ); + virtualSizeIU.y = std::min( virtualSizeIU.y, MAX_AXIS ); + + if( screen->m_Center ) + { + screen->m_DrawOrg.x = -KiROUND( virtualSizeIU.x / 2.0 ); + screen->m_DrawOrg.y = -KiROUND( virtualSizeIU.y / 2.0 ); + } + else + { + screen->m_DrawOrg.x = -KiROUND( ( virtualSizeIU.x - pageRectIU.GetWidth() ) / 2.0 ); + screen->m_DrawOrg.y = -KiROUND( ( virtualSizeIU.y - pageRectIU.GetHeight() ) / 2.0 ); + } + + /* Always set scrollbar pixels per unit to 1 unless you want the zoom + * around cursor to jump around. This reported problem occurs when the + * zoom point is not on a pixel per unit increment. If you set the + * pixels per unit to 10, you have potential for the zoom point to + * jump around +/-5 pixels from the nearest grid point. + */ + screen->m_ScrollPixelsPerUnitX = screen->m_ScrollPixelsPerUnitY = 1; + + // Number of scroll bar units for the given zoom level in device units. + double unitsX = virtualSizeIU.x * scale; + double unitsY = virtualSizeIU.y * scale; + + // Calculate the scroll bar position in internal units to place the + // center position at the center of client rectangle. + SetScrollCenterPosition( centerPositionIU ); + + double posX = centerPositionIU.x - clientRectIU.GetWidth() / 2.0 - screen->m_DrawOrg.x; + double posY = centerPositionIU.y - clientRectIU.GetHeight() / 2.0 - screen->m_DrawOrg.y; + + // Convert scroll bar position to device units. + posX = KiROUND( posX * scale ); + posY = KiROUND( posY * scale ); + + if( posX < 0 ) + { + wxLogTrace( traceScrollSettings, wxT( "Required scroll bar X position %.10g" ), posX ); + posX = 0; + } + + if( posX > unitsX ) + { + wxLogTrace( traceScrollSettings, wxT( "Required scroll bar X position %.10g" ), posX ); + posX = unitsX; + } + + if( posY < 0 ) + { + wxLogTrace( traceScrollSettings, wxT( "Required scroll bar Y position %.10g" ), posY ); + posY = 0; + } + + if( posY > unitsY ) + { + wxLogTrace( traceScrollSettings, wxT( "Required scroll bar Y position %.10g" ), posY ); + posY = unitsY; + } + + screen->m_ScrollbarPos = wxPoint( KiROUND( posX ), KiROUND( posY ) ); + screen->m_ScrollbarNumber = wxSize( KiROUND( unitsX ), KiROUND( unitsY ) ); + + wxLogTrace( traceScrollSettings, + wxT( "Drawing = (%.10g, %.10g), Client = (%.10g, %.10g), Offset = (%d, %d), SetScrollbars(%d, %d, %d, %d, %d, %d)" ), + virtualSizeIU.x, virtualSizeIU.y, clientSizeIU.x, clientSizeIU.y, + screen->m_DrawOrg.x, screen->m_DrawOrg.y, + screen->m_ScrollPixelsPerUnitX, screen->m_ScrollPixelsPerUnitY, + screen->m_ScrollbarNumber.x, screen->m_ScrollbarNumber.y, + screen->m_ScrollbarPos.x, screen->m_ScrollbarPos.y ); + + bool noRefresh = true; + + m_canvas->SetScrollbars( screen->m_ScrollPixelsPerUnitX, + screen->m_ScrollPixelsPerUnitY, + screen->m_ScrollbarNumber.x, + screen->m_ScrollbarNumber.y, + screen->m_ScrollbarPos.x, + screen->m_ScrollbarPos.y, noRefresh ); +} + + +void EDA_DRAW_FRAME::UseGalCanvas( bool aEnable ) +{ + KIGFX::VIEW* view = GetGalCanvas()->GetView(); + KIGFX::GAL* gal = GetGalCanvas()->GetGAL(); + + // Display the same view after canvas switching + if( aEnable ) + { + // Switch to GAL renderer from legacy + if( !m_galCanvasActive ) + { + // Set up viewport + double zoomFactor = gal->GetWorldScale() / gal->GetZoomFactor(); + double zoom = 1.0 / ( zoomFactor * m_canvas->GetZoom() ); + view->SetScale( zoom ); + view->SetCenter( VECTOR2D( m_canvas->GetScreenCenterLogicalPosition() ) ); + } + + // Set up grid settings + gal->SetGridVisibility( IsGridVisible() ); + gal->SetGridSize( VECTOR2D( GetScreen()->GetGridSize() ) ); + gal->SetGridOrigin( VECTOR2D( GetGridOrigin() ) ); + + // Transfer EDA_DRAW_PANEL settings + GetGalCanvas()->GetViewControls()->EnableCursorWarping( !m_canvas->GetEnableZoomNoCenter() ); + GetGalCanvas()->GetViewControls()->EnableMousewheelPan( m_canvas->GetEnableMousewheelPan() ); + GetToolManager()->RunAction( "pcbnew.Control.switchCursor" ); + } + else if( m_galCanvasActive ) + { + // Switch to legacy renderer from GAL + double zoomFactor = gal->GetWorldScale() / gal->GetZoomFactor(); + // TODO replace it with EDA_DRAW_PANEL_GAL::GetLegacyZoom + m_canvas->SetZoom( 1.0 / ( zoomFactor * view->GetScale() ) ); + VECTOR2D center = view->GetCenter(); + AdjustScrollBars( wxPoint( center.x, center.y ) ); + } + + m_canvas->SetEvtHandlerEnabled( !aEnable ); + GetGalCanvas()->SetEvtHandlerEnabled( aEnable ); + + // Switch panes + m_auimgr.GetPane( wxT( "DrawFrame" ) ).Show( !aEnable ); + m_auimgr.GetPane( wxT( "DrawFrameGal" ) ).Show( aEnable ); + m_auimgr.Update(); + + // Reset current tool on switch(); + SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString ); + + m_galCanvasActive = aEnable; +} + +//-----< BASE_SCREEN API moved here >-------------------------------------------- + +wxPoint EDA_DRAW_FRAME::GetCrossHairPosition( bool aInvertY ) const +{ + // subject to change, borrow from old BASE_SCREEN for now. + if( IsGalCanvasActive() ) + { + VECTOR2I cursor = GetGalCanvas()->GetViewControls()->GetCursorPosition(); + + return wxPoint( cursor.x, cursor.y ); + } + else + { + BASE_SCREEN* screen = GetScreen(); // virtual call + return screen->getCrossHairPosition( aInvertY ); + } +} + + +void EDA_DRAW_FRAME::SetCrossHairPosition( const wxPoint& aPosition, bool aSnapToGrid ) +{ + BASE_SCREEN* screen = GetScreen(); // virtual call + screen->setCrossHairPosition( aPosition, GetGridOrigin(), aSnapToGrid ); +} + + +wxPoint EDA_DRAW_FRAME::GetCursorPosition( bool aOnGrid, wxRealPoint* aGridSize ) const +{ + BASE_SCREEN* screen = GetScreen(); // virtual call + return screen->getCursorPosition( aOnGrid, GetGridOrigin(), aGridSize ); +} + + +wxPoint EDA_DRAW_FRAME::GetNearestGridPosition( const wxPoint& aPosition, wxRealPoint* aGridSize ) const +{ + BASE_SCREEN* screen = GetScreen(); // virtual call + return screen->getNearestGridPosition( aPosition, GetGridOrigin(), aGridSize ); +} + + +wxPoint EDA_DRAW_FRAME::GetCrossHairScreenPosition() const +{ + BASE_SCREEN* screen = GetScreen(); // virtual call + return screen->getCrossHairScreenPosition(); +} + + +void EDA_DRAW_FRAME::SetMousePosition( const wxPoint& aPosition ) +{ + BASE_SCREEN* screen = GetScreen(); // virtual call + screen->setMousePosition( aPosition ); +} + + +wxPoint EDA_DRAW_FRAME::RefPos( bool useMouse ) const +{ + BASE_SCREEN* screen = GetScreen(); // virtual call + return screen->refPos( useMouse ); +} + + +const wxPoint& EDA_DRAW_FRAME::GetScrollCenterPosition() const +{ + BASE_SCREEN* screen = GetScreen(); // virtual call + return screen->getScrollCenterPosition(); +} + + +void EDA_DRAW_FRAME::SetScrollCenterPosition( const wxPoint& aPoint ) +{ + BASE_SCREEN* screen = GetScreen(); // virtual call + screen->setScrollCenterPosition( aPoint ); +} + +//-----</BASE_SCREEN API moved here >-------------------------------------------- + +void EDA_DRAW_FRAME::RefreshCrossHair( const wxPoint &aOldPos, + const wxPoint &aEvtPos, + wxDC* aDC ) +{ + wxPoint newpos = GetCrossHairPosition(); + + // Redraw the crosshair if it moved + if( aOldPos != newpos ) + { + SetCrossHairPosition( aOldPos, false ); + m_canvas->CrossHairOff( aDC ); + SetCrossHairPosition( newpos, false ); + m_canvas->CrossHairOn( aDC ); + + if( m_canvas->IsMouseCaptured() ) + { +#ifdef USE_WX_OVERLAY + wxDCOverlay oDC( m_overlay, (wxWindowDC*)aDC ); + oDC.Clear(); + m_canvas->CallMouseCapture( aDC, aEvtPos, false ); +#else + m_canvas->CallMouseCapture( aDC, aEvtPos, true ); +#endif + } +#ifdef USE_WX_OVERLAY + else + { + m_overlay.Reset(); + } +#endif + } +} + +void EDA_DRAW_FRAME::GeneralControlKeyMovement( int aHotKey, wxPoint *aPos, + bool aSnapToGrid ) +{ + + // If requested snap the current position to the grid + if( aSnapToGrid ) + *aPos = GetNearestGridPosition( *aPos ); + + switch( aHotKey ) + { + // All these keys have almost the same treatment + case GR_KB_CTRL | WXK_NUMPAD8: + case GR_KB_CTRL | WXK_UP: + case GR_KB_CTRL | WXK_NUMPAD2: + case GR_KB_CTRL | WXK_DOWN: + case GR_KB_CTRL | WXK_NUMPAD4: + case GR_KB_CTRL | WXK_LEFT: + case GR_KB_CTRL | WXK_NUMPAD6: + case GR_KB_CTRL | WXK_RIGHT: + case WXK_NUMPAD8: + case WXK_UP: + case WXK_NUMPAD2: + case WXK_DOWN: + case WXK_NUMPAD4: + case WXK_LEFT: + case WXK_NUMPAD6: + case WXK_RIGHT: + { + /* Here's a tricky part: when doing cursor key movement, the + * 'previous' point should be taken from memory, *not* from the + * freshly computed position in the event. Otherwise you can't do + * sub-pixel movement. The m_movingCursorWithKeyboard oneshot 'eats' + * the automatic motion event generated by cursor warping */ + wxRealPoint gridSize = GetScreen()->GetGridSize(); + *aPos = GetCrossHairPosition(); + + // Bonus: ^key moves faster (x10) + switch( aHotKey ) + { + case GR_KB_CTRL|WXK_NUMPAD8: + case GR_KB_CTRL|WXK_UP: + aPos->y -= KiROUND( 10 * gridSize.y ); + break; + + case GR_KB_CTRL|WXK_NUMPAD2: + case GR_KB_CTRL|WXK_DOWN: + aPos->y += KiROUND( 10 * gridSize.y ); + break; + + case GR_KB_CTRL|WXK_NUMPAD4: + case GR_KB_CTRL|WXK_LEFT: + aPos->x -= KiROUND( 10 * gridSize.x ); + break; + + case GR_KB_CTRL|WXK_NUMPAD6: + case GR_KB_CTRL|WXK_RIGHT: + aPos->x += KiROUND( 10 * gridSize.x ); + break; + + case WXK_NUMPAD8: + case WXK_UP: + aPos->y -= KiROUND( gridSize.y ); + break; + + case WXK_NUMPAD2: + case WXK_DOWN: + aPos->y += KiROUND( gridSize.y ); + break; + + case WXK_NUMPAD4: + case WXK_LEFT: + aPos->x -= KiROUND( gridSize.x ); + break; + + case WXK_NUMPAD6: + case WXK_RIGHT: + aPos->x += KiROUND( gridSize.x ); + break; + + default: /* Can't happen since we entered the statement */ + break; + } + m_canvas->MoveCursor( *aPos ); + m_movingCursorWithKeyboard = true; + } + break; + + default: + break; + } +} + diff --git a/common/draw_panel.cpp b/common/draw_panel.cpp new file mode 100644 index 0000000..3bd1103 --- /dev/null +++ b/common/draw_panel.cpp @@ -0,0 +1,1619 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2009 Jean-Pierre Charras, jean-pierre.charras@gipsa-lab.inpg.fr + * Copyright (C) 2007-2011 Wayne Stambaugh <stambaughw@verizon.net> + * Copyright (C) 1992-2016 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 + */ + +/** + * @file draw_panel.cpp + */ + +#include <fctsys.h> +#include <pgm_base.h> +#include <kiface_i.h> +#include <gr_basic.h> +#include <common.h> +#include <macros.h> +#include <id.h> +#include <class_drawpanel.h> +#include <class_draw_panel_gal.h> +#include <class_base_screen.h> +#include <draw_frame.h> +#include <view/view_controls.h> + +#include <kicad_device_context.h> + +static const int CURSOR_SIZE = 12; ///< Cursor size in pixels + +#define CLIP_BOX_PADDING 2 + +// keys to store options in config: +#define ENBL_ZOOM_NO_CENTER_KEY wxT( "ZoomNoCenter" ) +#define ENBL_MOUSEWHEEL_PAN_KEY wxT( "MousewheelPAN" ) +#define ENBL_MIDDLE_BUTT_PAN_KEY wxT( "MiddleButtonPAN" ) +#define MIDDLE_BUTT_PAN_LIMITED_KEY wxT( "MiddleBtnPANLimited" ) +#define ENBL_AUTO_PAN_KEY wxT( "AutoPAN" ) + + +// Definitions for enabling and disabling debugging features in drawpanel.cpp. +// Please don't forget to turn these off before making any commits to Launchpad. +#define DEBUG_SHOW_CLIP_RECT 0 // Set to 1 to draw clipping rectangle. + + +/** + * Trace mask used to enable or disable the trace output of coordinates during drawing + * functions. The coordinate dumping can be turned on by setting the WXTRACE environment + * variable to "kicad_dump_coords". See the wxWidgets documentation on wxLogTrace for + * more information. + */ +#define KICAD_TRACE_COORDS wxT( "kicad_dump_coords" ) + + +// Events used by EDA_DRAW_PANEL +BEGIN_EVENT_TABLE( EDA_DRAW_PANEL, wxScrolledWindow ) + EVT_LEAVE_WINDOW( EDA_DRAW_PANEL::OnMouseLeaving ) + EVT_ENTER_WINDOW( EDA_DRAW_PANEL::OnMouseEntering ) + EVT_MOUSEWHEEL( EDA_DRAW_PANEL::OnMouseWheel ) +#ifdef USE_OSX_MAGNIFY_EVENT + EVT_MAGNIFY( EDA_DRAW_PANEL::OnMagnify ) +#endif + EVT_MOUSE_EVENTS( EDA_DRAW_PANEL::OnMouseEvent ) + EVT_CHAR( EDA_DRAW_PANEL::OnKeyEvent ) + EVT_CHAR_HOOK( EDA_DRAW_PANEL::OnCharHook ) + EVT_PAINT( EDA_DRAW_PANEL::OnPaint ) + EVT_ERASE_BACKGROUND( EDA_DRAW_PANEL::OnEraseBackground ) + EVT_SCROLLWIN( EDA_DRAW_PANEL::OnScroll ) + EVT_ACTIVATE( EDA_DRAW_PANEL::OnActivate ) + EVT_MENU_RANGE( ID_PAN_UP, ID_PAN_RIGHT, EDA_DRAW_PANEL::OnPan ) +END_EVENT_TABLE() + + +/***********************************************************************/ +/* EDA_DRAW_PANEL base functions (EDA_DRAW_PANEL is the main panel)*/ +/***********************************************************************/ + +EDA_DRAW_PANEL::EDA_DRAW_PANEL( EDA_DRAW_FRAME* parent, int id, + const wxPoint& pos, const wxSize& size ) : + wxScrolledWindow( parent, id, pos, size, wxBORDER | wxHSCROLL | wxVSCROLL ) +{ + wxASSERT( parent ); + + ShowScrollbars( wxSHOW_SB_ALWAYS, wxSHOW_SB_ALWAYS ); + DisableKeyboardScrolling(); + + m_scrollIncrementX = std::min( size.x / 8, 10 ); + m_scrollIncrementY = std::min( size.y / 8, 10 ); + + SetLayoutDirection( wxLayout_LeftToRight ); + + SetBackgroundColour( MakeColour( parent->GetDrawBgColor() ) ); + +#if KICAD_USE_BUFFERED_DC || KICAD_USE_BUFFERED_PAINTDC + SetBackgroundStyle( wxBG_STYLE_CUSTOM ); +#endif + + m_ClipBox.SetSize( size ); + m_ClipBox.SetX( 0 ); + m_ClipBox.SetY( 0 ); + m_canStartBlock = -1; // Command block can start if >= 0 + m_abortRequest = false; + m_enableMousewheelPan = false; + m_enableMiddleButtonPan = true; + m_enableZoomNoCenter = false; + m_panScrollbarLimits = false; + m_enableAutoPan = true; + m_ignoreMouseEvents = false; + // Be sure a mouse release button event will be ignored when creating the canvas + // if the mouse click was not made inside the canvas (can happen sometimes, when + // launching an editor from a double click made in an other frame) + m_ignoreNextLeftButtonRelease = true; + + m_mouseCaptureCallback = NULL; + m_endMouseCaptureCallback = NULL; + + wxConfigBase* cfg = Kiface().KifaceSettings(); + + if( cfg ) + { + cfg->Read( ENBL_MOUSEWHEEL_PAN_KEY, &m_enableMousewheelPan, false ); + cfg->Read( ENBL_MIDDLE_BUTT_PAN_KEY, &m_enableMiddleButtonPan, true ); + cfg->Read( ENBL_ZOOM_NO_CENTER_KEY, &m_enableZoomNoCenter, false ); + cfg->Read( MIDDLE_BUTT_PAN_LIMITED_KEY, &m_panScrollbarLimits, false ); + cfg->Read( ENBL_AUTO_PAN_KEY, &m_enableAutoPan, true ); + } + + m_requestAutoPan = false; + m_enableBlockCommands = false; + m_minDragEventCount = 0; + +#ifdef __WXMAC__ + m_defaultCursor = m_currentCursor = wxCURSOR_CROSS; + m_showCrossHair = false; +#else + m_defaultCursor = m_currentCursor = wxCURSOR_ARROW; + m_showCrossHair = true; +#endif + + m_cursorLevel = 0; + m_PrintIsMirrored = false; +} + + +EDA_DRAW_PANEL::~EDA_DRAW_PANEL() +{ + wxConfigBase* cfg = Kiface().KifaceSettings(); + + if( cfg ) + { + cfg->Write( ENBL_MOUSEWHEEL_PAN_KEY, m_enableMousewheelPan ); + cfg->Write( ENBL_MIDDLE_BUTT_PAN_KEY, m_enableMiddleButtonPan ); + cfg->Write( ENBL_ZOOM_NO_CENTER_KEY, m_enableZoomNoCenter ); + cfg->Write( MIDDLE_BUTT_PAN_LIMITED_KEY, m_panScrollbarLimits ); + cfg->Write( ENBL_AUTO_PAN_KEY, m_enableAutoPan ); + } +} + + +EDA_DRAW_FRAME* EDA_DRAW_PANEL::GetParent() const +{ + wxWindow* mom = wxScrolledWindow::GetParent(); + return (EDA_DRAW_FRAME*) mom; +} + + +void* EDA_DRAW_PANEL::GetDisplayOptions() +{ + return GetParent()->GetDisplayOptions(); +} + + +BASE_SCREEN* EDA_DRAW_PANEL::GetScreen() +{ + EDA_DRAW_FRAME* parentFrame = GetParent(); + + return parentFrame->GetScreen(); +} + + +wxPoint EDA_DRAW_PANEL::ToDeviceXY( const wxPoint& pos ) +{ + wxPoint ret; + INSTALL_UNBUFFERED_DC( dc, this ); + ret.x = dc.LogicalToDeviceX( pos.x ); + ret.y = dc.LogicalToDeviceY( pos.y ); + return ret; +} + + +wxPoint EDA_DRAW_PANEL::ToLogicalXY( const wxPoint& pos ) +{ + wxPoint ret; + INSTALL_UNBUFFERED_DC( dc, this ); + ret.x = dc.DeviceToLogicalX( pos.x ); + ret.y = dc.DeviceToLogicalY( pos.y ); + return ret; +} + + +void EDA_DRAW_PANEL::DrawCrossHair( wxDC* aDC, EDA_COLOR_T aColor ) +{ + if( m_cursorLevel != 0 || aDC == NULL || !m_showCrossHair ) + return; + + wxPoint cursor = GetParent()->GetCrossHairPosition(); + + GRSetDrawMode( aDC, GR_XOR ); + + if( GetParent()->m_cursorShape != 0 ) // Draws full screen crosshair. + { + wxSize clientSize = GetClientSize(); + + // Y axis + wxPoint lineStart( cursor.x, aDC->DeviceToLogicalY( 0 ) ); + wxPoint lineEnd( cursor.x, aDC->DeviceToLogicalY( clientSize.y ) ); + + GRLine( &m_ClipBox, aDC, lineStart, lineEnd, 0, aColor ); + + // X axis + lineStart = wxPoint( aDC->DeviceToLogicalX( 0 ), cursor.y ); + lineEnd = wxPoint( aDC->DeviceToLogicalX( clientSize.x ), cursor.y ); + + GRLine( &m_ClipBox, aDC, lineStart, lineEnd, 0, aColor ); + } + else + { + int len = aDC->DeviceToLogicalXRel( CURSOR_SIZE ); + + GRLine( &m_ClipBox, aDC, cursor.x - len, cursor.y, + cursor.x + len, cursor.y, 0, aColor ); + GRLine( &m_ClipBox, aDC, cursor.x, cursor.y - len, + cursor.x, cursor.y + len, 0, aColor ); + } +} + + +void EDA_DRAW_PANEL::CrossHairOff( wxDC* DC ) +{ + DrawCrossHair( DC ); + --m_cursorLevel; +} + + +void EDA_DRAW_PANEL::CrossHairOn( wxDC* DC ) +{ + ++m_cursorLevel; + DrawCrossHair( DC ); + + if( m_cursorLevel > 0 ) // Shouldn't happen, but just in case .. + m_cursorLevel = 0; +} + + +double EDA_DRAW_PANEL::GetZoom() +{ + return GetScreen()->GetZoom(); +} + + +void EDA_DRAW_PANEL::SetZoom( double zoom ) +{ + GetScreen()->SetZoom( zoom ); +} + + +wxRealPoint EDA_DRAW_PANEL::GetGrid() +{ + return GetScreen()->GetGridSize(); +} + + +bool EDA_DRAW_PANEL::IsPointOnDisplay( const wxPoint& aPosition ) +{ + wxPoint pos; + EDA_RECT display_rect; + + INSTALL_UNBUFFERED_DC( dc, this ); // Refresh the clip box to the entire screen size. + SetClipBox( dc ); + + display_rect = m_ClipBox; + + // Slightly decreased the size of the useful screen area to avoid drawing limits. + #define PIXEL_MARGIN 8 + display_rect.Inflate( -PIXEL_MARGIN ); + + return display_rect.Contains( aPosition ); +} + + +void EDA_DRAW_PANEL::RefreshDrawingRect( const EDA_RECT& aRect, bool aEraseBackground ) +{ + INSTALL_UNBUFFERED_DC( dc, this ); + + wxRect rect = aRect; + + rect.x = dc.LogicalToDeviceX( rect.x ); + rect.y = dc.LogicalToDeviceY( rect.y ); + rect.width = dc.LogicalToDeviceXRel( rect.width ); + rect.height = dc.LogicalToDeviceYRel( rect.height ); + + wxLogTrace( KICAD_TRACE_COORDS, + wxT( "Refresh area: drawing (%d, %d, %d, %d), device (%d, %d, %d, %d)" ), + aRect.GetX(), aRect.GetY(), aRect.GetWidth(), aRect.GetHeight(), + rect.x, rect.y, rect.width, rect.height ); + + RefreshRect( rect, aEraseBackground ); +} + + +void EDA_DRAW_PANEL::Refresh( bool eraseBackground, const wxRect* rect ) +{ + if( GetParent()->IsGalCanvasActive() ) + { + GetParent()->GetGalCanvas()->Refresh(); + } + else + { + wxScrolledWindow::Refresh( eraseBackground, rect ); + } +} + + +wxPoint EDA_DRAW_PANEL::GetScreenCenterLogicalPosition() +{ + wxSize size = GetClientSize() / 2; + INSTALL_UNBUFFERED_DC( dc, this ); + + return wxPoint( dc.DeviceToLogicalX( size.x ), dc.DeviceToLogicalY( size.y ) ); +} + + +void EDA_DRAW_PANEL::MoveCursorToCrossHair() +{ + MoveCursor( GetParent()->GetCrossHairPosition() ); +} + + +void EDA_DRAW_PANEL::MoveCursor( const wxPoint& aPosition ) +{ + if( GetParent()->IsGalCanvasActive() ) + return; + + int x, y, xPpu, yPpu; + wxPoint screenPos, drawingPos; + wxRect clientRect( wxPoint( 0, 0 ), GetClientSize() ); + + INSTALL_UNBUFFERED_DC( dc, this ); + screenPos.x = dc.LogicalToDeviceX( aPosition.x ); + screenPos.y = dc.LogicalToDeviceY( aPosition.y ); + + // Scroll if the requested mouse position cursor is outside the drawing area. + if( !clientRect.Contains( screenPos ) ) + { + GetViewStart( &x, &y ); + GetScrollPixelsPerUnit( &xPpu, &yPpu ); + CalcUnscrolledPosition( screenPos.x, screenPos.y, &drawingPos.x, &drawingPos.y ); + + wxLogTrace( KICAD_TRACE_COORDS, + wxT( "MoveCursor() initial screen position(%d, %d) " ) \ + wxT( "rectangle(%d, %d, %d, %d) view(%d, %d)" ), + screenPos.x, screenPos.y, clientRect.x, clientRect.y, + clientRect.width, clientRect.height, x, y ); + + if( screenPos.y < clientRect.GetTop() ) + y -= m_scrollIncrementY * yPpu; + else if( screenPos.y > clientRect.GetBottom() ) + y += m_scrollIncrementY * yPpu; + else if( clientRect.GetRight() < screenPos.x ) + x += m_scrollIncrementX * xPpu; + else + x -= m_scrollIncrementX * xPpu; + + Scroll( x, y ); + CalcScrolledPosition( drawingPos.x, drawingPos.y, &screenPos.x, &screenPos.y ); + + wxLogTrace( KICAD_TRACE_COORDS, + wxT( "MoveCursor() scrolled screen position(%d, %d) view(%d, %d)" ), + screenPos.x, screenPos.y, x, y ); + } + + WarpPointer( screenPos.x, screenPos.y ); +} + + +void EDA_DRAW_PANEL::OnActivate( wxActivateEvent& event ) +{ + m_canStartBlock = -1; // Block Command can't start + event.Skip(); +} + + +void EDA_DRAW_PANEL::OnScroll( wxScrollWinEvent& event ) +{ + int id = event.GetEventType(); + int x, y; + int ppux, ppuy; + int csizeX, csizeY; + int unitsX, unitsY; + + GetViewStart( &x, &y ); + GetScrollPixelsPerUnit( &ppux, &ppuy ); + GetClientSize( &csizeX, &csizeY ); + GetVirtualSize( &unitsX, &unitsY ); + + int tmpX = x; + int tmpY = y; + + csizeX /= ppux; + csizeY /= ppuy; + + unitsX /= ppux; + unitsY /= ppuy; + + int dir = event.GetOrientation(); // wxHORIZONTAL or wxVERTICAL + + // On windows and on wxWidgets >= 2.9.5 and < 3.1, + // there is a bug in mousewheel event which always generates 2 scroll events + // (should be the case only for the default mousewheel event) + // with id = wxEVT_SCROLLWIN_LINEUP or wxEVT_SCROLLWIN_LINEDOWN + // so we skip these events. + // Note they are here just in case, because they are not actually used + // in Kicad +#if wxCHECK_VERSION( 3, 1, 0 ) || !wxCHECK_VERSION( 2, 9, 5 ) || ( !defined (__WINDOWS__) && !defined (__WXMAC__) ) + int maxX = unitsX - csizeX; + int maxY = unitsY - csizeY; + + if( id == wxEVT_SCROLLWIN_LINEUP ) + { + if( dir == wxHORIZONTAL ) + { + x -= m_scrollIncrementX; + + if( x < 0 ) + x = 0; + } + else + { + y -= m_scrollIncrementY; + + if( y < 0 ) + y = 0; + } + } + else if( id == wxEVT_SCROLLWIN_LINEDOWN ) + { + if( dir == wxHORIZONTAL ) + { + x += m_scrollIncrementX; + if( x > maxX ) + x = maxX; + } + else + { + y += m_scrollIncrementY; + + if( y > maxY ) + y = maxY; + } + } + else +#endif + if( id == wxEVT_SCROLLWIN_THUMBTRACK ) + { + if( dir == wxHORIZONTAL ) + x = event.GetPosition(); + else + y = event.GetPosition(); + } + else + { + event.Skip(); + return; + } + + wxLogTrace( KICAD_TRACE_COORDS, + wxT( "Setting scroll bars ppuX=%d, ppuY=%d, unitsX=%d, unitsY=%d, posX=%d, posY=%d" ), + ppux, ppuy, unitsX, unitsY, x, y ); + + double scale = GetParent()->GetScreen()->GetScalingFactor(); + + wxPoint center = GetParent()->GetScrollCenterPosition(); + center.x += KiROUND( (double) ( x - tmpX ) / scale ); + center.y += KiROUND( (double) ( y - tmpY ) / scale ); + GetParent()->SetScrollCenterPosition( center ); + + Scroll( x, y ); + event.Skip(); +} + + +void EDA_DRAW_PANEL::SetClipBox( wxDC& aDC, const wxRect* aRect ) +{ + wxRect clipBox; + + // Use the entire visible device area if no clip area was defined. + if( aRect == NULL ) + { + BASE_SCREEN* Screen = GetScreen(); + + if( !Screen ) + return; + + Screen->m_StartVisu = CalcUnscrolledPosition( wxPoint( 0, 0 ) ); + clipBox.SetSize( GetClientSize() ); + + int scrollX, scrollY; + + double scalar = Screen->GetScalingFactor(); + scrollX = KiROUND( Screen->GetGridSize().x * scalar ); + scrollY = KiROUND( Screen->GetGridSize().y * scalar ); + + m_scrollIncrementX = std::max( GetClientSize().x / 8, scrollX ); + m_scrollIncrementY = std::max( GetClientSize().y / 8, scrollY ); + Screen->m_ScrollbarPos.x = GetScrollPos( wxHORIZONTAL ); + Screen->m_ScrollbarPos.y = GetScrollPos( wxVERTICAL ); + } + else + { + clipBox = *aRect; + } + + // Pad clip box in device units. + clipBox.Inflate( CLIP_BOX_PADDING ); + + // Convert from device units to drawing units. + m_ClipBox.SetOrigin( wxPoint( aDC.DeviceToLogicalX( clipBox.x ), + aDC.DeviceToLogicalY( clipBox.y ) ) ); + m_ClipBox.SetSize( wxSize( aDC.DeviceToLogicalXRel( clipBox.width ), + aDC.DeviceToLogicalYRel( clipBox.height ) ) ); + + wxLogTrace( KICAD_TRACE_COORDS, + wxT( "Device clip box=(%d, %d, %d, %d), Logical clip box=(%d, %d, %d, %d)" ), + clipBox.x, clipBox.y, clipBox.width, clipBox.height, + m_ClipBox.GetX(), m_ClipBox.GetY(), m_ClipBox.GetWidth(), m_ClipBox.GetHeight() ); +} + + +void EDA_DRAW_PANEL::EraseScreen( wxDC* DC ) +{ + GRSetDrawMode( DC, GR_COPY ); + + EDA_COLOR_T bgColor = GetParent()->GetDrawBgColor(); + + GRSFilledRect( NULL, DC, m_ClipBox.GetX(), m_ClipBox.GetY(), + m_ClipBox.GetRight(), m_ClipBox.GetBottom(), + 0, bgColor, bgColor ); + + // Set to one (1) to draw bounding box validate bounding box calculation. +#if DEBUG_SHOW_CLIP_RECT + EDA_RECT bBox = m_ClipBox; + GRRect( NULL, DC, bBox.GetOrigin().x, bBox.GetOrigin().y, + bBox.GetEnd().x, bBox.GetEnd().y, 0, LIGHTMAGENTA ); +#endif +} + + +void EDA_DRAW_PANEL::DoPrepareDC( wxDC& dc ) +{ + wxScrolledWindow::DoPrepareDC( dc ); + + if( GetScreen() != NULL ) + { + double scale = GetScreen()->GetScalingFactor(); + dc.SetUserScale( scale, scale ); + + wxPoint pt = GetScreen()->m_DrawOrg; + dc.SetLogicalOrigin( pt.x, pt.y ); + } + + SetClipBox( dc ); // Reset the clip box to the entire screen. + GRResetPenAndBrush( &dc ); + dc.SetBackgroundMode( wxTRANSPARENT ); +} + + +void EDA_DRAW_PANEL::OnPaint( wxPaintEvent& event ) +{ + if( GetScreen() == NULL ) + { + event.Skip(); + return; + } + + INSTALL_PAINTDC( paintDC, this ); + + wxRect region = GetUpdateRegion().GetBox(); + SetClipBox( paintDC, ®ion ); + ReDraw( &paintDC, true ); +} + + +void EDA_DRAW_PANEL::ReDraw( wxDC* DC, bool erasebg ) +{ + BASE_SCREEN* Screen = GetScreen(); + + if( Screen == NULL ) + return; + + EDA_COLOR_T bgColor = GetParent()->GetDrawBgColor(); + + if( ( bgColor != WHITE ) && ( bgColor != BLACK ) ) + bgColor = BLACK; + + if( bgColor == WHITE ) + { + g_XorMode = GR_NXOR; + g_GhostColor = BLACK; + } + else + { + g_XorMode = GR_XOR; + g_GhostColor = WHITE; + } + + GRResetPenAndBrush( DC ); + + DC->SetBackground( bgColor == BLACK ? *wxBLACK_BRUSH : *wxWHITE_BRUSH ); + DC->SetBackgroundMode( wxSOLID ); + + if( erasebg ) + EraseScreen( DC ); + + GetParent()->RedrawActiveWindow( DC, erasebg ); + + // Verfies that the clipping is working correctly. If these two sets of numbers are + // not the same or really close. The clipping algorithms are broken. + wxLogTrace( KICAD_TRACE_COORDS, + wxT( "Clip box: (%d, %d, %d, %d), Draw extents (%d, %d, %d, %d)" ), + m_ClipBox.GetX(), m_ClipBox.GetY(), m_ClipBox.GetRight(), m_ClipBox.GetBottom(), + DC->MinX(), DC->MinY(), DC->MaxX(), DC->MaxY() ); +} + + +void EDA_DRAW_PANEL::SetEnableMousewheelPan( bool aEnable ) +{ + m_enableMousewheelPan = aEnable; + + if( GetParent()->IsGalCanvasActive() ) + GetParent()->GetGalCanvas()->GetViewControls()->EnableMousewheelPan( aEnable ); +} + + +void EDA_DRAW_PANEL::SetEnableZoomNoCenter( bool aEnable ) +{ + m_enableZoomNoCenter = aEnable; + + if( GetParent()->IsGalCanvasActive() ) + GetParent()->GetGalCanvas()->GetViewControls()->EnableCursorWarping( !aEnable ); +} + + +void EDA_DRAW_PANEL::DrawBackGround( wxDC* DC ) +{ + EDA_COLOR_T axis_color = BLUE; + + GRSetDrawMode( DC, GR_COPY ); + + if( GetParent()->IsGridVisible() ) + DrawGrid( DC ); + + // Draw axis + if( GetParent()->m_showAxis ) + { + wxSize pageSize = GetParent()->GetPageSizeIU(); + + // Draw the Y axis + GRLine( &m_ClipBox, DC, 0, -pageSize.y, 0, pageSize.y, 0, axis_color ); + + // Draw the X axis + GRLine( &m_ClipBox, DC, -pageSize.x, 0, pageSize.x, 0, 0, axis_color ); + } + + if( GetParent()->m_showOriginAxis ) + DrawAuxiliaryAxis( DC, GR_COPY ); + + if( GetParent()->m_showGridAxis ) + DrawGridAxis( DC, GR_COPY, GetParent()->GetGridOrigin() ); +} + + +void EDA_DRAW_PANEL::DrawGrid( wxDC* aDC ) +{ + #define MIN_GRID_SIZE 10 // min grid size in pixels to allow drawing + BASE_SCREEN* screen = GetScreen(); + wxRealPoint gridSize; + wxSize screenSize; + wxPoint org; + wxRealPoint screenGridSize; + + /* The grid must be visible. this is possible only is grid value + * and zoom value are sufficient + */ + gridSize = screen->GetGridSize(); + screen->m_StartVisu = CalcUnscrolledPosition( wxPoint( 0, 0 ) ); + screenSize = GetClientSize(); + + screenGridSize.x = aDC->LogicalToDeviceXRel( KiROUND( gridSize.x ) ); + screenGridSize.y = aDC->LogicalToDeviceYRel( KiROUND( gridSize.y ) ); + + org = m_ClipBox.GetPosition(); + + if( screenGridSize.x < MIN_GRID_SIZE || screenGridSize.y < MIN_GRID_SIZE ) + { + screenGridSize.x *= 2.0; + screenGridSize.y *= 2.0; + gridSize.x *= 2.0; + gridSize.y *= 2.0; + } + + if( screenGridSize.x < MIN_GRID_SIZE || screenGridSize.y < MIN_GRID_SIZE ) + return; + + org = GetParent()->GetNearestGridPosition( org, &gridSize ); + + // Setting the nearest grid position can select grid points outside the clip box. + // Incrementing the start point by one grid step should prevent drawing grid points + // outside the clip box. + if( org.x < m_ClipBox.GetX() ) + org.x += KiROUND( gridSize.x ); + + if( org.y < m_ClipBox.GetY() ) + org.y += KiROUND( gridSize.y ); + + // Use a pixel based draw to display grid. There are a lot of calls, so the cost is + // high and grid is slowly drawn on some platforms. Please note that this should + // always be enabled until the bitmap based solution below is fixed. +#ifndef __WXMAC__ + GRSetColorPen( aDC, GetParent()->GetGridColor() ); +#else + // On mac (Cocoa), a point isn't a pixel and being of size 1 don't survive to antialiasing + GRSetColorPen( aDC, GetParent()->GetGridColor(), aDC->DeviceToLogicalXRel( 2 ) ); +#endif + + int xpos; + double right = ( double ) m_ClipBox.GetRight(); + double bottom = ( double ) m_ClipBox.GetBottom(); + +#if defined( __WXMAC__ ) && defined( USE_WX_GRAPHICS_CONTEXT ) + wxGCDC *gcdc = wxDynamicCast( aDC, wxGCDC ); + if( gcdc ) + { + wxGraphicsContext *gc = gcdc->GetGraphicsContext(); + + // Grid point size + const int gsz = 1; + const double w = aDC->DeviceToLogicalXRel( gsz ); + const double h = aDC->DeviceToLogicalYRel( gsz ); + + // Use our own pen + wxPen pen( MakeColour( GetParent()->GetGridColor() ), h ); + pen.SetCap( wxCAP_BUTT ); + gc->SetPen( pen ); + + // draw grid + wxGraphicsPath path = gc->CreatePath(); + for( double x = (double) org.x - w/2.0; x <= right - w/2.0; x += gridSize.x ) + { + for( double y = (double) org.y; y <= bottom; y += gridSize.y ) + { + path.MoveToPoint( x, y ); + path.AddLineToPoint( x+w, y ); + } + } + gc->StrokePath( path ); + } + else +#endif + { + GRSetColorPen( aDC, GetParent()->GetGridColor() ); + + for( double x = (double) org.x; x <= right; x += gridSize.x ) + { + xpos = KiROUND( x ); + + for( double y = (double) org.y; y <= bottom; y += gridSize.y ) + { + aDC->DrawPoint( xpos, KiROUND( y ) ); + } + } + } +} + +// Set to 1 to draw auxirilary axis as lines, 0 to draw as target (circle with cross) +#define DRAW_AXIS_AS_LINES 0 +// Size in pixels of the target shape +#define AXIS_SIZE_IN_PIXELS 15 + +void EDA_DRAW_PANEL::DrawAuxiliaryAxis( wxDC* aDC, GR_DRAWMODE aDrawMode ) +{ + wxPoint origin = GetParent()->GetAuxOrigin(); + + if( origin == wxPoint( 0, 0 ) ) + return; + + EDA_COLOR_T color = RED; + + GRSetDrawMode( aDC, aDrawMode ); + +#if DRAW_AXIS_AS_LINES + wxSize pageSize = GetParent()->GetPageSizeIU(); + // Draw the Y axis + GRLine( &m_ClipBox, aDC, origin.x, -pageSize.y, + origin.x, pageSize.y, 0, color ); + + // Draw the X axis + GRLine( &m_ClipBox, aDC, -pageSize.x, origin.y, + pageSize.x, origin.y, 0, color ); +#else + int radius = aDC->DeviceToLogicalXRel( AXIS_SIZE_IN_PIXELS ); + int linewidth = aDC->DeviceToLogicalXRel( 1 ); + + GRSetColorPen( aDC, color, linewidth ); + + GRLine( &m_ClipBox, aDC, origin.x, origin.y-radius, + origin.x, origin.y+radius, 0, color ); + + // Draw the + shape + GRLine( &m_ClipBox, aDC, origin.x-radius, origin.y, + origin.x+radius, origin.y, 0, color ); + + GRCircle( &m_ClipBox, aDC, origin, radius, linewidth, color ); +#endif +} + + +void EDA_DRAW_PANEL::DrawGridAxis( wxDC* aDC, GR_DRAWMODE aDrawMode, const wxPoint& aGridOrigin ) +{ + if( !GetParent()->m_showGridAxis || ( !aGridOrigin.x && !aGridOrigin.y ) ) + return; + + EDA_COLOR_T color = GetParent()->GetGridColor(); + + GRSetDrawMode( aDC, aDrawMode ); + +#if DRAW_AXIS_AS_LINES + wxSize pageSize = GetParent()->GetPageSizeIU(); + // Draw the Y axis + GRLine( &m_ClipBox, aDC, aGridOrigin.x, -pageSize.y, + aGridOrigin.x, pageSize.y, 0, color ); + + // Draw the X axis + GRLine( &m_ClipBox, aDC, -pageSize.x, aGridOrigin.y, + pageSize.x, aGridOrigin.y, 0, color ); +#else + int radius = aDC->DeviceToLogicalXRel( AXIS_SIZE_IN_PIXELS ); + int linewidth = aDC->DeviceToLogicalXRel( 1 ); + + GRSetColorPen( aDC, GetParent()->GetGridColor(), linewidth ); + + GRLine( &m_ClipBox, aDC, aGridOrigin.x-radius, aGridOrigin.y-radius, + aGridOrigin.x+radius, aGridOrigin.y+radius, 0, color ); + + // Draw the X shape + GRLine( &m_ClipBox, aDC, aGridOrigin.x+radius, aGridOrigin.y-radius, + aGridOrigin.x-radius, aGridOrigin.y+radius, 0, color ); + + GRCircle( &m_ClipBox, aDC, aGridOrigin, radius, linewidth, color ); +#endif +} + + +bool EDA_DRAW_PANEL::OnRightClick( wxMouseEvent& event ) +{ + wxPoint pos; + wxMenu MasterMenu; + + INSTALL_UNBUFFERED_DC( dc, this ); + + pos = event.GetLogicalPosition( dc ); + + if( !GetParent()->OnRightClick( pos, &MasterMenu ) ) + return false; + + GetParent()->AddMenuZoomAndGrid( &MasterMenu ); + + pos = event.GetPosition(); + m_ignoreMouseEvents = true; + PopupMenu( &MasterMenu, pos ); + // here, we are waiting for popup menu closing. + // Among different ways, it can be closed by clicking on the left mouse button. + // The expected behavior is to move the mouse cursor to its initial + // location, where the right click was made. + // However there is a case where the move cursor does not work as expected: + // when the user left clicks on the caption frame: the entire window is moved. + // Calling wxSafeYield avoid this behavior because it allows the left click + // to be proceeded before moving the mouse + wxSafeYield(); + + // Move the mouse cursor to its initial position: + MoveCursorToCrossHair(); + m_ignoreMouseEvents = false; + + return true; +} + + +void EDA_DRAW_PANEL::OnMouseEntering( wxMouseEvent& aEvent ) +{ + // This is an ugly hack that fixes some cross hair display bugs when the mouse leaves the + // canvas area during middle button mouse panning. + if( m_cursorLevel != 0 ) + m_cursorLevel = 0; + + aEvent.Skip(); +} + + +void EDA_DRAW_PANEL::OnMouseLeaving( wxMouseEvent& event ) +{ + if( m_mouseCaptureCallback == NULL ) // No command in progress. + m_requestAutoPan = false; + + if( !m_enableAutoPan || !m_requestAutoPan || m_ignoreMouseEvents ) + return; + + // Auto pan when mouse has left the client window + // Ensure the cross_hair position is updated, + // because it will be used to center the screen. + // We use a position inside the client window + wxRect area( wxPoint( 0, 0 ), GetClientSize() ); + wxPoint cross_hair_pos = event.GetPosition(); + + // Certain window managers (e.g. awesome wm) incorrectly trigger "on leave" event, + // therefore test if the cursor has really left the panel area + if( !area.Contains( cross_hair_pos ) ) + { + INSTALL_UNBUFFERED_DC( dc, this ); + cross_hair_pos.x = dc.DeviceToLogicalX( cross_hair_pos.x ); + cross_hair_pos.y = dc.DeviceToLogicalY( cross_hair_pos.y ); + + GetParent()->SetCrossHairPosition( cross_hair_pos ); + + wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED, ID_POPUP_ZOOM_CENTER ); + cmd.SetEventObject( this ); + GetEventHandler()->ProcessEvent( cmd ); + } + + event.Skip(); +} + + +void EDA_DRAW_PANEL::OnMouseWheel( wxMouseEvent& event ) +{ + if( m_ignoreMouseEvents ) + return; + + wxRect rect = wxRect( wxPoint( 0, 0 ), GetClientSize() ); + + // Ignore scroll events if the cursor is outside the drawing area. + if( event.GetWheelRotation() == 0 || !GetParent()->IsEnabled() + || !rect.Contains( event.GetPosition() ) ) + { + wxLogTrace( KICAD_TRACE_COORDS, + wxT( "OnMouseWheel() position(%d, %d) rectangle(%d, %d, %d, %d)" ), + event.GetPosition().x, event.GetPosition().y, + rect.x, rect.y, rect.width, rect.height ); + event.Skip(); + return; + } + + INSTALL_UNBUFFERED_DC( dc, this ); + GetParent()->SetCrossHairPosition( event.GetLogicalPosition( dc ) ); + + wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED ); + cmd.SetEventObject( this ); + + bool offCenterReq = event.ControlDown() && event.ShiftDown(); + offCenterReq = offCenterReq || m_enableZoomNoCenter; + + int axis = event.GetWheelAxis(); + int wheelRotation = event.GetWheelRotation(); + + if( m_enableMousewheelPan ) + { + wxPoint newStart = GetViewStart(); + if( axis == wxMOUSE_WHEEL_HORIZONTAL ) + newStart.x += wheelRotation; + else + newStart.y -= wheelRotation; + + wxPoint center = GetScreenCenterLogicalPosition(); + GetParent()->SetScrollCenterPosition( center ); + Scroll( newStart ); + } + else if( wheelRotation > 0 ) + { + if( event.ShiftDown() && !event.ControlDown() ) + cmd.SetId( ID_PAN_UP ); + else if( event.ControlDown() && !event.ShiftDown() ) + cmd.SetId( ID_PAN_LEFT ); + else if( offCenterReq ) + cmd.SetId( ID_OFFCENTER_ZOOM_IN ); + else + cmd.SetId( ID_POPUP_ZOOM_IN ); + } + else if( wheelRotation < 0 ) + { + if( event.ShiftDown() && !event.ControlDown() ) + cmd.SetId( ID_PAN_DOWN ); + else if( event.ControlDown() && !event.ShiftDown() ) + cmd.SetId( ID_PAN_RIGHT ); + else if( offCenterReq ) + cmd.SetId( ID_OFFCENTER_ZOOM_OUT ); + else + cmd.SetId( ID_POPUP_ZOOM_OUT ); + } + + if( cmd.GetId() ) + GetEventHandler()->ProcessEvent( cmd ); + event.Skip(); +} + + +#ifdef USE_OSX_MAGNIFY_EVENT +void EDA_DRAW_PANEL::OnMagnify( wxMouseEvent& event ) +{ + // Scale the panel around our cursor position. + bool warpCursor = false; + + wxPoint cursorPosition = GetParent()->GetCursorPosition( false ); + wxPoint centerPosition = GetParent()->GetScrollCenterPosition(); + wxPoint vector = centerPosition - cursorPosition; + + double magnification = ( event.GetMagnification() + 1.0f ); + double scaleFactor = GetZoom() / magnification; + + // Scale the vector between the cursor and center point + vector.x /= magnification; + vector.y /= magnification; + + SetZoom(scaleFactor); + + // Recenter the window along our scaled vector such that the + // cursor becomes our scale axis, remaining fixed on screen + GetParent()->RedrawScreen( cursorPosition + vector, warpCursor ); + + event.Skip(); +} +#endif + + +void EDA_DRAW_PANEL::OnMouseEvent( wxMouseEvent& event ) +{ + int localrealbutt = 0, localbutt = 0; + BASE_SCREEN* screen = GetScreen(); + + if( !screen ) + return; + + /* Adjust value to filter mouse displacement before consider the drag + * mouse is really a drag command, not just a movement while click + */ +#define MIN_DRAG_COUNT_FOR_START_BLOCK_COMMAND 5 + + if( event.Leaving() ) + m_canStartBlock = -1; + + if( !IsMouseCaptured() ) // No mouse capture in progress. + m_requestAutoPan = false; + + if( GetParent()->IsActive() ) + SetFocus(); + else + return; + + if( !event.IsButton() && !event.Moving() && !event.Dragging() ) + return; + + if( event.RightDown() ) + { + OnRightClick( event ); + return; + } + + if( m_ignoreMouseEvents ) + return; + + if( event.LeftIsDown() ) + localrealbutt |= GR_M_LEFT_DOWN; + + if( event.MiddleIsDown() ) + localrealbutt |= GR_M_MIDDLE_DOWN; + + if( event.LeftDown() ) + localbutt = GR_M_LEFT_DOWN; + + if( event.ButtonDClick( 1 ) ) + localbutt = GR_M_LEFT_DOWN | GR_M_DCLICK; + + if( event.MiddleDown() ) + localbutt = GR_M_MIDDLE_DOWN; + + localrealbutt |= localbutt; // compensation default wxGTK + + INSTALL_UNBUFFERED_DC( DC, this ); + DC.SetBackground( *wxBLACK_BRUSH ); + + // Compute the cursor position in drawing (logical) units. + GetParent()->SetMousePosition( event.GetLogicalPosition( DC ) ); + + int kbstat = 0; + + if( event.ShiftDown() ) + kbstat |= GR_KB_SHIFT; + + if( event.ControlDown() ) + kbstat |= GR_KB_CTRL; + + if( event.AltDown() ) + kbstat |= GR_KB_ALT; + + // Calling Double Click and Click functions : + if( localbutt == (int) ( GR_M_LEFT_DOWN | GR_M_DCLICK ) ) + { + GetParent()->OnLeftDClick( &DC, GetParent()->RefPos( true ) ); + + // inhibit a response to the mouse left button release, + // because we have a double click, and we do not want a new + // OnLeftClick command at end of this Double Click + m_ignoreNextLeftButtonRelease = true; + } + else if( event.LeftUp() ) + { + // A block command is in progress: a left up is the end of block + // or this is the end of a double click, already seen + // Note also m_ignoreNextLeftButtonRelease can be set by + // the call to OnLeftClick(), so do not change it after calling OnLeftClick + bool ignoreEvt = m_ignoreNextLeftButtonRelease; + m_ignoreNextLeftButtonRelease = false; + + if( screen->m_BlockLocate.GetState() == STATE_NO_BLOCK && !ignoreEvt ) + GetParent()->OnLeftClick( &DC, GetParent()->RefPos( true ) ); + + } + else if( !event.LeftIsDown() ) + { + /* be sure there is a response to a left button release command + * even when a LeftUp event is not seen. This happens when a + * double click opens a dialog box, and the release mouse button + * is made when the dialog box is opened. + */ + m_ignoreNextLeftButtonRelease = false; + } + + if( event.ButtonDown( wxMOUSE_BTN_MIDDLE ) && m_enableMiddleButtonPan ) + { + if( m_panScrollbarLimits ) + { + int ppux, ppuy; + GetScrollPixelsPerUnit( &ppux, &ppuy ); + GetViewStart( &m_PanStartCenter.x, &m_PanStartCenter.y ); + m_PanStartCenter.x *= ppux; + m_PanStartCenter.y *= ppuy; + } + else + m_PanStartCenter = GetParent()->GetScrollCenterPosition(); + + m_PanStartEventPosition = event.GetPosition(); + + INSTALL_UNBUFFERED_DC( dc, this ); + CrossHairOff( &dc ); + } + + if( event.ButtonUp( wxMOUSE_BTN_MIDDLE ) && m_enableMiddleButtonPan ) + { + INSTALL_UNBUFFERED_DC( dc, this ); + CrossHairOn( &dc ); + } + + if( event.MiddleIsDown() && m_enableMiddleButtonPan ) + { + wxPoint currentPosition = event.GetPosition(); + + if( m_panScrollbarLimits ) + { + int x, y; + int tmpX, tmpY; + int ppux, ppuy; + int maxX, maxY; + int vsizeX, vsizeY; + int csizeX, csizeY; + + GetViewStart( &tmpX, &tmpY ); + GetScrollPixelsPerUnit( &ppux, &ppuy ); + GetVirtualSize( &vsizeX, &vsizeY ); + GetClientSize( &csizeX, &csizeY ); + + maxX = vsizeX - csizeX; + maxY = vsizeY - csizeY; + + x = m_PanStartCenter.x + m_PanStartEventPosition.x - currentPosition.x; + y = m_PanStartCenter.y + m_PanStartEventPosition.y - currentPosition.y; + + bool shouldMoveCursor = false; + + if( x < 0 ) + { + currentPosition.x += x; + x = 0; + shouldMoveCursor = true; + } + + if( y < 0 ) + { + currentPosition.y += y; + y = 0; + shouldMoveCursor = true; + } + + if( x > maxX ) + { + currentPosition.x += ( x - maxX ); + x = maxX; + shouldMoveCursor = true; + } + + if( y > maxY ) + { + currentPosition.y += ( y - maxY ); + y = maxY; + shouldMoveCursor = true; + } + + if( shouldMoveCursor ) + WarpPointer( currentPosition.x, currentPosition.y ); + + Scroll( x/ppux, y/ppuy ); + + double scale = GetParent()->GetScreen()->GetScalingFactor(); + + wxPoint center = GetParent()->GetScrollCenterPosition(); + center.x += KiROUND( (double) ( x - tmpX ) / scale ) / ppux; + center.y += KiROUND( (double) ( y - tmpY ) / scale ) / ppuy; + GetParent()->SetScrollCenterPosition( center ); + + Refresh(); + Update(); + } + else + { + double scale = GetParent()->GetScreen()->GetScalingFactor(); + int x = m_PanStartCenter.x + + KiROUND( (double) ( m_PanStartEventPosition.x - currentPosition.x ) / scale ); + int y = m_PanStartCenter.y + + KiROUND( (double) ( m_PanStartEventPosition.y - currentPosition.y ) / scale ); + + GetParent()->RedrawScreen( wxPoint( x, y ), false ); + } + } + + if( event.ButtonUp( wxMOUSE_BTN_MIDDLE ) && !m_enableMiddleButtonPan && + (screen->m_BlockLocate.GetState() == STATE_NO_BLOCK) ) + { + // The middle button has been released, with no block command: + // We use it for a zoom center at cursor position command + wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED, ID_POPUP_ZOOM_CENTER ); + cmd.SetEventObject( this ); + GetEventHandler()->ProcessEvent( cmd ); + } + + // Calling the general function on mouse changes (and pseudo key commands) + GetParent()->GeneralControl( &DC, event.GetLogicalPosition( DC ), 0 ); + + /*******************************/ + /* Control of block commands : */ + /*******************************/ + + // Command block can't start if mouse is dragging a new panel + static EDA_DRAW_PANEL* lastPanel; + if( lastPanel != this ) + { + m_minDragEventCount = 0; + m_canStartBlock = -1; + } + + /* A new command block can start after a release buttons + * and if the drag is enough + * This is to avoid a false start block when a dialog box is dismissed, + * or when changing panels in hierarchy navigation + * or when clicking while and moving mouse + */ + if( !event.LeftIsDown() && !event.MiddleIsDown() ) + { + m_minDragEventCount = 0; + m_canStartBlock = 0; + + /* Remember the last cursor position when a drag mouse starts + * this is the last position ** before ** clicking a button + * this is useful to start a block command from the point where the + * mouse was clicked first + * (a filter creates a delay for the real block command start, and + * we must remember this point) + */ + m_CursorStartPos = GetParent()->GetCrossHairPosition(); + } + + if( m_enableBlockCommands && !(localbutt & GR_M_DCLICK) ) + { + if( !screen->IsBlockActive() ) + { + screen->m_BlockLocate.SetOrigin( m_CursorStartPos ); + } + + if( event.LeftDown() || ( !m_enableMiddleButtonPan && event.MiddleDown() ) ) + { + if( screen->m_BlockLocate.GetState() == STATE_BLOCK_MOVE ) + { + m_requestAutoPan = false; + GetParent()->HandleBlockPlace( &DC ); + m_ignoreNextLeftButtonRelease = true; + } + } + else if( ( m_canStartBlock >= 0 ) + && ( event.LeftIsDown() || ( !m_enableMiddleButtonPan && event.MiddleIsDown() ) ) + && !IsMouseCaptured() ) + { + // Mouse is dragging: if no block in progress, start a block command. + if( screen->m_BlockLocate.GetState() == STATE_NO_BLOCK ) + { + // Start a block command + int cmd_type = kbstat; + + if( !m_enableMiddleButtonPan && event.MiddleIsDown() ) + cmd_type |= MOUSE_MIDDLE; + + // A block command is started if the drag is enough. A small + // drag is ignored (it is certainly a little mouse move when + // clicking) not really a drag mouse + if( m_minDragEventCount < MIN_DRAG_COUNT_FOR_START_BLOCK_COMMAND ) + m_minDragEventCount++; + else + { + if( !GetParent()->HandleBlockBegin( &DC, cmd_type, m_CursorStartPos ) ) + { + // should not occur: error + GetParent()->DisplayToolMsg( + wxT( "EDA_DRAW_PANEL::OnMouseEvent() Block Error" ) ); + } + else + { + m_requestAutoPan = true; + SetCursor( wxCURSOR_SIZING ); + } + } + } + } + + if( event.ButtonUp( wxMOUSE_BTN_LEFT ) || + ( !m_enableMiddleButtonPan && event.ButtonUp( wxMOUSE_BTN_MIDDLE ) ) ) + { + /* Release the mouse button: end of block. + * The command can finish (DELETE) or have a next command (MOVE, + * COPY). However the block command is canceled if the block + * size is small because a block command filtering is already + * made, this case happens, but only when the on grid cursor has + * not moved. + */ + #define BLOCK_MINSIZE_LIMIT 1 + bool BlockIsSmall = + ( std::abs( screen->m_BlockLocate.GetWidth() ) < BLOCK_MINSIZE_LIMIT ) + && ( std::abs( screen->m_BlockLocate.GetHeight() ) < BLOCK_MINSIZE_LIMIT ); + + if( (screen->m_BlockLocate.GetState() != STATE_NO_BLOCK) && BlockIsSmall ) + { + if( m_endMouseCaptureCallback ) + { + m_endMouseCaptureCallback( this, &DC ); + m_requestAutoPan = false; + } + + SetCursor( (wxStockCursor) m_currentCursor ); + } + else if( screen->m_BlockLocate.GetState() == STATE_BLOCK_END ) + { + m_requestAutoPan = false; + GetParent()->HandleBlockEnd( &DC ); + SetCursor( (wxStockCursor) m_currentCursor ); + if( screen->m_BlockLocate.GetState() == STATE_BLOCK_MOVE ) + { + m_requestAutoPan = true; + SetCursor( wxCURSOR_HAND ); + } + } + } + } + + // End of block command on a double click + // To avoid an unwanted block move command if the mouse is moved while double clicking + if( localbutt == (int) ( GR_M_LEFT_DOWN | GR_M_DCLICK ) ) + { + if( !screen->IsBlockActive() && IsMouseCaptured() ) + { + m_endMouseCaptureCallback( this, &DC ); + } + } + +#if 0 + wxString msg_debug; + msg_debug.Printf( " block state %d, cmd %d", + screen->m_BlockLocate.GetState(), + screen->m_BlockLocate.GetCommand() ); + GetParent()->PrintMsg( msg_debug ); +#endif + + lastPanel = this; +} + + + +void EDA_DRAW_PANEL::OnCharHook( wxKeyEvent& event ) +{ + event.Skip(); +} + +void EDA_DRAW_PANEL::OnKeyEvent( wxKeyEvent& event ) +{ + int localkey; + wxPoint pos; + + localkey = event.GetKeyCode(); + + switch( localkey ) + { + default: + break; + + case WXK_ESCAPE: + m_abortRequest = true; + + if( IsMouseCaptured() ) + EndMouseCapture(); + else + EndMouseCapture( ID_NO_TOOL_SELECTED, m_defaultCursor, wxEmptyString ); + break; + } + + /* Normalize keys code to easily handle keys from Ctrl+A to Ctrl+Z + * They have an ascii code from 1 to 27 remapped + * to GR_KB_CTRL + 'A' to GR_KB_CTRL + 'Z' + */ + if( event.ControlDown() && localkey >= WXK_CONTROL_A && localkey <= WXK_CONTROL_Z ) + localkey += 'A' - 1; + + /* Disallow shift for keys that have two keycodes on them (e.g. number and + * punctuation keys) leaving only the "letter keys" of A-Z. + * Then, you can have, e.g. Ctrl-5 and Ctrl-% (GB layout) + * and Ctrl-( and Ctrl-5 (FR layout). + * Otherwise, you'd have to have to say Ctrl-Shift-5 on a FR layout + */ + bool keyIsLetter = ( localkey >= 'A' && localkey <= 'Z' ) || + ( localkey >= 'a' && localkey <= 'z' ); + + if( event.ShiftDown() && ( keyIsLetter || localkey > 256 ) ) + localkey |= GR_KB_SHIFT; + + if( event.ControlDown() ) + localkey |= GR_KB_CTRL; + + if( event.AltDown() ) + localkey |= GR_KB_ALT; + + INSTALL_UNBUFFERED_DC( DC, this ); + + // Some key commands use the current mouse position: refresh it. + pos = wxGetMousePosition() - GetScreenPosition(); + + // Compute the cursor position in drawing units. Also known as logical units to wxDC. + pos = wxPoint( DC.DeviceToLogicalX( pos.x ), DC.DeviceToLogicalY( pos.y ) ); + + GetParent()->SetMousePosition( pos ); + + if( !GetParent()->GeneralControl( &DC, pos, localkey ) ) + event.Skip(); +} + + +void EDA_DRAW_PANEL::OnPan( wxCommandEvent& event ) +{ + int x, y; + int ppux, ppuy; + int unitsX, unitsY; + int maxX, maxY; + int tmpX, tmpY; + + GetViewStart( &x, &y ); + GetScrollPixelsPerUnit( &ppux, &ppuy ); + GetVirtualSize( &unitsX, &unitsY ); + tmpX = x; + tmpY = y; + maxX = unitsX; + maxY = unitsY; + unitsX /= ppux; + unitsY /= ppuy; + + wxLogTrace( KICAD_TRACE_COORDS, + wxT( "Scroll center position before pan: (%d, %d)" ), tmpX, tmpY ); + + switch( event.GetId() ) + { + case ID_PAN_UP: + y -= m_scrollIncrementY; + break; + + case ID_PAN_DOWN: + y += m_scrollIncrementY; + break; + + case ID_PAN_LEFT: + x -= m_scrollIncrementX; + break; + + case ID_PAN_RIGHT: + x += m_scrollIncrementX; + break; + + default: + wxLogDebug( wxT( "Unknown ID %d in EDA_DRAW_PANEL::OnPan()." ), event.GetId() ); + } + + bool updateCenterScrollPos = true; + + if( x < 0 ) + { + x = 0; + updateCenterScrollPos = false; + } + + if( y < 0 ) + { + y = 0; + updateCenterScrollPos = false; + } + + if( x > maxX ) + { + x = maxX; + updateCenterScrollPos = false; + } + + if( y > maxY ) + { + y = maxY; + updateCenterScrollPos = false; + } + + // Don't update the scroll position beyond the scroll limits. + if( updateCenterScrollPos ) + { + double scale = GetParent()->GetScreen()->GetScalingFactor(); + + wxPoint center = GetParent()->GetScrollCenterPosition(); + center.x += KiROUND( (double) ( x - tmpX ) / scale ); + center.y += KiROUND( (double) ( y - tmpY ) / scale ); + GetParent()->SetScrollCenterPosition( center ); + + wxLogTrace( KICAD_TRACE_COORDS, + wxT( "Scroll center position after pan: (%d, %d)" ), center.x, center.y ); + } + + Scroll( x/ppux, y/ppuy ); +} + + +void EDA_DRAW_PANEL::EndMouseCapture( int id, int cursor, const wxString& title, + bool aCallEndFunc ) +{ + if( m_mouseCaptureCallback && m_endMouseCaptureCallback && aCallEndFunc ) + { + INSTALL_UNBUFFERED_DC( dc, this ); + m_endMouseCaptureCallback( this, &dc ); + } + + m_mouseCaptureCallback = NULL; + m_endMouseCaptureCallback = NULL; + m_requestAutoPan = false; + + if( id != -1 && cursor != -1 ) + { + wxASSERT( cursor > wxCURSOR_NONE && cursor < wxCURSOR_MAX ); + GetParent()->SetToolID( id, cursor, title ); + } +} + + +void EDA_DRAW_PANEL::CallMouseCapture( wxDC* aDC, const wxPoint& aPosition, bool aErase ) +{ + wxCHECK_RET( aDC != NULL, wxT( "Invalid device context." ) ); + wxCHECK_RET( m_mouseCaptureCallback != NULL, wxT( "Mouse capture callback not set." ) ); + + m_mouseCaptureCallback( this, aDC, aPosition, aErase ); +} + + +void EDA_DRAW_PANEL::CallEndMouseCapture( wxDC* aDC ) +{ + wxCHECK_RET( aDC != NULL, wxT( "Invalid device context." ) ); + + // CallEndMouseCapture is sometimes called with m_endMouseCaptureCallback == NULL + // for instance after an ABORT in block paste. + if( m_endMouseCaptureCallback ) + m_endMouseCaptureCallback( this, aDC ); +} diff --git a/common/draw_panel_gal.cpp b/common/draw_panel_gal.cpp new file mode 100644 index 0000000..2cd2e4c --- /dev/null +++ b/common/draw_panel_gal.cpp @@ -0,0 +1,415 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013-2015 CERN + * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 <wx/wx.h> +#include <wx/frame.h> +#include <wx/window.h> +#include <wx/event.h> +#include <wx/colour.h> +#include <wx/filename.h> +#include <confirm.h> + +#include <class_draw_panel_gal.h> +#include <view/view.h> +#include <view/wx_view_controls.h> +#include <pcb_painter.h> + +#include <gal/graphics_abstraction_layer.h> +#include <gal/opengl/opengl_gal.h> +#include <gal/cairo/cairo_gal.h> + +#include <tool/tool_dispatcher.h> +#include <tool/tool_manager.h> + +#include <boost/foreach.hpp> + +#ifdef __WXDEBUG__ +#include <profile.h> +#endif /* __WXDEBUG__ */ + +EDA_DRAW_PANEL_GAL::EDA_DRAW_PANEL_GAL( wxWindow* aParentWindow, wxWindowID aWindowId, + const wxPoint& aPosition, const wxSize& aSize, + GAL_TYPE aGalType ) : + wxScrolledCanvas( aParentWindow, aWindowId, aPosition, aSize ) +{ + m_parent = aParentWindow; + m_gal = NULL; + m_backend = GAL_TYPE_NONE; + m_view = NULL; + m_painter = NULL; + m_eventDispatcher = NULL; + m_lostFocus = false; + + SetLayoutDirection( wxLayout_LeftToRight ); + + SwitchBackend( aGalType ); + SetBackgroundStyle( wxBG_STYLE_CUSTOM ); + +// Scrollbars broken in GAL on OSX +#ifdef __WXMAC__ + ShowScrollbars( wxSHOW_SB_NEVER, wxSHOW_SB_NEVER ); +#else + ShowScrollbars( wxSHOW_SB_ALWAYS, wxSHOW_SB_ALWAYS ); +#endif + EnableScrolling( false, false ); // otherwise Zoom Auto disables GAL canvas + + m_painter = new KIGFX::PCB_PAINTER( m_gal ); + + m_view = new KIGFX::VIEW( true ); + m_view->SetPainter( m_painter ); + m_view->SetGAL( m_gal ); + + Connect( wxEVT_SIZE, wxSizeEventHandler( EDA_DRAW_PANEL_GAL::onSize ), NULL, this ); + Connect( wxEVT_ENTER_WINDOW, wxEventHandler( EDA_DRAW_PANEL_GAL::onEnter ), NULL, this ); + Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( EDA_DRAW_PANEL_GAL::onLostFocus ), NULL, this ); + + const wxEventType events[] = + { + wxEVT_LEFT_UP, wxEVT_LEFT_DOWN, wxEVT_LEFT_DCLICK, + wxEVT_RIGHT_UP, wxEVT_RIGHT_DOWN, wxEVT_RIGHT_DCLICK, + wxEVT_MIDDLE_UP, wxEVT_MIDDLE_DOWN, wxEVT_MIDDLE_DCLICK, + wxEVT_MOTION, wxEVT_MOUSEWHEEL, wxEVT_CHAR, +#ifdef USE_OSX_MAGNIFY_EVENT + wxEVT_MAGNIFY, +#endif + KIGFX::WX_VIEW_CONTROLS::EVT_REFRESH_MOUSE + }; + + BOOST_FOREACH( wxEventType eventType, events ) + { + Connect( eventType, wxEventHandler( EDA_DRAW_PANEL_GAL::onEvent ), + NULL, m_eventDispatcher ); + } + + // View controls is the first in the event handler chain, so the Tool Framework operates + // on updated viewport data. + m_viewControls = new KIGFX::WX_VIEW_CONTROLS( m_view, this ); + + // Set up timer that prevents too frequent redraw commands + m_refreshTimer.SetOwner( this ); + m_pendingRefresh = false; + m_drawing = false; + m_drawingEnabled = false; + Connect( wxEVT_TIMER, wxTimerEventHandler( EDA_DRAW_PANEL_GAL::onRefreshTimer ), NULL, this ); +} + + +EDA_DRAW_PANEL_GAL::~EDA_DRAW_PANEL_GAL() +{ + delete m_painter; + delete m_viewControls; + delete m_view; + delete m_gal; +} + + +void EDA_DRAW_PANEL_GAL::SetFocus() +{ +// Windows has a strange manner on bringing up and activating windows +// containing a GAL canvas just after moving the mouse cursor into its area. +// Feel free to uncomment or extend the following #ifdef if you experience +// similar problems on your platform. +#ifdef __WINDOWS__ + if( !GetParent()->IsDescendant( wxWindow::FindFocus() ) ) + return; +#endif + + wxScrolledCanvas::SetFocus(); + m_lostFocus = false; +} + + +void EDA_DRAW_PANEL_GAL::onPaint( wxPaintEvent& WXUNUSED( aEvent ) ) +{ + m_pendingRefresh = false; + + if( m_drawing ) + return; + + m_drawing = true; + +// Scrollbars broken in GAL on OSX +#ifndef __WXMAC__ + m_viewControls->UpdateScrollbars(); +#endif + + m_view->UpdateItems(); + m_gal->BeginDrawing(); + m_gal->ClearScreen( m_painter->GetSettings()->GetBackgroundColor() ); + + KIGFX::COLOR4D gridColor = static_cast<KIGFX::PCB_RENDER_SETTINGS*> (m_painter->GetSettings())->GetLayerColor( ITEM_GAL_LAYER ( GRID_VISIBLE ) ); + m_gal->SetGridColor ( gridColor ); + + if( m_view->IsDirty() ) + { + m_view->ClearTargets(); + + // Grid has to be redrawn only when the NONCACHED target is redrawn + if( m_view->IsTargetDirty( KIGFX::TARGET_NONCACHED ) ) + m_gal->DrawGrid(); + + m_view->Redraw(); + } + + m_gal->DrawCursor( m_viewControls->GetCursorPosition() ); + m_gal->EndDrawing(); + + m_lastRefresh = wxGetLocalTimeMillis(); + m_drawing = false; +} + + +void EDA_DRAW_PANEL_GAL::onSize( wxSizeEvent& aEvent ) +{ + m_gal->ResizeScreen( aEvent.GetSize().x, aEvent.GetSize().y ); + m_view->MarkTargetDirty( KIGFX::TARGET_CACHED ); + m_view->MarkTargetDirty( KIGFX::TARGET_NONCACHED ); +} + + +void EDA_DRAW_PANEL_GAL::Refresh( bool aEraseBackground, const wxRect* aRect ) +{ + if( m_pendingRefresh ) + return; + + m_pendingRefresh = true; + +#ifdef __WXMAC__ + // Timers on OS X may have a high latency (seen up to 500ms and more) which + // makes repaints jerky. No negative impact seen without throttling, so just + // do an unconditional refresh for OS X. + ForceRefresh(); +#else + wxLongLong t = wxGetLocalTimeMillis(); + wxLongLong delta = t - m_lastRefresh; + + if( delta >= MinRefreshPeriod ) + { + ForceRefresh(); + } + else + { + // One shot timer + m_refreshTimer.Start( ( MinRefreshPeriod - delta ).ToLong(), true ); + } +#endif +} + + +void EDA_DRAW_PANEL_GAL::ForceRefresh() +{ + wxPaintEvent redrawEvent; + wxPostEvent( this, redrawEvent ); +} + + +void EDA_DRAW_PANEL_GAL::SetEventDispatcher( TOOL_DISPATCHER* aEventDispatcher ) +{ + m_eventDispatcher = aEventDispatcher; + const wxEventType eventTypes[] = { wxEVT_TOOL }; + + if( m_eventDispatcher ) + { + BOOST_FOREACH( wxEventType type, eventTypes ) + { + m_parent->Connect( type, wxCommandEventHandler( TOOL_DISPATCHER::DispatchWxCommand ), + NULL, m_eventDispatcher ); + } + } + else + { + BOOST_FOREACH( wxEventType type, eventTypes ) + { + // While loop is used to be sure that all event handlers are removed. + while( m_parent->Disconnect( type, + wxCommandEventHandler( TOOL_DISPATCHER::DispatchWxCommand ), + NULL, m_eventDispatcher ) ); + } + } +} + + +void EDA_DRAW_PANEL_GAL::StartDrawing() +{ + // Start querying GAL if it is ready + m_refreshTimer.StartOnce( 100 ); +} + + +void EDA_DRAW_PANEL_GAL::StopDrawing() +{ + m_drawingEnabled = false; + Disconnect( wxEVT_PAINT, wxPaintEventHandler( EDA_DRAW_PANEL_GAL::onPaint ), NULL, this ); + m_pendingRefresh = false; + m_drawing = true; + m_refreshTimer.Stop(); +} + + +void EDA_DRAW_PANEL_GAL::SetHighContrastLayer( LAYER_ID aLayer ) +{ + // Set display settings for high contrast mode + KIGFX::RENDER_SETTINGS* rSettings = m_view->GetPainter()->GetSettings(); + + SetTopLayer( aLayer ); + + rSettings->ClearActiveLayers(); + rSettings->SetActiveLayer( aLayer ); + + m_view->UpdateAllLayersColor(); +} + + +void EDA_DRAW_PANEL_GAL::SetTopLayer( LAYER_ID aLayer ) +{ + m_view->ClearTopLayers(); + m_view->SetTopLayer( aLayer ); + m_view->UpdateAllLayersOrder(); +} + + +double EDA_DRAW_PANEL_GAL::GetLegacyZoom() const +{ + double zoomFactor = m_gal->GetWorldScale() / m_gal->GetZoomFactor(); + return ( 1.0 / ( zoomFactor * m_view->GetScale() ) ); +} + + +bool EDA_DRAW_PANEL_GAL::SwitchBackend( GAL_TYPE aGalType ) +{ + // Do not do anything if the currently used GAL is correct + if( aGalType == m_backend && m_gal != NULL ) + return true; + + bool result = true; // assume everything will be fine + + // Prevent refreshing canvas during backend switch + StopDrawing(); + + KIGFX::GAL* new_gal = NULL; + + try + { + switch( aGalType ) + { + case GAL_TYPE_OPENGL: + new_gal = new KIGFX::OPENGL_GAL( this, this, this ); + break; + + case GAL_TYPE_CAIRO: + new_gal = new KIGFX::CAIRO_GAL( this, this, this ); + break; + + default: + assert( false ); + // warn about unhandled GAL canvas type, but continue with the fallback option + + case GAL_TYPE_NONE: + // KIGFX::GAL is a stub - it actually does cannot display anything, + // but prevents code relying on GAL canvas existence from crashing + new_gal = new KIGFX::GAL(); + break; + } + } + catch( std::runtime_error& err ) + { + new_gal = new KIGFX::GAL(); + aGalType = GAL_TYPE_NONE; + DisplayError( m_parent, wxString( err.what() ) ); + result = false; + } + + assert( new_gal ); + delete m_gal; + m_gal = new_gal; + + wxSize size = GetClientSize(); + m_gal->ResizeScreen( size.GetX(), size.GetY() ); + + if( m_painter ) + m_painter->SetGAL( m_gal ); + + if( m_view ) + m_view->SetGAL( m_gal ); + + m_backend = aGalType; + + return result; +} + + +void EDA_DRAW_PANEL_GAL::onEvent( wxEvent& aEvent ) +{ + if( m_lostFocus ) + SetFocus(); + + if( !m_eventDispatcher ) + aEvent.Skip(); + else + m_eventDispatcher->DispatchWxEvent( aEvent ); + + Refresh(); +} + + +void EDA_DRAW_PANEL_GAL::onEnter( wxEvent& aEvent ) +{ + // Getting focus is necessary in order to receive key events properly + SetFocus(); + + aEvent.Skip(); +} + + +void EDA_DRAW_PANEL_GAL::onLostFocus( wxFocusEvent& aEvent ) +{ + m_lostFocus = true; + + aEvent.Skip(); +} + + +void EDA_DRAW_PANEL_GAL::onRefreshTimer( wxTimerEvent& aEvent ) +{ + if( !m_drawingEnabled ) + { + if( m_gal->IsInitialized() ) + { + m_drawing = false; + m_pendingRefresh = true; + Connect( wxEVT_PAINT, wxPaintEventHandler( EDA_DRAW_PANEL_GAL::onPaint ), NULL, this ); + m_drawingEnabled = true; + } + else + { + // Try again soon + m_refreshTimer.Start( 100, true ); + return; + } + } + + wxPaintEvent redrawEvent; + wxPostEvent( this, redrawEvent ); +} diff --git a/common/drawtxt.cpp b/common/drawtxt.cpp new file mode 100644 index 0000000..15a2280 --- /dev/null +++ b/common/drawtxt.cpp @@ -0,0 +1,685 @@ +/** + * Functions to draw and plot text on screen + * @file drawtxt.cpp + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr + * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2012 Wayne Stambaugh <stambaughw@verizon.net> + * Copyright (C) 1992-2012 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 <fctsys.h> +#include <gr_basic.h> +#include <common.h> +#include <plot_common.h> +#include <eda_text.h> // EDA_TEXT_HJUSTIFY_T and EDA_TEXT_VJUSTIFY_T +#include <trigo.h> +#include <macros.h> +#include <class_drawpanel.h> +#include <class_base_screen.h> + +#include <gal/stroke_font.h> + + +#include <newstroke_font.h> +#include <plot_common.h> + +using namespace KIGFX; + + +// A ugly hack to avoid UTF8::uni_iter method not found at link stage +// in gerbview and pl_editor. +// Hoping I (JPC) can remove that soon. +void dummy() +{ + UTF8 text = "x"; + for( UTF8::uni_iter it = text.ubegin(), end = text.uend(); it < end; ++it ) + ; +} + + +int OverbarPositionY( int size_v ) +{ + return KiROUND( size_v * STROKE_FONT::OVERBAR_HEIGHT ); +} + + +/** + * Function GetPensizeForBold + * @return the "best" value for a pen size to draw/plot a bold text + * @param aTextSize = the char size (height or width) + */ +int GetPenSizeForBold( int aTextSize ) +{ + return KiROUND( aTextSize / 5.0 ); +} + + +/** + * Function Clamp_Text_PenSize + * As a rule, pen width should not be >1/4em, otherwise the character + * will be cluttered up in its own fatness + * so pen width max is aSize/4 for bold text, and aSize/6 for normal text + * The "best" pen width is aSize/5 for bold texts, + * so the clamp is consistant with bold option. + * @param aPenSize = the pen size to clamp + * @param aSize the char size (height or width) + * @param aBold = true if text accept bold pen size + * @return the max pen size allowed + */ +int Clamp_Text_PenSize( int aPenSize, int aSize, bool aBold ) +{ + int penSize = aPenSize; + double scale = aBold ? 4.0 : 6.0; + int maxWidth = KiROUND( std::abs( aSize ) / scale ); + + if( penSize > maxWidth ) + penSize = maxWidth; + + return penSize; +} + + +int Clamp_Text_PenSize( int aPenSize, wxSize aSize, bool aBold ) +{ + int size = std::min( std::abs( aSize.x ), std::abs( aSize.y ) ); + + return Clamp_Text_PenSize( aPenSize, size, aBold ); +} + + +/* Functions to draw / plot a string. + * texts have only one line. + * They can be in italic. + * Horizontal and Vertical justification are handled. + * Texts can be rotated + * substrings between ~ markers can be "negated" (i.e. with an over bar + */ + +/** + * Function NegableTextLength + * Return the text length (char count) of a negable string, + * excluding the ~ markers + */ +int NegableTextLength( const wxString& aText ) +{ + int char_count = aText.length(); + + // Fix the character count, removing the ~ found + for( int i = char_count - 1; i >= 0; i-- ) + { + if( aText[i] == '~' ) + { + // '~~' draw as '~' and count as two chars + if( i > 0 && aText[i - 1] == '~' ) + i--; + else + char_count--; + } + } + + return char_count; +} + + +/* Function GetHersheyShapeDescription + * return a pointer to the shape corresponding to unicode value AsciiCode + * Note we use the same font for Bold and Normal texts + * because KiCad handles a variable pen size to do that + * that gives better results in XOR draw mode. + */ +static const char* GetHersheyShapeDescription( int AsciiCode ) +{ + // calculate font length + int font_length_max = newstroke_font_bufsize; + + if( AsciiCode >= (32 + font_length_max) ) + AsciiCode = '?'; + + if( AsciiCode < 32 ) + AsciiCode = 32; // Clamp control chars + + AsciiCode -= 32; + + return newstroke_font[AsciiCode]; +} + + +int GraphicTextWidth( const wxString& aText, int aXSize, bool aItalic, bool aWidth ) +{ + int tally = 0; + int char_count = aText.length(); + + for( int i = 0; i < char_count; i++ ) + { + int asciiCode = aText[i]; + + /* Skip the negation marks + * and first '~' char of '~~' + * ('~~' draw as '~') + */ + if( asciiCode == '~' ) + { + if( i == 0 || aText[i - 1] != '~' ) + continue; + } + + const char* shape_ptr = GetHersheyShapeDescription( asciiCode ); + // Get metrics + int xsta = *shape_ptr++ - 'R'; + int xsto = *shape_ptr++ - 'R'; + tally += KiROUND( aXSize * (xsto - xsta) * STROKE_FONT::STROKE_FONT_SCALE ); + } + + // For italic correction, add 1/8 size + if( aItalic ) + { + tally += KiROUND( aXSize * STROKE_FONT::ITALIC_TILT ); + } + + return tally; +} + + +// Helper function for drawing character polylines +static void DrawGraphicTextPline( EDA_RECT* aClipBox, + wxDC* aDC, + EDA_COLOR_T aColor, + int aWidth, + bool aSketchMode, + int point_count, + wxPoint* coord, + void (* aCallback)( int x0, int y0, int xf, int yf ), + PLOTTER* aPlotter ) +{ + if( aPlotter ) + { + aPlotter->MoveTo( coord[0] ); + + for( int ik = 1; ik < point_count; ik++ ) + { + aPlotter->LineTo( coord[ik] ); + } + + aPlotter->PenFinish(); + } + else if( aCallback ) + { + for( int ik = 0; ik < (point_count - 1); ik++ ) + { + aCallback( coord[ik].x, coord[ik].y, + coord[ik + 1].x, coord[ik + 1].y ); + } + } + else if( aDC ) + { + if( aSketchMode ) + { + for( int ik = 0; ik < (point_count - 1); ik++ ) + GRCSegm( aClipBox, aDC, coord[ik].x, coord[ik].y, + coord[ik + 1].x, coord[ik + 1].y, aWidth, aColor ); + } + else + GRPoly( aClipBox, aDC, point_count, coord, 0, + aWidth, aColor, aColor ); + } +} + + +/** + * Function DrawGraphicText + * Draw a graphic text (like module texts) + * @param aClipBox = the clipping rect, or NULL if no clipping + * @param aDC = the current Device Context. NULL if draw within a 3D GL Canvas + * @param aPos = text position (according to h_justify, v_justify) + * @param aColor (enum EDA_COLOR_T) = text color + * @param aText = text to draw + * @param aOrient = angle in 0.1 degree + * @param aSize = text size (size.x or size.y can be < 0 for mirrored texts) + * @param aH_justify = horizontal justification (Left, center, right) + * @param aV_justify = vertical justification (bottom, center, top) + * @param aWidth = line width (pen width) (use default width if aWidth = 0) + * if width < 0 : draw segments in sketch mode, width = abs(width) + * Use a value min(aSize.x, aSize.y) / 5 for a bold text + * @param aItalic = true to simulate an italic font + * @param aBold = true to use a bold font. Useful only with default width value (aWidth = 0) + * @param aCallback() = function called (if non null) to draw each segment. + * used to draw 3D texts or for plotting, NULL for normal drawings + * @param aPlotter = a pointer to a PLOTTER instance, when this function is used to plot + * the text. NULL to draw this text. + */ +void DrawGraphicText( EDA_RECT* aClipBox, + wxDC* aDC, + const wxPoint& aPos, + EDA_COLOR_T aColor, + const wxString& aText, + double aOrient, + const wxSize& aSize, + enum EDA_TEXT_HJUSTIFY_T aH_justify, + enum EDA_TEXT_VJUSTIFY_T aV_justify, + int aWidth, + bool aItalic, + bool aBold, + void (* aCallback)( int x0, int y0, int xf, int yf ), + PLOTTER* aPlotter ) +{ + int AsciiCode; + int x0, y0; + int size_h, size_v; + unsigned ptr; + int dx, dy; // Draw coordinate for segments to draw. also used in some other calculation + wxPoint current_char_pos; // Draw coordinates for the current char + wxPoint overbar_pos; // Start point for the current overbar + int overbar_italic_comp; // Italic compensation for overbar + #define BUF_SIZE 100 + wxPoint coord[BUF_SIZE + 1]; // Buffer coordinate used to draw polylines (one char shape) + bool sketch_mode = false; + bool italic_reverse = false; // true for mirrored texts with m_Size.x < 0 + + size_h = aSize.x; /* PLEASE NOTE: H is for HORIZONTAL not for HEIGHT */ + size_v = aSize.y; + + if( aWidth == 0 && aBold ) // Use default values if aWidth == 0 + aWidth = GetPenSizeForBold( std::min( aSize.x, aSize.y ) ); + + if( aWidth < 0 ) + { + aWidth = -aWidth; + sketch_mode = true; + } + +#ifdef CLIP_PEN // made by draw and plot functions + aWidth = Clamp_Text_PenSize( aWidth, aSize, aBold ); +#endif + + if( size_h < 0 ) // text is mirrored using size.x < 0 (mirror / Y axis) + italic_reverse = true; + + unsigned char_count = NegableTextLength( aText ); + + if( char_count == 0 ) + return; + + current_char_pos = aPos; + + dx = GraphicTextWidth( aText, size_h, aItalic, aWidth ); + dy = size_v; + + /* Do not draw the text if out of draw area! */ + if( aClipBox ) + { + int xm, ym, ll, xc, yc; + ll = std::abs( dx ); + + xc = current_char_pos.x; + yc = current_char_pos.y; + + x0 = aClipBox->GetX() - ll; + y0 = aClipBox->GetY() - ll; + xm = aClipBox->GetRight() + ll; + ym = aClipBox->GetBottom() + ll; + + if( xc < x0 ) + return; + + if( yc < y0 ) + return; + + if( xc > xm ) + return; + + if( yc > ym ) + return; + } + + + /* Compute the position of the first letter of the text + * this position is the position of the left bottom point of the letter + * this is the same as the text position only for a left and bottom justified text + * In others cases, this position must be calculated from the text position ans size + */ + + switch( aH_justify ) + { + case GR_TEXT_HJUSTIFY_CENTER: + current_char_pos.x -= dx / 2; + break; + + case GR_TEXT_HJUSTIFY_RIGHT: + current_char_pos.x -= dx; + break; + + case GR_TEXT_HJUSTIFY_LEFT: + break; + } + + switch( aV_justify ) + { + case GR_TEXT_VJUSTIFY_CENTER: + current_char_pos.y += dy / 2; + break; + + case GR_TEXT_VJUSTIFY_TOP: + current_char_pos.y += dy; + break; + + case GR_TEXT_VJUSTIFY_BOTTOM: + break; + } + + // Note: if aPanel == NULL, we are using a GL Canvas that handle scaling + if( aSize.x == 0 ) + return; + + /* if a text size is too small, the text cannot be drawn, and it is drawn as a single + * graphic line */ + if( aDC && ( aDC->LogicalToDeviceYRel( std::abs( aSize.y ) ) < MIN_DRAWABLE_TEXT_SIZE )) + { + // draw the text as a line always vertically centered + wxPoint end( current_char_pos.x + dx, current_char_pos.y ); + + RotatePoint( ¤t_char_pos, aPos, aOrient ); + RotatePoint( &end, aPos, aOrient ); + + if( aPlotter ) + { + aPlotter->MoveTo( current_char_pos ); + aPlotter->FinishTo( end ); + } + else if( aCallback ) + { + aCallback( current_char_pos.x, current_char_pos.y, end.x, end.y ); + } + else + GRLine( aClipBox, aDC, + current_char_pos.x, current_char_pos.y, end.x, end.y, aWidth, aColor ); + + return; + } + + if( aItalic ) + { + overbar_italic_comp = KiROUND( OverbarPositionY( size_v ) * STROKE_FONT::ITALIC_TILT ); + + if( italic_reverse ) + { + overbar_italic_comp = -overbar_italic_comp; + } + } + else + { + overbar_italic_comp = 0; + } + + int overbars = 0; // Number of '~' seen (except '~~') + ptr = 0; // ptr = text index + + while( ptr < char_count ) + { + if( aText[ptr + overbars] == '~' ) + { + if( ptr + overbars + 1 < aText.length() + && aText[ptr + overbars + 1] == '~' ) /* '~~' draw as '~' */ + ptr++; // skip first '~' char and draw second + + else + { + // Found an overbar, adjust the pointers + overbars++; + + if( overbars & 1 ) // odd overbars count + { + // Starting the overbar + overbar_pos = current_char_pos; + overbar_pos.x += overbar_italic_comp; + overbar_pos.y -= OverbarPositionY( size_v ); + RotatePoint( &overbar_pos, aPos, aOrient ); + } + else + { + // Ending the overbar + coord[0] = overbar_pos; + overbar_pos = current_char_pos; + overbar_pos.x += overbar_italic_comp; + overbar_pos.y -= OverbarPositionY( size_v ); + RotatePoint( &overbar_pos, aPos, aOrient ); + coord[1] = overbar_pos; + // Plot the overbar segment + DrawGraphicTextPline( aClipBox, aDC, aColor, aWidth, + sketch_mode, 2, coord, aCallback, aPlotter ); + } + + continue; // Skip ~ processing + } + } + + AsciiCode = aText.GetChar( ptr + overbars ); + + const char* ptcar = GetHersheyShapeDescription( AsciiCode ); + // Get metrics + int xsta = *ptcar++ - 'R'; + int xsto = *ptcar++ - 'R'; + int point_count = 0; + bool endcar = false; + + while( !endcar ) + { + int hc1, hc2; + hc1 = *ptcar++; + + if( hc1 ) + { + hc2 = *ptcar++; + } + else + { + // End of character, insert a synthetic pen up: + hc1 = ' '; + hc2 = 'R'; + endcar = true; + } + + // Do the Hershey decode thing: + // coordinates values are coded as <value> + 'R' + hc1 -= 'R'; + hc2 -= 'R'; + + // Pen up request + if( hc1 == -50 && hc2 == 0 ) + { + if( point_count ) + { + if( aWidth <= 1 ) + aWidth = 0; + + DrawGraphicTextPline( aClipBox, aDC, aColor, aWidth, + sketch_mode, point_count, coord, + aCallback, aPlotter ); + } + + point_count = 0; + } + else + { + wxPoint currpoint; + hc1 -= xsta; hc2 -= 10; // Align the midpoint + hc1 = KiROUND( hc1 * size_h * STROKE_FONT::STROKE_FONT_SCALE ); + hc2 = KiROUND( hc2 * size_v * STROKE_FONT::STROKE_FONT_SCALE ); + + // To simulate an italic font, + // add a x offset depending on the y offset + if( aItalic ) + hc1 -= KiROUND( italic_reverse ? -hc2 * STROKE_FONT::ITALIC_TILT + : hc2 * STROKE_FONT::ITALIC_TILT ); + + currpoint.x = hc1 + current_char_pos.x; + currpoint.y = hc2 + current_char_pos.y; + + RotatePoint( &currpoint, aPos, aOrient ); + coord[point_count] = currpoint; + + if( point_count < BUF_SIZE - 1 ) + point_count++; + } + } // end draw 1 char + + ptr++; + + // Apply the advance width + current_char_pos.x += KiROUND( size_h * (xsto - xsta) * STROKE_FONT::STROKE_FONT_SCALE ); + } + + if( overbars % 2 ) + { + // Close the last overbar + coord[0] = overbar_pos; + overbar_pos = current_char_pos; + overbar_pos.y -= OverbarPositionY( size_v ); + RotatePoint( &overbar_pos, aPos, aOrient ); + coord[1] = overbar_pos; + + // Plot the overbar segment + DrawGraphicTextPline( aClipBox, aDC, aColor, aWidth, + sketch_mode, 2, coord, aCallback, aPlotter ); + } +} + +void DrawGraphicHaloText( EDA_RECT* aClipBox, wxDC * aDC, + const wxPoint &aPos, + enum EDA_COLOR_T aBgColor, + enum EDA_COLOR_T aColor1, + enum EDA_COLOR_T aColor2, + const wxString &aText, + double aOrient, + const wxSize &aSize, + enum EDA_TEXT_HJUSTIFY_T aH_justify, + enum EDA_TEXT_VJUSTIFY_T aV_justify, + int aWidth, bool aItalic, bool aBold, + void (*aCallback)( int x0, int y0, int xf, int yf ), + PLOTTER * aPlotter ) +{ + // Swap color if contrast would be better + if( ColorIsLight( aBgColor ) ) + { + EDA_COLOR_T c = aColor1; + aColor1 = aColor2; + aColor2 = c; + } + + DrawGraphicText( aClipBox, aDC, aPos, aColor1, aText, aOrient, aSize, + aH_justify, aV_justify, aWidth, aItalic, aBold, + aCallback, aPlotter ); + + DrawGraphicText( aClipBox, aDC, aPos, aColor2, aText, aOrient, aSize, + aH_justify, aV_justify, aWidth / 4, aItalic, aBold, + aCallback, aPlotter ); +} + +/** + * Function PlotGraphicText + * same as DrawGraphicText, but plot graphic text insteed of draw it + * @param aPos = text position (according to aH_justify, aV_justify) + * @param aColor (enum EDA_COLOR_T) = text color + * @param aText = text to draw + * @param aOrient = angle in 0.1 degree + * @param aSize = text size (size.x or size.y can be < 0 for mirrored texts) + * @param aH_justify = horizontal justification (Left, center, right) + * @param aV_justify = vertical justification (bottom, center, top) + * @param aWidth = line width (pen width) (default = 0) + * if width < 0 : draw segments in sketch mode, width = abs(width) + * Use a value min(aSize.x, aSize.y) / 5 for a bold text + * @param aItalic = true to simulate an italic font + * @param aBold = true to use a bold font Useful only with default width value (aWidth = 0) + * @param aMultilineAllowed = true to plot text as multiline, otherwise single line + */ +void PLOTTER::Text( const wxPoint& aPos, + enum EDA_COLOR_T aColor, + const wxString& aText, + double aOrient, + const wxSize& aSize, + enum EDA_TEXT_HJUSTIFY_T aH_justify, + enum EDA_TEXT_VJUSTIFY_T aV_justify, + int aWidth, + bool aItalic, + bool aBold, + bool aMultilineAllowed ) +{ + int textPensize = aWidth; + + if( textPensize == 0 && aBold ) // Use default values if aWidth == 0 + textPensize = GetPenSizeForBold( std::min( aSize.x, aSize.y ) ); + + if( textPensize >= 0 ) + textPensize = Clamp_Text_PenSize( aWidth, aSize, aBold ); + else + textPensize = -Clamp_Text_PenSize( -aWidth, aSize, aBold ); + + SetCurrentLineWidth( textPensize ); + + if( aColor >= 0 ) + SetColor( aColor ); + + if( aMultilineAllowed ) + { + // EDA_TEXT needs for calculations of the position of every + // line according to orientation and justifications + wxArrayString strings; + EDA_TEXT* multilineText = new EDA_TEXT( aText ); + multilineText->SetSize( aSize ); + multilineText->SetTextPosition( aPos ); + multilineText->SetOrientation( aOrient ); + multilineText->SetHorizJustify( aH_justify ); + multilineText->SetVertJustify( aV_justify ); + multilineText->SetThickness( aWidth ); + multilineText->SetMultilineAllowed( aMultilineAllowed ); + + std::vector<wxPoint> positions; + wxStringSplit( aText, strings, '\n' ); + positions.reserve( strings.Count() ); + + multilineText->GetPositionsOfLinesOfMultilineText( + positions, strings.Count() ); + + for( unsigned ii = 0; ii < strings.Count(); ii++ ) + { + wxString& txt = strings.Item( ii ); + DrawGraphicText( NULL, NULL, positions[ii], aColor, txt, + aOrient, aSize, + aH_justify, aV_justify, + textPensize, aItalic, aBold, NULL, this ); + } + + delete multilineText; + } + else + { + DrawGraphicText( NULL, NULL, aPos, aColor, aText, + aOrient, aSize, + aH_justify, aV_justify, + textPensize, aItalic, aBold, NULL, this ); + } + + if( aWidth != textPensize ) + SetCurrentLineWidth( aWidth ); +} diff --git a/common/dsnlexer.cpp b/common/dsnlexer.cpp new file mode 100644 index 0000000..2211427 --- /dev/null +++ b/common/dsnlexer.cpp @@ -0,0 +1,835 @@ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2007-2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2007-2015 KiCad Developers, see change_log.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 <cstdarg> +#include <cstdio> +#include <cstdlib> // bsearch() +#include <cctype> + +#include <macros.h> +#include <fctsys.h> +#include <dsnlexer.h> + + +//#define STANDALONE 1 // enable this for stand alone testing. + +#define FMT_CLIPBOARD _( "clipboard" ) + + +//-----<DSNLEXER>------------------------------------------------------------- + +void DSNLEXER::init() +{ + curTok = DSN_NONE; + prevTok = DSN_NONE; + + stringDelimiter = '"'; + + specctraMode = false; + space_in_quoted_tokens = false; + commentsAreTokens = false; + + curOffset = 0; + +#if 1 + if( keywordCount > 11 ) + { + // resize the hashtable bucket count + keyword_hash.reserve( keywordCount ); + } + + // fill the specialized "C string" hashtable from keywords[] + const KEYWORD* it = keywords; + const KEYWORD* end = it + keywordCount; + + for( ; it < end; ++it ) + { + keyword_hash[it->name] = it->token; + } +#endif +} + + +DSNLEXER::DSNLEXER( const KEYWORD* aKeywordTable, unsigned aKeywordCount, + FILE* aFile, const wxString& aFilename ) : + iOwnReaders( true ), + start( NULL ), + next( NULL ), + limit( NULL ), + reader( NULL ), + keywords( aKeywordTable ), + keywordCount( aKeywordCount ) +{ + FILE_LINE_READER* fileReader = new FILE_LINE_READER( aFile, aFilename ); + PushReader( fileReader ); + init(); +} + + +DSNLEXER::DSNLEXER( const KEYWORD* aKeywordTable, unsigned aKeywordCount, + const std::string& aClipboardTxt, const wxString& aSource ) : + iOwnReaders( true ), + start( NULL ), + next( NULL ), + limit( NULL ), + reader( NULL ), + keywords( aKeywordTable ), + keywordCount( aKeywordCount ) +{ + STRING_LINE_READER* stringReader = new STRING_LINE_READER( aClipboardTxt, aSource.IsEmpty() ? + wxString( FMT_CLIPBOARD ) : aSource ); + PushReader( stringReader ); + init(); +} + + +DSNLEXER::DSNLEXER( const KEYWORD* aKeywordTable, unsigned aKeywordCount, + LINE_READER* aLineReader ) : + iOwnReaders( false ), + start( NULL ), + next( NULL ), + limit( NULL ), + reader( NULL ), + keywords( aKeywordTable ), + keywordCount( aKeywordCount ) +{ + if( aLineReader ) + PushReader( aLineReader ); + init(); +} + + +static const KEYWORD empty_keywords[1] = {}; + +DSNLEXER::DSNLEXER( const std::string& aSExpression, const wxString& aSource ) : + iOwnReaders( true ), + start( NULL ), + next( NULL ), + limit( NULL ), + reader( NULL ), + keywords( empty_keywords ), + keywordCount( 0 ) +{ + STRING_LINE_READER* stringReader = new STRING_LINE_READER( aSExpression, aSource.IsEmpty() ? + wxString( FMT_CLIPBOARD ) : aSource ); + PushReader( stringReader ); + init(); +} + + +DSNLEXER::~DSNLEXER() +{ + if( iOwnReaders ) + { + // delete the LINE_READERs from the stack, since I own them. + for( READER_STACK::iterator it = readerStack.begin(); it!=readerStack.end(); ++it ) + delete *it; + } +} + +void DSNLEXER::SetSpecctraMode( bool aMode ) +{ + specctraMode = aMode; + if( aMode ) + { + // specctra mode defaults, some of which can still be changed in this mode. + space_in_quoted_tokens = true; + } + else + { + space_in_quoted_tokens = false; + stringDelimiter = '"'; + } +} + + +bool DSNLEXER::SyncLineReaderWith( DSNLEXER& aLexer ) +{ + // Synchronize the pointers handling the data read by the LINE_READER + // only if aLexer shares the same LINE_READER, because only in this case + // the char buffer is be common + + if( reader != aLexer.reader ) + return false; + + // We can synchronize the pointers which handle the data currently read + start = aLexer.start; + next = aLexer.next; + limit = aLexer.limit; + + // Sync these parameters is not mandatory, but could help + // for instance in debug + curText = aLexer.curText; + curOffset = aLexer.curOffset; + + return true; +} + + +void DSNLEXER::PushReader( LINE_READER* aLineReader ) +{ + readerStack.push_back( aLineReader ); + reader = aLineReader; + start = (const char*) (*reader); + + // force a new readLine() as first thing. + limit = start; + next = start; +} + + +LINE_READER* DSNLEXER::PopReader() +{ + LINE_READER* ret = 0; + + if( readerStack.size() ) + { + ret = reader; + readerStack.pop_back(); + + if( readerStack.size() ) + { + reader = readerStack.back(); + start = reader->Line(); + + // force a new readLine() as first thing. + limit = start; + next = start; + } + else + { + reader = 0; + start = dummy; + limit = dummy; + limit = dummy; + } + } + return ret; +} + + +#if 0 +static int compare( const void* a1, const void* a2 ) +{ + const KEYWORD* k1 = (const KEYWORD*) a1; + const KEYWORD* k2 = (const KEYWORD*) a2; + + int ret = strcmp( k1->name, k2->name ); + return ret; +} + +int DSNLEXER::findToken( const std::string& tok ) +{ + KEYWORD search; + + search.name = tok.c_str(); + + const KEYWORD* findings = (const KEYWORD*) bsearch( &search, + keywords, keywordCount, + sizeof(KEYWORD), compare ); + if( findings ) + return findings->token; + else + return DSN_SYMBOL; // not a keyword, some arbitrary symbol. +} + +#else + +inline int DSNLEXER::findToken( const std::string& tok ) +{ + KEYWORD_MAP::const_iterator it = keyword_hash.find( tok.c_str() ); + if( it != keyword_hash.end() ) + return it->second; + + return DSN_SYMBOL; // not a keyword, some arbitrary symbol. +} +#endif + + +const char* DSNLEXER::Syntax( int aTok ) +{ + const char* ret; + + switch( aTok ) + { + case DSN_NONE: + ret = "NONE"; + break; + case DSN_STRING_QUOTE: + ret = "string_quote"; // a special DSN syntax token, see specctra spec. + break; + case DSN_QUOTE_DEF: + ret = "quoted text delimiter"; + break; + case DSN_DASH: + ret = "-"; + break; + case DSN_SYMBOL: + ret = "symbol"; + break; + case DSN_NUMBER: + ret = "number"; + break; + case DSN_RIGHT: + ret = ")"; + break; + case DSN_LEFT: + ret = "("; + break; + case DSN_STRING: + ret = "quoted string"; + break; + case DSN_EOF: + ret = "end of input"; + break; + default: + ret = "???"; + } + + return ret; +} + + +const char* DSNLEXER::GetTokenText( int aTok ) +{ + const char* ret; + + if( aTok < 0 ) + { + return Syntax( aTok ); + } + else if( (unsigned) aTok < keywordCount ) + { + ret = keywords[aTok].name; + } + else + ret = "token too big"; + + return ret; +} + + +wxString DSNLEXER::GetTokenString( int aTok ) +{ + wxString ret; + + ret << wxT("'") << wxString::FromUTF8( GetTokenText(aTok) ) << wxT("'"); + + return ret; +} + + +bool DSNLEXER::IsSymbol( int aTok ) +{ + // This is static and not inline to reduce code space. + + // if aTok is >= 0, then it is a coincidental match to a keyword. + return aTok==DSN_SYMBOL + || aTok==DSN_STRING + || aTok>=0 + ; +} + + +void DSNLEXER::Expecting( int aTok ) throw( IO_ERROR ) +{ + wxString errText = wxString::Format( + _("Expecting '%s'"), GetChars( GetTokenString( aTok ) ) ); + THROW_PARSE_ERROR( errText, CurSource(), CurLine(), CurLineNumber(), CurOffset() ); +} + + +void DSNLEXER::Expecting( const char* text ) throw( IO_ERROR ) +{ + wxString errText = wxString::Format( + _("Expecting '%s'"), GetChars( wxString::FromUTF8( text ) ) ); + THROW_PARSE_ERROR( errText, CurSource(), CurLine(), CurLineNumber(), CurOffset() ); +} + + +void DSNLEXER::Unexpected( int aTok ) throw( IO_ERROR ) +{ + wxString errText = wxString::Format( + _("Unexpected '%s'"), GetChars( GetTokenString( aTok ) ) ); + THROW_PARSE_ERROR( errText, CurSource(), CurLine(), CurLineNumber(), CurOffset() ); +} + + +void DSNLEXER::Duplicate( int aTok ) throw( IO_ERROR ) +{ + wxString errText = wxString::Format( + _("%s is a duplicate"), GetTokenString( aTok ).GetData() ); + THROW_PARSE_ERROR( errText, CurSource(), CurLine(), CurLineNumber(), CurOffset() ); +} + + +void DSNLEXER::Unexpected( const char* text ) throw( IO_ERROR ) +{ + wxString errText = wxString::Format( + _("Unexpected '%s'"), GetChars( wxString::FromUTF8( text ) ) ); + THROW_PARSE_ERROR( errText, CurSource(), CurLine(), CurLineNumber(), CurOffset() ); +} + + +void DSNLEXER::NeedLEFT() throw( IO_ERROR ) +{ + int tok = NextTok(); + if( tok != DSN_LEFT ) + Expecting( DSN_LEFT ); +} + + +void DSNLEXER::NeedRIGHT() throw( IO_ERROR ) +{ + int tok = NextTok(); + if( tok != DSN_RIGHT ) + Expecting( DSN_RIGHT ); +} + + +int DSNLEXER::NeedSYMBOL() throw( IO_ERROR ) +{ + int tok = NextTok(); + if( !IsSymbol( tok ) ) + Expecting( DSN_SYMBOL ); + return tok; +} + + +int DSNLEXER::NeedSYMBOLorNUMBER() throw( IO_ERROR ) +{ + int tok = NextTok(); + if( !IsSymbol( tok ) && tok!=DSN_NUMBER ) + Expecting( "symbol|number" ); + return tok; +} + + +int DSNLEXER::NeedNUMBER( const char* aExpectation ) throw( IO_ERROR ) +{ + int tok = NextTok(); + if( tok != DSN_NUMBER ) + { + wxString errText = wxString::Format( + _("need a NUMBER for '%s'"), wxString::FromUTF8( aExpectation ).GetData() ); + THROW_PARSE_ERROR( errText, CurSource(), CurLine(), CurLineNumber(), CurOffset() ); + } + return tok; +} + + +/** + * Function isSpace + * tests for whitespace. Our whitespace, by our definition, is a subset of ASCII, + * i.e. no bytes with MSB on can be considered whitespace, since they are likely part + * of a multibyte UTF8 character. + */ +static bool isSpace( char cc ) +{ + // cc is signed, so it is often negative. + // Treat negative as large positive to exclude rapidly. + if( (unsigned char) cc <= ' ' ) + { + switch( (unsigned char) cc ) + { + case ' ': + case '\n': + case '\r': + case '\t': + case '\0': // PCAD s-expression files have this. + return true; + } + } + return false; +} + + +inline bool isDigit( char cc ) +{ + return '0' <= cc && cc <= '9'; +} + + +/// return true if @a cc is an s-expression separator character +inline bool isSep( char cc ) +{ + return isSpace( cc ) || cc=='(' || cc==')'; +} + + +/** + * Function isNumber + * returns true if the next sequence of text is a number: + * either an integer, fixed point, or float with exponent. Stops scanning + * at the first non-number character, even if it is not whitespace. + * + * @param cp is the start of the current token. + * @param limit is the end of the current token. + * + * @return bool - true if input token is a number, else false. + */ +static bool isNumber( const char* cp, const char* limit ) +{ + // regex for a float: "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?" i.e. any number, + // code traversal manually here: + + bool sawNumber = false; + + if( cp < limit && ( *cp=='-' || *cp=='+' ) ) + ++cp; + + while( cp < limit && isDigit( *cp ) ) + { + ++cp; + sawNumber = true; + } + + if( cp < limit && *cp == '.' ) + { + ++cp; + + while( cp < limit && isDigit( *cp ) ) + { + ++cp; + sawNumber = true; + } + } + + if( sawNumber ) + { + if( cp < limit && ( *cp=='E' || *cp=='e' ) ) + { + ++cp; + + sawNumber = false; // exponent mandates at least one digit thereafter. + + if( cp < limit && ( *cp=='-' || *cp=='+' ) ) + ++cp; + + while( cp < limit && isDigit( *cp ) ) + { + ++cp; + sawNumber = true; + } + } + } + + return sawNumber && cp==limit; +} + + +int DSNLEXER::NextTok() throw( IO_ERROR ) +{ + const char* cur = next; + const char* head = cur; + + prevTok = curTok; + + if( curTok == DSN_EOF ) + goto exit; + + if( cur >= limit ) + { +L_read: + // blank lines are returned as "\n" and will have a len of 1. + // EOF will have a len of 0 and so is detectable. + int len = readLine(); + if( len == 0 ) + { + cur = start; // after readLine(), since start can change, set cur offset to start + curTok = DSN_EOF; + goto exit; + } + + cur = start; // after readLine() since start can change. + + // skip leading whitespace + while( cur<limit && isSpace( *cur ) ) + ++cur; + + // If the first non-blank character is #, this line is a comment. + // Comments cannot follow any other token on the same line. + if( cur<limit && *cur=='#' ) + { + if( commentsAreTokens ) + { + // Grab the entire current line [excluding end of line char(s)] as the + // current token. The '#' character may not be at offset zero. + + while( limit[-1] == '\n' || limit[-1] == '\r' ) + --limit; + + curText.clear(); + curText.append( start, limit ); + + cur = start; // ensure a good curOffset below + curTok = DSN_COMMENT; + head = limit; // do a readLine() on next call in here. + goto exit; + } + else + goto L_read; + } + } + else + { + // skip leading whitespace + while( cur<limit && isSpace( *cur ) ) + ++cur; + } + + if( cur >= limit ) + goto L_read; + + if( *cur == '(' ) + { + curText = *cur; + curTok = DSN_LEFT; + head = cur+1; + goto exit; + } + + if( *cur == ')' ) + { + curText = *cur; + curTok = DSN_RIGHT; + head = cur+1; + goto exit; + } + + // Non-specctraMode, understands and deciphers escaped \, \r, \n, and \". + // Strips off leading and trailing double quotes + if( !specctraMode ) + { + // a quoted string, will return DSN_STRING + if( *cur == stringDelimiter ) + { + // copy the token, character by character so we can remove doubled up quotes. + curText.clear(); + + ++cur; // skip over the leading delimiter, which is always " in non-specctraMode + + head = cur; + + while( head<limit ) + { + // ESCAPE SEQUENCES: + if( *head =='\\' ) + { + char tbuf[8]; + char c; + int i; + + if( ++head >= limit ) + break; // throw exception at L_unterminated + + switch( *head++ ) + { + case '"': + case '\\': c = head[-1]; break; + case 'a': c = '\x07'; break; + case 'b': c = '\x08'; break; + case 'f': c = '\x0c'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\x09'; break; + case 'v': c = '\x0b'; break; + + case 'x': // 1 or 2 byte hex escape sequence + for( i=0; i<2; ++i ) + { + if( !isxdigit( head[i] ) ) + break; + tbuf[i] = head[i]; + } + tbuf[i] = '\0'; + if( i > 0 ) + c = (char) strtoul( tbuf, NULL, 16 ); + else + c = 'x'; // a goofed hex escape sequence, interpret as 'x' + head += i; + break; + + default: // 1-3 byte octal escape sequence + --head; + for( i=0; i<3; ++i ) + { + if( head[i] < '0' || head[i] > '7' ) + break; + tbuf[i] = head[i]; + } + tbuf[i] = '\0'; + if( i > 0 ) + c = (char) strtoul( tbuf, NULL, 8 ); + else + c = '\\'; // a goofed octal escape sequence, interpret as '\' + head += i; + break; + } + + curText += c; + } + + else if( *head == '"' ) // end of the non-specctraMode DSN_STRING + { + curTok = DSN_STRING; + ++head; // omit this trailing double quote + goto exit; + } + + else + curText += *head++; + + } // while + + // L_unterminated: + wxString errtxt( _( "Un-terminated delimited string" ) ); + THROW_PARSE_ERROR( errtxt, CurSource(), CurLine(), CurLineNumber(), CurOffset() ); + } + } + + else // is specctraMode, tests in this block should not occur in KiCad mode. + { + /* get the dash out of a <pin_reference> which is embedded for example + like: U2-14 or "U2"-"14" + This is detectable by a non-space immediately preceeding the dash. + */ + if( *cur == '-' && cur>start && !isSpace( cur[-1] ) ) + { + curText = '-'; + curTok = DSN_DASH; + head = cur+1; + goto exit; + } + + // switching the string_quote character + if( prevTok == DSN_STRING_QUOTE ) + { + static const wxString errtxt( _("String delimiter must be a single character of ', \", or $")); + + char cc = *cur; + switch( cc ) + { + case '\'': + case '$': + case '"': + break; + default: + THROW_PARSE_ERROR( errtxt, CurSource(), CurLine(), CurLineNumber(), CurOffset() ); + } + + curText = cc; + + head = cur+1; + + if( head<limit && !isSep( *head ) ) + { + THROW_PARSE_ERROR( errtxt, CurSource(), CurLine(), CurLineNumber(), CurOffset() ); + } + + curTok = DSN_QUOTE_DEF; + goto exit; + } + + // specctraMode DSN_STRING + if( *cur == stringDelimiter ) + { + ++cur; // skip over the leading delimiter: ",', or $ + + head = cur; + + while( head<limit && !isStringTerminator( *head ) ) + ++head; + + if( head >= limit ) + { + wxString errtxt( _( "Un-terminated delimited string" ) ); + THROW_PARSE_ERROR( errtxt, CurSource(), CurLine(), CurLineNumber(), CurOffset() ); + } + + curText.clear(); + curText.append( cur, head ); + + ++head; // skip over the trailing delimiter + + curTok = DSN_STRING; + goto exit; + } + } // specctraMode + + // non-quoted token, read it into curText. + curText.clear(); + + head = cur; + while( head<limit && !isSep( *head ) ) + curText += *head++; + + if( isNumber( curText.c_str(), curText.c_str() + curText.size() ) ) + { + curTok = DSN_NUMBER; + goto exit; + } + + if( specctraMode && curText == "string_quote" ) + { + curTok = DSN_STRING_QUOTE; + goto exit; + } + + curTok = findToken( curText ); + +exit: // single point of exit, no returns elsewhere please. + + curOffset = cur - start; + + next = head; + + // printf("tok:\"%s\"\n", curText.c_str() ); + return curTok; +} + + +wxArrayString* DSNLEXER::ReadCommentLines() throw( IO_ERROR ) +{ + wxArrayString* ret = 0; + bool cmt_setting = SetCommentsAreTokens( true ); + int tok = NextTok(); + + if( tok == DSN_COMMENT ) + { + ret = new wxArrayString(); + + do + { + ret->Add( FromUTF8() ); + } + while( ( tok = NextTok() ) == DSN_COMMENT ); + } + + SetCommentsAreTokens( cmt_setting ); + + return ret; +} diff --git a/common/eda_dde.cpp b/common/eda_dde.cpp new file mode 100644 index 0000000..92f965d --- /dev/null +++ b/common/eda_dde.cpp @@ -0,0 +1,211 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.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 + */ + +/** + * @file eda_dde.cpp + */ + +#include <fctsys.h> +#include <eda_dde.h> +#include <draw_frame.h> +#include <id.h> +#include <common.h> +#include <macros.h> + +static const wxString HOSTNAME( wxT( "localhost" ) ); + +// buffer for read and write data in socket connections +#define IPC_BUF_SIZE 4096 +static char client_ipc_buffer[IPC_BUF_SIZE]; + +static wxSocketServer* server; + + +/**********************************/ +/* Routines related to the server */ +/**********************************/ + +/* Function to initialize a server socket + */ +wxSocketServer* CreateServer( wxWindow* window, int service, bool local ) +{ + wxIPV4address addr; + + // Set the port number + addr.Service( service ); + + // Listen on localhost only if requested + if( local ) + addr.Hostname( HOSTNAME ); + + server = new wxSocketServer( addr ); + + if( server ) + { + server->SetNotify( wxSOCKET_CONNECTION_FLAG ); + server->SetEventHandler( *window, ID_EDA_SOCKET_EVENT_SERV ); + server->Notify( true ); + } + + return server; +} + + +/* Function called on every client request. + */ +void EDA_DRAW_FRAME::OnSockRequest( wxSocketEvent& evt ) +{ + size_t len; + wxSocketBase* sock = evt.GetSocket(); + + switch( evt.GetSocketEvent() ) + { + case wxSOCKET_INPUT: + sock->Read( client_ipc_buffer, 1 ); + + if( sock->LastCount() == 0 ) + break; // No data, occurs on opening connection + + sock->Read( client_ipc_buffer + 1, IPC_BUF_SIZE - 2 ); + len = 1 + sock->LastCount(); + client_ipc_buffer[len] = 0; + ExecuteRemoteCommand( client_ipc_buffer ); + break; + + case wxSOCKET_LOST: + return; + break; + + default: + wxPrintf( wxT( "EDA_DRAW_FRAME::OnSockRequest() error: Invalid event !" ) ); + break; + } +} + + +/* Function called when a connection is requested by a client. + */ +void EDA_DRAW_FRAME::OnSockRequestServer( wxSocketEvent& evt ) +{ + wxSocketBase* sock2; + wxSocketServer* server = (wxSocketServer*) evt.GetSocket(); + + sock2 = server->Accept(); + + if( sock2 == NULL ) + return; + + sock2->Notify( true ); + sock2->SetEventHandler( *this, ID_EDA_SOCKET_EVENT ); + sock2->SetNotify( wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG ); +} + + +/**********************************/ +/* Routines related to the CLIENT */ +/**********************************/ + +/* Used by a client to sent (by a socket connection) a data to a server. + * - Open a Socket Client connection + * - Send the buffer cmdline + * - Close the socket connection + * + * service is the service number for the TC/IP connection + */ +bool SendCommand( int service, const char* cmdline ) +{ + wxSocketClient* sock_client; + bool success = false; + wxIPV4address addr; + + // Create a connexion + addr.Hostname( HOSTNAME ); + addr.Service( service ); + + // Mini-tutorial for Connect() :-) + // (JP CHARRAS Note: see wxWidgets: sockets/client.cpp sample) + // --------------------------- + // + // There are two ways to use Connect(): blocking and non-blocking, + // depending on the value passed as the 'wait' (2nd) parameter. + // + // Connect(addr, true) will wait until the connection completes, + // returning true on success and false on failure. This call blocks + // the GUI (this might be changed in future releases to honor the + // wxSOCKET_BLOCK flag). + // + // Connect(addr, false) will issue a nonblocking connection request + // and return immediately. If the return value is true, then the + // connection has been already successfully established. If it is + // false, you must wait for the request to complete, either with + // WaitOnConnect() or by watching wxSOCKET_CONNECTION / LOST + // events (please read the documentation). + // + // WaitOnConnect() itself never blocks the GUI (this might change + // in the future to honor the wxSOCKET_BLOCK flag). This call will + // return false on timeout, or true if the connection request + // completes, which in turn might mean: + // + // a) That the connection was successfully established + // b) That the connection request failed (for example, because + // it was refused by the peer. + // + // Use IsConnected() to distinguish between these two. + // + // So, in a brief, you should do one of the following things: + // + // For blocking Connect: + // + // bool success = client->Connect(addr, true); + // + // For nonblocking Connect: + // + // client->Connect(addr, false); + // + // bool waitmore = true; + // while (! client->WaitOnConnect(seconds, millis) && waitmore ) + // { + // // possibly give some feedback to the user, + // // update waitmore if needed. + // } + // bool success = client->IsConnected(); + // + // And that's all :-) + + sock_client = new wxSocketClient(); + sock_client->SetTimeout( 2 ); // Time out in Seconds + sock_client->Connect( addr, false ); + sock_client->WaitOnConnect( 0, 100 ); + + if( sock_client->Ok() && sock_client->IsConnected() ) + { + success = true; + sock_client->SetFlags( wxSOCKET_NOWAIT /*wxSOCKET_WAITALL*/ ); + sock_client->Write( cmdline, strlen( cmdline ) ); + } + + sock_client->Close(); + sock_client->Destroy(); + return success; +} diff --git a/common/eda_doc.cpp b/common/eda_doc.cpp new file mode 100644 index 0000000..d94d00c --- /dev/null +++ b/common/eda_doc.cpp @@ -0,0 +1,241 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.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 + */ + +/** + * @file eda_doc.cpp + */ + +#include <fctsys.h> +#include <pgm_base.h> +#include <common.h> +#include <confirm.h> +#include <gestfich.h> + +#include <wx/mimetype.h> +#include <wx/tokenzr.h> +#include <wx/filename.h> +#include <macros.h> + + +void PGM_BASE::ReadPdfBrowserInfos() +{ + wxASSERT( m_common_settings ); + + wxString browser = m_common_settings->Read( wxT( "PdfBrowserName" ), wxEmptyString ); + SetPdfBrowserName( browser ); + + int tmp; + m_common_settings->Read( wxT( "UseSystemBrowser" ), &tmp, 0 ); + m_use_system_pdf_browser = bool( tmp ); +} + + +void PGM_BASE::WritePdfBrowserInfos() +{ + wxASSERT( m_common_settings ); + + m_common_settings->Write( wxT( "PdfBrowserName" ), GetPdfBrowserName() ); + m_common_settings->Write( wxT( "UseSystemBrowser" ), m_use_system_pdf_browser ); +} + + +// Mime type extensions (PDF files are not considered here) +static wxMimeTypesManager* mimeDatabase; +static const wxFileTypeInfo EDAfallbacks[] = +{ + wxFileTypeInfo( wxT( "text/html" ), + wxT( "wxhtml %s" ), + wxT( "wxhtml %s" ), + wxT( "html document (from KiCad)" ), + wxT( "htm" ), + wxT( "html" ),wxNullPtr ), + + wxFileTypeInfo( wxT( "application/sch" ), + wxT( "eeschema %s" ), + wxT( "eeschema -p %s" ), + wxT( "sch document (from KiCad)" ), + wxT( "sch" ), + wxT( "SCH" ), wxNullPtr ), + + // must terminate the table with this! + wxFileTypeInfo() +}; + + +bool GetAssociatedDocument( wxWindow* aParent, + const wxString& aDocName, + const wxPathList* aPaths) + +{ + wxString docname, fullfilename; + wxString msg; + wxString command; + bool success = false; + + // Is an internet url + static const wxChar* url_header[] = { + wxT( "http:" ), + wxT( "https:" ), + wxT( "ftp:" ), + wxT( "www." ) + }; + + for( unsigned ii = 0; ii < DIM(url_header); ii++ ) + { + if( aDocName.First( url_header[ii] ) == 0 ) // looks like an internet url + { + wxLaunchDefaultBrowser( aDocName ); + return true; + } + } + + docname = aDocName; + +#ifdef __WINDOWS__ + docname.Replace( UNIX_STRING_DIR_SEP, WIN_STRING_DIR_SEP ); +#else + docname.Replace( WIN_STRING_DIR_SEP, UNIX_STRING_DIR_SEP ); +#endif + + + /* Compute the full file name */ + if( wxIsAbsolutePath( aDocName ) || aPaths == NULL) + fullfilename = aDocName; + /* If the file exists, this is a trivial case: return the filename + * "as this". the name can be an absolute path, or a relative path + * like ./filename or ../<filename> + */ + else if( wxFileName::FileExists( aDocName ) ) + fullfilename = aDocName; + else + { + fullfilename = aPaths->FindValidPath( aDocName ); + } + + wxString mask( wxT( "*" ) ), extension; + +#ifdef __WINDOWS__ + mask += wxT( ".*" ); + extension = wxT( ".*" ); +#endif + + if( wxIsWild( fullfilename ) ) + { + fullfilename = EDA_FILE_SELECTOR( _( "Doc Files" ), + wxPathOnly( fullfilename ), + fullfilename, + extension, + mask, + aParent, + wxFD_OPEN, + true, + wxPoint( -1, -1 ) ); + if( fullfilename.IsEmpty() ) + return false; + } + + if( !wxFileExists( fullfilename ) ) + { + msg.Printf( _( "Doc File '%s' not found" ), GetChars( aDocName ) ); + DisplayError( aParent, msg ); + return false; + } + + wxFileName currentFileName( fullfilename ); + + wxString file_ext = currentFileName.GetExt(); + + if( file_ext == wxT( "pdf" ) ) + { + success = OpenPDF( fullfilename ); + return success; + } + + /* Try to launch some browser (useful under linux) */ + wxFileType* filetype; + + wxString type; + filetype = wxTheMimeTypesManager->GetFileTypeFromExtension( file_ext ); + + if( !filetype ) // 2nd attempt. + { + mimeDatabase = new wxMimeTypesManager; + mimeDatabase->AddFallbacks( EDAfallbacks ); + filetype = mimeDatabase->GetFileTypeFromExtension( file_ext ); + delete mimeDatabase; + mimeDatabase = NULL; + } + + if( filetype ) + { + wxFileType::MessageParameters params( fullfilename, type ); + + success = filetype->GetOpenCommand( &command, params ); + delete filetype; + + if( success ) + success = ProcessExecute( command ); + } + + if( !success ) + { + msg.Printf( _( "Unknown MIME type for doc file <%s>" ), GetChars( fullfilename ) ); + DisplayError( aParent, msg ); + } + + return success; +} + + +int KeyWordOk( const wxString& KeyList, const wxString& Database ) +{ + wxString KeysCopy, DataList; + + if( KeyList.IsEmpty() ) + return 0; + + KeysCopy = KeyList; KeysCopy.MakeUpper(); + DataList = Database; DataList.MakeUpper(); + + wxStringTokenizer Token( KeysCopy, wxT( " \n\r" ) ); + + while( Token.HasMoreTokens() ) + { + wxString Key = Token.GetNextToken(); + + // Search Key in Datalist: + wxStringTokenizer Data( DataList, wxT( " \n\r" ) ); + + while( Data.HasMoreTokens() ) + { + wxString word = Data.GetNextToken(); + + if( word == Key ) + return 1; // Key found ! + } + } + + // keyword not found + return 0; +} diff --git a/common/eda_text.cpp b/common/eda_text.cpp new file mode 100644 index 0000000..0b53d00 --- /dev/null +++ b/common/eda_text.cpp @@ -0,0 +1,517 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2016 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2004-2016 KiCad Developers, see change_log.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 + */ + +/** + * @file eda_text.cpp + * @brief Implementation of base KiCad text object. + */ + +#include <eda_text.h> +#include <drawtxt.h> +#include <macros.h> +#include <trigo.h> // RotatePoint +#include <class_drawpanel.h> // EDA_DRAW_PANEL + +// Conversion to application internal units defined at build time. +#if defined( PCBNEW ) + #include <class_board_item.h> // for FMT_IU +#elif defined( EESCHEMA ) + #include <sch_item_struct.h> // for FMT_IU +#elif defined( GERBVIEW ) +#elif defined( PL_EDITOR ) + #include <base_units.h> + #define FMT_IU Double2Str +#else +#error "Cannot resolve units formatting due to no definition of EESCHEMA or PCBNEW." +#endif + +#include <gal/stroke_font.h> + +#include <convert_to_biu.h> + +EDA_TEXT::EDA_TEXT( const wxString& text ) +{ + m_Size.x = m_Size.y = Mils2iu( DEFAULT_SIZE_TEXT ); // Width and height of font. + m_Orient = 0; // Rotation angle in 0.1 degrees. + m_Attributs = 0; + m_Mirror = false; // display mirror if true + m_HJustify = GR_TEXT_HJUSTIFY_CENTER; // Default horizontal justification is centered. + m_VJustify = GR_TEXT_VJUSTIFY_CENTER; // Default vertical justification is centered. + m_Thickness = 0; // thickness + m_Italic = false; // true = italic shape. + m_Bold = false; + m_MultilineAllowed = false; // Set to true for multiline text. + m_Text = text; +} + + +EDA_TEXT::EDA_TEXT( const EDA_TEXT& aText ) +{ + m_Pos = aText.m_Pos; + m_Size = aText.m_Size; + m_Orient = aText.m_Orient; + m_Attributs = aText.m_Attributs; + m_Mirror = aText.m_Mirror; + m_HJustify = aText.m_HJustify; + m_VJustify = aText.m_VJustify; + m_Thickness = aText.m_Thickness; + m_Italic = aText.m_Italic; + m_Bold = aText.m_Bold; + m_MultilineAllowed = aText.m_MultilineAllowed; + m_Text = aText.m_Text; +} + + +EDA_TEXT::~EDA_TEXT() +{ +} + + +int EDA_TEXT::LenSize( const wxString& aLine ) const +{ + return GraphicTextWidth( aLine, m_Size.x, m_Italic, m_Bold ); +} + + +wxString EDA_TEXT::ShortenedShownText() const +{ + wxString tmp = GetShownText(); + tmp.Replace( wxT( "\n" ), wxT( " " ) ); + tmp.Replace( wxT( "\r" ), wxT( " " ) ); + tmp.Replace( wxT( "\t" ), wxT( " " ) ); + + if( tmp.Length() > 15 ) + tmp = tmp.Left( 12 ) + wxT( "..." ); + + return tmp; +} + + +/* + * calculate the distance (pitch) between 2 text lines + * the distance includes the interline + room for chars like j { and [ + * Is used for multiline texts, but also for single line texts, to calculate + * the text bounding box + */ +int EDA_TEXT::GetInterline( int aTextThickness ) const +{ + int thickness = aTextThickness <= 0 ? m_Thickness : aTextThickness; + return KiROUND( m_Size.y * KIGFX::STROKE_FONT::INTERLINE_PITCH_RATIO ) + thickness; +} + +EDA_RECT EDA_TEXT::GetTextBox( int aLine, int aThickness, bool aInvertY ) const +{ + EDA_RECT rect; + wxPoint pos; + wxArrayString strings; + wxString text = GetShownText(); + int thickness = ( aThickness < 0 ) ? m_Thickness : aThickness; + int linecount = 1; + + if( m_MultilineAllowed ) + { + wxStringSplit( text, strings, '\n' ); + + if( strings.GetCount() ) // GetCount() == 0 for void strings + { + if( aLine >= 0 && (aLine < (int)strings.GetCount()) ) + text = strings.Item( aLine ); + else + text = strings.Item( 0 ); + + linecount = strings.GetCount(); + } + } + + // calculate the H and V size + int dx = LenSize( text ); + int dy = GetInterline( aThickness ); + + // Creates bounding box (rectangle) for an horizontal text + wxSize textsize = wxSize( dx, dy ); + + if( aInvertY ) + rect.SetOrigin( m_Pos.x, -m_Pos.y ); + else + rect.SetOrigin( m_Pos ); + + // The bbox vertical size returned by GetInterline( aThickness ) + // includes letters like j and y and ] + interval between lines. + // The interval below the last line is not usefull, and we can use its half value + // as vertical margin above the text + // the full interval is roughly m_Size.y * 0.4 - aThickness/2 + rect.Move( wxPoint( 0, aThickness/4 - KiROUND( m_Size.y * 0.2 ) ) ); + + // for multiline texts and aLine < 0, merge all rectangles + // ( if aLine < 0, we want the full text bounding box ) + if( m_MultilineAllowed && aLine < 0 ) + { + for( unsigned ii = 1; ii < strings.GetCount(); ii++ ) + { + text = strings.Item( ii ); + dx = LenSize( text ); + textsize.x = std::max( textsize.x, dx ); + textsize.y += dy; + } + } + + rect.SetSize( textsize ); + + /* Now, calculate the rect origin, according to text justification + * At this point the rectangle origin is the text origin (m_Pos). + * This is true only for left and top text justified texts (using top to bottom Y axis + * orientation). and must be recalculated for others justifications + * also, note the V justification is relative to the first line + */ + switch( m_HJustify ) + { + case GR_TEXT_HJUSTIFY_LEFT: + if( m_Mirror ) + rect.SetX( rect.GetX() - rect.GetWidth() ); + break; + + case GR_TEXT_HJUSTIFY_CENTER: + rect.SetX( rect.GetX() - (rect.GetWidth() / 2) ); + break; + + case GR_TEXT_HJUSTIFY_RIGHT: + if( !m_Mirror ) + rect.SetX( rect.GetX() - rect.GetWidth() ); + break; + } + + dy = m_Size.y + thickness; + + switch( m_VJustify ) + { + case GR_TEXT_VJUSTIFY_TOP: + break; + + case GR_TEXT_VJUSTIFY_CENTER: + rect.SetY( rect.GetY() - ( dy / 2) ); + break; + + case GR_TEXT_VJUSTIFY_BOTTOM: + rect.SetY( rect.GetY() - dy ); + break; + } + + if( linecount > 1 ) + { + int yoffset; + linecount -= 1; + + switch( m_VJustify ) + { + case GR_TEXT_VJUSTIFY_TOP: + break; + + case GR_TEXT_VJUSTIFY_CENTER: + yoffset = linecount * GetInterline() / 2; + rect.SetY( rect.GetY() - yoffset ); + break; + + case GR_TEXT_VJUSTIFY_BOTTOM: + yoffset = linecount * GetInterline( aThickness ); + rect.SetY( rect.GetY() - yoffset ); + break; + } + } + + rect.Normalize(); // Make h and v sizes always >= 0 + + return rect; +} + + +bool EDA_TEXT::TextHitTest( const wxPoint& aPoint, int aAccuracy ) const +{ + EDA_RECT rect = GetTextBox( -1 ); // Get the full text area. + wxPoint location = aPoint; + + rect.Inflate( aAccuracy ); + RotatePoint( &location, m_Pos, -m_Orient ); + + return rect.Contains( location ); +} + + +bool EDA_TEXT::TextHitTest( const EDA_RECT& aRect, bool aContains, int aAccuracy ) const +{ + EDA_RECT rect = aRect; + + rect.Inflate( aAccuracy ); + + if( aContains ) + return rect.Contains( GetTextBox( -1 ) ); + + return rect.Intersects( GetTextBox( -1 ) ); +} + + +void EDA_TEXT::Draw( EDA_RECT* aClipBox, wxDC* aDC, const wxPoint& aOffset, + EDA_COLOR_T aColor, GR_DRAWMODE aDrawMode, + EDA_DRAW_MODE_T aFillMode, EDA_COLOR_T aAnchor_color ) +{ + if( m_MultilineAllowed ) + { + std::vector<wxPoint> positions; + wxArrayString strings; + wxStringSplit( GetShownText(), strings, '\n' ); + positions.reserve( strings.Count() ); + + GetPositionsOfLinesOfMultilineText(positions, strings.Count() ); + + for( unsigned ii = 0; ii < strings.Count(); ii++ ) + { + wxString& txt = strings.Item( ii ); + drawOneLineOfText( aClipBox, aDC, aOffset, aColor, + aDrawMode, aFillMode, txt, positions[ii] ); + } + } + else + drawOneLineOfText( aClipBox, aDC, aOffset, aColor, + aDrawMode, aFillMode, GetShownText(), m_Pos ); + + // Draw text anchor, if requested + if( aAnchor_color != UNSPECIFIED_COLOR ) + { + GRDrawAnchor( aClipBox, aDC, + m_Pos.x + aOffset.x, m_Pos.y + aOffset.y, + DIM_ANCRE_TEXTE, aAnchor_color ); + } +} + + +void EDA_TEXT::GetPositionsOfLinesOfMultilineText( + std::vector<wxPoint>& aPositions, int aLineCount ) const +{ + wxPoint pos = m_Pos; // Position of first line of the + // multiline text according to + // the center of the multiline text block + + wxPoint offset; // Offset to next line. + + offset.y = GetInterline(); + + if( aLineCount > 1 ) + { + switch( m_VJustify ) + { + case GR_TEXT_VJUSTIFY_TOP: + break; + + case GR_TEXT_VJUSTIFY_CENTER: + pos.y -= ( aLineCount - 1 ) * offset.y / 2; + break; + + case GR_TEXT_VJUSTIFY_BOTTOM: + pos.y -= ( aLineCount - 1 ) * offset.y; + break; + } + } + + // Rotate the position of the first line + // around the center of the multiline text block + RotatePoint( &pos, m_Pos, m_Orient ); + + // Rotate the offset lines to increase happened in the right direction + RotatePoint( &offset, m_Orient ); + + for( int ii = 0; ii < aLineCount; ii++ ) + { + aPositions.push_back( pos ); + pos += offset; + } +} + +void EDA_TEXT::drawOneLineOfText( EDA_RECT* aClipBox, wxDC* aDC, + const wxPoint& aOffset, EDA_COLOR_T aColor, + GR_DRAWMODE aDrawMode, EDA_DRAW_MODE_T aFillMode, + const wxString& aText, const wxPoint &aPos ) +{ + int width = m_Thickness; + + if( aDrawMode != UNSPECIFIED_DRAWMODE ) + GRSetDrawMode( aDC, aDrawMode ); + + if( aFillMode == SKETCH ) + width = -width; + + wxSize size = m_Size; + + if( m_Mirror ) + size.x = -size.x; + + DrawGraphicText( aClipBox, aDC, aOffset + aPos, aColor, aText, m_Orient, size, + m_HJustify, m_VJustify, width, m_Italic, m_Bold ); +} + + +wxString EDA_TEXT::GetTextStyleName() +{ + int style = 0; + + if( m_Italic ) + style = 1; + + if( m_Bold ) + style += 2; + + wxString stylemsg[4] = { + _("Normal"), + _("Italic"), + _("Bold"), + _("Bold+Italic") + }; + + return stylemsg[style]; +} + + +bool EDA_TEXT::IsDefaultFormatting() const +{ + return ( ( m_Size.x == Mils2iu( DEFAULT_SIZE_TEXT ) ) + && ( m_Size.y == Mils2iu( DEFAULT_SIZE_TEXT ) ) + && ( m_Attributs == 0 ) + && ( m_Mirror == false ) + && ( m_HJustify == GR_TEXT_HJUSTIFY_CENTER ) + && ( m_VJustify == GR_TEXT_VJUSTIFY_CENTER ) + && ( m_Thickness == 0 ) + && ( m_Italic == false ) + && ( m_Bold == false ) + && ( m_MultilineAllowed == false ) ); +} + +void EDA_TEXT::Format( OUTPUTFORMATTER* aFormatter, int aNestLevel, int aControlBits ) const + throw( IO_ERROR ) +{ +#ifndef GERBVIEW // Gerbview does not use EDA_TEXT::Format + // and does not define FMT_IU, used here + // however this function should exist + if( !IsDefaultFormatting() ) + { + aFormatter->Print( aNestLevel+1, "(effects" ); + + if( ( m_Size.x != Mils2iu( DEFAULT_SIZE_TEXT ) ) + || ( m_Size.y != Mils2iu( DEFAULT_SIZE_TEXT ) ) + || ( m_Thickness != 0 ) || m_Bold || m_Italic ) + { + aFormatter->Print( 0, " (font" ); + + // Add font support here at some point in the future. + + if( ( m_Size.x != Mils2iu( DEFAULT_SIZE_TEXT ) ) + || ( m_Size.y != Mils2iu( DEFAULT_SIZE_TEXT ) ) ) + aFormatter->Print( 0, " (size %s %s)", FMT_IU( m_Size.GetHeight() ).c_str(), + FMT_IU( m_Size.GetWidth() ).c_str() ); + + if( m_Thickness != 0 ) + aFormatter->Print( 0, " (thickness %s)", FMT_IU( GetThickness() ).c_str() ); + + if( m_Bold ) + aFormatter->Print( 0, " bold" ); + + if( IsItalic() ) + aFormatter->Print( 0, " italic" ); + + aFormatter->Print( 0, ")"); + } + + if( m_Mirror || ( m_HJustify != GR_TEXT_HJUSTIFY_CENTER ) + || ( m_VJustify != GR_TEXT_VJUSTIFY_CENTER ) ) + { + aFormatter->Print( 0, " (justify"); + + if( m_HJustify != GR_TEXT_HJUSTIFY_CENTER ) + aFormatter->Print( 0, (m_HJustify == GR_TEXT_HJUSTIFY_LEFT) ? " left" : " right" ); + + if( m_VJustify != GR_TEXT_VJUSTIFY_CENTER ) + aFormatter->Print( 0, (m_VJustify == GR_TEXT_VJUSTIFY_TOP) ? " top" : " bottom" ); + + if( m_Mirror ) + aFormatter->Print( 0, " mirror" ); + + aFormatter->Print( 0, ")" ); + } + + // As of now the only place this is used is in Eeschema to hide or show the text. + if( m_Attributs ) + aFormatter->Print( 0, " hide" ); + + aFormatter->Print( 0, ")\n" ); + } +#endif +} + +// Convert the text shape to a list of segment +// each segment is stored as 2 wxPoints: its starting point and its ending point +// we are using DrawGraphicText to create the segments. +// and therefore a call-back function is needed +static std::vector<wxPoint>* s_cornerBuffer; + +// This is a call back function, used by DrawGraphicText to put each segment in buffer +static void addTextSegmToBuffer( int x0, int y0, int xf, int yf ) +{ + s_cornerBuffer->push_back( wxPoint( x0, y0 ) ); + s_cornerBuffer->push_back( wxPoint( xf, yf ) ); +} + +void EDA_TEXT::TransformTextShapeToSegmentList( std::vector<wxPoint>& aCornerBuffer ) const +{ + wxSize size = GetSize(); + + if( IsMirrored() ) + size.x = -size.x; + + s_cornerBuffer = &aCornerBuffer; + EDA_COLOR_T color = BLACK; // not actually used, but needed by DrawGraphicText + + if( IsMultilineAllowed() ) + { + wxArrayString strings_list; + wxStringSplit( GetShownText(), strings_list, wxChar('\n') ); + std::vector<wxPoint> positions; + positions.reserve( strings_list.Count() ); + GetPositionsOfLinesOfMultilineText( positions,strings_list.Count() ); + + for( unsigned ii = 0; ii < strings_list.Count(); ii++ ) + { + wxString txt = strings_list.Item( ii ); + DrawGraphicText( NULL, NULL, positions[ii], color, + txt, GetOrientation(), size, + GetHorizJustify(), GetVertJustify(), + GetThickness(), IsItalic(), + true, addTextSegmToBuffer ); + } + } + else + { + DrawGraphicText( NULL, NULL, GetTextPosition(), color, + GetText(), GetOrientation(), size, + GetHorizJustify(), GetVertJustify(), + GetThickness(), IsItalic(), + true, addTextSegmToBuffer ); + } +} diff --git a/common/filter_reader.cpp b/common/filter_reader.cpp new file mode 100644 index 0000000..7ab9018 --- /dev/null +++ b/common/filter_reader.cpp @@ -0,0 +1,107 @@ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2007-2010 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2007 KiCad Developers, see change_log.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 <string.h> +#include <richio.h> +#include <filter_reader.h> + + +FILTER_READER::FILTER_READER( LINE_READER& aReader ) : + LINE_READER( 1 ), + reader( aReader ) +{ + // Not using our own line buffer, will be using aReader's. This changes + // the meaning of this->line to be merely a pointer to aReader's line, which of course + // is not owned here. + delete [] line; + + line = 0; +} + + +FILTER_READER::~FILTER_READER() +{ + // Our 'line' points to aReader's, and he will delete that buffer. + // Prevent subsequent call to ~LINE_READER() from deleting a buffer we do not own. + line = 0; +} + + +char* FILTER_READER::ReadLine() throw( IO_ERROR ) +{ + char* s; + + while( ( s = reader.ReadLine() ) != NULL ) + { + if( !strchr( "#\n\r", s[0] ) ) + break; + } + + line = reader.Line(); + length = reader.Length(); + + return length ? line : NULL; +} + + +WHITESPACE_FILTER_READER::WHITESPACE_FILTER_READER( LINE_READER& aReader ) : + LINE_READER( 1 ), + reader( aReader ) +{ + // Not using our own line buffer, will be using aReader's. This changes + // the meaning of this->line to be merely a pointer to aReader's line, which of course + // is not owned here. + delete [] line; + + line = 0; +} + + +WHITESPACE_FILTER_READER::~WHITESPACE_FILTER_READER() +{ + // Our 'line' points to aReader's, and he will delete that buffer. + // Prevent subsequent call to ~LINE_READER() from deleting a buffer we do not own. + line = 0; +} + + +char* WHITESPACE_FILTER_READER::ReadLine() throw( IO_ERROR ) +{ + char* s; + + while( ( s = reader.ReadLine() ) != NULL ) + { + while( s != NULL && strchr( " \t", *s ) ) + s++; + + if( s != NULL && !strchr( "#\n\r", *s ) ) + break; + } + + line = s; + length = reader.Length(); + + return length ? line : NULL; +} diff --git a/common/findkicadhelppath.cpp.notused b/common/findkicadhelppath.cpp.notused new file mode 100644 index 0000000..d2d781e --- /dev/null +++ b/common/findkicadhelppath.cpp.notused @@ -0,0 +1,126 @@ + +#include <fctsys.h> +#include <pgm_base.h> +#include <macros.h> +#include <gestfich.h> + + +/** + * Function FindKicadHelpPath + * finds the absolute path for KiCad "help" (or "help/<language>") + * Find path kicad/doc/help/xx/ or kicad/doc/help/: + * from BinDir + * else from environment variable KICAD + * else from one of s_HelpPathList + * typically c:/kicad/doc/help or /usr/share/kicad/help + * or /usr/local/share/kicad/help + * (must have kicad in path name) + * + * xx = iso639-1 language id (2 letters (generic) or 4 letters): + * fr = french (or fr_FR) + * en = English (or en_GB or en_US ...) + * de = deutch + * es = spanish + * pt = portuguese (or pt_BR ...) + * + * default = en (if not found = fr) + */ +wxString FindKicadHelpPath() +{ + bool found = false; + wxString bin_dir = Pgm().GetExecutablePath(); + + if( bin_dir.Last() == '/' ) + bin_dir.RemoveLast(); + + wxString fullPath = bin_dir.BeforeLast( '/' ); // cd .. + + fullPath += wxT( "/doc/help/" ); + + wxString localeString = Pgm().GetLocale()->GetCanonicalName(); + + wxString path_tmp = fullPath; + +#ifdef __WINDOWS__ + path_tmp.MakeLower(); +#endif + + if( path_tmp.Contains( wxT( "kicad" ) ) ) + { + if( wxDirExists( fullPath ) ) + found = true; + } + + // find kicad/help/ from environment variable KICAD + if( !found && Pgm().IsKicadEnvVariableDefined() ) + { + fullPath = Pgm().GetKicadEnvVariable() + wxT( "/doc/help/" ); + + if( wxDirExists( fullPath ) ) + found = true; + } + + if( !found ) + { + // Possibilities online help + const static wxChar* possibilities[] = { +#ifdef __WINDOWS__ + wxT( "c:/kicad/doc/help/" ), + wxT( "d:/kicad/doc/help/" ), + wxT( "c:/Program Files/kicad/doc/help/" ), + wxT( "d:/Program Files/kicad/doc/help/" ), +#else + wxT( "/usr/share/doc/kicad/help/" ), + wxT( "/usr/local/share/doc/kicad/help/" ), + wxT( "/usr/local/kicad/doc/help/" ), // default install for "universal + // tarballs" and build for a server + // (new) + wxT( "/usr/local/kicad/help/" ), // default install for "universal + // tarballs" and build for a server + // (old) +#endif + }; + + for( unsigned i=0; i<DIM(possibilities); ++i ) + { + fullPath = possibilities[i]; + + if( wxDirExists( fullPath ) ) + { + found = true; + break; + } + } + } + + if( found ) + { + wxString langFullPath = fullPath + localeString + UNIX_STRING_DIR_SEP; + + if( wxDirExists( langFullPath ) ) + return langFullPath; + + langFullPath = fullPath + localeString.Left( 2 ) + UNIX_STRING_DIR_SEP; + + if( wxDirExists( langFullPath ) ) + return langFullPath; + + langFullPath = fullPath + wxT( "en/" ); + + if( wxDirExists( langFullPath ) ) + { + return langFullPath; + } + else + { + langFullPath = fullPath + wxT( "fr/" ); + + if( wxDirExists( langFullPath ) ) + return langFullPath; + } + return fullPath; + } + + return wxEmptyString; +} + diff --git a/common/footprint_info.cpp b/common/footprint_info.cpp new file mode 100644 index 0000000..3f5e64b --- /dev/null +++ b/common/footprint_info.cpp @@ -0,0 +1,317 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2011 Jean-Pierre Charras, <jp.charras@wanadoo.fr> + * Copyright (C) 2013-2016 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 1992-2017 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 + */ + +/** + * @file footprint_info.cpp + */ + + +/** + No. concurrent threads doing "http(s) GET". More than 6 is not significantly + faster, less than 6 is likely slower. Main thread is in this count, so if + set to 1 then no temp threads are created. +*/ +#define READER_THREADS 6 + +/* + * Functions to read footprint libraries and fill m_footprints by available footprints names + * and their documentation (comments and keywords) + */ + +#include <fctsys.h> +#include <common.h> +#include <macros.h> +#include <pgm_base.h> +#include <wildcards_and_files_ext.h> +#include <footprint_info.h> +#include <io_mgr.h> +#include <fp_lib_table.h> +#include <fpid.h> +#include <class_module.h> +#include <boost/thread.hpp> +#include <html_messagebox.h> + + +/* +static wxString ToHTMLFragment( const IO_ERROR* aDerivative ) +{ + @todo + + 1) change up IO_ERROR so it keeps linenumbers, source file name and + error message in separate strings. + + 2) Add a summarizing virtual member like + virtual wxString What() + to combine all portions of an IO_ERROR's text into a single wxString. + + 3) Do same for PARSE_ERROR. + + 4) Add a "reason or error category" to IO_ERROR and thereby also PARSE_ERROR? + + msg += " + + for( int i=0; i<aCount; ++i ) + { + + + wxArrayString* sl = wxStringSplit( aList, wxChar( '\n' ) ); + + + delete sl; + } + + wxString msg = wxT( "<ul>" ); + + for ( unsigned ii = 0; ii < strings_list->GetCount(); ii++ ) + { + msg += wxT( "<li>" ); + msg += strings_list->Item( ii ) + wxT( "</li>" ); + } + + msg += wxT( "</ul>" ); + + m_htmlWindow->AppendToPage( msg ); + + delete strings_list; +} +*/ + + +void FOOTPRINT_INFO::load() +{ + FP_LIB_TABLE* fptable = m_owner->GetTable(); + + wxASSERT( fptable ); + + std::auto_ptr<MODULE> m( fptable->FootprintLoad( m_nickname, m_fpname ) ); + + if( m.get() == NULL ) // Should happen only with malformed/broken libraries + { + m_pad_count = 0; + m_unique_pad_count = 0; + } + else + { + m_pad_count = m->GetPadCount( DO_NOT_INCLUDE_NPTH ); + m_unique_pad_count = m->GetUniquePadCount( DO_NOT_INCLUDE_NPTH ); + m_keywords = m->GetKeywords(); + m_doc = m->GetDescription(); + + // tell ensure_loaded() I'm loaded. + m_loaded = true; + } +} + + +void FOOTPRINT_LIST::loader_job( const wxString* aNicknameList, int aJobZ ) +{ + for( int i=0; i<aJobZ; ++i ) + { + const wxString& nickname = aNicknameList[i]; + + try + { + wxArrayString fpnames = m_lib_table->FootprintEnumerate( nickname ); + + for( unsigned ni=0; ni<fpnames.GetCount(); ++ni ) + { + FOOTPRINT_INFO* fpinfo = new FOOTPRINT_INFO( this, nickname, fpnames[ni] ); + + addItem( fpinfo ); + } + } + catch( const PARSE_ERROR& pe ) + { + // m_errors.push_back is not thread safe, lock its MUTEX. + MUTLOCK lock( m_errors_lock ); + + ++m_error_count; // modify only under lock + m_errors.push_back( new IO_ERROR( pe ) ); + } + catch( const IO_ERROR& ioe ) + { + MUTLOCK lock( m_errors_lock ); + + ++m_error_count; + m_errors.push_back( new IO_ERROR( ioe ) ); + } + + // Catch anything unexpected and map it into the expected. + // Likely even more important since this function runs on GUI-less + // worker threads. + catch( const std::exception& se ) + { + // This is a round about way to do this, but who knows what THROW_IO_ERROR() + // may be tricked out to do someday, keep it in the game. + try + { + THROW_IO_ERROR( se.what() ); + } + catch( const IO_ERROR& ioe ) + { + MUTLOCK lock( m_errors_lock ); + + ++m_error_count; + m_errors.push_back( new IO_ERROR( ioe ) ); + } + } + } +} + + +bool FOOTPRINT_LIST::ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxString* aNickname ) +{ + bool retv = true; + + m_lib_table = aTable; + + // Clear data before reading files + m_error_count = 0; + m_errors.clear(); + m_list.clear(); + + if( aNickname ) + // single footprint + loader_job( aNickname, 1 ); + else + { + std::vector< wxString > nicknames; + + // do all of them + nicknames = aTable->GetLogicalLibs(); + + // Even though the PLUGIN API implementation is the place for the + // locale toggling, in order to keep LOCAL_IO::C_count at 1 or greater + // for the duration of all helper threads, we increment by one here via instantiation. + // Only done here because of the multi-threaded nature of this code. + // Without this C_count skips in and out of "equal to zero" and causes + // needless locale toggling among the threads, based on which of them + // are in a PLUGIN::FootprintLoad() function. And that is occasionally + // none of them. + LOCALE_IO top_most_nesting; + + // Something which will not invoke a thread copy constructor, one of many ways obviously: + typedef boost::ptr_vector< boost::thread > MYTHREADS; + + MYTHREADS threads; + + unsigned jobz = (nicknames.size() + READER_THREADS - 1) / READER_THREADS; + + // Give each thread JOBZ nicknames to process. The last portion of, or if the entire + // size() is small, I'll do myself. + for( unsigned i=0; i<nicknames.size(); ) + { + if( i + jobz >= nicknames.size() ) // on the last iteration of this for(;;) + { + jobz = nicknames.size() - i; + + // Only a little bit to do, I'll do it myself on current thread. + // I am part of the READER_THREADS count. + loader_job( &nicknames[i], jobz ); + } + else + { + // Delegate the job to a temporary thread created here. + threads.push_back( new boost::thread( &FOOTPRINT_LIST::loader_job, + this, &nicknames[i], jobz ) ); + } + + i += jobz; + } + + // Wait for all the worker threads to complete, it does not matter in what order + // we wait for them as long as a full sweep is made. Think of the great race, + // everyone must finish. + for( unsigned i=0; i<threads.size(); ++i ) + { + threads[i].join(); + } + + m_list.sort(); + } + + // The result of this function can be a blend of successes and failures, whose + // mix is given by the Count()s of the two lists. The return value indicates whether + // an abort occurred, even true does not necessarily mean full success, although + // false definitely means failure. + + return retv; +} + + +FOOTPRINT_INFO* FOOTPRINT_LIST::GetModuleInfo( const wxString& aFootprintName ) +{ + if( aFootprintName.IsEmpty() ) + return NULL; + + BOOST_FOREACH( FOOTPRINT_INFO& fp, m_list ) + { + FPID fpid; + + wxCHECK_MSG( fpid.Parse( aFootprintName ) < 0, NULL, + wxString::Format( wxT( "'%s' is not a valid FPID." ), + GetChars( aFootprintName ) ) ); + + wxString libNickname = fpid.GetLibNickname(); + wxString footprintName = fpid.GetFootprintName(); + + if( libNickname == fp.GetNickname() && footprintName == fp.GetFootprintName() ) + return &fp; + } + + return NULL; +} + + +bool FOOTPRINT_INFO::InLibrary( const wxString& aLibrary ) const +{ + return aLibrary == m_nickname; +} + + +void FOOTPRINT_LIST::DisplayErrors( wxTopLevelWindow* aWindow ) +{ + // @todo: go to a more HTML !<table>! ? centric output, possibly with + // recommendations for remedy of errors. Add numeric error codes + // to PARSE_ERROR, and switch on them for remedies, etc. Full + // access is provided to everything in every exception! + + HTML_MESSAGE_BOX dlg( aWindow, _( "Load Error" ) ); + + dlg.MessageSet( _( "Errors were encountered loading footprints:" ) ); + + wxString msg; + + for( unsigned i = 0; i<m_errors.size(); ++i ) + { + msg += wxT( "<p>" ) + m_errors[i].errorText + wxT( "</p>" ); + } + + // Preserve new lines in error messages so queued errors don't run together. + msg.Replace( "\n", "<BR>" ); + dlg.AddHTML_Text( msg ); + + dlg.ShowModal(); +} diff --git a/common/fp_lib_table.cpp b/common/fp_lib_table.cpp new file mode 100644 index 0000000..d9fc1e7 --- /dev/null +++ b/common/fp_lib_table.cpp @@ -0,0 +1,789 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2010-2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2012 Wayne Stambaugh <stambaughw@verizon.net> + * Copyright (C) 2012-2015 KiCad Developers, see change_log.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 <fctsys.h> +#include <wx/config.h> // wxExpandEnvVars() +#include <wx/filename.h> +#include <wx/stdpaths.h> + +#include <set> + +//#include <pgm_base.h> +#include <kiface_i.h> +#include <search_stack.h> +#include <pcb_netlist.h> +#include <reporter.h> +#include <footprint_info.h> +#include <wildcards_and_files_ext.h> +#include <fpid.h> +#include <fp_lib_table_lexer.h> +#include <fp_lib_table.h> +#include <class_module.h> + +using namespace FP_LIB_TABLE_T; + + +static const wxChar global_tbl_name[] = wxT( "fp-lib-table" ); + + +void FP_LIB_TABLE::ROW::SetType( const wxString& aType ) +{ + type = IO_MGR::EnumFromStr( aType ); + + if( IO_MGR::PCB_FILE_T( -1 ) == type ) + type = IO_MGR::KICAD; +} + + +void FP_LIB_TABLE::ROW::SetFullURI( const wxString& aFullURI ) +{ + uri_user = aFullURI; + +#if !FP_LATE_ENVVAR + uri_expanded = FP_LIB_TABLE::ExpandSubstitutions( aFullURI ); +#endif +} + + +const wxString FP_LIB_TABLE::ROW::GetFullURI( bool aSubstituted ) const +{ + if( aSubstituted ) + { +#if !FP_LATE_ENVVAR // early expansion + return uri_expanded; + +#else // late expansion + return FP_LIB_TABLE::ExpandSubstitutions( uri_user ); +#endif + } + else + return uri_user; +} + + +FP_LIB_TABLE::ROW::ROW( const ROW& a ) : + nickName( a.nickName ), + type( a.type ), + options( a.options ), + description( a.description ), + properties( 0 ) +{ + // may call ExpandSubstitutions() + SetFullURI( a.uri_user ); + + if( a.properties ) + properties = new PROPERTIES( *a.properties ); +} + + +FP_LIB_TABLE::ROW& FP_LIB_TABLE::ROW::operator=( const ROW& r ) +{ + nickName = r.nickName; + type = r.type; + options = r.options; + description = r.description; + properties = r.properties ? new PROPERTIES( *r.properties ) : NULL; + + // may call ExpandSubstitutions() + SetFullURI( r.uri_user ); + + // Do not copy the PLUGIN, it is lazily created. Delete any existing + // destination plugin. + setPlugin( NULL ); + + return *this; +} + + +bool FP_LIB_TABLE::ROW::operator==( const ROW& r ) const +{ + return nickName == r.nickName + && uri_user == r.uri_user + && type == r.type + && options == r.options + && description == r.description + ; +} + + +FP_LIB_TABLE::FP_LIB_TABLE( FP_LIB_TABLE* aFallBackTable ) : + fallBack( aFallBackTable ) +{ + // not copying fall back, simply search aFallBackTable separately + // if "nickName not found". +} + + +FP_LIB_TABLE::~FP_LIB_TABLE() +{ + // *fallBack is not owned here. +} + + +wxArrayString FP_LIB_TABLE::FootprintEnumerate( const wxString& aNickname ) +{ + const ROW* row = FindRow( aNickname ); + wxASSERT( (PLUGIN*) row->plugin ); + return row->plugin->FootprintEnumerate( row->GetFullURI( true ), row->GetProperties() ); +} + + +MODULE* FP_LIB_TABLE::FootprintLoad( const wxString& aNickname, const wxString& aFootprintName ) +{ + const ROW* row = FindRow( aNickname ); + wxASSERT( (PLUGIN*) row->plugin ); + + MODULE* ret = row->plugin->FootprintLoad( row->GetFullURI( true ), aFootprintName, row->GetProperties() ); + + // The library cannot know its own name, because it might have been renamed or moved. + // Therefore footprints cannot know their own library nickname when residing in + // a footprint library. + // Only at this API layer can we tell the footprint about its actual library nickname. + if( ret ) + { + // remove "const"-ness, I really do want to set nickname without + // having to copy the FPID and its two strings, twice each. + FPID& fpid = (FPID&) ret->GetFPID(); + + // Catch any misbehaving plugin, which should be setting internal footprint name properly: + wxASSERT( aFootprintName == (wxString) fpid.GetFootprintName() ); + + // and clearing nickname + wxASSERT( !fpid.GetLibNickname().size() ); + + fpid.SetLibNickname( row->GetNickName() ); + } + + return ret; +} + + +FP_LIB_TABLE::SAVE_T FP_LIB_TABLE::FootprintSave( const wxString& aNickname, const MODULE* aFootprint, bool aOverwrite ) +{ + const ROW* row = FindRow( aNickname ); + wxASSERT( (PLUGIN*) row->plugin ); + + if( !aOverwrite ) + { + // Try loading the footprint to see if it already exists, caller wants overwrite + // protection, which is atypical, not the default. + + wxString fpname = aFootprint->GetFPID().GetFootprintName(); + + std::auto_ptr<MODULE> m( row->plugin->FootprintLoad( row->GetFullURI( true ), fpname, row->GetProperties() ) ); + + if( m.get() ) + return SAVE_SKIPPED; + } + + row->plugin->FootprintSave( row->GetFullURI( true ), aFootprint, row->GetProperties() ); + + return SAVE_OK; +} + + +void FP_LIB_TABLE::FootprintDelete( const wxString& aNickname, const wxString& aFootprintName ) +{ + const ROW* row = FindRow( aNickname ); + wxASSERT( (PLUGIN*) row->plugin ); + return row->plugin->FootprintDelete( row->GetFullURI( true ), aFootprintName, row->GetProperties() ); +} + + +bool FP_LIB_TABLE::IsFootprintLibWritable( const wxString& aNickname ) +{ + const ROW* row = FindRow( aNickname ); + wxASSERT( (PLUGIN*) row->plugin ); + return row->plugin->IsFootprintLibWritable( row->GetFullURI( true ) ); +} + + +void FP_LIB_TABLE::FootprintLibDelete( const wxString& aNickname ) +{ + const ROW* row = FindRow( aNickname ); + wxASSERT( (PLUGIN*) row->plugin ); + row->plugin->FootprintLibDelete( row->GetFullURI( true ), row->GetProperties() ); +} + + +void FP_LIB_TABLE::FootprintLibCreate( const wxString& aNickname ) +{ + const ROW* row = FindRow( aNickname ); + wxASSERT( (PLUGIN*) row->plugin ); + row->plugin->FootprintLibCreate( row->GetFullURI( true ), row->GetProperties() ); +} + + +const wxString FP_LIB_TABLE::GetDescription( const wxString& aNickname ) +{ + // use "no exception" form of find row: + const ROW* row = findRow( aNickname ); + if( row ) + return row->description; + else + return wxEmptyString; +} + + +void FP_LIB_TABLE::Parse( FP_LIB_TABLE_LEXER* in ) throw( IO_ERROR, PARSE_ERROR ) +{ + /* + (fp_lib_table + (lib (name NICKNAME)(descr DESCRIPTION)(type TYPE)(full_uri FULL_URI)(options OPTIONS)) + : + ) + + Elements after (name) are order independent. + */ + + T tok; + wxString errMsg; // to collect error messages + + // This table may be nested within a larger s-expression, or not. + // Allow for parser of that optional containing s-epression to have looked ahead. + if( in->CurTok() != T_fp_lib_table ) + { + in->NeedLEFT(); + if( ( tok = in->NextTok() ) != T_fp_lib_table ) + in->Expecting( T_fp_lib_table ); + } + + while( ( tok = in->NextTok() ) != T_RIGHT ) + { + ROW row; // reconstructed for each row in input stream. + + if( tok == T_EOF ) + in->Expecting( T_RIGHT ); + + if( tok != T_LEFT ) + in->Expecting( T_LEFT ); + + if( ( tok = in->NextTok() ) != T_lib ) + in->Expecting( T_lib ); + + // (name NICKNAME) + in->NeedLEFT(); + + if( ( tok = in->NextTok() ) != T_name ) + in->Expecting( T_name ); + + in->NeedSYMBOLorNUMBER(); + + row.SetNickName( in->FromUTF8() ); + + in->NeedRIGHT(); + + // After (name), remaining (lib) elements are order independent, and in + // some cases optional. + bool sawType = false; + bool sawOpts = false; + bool sawDesc = false; + bool sawUri = false; + + while( ( tok = in->NextTok() ) != T_RIGHT ) + { + if( tok == T_EOF ) + in->Unexpected( T_EOF ); + + if( tok != T_LEFT ) + in->Expecting( T_LEFT ); + + tok = in->NeedSYMBOLorNUMBER(); + + switch( tok ) + { + case T_uri: + if( sawUri ) + in->Duplicate( tok ); + sawUri = true; + in->NeedSYMBOLorNUMBER(); + // Saved path and file names use the Unix notation (separator = '/') + // However old files, and files edited by hand can use the windows + // separator. Force the unix notation. + // (It works on windows, and moreover, wxFileName and wxDir takes care to that + // on windows) + // moreover, URLs use the '/' as separator + { + wxString uri = in->FromUTF8(); + uri.Replace( '\\', '/' ); + row.SetFullURI( uri ); + } + break; + + case T_type: + if( sawType ) + in->Duplicate( tok ); + sawType = true; + in->NeedSYMBOLorNUMBER(); + row.SetType( in->FromUTF8() ); + break; + + case T_options: + if( sawOpts ) + in->Duplicate( tok ); + sawOpts = true; + in->NeedSYMBOLorNUMBER(); + row.SetOptions( in->FromUTF8() ); + break; + + case T_descr: + if( sawDesc ) + in->Duplicate( tok ); + sawDesc = true; + in->NeedSYMBOLorNUMBER(); + row.SetDescr( in->FromUTF8() ); + break; + + default: + in->Unexpected( tok ); + } + + in->NeedRIGHT(); + } + + if( !sawType ) + in->Expecting( T_type ); + + if( !sawUri ) + in->Expecting( T_uri ); + + // all nickNames within this table fragment must be unique, so we do not + // use doReplace in InsertRow(). (However a fallBack table can have a + // conflicting nickName and ours will supercede that one since in + // FindLib() we search this table before any fall back.) + if( !InsertRow( row ) ) + { + wxString msg = wxString::Format( + _( "'%s' is a duplicate footprint library nickName" ), + GetChars( row.nickName ) ); + if( !errMsg.IsEmpty() ) + errMsg << '\n'; + + errMsg << msg; + } + } + + if( !errMsg.IsEmpty() ) + THROW_IO_ERROR( errMsg ); +} + + +void FP_LIB_TABLE::Format( OUTPUTFORMATTER* out, int nestLevel ) const + throw( IO_ERROR, boost::interprocess::lock_exception ) +{ + out->Print( nestLevel, "(fp_lib_table\n" ); + + for( ROWS_CITER it = rows.begin(); it != rows.end(); ++it ) + it->Format( out, nestLevel+1 ); + + out->Print( nestLevel, ")\n" ); +} + + +void FP_LIB_TABLE::ROW::Format( OUTPUTFORMATTER* out, int nestLevel ) const + throw( IO_ERROR, boost::interprocess::lock_exception ) +{ + // In Kicad, we save path and file names using the Unix notation (separator = '/') + // So ensure separator is always '/' is saved URI string + wxString uri = GetFullURI(); + uri.Replace( '\\', '/' ); + + out->Print( nestLevel, "(lib (name %s)(type %s)(uri %s)(options %s)(descr %s))\n", + out->Quotew( GetNickName() ).c_str(), + out->Quotew( GetType() ).c_str(), + out->Quotew( uri ).c_str(), + out->Quotew( GetOptions() ).c_str(), + out->Quotew( GetDescr() ).c_str() + ); +} + +#define OPT_SEP '|' ///< options separator character + +PROPERTIES* FP_LIB_TABLE::ParseOptions( const std::string& aOptionsList ) +{ + if( aOptionsList.size() ) + { + const char* cp = &aOptionsList[0]; + const char* end = cp + aOptionsList.size(); + + PROPERTIES props; + std::string pair; + + // Parse all name=value pairs + while( cp < end ) + { + pair.clear(); + + // Skip leading white space. + while( cp < end && isspace( *cp ) ) + ++cp; + + // Find the end of pair/field + while( cp < end ) + { + if( *cp=='\\' && cp+1<end && cp[1]==OPT_SEP ) + { + ++cp; // skip the escape + pair += *cp++; // add the separator + } + else if( *cp==OPT_SEP ) + { + ++cp; // skip the separator + break; // process the pair + } + else + pair += *cp++; + } + + // stash the pair + if( pair.size() ) + { + // first equals sign separates 'name' and 'value'. + size_t eqNdx = pair.find( '=' ); + if( eqNdx != pair.npos ) + { + std::string name = pair.substr( 0, eqNdx ); + std::string value = pair.substr( eqNdx + 1 ); + props[name] = value; + } + else + props[pair] = ""; // property is present, but with no value. + } + } + + if( props.size() ) + return new PROPERTIES( props ); + } + return NULL; +} + + +UTF8 FP_LIB_TABLE::FormatOptions( const PROPERTIES* aProperties ) +{ + UTF8 ret; + + if( aProperties ) + { + for( PROPERTIES::const_iterator it = aProperties->begin(); it != aProperties->end(); ++it ) + { + const std::string& name = it->first; + + const UTF8& value = it->second; + + if( ret.size() ) + ret += OPT_SEP; + + ret += name; + + // the separation between name and value is '=' + if( value.size() ) + { + ret += '='; + + for( std::string::const_iterator si = value.begin(); si != value.end(); ++si ) + { + // escape any separator in the value. + if( *si == OPT_SEP ) + ret += '\\'; + + ret += *si; + } + } + } + } + + return ret; +} + + +std::vector<wxString> FP_LIB_TABLE::GetLogicalLibs() +{ + // Only return unique logical library names. Use std::set::insert() to + // quietly reject any duplicates, which can happen when encountering a duplicate + // nickname from one of the fall back table(s). + + std::set<wxString> unique; + std::vector<wxString> ret; + const FP_LIB_TABLE* cur = this; + + do + { + for( ROWS_CITER it = cur->rows.begin(); it!=cur->rows.end(); ++it ) + { + unique.insert( it->nickName ); + } + + } while( ( cur = cur->fallBack ) != 0 ); + + ret.reserve( unique.size() ); + + // DBG(printf( "%s: count:%zd\n", __func__, unique.size() );) + + // return a sorted, unique set of nicknames in a std::vector<wxString> to caller + for( std::set<wxString>::const_iterator it = unique.begin(); it!=unique.end(); ++it ) + { + //DBG(printf( " %s\n", TO_UTF8( *it ) );) + ret.push_back( *it ); + } + + return ret; +} + + +FP_LIB_TABLE::ROW* FP_LIB_TABLE::findRow( const wxString& aNickName ) const +{ + FP_LIB_TABLE* cur = (FP_LIB_TABLE*) this; + + do + { + cur->ensureIndex(); + + INDEX_CITER it = cur->nickIndex.find( aNickName ); + + if( it != cur->nickIndex.end() ) + { + return &cur->rows[it->second]; // found + } + + // not found, search fall back table(s), if any + } while( ( cur = cur->fallBack ) != 0 ); + + return 0; // not found +} + + +const FP_LIB_TABLE::ROW* FP_LIB_TABLE::FindRowByURI( const wxString& aURI ) +{ + FP_LIB_TABLE* cur = this; + + do + { + cur->ensureIndex(); + + for( unsigned i = 0; i < cur->rows.size(); i++ ) + { + wxString uri = cur->rows[i].GetFullURI( true ); + + if( wxFileName::GetPathSeparator() == wxChar( '\\' ) && uri.Find( wxChar( '/' ) ) >= 0 ) + uri.Replace( wxT( "/" ), wxT( "\\" ) ); + + if( (wxFileName::IsCaseSensitive() && uri == aURI) + || (!wxFileName::IsCaseSensitive() && uri.Upper() == aURI.Upper() ) ) + { + return &cur->rows[i]; // found + } + } + + // not found, search fall back table(s), if any + } while( ( cur = cur->fallBack ) != 0 ); + + return 0; // not found +} + + +bool FP_LIB_TABLE::InsertRow( const ROW& aRow, bool doReplace ) +{ + ensureIndex(); + + INDEX_CITER it = nickIndex.find( aRow.nickName ); + + if( it == nickIndex.end() ) + { + rows.push_back( aRow ); + nickIndex.insert( INDEX_VALUE( aRow.nickName, rows.size() - 1 ) ); + return true; + } + + if( doReplace ) + { + rows[it->second] = aRow; + return true; + } + + return false; +} + + +const FP_LIB_TABLE::ROW* FP_LIB_TABLE::FindRow( const wxString& aNickname ) + throw( IO_ERROR ) +{ + ROW* row = findRow( aNickname ); + + if( !row ) + { + wxString msg = wxString::Format( + _( "fp-lib-table files contain no lib with nickname '%s'" ), + GetChars( aNickname ) ); + + THROW_IO_ERROR( msg ); + } + + // We've been 'lazy' up until now, but it cannot be deferred any longer, + // instantiate a PLUGIN of the proper kind if it is not already in this ROW. + if( !row->plugin ) + row->setPlugin( IO_MGR::PluginFind( row->type ) ); + + return row; +} + + +// wxGetenv( wchar_t* ) is not re-entrant on linux. +// Put a lock on multithreaded use of wxGetenv( wchar_t* ), called from wxEpandEnvVars(), +// needed by bool ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxString* aNickname = NULL ); +#include <ki_mutex.h> + +const wxString FP_LIB_TABLE::ExpandSubstitutions( const wxString& aString ) +{ +// Duplicate code: the same is now in common.cpp, due to the fact it is used +// in many other places than FP_LIB_TABLE +#if 0 + static MUTEX getenv_mutex; + + MUTLOCK lock( getenv_mutex ); + + // We reserve the right to do this another way, by providing our own member + // function. + return wxExpandEnvVars( aString ); +#else + return ExpandEnvVarSubstitutions( aString ); +#endif +} + + +bool FP_LIB_TABLE::IsEmpty( bool aIncludeFallback ) +{ + if( !aIncludeFallback || !fallBack ) + return rows.empty(); + + return rows.empty() && fallBack->IsEmpty( true ); +} + + +MODULE* FP_LIB_TABLE::FootprintLoadWithOptionalNickname( const FPID& aFootprintId ) + throw( IO_ERROR, PARSE_ERROR, boost::interprocess::lock_exception ) +{ + wxString nickname = aFootprintId.GetLibNickname(); + wxString fpname = aFootprintId.GetFootprintName(); + + if( nickname.size() ) + { + return FootprintLoad( nickname, fpname ); + } + + // nickname is empty, sequentially search (alphabetically) all libs/nicks for first match: + else + { + std::vector<wxString> nicks = GetLogicalLibs(); + + // Search each library going through libraries alphabetically. + for( unsigned i = 0; i<nicks.size(); ++i ) + { + // FootprintLoad() returns NULL on not found, does not throw exception + // unless there's an IO_ERROR. + MODULE* ret = FootprintLoad( nicks[i], fpname ); + if( ret ) + return ret; + } + + return NULL; + } +} + + +const wxString FP_LIB_TABLE::GlobalPathEnvVariableName() +{ + return wxT( "KISYSMOD" ); +} + + +bool FP_LIB_TABLE::LoadGlobalTable( FP_LIB_TABLE& aTable ) + throw (IO_ERROR, PARSE_ERROR, boost::interprocess::lock_exception ) +{ + bool tableExists = true; + wxFileName fn = GetGlobalTableFileName(); + + if( !fn.FileExists() ) + { + tableExists = false; + + if( !fn.DirExists() && !fn.Mkdir( 0x777, wxPATH_MKDIR_FULL ) ) + { + THROW_IO_ERROR( wxString::Format( _( "Cannot create global library table path '%s'." ), + GetChars( fn.GetPath() ) ) ); + } + + // Attempt to copy the default global file table from the KiCad + // template folder to the user's home configuration path. + wxString fileName = Kiface().KifaceSearch().FindValidPath( global_tbl_name ); + + // The fallback is to create an empty global footprint table for the user to populate. + if( fileName.IsEmpty() || !::wxCopyFile( fileName, fn.GetFullPath(), false ) ) + { + FP_LIB_TABLE emptyTable; + + emptyTable.Save( fn.GetFullPath() ); + } + } + + aTable.Load( fn.GetFullPath() ); + + return tableExists; +} + + +wxString FP_LIB_TABLE::GetGlobalTableFileName() +{ + wxFileName fn; + + fn.SetPath( GetKicadConfigPath() ); + fn.SetName( global_tbl_name ); + + return fn.GetFullPath(); +} + +// prefer wxString filename so it can be seen in a debugger easier than wxFileName. + +void FP_LIB_TABLE::Load( const wxString& aFileName ) + throw( IO_ERROR ) +{ + // It's OK if footprint library tables are missing. + if( wxFileName::IsFileReadable( aFileName ) ) + { + FILE_LINE_READER reader( aFileName ); + FP_LIB_TABLE_LEXER lexer( &reader ); + + Parse( &lexer ); + } +} + + +void FP_LIB_TABLE::Save( const wxString& aFileName ) + const throw( IO_ERROR, boost::interprocess::lock_exception ) +{ + FILE_OUTPUTFORMATTER sf( aFileName ); + Format( &sf, 0 ); +} + diff --git a/common/fp_lib_table.keywords b/common/fp_lib_table.keywords new file mode 100644 index 0000000..27b1242 --- /dev/null +++ b/common/fp_lib_table.keywords @@ -0,0 +1,7 @@ +fp_lib_table +lib +name +type +uri +options +descr diff --git a/common/fpid.cpp b/common/fpid.cpp new file mode 100644 index 0000000..ba70c67 --- /dev/null +++ b/common/fpid.cpp @@ -0,0 +1,395 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2010 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2012 Wayne Stambaugh <stambaughw@verizon.net> + * Copyright (C) 2010 KiCad Developers, see change_log.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 <cstring> +#include <memory> +#include <wx/wx.h> // _() + +#include <macros.h> // TO_UTF8() +#include <fpid.h> +#include <kicad_string.h> + + +static inline bool isDigit( char c ) +{ + return c >= '0' && c <= '9'; +} + + +const char* EndsWithRev( const char* start, const char* tail, char separator ) +{ + bool sawDigit = false; + + while( tail > start && isDigit( *--tail ) ) + { + sawDigit = true; + } + + // if sawDigit, tail points to the 'v' here. + + if( sawDigit && tail-3 >= start ) + { + tail -= 3; + + if( tail[0]==separator && tail[1]=='r' && tail[2]=='e' && tail[3]=='v' ) + { + return tail+1; // omit separator, return "revN[N..]" + } + } + + return 0; +} + + +#if 0 // Not used +int RevCmp( const char* s1, const char* s2 ) +{ + int r = strncmp( s1, s2, 3 ); + + if( r || strlen(s1)<4 || strlen(s2)<4 ) + { + return r; + } + + int rnum1 = atoi( s1+3 ); + int rnum2 = atoi( s2+3 ); + + return -(rnum1 - rnum2); // swap the sign, higher revs first +} +#endif + +//----<Policy and field test functions>------------------------------------- + + +static inline int okLogical( const std::string& aField ) +{ + // std::string::npos is largest positive number, casting to int makes it -1. + // Returning that means success. + return int( aField.find_first_of( ":" ) ); +} + + +static int okRevision( const std::string& aField ) +{ + char rev[32]; // C string for speed + + if( aField.size() >= 4 ) + { + strcpy( rev, "x/" ); + strncat( rev, aField.c_str(), sizeof(rev)-strlen(rev)-1 ); + + if( EndsWithRev( rev, rev + strlen(rev), '/' ) == rev+2 ) + return -1; // success + } + + return 0; // first character position "is in error", is best we can do. +} + + +//----</Policy and field test functions>------------------------------------- + + +void FPID::clear() +{ + nickname.clear(); + footprint.clear(); + revision.clear(); +} + + +int FPID::Parse( const UTF8& aId ) +{ + clear(); + + const char* buffer = aId.c_str(); + const char* rev = EndsWithRev( buffer, buffer+aId.length(), '/' ); + size_t revNdx; + size_t partNdx; + int offset; + + //=====<revision>========================================= + // in a FPID like discret:R3/rev4 + if( rev ) + { + revNdx = rev - buffer; + + // no need to check revision, EndsWithRev did that. + revision = aId.substr( revNdx ); + --revNdx; // back up to omit the '/' which precedes the rev + } + else + { + revNdx = aId.size(); + } + + //=====<nickname>========================================== + if( ( partNdx = aId.find( ':' ) ) != aId.npos ) + { + offset = SetLibNickname( aId.substr( 0, partNdx ) ); + + if( offset > -1 ) + { + return offset; + } + + ++partNdx; // skip ':' + } + else + { + partNdx = 0; + } + + //=====<footprint name>==================================== + if( partNdx >= revNdx ) + return partNdx; // Error: no footprint name. + + // Be sure the footprint name is valid. + // Some chars can be found in board file (in old board files + // or converted files from an other EDA tool + std::string fpname = aId.substr( partNdx, revNdx-partNdx ); + ReplaceIllegalFileNameChars( &fpname, '_' ); + SetFootprintName( UTF8( fpname ) ); + + return -1; +} + + +FPID::FPID( const std::string& aId ) throw( PARSE_ERROR ) +{ + int offset = Parse( aId ); + + if( offset != -1 ) + { + THROW_PARSE_ERROR( _( "Illegal character found in FPID string" ), + wxString::FromUTF8( aId.c_str() ), + aId.c_str(), + 0, + offset ); + } +} + + +FPID::FPID( const wxString& aId ) throw( PARSE_ERROR ) +{ + UTF8 id = aId; + + int offset = Parse( id ); + + if( offset != -1 ) + { + THROW_PARSE_ERROR( _( "Illegal character found in FPID string" ), + aId, + id.c_str(), + 0, + offset ); + } +} + + +int FPID::SetLibNickname( const UTF8& aLogical ) +{ + int offset = okLogical( aLogical ); + + if( offset == -1 ) + { + nickname = aLogical; + } + + return offset; +} + + +int FPID::SetFootprintName( const UTF8& aFootprintName ) +{ + int separation = int( aFootprintName.find_first_of( "/" ) ); + + if( separation != -1 ) + { + footprint = aFootprintName.substr( 0, separation-1 ); + return separation; + } + else + { + footprint = aFootprintName; + } + + return -1; +} + + +int FPID::SetRevision( const UTF8& aRevision ) +{ + int offset = okRevision( aRevision ); + + if( offset == -1 ) + { + revision = aRevision; + } + + return offset; +} + + +UTF8 FPID::Format() const +{ + UTF8 ret; + + if( nickname.size() ) + { + ret += nickname; + ret += ':'; + } + + ret += footprint; + + if( revision.size() ) + { + ret += '/'; + ret += revision; + } + + return ret; +} + + +UTF8 FPID::GetFootprintNameAndRev() const +{ + UTF8 ret; + + if( revision.size() ) + { + ret += '/'; + ret += revision; + } + + return ret; +} + + +#if 0 // this is broken, it does not output aFootprintName for some reason + +UTF8 FPID::Format( const UTF8& aLogicalLib, const UTF8& aFootprintName, + const UTF8& aRevision ) + throw( PARSE_ERROR ) +{ + UTF8 ret; + int offset; + + if( aLogicalLib.size() ) + { + offset = okLogical( aLogicalLib ); + + if( offset != -1 ) + { + THROW_PARSE_ERROR( _( "Illegal character found in logical library name" ), + wxString::FromUTF8( aLogicalLib.c_str() ), + aLogicalLib.c_str(), + 0, + offset ); + } + + ret += aLogicalLib; + ret += ':'; + } + + if( aRevision.size() ) + { + offset = okRevision( aRevision ); + + if( offset != -1 ) + { + THROW_PARSE_ERROR( _( "Illegal character found in revision" ), + wxString::FromUTF8( aRevision.c_str() ), + aRevision.c_str(), + 0, + offset ); + } + + ret += '/'; + ret += aRevision; + } + + return ret; +} +#endif + + +int FPID::compare( const FPID& aFPID ) const +{ + // Don't bother comparing the same object. + if( this == &aFPID ) + return 0; + + int retv = nickname.compare( aFPID.nickname ); + + if( retv != 0 ) + return retv; + + retv = footprint.compare( aFPID.footprint ); + + if( retv != 0 ) + return retv; + + return revision.compare( aFPID.revision ); +} + + +#if 0 && defined(DEBUG) + +// build this with Debug CMAKE_BUILD_TYPE + +void FPID::Test() +{ + static const char* lpids[] = { + "smt:R_0805/rev0", + "mysmt:R_0805/rev2", + "device:AXIAL-0500", + }; + + for( unsigned i=0; i<sizeof(lpids)/sizeof(lpids[0]); ++i ) + { + // test some round tripping + + FPID lpid( lpids[i] ); // parse + + // format + printf( "input:'%s' full:'%s' nickname: %s footprint:'%s' rev:'%s'\n", + lpids[i], + lpid.Format().c_str(), + lpid.GetLibNickname().c_str(), + lpid.GetFootprintName().c_str(), + lpid.GetRevision().c_str() ); + } +} + + +int main( int argc, char** argv ) +{ + FPID::Test(); + + return 0; +} + +#endif diff --git a/common/gal/cairo/cairo_compositor.cpp b/common/gal/cairo/cairo_compositor.cpp new file mode 100644 index 0000000..f81cb5a --- /dev/null +++ b/common/gal/cairo/cairo_compositor.cpp @@ -0,0 +1,159 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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:O//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 + */ + +/** + * @file cairo_compositor.cpp + * @brief Class that handles multitarget rendering (ie. to different textures/surfaces) and + * later compositing into a single image (Cairo flavour). + */ + +#include <gal/cairo/cairo_compositor.h> +#include <wx/log.h> + +using namespace KIGFX; + +CAIRO_COMPOSITOR::CAIRO_COMPOSITOR( cairo_t** aMainContext ) : + m_current( 0 ), m_currentContext( aMainContext ), m_mainContext( *aMainContext ) +{ + // Do not have uninitialized members: + cairo_matrix_init_identity( &m_matrix ); + m_stride = 0; + m_bufferSize = 0; +} + + +CAIRO_COMPOSITOR::~CAIRO_COMPOSITOR() +{ + clean(); +} + + +void CAIRO_COMPOSITOR::Initialize() +{ + // Nothing has to be done +} + + +void CAIRO_COMPOSITOR::Resize( unsigned int aWidth, unsigned int aHeight ) +{ + clean(); + + assert( aWidth > 0 ); + assert( aHeight > 0 ); + + m_width = aWidth; + m_height = aHeight; + + m_stride = cairo_format_stride_for_width( CAIRO_FORMAT_ARGB32, m_width ); + m_bufferSize = m_stride * m_height; +} + + +unsigned int CAIRO_COMPOSITOR::CreateBuffer() +{ + // Pixel storage + BitmapPtr bitmap( new unsigned int[m_bufferSize] ); + + memset( bitmap.get(), 0x00, m_bufferSize * sizeof(int) ); + + // Create the Cairo surface + cairo_surface_t* surface = cairo_image_surface_create_for_data( + (unsigned char*) bitmap.get(), + CAIRO_FORMAT_ARGB32, m_width, + m_height, m_stride ); + cairo_t* context = cairo_create( surface ); +#ifdef __WXDEBUG__ + cairo_status_t status = cairo_status( context ); + wxASSERT_MSG( status == CAIRO_STATUS_SUCCESS, wxT( "Cairo context creation error" ) ); +#endif /* __WXDEBUG__ */ + + // Set default settings for the buffer + cairo_set_antialias( context, CAIRO_ANTIALIAS_SUBPIXEL ); + cairo_set_line_join( context, CAIRO_LINE_JOIN_ROUND ); + cairo_set_line_cap( context, CAIRO_LINE_CAP_ROUND ); + + // Use the same transformation matrix as the main context + cairo_get_matrix( m_mainContext, &m_matrix ); + cairo_set_matrix( context, &m_matrix ); + + // Store the new buffer + CAIRO_BUFFER buffer = { context, surface, bitmap }; + m_buffers.push_back( buffer ); + + return usedBuffers(); +} + + +void CAIRO_COMPOSITOR::SetBuffer( unsigned int aBufferHandle ) +{ + wxASSERT_MSG( aBufferHandle <= usedBuffers(), wxT( "Tried to use a not existing buffer" ) ); + + // Get currently used transformation matrix, so it can be applied to the new buffer + cairo_get_matrix( *m_currentContext, &m_matrix ); + + m_current = aBufferHandle - 1; + *m_currentContext = m_buffers[m_current].context; + + // Apply the current transformation matrix + cairo_set_matrix( *m_currentContext, &m_matrix ); +} + + +void CAIRO_COMPOSITOR::ClearBuffer() +{ + // Clear the pixel storage + memset( m_buffers[m_current].bitmap.get(), 0x00, m_bufferSize * sizeof(int) ); +} + + +void CAIRO_COMPOSITOR::DrawBuffer( unsigned int aBufferHandle ) +{ + wxASSERT_MSG( aBufferHandle <= usedBuffers(), wxT( "Tried to use a not existing buffer" ) ); + + // Reset the transformation matrix, so it is possible to composite images using + // screen coordinates instead of world coordinates + cairo_get_matrix( m_mainContext, &m_matrix ); + cairo_identity_matrix( m_mainContext ); + + // Draw the selected buffer contents + cairo_set_source_surface( m_mainContext, m_buffers[aBufferHandle - 1].surface, 0.0, 0.0 ); + cairo_paint( m_mainContext ); + + // Restore the transformation matrix + cairo_set_matrix( m_mainContext, &m_matrix ); +} + + +void CAIRO_COMPOSITOR::clean() +{ + CAIRO_BUFFERS::const_iterator it; + + for( it = m_buffers.begin(); it != m_buffers.end(); ++it ) + { + cairo_destroy( it->context ); + cairo_surface_destroy( it->surface ); + } + + m_buffers.clear(); +} diff --git a/common/gal/cairo/cairo_gal.cpp b/common/gal/cairo/cairo_gal.cpp new file mode 100644 index 0000000..b7ead4c --- /dev/null +++ b/common/gal/cairo/cairo_gal.cpp @@ -0,0 +1,1074 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * CAIRO_GAL - Graphics Abstraction Layer for Cairo + * + * 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 <wx/image.h> +#include <wx/log.h> + +#include <gal/cairo/cairo_gal.h> +#include <gal/cairo/cairo_compositor.h> +#include <gal/definitions.h> + +#include <limits> + +using namespace KIGFX; + + +const float CAIRO_GAL::LAYER_ALPHA = 0.8; + + +CAIRO_GAL::CAIRO_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener, + wxEvtHandler* aPaintListener, const wxString& aName ) : + wxWindow( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxEXPAND, aName ) +{ + parentWindow = aParent; + mouseListener = aMouseListener; + paintListener = aPaintListener; + + // Initialize the flags + isGrouping = false; + isInitialized = false; + isDeleteSavedPixels = false; + validCompositor = false; + groupCounter = 0; + + // Connecting the event handlers + Connect( wxEVT_PAINT, wxPaintEventHandler( CAIRO_GAL::onPaint ) ); + + // Mouse events are skipped to the parent + Connect( wxEVT_MOTION, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) ); + Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) ); + Connect( wxEVT_LEFT_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) ); + Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) ); + Connect( wxEVT_MIDDLE_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) ); + Connect( wxEVT_MIDDLE_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) ); + Connect( wxEVT_MIDDLE_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) ); + Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) ); + Connect( wxEVT_RIGHT_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) ); + Connect( wxEVT_RIGHT_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) ); + Connect( wxEVT_MOUSEWHEEL, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) ); +#if defined _WIN32 || defined _WIN64 + Connect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) ); +#endif + + SetSize( aParent->GetSize() ); + screenSize = VECTOR2I( aParent->GetSize() ); + + cursorPixels = NULL; + cursorPixelsSaved = NULL; + initCursor(); + + // Grid color settings are different in Cairo and OpenGL + SetGridColor( COLOR4D( 0.1, 0.1, 0.1, 0.8 ) ); + + // Allocate memory for pixel storage + allocateBitmaps(); + + initSurface(); +} + + +CAIRO_GAL::~CAIRO_GAL() +{ + deinitSurface(); + deleteBitmaps(); + + delete cursorPixels; + delete cursorPixelsSaved; + + ClearCache(); +} + + +void CAIRO_GAL::BeginDrawing() +{ + initSurface(); + + if( !validCompositor ) + setCompositor(); + + compositor->SetMainContext( context ); + compositor->SetBuffer( mainBuffer ); + + // Cairo grouping prevents display of overlapping items on the same layer in the lighter color + cairo_push_group( currentContext ); +} + + +void CAIRO_GAL::EndDrawing() +{ + // Force remaining objects to be drawn + Flush(); + + // Cairo grouping prevents display of overlapping items on the same layer in the lighter color + cairo_pop_group_to_source( currentContext ); + cairo_paint_with_alpha( currentContext, LAYER_ALPHA ); + + // Merge buffers on the screen + compositor->DrawBuffer( mainBuffer ); + compositor->DrawBuffer( overlayBuffer ); + + // This code was taken from the wxCairo example - it's not the most efficient one + // Here is a good place for optimizations + + // Now translate the raw context data from the format stored + // by cairo into a format understood by wxImage. + unsigned char* wxOutputPtr = wxOutput; + + for( size_t count = 0; count < bufferSize; count++ ) + { + unsigned int value = bitmapBuffer[count]; + *wxOutputPtr++ = ( value >> 16 ) & 0xff; // Red pixel + *wxOutputPtr++ = ( value >> 8 ) & 0xff; // Green pixel + *wxOutputPtr++ = value & 0xff; // Blue pixel + } + + wxImage img( screenSize.x, screenSize.y, (unsigned char*) wxOutput, true ); + wxBitmap bmp( img ); + wxClientDC client_dc( this ); + wxBufferedDC dc; + dc.Init( &client_dc, bmp ); + + // Now it is the time to blit the mouse cursor + blitCursor( dc ); + + deinitSurface(); +} + + +void CAIRO_GAL::DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) +{ + cairo_move_to( currentContext, aStartPoint.x, aStartPoint.y ); + cairo_line_to( currentContext, aEndPoint.x, aEndPoint.y ); + isElementAdded = true; +} + + +void CAIRO_GAL::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint, + double aWidth ) +{ + if( isFillEnabled ) + { + // Filled tracks mode + SetLineWidth( aWidth ); + + cairo_move_to( currentContext, (double) aStartPoint.x, (double) aStartPoint.y ); + cairo_line_to( currentContext, (double) aEndPoint.x, (double) aEndPoint.y ); + } + else + { + // Outline mode for tracks + VECTOR2D startEndVector = aEndPoint - aStartPoint; + double lineAngle = atan2( startEndVector.y, startEndVector.x ); + double lineLength = startEndVector.EuclideanNorm(); + + cairo_save( currentContext ); + + cairo_translate( currentContext, aStartPoint.x, aStartPoint.y ); + cairo_rotate( currentContext, lineAngle ); + + cairo_arc( currentContext, 0.0, 0.0, aWidth / 2.0, M_PI / 2.0, 3.0 * M_PI / 2.0 ); + cairo_arc( currentContext, lineLength, 0.0, aWidth / 2.0, -M_PI / 2.0, M_PI / 2.0 ); + + cairo_move_to( currentContext, 0.0, aWidth / 2.0 ); + cairo_line_to( currentContext, lineLength, aWidth / 2.0 ); + + cairo_move_to( currentContext, 0.0, -aWidth / 2.0 ); + cairo_line_to( currentContext, lineLength, -aWidth / 2.0 ); + + cairo_restore( currentContext ); + } + + isElementAdded = true; +} + + +void CAIRO_GAL::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius ) +{ + // A circle is drawn using an arc + cairo_new_sub_path( currentContext ); + cairo_arc( currentContext, aCenterPoint.x, aCenterPoint.y, aRadius, 0.0, 2 * M_PI ); + + isElementAdded = true; +} + + +void CAIRO_GAL::DrawArc( const VECTOR2D& aCenterPoint, double aRadius, double aStartAngle, + double aEndAngle ) +{ + SWAP( aStartAngle, >, aEndAngle ); + + cairo_new_sub_path( currentContext ); + cairo_arc( currentContext, aCenterPoint.x, aCenterPoint.y, aRadius, aStartAngle, aEndAngle ); + + if( isFillEnabled ) + { + VECTOR2D startPoint( cos( aStartAngle ) * aRadius + aCenterPoint.x, + sin( aStartAngle ) * aRadius + aCenterPoint.y ); + VECTOR2D endPoint( cos( aEndAngle ) * aRadius + aCenterPoint.x, + sin( aEndAngle ) * aRadius + aCenterPoint.y ); + + cairo_move_to( currentContext, aCenterPoint.x, aCenterPoint.y ); + cairo_line_to( currentContext, startPoint.x, startPoint.y ); + cairo_line_to( currentContext, endPoint.x, endPoint.y ); + cairo_close_path( currentContext ); + } + + isElementAdded = true; +} + + +void CAIRO_GAL::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) +{ + // Calculate the diagonal points + VECTOR2D diagonalPointA( aEndPoint.x, aStartPoint.y ); + VECTOR2D diagonalPointB( aStartPoint.x, aEndPoint.y ); + + // The path is composed from 4 segments + cairo_move_to( currentContext, aStartPoint.x, aStartPoint.y ); + cairo_line_to( currentContext, diagonalPointA.x, diagonalPointA.y ); + cairo_line_to( currentContext, aEndPoint.x, aEndPoint.y ); + cairo_line_to( currentContext, diagonalPointB.x, diagonalPointB.y ); + cairo_close_path( currentContext ); + + isElementAdded = true; +} + + +void CAIRO_GAL::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aControlPointA, + const VECTOR2D& aControlPointB, const VECTOR2D& aEndPoint ) +{ + cairo_move_to( currentContext, aStartPoint.x, aStartPoint.y ); + cairo_curve_to( currentContext, aControlPointA.x, aControlPointA.y, aControlPointB.x, + aControlPointB.y, aEndPoint.x, aEndPoint.y ); + cairo_line_to( currentContext, aEndPoint.x, aEndPoint.y ); + + isElementAdded = true; +} + + +void CAIRO_GAL::ResizeScreen( int aWidth, int aHeight ) +{ + screenSize = VECTOR2I( aWidth, aHeight ); + + // Recreate the bitmaps + deleteBitmaps(); + allocateBitmaps(); + + if( validCompositor ) + compositor->Resize( aWidth, aHeight ); + + validCompositor = false; + + SetSize( wxSize( aWidth, aHeight ) ); +} + + +bool CAIRO_GAL::Show( bool aShow ) +{ + bool s = wxWindow::Show( aShow ); + + if( aShow ) + wxWindow::Raise(); + + return s; +} + + +void CAIRO_GAL::Flush() +{ + storePath(); +} + + +void CAIRO_GAL::ClearScreen( const COLOR4D& aColor ) +{ + backgroundColor = aColor; + cairo_set_source_rgb( currentContext, aColor.r, aColor.g, aColor.b ); + cairo_rectangle( currentContext, 0.0, 0.0, screenSize.x, screenSize.y ); + cairo_fill( currentContext ); +} + + +void CAIRO_GAL::SetIsFill( bool aIsFillEnabled ) +{ + storePath(); + isFillEnabled = aIsFillEnabled; + + if( isGrouping ) + { + GROUP_ELEMENT groupElement; + groupElement.command = CMD_SET_FILL; + groupElement.boolArgument = aIsFillEnabled; + currentGroup->push_back( groupElement ); + } +} + + +void CAIRO_GAL::SetIsStroke( bool aIsStrokeEnabled ) +{ + storePath(); + isStrokeEnabled = aIsStrokeEnabled; + + if( isGrouping ) + { + GROUP_ELEMENT groupElement; + groupElement.command = CMD_SET_STROKE; + groupElement.boolArgument = aIsStrokeEnabled; + currentGroup->push_back( groupElement ); + } +} + + +void CAIRO_GAL::SetStrokeColor( const COLOR4D& aColor ) +{ + storePath(); + strokeColor = aColor; + + if( isGrouping ) + { + GROUP_ELEMENT groupElement; + groupElement.command = CMD_SET_STROKECOLOR; + groupElement.arguments[0] = strokeColor.r; + groupElement.arguments[1] = strokeColor.g; + groupElement.arguments[2] = strokeColor.b; + groupElement.arguments[3] = strokeColor.a; + currentGroup->push_back( groupElement ); + } +} + + +void CAIRO_GAL::SetFillColor( const COLOR4D& aColor ) +{ + storePath(); + fillColor = aColor; + + if( isGrouping ) + { + GROUP_ELEMENT groupElement; + groupElement.command = CMD_SET_FILLCOLOR; + groupElement.arguments[0] = fillColor.r; + groupElement.arguments[1] = fillColor.g; + groupElement.arguments[2] = fillColor.b; + groupElement.arguments[3] = fillColor.a; + currentGroup->push_back( groupElement ); + } +} + + +void CAIRO_GAL::SetLineWidth( double aLineWidth ) +{ + storePath(); + + lineWidth = aLineWidth; + + if( isGrouping ) + { + GROUP_ELEMENT groupElement; + groupElement.command = CMD_SET_LINE_WIDTH; + groupElement.arguments[0] = aLineWidth; + currentGroup->push_back( groupElement ); + } + else + { + // Make lines appear at least 1 pixel wide, no matter of zoom + double x = 1.0, y = 1.0; + cairo_device_to_user_distance( currentContext, &x, &y ); + double minWidth = std::min( fabs( x ), fabs( y ) ); + cairo_set_line_width( currentContext, std::max( aLineWidth, minWidth ) ); + } +} + + +void CAIRO_GAL::SetLayerDepth( double aLayerDepth ) +{ + super::SetLayerDepth( aLayerDepth ); + + if( isInitialized ) + { + storePath(); + + cairo_pop_group_to_source( currentContext ); + cairo_paint_with_alpha( currentContext, LAYER_ALPHA ); + + cairo_push_group( currentContext ); + } +} + + +void CAIRO_GAL::Transform( const MATRIX3x3D& aTransformation ) +{ + cairo_matrix_t cairoTransformation; + + cairo_matrix_init( &cairoTransformation, + aTransformation.m_data[0][0], + aTransformation.m_data[1][0], + aTransformation.m_data[0][1], + aTransformation.m_data[1][1], + aTransformation.m_data[0][2], + aTransformation.m_data[1][2] ); + + cairo_transform( currentContext, &cairoTransformation ); +} + + +void CAIRO_GAL::Rotate( double aAngle ) +{ + storePath(); + + if( isGrouping ) + { + GROUP_ELEMENT groupElement; + groupElement.command = CMD_ROTATE; + groupElement.arguments[0] = aAngle; + currentGroup->push_back( groupElement ); + } + else + { + cairo_rotate( currentContext, aAngle ); + } +} + + +void CAIRO_GAL::Translate( const VECTOR2D& aTranslation ) +{ + storePath(); + + if( isGrouping ) + { + GROUP_ELEMENT groupElement; + groupElement.command = CMD_TRANSLATE; + groupElement.arguments[0] = aTranslation.x; + groupElement.arguments[1] = aTranslation.y; + currentGroup->push_back( groupElement ); + } + else + { + cairo_translate( currentContext, aTranslation.x, aTranslation.y ); + } +} + + +void CAIRO_GAL::Scale( const VECTOR2D& aScale ) +{ + storePath(); + + if( isGrouping ) + { + GROUP_ELEMENT groupElement; + groupElement.command = CMD_SCALE; + groupElement.arguments[0] = aScale.x; + groupElement.arguments[1] = aScale.y; + currentGroup->push_back( groupElement ); + } + else + { + cairo_scale( currentContext, aScale.x, aScale.y ); + } +} + + +void CAIRO_GAL::Save() +{ + storePath(); + + if( isGrouping ) + { + GROUP_ELEMENT groupElement; + groupElement.command = CMD_SAVE; + currentGroup->push_back( groupElement ); + } + else + { + cairo_save( currentContext ); + } +} + + +void CAIRO_GAL::Restore() +{ + storePath(); + + if( isGrouping ) + { + GROUP_ELEMENT groupElement; + groupElement.command = CMD_RESTORE; + currentGroup->push_back( groupElement ); + } + else + { + cairo_restore( currentContext ); + } +} + + +int CAIRO_GAL::BeginGroup() +{ + initSurface(); + + // If the grouping is started: the actual path is stored in the group, when + // a attribute was changed or when grouping stops with the end group method. + storePath(); + + GROUP group; + int groupNumber = getNewGroupNumber(); + groups.insert( std::make_pair( groupNumber, group ) ); + currentGroup = &groups[groupNumber]; + isGrouping = true; + + return groupNumber; +} + + +void CAIRO_GAL::EndGroup() +{ + storePath(); + isGrouping = false; + + deinitSurface(); +} + + +void CAIRO_GAL::DrawGroup( int aGroupNumber ) +{ + // This method implements a small Virtual Machine - all stored commands + // are executed; nested calling is also possible + + storePath(); + + for( GROUP::iterator it = groups[aGroupNumber].begin(); + it != groups[aGroupNumber].end(); ++it ) + { + switch( it->command ) + { + case CMD_SET_FILL: + isFillEnabled = it->boolArgument; + break; + + case CMD_SET_STROKE: + isStrokeEnabled = it->boolArgument; + break; + + case CMD_SET_FILLCOLOR: + fillColor = COLOR4D( it->arguments[0], it->arguments[1], it->arguments[2], + it->arguments[3] ); + break; + + case CMD_SET_STROKECOLOR: + strokeColor = COLOR4D( it->arguments[0], it->arguments[1], it->arguments[2], + it->arguments[3] ); + break; + + case CMD_SET_LINE_WIDTH: + { + // Make lines appear at least 1 pixel wide, no matter of zoom + double x = 1.0, y = 1.0; + cairo_device_to_user_distance( currentContext, &x, &y ); + double minWidth = std::min( fabs( x ), fabs( y ) ); + cairo_set_line_width( currentContext, std::max( it->arguments[0], minWidth ) ); + } + break; + + + case CMD_STROKE_PATH: + cairo_set_source_rgb( currentContext, strokeColor.r, strokeColor.g, strokeColor.b ); + cairo_append_path( currentContext, it->cairoPath ); + cairo_stroke( currentContext ); + break; + + case CMD_FILL_PATH: + cairo_set_source_rgb( currentContext, fillColor.r, fillColor.g, fillColor.b ); + cairo_append_path( currentContext, it->cairoPath ); + cairo_fill( currentContext ); + break; + + case CMD_TRANSFORM: + cairo_matrix_t matrix; + cairo_matrix_init( &matrix, it->arguments[0], it->arguments[1], it->arguments[2], + it->arguments[3], it->arguments[4], it->arguments[5] ); + cairo_transform( currentContext, &matrix ); + break; + + case CMD_ROTATE: + cairo_rotate( currentContext, it->arguments[0] ); + break; + + case CMD_TRANSLATE: + cairo_translate( currentContext, it->arguments[0], it->arguments[1] ); + break; + + case CMD_SCALE: + cairo_scale( currentContext, it->arguments[0], it->arguments[1] ); + break; + + case CMD_SAVE: + cairo_save( currentContext ); + break; + + case CMD_RESTORE: + cairo_restore( currentContext ); + break; + + case CMD_CALL_GROUP: + DrawGroup( it->intArgument ); + break; + } + } +} + + +void CAIRO_GAL::ChangeGroupColor( int aGroupNumber, const COLOR4D& aNewColor ) +{ + storePath(); + + for( GROUP::iterator it = groups[aGroupNumber].begin(); + it != groups[aGroupNumber].end(); ++it ) + { + if( it->command == CMD_SET_FILLCOLOR || it->command == CMD_SET_STROKECOLOR ) + { + it->arguments[0] = aNewColor.r; + it->arguments[1] = aNewColor.g; + it->arguments[2] = aNewColor.b; + it->arguments[3] = aNewColor.a; + } + } +} + + +void CAIRO_GAL::ChangeGroupDepth( int aGroupNumber, int aDepth ) +{ + // Cairo does not have any possibilities to change the depth coordinate of stored items, + // it depends only on the order of drawing +} + + +void CAIRO_GAL::DeleteGroup( int aGroupNumber ) +{ + storePath(); + + // Delete the Cairo paths + std::deque<GROUP_ELEMENT>::iterator it, end; + + for( it = groups[aGroupNumber].begin(), end = groups[aGroupNumber].end(); it != end; ++it ) + { + if( it->command == CMD_FILL_PATH || it->command == CMD_STROKE_PATH ) + { + cairo_path_destroy( it->cairoPath ); + } + } + + // Delete the group + groups.erase( aGroupNumber ); +} + + +void CAIRO_GAL::ClearCache() +{ + for( int i = groups.size() - 1; i >= 0; --i ) + { + DeleteGroup( i ); + } +} + + +void CAIRO_GAL::SaveScreen() +{ + // Copy the current bitmap to the backup buffer + int offset = 0; + + for( int j = 0; j < screenSize.y; j++ ) + { + for( int i = 0; i < stride; i++ ) + { + bitmapBufferBackup[offset + i] = bitmapBuffer[offset + i]; + offset += stride; + } + } +} + + +void CAIRO_GAL::RestoreScreen() +{ + int offset = 0; + + for( int j = 0; j < screenSize.y; j++ ) + { + for( int i = 0; i < stride; i++ ) + { + bitmapBuffer[offset + i] = bitmapBufferBackup[offset + i]; + offset += stride; + } + } +} + + +void CAIRO_GAL::SetTarget( RENDER_TARGET aTarget ) +{ + // If the compositor is not set, that means that there is a recaching process going on + // and we do not need the compositor now + if( !validCompositor ) + return; + + // Cairo grouping prevents display of overlapping items on the same layer in the lighter color + if( isInitialized ) + { + storePath(); + + cairo_pop_group_to_source( currentContext ); + cairo_paint_with_alpha( currentContext, LAYER_ALPHA ); + } + + switch( aTarget ) + { + default: + case TARGET_CACHED: + case TARGET_NONCACHED: + compositor->SetBuffer( mainBuffer ); + break; + + case TARGET_OVERLAY: + compositor->SetBuffer( overlayBuffer ); + break; + } + + if( isInitialized ) + cairo_push_group( currentContext ); + + currentTarget = aTarget; +} + + +RENDER_TARGET CAIRO_GAL::GetTarget() const +{ + return currentTarget; +} + + +void CAIRO_GAL::ClearTarget( RENDER_TARGET aTarget ) +{ + // Save the current state + unsigned int currentBuffer = compositor->GetBuffer(); + + switch( aTarget ) + { + // Cached and noncached items are rendered to the same buffer + default: + case TARGET_CACHED: + case TARGET_NONCACHED: + compositor->SetBuffer( mainBuffer ); + break; + + case TARGET_OVERLAY: + compositor->SetBuffer( overlayBuffer ); + break; + } + + compositor->ClearBuffer(); + + // Restore the previous state + compositor->SetBuffer( currentBuffer ); +} + + +void CAIRO_GAL::SetCursorSize( unsigned int aCursorSize ) +{ + GAL::SetCursorSize( aCursorSize ); + initCursor(); +} + + +void CAIRO_GAL::DrawCursor( const VECTOR2D& aCursorPosition ) +{ + // Now we should only store the position of the mouse cursor + // The real drawing routines are in blitCursor() + cursorPosition = VECTOR2D( aCursorPosition.x - cursorSize / 2, + aCursorPosition.y - cursorSize / 2 ); +} + + +void CAIRO_GAL::drawGridLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) +{ + cairo_move_to( currentContext, aStartPoint.x, aStartPoint.y ); + cairo_line_to( currentContext, aEndPoint.x, aEndPoint.y ); + cairo_set_source_rgb( currentContext, gridColor.r, gridColor.g, gridColor.b ); + cairo_stroke( currentContext ); +} + + +void CAIRO_GAL::storePath() +{ + if( isElementAdded ) + { + isElementAdded = false; + + if( !isGrouping ) + { + if( isFillEnabled ) + { + cairo_set_source_rgb( currentContext, fillColor.r, fillColor.g, fillColor.b ); + cairo_fill_preserve( currentContext ); + } + + if( isStrokeEnabled ) + { + cairo_set_source_rgb( currentContext, strokeColor.r, strokeColor.g, + strokeColor.b ); + cairo_stroke_preserve( currentContext ); + } + } + else + { + // Copy the actual path, append it to the global path list + // then check, if the path needs to be stroked/filled and + // add this command to the group list; + if( isStrokeEnabled ) + { + GROUP_ELEMENT groupElement; + groupElement.cairoPath = cairo_copy_path( currentContext ); + groupElement.command = CMD_STROKE_PATH; + currentGroup->push_back( groupElement ); + } + + if( isFillEnabled ) + { + GROUP_ELEMENT groupElement; + groupElement.cairoPath = cairo_copy_path( currentContext ); + groupElement.command = CMD_FILL_PATH; + currentGroup->push_back( groupElement ); + } + } + + cairo_new_path( currentContext ); + } +} + + +void CAIRO_GAL::onPaint( wxPaintEvent& WXUNUSED( aEvent ) ) +{ + PostPaint(); +} + + +void CAIRO_GAL::skipMouseEvent( wxMouseEvent& aEvent ) +{ + // Post the mouse event to the event listener registered in constructor, if any + if( mouseListener ) + wxPostEvent( mouseListener, aEvent ); +} + + +void CAIRO_GAL::initCursor() +{ + if( cursorPixels ) + delete cursorPixels; + + if( cursorPixelsSaved ) + delete cursorPixelsSaved; + + cursorPixels = new wxBitmap( cursorSize, cursorSize ); + cursorPixelsSaved = new wxBitmap( cursorSize, cursorSize ); + + wxMemoryDC cursorShape( *cursorPixels ); + + cursorShape.SetBackground( *wxTRANSPARENT_BRUSH ); + wxColour color( cursorColor.r * cursorColor.a * 255, cursorColor.g * cursorColor.a * 255, + cursorColor.b * cursorColor.a * 255, 255 ); + wxPen pen = wxPen( color ); + cursorShape.SetPen( pen ); + cursorShape.Clear(); + + cursorShape.DrawLine( 0, cursorSize / 2, cursorSize, cursorSize / 2 ); + cursorShape.DrawLine( cursorSize / 2, 0, cursorSize / 2, cursorSize ); +} + + +void CAIRO_GAL::blitCursor( wxBufferedDC& clientDC ) +{ + if( !isCursorEnabled ) + return; + + wxMemoryDC cursorSave( *cursorPixelsSaved ); + wxMemoryDC cursorShape( *cursorPixels ); + + if( !isDeleteSavedPixels ) + { + // Restore pixels that were overpainted by the previous cursor + clientDC.Blit( savedCursorPosition.x, savedCursorPosition.y, + cursorSize, cursorSize, &cursorSave, 0, 0 ); + } + else + { + isDeleteSavedPixels = false; + } + + // Store pixels that are going to be overpainted + VECTOR2D cursorScreen = ToScreen( cursorPosition ) - cursorSize / 2.0f; + cursorSave.Blit( 0, 0, cursorSize, cursorSize, &clientDC, cursorScreen.x, cursorScreen.y ); + + // Draw the cursor + clientDC.Blit( cursorScreen.x, cursorScreen.y, cursorSize, cursorSize, + &cursorShape, 0, 0, wxOR ); + + savedCursorPosition.x = (wxCoord) cursorScreen.x; + savedCursorPosition.y = (wxCoord) cursorScreen.y; +} + + +void CAIRO_GAL::allocateBitmaps() +{ + // Create buffer, use the system independent Cairo context backend + stride = cairo_format_stride_for_width( GAL_FORMAT, screenSize.x ); + bufferSize = stride * screenSize.y; + + bitmapBuffer = new unsigned int[bufferSize]; + bitmapBufferBackup = new unsigned int[bufferSize]; + wxOutput = new unsigned char[bufferSize * 3]; +} + + +void CAIRO_GAL::deleteBitmaps() +{ + delete[] bitmapBuffer; + delete[] bitmapBufferBackup; + delete[] wxOutput; +} + + +void CAIRO_GAL::initSurface() +{ + if( isInitialized ) + return; + + // Create the Cairo surface + surface = cairo_image_surface_create_for_data( (unsigned char*) bitmapBuffer, GAL_FORMAT, + screenSize.x, screenSize.y, stride ); + context = cairo_create( surface ); +#ifdef __WXDEBUG__ + cairo_status_t status = cairo_status( context ); + wxASSERT_MSG( status == CAIRO_STATUS_SUCCESS, wxT( "Cairo context creation error" ) ); +#endif /* __WXDEBUG__ */ + currentContext = context; + + cairo_set_antialias( context, CAIRO_ANTIALIAS_SUBPIXEL ); + + // Clear the screen + ClearScreen( backgroundColor ); + + // Compute the world <-> screen transformations + ComputeWorldScreenMatrix(); + + cairo_matrix_init( &cairoWorldScreenMatrix, worldScreenMatrix.m_data[0][0], + worldScreenMatrix.m_data[1][0], worldScreenMatrix.m_data[0][1], + worldScreenMatrix.m_data[1][1], worldScreenMatrix.m_data[0][2], + worldScreenMatrix.m_data[1][2] ); + + cairo_set_matrix( context, &cairoWorldScreenMatrix ); + + // Start drawing with a new path + cairo_new_path( context ); + isElementAdded = true; + + cairo_set_line_join( context, CAIRO_LINE_JOIN_ROUND ); + cairo_set_line_cap( context, CAIRO_LINE_CAP_ROUND ); + + lineWidth = 0; + + isDeleteSavedPixels = true; + isInitialized = true; +} + + +void CAIRO_GAL::deinitSurface() +{ + if( !isInitialized ) + return; + + // Destroy Cairo objects + cairo_destroy( context ); + cairo_surface_destroy( surface ); + + isInitialized = false; +} + + +void CAIRO_GAL::setCompositor() +{ + // Recreate the compositor with the new Cairo context + compositor.reset( new CAIRO_COMPOSITOR( ¤tContext ) ); + compositor->Resize( screenSize.x, screenSize.y ); + + // Prepare buffers + mainBuffer = compositor->CreateBuffer(); + overlayBuffer = compositor->CreateBuffer(); + + validCompositor = true; +} + + +void CAIRO_GAL::drawPoly( const std::deque<VECTOR2D>& aPointList ) +{ + // Iterate over the point list and draw the segments + std::deque<VECTOR2D>::const_iterator it = aPointList.begin(); + + cairo_move_to( currentContext, it->x, it->y ); + + for( ++it; it != aPointList.end(); ++it ) + { + cairo_line_to( currentContext, it->x, it->y ); + } + + isElementAdded = true; +} + + +void CAIRO_GAL::drawPoly( const VECTOR2D aPointList[], int aListSize ) +{ + // Iterate over the point list and draw the segments + const VECTOR2D* ptr = aPointList; + + cairo_move_to( currentContext, ptr->x, ptr->y ); + + for( int i = 0; i < aListSize; ++i ) + { + ++ptr; + cairo_line_to( currentContext, ptr->x, ptr->y ); + } + + isElementAdded = true; +} + + +unsigned int CAIRO_GAL::getNewGroupNumber() +{ + wxASSERT_MSG( groups.size() < std::numeric_limits<unsigned int>::max(), + wxT( "There are no free slots to store a group" ) ); + + while( groups.find( groupCounter ) != groups.end() ) + { + groupCounter++; + } + + return groupCounter++; +} diff --git a/common/gal/color4d.cpp b/common/gal/color4d.cpp new file mode 100644 index 0000000..3b509d3 --- /dev/null +++ b/common/gal/color4d.cpp @@ -0,0 +1,179 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * Color class + * + * 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 <gal/color4d.h> + +using namespace KIGFX; + +COLOR4D::COLOR4D( EDA_COLOR_T aColor ) +{ + r = g_ColorRefs[aColor].m_Red / 255.0; + g = g_ColorRefs[aColor].m_Green / 255.0; + b = g_ColorRefs[aColor].m_Blue / 255.0; + a = 1.0; +} + + +#ifdef WX_COMPATIBILITY + COLOR4D::COLOR4D( const wxColour& aColor ) + { + r = aColor.Red(); + g = aColor.Green(); + b = aColor.Blue(); + a = aColor.Alpha(); + } +#endif + + +const bool COLOR4D::operator==( const COLOR4D& aColor ) +{ + return a == aColor.a && r == aColor.r && g == aColor.g && b == aColor.b; +} + + +const bool COLOR4D::operator!=( const COLOR4D& aColor ) +{ + return a != aColor.a || r != aColor.r || g != aColor.g || b != aColor.b; +} + + +void COLOR4D::ToHSV( double& aOutH, double& aOutS, double& aOutV ) const +{ + double min, max, delta; + + min = r < g ? r : g; + min = min < b ? min : b; + + max = r > g ? r : g; + max = max > b ? max : b; + + aOutV = max; // v + delta = max - min; + + if( max > 0.0 ) + { + aOutS = ( delta / max ); // s + } + else + { + // r = g = b = 0 // s = 0, v is undefined + aOutS = 0.0; + aOutH = NAN; // its now undefined + return; + } + + if( r >= max ) // > is bogus, just keeps compiler happy + aOutH = ( g - b ) / delta; // between yellow & magenta + else if( g >= max ) + aOutH = 2.0 + ( b - r ) / delta; // between cyan & yellow + else + aOutH = 4.0 + ( r - g ) / delta; // between magenta & cyan + + aOutH *= 60.0; // degrees + + if( aOutH < 0.0 ) + aOutH += 360.0; +} + + +void COLOR4D::FromHSV( double aInH, double aInS, double aInV ) +{ + double hh, p, q, t, ff; + long i; + + if( aInS <= 0.0 ) // < is bogus, just shuts up warnings + { + r = aInV; + g = aInV; + b = aInV; + return; + } + + hh = aInH; + + if( hh >= 360.0 ) + hh = 0.0; + + hh /= 60.0; + + i = (long) hh; + ff = hh - i; + + p = aInV * ( 1.0 - aInS ); + q = aInV * ( 1.0 - ( aInS * ff ) ); + t = aInV * ( 1.0 - ( aInS * ( 1.0 - ff ) ) ); + + switch( i ) + { + case 0: + r = aInV; + g = t; + b = p; + break; + + case 1: + r = q; + g = aInV; + b = p; + break; + + case 2: + r = p; + g = aInV; + b = t; + break; + + case 3: + r = p; + g = q; + b = aInV; + break; + + case 4: + r = t; + g = p; + b = aInV; + break; + + case 5: + default: + r = aInV; + g = p; + b = q; + break; + } +} + + +COLOR4D& COLOR4D::Saturate( double aFactor ) +{ + double h, s, v; + + ToHSV( h, s, v ); + FromHSV( h, aFactor, 1.0 ); + + return *this; +} diff --git a/common/gal/graphics_abstraction_layer.cpp b/common/gal/graphics_abstraction_layer.cpp new file mode 100644 index 0000000..d9e9eac --- /dev/null +++ b/common/gal/graphics_abstraction_layer.cpp @@ -0,0 +1,240 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * Graphics Abstraction Layer (GAL) - base class + * + * 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 <wx/log.h> + +#include <gal/graphics_abstraction_layer.h> +#include <gal/definitions.h> + +using namespace KIGFX; + + +const double GAL::METRIC_UNIT_LENGTH = 1e9; + + +GAL::GAL() : + strokeFont( this ) +{ + // Set the default values for the internal variables + SetIsFill( false ); + SetIsStroke( true ); + SetFillColor( COLOR4D( 0.0, 0.0, 0.0, 0.0 ) ); + SetStrokeColor( COLOR4D( 1.0, 1.0, 1.0, 1.0 ) ); + SetLookAtPoint( VECTOR2D( 0, 0 ) ); + SetZoomFactor( 1.0 ); + SetWorldUnitLength( 1.0 / METRIC_UNIT_LENGTH * 2.54 ); // 1 inch in nanometers + SetScreenDPI( 106 ); // Display resolution setting + SetDepthRange( VECTOR2D( GAL::MIN_DEPTH, GAL::MAX_DEPTH ) ); + SetLayerDepth( 0.0 ); + SetFlip( false, false ); + SetLineWidth( 1.0 ); + ComputeWorldScale(); + + // Set grid defaults + SetGridVisibility( true ); + SetGridStyle( GRID_STYLE_LINES ); + SetGridDrawThreshold( 10 ); + SetCoarseGrid( 10 ); + SetGridLineWidth( 0.5 ); + + // Initialize the cursor shape + SetCursorColor( COLOR4D( 1.0, 1.0, 1.0, 1.0 ) ); + SetCursorSize( 80 ); + SetCursorEnabled( false ); + + strokeFont.LoadNewStrokeFont( newstroke_font, newstroke_font_bufsize ); +} + + +GAL::~GAL() +{ +} + + +void GAL::SetTextAttributes( const EDA_TEXT* aText ) +{ + strokeFont.SetGlyphSize( VECTOR2D( aText->GetSize() ) ); + strokeFont.SetHorizontalJustify( aText->GetHorizJustify() ); + strokeFont.SetVerticalJustify( aText->GetVertJustify() ); + strokeFont.SetBold( aText->IsBold() ); + strokeFont.SetItalic( aText->IsItalic() ); + strokeFont.SetMirrored( aText->IsMirrored() ); +} + + +void GAL::ComputeWorldScreenMatrix() +{ + ComputeWorldScale(); + + worldScreenMatrix.SetIdentity(); + + MATRIX3x3D translation; + translation.SetIdentity(); + translation.SetTranslation( 0.5 * VECTOR2D( screenSize ) ); + + MATRIX3x3D scale; + scale.SetIdentity(); + scale.SetScale( VECTOR2D( worldScale, worldScale ) ); + + MATRIX3x3D flip; + flip.SetIdentity(); + flip.SetScale( VECTOR2D( flipX, flipY ) ); + + MATRIX3x3D lookat; + lookat.SetIdentity(); + lookat.SetTranslation( -lookAtPoint ); + + worldScreenMatrix = translation * flip * scale * lookat * worldScreenMatrix; + screenWorldMatrix = worldScreenMatrix.Inverse(); +} + + +void GAL::DrawGrid() +{ + if( !gridVisibility ) + return; + + SetTarget( TARGET_NONCACHED ); + + // Draw the grid + // For the drawing the start points, end points and increments have + // to be calculated in world coordinates + VECTOR2D worldStartPoint = screenWorldMatrix * VECTOR2D( 0.0, 0.0 ); + VECTOR2D worldEndPoint = screenWorldMatrix * VECTOR2D( screenSize ); + + int gridScreenSizeDense = KiROUND( gridSize.x * worldScale ); + int gridScreenSizeCoarse = KiROUND( gridSize.x * static_cast<double>( gridTick ) * worldScale ); + + // Compute the line marker or point radius of the grid + double marker = 2.0 * gridLineWidth / worldScale; + double doubleMarker = 2.0 * marker; + + // Check if the grid would not be too dense + if( std::max( gridScreenSizeDense, gridScreenSizeCoarse ) > gridDrawThreshold ) + { + // Compute grid variables + int gridStartX = KiROUND( worldStartPoint.x / gridSize.x ); + int gridEndX = KiROUND( worldEndPoint.x / gridSize.x ); + int gridStartY = KiROUND( worldStartPoint.y / gridSize.y ); + int gridEndY = KiROUND( worldEndPoint.y / gridSize.y ); + + assert( gridEndX >= gridStartX ); + assert( gridEndY >= gridStartY ); + + // Correct the index, else some lines are not correctly painted + gridStartX -= std::abs( gridOrigin.x / gridSize.x ) + 1; + gridStartY -= std::abs( gridOrigin.y / gridSize.y ) + 1; + gridEndX += std::abs( gridOrigin.x / gridSize.x ) + 1; + gridEndY += std::abs( gridOrigin.y / gridSize.y ) + 1; + + // Draw the grid behind all other layers + SetLayerDepth( depthRange.y * 0.75 ); + + if( gridStyle == GRID_STYLE_LINES ) + { + SetIsFill( false ); + SetIsStroke( true ); + SetStrokeColor( gridColor ); + + // Now draw the grid, every coarse grid line gets the double width + + // Vertical lines + for( int j = gridStartY; j < gridEndY; j += 1 ) + { + if( j % gridTick == 0 && gridScreenSizeDense > gridDrawThreshold ) + SetLineWidth( doubleMarker ); + else + SetLineWidth( marker ); + + if( ( j % gridTick == 0 && gridScreenSizeCoarse > gridDrawThreshold ) + || gridScreenSizeDense > gridDrawThreshold ) + { + drawGridLine( VECTOR2D( gridStartX * gridSize.x, j * gridSize.y + gridOrigin.y ), + VECTOR2D( gridEndX * gridSize.x, j * gridSize.y + gridOrigin.y ) ); + } + } + + // Horizontal lines + for( int i = gridStartX; i < gridEndX; i += 1 ) + { + if( i % gridTick == 0 && gridScreenSizeDense > gridDrawThreshold ) + SetLineWidth( doubleMarker ); + else + SetLineWidth( marker ); + + if( ( i % gridTick == 0 && gridScreenSizeCoarse > gridDrawThreshold ) + || gridScreenSizeDense > gridDrawThreshold ) + { + drawGridLine( VECTOR2D( i * gridSize.x + gridOrigin.x, gridStartY * gridSize.y ), + VECTOR2D( i * gridSize.x + gridOrigin.x, gridEndY * gridSize.y ) ); + } + } + } + else // Dotted grid + { + bool tickX, tickY; + SetIsFill( true ); + SetIsStroke( false ); + SetFillColor( gridColor ); + + for( int j = gridStartY; j < gridEndY; j += 1 ) + { + if( j % gridTick == 0 && gridScreenSizeDense > gridDrawThreshold ) + tickY = true; + else + tickY = false; + + for( int i = gridStartX; i < gridEndX; i += 1 ) + { + if( i % gridTick == 0 && gridScreenSizeDense > gridDrawThreshold ) + tickX = true; + else + tickX = false; + + if( tickX || tickY || gridScreenSizeDense > gridDrawThreshold ) + { + double radius = ( tickX && tickY ) ? doubleMarker : marker; + DrawRectangle( VECTOR2D( i * gridSize.x - radius + gridOrigin.x, + j * gridSize.y - radius + gridOrigin.y ), + VECTOR2D( i * gridSize.x + radius + gridOrigin.x, + j * gridSize.y + radius + gridOrigin.y ) ); + } + } + } + } + } +} + + +VECTOR2D GAL::GetGridPoint( const VECTOR2D& aPoint ) const +{ + return VECTOR2D( KiROUND( ( aPoint.x - gridOffset.x ) / gridSize.x ) * gridSize.x + gridOffset.x, + KiROUND( ( aPoint.y - gridOffset.y ) / gridSize.y ) * gridSize.y + gridOffset.y ); +} + +const int GAL::MIN_DEPTH = -1024; +const int GAL::MAX_DEPTH = 1023; +const int GAL::GRID_DEPTH = MAX_DEPTH - 1; diff --git a/common/gal/opengl/cached_container.cpp b/common/gal/opengl/cached_container.cpp new file mode 100644 index 0000000..8cf3513 --- /dev/null +++ b/common/gal/opengl/cached_container.cpp @@ -0,0 +1,547 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 + */ + +/** + * @file cached_container.cpp + * @brief Class to store instances of VERTEX with caching. It allows storing VERTEX objects and + * associates them with VERTEX_ITEMs. This leads to a possibility of caching vertices data in the + * GPU memory and a fast reuse of that data. + */ + +#include <gal/opengl/cached_container.h> +#include <gal/opengl/vertex_manager.h> +#include <gal/opengl/vertex_item.h> +#include <gal/opengl/shader.h> +#include <confirm.h> +#include <wx/log.h> +#include <list> +#ifdef __WXDEBUG__ +#include <profile.h> +#endif /* __WXDEBUG__ */ + +using namespace KIGFX; + +CACHED_CONTAINER::CACHED_CONTAINER( unsigned int aSize ) : + VERTEX_CONTAINER( aSize ), m_item( NULL ) +{ + // In the beginning there is only free space + m_freeChunks.insert( CHUNK( aSize, 0 ) ); + + // Do not have uninitialized members: + m_chunkSize = 0; + m_chunkOffset = 0; + m_itemSize = 0; +} + + +void CACHED_CONTAINER::SetItem( VERTEX_ITEM* aItem ) +{ + wxASSERT( aItem != NULL ); + + m_item = aItem; + m_itemSize = m_item->GetSize(); + m_chunkSize = m_itemSize; + + if( m_itemSize == 0 ) + m_items.insert( m_item ); // The item was not stored before + else + m_chunkOffset = m_item->GetOffset(); + +#if CACHED_CONTAINER_TEST > 1 + wxLogDebug( wxT( "Adding/editing item 0x%08lx (size %d)" ), (long) m_item, m_itemSize ); +#endif +} + + +void CACHED_CONTAINER::FinishItem() +{ + wxASSERT( m_item != NULL ); + wxASSERT( m_item->GetSize() == m_itemSize ); + + // Finishing the previously edited item + if( m_itemSize < m_chunkSize ) + { + // There is some not used but reserved memory left, so we should return it to the pool + int itemOffset = m_item->GetOffset(); + + // Add the not used memory back to the pool + m_freeChunks.insert( CHUNK( m_chunkSize - m_itemSize, itemOffset + m_itemSize ) ); + m_freeSpace += ( m_chunkSize - m_itemSize ); + // mergeFreeChunks(); // veery slow and buggy + } + +#if CACHED_CONTAINER_TEST > 1 + wxLogDebug( wxT( "Finishing item 0x%08lx (size %d)" ), (long) m_item, m_itemSize ); + test(); + m_item = NULL; // electric fence +#endif +} + + +VERTEX* CACHED_CONTAINER::Allocate( unsigned int aSize ) +{ + wxASSERT( m_item != NULL ); + + if( m_failed ) + return NULL; + + if( m_itemSize + aSize > m_chunkSize ) + { + // There is not enough space in the currently reserved chunk, so we have to resize it + + // Reserve a bigger memory chunk for the current item and + // make it multiple of 3 to store triangles + m_chunkSize = ( 2 * m_itemSize ) + aSize + ( 3 - aSize % 3 ); + // Save the current size before reallocating + m_chunkOffset = reallocate( m_chunkSize ); + + if( m_chunkOffset > m_currentSize ) + { + m_failed = true; + return NULL; + } + } + + VERTEX* reserved = &m_vertices[m_chunkOffset + m_itemSize]; + m_itemSize += aSize; + // Now the item officially possesses the memory chunk + m_item->setSize( m_itemSize ); + + // The content has to be updated + m_dirty = true; + +#if CACHED_CONTAINER_TEST > 1 + test(); +#endif +#if CACHED_CONTAINER_TEST > 2 + showFreeChunks(); + showReservedChunks(); +#endif + + return reserved; +} + + +void CACHED_CONTAINER::Delete( VERTEX_ITEM* aItem ) +{ + wxASSERT( aItem != NULL ); + wxASSERT( m_items.find( aItem ) != m_items.end() ); + + int size = aItem->GetSize(); + int offset = aItem->GetOffset(); + +#if CACHED_CONTAINER_TEST > 1 + wxLogDebug( wxT( "Removing 0x%08lx (size %d offset %d)" ), (long) aItem, size, offset ); +#endif + + // Insert a free memory chunk entry in the place where item was stored + if( size > 0 ) + { + m_freeChunks.insert( CHUNK( size, offset ) ); + m_freeSpace += size; + // Indicate that the item is not stored in the container anymore + aItem->setSize( 0 ); + } + + m_items.erase( aItem ); + +#if CACHED_CONTAINER_TEST > 1 + test(); +#endif + + // Dynamic memory freeing, there is no point in holding + // a large amount of memory when there is no use for it + if( m_freeSpace > ( 0.75 * m_currentSize ) && m_currentSize > m_initialSize ) + { + resizeContainer( 0.5 * m_currentSize ); + } +} + + +void CACHED_CONTAINER::Clear() +{ + // Change size to the default one + m_vertices = static_cast<VERTEX*>( realloc( m_vertices, + m_initialSize * sizeof( VERTEX ) ) ); + + // Reset state variables + m_freeSpace = m_initialSize; + m_currentSize = m_initialSize; + m_failed = false; + + // Set the size of all the stored VERTEX_ITEMs to 0, so it is clear that they are not held + // in the container anymore + ITEMS::iterator it; + + for( it = m_items.begin(); it != m_items.end(); ++it ) + { + ( *it )->setSize( 0 ); + } + + m_items.clear(); + + + // Now there is only free space left + m_freeChunks.clear(); + m_freeChunks.insert( CHUNK( m_freeSpace, 0 ) ); +} + + +unsigned int CACHED_CONTAINER::reallocate( unsigned int aSize ) +{ + wxASSERT( aSize > 0 ); + +#if CACHED_CONTAINER_TEST > 2 + wxLogDebug( wxT( "Resize 0x%08lx from %d to %d" ), (long) m_item, m_itemSize, aSize ); +#endif + + // Is there enough space to store vertices? + if( m_freeSpace < aSize ) + { + bool result; + + // Would it be enough to double the current space? + if( aSize < m_freeSpace + m_currentSize ) + { + // Yes: exponential growing + result = resizeContainer( m_currentSize * 2 ); + } + else + { + // No: grow to the nearest greater power of 2 + result = resizeContainer( pow( 2, ceil( log2( m_currentSize * 2 + aSize ) ) ) ); + } + + if( !result ) + return UINT_MAX; + } + + // Look for the free space chunk of at least given size + FREE_CHUNK_MAP::iterator newChunk = m_freeChunks.lower_bound( aSize ); + + if( newChunk == m_freeChunks.end() ) + { + // In the case when there is enough space to store the vertices, + // but the free space is not continous we should defragment the container + if( !defragment() ) + return UINT_MAX; + + // Update the current offset + m_chunkOffset = m_item->GetOffset(); + + // We can take the first free chunk, as there is only one after defragmentation + // and we can be sure that it provides enough space to store the object + newChunk = m_freeChunks.begin(); + } + + // Parameters of the allocated cuhnk + unsigned int chunkSize = newChunk->first; + unsigned int chunkOffset = newChunk->second; + + wxASSERT( chunkSize >= aSize ); + wxASSERT( chunkOffset < m_currentSize ); + + // Check if the item was previously stored in the container + if( m_itemSize > 0 ) + { +#if CACHED_CONTAINER_TEST > 3 + wxLogDebug( wxT( "Moving 0x%08x from 0x%08x to 0x%08x" ), + (int) m_item, oldChunkOffset, chunkOffset ); +#endif + // The item was reallocated, so we have to copy all the old data to the new place + memcpy( &m_vertices[chunkOffset], &m_vertices[m_chunkOffset], m_itemSize * VertexSize ); + + // Free the space previously used by the chunk + wxASSERT( m_itemSize > 0 ); + m_freeChunks.insert( CHUNK( m_itemSize, m_chunkOffset ) ); + m_freeSpace += m_itemSize; + } + + // Remove the allocated chunk from the free space pool + m_freeChunks.erase( newChunk ); + + // If there is some space left, return it to the pool - add an entry for it + if( chunkSize > aSize ) + { + m_freeChunks.insert( CHUNK( chunkSize - aSize, chunkOffset + aSize ) ); + } + + m_freeSpace -= aSize; + // mergeFreeChunks(); // veery slow and buggy + + m_item->setOffset( chunkOffset ); + + return chunkOffset; +} + + +bool CACHED_CONTAINER::defragment( VERTEX* aTarget ) +{ +#if CACHED_CONTAINER_TEST > 0 + wxLogDebug( wxT( "Defragmenting" ) ); + + prof_counter totalTime; + prof_start( &totalTime ); +#endif + + if( aTarget == NULL ) + { + // No target was specified, so we have to reallocate our own space + int size = m_currentSize * sizeof( VERTEX ); + aTarget = static_cast<VERTEX*>( malloc( size ) ); + + if( aTarget == NULL ) + { + DisplayError( NULL, wxString::Format( + wxT( "CACHED_CONTAINER::defragment: Run out of memory (malloc %d bytes)" ), + size ) ); + return false; + } + } + + int newOffset = 0; + ITEMS::iterator it, it_end; + + for( it = m_items.begin(), it_end = m_items.end(); it != it_end; ++it ) + { + VERTEX_ITEM* item = *it; + int itemOffset = item->GetOffset(); + int itemSize = item->GetSize(); + + // Move an item to the new container + memcpy( &aTarget[newOffset], &m_vertices[itemOffset], itemSize * VertexSize ); + + // Update new offset + item->setOffset( newOffset ); + + // Move to the next free space + newOffset += itemSize; + } + + free( m_vertices ); + m_vertices = aTarget; + + // Now there is only one big chunk of free memory + m_freeChunks.clear(); + wxASSERT( m_freeSpace > 0 ); + m_freeChunks.insert( CHUNK( m_freeSpace, m_currentSize - m_freeSpace ) ); + +#if CACHED_CONTAINER_TEST > 0 + prof_end( &totalTime ); + + wxLogDebug( wxT( "Defragmented the container storing %d vertices / %.1f ms" ), + m_currentSize - m_freeSpace, totalTime.msecs() ); +#endif + + return true; +} + + +void CACHED_CONTAINER::mergeFreeChunks() +{ + if( m_freeChunks.size() <= 1 ) // There are no chunks that can be merged + return; + +#if CACHED_CONTAINER_TEST > 0 + prof_counter totalTime; + prof_start( &totalTime ); +#endif + + // Reversed free chunks map - this one stores chunk size with its offset as the key + std::list<CHUNK> freeChunks; + + FREE_CHUNK_MAP::const_iterator it, it_end; + + for( it = m_freeChunks.begin(), it_end = m_freeChunks.end(); it != it_end; ++it ) + { + freeChunks.push_back( std::make_pair( it->second, it->first ) ); + } + + m_freeChunks.clear(); + freeChunks.sort(); + + std::list<CHUNK>::const_iterator itf, itf_end; + unsigned int offset = freeChunks.front().first; + unsigned int size = freeChunks.front().second; + freeChunks.pop_front(); + + for( itf = freeChunks.begin(), itf_end = freeChunks.end(); itf != itf_end; ++itf ) + { + if( itf->first == offset + size ) + { + // These chunks can be merged, so just increase the current chunk size and go on + size += itf->second; + } + else + { + // These chunks cannot be merged + // So store the previous one + m_freeChunks.insert( std::make_pair( size, offset ) ); + // and let's check the next chunk + offset = itf->first; + size = itf->second; + + } + } + + // Add the last one + m_freeChunks.insert( std::make_pair( size, offset ) ); + +#if CACHED_CONTAINER_TEST > 0 + prof_end( &totalTime ); + + wxLogDebug( wxT( "Merged free chunks / %.1f ms" ), totalTime.msecs() ); +#endif + + test(); +} + + +bool CACHED_CONTAINER::resizeContainer( unsigned int aNewSize ) +{ + wxASSERT( aNewSize != m_currentSize ); + +#if CACHED_CONTAINER_TEST > 0 + wxLogDebug( wxT( "Resizing container from %d to %d" ), m_currentSize, aNewSize ); +#endif + + VERTEX* newContainer; + + if( aNewSize < m_currentSize ) + { + // Shrinking container + // Sanity check, no shrinking if we cannot fit all the data + if( reservedSpace() > aNewSize ) + return false; + + int size = aNewSize * sizeof( VERTEX ); + newContainer = static_cast<VERTEX*>( malloc( size ) ); + + if( newContainer == NULL ) + { + DisplayError( NULL, wxString::Format( + wxT( "CACHED_CONTAINER::resizeContainer:\n" + "Run out of memory (malloc %d bytes)" ), + size ) ); + return false; + } + + // Defragment directly to the new, smaller container + defragment( newContainer ); + + // We have to correct freeChunks after defragmentation + m_freeChunks.clear(); + wxASSERT( aNewSize - reservedSpace() > 0 ); + m_freeChunks.insert( CHUNK( aNewSize - reservedSpace(), reservedSpace() ) ); + } + else + { + // Enlarging container + int size = aNewSize * sizeof( VERTEX ); + newContainer = static_cast<VERTEX*>( realloc( m_vertices, size ) ); + + if( newContainer == NULL ) + { + DisplayError( NULL, wxString::Format( + wxT( "CACHED_CONTAINER::resizeContainer:\n" + "Run out of memory (realloc from %d to %d bytes)" ), + m_currentSize * sizeof( VERTEX ), size ) ); + return false; + } + + // Add an entry for the new memory chunk at the end of the container + m_freeChunks.insert( CHUNK( aNewSize - m_currentSize, m_currentSize ) ); + } + + m_vertices = newContainer; + + m_freeSpace += ( aNewSize - m_currentSize ); + m_currentSize = aNewSize; + + return true; +} + + +#ifdef CACHED_CONTAINER_TEST +void CACHED_CONTAINER::showFreeChunks() +{ + FREE_CHUNK_MAP::iterator it; + + wxLogDebug( wxT( "Free chunks:" ) ); + + for( it = m_freeChunks.begin(); it != m_freeChunks.end(); ++it ) + { + unsigned int offset = getChunkOffset( *it ); + unsigned int size = getChunkSize( *it ); + wxASSERT( size > 0 ); + + wxLogDebug( wxT( "[0x%08x-0x%08x] (size %d)" ), + offset, offset + size - 1, size ); + } +} + + +void CACHED_CONTAINER::showReservedChunks() +{ + ITEMS::iterator it; + + wxLogDebug( wxT( "Reserved chunks:" ) ); + + for( it = m_items.begin(); it != m_items.end(); ++it ) + { + VERTEX_ITEM* item = *it; + unsigned int offset = item->GetOffset(); + unsigned int size = item->GetSize(); + wxASSERT( size > 0 ); + + wxLogDebug( wxT( "[0x%08x-0x%08x] @ 0x%08lx (size %d)" ), + offset, offset + size - 1, (long) item, size ); + } +} + + +void CACHED_CONTAINER::test() +{ + // Free space check + unsigned int freeSpace = 0; + FREE_CHUNK_MAP::iterator itf; + + for( itf = m_freeChunks.begin(); itf != m_freeChunks.end(); ++itf ) + freeSpace += getChunkSize( *itf ); + + wxASSERT( freeSpace == m_freeSpace ); + + // Reserved space check + /*unsigned int reservedSpace = 0; + ITEMS::iterator itr; + for( itr = m_items.begin(); itr != m_items.end(); ++itr ) + reservedSpace += ( *itr )->GetSize(); + reservedSpace += m_itemSize; // Add the current chunk size + + wxASSERT( ( freeSpace + reservedSpace ) == m_currentSize );*/ + + // Overlapping check TBD +} + +#endif /* CACHED_CONTAINER_TEST */ diff --git a/common/gal/opengl/gpu_manager.cpp b/common/gal/opengl/gpu_manager.cpp new file mode 100644 index 0000000..be34a56 --- /dev/null +++ b/common/gal/opengl/gpu_manager.cpp @@ -0,0 +1,302 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 + */ + +/** + * @file gpu_manager.cpp + * @brief Class to handle uploading vertices and indices to GPU in drawing purposes. + */ + +#include <gal/opengl/gpu_manager.h> +#include <gal/opengl/cached_container.h> +#include <gal/opengl/noncached_container.h> +#include <gal/opengl/shader.h> +#include <typeinfo> +#include <wx/msgdlg.h> +#include <confirm.h> +#ifdef PROFILE +#include <profile.h> +#include <wx/debug.h> +#include <wx/log.h> +#endif /* PROFILE */ + +using namespace KIGFX; + +GPU_MANAGER* GPU_MANAGER::MakeManager( VERTEX_CONTAINER* aContainer ) +{ + if( typeid( *aContainer ) == typeid( CACHED_CONTAINER ) ) + return new GPU_CACHED_MANAGER( aContainer ); + else if( typeid( *aContainer ) == typeid( NONCACHED_CONTAINER ) ) + return new GPU_NONCACHED_MANAGER( aContainer ); + + wxASSERT_MSG( false, wxT( "Not handled container type" ) ); + return NULL; +} + + +GPU_MANAGER::GPU_MANAGER( VERTEX_CONTAINER* aContainer ) : + m_isDrawing( false ), m_container( aContainer ), m_shader( NULL ), m_shaderAttrib( 0 ) +{ +} + + +GPU_MANAGER::~GPU_MANAGER() +{ +} + + +void GPU_MANAGER::SetShader( SHADER& aShader ) +{ + m_shader = &aShader; + + m_shaderAttrib = m_shader->GetAttribute( "attrShaderParams" ); + + if( m_shaderAttrib == -1 ) + { + DisplayError( NULL, wxT( "Could not get the shader attribute location" ) ); + } +} + + +// Cached manager +GPU_CACHED_MANAGER::GPU_CACHED_MANAGER( VERTEX_CONTAINER* aContainer ) : + GPU_MANAGER( aContainer ), m_buffersInitialized( false ), m_indicesPtr( NULL ), + m_verticesBuffer( 0 ), m_indicesBuffer( 0 ), m_indicesSize( 0 ), m_indicesCapacity( 0 ) +{ + // Allocate the biggest possible buffer for indices + resizeIndices( aContainer->GetSize() ); +} + + +GPU_CACHED_MANAGER::~GPU_CACHED_MANAGER() +{ + if( m_buffersInitialized ) + { + glBindBuffer( GL_ARRAY_BUFFER, 0 ); + glDeleteBuffers( 1, &m_verticesBuffer ); + glDeleteBuffers( 1, &m_indicesBuffer ); + } +} + + +void GPU_CACHED_MANAGER::Initialize() +{ + wxASSERT( !m_buffersInitialized ); + + if( !m_buffersInitialized ) + { + glGenBuffers( 1, &m_verticesBuffer ); + glGenBuffers( 1, &m_indicesBuffer ); + m_buffersInitialized = true; + } +} + + +void GPU_CACHED_MANAGER::BeginDrawing() +{ + wxASSERT( !m_isDrawing ); + + if( m_container->IsDirty() ) + uploadToGpu(); + + // Number of vertices to be drawn in the EndDrawing() + m_indicesSize = 0; + // Set the indices pointer to the beginning of the indices-to-draw buffer + m_indicesPtr = m_indices.get(); + + m_isDrawing = true; +} + + +void GPU_CACHED_MANAGER::DrawIndices( unsigned int aOffset, unsigned int aSize ) +{ + wxASSERT( m_isDrawing ); + + // Copy indices of items that should be drawn to GPU memory + for( unsigned int i = aOffset; i < aOffset + aSize; *m_indicesPtr++ = i++ ); + + m_indicesSize += aSize; +} + + +void GPU_CACHED_MANAGER::DrawAll() +{ + wxASSERT( m_isDrawing ); + + m_indicesSize = m_container->GetSize(); + for( unsigned int i = 0; i < m_indicesSize; *m_indicesPtr++ = i++ ); +} + + +void GPU_CACHED_MANAGER::EndDrawing() +{ + wxASSERT( m_isDrawing ); + + // Prepare buffers + glEnableClientState( GL_VERTEX_ARRAY ); + glEnableClientState( GL_COLOR_ARRAY ); + + // Bind vertices data buffers + glBindBuffer( GL_ARRAY_BUFFER, m_verticesBuffer ); + glVertexPointer( CoordStride, GL_FLOAT, VertexSize, 0 ); + glColorPointer( ColorStride, GL_UNSIGNED_BYTE, VertexSize, (GLvoid*) ColorOffset ); + + if( m_shader != NULL ) // Use shader if applicable + { + m_shader->Use(); + glEnableVertexAttribArray( m_shaderAttrib ); + glVertexAttribPointer( m_shaderAttrib, ShaderStride, GL_FLOAT, GL_FALSE, + VertexSize, (GLvoid*) ShaderOffset ); + } + + glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indicesBuffer ); + glBufferData( GL_ELEMENT_ARRAY_BUFFER, m_indicesSize * sizeof(int), (GLvoid*) m_indices.get(), GL_DYNAMIC_DRAW ); + + glDrawElements( GL_TRIANGLES, m_indicesSize, GL_UNSIGNED_INT, 0 ); + + glBindBuffer( GL_ARRAY_BUFFER, 0 ); + glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 ); + + // Deactivate vertex array + glDisableClientState( GL_COLOR_ARRAY ); + glDisableClientState( GL_VERTEX_ARRAY ); + + if( m_shader != NULL ) + { + glDisableVertexAttribArray( m_shaderAttrib ); + m_shader->Deactivate(); + } + + m_isDrawing = false; +} + + +void GPU_CACHED_MANAGER::uploadToGpu() +{ +#ifdef PROFILE + prof_counter totalTime; + prof_start( &totalTime ); +#endif /* PROFILE */ + + if( !m_buffersInitialized ) + Initialize(); + + int bufferSize = m_container->GetSize(); + GLfloat* vertices = (GLfloat*) m_container->GetAllVertices(); + + // Upload vertices coordinates and shader types to GPU memory + glBindBuffer( GL_ARRAY_BUFFER, m_verticesBuffer ); + glBufferData( GL_ARRAY_BUFFER, bufferSize * VertexSize, vertices, GL_STATIC_DRAW ); + glBindBuffer( GL_ARRAY_BUFFER, 0 ); + + // Allocate the biggest possible buffer for indices + resizeIndices( bufferSize ); + + if( glGetError() != GL_NO_ERROR ) + DisplayError( NULL, wxT( "Error during data upload to the GPU memory" ) ); + +#ifdef PROFILE + prof_end( &totalTime ); + + wxLogDebug( wxT( "Uploading %d vertices to GPU / %.1f ms" ), bufferSize, totalTime.msecs() ); +#endif /* PROFILE */ +} + + +void GPU_CACHED_MANAGER::resizeIndices( unsigned int aNewSize ) +{ + if( aNewSize > m_indicesCapacity ) + { + m_indicesCapacity = aNewSize; + m_indices.reset( new GLuint[m_indicesCapacity] ); + } +} + + +// Noncached manager +GPU_NONCACHED_MANAGER::GPU_NONCACHED_MANAGER( VERTEX_CONTAINER* aContainer ) : + GPU_MANAGER( aContainer ) +{ +} + + +void GPU_NONCACHED_MANAGER::Initialize() +{ + // Nothing has to be intialized +} + + +void GPU_NONCACHED_MANAGER::BeginDrawing() +{ + // Nothing has to be prepared +} + + +void GPU_NONCACHED_MANAGER::DrawIndices( unsigned int aOffset, unsigned int aSize ) +{ + wxASSERT_MSG( false, wxT( "Not implemented yet" ) ); +} + + +void GPU_NONCACHED_MANAGER::DrawAll() +{ + // This is the default use case, nothing has to be done + // The real rendering takes place in the EndDrawing() function +} + + +void GPU_NONCACHED_MANAGER::EndDrawing() +{ + VERTEX* vertices = m_container->GetAllVertices(); + GLfloat* coordinates = (GLfloat*) ( vertices ); + GLubyte* colors = (GLubyte*) ( vertices ) + ColorOffset; + + // Prepare buffers + glEnableClientState( GL_VERTEX_ARRAY ); + glEnableClientState( GL_COLOR_ARRAY ); + + glVertexPointer( CoordStride, GL_FLOAT, VertexSize, coordinates ); + glColorPointer( ColorStride, GL_UNSIGNED_BYTE, VertexSize, colors ); + + if( m_shader != NULL ) // Use shader if applicable + { + GLfloat* shaders = (GLfloat*) ( vertices ) + ShaderOffset / sizeof(GLfloat); + + m_shader->Use(); + glEnableVertexAttribArray( m_shaderAttrib ); + glVertexAttribPointer( m_shaderAttrib, ShaderStride, GL_FLOAT, GL_FALSE, + VertexSize, shaders ); + } + + glDrawArrays( GL_TRIANGLES, 0, m_container->GetSize() ); + + // Deactivate vertex array + glDisableClientState( GL_COLOR_ARRAY ); + glDisableClientState( GL_VERTEX_ARRAY ); + + if( m_shader != NULL ) + { + glDisableVertexAttribArray( m_shaderAttrib ); + m_shader->Deactivate(); + } +} diff --git a/common/gal/opengl/noncached_container.cpp b/common/gal/opengl/noncached_container.cpp new file mode 100644 index 0000000..c902c47 --- /dev/null +++ b/common/gal/opengl/noncached_container.cpp @@ -0,0 +1,89 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 + */ + +/** + * @file noncached_container.cpp + * @brief Class to store instances of VERTEX without caching. It allows a fast one-frame drawing + * and then clearing the buffer and starting from scratch. + */ + +#include <gal/opengl/noncached_container.h> +#include <cstdlib> + +using namespace KIGFX; + +NONCACHED_CONTAINER::NONCACHED_CONTAINER( unsigned int aSize ) : + VERTEX_CONTAINER( aSize ), m_freePtr( 0 ) +{ +} + + +NONCACHED_CONTAINER::~NONCACHED_CONTAINER() +{ +} + + +void NONCACHED_CONTAINER::SetItem( VERTEX_ITEM* aItem ) +{ + // Nothing has to be done, as the noncached container + // does not care about VERTEX_ITEMs ownership +} + + +VERTEX* NONCACHED_CONTAINER::Allocate( unsigned int aSize ) +{ + if( m_freeSpace < aSize ) + { + // Double the space + VERTEX* newVertices = static_cast<VERTEX*>( realloc( m_vertices, + m_currentSize * 2 * + sizeof(VERTEX) ) ); + + if( newVertices != NULL ) + { + m_vertices = newVertices; + m_freeSpace += m_currentSize; + m_currentSize *= 2; + } + else + { + return NULL; + } + } + + VERTEX* freeVertex = &m_vertices[m_freePtr]; + + // Move to the next free chunk + m_freePtr += aSize; + m_freeSpace -= aSize; + + return freeVertex; +} + + +void NONCACHED_CONTAINER::Clear() +{ + m_freePtr = 0; + m_freeSpace = m_currentSize; +} diff --git a/common/gal/opengl/opengl_compositor.cpp b/common/gal/opengl/opengl_compositor.cpp new file mode 100644 index 0000000..9f35d9e --- /dev/null +++ b/common/gal/opengl/opengl_compositor.cpp @@ -0,0 +1,291 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013-2015 CERN + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 + */ + +/** + * @file opengl_compositor.cpp + * @brief Class that handles multitarget rendering (i.e. to different textures/surfaces) and + * later compositing into a single image (OpenGL flavour). + */ + +#include <gal/opengl/opengl_compositor.h> + +#include <stdexcept> +#include <cassert> + +using namespace KIGFX; + +OPENGL_COMPOSITOR::OPENGL_COMPOSITOR() : + m_initialized( false ), m_current( 0 ), m_currentFbo( DIRECT_RENDERING ) +{ + // Avoid not initialized members: + m_framebuffer = 0; + m_depthBuffer = 0; +} + + +OPENGL_COMPOSITOR::~OPENGL_COMPOSITOR() +{ + if( m_initialized ) + clean(); +} + + +void OPENGL_COMPOSITOR::Initialize() +{ + if( m_initialized ) + return; + + // We need framebuffer objects for drawing the screen contents + // Generate framebuffer and a depth buffer + glGenFramebuffersEXT( 1, &m_framebuffer ); + glBindFramebufferEXT( GL_FRAMEBUFFER, m_framebuffer ); + m_currentFbo = m_framebuffer; + + // Allocate memory for the depth buffer + // Attach the depth buffer to the framebuffer + glGenRenderbuffersEXT( 1, &m_depthBuffer ); + glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, m_depthBuffer ); + + // Use here a size of 24 bits for the depth buffer, 8 bits for the stencil buffer + // this is required later for anti-aliasing + glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT16, m_width, m_height ); + glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER_EXT, m_depthBuffer ); + + // Unbind the framebuffer, so by default all the rendering goes directly to the display + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, DIRECT_RENDERING ); + m_currentFbo = DIRECT_RENDERING; + + m_initialized = true; +} + + +void OPENGL_COMPOSITOR::Resize( unsigned int aWidth, unsigned int aHeight ) +{ + if( m_initialized ) + clean(); + + m_width = aWidth; + m_height = aHeight; +} + + +unsigned int OPENGL_COMPOSITOR::CreateBuffer() +{ + assert( m_initialized ); + + unsigned int maxBuffers; + + // Get the maximum number of buffers + glGetIntegerv( GL_MAX_COLOR_ATTACHMENTS, (GLint*) &maxBuffers ); + + if( usedBuffers() >= maxBuffers ) + { + throw std::runtime_error("Cannot create more framebuffers. OpenGL rendering " + "backend requires at least 3 framebuffers. You may try to update/change " + "your graphic drivers."); + } + + // GL_COLOR_ATTACHMENTn are consecutive integers + GLuint attachmentPoint = GL_COLOR_ATTACHMENT0 + usedBuffers(); + GLuint textureTarget; + + // Generate the texture for the pixel storage + glGenTextures( 1, &textureTarget ); + glBindTexture( GL_TEXTURE_2D, textureTarget ); + + // Set texture parameters + glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, m_width, m_height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, NULL ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + + // Bind the texture to the specific attachment point, clear and rebind the screen + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_framebuffer ); + m_currentFbo = m_framebuffer; + glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, attachmentPoint, + GL_TEXTURE_2D, textureTarget, 0 ); + + // Check the status, exit if the framebuffer can't be created + GLenum status = glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT ); + + if( status != GL_FRAMEBUFFER_COMPLETE_EXT ) + { + switch( status ) + { + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: + throw std::runtime_error( "Cannot create the framebuffer." ); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: + throw std::runtime_error( "The framebuffer attachment points are incomplete." ); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: + throw std::runtime_error( "The framebuffer does not have at least one " + "image attached to it." ); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: + throw std::runtime_error( "The framebuffer read buffer is incomplete." ); + break; + + case GL_FRAMEBUFFER_UNSUPPORTED_EXT: + throw std::runtime_error( "The combination of internal formats of the attached " + "images violates an implementation-dependent set of restrictions." ); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT: + throw std::runtime_error( "GL_RENDERBUFFER_SAMPLES is not the same for " + "all attached renderbuffers" ); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT: + throw std::runtime_error( "Framebuffer incomplete layer targets errors." ); + break; + + default: + throw std::runtime_error( "Cannot create the framebuffer." ); + break; + } + + return 0; + } + + ClearBuffer(); + + // Return to direct rendering (we were asked only to create a buffer, not switch to one) + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, DIRECT_RENDERING ); + m_currentFbo = DIRECT_RENDERING; + + // Store the new buffer + OPENGL_BUFFER buffer = { textureTarget, attachmentPoint }; + m_buffers.push_back( buffer ); + + return usedBuffers(); +} + + +void OPENGL_COMPOSITOR::SetBuffer( unsigned int aBufferHandle ) +{ + if( aBufferHandle > usedBuffers() ) + return; + + // Change the rendering destination to the selected attachment point + if( aBufferHandle == DIRECT_RENDERING ) + { + m_currentFbo = DIRECT_RENDERING; + } + else if( m_currentFbo != m_framebuffer ) + { + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_framebuffer ); + m_currentFbo = m_framebuffer; + } + + if( m_currentFbo != DIRECT_RENDERING ) + { + m_current = aBufferHandle - 1; + glDrawBuffer( m_buffers[m_current].attachmentPoint ); + } +} + + +void OPENGL_COMPOSITOR::ClearBuffer() +{ + assert( m_initialized ); + + glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); +} + + +void OPENGL_COMPOSITOR::DrawBuffer( unsigned int aBufferHandle ) +{ + assert( m_initialized ); + assert( aBufferHandle != 0 && aBufferHandle <= usedBuffers() ); + + // Switch to the main framebuffer and blit the scene + glBindFramebufferEXT( GL_FRAMEBUFFER, DIRECT_RENDERING ); + m_currentFbo = DIRECT_RENDERING; + + // Depth test has to be disabled to make transparency working + glDisable( GL_DEPTH_TEST ); + glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA ); + + // Enable texturing and bind the main texture + glEnable( GL_TEXTURE_2D ); + glBindTexture( GL_TEXTURE_2D, m_buffers[aBufferHandle - 1].textureTarget ); + + // Draw a full screen quad with the texture + glMatrixMode( GL_MODELVIEW ); + glPushMatrix(); + glLoadIdentity(); + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); + glLoadIdentity(); + + glBegin( GL_TRIANGLES ); + glTexCoord2f( 0.0f, 1.0f ); + glVertex2f( -1.0f, -1.0f ); + glTexCoord2f( 1.0f, 1.0f ); + glVertex2f( 1.0f, -1.0f ); + glTexCoord2f( 1.0f, 0.0f ); + glVertex2f( 1.0f, 1.0f ); + + glTexCoord2f( 0.0f, 1.0f ); + glVertex2f( -1.0f, -1.0f ); + glTexCoord2f( 1.0f, 0.0f ); + glVertex2f( 1.0f, 1.0f ); + glTexCoord2f( 0.0f, 0.0f ); + glVertex2f( -1.0f, 1.0f ); + glEnd(); + + glPopMatrix(); + glMatrixMode( GL_MODELVIEW ); + glPopMatrix(); +} + + +void OPENGL_COMPOSITOR::clean() +{ + assert( m_initialized ); + + glBindFramebufferEXT( GL_FRAMEBUFFER, DIRECT_RENDERING ); + m_currentFbo = DIRECT_RENDERING; + + OPENGL_BUFFERS::const_iterator it; + + for( it = m_buffers.begin(); it != m_buffers.end(); ++it ) + { + glDeleteTextures( 1, &it->textureTarget ); + } + + m_buffers.clear(); + + glDeleteFramebuffersEXT( 1, &m_framebuffer ); + glDeleteRenderbuffersEXT( 1, &m_depthBuffer ); + + m_initialized = false; +} diff --git a/common/gal/opengl/opengl_gal.cpp b/common/gal/opengl/opengl_gal.cpp new file mode 100644 index 0000000..945161f --- /dev/null +++ b/common/gal/opengl/opengl_gal.cpp @@ -0,0 +1,1204 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * Copyright (C) 2013-2016 CERN + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * Graphics Abstraction Layer (GAL) for OpenGL + * + * 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 <gal/opengl/opengl_gal.h> +#include <gal/definitions.h> + +#include <wx/log.h> +#include <macros.h> +#ifdef __WXDEBUG__ +#include <profile.h> +#endif /* __WXDEBUG__ */ + +#include <limits> +#include <boost/bind.hpp> + +using namespace KIGFX; + +static void InitTesselatorCallbacks( GLUtesselator* aTesselator ); +const int glAttributes[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 8, 0 }; +wxGLContext* OPENGL_GAL::glContext = NULL; + +OPENGL_GAL::OPENGL_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener, + wxEvtHandler* aPaintListener, const wxString& aName ) : + wxGLCanvas( aParent, wxID_ANY, (int*) glAttributes, wxDefaultPosition, wxDefaultSize, + wxEXPAND, aName ), + mouseListener( aMouseListener ), + paintListener( aPaintListener ), + cachedManager( true ), + nonCachedManager( false ), + overlayManager( false ) +{ + if( glContext == NULL ) + glContext = new wxGLContext( this ); + + // Check if OpenGL requirements are met + runTest(); + + // Make VBOs use shaders + cachedManager.SetShader( shader ); + nonCachedManager.SetShader( shader ); + overlayManager.SetShader( shader ); + + // Initialize the flags + isFramebufferInitialized = false; + isGrouping = false; + groupCounter = 0; + +#ifdef RETINA_OPENGL_PATCH + SetViewWantsBestResolution( true ); +#endif + + // Connecting the event handlers + Connect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::onPaint ) ); + + // Mouse events are skipped to the parent + Connect( wxEVT_MOTION, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); + Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); + Connect( wxEVT_LEFT_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); + Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); + Connect( wxEVT_MIDDLE_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); + Connect( wxEVT_MIDDLE_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); + Connect( wxEVT_MIDDLE_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); + Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); + Connect( wxEVT_RIGHT_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); + Connect( wxEVT_RIGHT_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); + Connect( wxEVT_MOUSEWHEEL, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); +#ifdef USE_OSX_MAGNIFY_EVENT + Connect( wxEVT_MAGNIFY, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); +#endif +#if defined _WIN32 || defined _WIN64 + Connect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); +#endif + + SetSize( aParent->GetSize() ); + screenSize = VECTOR2I( aParent->GetSize() ); + + // Grid color settings are different in Cairo and OpenGL + SetGridColor( COLOR4D( 0.8, 0.8, 0.8, 0.1 ) ); + + // Tesselator initialization + tesselator = gluNewTess(); + InitTesselatorCallbacks( tesselator ); + + if( tesselator == NULL ) + throw std::runtime_error( "Could not create the tesselator" ); + + gluTessProperty( tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE ); + + currentManager = &nonCachedManager; +} + + +OPENGL_GAL::~OPENGL_GAL() +{ + glFlush(); + + gluDeleteTess( tesselator ); + ClearCache(); +} + + +void OPENGL_GAL::BeginDrawing() +{ + if( !IsShownOnScreen() ) + return; + + SetCurrent( *glContext ); + clientDC = new wxClientDC( this ); + +#ifdef RETINA_OPENGL_PATCH + const float scaleFactor = GetBackingScaleFactor(); +#else + const float scaleFactor = 1.0f; +#endif + + // Set up the view port + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glViewport( 0, 0, (GLsizei) screenSize.x * scaleFactor, (GLsizei) screenSize.y * scaleFactor ); + + // Create the screen transformation + glOrtho( 0, (GLint) screenSize.x, 0, (GLsizei) screenSize.y, + -depthRange.x, -depthRange.y ); + + if( !isFramebufferInitialized ) + { + // Prepare rendering target buffers + compositor.Initialize(); + mainBuffer = compositor.CreateBuffer(); + overlayBuffer = compositor.CreateBuffer(); + + isFramebufferInitialized = true; + } + + // Disable 2D Textures + glDisable( GL_TEXTURE_2D ); + + // Enable the depth buffer + glEnable( GL_DEPTH_TEST ); + glDepthFunc( GL_LESS ); + + // Setup blending, required for transparent objects + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + + glMatrixMode( GL_MODELVIEW ); + + // Set up the world <-> screen transformation + ComputeWorldScreenMatrix(); + GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; + matrixData[0] = worldScreenMatrix.m_data[0][0]; + matrixData[1] = worldScreenMatrix.m_data[1][0]; + matrixData[2] = worldScreenMatrix.m_data[2][0]; + matrixData[4] = worldScreenMatrix.m_data[0][1]; + matrixData[5] = worldScreenMatrix.m_data[1][1]; + matrixData[6] = worldScreenMatrix.m_data[2][1]; + matrixData[12] = worldScreenMatrix.m_data[0][2]; + matrixData[13] = worldScreenMatrix.m_data[1][2]; + matrixData[14] = worldScreenMatrix.m_data[2][2]; + glLoadMatrixd( matrixData ); + + // Set defaults + SetFillColor( fillColor ); + SetStrokeColor( strokeColor ); + + // Unbind buffers - set compositor for direct drawing + compositor.SetBuffer( OPENGL_COMPOSITOR::DIRECT_RENDERING ); + + // Remove all previously stored items + nonCachedManager.Clear(); + overlayManager.Clear(); + + cachedManager.BeginDrawing(); + nonCachedManager.BeginDrawing(); + overlayManager.BeginDrawing(); +} + + +void OPENGL_GAL::EndDrawing() +{ + // Cached & non-cached containers are rendered to the same buffer + compositor.SetBuffer( mainBuffer ); + nonCachedManager.EndDrawing(); + cachedManager.EndDrawing(); + + // Overlay container is rendered to a different buffer + compositor.SetBuffer( overlayBuffer ); + overlayManager.EndDrawing(); + + // Be sure that the framebuffer is not colorized (happens on specific GPU&drivers combinations) + glColor4d( 1.0, 1.0, 1.0, 1.0 ); + + // Draw the remaining contents, blit the rendering targets to the screen, swap the buffers + compositor.DrawBuffer( mainBuffer ); + compositor.DrawBuffer( overlayBuffer ); + blitCursor(); + + glFlush(); + SwapBuffers(); + + delete clientDC; +} + + +void OPENGL_GAL::DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) +{ + const VECTOR2D startEndVector = aEndPoint - aStartPoint; + double lineAngle = startEndVector.Angle(); + + currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a ); + + drawLineQuad( aStartPoint, aEndPoint ); + + // Line caps + if( lineWidth > 1.0 ) + { + drawFilledSemiCircle( aStartPoint, lineWidth / 2, lineAngle + M_PI / 2 ); + drawFilledSemiCircle( aEndPoint, lineWidth / 2, lineAngle - M_PI / 2 ); + } +} + + +void OPENGL_GAL::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint, + double aWidth ) +{ + VECTOR2D startEndVector = aEndPoint - aStartPoint; + double lineAngle = startEndVector.Angle(); + + if( isFillEnabled ) + { + // Filled tracks + currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a ); + + SetLineWidth( aWidth ); + drawLineQuad( aStartPoint, aEndPoint ); + + // Draw line caps + drawFilledSemiCircle( aStartPoint, aWidth / 2, lineAngle + M_PI / 2 ); + drawFilledSemiCircle( aEndPoint, aWidth / 2, lineAngle - M_PI / 2 ); + } + else + { + // Outlined tracks + double lineLength = startEndVector.EuclideanNorm(); + + currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a ); + + Save(); + + currentManager->Translate( aStartPoint.x, aStartPoint.y, 0.0 ); + currentManager->Rotate( lineAngle, 0.0f, 0.0f, 1.0f ); + + drawLineQuad( VECTOR2D( 0.0, aWidth / 2.0 ), + VECTOR2D( lineLength, aWidth / 2.0 ) ); + + drawLineQuad( VECTOR2D( 0.0, -aWidth / 2.0 ), + VECTOR2D( lineLength, -aWidth / 2.0 ) ); + + // Draw line caps + drawStrokedSemiCircle( VECTOR2D( 0.0, 0.0 ), aWidth / 2, M_PI / 2 ); + drawStrokedSemiCircle( VECTOR2D( lineLength, 0.0 ), aWidth / 2, -M_PI / 2 ); + + Restore(); + } +} + + +void OPENGL_GAL::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius ) +{ + if( isFillEnabled ) + { + currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a ); + + /* Draw a triangle that contains the circle, then shade it leaving only the circle. + * Parameters given to setShader are indices of the triangle's vertices + * (if you want to understand more, check the vertex shader source [shader.vert]). + * Shader uses this coordinates to determine if fragments are inside the circle or not. + * v2 + * /\ + * //\\ + * v0 /_\/_\ v1 + */ + currentManager->Shader( SHADER_FILLED_CIRCLE, 1.0 ); + currentManager->Vertex( aCenterPoint.x - aRadius * sqrt( 3.0f ), // v0 + aCenterPoint.y - aRadius, layerDepth ); + + currentManager->Shader( SHADER_FILLED_CIRCLE, 2.0 ); + currentManager->Vertex( aCenterPoint.x + aRadius * sqrt( 3.0f ), // v1 + aCenterPoint.y - aRadius, layerDepth ); + + currentManager->Shader( SHADER_FILLED_CIRCLE, 3.0 ); + currentManager->Vertex( aCenterPoint.x, aCenterPoint.y + aRadius * 2.0f, // v2 + layerDepth ); + } + + if( isStrokeEnabled ) + { + currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a ); + + /* Draw a triangle that contains the circle, then shade it leaving only the circle. + * Parameters given to setShader are indices of the triangle's vertices + * (if you want to understand more, check the vertex shader source [shader.vert]). + * and the line width. Shader uses this coordinates to determine if fragments are + * inside the circle or not. + * v2 + * /\ + * //\\ + * v0 /_\/_\ v1 + */ + double outerRadius = aRadius + ( lineWidth / 2 ); + currentManager->Shader( SHADER_STROKED_CIRCLE, 1.0, aRadius, lineWidth ); + currentManager->Vertex( aCenterPoint.x - outerRadius * sqrt( 3.0f ), // v0 + aCenterPoint.y - outerRadius, layerDepth ); + + currentManager->Shader( SHADER_STROKED_CIRCLE, 2.0, aRadius, lineWidth ); + currentManager->Vertex( aCenterPoint.x + outerRadius * sqrt( 3.0f ), // v1 + aCenterPoint.y - outerRadius, layerDepth ); + + currentManager->Shader( SHADER_STROKED_CIRCLE, 3.0, aRadius, lineWidth ); + currentManager->Vertex( aCenterPoint.x, aCenterPoint.y + outerRadius * 2.0f, // v2 + layerDepth ); + } +} + + +void OPENGL_GAL::DrawArc( const VECTOR2D& aCenterPoint, double aRadius, double aStartAngle, + double aEndAngle ) +{ + if( aRadius <= 0 ) + return; + + // Swap the angles, if start angle is greater than end angle + SWAP( aStartAngle, >, aEndAngle ); + + Save(); + currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0 ); + + if( isStrokeEnabled ) + { + const double alphaIncrement = 2.0 * M_PI / CIRCLE_POINTS; + currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a ); + + VECTOR2D p( cos( aStartAngle ) * aRadius, sin( aStartAngle ) * aRadius ); + double alpha; + + for( alpha = aStartAngle + alphaIncrement; alpha <= aEndAngle; alpha += alphaIncrement ) + { + VECTOR2D p_next( cos( alpha ) * aRadius, sin( alpha ) * aRadius ); + DrawLine( p, p_next ); + + p = p_next; + } + + // Draw the last missing part + if( alpha != aEndAngle ) + { + VECTOR2D p_last( cos( aEndAngle ) * aRadius, sin( aEndAngle ) * aRadius ); + DrawLine( p, p_last ); + } + } + + if( isFillEnabled ) + { + const double alphaIncrement = 2 * M_PI / CIRCLE_POINTS; + double alpha; + currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a ); + currentManager->Shader( SHADER_NONE ); + + // Triangle fan + for( alpha = aStartAngle; ( alpha + alphaIncrement ) < aEndAngle; ) + { + currentManager->Vertex( 0.0, 0.0, 0.0 ); + currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius, 0.0 ); + alpha += alphaIncrement; + currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius, 0.0 ); + } + + // The last missing triangle + const VECTOR2D endPoint( cos( aEndAngle ) * aRadius, sin( aEndAngle ) * aRadius ); + currentManager->Vertex( 0.0, 0.0, 0.0 ); + currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius, 0.0 ); + currentManager->Vertex( endPoint.x, endPoint.y, 0.0 ); + } + + Restore(); +} + + +void OPENGL_GAL::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) +{ + // Compute the diagonal points of the rectangle + VECTOR2D diagonalPointA( aEndPoint.x, aStartPoint.y ); + VECTOR2D diagonalPointB( aStartPoint.x, aEndPoint.y ); + + // Stroke the outline + if( isStrokeEnabled ) + { + currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a ); + + std::deque<VECTOR2D> pointList; + pointList.push_back( aStartPoint ); + pointList.push_back( diagonalPointA ); + pointList.push_back( aEndPoint ); + pointList.push_back( diagonalPointB ); + pointList.push_back( aStartPoint ); + DrawPolyline( pointList ); + } + + // Fill the rectangle + if( isFillEnabled ) + { + currentManager->Shader( SHADER_NONE ); + currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a ); + + currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth ); + currentManager->Vertex( diagonalPointA.x, diagonalPointA.y, layerDepth ); + currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth ); + + currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth ); + currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth ); + currentManager->Vertex( diagonalPointB.x, diagonalPointB.y, layerDepth ); + } +} + + +void OPENGL_GAL::DrawPolyline( const std::deque<VECTOR2D>& aPointList ) +{ + if( aPointList.size() < 2 ) + return; + + currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a ); + + std::deque<VECTOR2D>::const_iterator it = aPointList.begin(); + + // Start from the second point + for( ++it; it != aPointList.end(); ++it ) + { + const VECTOR2D startEndVector = ( *it - *( it - 1 ) ); + double lineAngle = startEndVector.Angle(); + + drawLineQuad( *( it - 1 ), *it ); + + // There is no need to draw line caps on both ends of polyline's segments + drawFilledSemiCircle( *( it - 1 ), lineWidth / 2, lineAngle + M_PI / 2 ); + } + + // ..and now - draw the ending cap + const VECTOR2D startEndVector = ( *( it - 1 ) - *( it - 2 ) ); + double lineAngle = startEndVector.Angle(); + drawFilledSemiCircle( *( it - 1 ), lineWidth / 2, lineAngle - M_PI / 2 ); +} + + +void OPENGL_GAL::DrawPolyline( const VECTOR2D aPointList[], int aListSize ) +{ + if( aListSize < 2 ) + return; + + currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a ); + + // Start from the second point + for( int i = 1; i < aListSize; ++i ) + { + const VECTOR2D startEndVector = ( aPointList[i] - aPointList[i - 1] ); + double lineAngle = startEndVector.Angle(); + + drawLineQuad( aPointList[i - 1], aPointList[i] ); + + // There is no need to draw line caps on both ends of polyline's segments + drawFilledSemiCircle( aPointList[i - 1], lineWidth / 2, lineAngle + M_PI / 2 ); + } + + // ..and now - draw the ending cap + const VECTOR2D startEndVector = ( aPointList[aListSize - 1] - aPointList[aListSize - 2] ); + double lineAngle = startEndVector.Angle(); + drawFilledSemiCircle( aPointList[aListSize - 1], lineWidth / 2, lineAngle - M_PI / 2 ); +} + + +void OPENGL_GAL::DrawPolygon( const std::deque<VECTOR2D>& aPointList ) +{ + currentManager->Shader( SHADER_NONE ); + currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a ); + + // Any non convex polygon needs to be tesselated + // for this purpose the GLU standard functions are used + TessParams params = { currentManager, tessIntersects }; + gluTessBeginPolygon( tesselator, ¶ms ); + gluTessBeginContour( tesselator ); + + boost::shared_array<GLdouble> points( new GLdouble[3 * aPointList.size()] ); + int v = 0; + + for( std::deque<VECTOR2D>::const_iterator it = aPointList.begin(); it != aPointList.end(); ++it ) + { + points[v] = it->x; + points[v + 1] = it->y; + points[v + 2] = layerDepth; + gluTessVertex( tesselator, &points[v], &points[v] ); + v += 3; + } + + gluTessEndContour( tesselator ); + gluTessEndPolygon( tesselator ); + + // Free allocated intersecting points + tessIntersects.clear(); + + // vertexList destroyed here +} + + +void OPENGL_GAL::DrawPolygon( const VECTOR2D aPointList[], int aListSize ) +{ + currentManager->Shader( SHADER_NONE ); + currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a ); + + // Any non convex polygon needs to be tesselated + // for this purpose the GLU standard functions are used + TessParams params = { currentManager, tessIntersects }; + gluTessBeginPolygon( tesselator, ¶ms ); + gluTessBeginContour( tesselator ); + + boost::shared_array<GLdouble> points( new GLdouble[3 * aListSize] ); + int v = 0; + const VECTOR2D* ptr = aPointList; + + for( int i = 0; i < aListSize; ++i ) + { + points[v] = ptr->x; + points[v + 1] = ptr->y; + points[v + 2] = layerDepth; + gluTessVertex( tesselator, &points[v], &points[v] ); + ++ptr; + v += 3; + } + + gluTessEndContour( tesselator ); + gluTessEndPolygon( tesselator ); + + // Free allocated intersecting points + tessIntersects.clear(); + + // vertexList destroyed here +} + + +void OPENGL_GAL::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aControlPointA, + const VECTOR2D& aControlPointB, const VECTOR2D& aEndPoint ) +{ + // FIXME The drawing quality needs to be improved + // FIXME Perhaps choose a quad/triangle strip instead? + // FIXME Brute force method, use a better (recursive?) algorithm + + std::deque<VECTOR2D> pointList; + + double t = 0.0; + double dt = 1.0 / (double) CURVE_POINTS; + + for( int i = 0; i <= CURVE_POINTS; i++ ) + { + double omt = 1.0 - t; + double omt2 = omt * omt; + double omt3 = omt * omt2; + double t2 = t * t; + double t3 = t * t2; + + VECTOR2D vertex = omt3 * aStartPoint + 3.0 * t * omt2 * aControlPointA + + 3.0 * t2 * omt * aControlPointB + t3 * aEndPoint; + + pointList.push_back( vertex ); + + t += dt; + } + + DrawPolyline( pointList ); +} + + +void OPENGL_GAL::ResizeScreen( int aWidth, int aHeight ) +{ + screenSize = VECTOR2I( aWidth, aHeight ); + +#ifdef RETINA_OPENGL_PATCH + const float scaleFactor = GetBackingScaleFactor(); +#else + const float scaleFactor = 1.0f; +#endif + + // Resize framebuffers + compositor.Resize( aWidth * scaleFactor, aHeight * scaleFactor ); + isFramebufferInitialized = false; + + wxGLCanvas::SetSize( aWidth, aHeight ); +} + + +bool OPENGL_GAL::Show( bool aShow ) +{ + bool s = wxGLCanvas::Show( aShow ); + + if( aShow ) + wxGLCanvas::Raise(); + + return s; +} + + +void OPENGL_GAL::Flush() +{ + glFlush(); +} + + +void OPENGL_GAL::ClearScreen( const COLOR4D& aColor ) +{ + // Clear screen + glClearColor( aColor.r, aColor.g, aColor.b, aColor.a ); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); +} + + +void OPENGL_GAL::Transform( const MATRIX3x3D& aTransformation ) +{ + GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; + + matrixData[0] = aTransformation.m_data[0][0]; + matrixData[1] = aTransformation.m_data[1][0]; + matrixData[2] = aTransformation.m_data[2][0]; + matrixData[4] = aTransformation.m_data[0][1]; + matrixData[5] = aTransformation.m_data[1][1]; + matrixData[6] = aTransformation.m_data[2][1]; + matrixData[12] = aTransformation.m_data[0][2]; + matrixData[13] = aTransformation.m_data[1][2]; + matrixData[14] = aTransformation.m_data[2][2]; + + glMultMatrixd( matrixData ); +} + + +void OPENGL_GAL::Rotate( double aAngle ) +{ + currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f ); +} + + +void OPENGL_GAL::Translate( const VECTOR2D& aVector ) +{ + currentManager->Translate( aVector.x, aVector.y, 0.0f ); +} + + +void OPENGL_GAL::Scale( const VECTOR2D& aScale ) +{ + currentManager->Scale( aScale.x, aScale.y, 0.0f ); +} + + +void OPENGL_GAL::Save() +{ + currentManager->PushMatrix(); +} + + +void OPENGL_GAL::Restore() +{ + currentManager->PopMatrix(); +} + + +int OPENGL_GAL::BeginGroup() +{ + isGrouping = true; + + boost::shared_ptr<VERTEX_ITEM> newItem( new VERTEX_ITEM( cachedManager ) ); + int groupNumber = getNewGroupNumber(); + groups.insert( std::make_pair( groupNumber, newItem ) ); + + return groupNumber; +} + + +void OPENGL_GAL::EndGroup() +{ + cachedManager.FinishItem(); + isGrouping = false; +} + + +void OPENGL_GAL::DrawGroup( int aGroupNumber ) +{ + cachedManager.DrawItem( *groups[aGroupNumber] ); +} + + +void OPENGL_GAL::ChangeGroupColor( int aGroupNumber, const COLOR4D& aNewColor ) +{ + cachedManager.ChangeItemColor( *groups[aGroupNumber], aNewColor ); +} + + +void OPENGL_GAL::ChangeGroupDepth( int aGroupNumber, int aDepth ) +{ + cachedManager.ChangeItemDepth( *groups[aGroupNumber], aDepth ); +} + + +void OPENGL_GAL::DeleteGroup( int aGroupNumber ) +{ + // Frees memory in the container as well + groups.erase( aGroupNumber ); +} + + +void OPENGL_GAL::ClearCache() +{ + groups.clear(); + cachedManager.Clear(); +} + + +void OPENGL_GAL::SaveScreen() +{ + wxASSERT_MSG( false, wxT( "Not implemented yet" ) ); +} + + +void OPENGL_GAL::RestoreScreen() +{ + wxASSERT_MSG( false, wxT( "Not implemented yet" ) ); +} + + +void OPENGL_GAL::SetTarget( RENDER_TARGET aTarget ) +{ + switch( aTarget ) + { + default: + case TARGET_CACHED: + currentManager = &cachedManager; + break; + + case TARGET_NONCACHED: + currentManager = &nonCachedManager; + break; + + case TARGET_OVERLAY: + currentManager = &overlayManager; + break; + } + + currentTarget = aTarget; +} + + +RENDER_TARGET OPENGL_GAL::GetTarget() const +{ + return currentTarget; +} + + +void OPENGL_GAL::ClearTarget( RENDER_TARGET aTarget ) +{ + // Save the current state + unsigned int oldTarget = compositor.GetBuffer(); + + switch( aTarget ) + { + // Cached and noncached items are rendered to the same buffer + default: + case TARGET_CACHED: + case TARGET_NONCACHED: + compositor.SetBuffer( mainBuffer ); + break; + + case TARGET_OVERLAY: + compositor.SetBuffer( overlayBuffer ); + break; + } + + compositor.ClearBuffer(); + + // Restore the previous state + compositor.SetBuffer( oldTarget ); +} + + +void OPENGL_GAL::DrawCursor( const VECTOR2D& aCursorPosition ) +{ + // Now we should only store the position of the mouse cursor + // The real drawing routines are in blitCursor() + VECTOR2D screenCursor = worldScreenMatrix * aCursorPosition; + + cursorPosition = screenWorldMatrix * VECTOR2D( screenCursor.x, screenSize.y - screenCursor.y ); +} + + +void OPENGL_GAL::drawGridLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) +{ + compositor.SetBuffer( mainBuffer ); + + // We do not need a very precise comparison here (the lineWidth is set by GAL::DrawGrid()) + if( fabs( lineWidth - 2.0 * gridLineWidth / worldScale ) < 0.1 ) + glLineWidth( 1.0 ); + else + glLineWidth( 2.0 ); + + glColor4d( gridColor.r, gridColor.g, gridColor.b, gridColor.a ); + + glBegin( GL_LINES ); + glVertex3d( aStartPoint.x, aStartPoint.y, layerDepth ); + glVertex3d( aEndPoint.x, aEndPoint.y, layerDepth ); + glEnd(); + + // Restore the default color, so textures will be drawn properly + glColor4d( 1.0, 1.0, 1.0, 1.0 ); +} + + +void OPENGL_GAL::drawLineQuad( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) +{ + /* Helper drawing: ____--- v3 ^ + * ____---- ... \ \ + * ____---- ... \ end \ + * v1 ____---- ... ____---- \ width + * ---- ...___---- \ \ + * \ ___...-- \ v + * \ ____----... ____---- v2 + * ---- ... ____---- + * start \ ... ____---- + * \... ____---- + * ---- + * v0 + * dots mark triangles' hypotenuses + */ + + VECTOR2D startEndVector = aEndPoint - aStartPoint; + double lineLength = startEndVector.EuclideanNorm(); + + if( lineLength <= 0.0 ) + return; + + double scale = 0.5 * lineWidth / lineLength; + + // The perpendicular vector also needs transformations + glm::vec4 vector = currentManager->GetTransformation() * + glm::vec4( -startEndVector.y * scale, startEndVector.x * scale, 0.0, 0.0 ); + + // Line width is maintained by the vertex shader + currentManager->Shader( SHADER_LINE, vector.x, vector.y, lineWidth ); + currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth ); // v0 + + currentManager->Shader( SHADER_LINE, -vector.x, -vector.y, lineWidth ); + currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth ); // v1 + + currentManager->Shader( SHADER_LINE, -vector.x, -vector.y, lineWidth ); + currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth ); // v3 + + currentManager->Shader( SHADER_LINE, vector.x, vector.y, lineWidth ); + currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth ); // v0 + + currentManager->Shader( SHADER_LINE, -vector.x, -vector.y, lineWidth ); + currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth ); // v3 + + currentManager->Shader( SHADER_LINE, vector.x, vector.y, lineWidth ); + currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth ); // v2 +} + + +void OPENGL_GAL::drawSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle ) +{ + if( isFillEnabled ) + { + currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a ); + drawFilledSemiCircle( aCenterPoint, aRadius, aAngle ); + } + + if( isStrokeEnabled ) + { + currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a ); + drawStrokedSemiCircle( aCenterPoint, aRadius, aAngle ); + } +} + + +void OPENGL_GAL::drawFilledSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, + double aAngle ) +{ + Save(); + currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f ); + currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f ); + + /* Draw a triangle that contains the semicircle, then shade it to leave only + * the semicircle. Parameters given to setShader are indices of the triangle's vertices + * (if you want to understand more, check the vertex shader source [shader.vert]). + * Shader uses these coordinates to determine if fragments are inside the semicircle or not. + * v2 + * /\ + * /__\ + * v0 //__\\ v1 + */ + currentManager->Shader( SHADER_FILLED_CIRCLE, 4.0f ); + currentManager->Vertex( -aRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v0 + + currentManager->Shader( SHADER_FILLED_CIRCLE, 5.0f ); + currentManager->Vertex( aRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v1 + + currentManager->Shader( SHADER_FILLED_CIRCLE, 6.0f ); + currentManager->Vertex( 0.0f, aRadius * 2.0f, layerDepth ); // v2 + + Restore(); +} + + +void OPENGL_GAL::drawStrokedSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, + double aAngle ) +{ + double outerRadius = aRadius + ( lineWidth / 2 ); + + Save(); + currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f ); + currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f ); + + /* Draw a triangle that contains the semicircle, then shade it to leave only + * the semicircle. Parameters given to setShader are indices of the triangle's vertices + * (if you want to understand more, check the vertex shader source [shader.vert]), the + * radius and the line width. Shader uses these coordinates to determine if fragments are + * inside the semicircle or not. + * v2 + * /\ + * /__\ + * v0 //__\\ v1 + */ + currentManager->Shader( SHADER_STROKED_CIRCLE, 4.0f, aRadius, lineWidth ); + currentManager->Vertex( -outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v0 + + currentManager->Shader( SHADER_STROKED_CIRCLE, 5.0f, aRadius, lineWidth ); + currentManager->Vertex( outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v1 + + currentManager->Shader( SHADER_STROKED_CIRCLE, 6.0f, aRadius, lineWidth ); + currentManager->Vertex( 0.0f, outerRadius * 2.0f, layerDepth ); // v2 + + Restore(); +} + + +void OPENGL_GAL::onPaint( wxPaintEvent& WXUNUSED( aEvent ) ) +{ + PostPaint(); +} + + +void OPENGL_GAL::skipMouseEvent( wxMouseEvent& aEvent ) +{ + // Post the mouse event to the event listener registered in constructor, if any + if( mouseListener ) + wxPostEvent( mouseListener, aEvent ); +} + + +void OPENGL_GAL::blitCursor() +{ + if( !isCursorEnabled ) + return; + + compositor.SetBuffer( OPENGL_COMPOSITOR::DIRECT_RENDERING ); + + VECTOR2D cursorBegin = cursorPosition - cursorSize / ( 2 * worldScale ); + VECTOR2D cursorEnd = cursorPosition + cursorSize / ( 2 * worldScale ); + VECTOR2D cursorCenter = ( cursorBegin + cursorEnd ) / 2; + + glDisable( GL_TEXTURE_2D ); + glLineWidth( 1.0 ); + glColor4d( cursorColor.r, cursorColor.g, cursorColor.b, cursorColor.a ); + + glBegin( GL_LINES ); + glVertex2d( cursorCenter.x, cursorBegin.y ); + glVertex2d( cursorCenter.x, cursorEnd.y ); + + glVertex2d( cursorBegin.x, cursorCenter.y ); + glVertex2d( cursorEnd.x, cursorCenter.y ); + glEnd(); +} + + +unsigned int OPENGL_GAL::getNewGroupNumber() +{ + wxASSERT_MSG( groups.size() < std::numeric_limits<unsigned int>::max(), + wxT( "There are no free slots to store a group" ) ); + + while( groups.find( groupCounter ) != groups.end() ) + { + groupCounter++; + } + + return groupCounter++; +} + + +bool OPENGL_GAL::runTest() +{ + wxDialog dlgtest( GetParent(), -1, wxT( "opengl test" ), wxPoint( 50, 50 ), + wxDLG_UNIT( GetParent(), wxSize( 50, 50 ) ) ); + OPENGL_TEST* test = new OPENGL_TEST( &dlgtest, this ); + + dlgtest.Raise(); // on Linux, on some windows managers (Unity for instance) this is needed to actually show the dialog + dlgtest.ShowModal(); + bool result = test->IsOk(); + + if( !result ) + throw std::runtime_error( test->GetError() ); + + return result; +} + + +OPENGL_GAL::OPENGL_TEST::OPENGL_TEST( wxDialog* aParent, OPENGL_GAL* aGal ) : + wxGLCanvas( aParent, wxID_ANY, glAttributes, wxDefaultPosition, + wxDefaultSize, 0, wxT( "GLCanvas" ) ), + m_parent( aParent ), m_gal( aGal ), m_tested( false ), m_result( false ) +{ + m_timeoutTimer.SetOwner( this ); + Connect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::OPENGL_TEST::Render ) ); + Connect( wxEVT_TIMER, wxTimerEventHandler( OPENGL_GAL::OPENGL_TEST::OnTimeout ) ); + m_parent->Connect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::OPENGL_TEST::OnDialogPaint ), NULL, this ); +} + + +void OPENGL_GAL::OPENGL_TEST::Render( wxPaintEvent& WXUNUSED( aEvent ) ) +{ + if( !m_tested ) + { + if( !IsShownOnScreen() ) + return; + + m_timeoutTimer.Stop(); + m_result = true; // Assume everything is fine, until proven otherwise + + // One test is enough - close the testing dialog when the test is finished + Disconnect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::OPENGL_TEST::Render ) ); + CallAfter( boost::bind( &wxDialog::EndModal, m_parent, wxID_NONE ) ); + + SetCurrent( *OPENGL_GAL::glContext ); + GLenum err = glewInit(); + + if( GLEW_OK != err ) + { + error( (const char*) glewGetErrorString( err ) ); + return; + } + else + { + wxLogDebug( wxString( wxT( "Status: Using GLEW " ) ) + + FROM_UTF8( (char*) glewGetString( GLEW_VERSION ) ) ); + } + + // Check the OpenGL version (minimum 2.1 is required) + if( GLEW_VERSION_2_1 ) + { + wxLogInfo( wxT( "OpenGL 2.1 supported." ) ); + } + else + { + error( "OpenGL 2.1 or higher is required!" ); + return; + } + + // Framebuffers have to be supported + if( !GLEW_EXT_framebuffer_object ) + { + error( "Framebuffer objects are not supported!" ); + return; + } + + // Vertex buffer has to be supported + if( !GLEW_ARB_vertex_buffer_object ) + { + error( "Vertex buffer objects are not supported!" ); + return; + } + + // Prepare shaders + if( !m_gal->shader.LoadBuiltinShader( 0, SHADER_TYPE_VERTEX ) ) + { + error( "Cannot compile vertex shader!" ); + return; + } + + if( !m_gal->shader.LoadBuiltinShader( 1, SHADER_TYPE_FRAGMENT ) ) + { + error( "Cannot compile fragment shader!" ); + return; + } + + if( !m_gal->shader.Link() ) + { + error( "Cannot link the shaders!" ); + return; + } + + m_tested = true; + } +} + + +void OPENGL_GAL::OPENGL_TEST::OnTimeout( wxTimerEvent& aEvent ) +{ + error( "Could not create OpenGL canvas" ); + m_parent->EndModal( wxID_NONE ); +} + + +void OPENGL_GAL::OPENGL_TEST::OnDialogPaint( wxPaintEvent& aEvent ) +{ + // GL canvas may never appear on the screen (e.g. due to missing GL extensions), and the test + // will not be run. Therefore give at most a second to perform the test, otherwise we conclude + // it has failed. + // Also, wxWidgets OnShow event is triggered before a window is shown, therefore here we use + // OnPaint event, which is executed when a window is actually visible. + m_timeoutTimer.StartOnce( 1000 ); +} + + +void OPENGL_GAL::OPENGL_TEST::error( const std::string& aError ) +{ + m_timeoutTimer.Stop(); + m_result = false; + m_tested = true; + m_error = aError; +} + +// ------------------------------------- // Callback functions for the tesselator // ------------------------------------- // Compare Redbook Chapter 11 +void CALLBACK VertexCallback( GLvoid* aVertexPtr, void* aData ) +{ + GLdouble* vertex = static_cast<GLdouble*>( aVertexPtr ); + OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData ); + VERTEX_MANAGER* vboManager = param->vboManager; + + if( vboManager ) + vboManager->Vertex( vertex[0], vertex[1], vertex[2] ); +} + + +void CALLBACK CombineCallback( GLdouble coords[3], + GLdouble* vertex_data[4], + GLfloat weight[4], GLdouble** dataOut, void* aData ) +{ + GLdouble* vertex = new GLdouble[3]; + OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData ); + + // Save the pointer so we can delete it later + param->intersectPoints.push_back( boost::shared_array<GLdouble>( vertex ) ); + + memcpy( vertex, coords, 3 * sizeof(GLdouble) ); + + *dataOut = vertex; +} + + +void CALLBACK EdgeCallback( GLboolean aEdgeFlag ) +{ + // This callback is needed to force GLU tesselator to use triangles only +} + + +void CALLBACK ErrorCallback( GLenum aErrorCode ) +{ + //throw std::runtime_error( std::string( "Tessellation error: " ) + + //std::string( (const char*) gluErrorString( aErrorCode ) ); +} + + +static void InitTesselatorCallbacks( GLUtesselator* aTesselator ) +{ + gluTessCallback( aTesselator, GLU_TESS_VERTEX_DATA, ( void (CALLBACK*)() )VertexCallback ); + gluTessCallback( aTesselator, GLU_TESS_COMBINE_DATA, ( void (CALLBACK*)() )CombineCallback ); + gluTessCallback( aTesselator, GLU_TESS_EDGE_FLAG, ( void (CALLBACK*)() )EdgeCallback ); + gluTessCallback( aTesselator, GLU_TESS_ERROR, ( void (CALLBACK*)() )ErrorCallback ); +} diff --git a/common/gal/opengl/shader.cpp b/common/gal/opengl/shader.cpp new file mode 100644 index 0000000..228d53d --- /dev/null +++ b/common/gal/opengl/shader.cpp @@ -0,0 +1,272 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * Graphics Abstraction Layer (GAL) for OpenGL + * + * Shader class + * + * 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 <iostream> +#include <fstream> +#include <stdexcept> + +#include <cstring> +#include <cassert> + +#include <gal/opengl/shader.h> +#include "shader_src.h" + +using namespace KIGFX; + +SHADER::SHADER() : + isProgramCreated( false ), + isShaderLinked( false ), + active( false ), + maximumVertices( 4 ), + geomInputType( GL_LINES ), + geomOutputType( GL_LINES ) + +{ + // Do not have uninitialized members: + programNumber = 0; +} + + +SHADER::~SHADER() +{ + if( isProgramCreated ) + { + // Delete the shaders and the program + for( std::deque<GLuint>::iterator it = shaderNumbers.begin(); it != shaderNumbers.end(); + ++it ) + { + glDeleteShader( *it ); + } + + glDeleteProgram( programNumber ); + } +} + + +bool SHADER::LoadBuiltinShader( unsigned int aShaderNumber, SHADER_TYPE aShaderType ) +{ + if( aShaderNumber >= shaders_number ) + return false; + + return addSource( std::string( shaders_src[aShaderNumber] ), aShaderType ); +} + + +bool SHADER::LoadShaderFromFile( const std::string& aShaderSourceName, SHADER_TYPE aShaderType ) +{ + // Load shader sources + const std::string shaderSource = readSource( aShaderSourceName ); + + return addSource( shaderSource, aShaderType ); +} + + +void SHADER::ConfigureGeometryShader( GLuint maxVertices, GLuint geometryInputType, + GLuint geometryOutputType ) +{ + maximumVertices = maxVertices; + geomInputType = geometryInputType; + geomOutputType = geometryOutputType; +} + + +bool SHADER::Link() +{ + // Shader linking + glLinkProgram( programNumber ); + programInfo( programNumber ); + + // Check the Link state + glGetObjectParameterivARB( programNumber, GL_OBJECT_LINK_STATUS_ARB, + (GLint*) &isShaderLinked ); + +#ifdef DEBUG + if( !isShaderLinked ) + { + int maxLength; + glGetProgramiv( programNumber, GL_INFO_LOG_LENGTH, &maxLength ); + maxLength = maxLength + 1; + char* linkInfoLog = new char[maxLength]; + glGetProgramInfoLog( programNumber, maxLength, &maxLength, linkInfoLog ); + std::cerr << "Shader linking error:" << std::endl; + std::cerr << linkInfoLog; + delete[] linkInfoLog; + } +#endif /* DEBUG */ + + return isShaderLinked; +} + + +int SHADER::AddParameter( const std::string& aParameterName ) +{ + GLint location = glGetUniformLocation( programNumber, aParameterName.c_str() ); + + if( location != -1 ) + parameterLocation.push_back( location ); + + return location; +} + + +void SHADER::SetParameter( int parameterNumber, float value ) const +{ + glUniform1f( parameterLocation[parameterNumber], value ); +} + + +void SHADER::SetParameter( int parameterNumber, int value ) const +{ + glUniform1i( parameterLocation[parameterNumber], value ); +} + + +int SHADER::GetAttribute( std::string aAttributeName ) const +{ + return glGetAttribLocation( programNumber, aAttributeName.c_str() ); +} + + +void SHADER::programInfo( GLuint aProgram ) +{ + GLint glInfoLogLength = 0; + GLint writtenChars = 0; + + // Get the length of the info string + glGetProgramiv( aProgram, GL_INFO_LOG_LENGTH, &glInfoLogLength ); + + // Print the information + if( glInfoLogLength > 2 ) + { + GLchar* glInfoLog = new GLchar[glInfoLogLength]; + glGetProgramInfoLog( aProgram, glInfoLogLength, &writtenChars, glInfoLog ); + + std::cerr << glInfoLog << std::endl; + + delete[] glInfoLog; + } +} + + +void SHADER::shaderInfo( GLuint aShader ) +{ + GLint glInfoLogLength = 0; + GLint writtenChars = 0; + + // Get the length of the info string + glGetShaderiv( aShader, GL_INFO_LOG_LENGTH, &glInfoLogLength ); + + // Print the information + if( glInfoLogLength > 2 ) + { + GLchar* glInfoLog = new GLchar[glInfoLogLength]; + glGetShaderInfoLog( aShader, glInfoLogLength, &writtenChars, glInfoLog ); + + std::cerr << glInfoLog << std::endl; + + delete[] glInfoLog; + } +} + + +std::string SHADER::readSource( std::string aShaderSourceName ) +{ + // Open the shader source for reading + std::ifstream inputFile( aShaderSourceName.c_str(), std::ifstream::in ); + std::string shaderSource; + + if( !inputFile ) + throw std::runtime_error( "Can't read the shader source: " + aShaderSourceName ); + + std::string shaderSourceLine; + + // Read all lines from the text file + while( getline( inputFile, shaderSourceLine ) ) + { + shaderSource += shaderSourceLine; + shaderSource += "\n"; + } + + return shaderSource; +} + + +bool SHADER::addSource( const std::string& aShaderSource, SHADER_TYPE aShaderType ) +{ + assert( !isShaderLinked ); + + // Create the program + if( !isProgramCreated ) + { + programNumber = glCreateProgram(); + isProgramCreated = true; + } + + // Create a shader + GLuint shaderNumber = glCreateShader( aShaderType ); + shaderNumbers.push_back( shaderNumber ); + + // Get the program info + programInfo( programNumber ); + + // Copy to char array + char* source = new char[aShaderSource.size() + 1]; + strncpy( source, aShaderSource.c_str(), aShaderSource.size() + 1 ); + const char** source_ = (const char**) ( &source ); + + // Attach the source + glShaderSource( shaderNumber, 1, source_, NULL ); + programInfo( programNumber ); + + // Delete the allocated char array + delete[] source; + + // Compile and attach shader to the program + glCompileShader( shaderNumber ); + GLint status; + glGetShaderiv( shaderNumber, GL_COMPILE_STATUS, &status ); + + if( status != GL_TRUE ) + { + shaderInfo( shaderNumber ); + throw std::runtime_error( "Shader compilation error" ); + } + + glAttachShader( programNumber, shaderNumber ); + programInfo( programNumber ); + + // Special handling for the geometry shader + if( aShaderType == SHADER_TYPE_GEOMETRY ) + { + glProgramParameteriEXT( programNumber, GL_GEOMETRY_VERTICES_OUT_EXT, maximumVertices ); + glProgramParameteriEXT( programNumber, GL_GEOMETRY_INPUT_TYPE_EXT, geomInputType ); + glProgramParameteriEXT( programNumber, GL_GEOMETRY_OUTPUT_TYPE_EXT, geomOutputType ); + } + + return true; +} diff --git a/common/gal/opengl/shader.frag b/common/gal/opengl/shader.frag new file mode 100644 index 0000000..7d7d96c --- /dev/null +++ b/common/gal/opengl/shader.frag @@ -0,0 +1,75 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * Fragment shader + * + * 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 + */ + +#version 120 + +// Shader types +const float SHADER_LINE = 1.0; +const float SHADER_FILLED_CIRCLE = 2.0; +const float SHADER_STROKED_CIRCLE = 3.0; + +varying vec4 shaderParams; +varying vec2 circleCoords; + +void filledCircle( vec2 aCoord ) +{ + if( dot( aCoord, aCoord ) < 1.0 ) + gl_FragColor = gl_Color; + else + discard; +} + + +void strokedCircle( vec2 aCoord, float aRadius, float aWidth ) +{ + float outerRadius = aRadius + ( aWidth / 2 ); + float innerRadius = aRadius - ( aWidth / 2 ); + float relWidth = innerRadius / outerRadius; + + if( ( dot( aCoord, aCoord ) < 1.0 ) && + ( dot( aCoord, aCoord ) > relWidth * relWidth ) ) + gl_FragColor = gl_Color; + else + discard; +} + + +void main() +{ + if( shaderParams[0] == SHADER_FILLED_CIRCLE ) + { + filledCircle( circleCoords ); + } + else if( shaderParams[0] == SHADER_STROKED_CIRCLE ) + { + strokedCircle( circleCoords, shaderParams[2], shaderParams[3] ); + } + else + { + // Simple pass-through + gl_FragColor = gl_Color; + } +} diff --git a/common/gal/opengl/shader.vert b/common/gal/opengl/shader.vert new file mode 100644 index 0000000..24521a4 --- /dev/null +++ b/common/gal/opengl/shader.vert @@ -0,0 +1,95 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * Vertex shader + * + * 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 + */ + +#version 120 + +// Shader types +const float SHADER_LINE = 1.0; +const float SHADER_FILLED_CIRCLE = 2.0; +const float SHADER_STROKED_CIRCLE = 3.0; + +// Minimum line width +const float MIN_WIDTH = 1.0; + +attribute vec4 attrShaderParams; +varying vec4 shaderParams; +varying vec2 circleCoords; + +void main() +{ + // Pass attributes to the fragment shader + shaderParams = attrShaderParams; + + if( shaderParams[0] == SHADER_LINE ) + { + float lineWidth = shaderParams[3]; + float worldScale = gl_ModelViewMatrix[0][0]; + + // Make lines appear to be at least 1 pixel wide + if( worldScale * lineWidth < MIN_WIDTH ) + gl_Position = gl_ModelViewProjectionMatrix * + ( gl_Vertex + vec4( shaderParams.yz * MIN_WIDTH / ( worldScale * lineWidth ), 0.0, 0.0 ) ); + else + gl_Position = gl_ModelViewProjectionMatrix * + ( gl_Vertex + vec4( shaderParams.yz, 0.0, 0.0 ) ); + } + else if( ( shaderParams[0] == SHADER_STROKED_CIRCLE ) || + ( shaderParams[0] == SHADER_FILLED_CIRCLE ) ) + { + // Compute relative circle coordinates basing on indices + // Circle + if( shaderParams[1] == 1.0 ) + circleCoords = vec2( -sqrt( 3.0 ), -1.0 ); + else if( shaderParams[1] == 2.0 ) + circleCoords = vec2( sqrt( 3.0 ), -1.0 ); + else if( shaderParams[1] == 3.0 ) + circleCoords = vec2( 0.0, 2.0 ); + + // Semicircle + else if( shaderParams[1] == 4.0 ) + circleCoords = vec2( -3.0 / sqrt( 3.0 ), 0.0 ); + else if( shaderParams[1] == 5.0 ) + circleCoords = vec2( 3.0 / sqrt( 3.0 ), 0.0 ); + else if( shaderParams[1] == 6.0 ) + circleCoords = vec2( 0.0, 2.0 ); + + // Make the line appear to be at least 1 pixel wide + float lineWidth = shaderParams[3]; + float worldScale = gl_ModelViewMatrix[0][0]; + + if( worldScale * lineWidth < MIN_WIDTH ) + shaderParams[3] = shaderParams[3] / ( worldScale * lineWidth ); + + gl_Position = ftransform(); + } + else + { + // Pass through the coordinates like in the fixed pipeline + gl_Position = ftransform(); + } + + gl_FrontColor = gl_Color; +} diff --git a/common/gal/opengl/vertex_container.cpp b/common/gal/opengl/vertex_container.cpp new file mode 100644 index 0000000..fa41ecb --- /dev/null +++ b/common/gal/opengl/vertex_container.cpp @@ -0,0 +1,60 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 + */ + +/** + * @file vertex_container.cpp + * @brief Class to store vertices and handle transfers between system memory and GPU memory. + */ + +#include <gal/opengl/vertex_container.h> +#include <gal/opengl/cached_container.h> +#include <gal/opengl/noncached_container.h> +#include <gal/opengl/shader.h> +#include <cstdlib> +#include <cstring> + +using namespace KIGFX; + +VERTEX_CONTAINER* VERTEX_CONTAINER::MakeContainer( bool aCached ) +{ + if( aCached ) + return new CACHED_CONTAINER; + else + return new NONCACHED_CONTAINER; +} + + +VERTEX_CONTAINER::VERTEX_CONTAINER( unsigned int aSize ) : + m_freeSpace( aSize ), m_currentSize( aSize ), m_initialSize( aSize ), + m_failed( false ), m_dirty( true ) +{ + m_vertices = static_cast<VERTEX*>( malloc( aSize * sizeof( VERTEX ) ) ); + memset( m_vertices, 0x00, aSize * sizeof( VERTEX ) ); +} + + +VERTEX_CONTAINER::~VERTEX_CONTAINER() +{ + free( m_vertices ); +} diff --git a/common/gal/opengl/vertex_item.cpp b/common/gal/opengl/vertex_item.cpp new file mode 100644 index 0000000..a6fd352 --- /dev/null +++ b/common/gal/opengl/vertex_item.cpp @@ -0,0 +1,53 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 + */ + +/** + * @file vertex_item.cpp + * @brief Class to handle an item held in a container. + */ + +#include <gal/opengl/vertex_item.h> +#include <gal/opengl/vertex_manager.h> +#include <cstring> + +using namespace KIGFX; + +VERTEX_ITEM::VERTEX_ITEM( const VERTEX_MANAGER& aManager ) : + m_manager( aManager ), m_offset( 0 ), m_size( 0 ) +{ + // As the item is created, we are going to modify it, so call to SetItem() is needed + m_manager.SetItem( *this ); +} + + +VERTEX_ITEM::~VERTEX_ITEM() +{ + m_manager.FreeItem( *this ); +} + + +VERTEX* VERTEX_ITEM::GetVertices() const +{ + return m_manager.GetVertices( *this ); +} diff --git a/common/gal/opengl/vertex_manager.cpp b/common/gal/opengl/vertex_manager.cpp new file mode 100644 index 0000000..d055b52 --- /dev/null +++ b/common/gal/opengl/vertex_manager.cpp @@ -0,0 +1,234 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 + */ + +/** + * @file vertex_manager.cpp + * @brief Class to control vertex container and GPU with possibility of emulating old-style OpenGL + * 1.0 state machine using modern OpenGL methods. + */ + +#include <gal/opengl/vertex_manager.h> +#include <gal/opengl/cached_container.h> +#include <gal/opengl/noncached_container.h> +#include <gal/opengl/gpu_manager.h> +#include <gal/opengl/vertex_item.h> +#include <confirm.h> + +using namespace KIGFX; + +VERTEX_MANAGER::VERTEX_MANAGER( bool aCached ) : + m_noTransform( true ), m_transform( 1.0f ) +{ + m_container.reset( VERTEX_CONTAINER::MakeContainer( aCached ) ); + m_gpu.reset( GPU_MANAGER::MakeManager( m_container.get() ) ); + + // There is no shader used by default + for( unsigned int i = 0; i < ShaderStride; ++i ) + m_shader[i] = 0.0f; +} + + +void VERTEX_MANAGER::Vertex( GLfloat aX, GLfloat aY, GLfloat aZ ) const +{ + // flag to avoid hanging by calling DisplayError too many times: + static bool show_err = true; + + // Obtain the pointer to the vertex in the currently used container + VERTEX* newVertex = m_container->Allocate( 1 ); + + if( newVertex == NULL ) + { + if( show_err ) + { + DisplayError( NULL, wxT( "VERTEX_MANAGER::Vertex: Vertex allocation error" ) ); + show_err = false; + } + + return; + } + + putVertex( *newVertex, aX, aY, aZ ); +} + + +void VERTEX_MANAGER::Vertices( const VERTEX aVertices[], unsigned int aSize ) const +{ + // flag to avoid hanging by calling DisplayError too many times: + static bool show_err = true; + + // Obtain pointer to the vertex in currently used container + VERTEX* newVertex = m_container->Allocate( aSize ); + + if( newVertex == NULL ) + { + if( show_err ) + { + DisplayError( NULL, wxT( "VERTEX_MANAGER::Vertices: Vertex allocation error" ) ); + show_err = false; + } + + return; + } + + // Put vertices in already allocated memory chunk + for( unsigned int i = 0; i < aSize; ++i ) + { + putVertex( newVertex[i], aVertices[i].x, aVertices[i].y, aVertices[i].z ); + } +} + + +void VERTEX_MANAGER::SetItem( VERTEX_ITEM& aItem ) const +{ + m_container->SetItem( &aItem ); +} + + +void VERTEX_MANAGER::FinishItem() const +{ + m_container->FinishItem(); +} + + +void VERTEX_MANAGER::FreeItem( VERTEX_ITEM& aItem ) const +{ + m_container->Delete( &aItem ); +} + + +void VERTEX_MANAGER::ChangeItemColor( const VERTEX_ITEM& aItem, const COLOR4D& aColor ) const +{ + unsigned int size = aItem.GetSize(); + unsigned int offset = aItem.GetOffset(); + + VERTEX* vertex = m_container->GetVertices( offset ); + + for( unsigned int i = 0; i < size; ++i ) + { + vertex->r = aColor.r * 255.0; + vertex->g = aColor.g * 255.0; + vertex->b = aColor.b * 255.0; + vertex->a = aColor.a * 255.0; + vertex++; + } + + m_container->SetDirty(); +} + + +void VERTEX_MANAGER::ChangeItemDepth( const VERTEX_ITEM& aItem, GLfloat aDepth ) const +{ + unsigned int size = aItem.GetSize(); + unsigned int offset = aItem.GetOffset(); + + VERTEX* vertex = m_container->GetVertices( offset ); + + for( unsigned int i = 0; i < size; ++i ) + { + vertex->z = aDepth; + vertex++; + } + + m_container->SetDirty(); +} + + +VERTEX* VERTEX_MANAGER::GetVertices( const VERTEX_ITEM& aItem ) const +{ + if( aItem.GetSize() == 0 ) + return NULL; // The item is not stored in the container + + return m_container->GetVertices( aItem.GetOffset() ); +} + + +void VERTEX_MANAGER::SetShader( SHADER& aShader ) const +{ + m_gpu->SetShader( aShader ); +} + + +void VERTEX_MANAGER::Clear() const +{ + m_container->Clear(); +} + + +void VERTEX_MANAGER::BeginDrawing() const +{ + m_gpu->BeginDrawing(); +} + + +void VERTEX_MANAGER::DrawItem( const VERTEX_ITEM& aItem ) const +{ + int size = aItem.GetSize(); + + if( size > 0 ) + { + int offset = aItem.GetOffset(); + m_gpu->DrawIndices( offset, size ); + } +} + + +void VERTEX_MANAGER::EndDrawing() const +{ + m_gpu->EndDrawing(); +} + + +void VERTEX_MANAGER::putVertex( VERTEX& aTarget, GLfloat aX, GLfloat aY, GLfloat aZ ) const +{ + // Modify the vertex according to the currently used transformations + if( m_noTransform ) + { + // Simply copy coordinates, when the transform matrix is the identity matrix + aTarget.x = aX; + aTarget.y = aY; + aTarget.z = aZ; + } + else + { + // Apply transformations + glm::vec4 transVertex( aX, aY, aZ, 1.0f ); + transVertex = m_transform * transVertex; + + aTarget.x = transVertex.x; + aTarget.y = transVertex.y; + aTarget.z = transVertex.z; + } + + // Apply currently used color + aTarget.r = m_color[0]; + aTarget.g = m_color[1]; + aTarget.b = m_color[2]; + aTarget.a = m_color[3]; + + // Apply currently used shader + for( unsigned int j = 0; j < ShaderStride; ++j ) + { + aTarget.shader[j] = m_shader[j]; + } +} diff --git a/common/gal/stroke_font.cpp b/common/gal/stroke_font.cpp new file mode 100644 index 0000000..636f75e --- /dev/null +++ b/common/gal/stroke_font.cpp @@ -0,0 +1,403 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * Copyright (C) 2013 CERN + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * Stroke font class + * + * 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 <gal/stroke_font.h> +#include <gal/graphics_abstraction_layer.h> +#include <wx/string.h> + +using namespace KIGFX; + +const double STROKE_FONT::INTERLINE_PITCH_RATIO = 1.5; +const double STROKE_FONT::OVERBAR_HEIGHT = 1.22; +const double STROKE_FONT::BOLD_FACTOR = 1.3; +const double STROKE_FONT::STROKE_FONT_SCALE = 1.0 / 21.0; +const double STROKE_FONT::ITALIC_TILT = 1.0 / 8; + +STROKE_FONT::STROKE_FONT( GAL* aGal ) : + m_gal( aGal ), + m_bold( false ), + m_italic( false ), + m_mirrored( false ), + m_overbar( false ) +{ + // Default values + m_glyphSize = VECTOR2D( 10.0, 10.0 ); + m_verticalJustify = GR_TEXT_VJUSTIFY_BOTTOM; + m_horizontalJustify = GR_TEXT_HJUSTIFY_LEFT; +} + + +bool STROKE_FONT::LoadNewStrokeFont( const char* const aNewStrokeFont[], int aNewStrokeFontSize ) +{ + m_glyphs.clear(); + m_glyphBoundingBoxes.clear(); + m_glyphs.resize( aNewStrokeFontSize ); + m_glyphBoundingBoxes.resize( aNewStrokeFontSize ); + + for( int j = 0; j < aNewStrokeFontSize; j++ ) + { + GLYPH glyph; + double glyphStartX = 0.0; + double glyphEndX = 0.0; + VECTOR2D glyphBoundingX; + + std::deque<VECTOR2D> pointList; + + int i = 0; + + while( aNewStrokeFont[j][i] ) + { + VECTOR2D point( 0.0, 0.0 ); + char coordinate[2] = { 0, }; + + for( int k = 0; k < 2; k++ ) + { + coordinate[k] = aNewStrokeFont[j][i + k]; + } + + if( i < 2 ) + { + // The first two values contain the width of the char + glyphStartX = ( coordinate[0] - 'R' ) * STROKE_FONT_SCALE; + glyphEndX = ( coordinate[1] - 'R' ) * STROKE_FONT_SCALE; + glyphBoundingX = VECTOR2D( 0, glyphEndX - glyphStartX ); + } + else if( ( coordinate[0] == ' ' ) && ( coordinate[1] == 'R' ) ) + { + // Raise pen + if( pointList.size() > 0 ) + glyph.push_back( pointList ); + + pointList.clear(); + } + else + { + // Every coordinate description of the Hershey format has an offset, + // it has to be subtracted + point.x = (double) ( coordinate[0] - 'R' ) * STROKE_FONT_SCALE - glyphStartX; + // -10 is here to keep GAL rendering consistent with the legacy gfx stuff + point.y = (double) ( coordinate[1] - 'R' - 10) * STROKE_FONT_SCALE; + pointList.push_back( point ); + } + + i += 2; + } + + if( pointList.size() > 0 ) + glyph.push_back( pointList ); + + m_glyphs[j] = glyph; + + // Compute the bounding box of the glyph + m_glyphBoundingBoxes[j] = computeBoundingBox( glyph, glyphBoundingX ); + } + + return true; +} + + +int STROKE_FONT::getInterline() const +{ + return KiROUND( m_glyphSize.y * INTERLINE_PITCH_RATIO ) + m_gal->GetLineWidth(); +} + + +BOX2D STROKE_FONT::computeBoundingBox( const GLYPH& aGLYPH, const VECTOR2D& aGLYPHBoundingX ) const +{ + BOX2D boundingBox; + + std::deque<VECTOR2D> boundingPoints; + + boundingPoints.push_back( VECTOR2D( aGLYPHBoundingX.x, 0 ) ); + boundingPoints.push_back( VECTOR2D( aGLYPHBoundingX.y, 0 ) ); + + for( GLYPH::const_iterator pointListIt = aGLYPH.begin(); pointListIt != aGLYPH.end(); ++pointListIt ) + { + for( std::deque<VECTOR2D>::const_iterator pointIt = pointListIt->begin(); + pointIt != pointListIt->end(); ++pointIt ) + { + boundingPoints.push_back( VECTOR2D( aGLYPHBoundingX.x, pointIt->y ) ); + } + } + + boundingBox.Compute( boundingPoints ); + + return boundingBox; +} + + +void STROKE_FONT::Draw( const UTF8& aText, const VECTOR2D& aPosition, double aRotationAngle ) +{ + if( aText.empty() ) + return; + + // Context needs to be saved before any transformations + m_gal->Save(); + + m_gal->Translate( aPosition ); + m_gal->Rotate( -aRotationAngle ); + + // Single line height + int lineHeight = getInterline( ); + int lineCount = linesCount( aText ); + + // align the 1st line of text + switch( m_verticalJustify ) + { + case GR_TEXT_VJUSTIFY_TOP: + m_gal->Translate( VECTOR2D( 0, m_glyphSize.y ) ); + break; + + case GR_TEXT_VJUSTIFY_CENTER: + m_gal->Translate( VECTOR2D( 0, m_glyphSize.y / 2.0 ) ); + break; + + case GR_TEXT_VJUSTIFY_BOTTOM: + break; + + default: + break; + } + + if( lineCount > 1 ) + { + switch( m_verticalJustify ) + { + case GR_TEXT_VJUSTIFY_TOP: + break; + + case GR_TEXT_VJUSTIFY_CENTER: + m_gal->Translate( VECTOR2D(0, -( lineCount - 1 ) * lineHeight / 2) ); + break; + + case GR_TEXT_VJUSTIFY_BOTTOM: + m_gal->Translate( VECTOR2D(0, -( lineCount - 1 ) * lineHeight ) ); + break; + } + } + + m_gal->SetIsStroke( true ); + m_gal->SetIsFill( false ); + + if( m_bold ) + m_gal->SetLineWidth( m_gal->GetLineWidth() * BOLD_FACTOR ); + + // Split multiline strings into separate ones and draw them line by line + size_t begin = 0; + size_t newlinePos = aText.find( '\n' ); + + while( newlinePos != aText.npos ) + { + size_t length = newlinePos - begin; + + drawSingleLineText( aText.substr( begin, length ) ); + m_gal->Translate( VECTOR2D( 0.0, lineHeight ) ); + + begin = newlinePos + 1; + newlinePos = aText.find( '\n', begin ); + } + + // Draw the last (or the only one) line + if( !aText.empty() ) + drawSingleLineText( aText.substr( begin ) ); + + m_gal->Restore(); +} + + +void STROKE_FONT::drawSingleLineText( const UTF8& aText ) +{ + // By default the overbar is turned off + m_overbar = false; + + double xOffset; + VECTOR2D glyphSize( m_glyphSize ); + double overbar_italic_comp = 0.0; + + // Compute the text size + VECTOR2D textSize = computeTextSize( aText ); + + m_gal->Save(); + + // Adjust the text position to the given alignment + switch( m_horizontalJustify ) + { + case GR_TEXT_HJUSTIFY_CENTER: + m_gal->Translate( VECTOR2D( -textSize.x / 2.0, 0 ) ); + break; + + case GR_TEXT_HJUSTIFY_RIGHT: + if( !m_mirrored ) + m_gal->Translate( VECTOR2D( -textSize.x, 0 ) ); + break; + + case GR_TEXT_HJUSTIFY_LEFT: + if( m_mirrored ) + m_gal->Translate( VECTOR2D( -textSize.x, 0 ) ); + break; + + default: + break; + } + + if( m_mirrored ) + { + // In case of mirrored text invert the X scale of points and their X direction + // (m_glyphSize.x) and start drawing from the position where text normally should end + // (textSize.x) + xOffset = textSize.x; + glyphSize.x = -m_glyphSize.x; + } + else + { + xOffset = 0.0; + } + + // The overbar is indented inward at the beginning of an italicized section, but + // must not be indented on subsequent letters to ensure that the bar segments + // overlap. + bool last_had_overbar = false; + + for( UTF8::uni_iter chIt = aText.ubegin(), end = aText.uend(); chIt < end; ++chIt ) + { + // Toggle overbar + if( *chIt == '~' ) + { + if( ++chIt >= end ) + break; + + if( *chIt != '~' ) // It was a single tilda, it toggles overbar + m_overbar = !m_overbar; + + // If it is a double tilda, just process the second one + } + + int dd = *chIt - ' '; + + if( dd >= (int) m_glyphBoundingBoxes.size() || dd < 0 ) + dd = '?' - ' '; + + GLYPH& glyph = m_glyphs[dd]; + BOX2D& bbox = m_glyphBoundingBoxes[dd]; + + if( m_overbar && m_italic ) + { + if( m_mirrored ) + { + overbar_italic_comp = (-m_glyphSize.y * OVERBAR_HEIGHT) / ITALIC_TILT; + } + else + { + overbar_italic_comp = (m_glyphSize.y * OVERBAR_HEIGHT) / ITALIC_TILT; + } + } + + if( m_overbar ) + { + double overbar_start_x = xOffset; + double overbar_start_y = -m_glyphSize.y * OVERBAR_HEIGHT; + double overbar_end_x = xOffset + glyphSize.x * bbox.GetEnd().x; + double overbar_end_y = -m_glyphSize.y * OVERBAR_HEIGHT; + + if( !last_had_overbar ) + { + overbar_start_x += overbar_italic_comp; + last_had_overbar = true; + } + + VECTOR2D startOverbar( overbar_start_x, overbar_start_y ); + VECTOR2D endOverbar( overbar_end_x, overbar_end_y ); + + m_gal->DrawLine( startOverbar, endOverbar ); + } + else + { + last_had_overbar = false; + } + + for( GLYPH::iterator pointListIt = glyph.begin(); pointListIt != glyph.end(); + ++pointListIt ) + { + std::deque<VECTOR2D> pointListScaled; + + for( std::deque<VECTOR2D>::iterator pointIt = pointListIt->begin(); + pointIt != pointListIt->end(); ++pointIt ) + { + VECTOR2D pointPos( pointIt->x * glyphSize.x + xOffset, pointIt->y * glyphSize.y ); + + if( m_italic ) + { + // FIXME should be done other way - referring to the lowest Y value of point + // because now italic fonts are translated a bit + if( m_mirrored ) + pointPos.x += pointPos.y * 0.1; + else + pointPos.x -= pointPos.y * 0.1; + } + + pointListScaled.push_back( pointPos ); + } + + m_gal->DrawPolyline( pointListScaled ); + } + + xOffset += glyphSize.x * bbox.GetEnd().x; + } + + m_gal->Restore(); +} + + +VECTOR2D STROKE_FONT::computeTextSize( const UTF8& aText ) const +{ + VECTOR2D result = VECTOR2D( 0.0, m_glyphSize.y ); + + for( UTF8::uni_iter it = aText.ubegin(), end = aText.uend(); it < end; ++it ) + { + wxASSERT_MSG( *it != '\n', + wxT( "This function is intended to work with single line strings" ) ); + + // If it is double tilda, then it is displayed as a single tilda + // If it is single tilda, then it is toggling overbar, so we need to skip it + if( *it == '~' ) + { + if( ++it >= end ) + break; + } + + // Index in the bounding boxes table + int dd = *it - ' '; + + if( dd >= (int) m_glyphBoundingBoxes.size() || dd < 0 ) + dd = '?' - ' '; + + result.x += m_glyphSize.x * m_glyphBoundingBoxes[dd].GetEnd().x; + } + + return result; +} diff --git a/common/geometry/hetriang.cpp b/common/geometry/hetriang.cpp new file mode 100644 index 0000000..e5cf26c --- /dev/null +++ b/common/geometry/hetriang.cpp @@ -0,0 +1,739 @@ +/* + * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT, + * Applied Mathematics, Norway. + * Copyright (C) 2013 CERN + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * Contact information: E-mail: tor.dokken@sintef.no + * SINTEF ICT, Department of Applied Mathematics, + * P.O. Box 124 Blindern, + * 0314 Oslo, Norway. + * + * This file is part of TTL. + * + * TTL is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * TTL 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with TTL. If not, see + * <http://www.gnu.org/licenses/>. + * + * In accordance with Section 7(b) of the GNU Affero General Public + * License, a covered work must retain the producer line in every data + * file that is created or manipulated using TTL. + * + * Other Usage + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial activities involving the TTL library without + * disclosing the source code of your own applications. + * + * This file may be used in accordance with the terms contained in a + * written agreement between you and SINTEF ICT. + */ + +#include <ttl/halfedge/hetriang.h> +#include <ttl/halfedge/hetraits.h> +#include <ttl/ttl.h> +#include <algorithm> +#include <fstream> +#include <limits> +#include <boost/foreach.hpp> +#include <boost/make_shared.hpp> +#include <class_board_connected_item.h> + +using namespace hed; + +#ifdef TTL_USE_NODE_ID + int NODE::id_count = 0; +#endif + + +void NODE::updateLayers() +{ + assert( m_layers.none() ); + + BOOST_FOREACH( const BOARD_CONNECTED_ITEM* item, m_parents ) + m_layers |= item->GetLayerSet(); +} + + +//#define DEBUG_HE +#ifdef DEBUG_HE +#include <iostream> +static void errorAndExit( char* aMessage ) +{ + cout << "\n!!! ERROR: "<< aMessage << " !!!\n" << endl; + exit( -1 ); +} +#endif + + +static EDGE_PTR getLeadingEdgeInTriangle( const EDGE_PTR& aEdge ) +{ + EDGE_PTR edge = aEdge; + + // Code: 3EF (assumes triangle) + if( !edge->IsLeadingEdge() ) + { + edge = edge->GetNextEdgeInFace(); + + if( !edge->IsLeadingEdge() ) + edge = edge->GetNextEdgeInFace(); + } + + if( !edge->IsLeadingEdge() ) + { + return EDGE_PTR(); + } + + return edge; +} + + +static void getLimits( NODES_CONTAINER::iterator aFirst, NODES_CONTAINER::iterator aLast, + int& aXmin, int& aYmin, int& aXmax, int& aYmax) +{ + aXmin = aYmin = std::numeric_limits<int>::min(); + aXmax = aYmax = std::numeric_limits<int>::max(); + + NODES_CONTAINER::iterator it; + + for( it = aFirst; it != aLast; ++it ) + { + aXmin = std::min( aXmin, ( *it )->GetX() ); + aYmin = std::min( aYmin, ( *it )->GetY() ); + aXmax = std::max( aXmax, ( *it )->GetX() ); + aYmax = std::max( aYmax, ( *it )->GetY() ); + } +} + + +EDGE_PTR TRIANGULATION::InitTwoEnclosingTriangles( NODES_CONTAINER::iterator aFirst, + NODES_CONTAINER::iterator aLast) +{ + int xmin, ymin, xmax, ymax; + getLimits( aFirst, aLast, xmin, ymin, xmax, ymax ); + + // Add 10% of range: + double fac = 10.0; + double dx = ( xmax - xmin ) / fac; + double dy = ( ymax - ymin ) / fac; + + NODE_PTR n1 = boost::make_shared<NODE>( xmin - dx, ymin - dy ); + NODE_PTR n2 = boost::make_shared<NODE>( xmax + dx, ymin - dy ); + NODE_PTR n3 = boost::make_shared<NODE>( xmax + dx, ymax + dy ); + NODE_PTR n4 = boost::make_shared<NODE>( xmin - dx, ymax + dy ); + + // diagonal + EDGE_PTR e1d = boost::make_shared<EDGE>(); + EDGE_PTR e2d = boost::make_shared<EDGE>(); + + // lower triangle + EDGE_PTR e11 = boost::make_shared<EDGE>(); + EDGE_PTR e12 = boost::make_shared<EDGE>(); + + // upper triangle + EDGE_PTR e21 = boost::make_shared<EDGE>(); + EDGE_PTR e22 = boost::make_shared<EDGE>(); + + // lower triangle + e1d->SetSourceNode( n3 ); + e1d->SetNextEdgeInFace( e11 ); + e1d->SetTwinEdge( e2d ); + addLeadingEdge( e1d ); + + e11->SetSourceNode( n1 ); + e11->SetNextEdgeInFace( e12 ); + + e12->SetSourceNode( n2 ); + e12->SetNextEdgeInFace( e1d ); + + // upper triangle + e2d->SetSourceNode( n1 ); + e2d->SetNextEdgeInFace( e21 ); + e2d->SetTwinEdge( e1d ); + addLeadingEdge( e2d ); + + e21->SetSourceNode( n3 ); + e21->SetNextEdgeInFace( e22 ); + + e22->SetSourceNode( n4 ); + e22->SetNextEdgeInFace( e2d ); + + return e11; +} + + +TRIANGULATION::TRIANGULATION() +{ + m_helper = new ttl::TRIANGULATION_HELPER( *this ); +} + + +TRIANGULATION::TRIANGULATION( const TRIANGULATION& aTriangulation ) +{ + m_helper = 0; // make coverity and static analysers quiet. + // Triangulation: Copy constructor not present + assert( false ); +} + + +TRIANGULATION::~TRIANGULATION() +{ + cleanAll(); + delete m_helper; +} + + +void TRIANGULATION::CreateDelaunay( NODES_CONTAINER::iterator aFirst, + NODES_CONTAINER::iterator aLast ) +{ + cleanAll(); + + EDGE_PTR bedge = InitTwoEnclosingTriangles( aFirst, aLast ); + DART dc( bedge ); + + DART d_iter = dc; + + NODES_CONTAINER::iterator it; + for( it = aFirst; it != aLast; ++it ) + { + m_helper->InsertNode<TTLtraits>( d_iter, *it ); + } + + // In general (e.g. for the triangle based data structure), the initial dart + // may have been changed. + // It is the users responsibility to get a valid boundary dart here. + // The half-edge data structure preserves the initial dart. + // (A dart at the boundary can also be found by trying to locate a + // triangle "outside" the triangulation.) + + // Assumes rectangular domain + m_helper->RemoveRectangularBoundary<TTLtraits>( dc ); +} + + +void TRIANGULATION::RemoveTriangle( EDGE_PTR& aEdge ) +{ + EDGE_PTR e1 = getLeadingEdgeInTriangle( aEdge ); + +#ifdef DEBUG_HE + if( !e1 ) + errorAndExit( "Triangulation::removeTriangle: could not find leading aEdge" ); +#endif + + removeLeadingEdgeFromList( e1 ); + // cout << "No leading edges = " << leadingEdges_.size() << endl; + // Remove the triangle + EDGE_PTR e2( e1->GetNextEdgeInFace() ); + EDGE_PTR e3( e2->GetNextEdgeInFace() ); + + e1->Clear(); + e2->Clear(); + e3->Clear(); +} + + +void TRIANGULATION::ReverseSplitTriangle( EDGE_PTR& aEdge ) +{ + // Reverse operation of splitTriangle + EDGE_PTR e1( aEdge->GetNextEdgeInFace() ); + EDGE_PTR le( getLeadingEdgeInTriangle( e1 ) ); +#ifdef DEBUG_HE + if (!le) + errorAndExit("Triangulation::removeTriangle: could not find leading edge"); +#endif + removeLeadingEdgeFromList( le ); + + EDGE_PTR e2( e1->GetNextEdgeInFace()->GetTwinEdge()->GetNextEdgeInFace() ); + le = getLeadingEdgeInTriangle( e2 ); +#ifdef DEBUG_HE + if (!le) + errorAndExit("Triangulation::removeTriangle: could not find leading edge"); +#endif + removeLeadingEdgeFromList( le ); + + EDGE_PTR e3( aEdge->GetTwinEdge()->GetNextEdgeInFace()->GetNextEdgeInFace() ); + le = getLeadingEdgeInTriangle( e3 ); +#ifdef DEBUG_HE + if (!le) + errorAndExit("Triangulation::removeTriangle: could not find leading edge"); +#endif + removeLeadingEdgeFromList( le ); + + // The three triangles at the node have now been removed + // from the triangulation, but the arcs have not been deleted. + // Next delete the 6 half edges radiating from the node + // The node is maintained by handle and need not be deleted explicitly + EDGE_PTR estar = aEdge; + EDGE_PTR enext = estar->GetTwinEdge()->GetNextEdgeInFace(); + estar->GetTwinEdge()->Clear(); + estar->Clear(); + + estar = enext; + enext = estar->GetTwinEdge()->GetNextEdgeInFace(); + estar->GetTwinEdge()->Clear(); + estar->Clear(); + + enext->GetTwinEdge()->Clear(); + enext->Clear(); + + // Create the new triangle + e1->SetNextEdgeInFace( e2 ); + e2->SetNextEdgeInFace( e3 ); + e3->SetNextEdgeInFace( e1 ); + addLeadingEdge( e1 ); +} + + +DART TRIANGULATION::CreateDart() +{ + // Return an arbitrary CCW dart + return DART( *m_leadingEdges.begin() ); +} + + +bool TRIANGULATION::removeLeadingEdgeFromList( EDGE_PTR& aLeadingEdge ) +{ + // Remove the edge from the list of leading edges, + // but don't delete it. + // Also set flag for leading edge to false. + // Must search from start of list. Since edges are added to the + // start of the list during triangulation, this operation will + // normally be fast (when used in the triangulation algorithm) + std::list<EDGE_PTR>::iterator it; + for( it = m_leadingEdges.begin(); it != m_leadingEdges.end(); ++it ) + { + EDGE_PTR edge = *it; + + if( edge == aLeadingEdge ) + { + edge->SetAsLeadingEdge( false ); + it = m_leadingEdges.erase( it ); + + return true; + } + } + + return false; +} + + +void TRIANGULATION::cleanAll() +{ + BOOST_FOREACH( EDGE_PTR& edge, m_leadingEdges ) + edge->SetNextEdgeInFace( EDGE_PTR() ); +} + + +void TRIANGULATION::swapEdge( DART& aDart ) +{ + SwapEdge( aDart.GetEdge() ); +} + + +void TRIANGULATION::splitTriangle( DART& aDart, const NODE_PTR& aPoint ) +{ + EDGE_PTR edge = SplitTriangle( aDart.GetEdge(), aPoint ); + aDart.Init( edge ); +} + + +void TRIANGULATION::reverseSplitTriangle( DART& aDart ) +{ + ReverseSplitTriangle( aDart.GetEdge() ); +} + + +void TRIANGULATION::removeBoundaryTriangle( DART& aDart ) +{ + RemoveTriangle( aDart.GetEdge() ); +} + + +#ifdef TTL_USE_NODE_FLAG +void TRIANGULATION::FlagNodes( bool aFlag ) const +{ + std::list<EDGE_PTR>::const_iterator it; + for( it = m_leadingEdges.begin(); it != m_leadingEdges.end(); ++it ) + { + EDGE_PTR edge = *it; + + for( int i = 0; i < 3; ++i ) + { + edge->GetSourceNode()->SetFlag( aFlag ); + edge = edge->GetNextEdgeInFace(); + } + } +} + + +std::list<NODE_PTR>* TRIANGULATION::GetNodes() const +{ + FlagNodes( false ); + std::list<NODE_PTR>* nodeList = new std::list<NODE_PTR>; + std::list<EDGE_PTR>::const_iterator it; + + for( it = m_leadingEdges.begin(); it != m_leadingEdges.end(); ++it ) + { + EDGE_PTR edge = *it; + + for( int i = 0; i < 3; ++i ) + { + const NODE_PTR& node = edge->GetSourceNode(); + + if( node->GetFlag() == false ) + { + nodeList->push_back( node ); + node->SetFlag( true ); + } + edge = edge->GetNextEdgeInFace(); + } + } + return nodeList; +} +#endif + + +std::list<EDGE_PTR>* TRIANGULATION::GetEdges( bool aSkipBoundaryEdges ) const +{ + // collect all arcs (one half edge for each arc) + // (boundary edges are also collected). + std::list<EDGE_PTR>::const_iterator it; + std::list<EDGE_PTR>* elist = new std::list<EDGE_PTR>; + + for( it = m_leadingEdges.begin(); it != m_leadingEdges.end(); ++it ) + { + EDGE_PTR edge = *it; + for( int i = 0; i < 3; ++i ) + { + EDGE_PTR twinedge = edge->GetTwinEdge(); + // only one of the half-edges + + if( ( !twinedge && !aSkipBoundaryEdges ) + || ( twinedge && ( (size_t) edge.get() > (size_t) twinedge.get() ) ) ) + elist->push_front( edge ); + + edge = edge->GetNextEdgeInFace(); + } + } + + return elist; +} + + +EDGE_PTR TRIANGULATION::SplitTriangle( EDGE_PTR& aEdge, const NODE_PTR& aPoint ) +{ + // Add a node by just splitting a triangle into three triangles + // Assumes the half aEdge is located in the triangle + // Returns a half aEdge with source node as the new node + + // e#_n are new edges + // e# are existing edges + // e#_n and e##_n are new twin edges + // e##_n are edges incident to the new node + + // Add the node to the structure + //NODE_PTR new_node(new Node(x,y,z)); + + NODE_PTR n1( aEdge->GetSourceNode() ); + EDGE_PTR e1( aEdge ); + + EDGE_PTR e2( aEdge->GetNextEdgeInFace() ); + NODE_PTR n2( e2->GetSourceNode() ); + + EDGE_PTR e3( e2->GetNextEdgeInFace() ); + NODE_PTR n3( e3->GetSourceNode() ); + + EDGE_PTR e1_n = boost::make_shared<EDGE>(); + EDGE_PTR e11_n = boost::make_shared<EDGE>(); + EDGE_PTR e2_n = boost::make_shared<EDGE>(); + EDGE_PTR e22_n = boost::make_shared<EDGE>(); + EDGE_PTR e3_n = boost::make_shared<EDGE>(); + EDGE_PTR e33_n = boost::make_shared<EDGE>(); + + e1_n->SetSourceNode( n1 ); + e11_n->SetSourceNode( aPoint ); + e2_n->SetSourceNode( n2 ); + e22_n->SetSourceNode( aPoint ); + e3_n->SetSourceNode( n3 ); + e33_n->SetSourceNode( aPoint ); + + e1_n->SetTwinEdge( e11_n ); + e11_n->SetTwinEdge( e1_n ); + e2_n->SetTwinEdge( e22_n ); + e22_n->SetTwinEdge( e2_n ); + e3_n->SetTwinEdge( e33_n ); + e33_n->SetTwinEdge( e3_n ); + + e1_n->SetNextEdgeInFace( e33_n ); + e2_n->SetNextEdgeInFace( e11_n ); + e3_n->SetNextEdgeInFace( e22_n ); + + e11_n->SetNextEdgeInFace( e1 ); + e22_n->SetNextEdgeInFace( e2 ); + e33_n->SetNextEdgeInFace( e3 ); + + // and update old's next aEdge + e1->SetNextEdgeInFace( e2_n ); + e2->SetNextEdgeInFace( e3_n ); + e3->SetNextEdgeInFace( e1_n ); + + // add the three new leading edges, + // Must remove the old leading aEdge from the list. + // Use the field telling if an aEdge is a leading aEdge + // NOTE: Must search in the list!!! + + if( e1->IsLeadingEdge() ) + removeLeadingEdgeFromList( e1 ); + else if( e2->IsLeadingEdge() ) + removeLeadingEdgeFromList( e2 ); + else if( e3->IsLeadingEdge() ) + removeLeadingEdgeFromList( e3 ); + else + assert( false ); // one of the edges should be leading + + addLeadingEdge( e1_n ); + addLeadingEdge( e2_n ); + addLeadingEdge( e3_n ); + + // Return a half aEdge incident to the new node (with the new node as source node) + + return e11_n; +} + + +void TRIANGULATION::SwapEdge( EDGE_PTR& aDiagonal ) +{ + // Note that diagonal is both input and output and it is always + // kept in counterclockwise direction (this is not required by all + // functions in TriangulationHelper now) + + // Swap by rotating counterclockwise + // Use the same objects - no deletion or new objects + EDGE_PTR eL( aDiagonal ); + EDGE_PTR eR( eL->GetTwinEdge() ); + EDGE_PTR eL_1( eL->GetNextEdgeInFace() ); + EDGE_PTR eL_2( eL_1->GetNextEdgeInFace() ); + EDGE_PTR eR_1( eR->GetNextEdgeInFace() ); + EDGE_PTR eR_2( eR_1->GetNextEdgeInFace() ); + + // avoid node to be dereferenced to zero and deleted + NODE_PTR nR( eR_2->GetSourceNode() ); + NODE_PTR nL( eL_2->GetSourceNode() ); + + eL->SetSourceNode( nR ); + eR->SetSourceNode( nL ); + + // and now 6 1-sewings + eL->SetNextEdgeInFace( eL_2 ); + eL_2->SetNextEdgeInFace( eR_1 ); + eR_1->SetNextEdgeInFace( eL ); + + eR->SetNextEdgeInFace( eR_2 ); + eR_2->SetNextEdgeInFace( eL_1 ); + eL_1->SetNextEdgeInFace( eR ); + + if( eL->IsLeadingEdge() ) + removeLeadingEdgeFromList( eL ); + else if( eL_1->IsLeadingEdge() ) + removeLeadingEdgeFromList( eL_1 ); + else if( eL_2->IsLeadingEdge() ) + removeLeadingEdgeFromList( eL_2 ); + + if( eR->IsLeadingEdge() ) + removeLeadingEdgeFromList( eR ); + else if( eR_1->IsLeadingEdge() ) + removeLeadingEdgeFromList( eR_1 ); + else if( eR_2->IsLeadingEdge() ) + removeLeadingEdgeFromList( eR_2 ); + + addLeadingEdge( eL ); + addLeadingEdge( eR ); +} + + +bool TRIANGULATION::CheckDelaunay() const +{ + // ???? outputs !!!! + // ofstream os("qweND.dat"); + const std::list<EDGE_PTR>& leadingEdges = GetLeadingEdges(); + + std::list<EDGE_PTR>::const_iterator it; + bool ok = true; + int noNotDelaunay = 0; + + for( it = leadingEdges.begin(); it != leadingEdges.end(); ++it ) + { + EDGE_PTR edge = *it; + + for( int i = 0; i < 3; ++i ) + { + EDGE_PTR twinedge = edge->GetTwinEdge(); + + // only one of the half-edges + if( !twinedge || (size_t) edge.get() > (size_t) twinedge.get() ) + { + DART dart( edge ); + if( m_helper->SwapTestDelaunay<TTLtraits>( dart ) ) + { + noNotDelaunay++; + + //printEdge(dart,os); os << "\n"; + ok = false; + //cout << "............. not Delaunay .... " << endl; + } + } + + edge = edge->GetNextEdgeInFace(); + } + } + +#ifdef DEBUG_HE + cout << "!!! Triangulation is NOT Delaunay: " << noNotDelaunay << " edges\n" << endl; +#endif + + return ok; +} + + +void TRIANGULATION::OptimizeDelaunay() +{ + // This function is also present in ttl where it is implemented + // generically. + // The implementation below is tailored for the half-edge data structure, + // and is thus more efficient + + // Collect all interior edges (one half edge for each arc) + bool skip_boundary_edges = true; + std::list<EDGE_PTR>* elist = GetEdges( skip_boundary_edges ); + + // Assumes that elist has only one half-edge for each arc. + bool cycling_check = true; + bool optimal = false; + std::list<EDGE_PTR>::const_iterator it; + + while( !optimal ) + { + optimal = true; + + for( it = elist->begin(); it != elist->end(); ++it ) + { + EDGE_PTR edge = *it; + + DART dart( edge ); + // Constrained edges should not be swapped + if( m_helper->SwapTestDelaunay<TTLtraits>( dart, cycling_check ) ) + { + optimal = false; + SwapEdge( edge ); + } + } + } + + delete elist; +} + + +EDGE_PTR TRIANGULATION::GetInteriorNode() const +{ + const std::list<EDGE_PTR>& leadingEdges = GetLeadingEdges(); + std::list<EDGE_PTR>::const_iterator it; + + for( it = leadingEdges.begin(); it != leadingEdges.end(); ++it ) + { + EDGE_PTR edge = *it; + + // multiple checks, but only until found + for( int i = 0; i < 3; ++i ) + { + if( edge->GetTwinEdge() ) + { + if( !m_helper->IsBoundaryNode( DART( edge ) ) ) + return edge; + } + + edge = edge->GetNextEdgeInFace(); + } + } + + return EDGE_PTR(); // no boundary nodes +} + + +EDGE_PTR TRIANGULATION::GetBoundaryEdgeInTriangle( const EDGE_PTR& aEdge ) const +{ + EDGE_PTR edge = aEdge; + + if( m_helper->IsBoundaryEdge( DART( edge ) ) ) + return edge; + + edge = edge->GetNextEdgeInFace(); + if( m_helper->IsBoundaryEdge( DART( edge ) ) ) + return edge; + + edge = edge->GetNextEdgeInFace(); + if( m_helper->IsBoundaryEdge( DART( edge ) ) ) + return edge; + + return EDGE_PTR(); +} + + +EDGE_PTR TRIANGULATION::GetBoundaryEdge() const +{ + // Get an arbitrary (CCW) boundary edge + // If the triangulation is closed, NULL is returned + const std::list<EDGE_PTR>& leadingEdges = GetLeadingEdges(); + std::list<EDGE_PTR>::const_iterator it; + EDGE_PTR edge; + + for( it = leadingEdges.begin(); it != leadingEdges.end(); ++it ) + { + edge = GetBoundaryEdgeInTriangle( *it ); + + if( edge ) + return edge; + } + return EDGE_PTR(); +} + + +void TRIANGULATION::PrintEdges( std::ofstream& aOutput ) const +{ + // Print source node and target node for each edge face by face, + // but only one of the half-edges. + const std::list<EDGE_PTR>& leadingEdges = GetLeadingEdges(); + std::list<EDGE_PTR>::const_iterator it; + + for( it = leadingEdges.begin(); it != leadingEdges.end(); ++it ) + { + EDGE_PTR edge = *it; + + for( int i = 0; i < 3; ++i ) + { + EDGE_PTR twinedge = edge->GetTwinEdge(); + + // Print only one edge (the highest value of the pointer) + if( !twinedge || (size_t) edge.get() > (size_t) twinedge.get() ) + { + // Print source node and target node + NODE_PTR node = edge->GetSourceNode(); + aOutput << node->GetX() << " " << node->GetY() << std::endl; + node = edge->GetTargetNode(); + aOutput << node->GetX() << " " << node->GetY() << std::endl; + aOutput << '\n'; // blank line + } + + edge = edge->GetNextEdgeInFace(); + } + } +} diff --git a/common/geometry/seg.cpp b/common/geometry/seg.cpp new file mode 100644 index 0000000..f1796b6 --- /dev/null +++ b/common/geometry/seg.cpp @@ -0,0 +1,158 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * + * 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 <geometry/seg.h> + +template <typename T> +int sgn( T aVal ) +{ + return ( T( 0 ) < aVal ) - ( aVal < T( 0 ) ); +} + + +bool SEG::PointCloserThan( const VECTOR2I& aP, int aDist ) const +{ + VECTOR2I d = B - A; + ecoord dist_sq = (ecoord) aDist * aDist; + + SEG::ecoord l_squared = d.Dot( d ); + SEG::ecoord t = d.Dot( aP - A ); + + if( t <= 0 || !l_squared ) + return ( aP - A ).SquaredEuclideanNorm() < dist_sq; + else if( t >= l_squared ) + return ( aP - B ).SquaredEuclideanNorm() < dist_sq; + + int dxdy = abs( d.x ) - abs( d.y ); + + if( ( dxdy >= -1 && dxdy <= 1 ) || abs( d.x ) <= 1 || abs( d.y ) <= 1 ) + { + int ca = -sgn( d.y ); + int cb = sgn( d.x ); + int cc = -ca * A.x - cb * A.y; + + ecoord num = (ecoord) ca * aP.x + (ecoord) cb * aP.y + cc; + num *= num; + + if( ca && cb ) + num >>= 1; + + if( num > ( dist_sq + 100 ) ) + return false; + + else if( num < ( dist_sq - 100 ) ) + return true; + } + + VECTOR2I nearest; + nearest.x = A.x + rescale( t, (ecoord) d.x, l_squared ); + nearest.y = A.y + rescale( t, (ecoord) d.y, l_squared ); + + return ( nearest - aP ).SquaredEuclideanNorm() <= dist_sq; +} + + +SEG::ecoord SEG::SquaredDistance( const SEG& aSeg ) const +{ + // fixme: rather inefficient.... + if( Intersect( aSeg ) ) + return 0; + + const VECTOR2I pts[4] = + { + aSeg.NearestPoint( A ) - A, + aSeg.NearestPoint( B ) - B, + NearestPoint( aSeg.A ) - aSeg.A, + NearestPoint( aSeg.B ) - aSeg.B + }; + + ecoord m = VECTOR2I::ECOORD_MAX; + + for( int i = 0; i < 4; i++ ) + m = std::min( m, pts[i].SquaredEuclideanNorm() ); + + return m; +} + + +OPT_VECTOR2I SEG::Intersect( const SEG& aSeg, bool aIgnoreEndpoints, bool aLines ) const +{ + const VECTOR2I e( B - A ); + const VECTOR2I f( aSeg.B - aSeg.A ); + const VECTOR2I ac( aSeg.A - A ); + + ecoord d = f.Cross( e ); + ecoord p = f.Cross( ac ); + ecoord q = e.Cross( ac ); + + if( d == 0 ) + return OPT_VECTOR2I(); + + if( !aLines && d > 0 && ( q < 0 || q > d || p < 0 || p > d ) ) + return OPT_VECTOR2I(); + + if( !aLines && d < 0 && ( q < d || p < d || p > 0 || q > 0 ) ) + return OPT_VECTOR2I(); + + if( !aLines && aIgnoreEndpoints && ( q == 0 || q == d ) && ( p == 0 || p == d ) ) + return OPT_VECTOR2I(); + + VECTOR2I ip( aSeg.A.x + rescale( q, (ecoord) f.x, d ), + aSeg.A.y + rescale( q, (ecoord) f.y, d ) ); + + return ip; +} + + +bool SEG::ccw( const VECTOR2I& aA, const VECTOR2I& aB, const VECTOR2I& aC ) const +{ + return (ecoord) ( aC.y - aA.y ) * ( aB.x - aA.x ) > (ecoord) ( aB.y - aA.y ) * ( aC.x - aA.x ); +} + + +bool SEG::Collide( const SEG& aSeg, int aClearance ) const +{ + // check for intersection + // fixme: move to a method + if( ccw( A, aSeg.A, aSeg.B ) != ccw( B, aSeg.A, aSeg.B ) && + ccw( A, B, aSeg.A ) != ccw( A, B, aSeg.B ) ) + return true; + +#define CHK( _seg, _pt ) \ + if( (_seg).PointCloserThan( _pt, aClearance ) ) return true; + + CHK( *this, aSeg.A ); + CHK( *this, aSeg.B ); + CHK( aSeg, A ); + CHK( aSeg, B ); +#undef CHK + + return false; +} + + +bool SEG::Contains( const VECTOR2I& aP ) const +{ + return PointCloserThan( aP, 1 ); +} diff --git a/common/geometry/shape.cpp b/common/geometry/shape.cpp new file mode 100644 index 0000000..0e312c5 --- /dev/null +++ b/common/geometry/shape.cpp @@ -0,0 +1,38 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 CERN + * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * + * 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 <geometry/shape.h> + +bool SHAPE::Parse( std::stringstream& aStream ) +{ + assert ( false ); + return false; +}; + +const std::string SHAPE::Format( ) const +{ + assert ( false ); + return std::string(""); +}; diff --git a/common/geometry/shape_collisions.cpp b/common/geometry/shape_collisions.cpp new file mode 100644 index 0000000..773dcc9 --- /dev/null +++ b/common/geometry/shape_collisions.cpp @@ -0,0 +1,496 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * + * 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 <math/vector2d.h> +#include <math.h> + +#include <geometry/shape.h> +#include <geometry/shape_line_chain.h> +#include <geometry/shape_circle.h> +#include <geometry/shape_rect.h> +#include <geometry/shape_segment.h> +#include <geometry/shape_convex.h> + +typedef VECTOR2I::extended_type ecoord; + +static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_CIRCLE& aB, int aClearance, + bool aNeedMTV, VECTOR2I& aMTV ) +{ + ecoord min_dist = aClearance + aA.GetRadius() + aB.GetRadius(); + ecoord min_dist_sq = min_dist * min_dist; + + const VECTOR2I delta = aB.GetCenter() - aA.GetCenter(); + + ecoord dist_sq = delta.SquaredEuclideanNorm(); + + if( dist_sq >= min_dist_sq ) + return false; + + if( aNeedMTV ) + aMTV = delta.Resize( min_dist - sqrt( dist_sq ) + 3 ); // fixme: apparent rounding error + + return true; +} + + +static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_CIRCLE& aB, int aClearance, + bool aNeedMTV, VECTOR2I& aMTV ) +{ + const VECTOR2I c = aB.GetCenter(); + const VECTOR2I p0 = aA.GetPosition(); + const VECTOR2I size = aA.GetSize(); + const int r = aB.GetRadius(); + const int min_dist = aClearance + r; + + const VECTOR2I vts[] = + { + VECTOR2I( p0.x, p0.y ), + VECTOR2I( p0.x, p0.y + size.y ), + VECTOR2I( p0.x + size.x, p0.y + size.y ), + VECTOR2I( p0.x + size.x, p0.y ), + VECTOR2I( p0.x, p0.y ) + }; + + int nearest_seg_dist = INT_MAX; + VECTOR2I nearest; + + bool inside = c.x >= p0.x && c.x <= ( p0.x + size.x ) + && c.y >= p0.y && c.y <= ( p0.y + size.y ); + + + if( !aNeedMTV && inside ) + return true; + + for( int i = 0; i < 4; i++ ) + { + const SEG seg( vts[i], vts[i + 1] ); + + VECTOR2I pn = seg.NearestPoint( c ); + + int d = ( pn - c ).EuclideanNorm(); + + if( ( d < min_dist ) && !aNeedMTV ) + return true; + + if( d < nearest_seg_dist ) + { + nearest = pn; + nearest_seg_dist = d; + } + } + + if( nearest_seg_dist >= min_dist && !inside ) + return false; + + VECTOR2I delta = c - nearest; + + if( !aNeedMTV ) + return true; + + + if( inside ) + aMTV = -delta.Resize( abs( min_dist + 1 + nearest_seg_dist ) + 1 ); + else + aMTV = delta.Resize( abs( min_dist + 1 - nearest_seg_dist ) + 1 ); + + + return true; +} + + +static VECTOR2I pushoutForce( const SHAPE_CIRCLE& aA, const SEG& aB, int aClearance ) +{ + VECTOR2I f( 0, 0 ); + + const VECTOR2I c = aA.GetCenter(); + const VECTOR2I nearest = aB.NearestPoint( c ); + + const int r = aA.GetRadius(); + + int dist = ( nearest - c ).EuclideanNorm(); + int min_dist = aClearance + r; + + if( dist < min_dist ) + { + for( int corr = 0; corr < 5; corr++ ) + { + f = ( aA.GetCenter() - nearest ).Resize( min_dist - dist + corr ); + + if( aB.Distance( c + f ) >= min_dist ) + break; + } + } + + return f; +} + + +static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_LINE_CHAIN& aB, int aClearance, + bool aNeedMTV, VECTOR2I& aMTV ) +{ + bool found = false; + + for( int s = 0; s < aB.SegmentCount(); s++ ) + { + if( aA.Collide( aB.CSegment( s ), aClearance ) ) + { + found = true; + break; + } + } + + if( !aNeedMTV || !found ) + return found; + + SHAPE_CIRCLE cmoved( aA ); + VECTOR2I f_total( 0, 0 ); + + for( int s = 0; s < aB.SegmentCount(); s++ ) + { + VECTOR2I f = pushoutForce( cmoved, aB.CSegment( s ), aClearance ); + cmoved.SetCenter( cmoved.GetCenter() + f ); + f_total += f; + } + + aMTV = f_total; + return found; +} + + +static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_CONVEX& aB, int aClearance, + bool aNeedMTV, VECTOR2I& aMTV ) +{ + bool found; + const SHAPE_LINE_CHAIN& lc( aB.Vertices() ); + + found = lc.Distance( aA.GetCenter() ) <= aClearance + aA.GetRadius(); + + if( !aNeedMTV || !found ) + return found; + + SHAPE_CIRCLE cmoved( aA ); + VECTOR2I f_total( 0, 0 ); + + for( int s = 0; s < lc.SegmentCount(); s++ ) + { + VECTOR2I f = pushoutForce( cmoved, lc.CSegment( s ), aClearance ); + cmoved.SetCenter( cmoved.GetCenter() + f ); + f_total += f; + } + + aMTV = f_total; + return found; +} + + +static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_SEGMENT& aSeg, int aClearance, + bool aNeedMTV, VECTOR2I& aMTV ) +{ + bool col = aA.Collide( aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2); + + if( col && aNeedMTV ) + { + aMTV = -pushoutForce( aA, aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2); + } + return col; +} + + +static inline bool Collide( const SHAPE_LINE_CHAIN& aA, const SHAPE_LINE_CHAIN& aB, int aClearance, + bool aNeedMTV, VECTOR2I& aMTV ) +{ + for( int i = 0; i < aB.SegmentCount(); i++ ) + if( aA.Collide( aB.CSegment( i ), aClearance ) ) + return true; + + return false; +} + + +static inline bool Collide( const SHAPE_LINE_CHAIN& aA, const SHAPE_CONVEX& aB, int aClearance, + bool aNeedMTV, VECTOR2I& aMTV ) +{ + return Collide( aA, aB.Vertices(), aClearance, aNeedMTV, aMTV ); +} + + +static inline bool Collide( const SHAPE_CONVEX& aA, const SHAPE_CONVEX& aB, int aClearance, + bool aNeedMTV, VECTOR2I& aMTV ) +{ + return Collide( aA.Vertices(), aB.Vertices(), aClearance, aNeedMTV, aMTV ); +} + + +static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_LINE_CHAIN& aB, int aClearance, + bool aNeedMTV, VECTOR2I& aMTV ) +{ + for( int s = 0; s < aB.SegmentCount(); s++ ) + { + SEG seg = aB.CSegment( s ); + + if( aA.Collide( seg, aClearance ) ) + return true; + } + + return false; +} + + +static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_CONVEX& aB, int aClearance, + bool aNeedMTV, VECTOR2I& aMTV ) +{ + return Collide( aA, aB.Vertices(), aClearance, aNeedMTV, aMTV ); +} + + +static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_SEGMENT& aSeg, int aClearance, + bool aNeedMTV, VECTOR2I& aMTV ) +{ + return aA.Collide( aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2 ); +} + + +static inline bool Collide( const SHAPE_SEGMENT& aA, const SHAPE_SEGMENT& aB, int aClearance, + bool aNeedMTV, VECTOR2I& aMTV ) +{ + return aA.Collide( aB.GetSeg(), aClearance + aB.GetWidth() / 2 ); +} + + +static inline bool Collide( const SHAPE_LINE_CHAIN& aA, const SHAPE_SEGMENT& aB, int aClearance, + bool aNeedMTV, VECTOR2I& aMTV ) +{ + if( aA.Collide( aB.GetSeg(), aClearance + aB.GetWidth() / 2 ) ) + return true; + + return false; +} + + +static inline bool Collide( const SHAPE_CONVEX& aA, const SHAPE_SEGMENT& aB, int aClearance, + bool aNeedMTV, VECTOR2I& aMTV ) +{ + return Collide( aA.Vertices(), aB, aClearance, aNeedMTV, aMTV ); +} + +static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_RECT& aB, int aClearance, + bool aNeedMTV, VECTOR2I& aMTV ) +{ + return Collide( aA.Outline(), aB.Outline(), aClearance, aNeedMTV, aMTV ); +} + + +template<class ShapeAType, class ShapeBType> +inline bool CollCase( const SHAPE* aA, const SHAPE* aB, int aClearance, bool aNeedMTV, VECTOR2I& aMTV ) +{ + return Collide (*static_cast<const ShapeAType*>( aA ), + *static_cast<const ShapeBType*>( aB ), + aClearance, aNeedMTV, aMTV); +} + +template<class ShapeAType, class ShapeBType> +inline bool CollCaseReversed ( const SHAPE* aA, const SHAPE* aB, int aClearance, bool aNeedMTV, VECTOR2I& aMTV ) +{ + bool rv = Collide (*static_cast<const ShapeBType*>( aB ), + *static_cast<const ShapeAType*>( aA ), + aClearance, aNeedMTV, aMTV); + if(rv && aNeedMTV) + aMTV = -aMTV; + return rv; +} + + +bool CollideShapes( const SHAPE* aA, const SHAPE* aB, int aClearance, bool aNeedMTV, VECTOR2I& aMTV ) +{ + switch( aA->Type() ) + { + case SH_RECT: + switch( aB->Type() ) + { + case SH_RECT: + return CollCase<SHAPE_RECT, SHAPE_RECT>( aA, aB, aClearance, aNeedMTV, aMTV ); + + case SH_CIRCLE: + return CollCase<SHAPE_RECT, SHAPE_CIRCLE>( aA, aB, aClearance, aNeedMTV, aMTV ); + + case SH_LINE_CHAIN: + return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aNeedMTV, aMTV ); + + case SH_SEGMENT: + return CollCase<SHAPE_RECT, SHAPE_SEGMENT>( aA, aB, aClearance, aNeedMTV, aMTV ); + + case SH_CONVEX: + return CollCase<SHAPE_RECT, SHAPE_CONVEX>( aA, aB, aClearance, aNeedMTV, aMTV ); + + default: + break; + } + break; + + case SH_CIRCLE: + switch( aB->Type() ) + { + case SH_RECT: + return CollCaseReversed<SHAPE_CIRCLE, SHAPE_RECT>( aA, aB, aClearance, aNeedMTV, aMTV ); + + case SH_CIRCLE: + return CollCase<SHAPE_CIRCLE, SHAPE_CIRCLE>( aA, aB, aClearance, aNeedMTV, aMTV ); + + case SH_LINE_CHAIN: + return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aNeedMTV, aMTV ); + + case SH_SEGMENT: + return CollCase<SHAPE_CIRCLE, SHAPE_SEGMENT>( aA, aB, aClearance, aNeedMTV, aMTV ); + + case SH_CONVEX: + return CollCase<SHAPE_CIRCLE, SHAPE_CONVEX>( aA, aB, aClearance, aNeedMTV, aMTV ); + + default: + break; + } + break; + + case SH_LINE_CHAIN: + switch( aB->Type() ) + { + case SH_RECT: + return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN>( aB, aA, aClearance, aNeedMTV, aMTV ); + + case SH_CIRCLE: + return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN>( aB, aA, aClearance, aNeedMTV, aMTV ); + + case SH_LINE_CHAIN: + return CollCase<SHAPE_LINE_CHAIN, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aNeedMTV, aMTV ); + + case SH_SEGMENT: + return CollCase<SHAPE_LINE_CHAIN, SHAPE_SEGMENT>( aA, aB, aClearance, aNeedMTV, aMTV ); + + case SH_CONVEX: + return CollCase<SHAPE_LINE_CHAIN, SHAPE_CONVEX>( aA, aB, aClearance, aNeedMTV, aMTV ); + + default: + break; + } + break; + + case SH_SEGMENT: + switch( aB->Type() ) + { + case SH_RECT: + return CollCase<SHAPE_RECT, SHAPE_SEGMENT>( aB, aA, aClearance, aNeedMTV, aMTV ); + + case SH_CIRCLE: + return CollCaseReversed<SHAPE_SEGMENT, SHAPE_CIRCLE>( aA, aB, aClearance, aNeedMTV, aMTV ); + + case SH_LINE_CHAIN: + return CollCase<SHAPE_LINE_CHAIN, SHAPE_SEGMENT>( aB, aA, aClearance, aNeedMTV, aMTV ); + + case SH_SEGMENT: + return CollCase<SHAPE_SEGMENT, SHAPE_SEGMENT>( aA, aB, aClearance, aNeedMTV, aMTV ); + + case SH_CONVEX: + return CollCase<SHAPE_CONVEX, SHAPE_SEGMENT>( aB, aA, aClearance, aNeedMTV, aMTV ); + + default: + break; + } + break; + + case SH_CONVEX: + switch( aB->Type() ) + { + case SH_RECT: + return CollCase<SHAPE_RECT, SHAPE_CONVEX>( aB, aA, aClearance, aNeedMTV, aMTV ); + + case SH_CIRCLE: + return CollCase<SHAPE_CIRCLE, SHAPE_CONVEX>( aB, aA, aClearance, aNeedMTV, aMTV ); + + case SH_LINE_CHAIN: + return CollCase<SHAPE_LINE_CHAIN, SHAPE_CONVEX>( aB, aA, aClearance, aNeedMTV, aMTV ); + + case SH_SEGMENT: + return CollCase<SHAPE_CONVEX, SHAPE_SEGMENT>( aA, aB, aClearance, aNeedMTV, aMTV ); + + case SH_CONVEX: + return CollCase<SHAPE_CONVEX, SHAPE_CONVEX>( aA, aB, aClearance, aNeedMTV, aMTV ); + + default: + break; + } + break; + + default: + break; + } + + bool unsupported_collision = true; + (void) unsupported_collision; // make gcc quiet + + assert( unsupported_collision == false ); + + return false; +} + + +bool SHAPE::Collide( const SHAPE* aShape, int aClerance, VECTOR2I& aMTV ) const +{ + return CollideShapes( this, aShape, aClerance, true, aMTV ); +} + + +bool SHAPE::Collide( const SHAPE* aShape, int aClerance ) const +{ + VECTOR2I dummy; + + return CollideShapes( this, aShape, aClerance, false, dummy ); +} + +bool SHAPE_RECT::Collide( const SEG& aSeg, int aClearance ) const + { + //VECTOR2I pmin = VECTOR2I( std::min( aSeg.a.x, aSeg.b.x ), std::min( aSeg.a.y, aSeg.b.y ) ); + //VECTOR2I pmax = VECTOR2I( std::max( aSeg.a.x, aSeg.b.x ), std::max( aSeg.a.y, aSeg.b.y )); + //BOX2I r( pmin, VECTOR2I( pmax.x - pmin.x, pmax.y - pmin.y ) ); + + //if( BBox( 0 ).SquaredDistance( r ) > aClearance * aClearance ) + // return false; + + if( BBox( 0 ).Contains( aSeg.A ) || BBox( 0 ).Contains( aSeg.B ) ) + return true; + + VECTOR2I vts[] = { VECTOR2I( m_p0.x, m_p0.y ), + VECTOR2I( m_p0.x, m_p0.y + m_h ), + VECTOR2I( m_p0.x + m_w, m_p0.y + m_h ), + VECTOR2I( m_p0.x + m_w, m_p0.y ), + VECTOR2I( m_p0.x, m_p0.y ) }; + + for( int i = 0; i < 4; i++ ) + { + SEG s( vts[i], vts[i + 1], i ); + + if( s.Distance( aSeg ) < aClearance ) + return true; + } + + return false; + } diff --git a/common/geometry/shape_file_io.cpp b/common/geometry/shape_file_io.cpp new file mode 100644 index 0000000..a87fd74 --- /dev/null +++ b/common/geometry/shape_file_io.cpp @@ -0,0 +1,146 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * + * 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 <string> +#include <cassert> + +#include <geometry/shape.h> +#include <geometry/shape_file_io.h> + +SHAPE_FILE_IO::SHAPE_FILE_IO( const std::string& aFilename, SHAPE_FILE_IO::IO_MODE aMode ) +{ + m_groupActive = false; + + if( aFilename.length() ) + { + switch( aMode ) + { + case IOM_READ: m_file = fopen( aFilename.c_str(), "rb" ); break; + case IOM_WRITE: m_file = fopen( aFilename.c_str(), "wb" ); break; + case IOM_APPEND: m_file = fopen( aFilename.c_str(), "ab" ); break; + default: + return; + } + } + else + { + m_file = NULL; + } + + m_mode = aMode; + // fixme: exceptions +} + + +SHAPE_FILE_IO::~SHAPE_FILE_IO() +{ + if( !m_file ) + return; + + if( m_groupActive && m_mode != IOM_READ ) + fprintf( m_file, "endgroup\n" ); + + fclose( m_file ); +} + + +SHAPE* SHAPE_FILE_IO::Read() +{ + /* char tmp[1024]; + + do { + + if (fscanf(m_file, "%s", tmp) != 1) + return NULL; + + if( !strcmp( tmp, "shape" ) + break; + } + + int type; + + SHAPE *rv = NULL; + + fscanf(m_file,"%d %s", &type, tmp); + + printf("create shape %d\n", type); + + switch(type) + { + case SHAPE::LINE_CHAIN: + rv = new SHAPE_LINE_CHAIN; + break; + } + + if(!rv) + return NULL; + + rv.Parse ( ) + + fprintf(m_file,"shape %d %s %s\n", aShape->Type(), aName.c_str(), sh.c_str() ); +*/ + assert( false ); + return NULL; +} + + +void SHAPE_FILE_IO::BeginGroup( const std::string aName ) +{ + assert( m_mode != IOM_READ ); + + if( !m_file ) + return; + + fprintf( m_file, "group %s\n", aName.c_str() ); + m_groupActive = true; +} + + +void SHAPE_FILE_IO::EndGroup() +{ + assert( m_mode != IOM_READ ); + + if( !m_file || !m_groupActive ) + return; + + fprintf( m_file, "endgroup\n" ); + m_groupActive = false; +} + + +void SHAPE_FILE_IO::Write( const SHAPE* aShape, const std::string aName ) +{ + assert( m_mode != IOM_READ ); + + if( !m_file ) + return; + + if( !m_groupActive ) + fprintf( m_file,"group default\n" ); + + std::string sh = aShape->Format(); + + fprintf( m_file, "shape %d %s %s\n", aShape->Type(), aName.c_str(), sh.c_str() ); + fflush( m_file ); +}
\ No newline at end of file diff --git a/common/geometry/shape_line_chain.cpp b/common/geometry/shape_line_chain.cpp new file mode 100644 index 0000000..a097b0b --- /dev/null +++ b/common/geometry/shape_line_chain.cpp @@ -0,0 +1,580 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * + * 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 <geometry/shape_line_chain.h> +#include <geometry/shape_circle.h> + +using boost::optional; + +bool SHAPE_LINE_CHAIN::Collide( const VECTOR2I& aP, int aClearance ) const +{ + // fixme: ugly! + SEG s( aP, aP ); + return this->Collide( s, aClearance ); +} + + +bool SHAPE_LINE_CHAIN::Collide( const SEG& aSeg, int aClearance ) const +{ + BOX2I box_a( aSeg.A, aSeg.B - aSeg.A ); + BOX2I::ecoord_type dist_sq = (BOX2I::ecoord_type) aClearance * aClearance; + + for( int i = 0; i < SegmentCount(); i++ ) + { + const SEG& s = CSegment( i ); + BOX2I box_b( s.A, s.B - s.A ); + + BOX2I::ecoord_type d = box_a.SquaredDistance( box_b ); + + if( d < dist_sq ) + { + if( s.Collide( aSeg, aClearance ) ) + return true; + } + } + + return false; +} + + +const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Reverse() const +{ + SHAPE_LINE_CHAIN a( *this ); + + reverse( a.m_points.begin(), a.m_points.end() ); + a.m_closed = m_closed; + + return a; +} + + +int SHAPE_LINE_CHAIN::Length() const +{ + int l = 0; + + for( int i = 0; i < SegmentCount(); i++ ) + l += CSegment( i ).Length(); + + return l; +} + + +void SHAPE_LINE_CHAIN::Replace( int aStartIndex, int aEndIndex, const VECTOR2I& aP ) +{ + if( aEndIndex < 0 ) + aEndIndex += PointCount(); + + if( aStartIndex < 0 ) + aStartIndex += PointCount(); + + if( aStartIndex == aEndIndex ) + m_points[aStartIndex] = aP; + else + { + m_points.erase( m_points.begin() + aStartIndex + 1, m_points.begin() + aEndIndex + 1 ); + m_points[aStartIndex] = aP; + } +} + + +void SHAPE_LINE_CHAIN::Replace( int aStartIndex, int aEndIndex, const SHAPE_LINE_CHAIN& aLine ) +{ + if( aEndIndex < 0 ) + aEndIndex += PointCount(); + + if( aStartIndex < 0 ) + aStartIndex += PointCount(); + + m_points.erase( m_points.begin() + aStartIndex, m_points.begin() + aEndIndex + 1 ); + m_points.insert( m_points.begin() + aStartIndex, aLine.m_points.begin(), aLine.m_points.end() ); +} + + +void SHAPE_LINE_CHAIN::Remove( int aStartIndex, int aEndIndex ) +{ + if( aEndIndex < 0 ) + aEndIndex += PointCount(); + + if( aStartIndex < 0 ) + aStartIndex += PointCount(); + + m_points.erase( m_points.begin() + aStartIndex, m_points.begin() + aEndIndex + 1 ); +} + + +int SHAPE_LINE_CHAIN::Distance( const VECTOR2I& aP ) const +{ + int d = INT_MAX; + + if( IsClosed() && PointInside( aP ) ) + return 0; + + for( int s = 0; s < SegmentCount(); s++ ) + d = std::min( d, CSegment( s ).Distance( aP ) ); + + return d; +} + + +int SHAPE_LINE_CHAIN::Split( const VECTOR2I& aP ) +{ + int ii = -1; + int min_dist = 2; + + int found_index = Find( aP ); + + for( int s = 0; s < SegmentCount(); s++ ) + { + const SEG seg = CSegment( s ); + int dist = seg.Distance( aP ); + + // make sure we are not producing a 'slightly concave' primitive. This might happen + // if aP lies very close to one of already existing points. + if( dist < min_dist && seg.A != aP && seg.B != aP ) + { + min_dist = dist; + if( found_index < 0 ) + ii = s; + else if( s < found_index ) + ii = s; + } + } + + if( ii < 0 ) + ii = found_index; + + if( ii >= 0 ) + { + m_points.insert( m_points.begin() + ii + 1, aP ); + + return ii + 1; + } + + return -1; +} + + +int SHAPE_LINE_CHAIN::Find( const VECTOR2I& aP ) const +{ + for( int s = 0; s < PointCount(); s++ ) + if( CPoint( s ) == aP ) + return s; + + return -1; +} + + +int SHAPE_LINE_CHAIN::FindSegment( const VECTOR2I& aP ) const +{ + for( int s = 0; s < SegmentCount(); s++ ) + if( CSegment( s ).Distance( aP ) <= 1 ) + return s; + + return -1; +} + + +const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Slice( int aStartIndex, int aEndIndex ) const +{ + SHAPE_LINE_CHAIN rv; + + if( aEndIndex < 0 ) + aEndIndex += PointCount(); + + if( aStartIndex < 0 ) + aStartIndex += PointCount(); + + for( int i = aStartIndex; i <= aEndIndex; i++ ) + rv.Append( m_points[i] ); + + return rv; +} + + +struct compareOriginDistance +{ + compareOriginDistance( VECTOR2I& aOrigin ) : + m_origin( aOrigin ) {}; + + bool operator()( const SHAPE_LINE_CHAIN::INTERSECTION& aA, + const SHAPE_LINE_CHAIN::INTERSECTION& aB ) + { + return ( m_origin - aA.p ).EuclideanNorm() < ( m_origin - aB.p ).EuclideanNorm(); + } + + VECTOR2I m_origin; +}; + + +int SHAPE_LINE_CHAIN::Intersect( const SEG& aSeg, INTERSECTIONS& aIp ) const +{ + for( int s = 0; s < SegmentCount(); s++ ) + { + OPT_VECTOR2I p = CSegment( s ).Intersect( aSeg ); + + if( p ) + { + INTERSECTION is; + is.our = CSegment( s ); + is.their = aSeg; + is.p = *p; + aIp.push_back( is ); + } + } + + compareOriginDistance comp( aSeg.A ); + sort( aIp.begin(), aIp.end(), comp ); + + return aIp.size(); +} + + +int SHAPE_LINE_CHAIN::Intersect( const SHAPE_LINE_CHAIN& aChain, INTERSECTIONS& aIp ) const +{ + BOX2I bb_other = aChain.BBox(); + + for( int s1 = 0; s1 < SegmentCount(); s1++ ) + { + const SEG& a = CSegment( s1 ); + const BOX2I bb_cur( a.A, a.B - a.A ); + + if( !bb_other.Intersects( bb_cur ) ) + continue; + + for( int s2 = 0; s2 < aChain.SegmentCount(); s2++ ) + { + const SEG& b = aChain.CSegment( s2 ); + INTERSECTION is; + + if( a.Collinear( b ) ) + { + is.our = a; + is.their = b; + + if( a.Contains( b.A ) ) { is.p = b.A; aIp.push_back( is ); } + if( a.Contains( b.B ) ) { is.p = b.B; aIp.push_back( is ); } + if( b.Contains( a.A ) ) { is.p = a.A; aIp.push_back( is ); } + if( b.Contains( a.B ) ) { is.p = a.B; aIp.push_back( is ); } + } + else + { + OPT_VECTOR2I p = a.Intersect( b ); + + if( p ) + { + is.p = *p; + is.our = a; + is.their = b; + aIp.push_back( is ); + } + } + } + } + + return aIp.size(); +} + + +int SHAPE_LINE_CHAIN::PathLength( const VECTOR2I& aP ) const +{ + int sum = 0; + + for( int i = 0; i < SegmentCount(); i++ ) + { + const SEG seg = CSegment( i ); + int d = seg.Distance( aP ); + + if( d <= 1 ) + { + sum += ( aP - seg.A ).EuclideanNorm(); + return sum; + } + else + sum += seg.Length(); + } + + return -1; +} + + +bool SHAPE_LINE_CHAIN::PointInside( const VECTOR2I& aP ) const +{ + if( !m_closed || SegmentCount() < 3 ) + return false; + + int cur = CSegment( 0 ).Side( aP ); + + if( cur == 0 ) + return false; + + for( int i = 1; i < SegmentCount(); i++ ) + { + const SEG s = CSegment( i ); + + if( aP == s.A || aP == s.B ) // edge does not belong to the interior! + return false; + + if( s.Side( aP ) != cur ) + return false; + } + + return true; +} + + +bool SHAPE_LINE_CHAIN::PointOnEdge( const VECTOR2I& aP ) const +{ + if( !PointCount() ) + return false; + + else if( PointCount() == 1 ) + return m_points[0] == aP; + + for( int i = 0; i < SegmentCount(); i++ ) + { + const SEG s = CSegment( i ); + + if( s.A == aP || s.B == aP ) + return true; + + if( s.Distance( aP ) <= 1 ) + return true; + } + + return false; +} + + +const optional<SHAPE_LINE_CHAIN::INTERSECTION> SHAPE_LINE_CHAIN::SelfIntersecting() const +{ + for( int s1 = 0; s1 < SegmentCount(); s1++ ) + { + for( int s2 = s1 + 1; s2 < SegmentCount(); s2++ ) + { + const VECTOR2I s2a = CSegment( s2 ).A, s2b = CSegment( s2 ).B; + + if( s1 + 1 != s2 && CSegment( s1 ).Contains( s2a ) ) + { + INTERSECTION is; + is.our = CSegment( s1 ); + is.their = CSegment( s2 ); + is.p = s2a; + return is; + } + else if( CSegment( s1 ).Contains( s2b ) ) + { + INTERSECTION is; + is.our = CSegment( s1 ); + is.their = CSegment( s2 ); + is.p = s2b; + return is; + } + else + { + OPT_VECTOR2I p = CSegment( s1 ).Intersect( CSegment( s2 ), true ); + + if( p ) + { + INTERSECTION is; + is.our = CSegment( s1 ); + is.their = CSegment( s2 ); + is.p = *p; + return is; + } + } + } + } + + return optional<INTERSECTION>(); +} + + +SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify() +{ + std::vector<VECTOR2I> pts_unique; + + if( PointCount() < 2 ) + { + return *this; + } + else if( PointCount() == 2 ) + { + if( m_points[0] == m_points[1] ) + m_points.pop_back(); + + return *this; + } + + int i = 0; + int np = PointCount(); + + // stage 1: eliminate duplicate vertices + while( i < np ) + { + int j = i + 1; + + while( j < np && CPoint( i ) == CPoint( j ) ) + j++; + + pts_unique.push_back( CPoint( i ) ); + i = j; + } + + m_points.clear(); + np = pts_unique.size(); + + i = 0; + + // stage 1: eliminate collinear segments + while( i < np - 2 ) + { + const VECTOR2I p0 = pts_unique[i]; + const VECTOR2I p1 = pts_unique[i + 1]; + int n = i; + + while( n < np - 2 && SEG( p0, p1 ).LineDistance( pts_unique[n + 2] ) <= 1 ) + n++; + + m_points.push_back( p0 ); + + if( n > i ) + i = n; + + if( n == np ) + { + m_points.push_back( pts_unique[n - 1] ); + return *this; + } + + i++; + } + + if( np > 1 ) + m_points.push_back( pts_unique[np - 2] ); + + m_points.push_back( pts_unique[np - 1] ); + + return *this; +} + + +const VECTOR2I SHAPE_LINE_CHAIN::NearestPoint( const VECTOR2I& aP ) const +{ + int min_d = INT_MAX; + int nearest = 0; + + for( int i = 0; i < SegmentCount(); i++ ) + { + int d = CSegment( i ).Distance( aP ); + + if( d < min_d ) + { + min_d = d; + nearest = i; + } + } + + return CSegment( nearest ).NearestPoint( aP ); +} + + +const VECTOR2I SHAPE_LINE_CHAIN::NearestPoint( const SEG& aSeg, int& dist ) const +{ + int nearest = 0; + + dist = INT_MAX; + for( int i = 0; i < PointCount(); i++ ) + { + int d = aSeg.LineDistance( CPoint( i ) ); + + if( d < dist ) + { + dist = d; + nearest = i; + } + } + + return CPoint( nearest ); +} + + +const std::string SHAPE_LINE_CHAIN::Format() const +{ + std::stringstream ss; + + ss << m_points.size() << " " << ( m_closed ? 1 : 0 ) << " "; + + for( int i = 0; i < PointCount(); i++ ) + ss << m_points[i].x << " " << m_points[i].y << " "; // Format() << " "; + + return ss.str(); +} + + +bool SHAPE_LINE_CHAIN::CompareGeometry ( const SHAPE_LINE_CHAIN & aOther ) const +{ + SHAPE_LINE_CHAIN a(*this), b( aOther ); + a.Simplify(); + b.Simplify(); + + if( a.m_points.size() != b.m_points.size() ) + return false; + + for( int i = 0; i < a.PointCount(); i++) + if( a.CPoint( i ) != b.CPoint( i ) ) + return false; + return true; +} + + +bool SHAPE_LINE_CHAIN::Intersects( const SHAPE_LINE_CHAIN& aChain ) const +{ + INTERSECTIONS dummy; + return Intersect( aChain, dummy ) != 0; +} + + +SHAPE* SHAPE_LINE_CHAIN::Clone() const +{ + return new SHAPE_LINE_CHAIN( *this ); +} + +bool SHAPE_LINE_CHAIN::Parse( std::stringstream& aStream ) +{ + int n_pts; + + m_points.clear(); + aStream >> n_pts; + aStream >> m_closed; + + for( int i = 0; i < n_pts; i++ ) + { + int x, y; + aStream >> x; + aStream >> y; + m_points.push_back( VECTOR2I( x, y ) ); + } + + return true; +}
\ No newline at end of file diff --git a/common/geometry/shape_poly_set.cpp b/common/geometry/shape_poly_set.cpp new file mode 100644 index 0000000..ce28926 --- /dev/null +++ b/common/geometry/shape_poly_set.cpp @@ -0,0 +1,805 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 CERN + * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * + * Point in polygon algorithm adapted from Clipper Library (C) Angus Johnson, + * subject to Clipper library license. + * + * 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 <vector> +#include <cstdio> +#include <set> +#include <list> +#include <algorithm> + +#include <boost/foreach.hpp> + +#include <geometry/shape.h> +#include <geometry/shape_line_chain.h> +#include <geometry/shape_poly_set.h> + +using namespace ClipperLib; + +SHAPE_POLY_SET::SHAPE_POLY_SET() : + SHAPE( SH_POLY_SET ) +{ + +} + + +SHAPE_POLY_SET::~SHAPE_POLY_SET() +{ +} + + +int SHAPE_POLY_SET::NewOutline() +{ + SHAPE_LINE_CHAIN empty_path; + POLYGON poly; + poly.push_back( empty_path ); + m_polys.push_back( poly ); + return m_polys.size() - 1; +} + + +int SHAPE_POLY_SET::NewHole( int aOutline ) +{ + m_polys.back().push_back( SHAPE_LINE_CHAIN() ); + + return m_polys.back().size() - 2; +} + + +int SHAPE_POLY_SET::Append( int x, int y, int aOutline, int aHole ) +{ + if( aOutline < 0 ) + aOutline += m_polys.size(); + + int idx; + + if( aHole < 0 ) + idx = 0; + else + idx = aHole + 1; + + assert( aOutline < (int)m_polys.size() ); + assert( idx < (int)m_polys[aOutline].size() ); + + m_polys[aOutline][idx].Append( x, y ); + + return m_polys[aOutline][idx].PointCount(); +} + + +int SHAPE_POLY_SET::VertexCount( int aOutline , int aHole ) const +{ + if( aOutline < 0 ) + aOutline += m_polys.size(); + + int idx; + + if( aHole < 0 ) + idx = 0; + else + idx = aHole + 1; + + assert ( aOutline < (int)m_polys.size() ); + assert ( idx < (int)m_polys[aOutline].size() ); + + return m_polys[aOutline][idx].PointCount(); +} + + +const VECTOR2I& SHAPE_POLY_SET::CVertex( int index, int aOutline , int aHole ) const +{ + if( aOutline < 0 ) + aOutline += m_polys.size(); + + int idx; + + if( aHole < 0 ) + idx = 0; + else + idx = aHole + 1; + + assert( aOutline < (int)m_polys.size() ); + assert( idx < (int)m_polys[aOutline].size() ); + + return m_polys[aOutline][idx].CPoint( index ); +} + + +VECTOR2I& SHAPE_POLY_SET::Vertex( int index, int aOutline , int aHole ) +{ + if( aOutline < 0 ) + aOutline += m_polys.size(); + + int idx; + + if( aHole < 0 ) + idx = 0; + else + idx = aHole + 1; + + assert( aOutline < (int)m_polys.size() ); + assert( idx < (int)m_polys[aOutline].size() ); + + return m_polys[aOutline][idx].Point( index ); +} + + +int SHAPE_POLY_SET::AddOutline( const SHAPE_LINE_CHAIN& aOutline ) +{ + assert( aOutline.IsClosed() ); + + POLYGON poly; + + poly.push_back( aOutline ); + + m_polys.push_back( poly ); + + return m_polys.size() - 1; +} + + +int SHAPE_POLY_SET::AddHole( const SHAPE_LINE_CHAIN& aHole, int aOutline ) +{ + assert ( m_polys.size() ); + + if( aOutline < 0 ) + aOutline += m_polys.size(); + + POLYGON& poly = m_polys[aOutline]; + + assert( poly.size() ); + + poly.push_back( aHole ); + + return poly.size() - 1; +} + + +const Path SHAPE_POLY_SET::convertToClipper( const SHAPE_LINE_CHAIN& aPath, bool aRequiredOrientation ) +{ + Path c_path; + + for( int i = 0; i < aPath.PointCount(); i++ ) + { + const VECTOR2I& vertex = aPath.CPoint( i ); + c_path.push_back( IntPoint( vertex.x, vertex.y ) ); + } + + if( Orientation( c_path ) != aRequiredOrientation ) + ReversePath( c_path ); + + return c_path; +} + + +const SHAPE_LINE_CHAIN SHAPE_POLY_SET::convertFromClipper( const Path& aPath ) +{ + SHAPE_LINE_CHAIN lc; + + for( unsigned int i = 0; i < aPath.size(); i++ ) + lc.Append( aPath[i].X, aPath[i].Y ); + + return lc; +} + +void SHAPE_POLY_SET::booleanOp( ClipType aType, const SHAPE_POLY_SET& aOtherShape, + bool aFastMode ) +{ + Clipper c; + + if( !aFastMode ) + c.StrictlySimple( true ); + + BOOST_FOREACH( const POLYGON& poly, m_polys ) + { + for( unsigned int i = 0; i < poly.size(); i++ ) + c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), ptSubject, true ); + } + + BOOST_FOREACH( const POLYGON& poly, aOtherShape.m_polys ) + { + for( unsigned int i = 0; i < poly.size(); i++ ) + c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), ptClip, true ); + } + + PolyTree solution; + + c.Execute( aType, solution, pftNonZero, pftNonZero ); + + importTree( &solution ); +} + + +void SHAPE_POLY_SET::booleanOp( ClipperLib::ClipType aType, + const SHAPE_POLY_SET& aShape, + const SHAPE_POLY_SET& aOtherShape, + bool aFastMode ) +{ + Clipper c; + + if( !aFastMode ) + c.StrictlySimple( true ); + + BOOST_FOREACH( const POLYGON& poly, aShape.m_polys ) + { + for( unsigned int i = 0; i < poly.size(); i++ ) + c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), ptSubject, true ); + } + + BOOST_FOREACH( const POLYGON& poly, aOtherShape.m_polys ) + { + for( unsigned int i = 0; i < poly.size(); i++ ) + c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), ptClip, true ); + } + + PolyTree solution; + + c.Execute( aType, solution, pftNonZero, pftNonZero ); + + importTree( &solution ); +} + + +void SHAPE_POLY_SET::BooleanAdd( const SHAPE_POLY_SET& b, bool aFastMode ) +{ + booleanOp( ctUnion, b, aFastMode ); +} + + +void SHAPE_POLY_SET::BooleanSubtract( const SHAPE_POLY_SET& b, bool aFastMode ) +{ + booleanOp( ctDifference, b, aFastMode ); +} + + +void SHAPE_POLY_SET::BooleanIntersection( const SHAPE_POLY_SET& b, bool aFastMode ) +{ + booleanOp( ctIntersection, b, aFastMode ); +} + + +void SHAPE_POLY_SET::BooleanAdd( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, bool aFastMode ) +{ + booleanOp( ctUnion, a, b, aFastMode ); +} + + +void SHAPE_POLY_SET::BooleanSubtract( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, bool aFastMode ) +{ + booleanOp( ctDifference, a, b, aFastMode ); +} + + +void SHAPE_POLY_SET::BooleanIntersection( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, bool aFastMode ) +{ + booleanOp( ctIntersection, a, b, aFastMode ); +} + + +void SHAPE_POLY_SET::Inflate( int aFactor, int aCircleSegmentsCount ) +{ + ClipperOffset c; + + BOOST_FOREACH( const POLYGON& poly, m_polys ) + { + for( unsigned int i = 0; i < poly.size(); i++ ) + c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), jtRound, etClosedPolygon ); + } + + PolyTree solution; + + c.ArcTolerance = fabs( (double) aFactor ) / M_PI / aCircleSegmentsCount; + + c.Execute( solution, aFactor ); + + importTree( &solution ); +} + + +void SHAPE_POLY_SET::importTree( PolyTree* tree) +{ + m_polys.clear(); + + for( PolyNode* n = tree->GetFirst(); n; n = n->GetNext() ) + { + if( !n->IsHole() ) + { + POLYGON paths; + paths.push_back( convertFromClipper( n->Contour ) ); + + for( unsigned int i = 0; i < n->Childs.size(); i++ ) + paths.push_back( convertFromClipper( n->Childs[i]->Contour ) ); + + m_polys.push_back(paths); + } + } +} + +// Polygon fracturing code. Work in progress. + +struct FractureEdge +{ + FractureEdge( bool connected, SHAPE_LINE_CHAIN* owner, int index ) : + m_connected( connected ), + m_next( NULL ) + { + m_p1 = owner->CPoint( index ); + m_p2 = owner->CPoint( index + 1 ); + } + + FractureEdge( int y = 0 ) : + m_connected( false ), + m_next( NULL ) + { + m_p1.x = m_p2.y = y; + } + + FractureEdge( bool connected, const VECTOR2I& p1, const VECTOR2I& p2 ) : + m_connected( connected ), + m_p1( p1 ), + m_p2( p2 ), + m_next( NULL ) + { + } + + bool matches( int y ) const + { + int y_min = std::min( m_p1.y, m_p2.y ); + int y_max = std::max( m_p1.y, m_p2.y ); + + return ( y >= y_min ) && ( y <= y_max ); + } + + bool m_connected; + VECTOR2I m_p1, m_p2; + FractureEdge* m_next; +}; + + +typedef std::vector<FractureEdge*> FractureEdgeSet; + +static int processEdge( FractureEdgeSet& edges, FractureEdge* edge ) +{ + int x = edge->m_p1.x; + int y = edge->m_p1.y; + int min_dist = std::numeric_limits<int>::max(); + int x_nearest = 0; + + FractureEdge* e_nearest = NULL; + + for( FractureEdgeSet::iterator i = edges.begin(); i != edges.end(); ++i ) + { + if( !(*i)->matches( y ) ) + continue; + + int x_intersect; + + if( (*i)->m_p1.y == (*i)->m_p2.y ) // horizontal edge + x_intersect = std::max ( (*i)->m_p1.x, (*i)->m_p2.x ); + else + x_intersect = (*i)->m_p1.x + rescale((*i)->m_p2.x - (*i)->m_p1.x, y - (*i)->m_p1.y, (*i)->m_p2.y - (*i)->m_p1.y ); + + int dist = ( x - x_intersect ); + + if( dist >= 0 && dist < min_dist && (*i)->m_connected ) + { + min_dist = dist; + x_nearest = x_intersect; + e_nearest = (*i); + } + } + + if( e_nearest && e_nearest->m_connected ) + { + int count = 0; + + FractureEdge* lead1 = new FractureEdge( true, VECTOR2I( x_nearest, y ), VECTOR2I( x, y ) ); + FractureEdge* lead2 = new FractureEdge( true, VECTOR2I( x, y ), VECTOR2I( x_nearest, y ) ); + FractureEdge* split_2 = new FractureEdge( true, VECTOR2I( x_nearest, y ), e_nearest->m_p2 ); + + edges.push_back( split_2 ); + edges.push_back( lead1 ); + edges.push_back( lead2 ); + + FractureEdge* link = e_nearest->m_next; + + e_nearest->m_p2 = VECTOR2I( x_nearest, y ); + e_nearest->m_next = lead1; + lead1->m_next = edge; + + FractureEdge*last; + for( last = edge; last->m_next != edge; last = last->m_next ) + { + last->m_connected = true; + count++; + } + + last->m_connected = true; + last->m_next = lead2; + lead2->m_next = split_2; + split_2->m_next = link; + + return count + 1; + } + + return 0; +} + +void SHAPE_POLY_SET::fractureSingle( POLYGON& paths ) +{ + FractureEdgeSet edges; + FractureEdgeSet border_edges; + FractureEdge* root = NULL; + + bool first = true; + + if( paths.size() == 1 ) + return; + + int num_unconnected = 0; + + BOOST_FOREACH( SHAPE_LINE_CHAIN& path, paths ) + { + int index = 0; + + FractureEdge *prev = NULL, *first_edge = NULL; + + int x_min = std::numeric_limits<int>::max(); + + for( int i = 0; i < path.PointCount(); i++ ) + { + const VECTOR2I& p = path.CPoint( i ); + + if( p.x < x_min ) + x_min = p.x; + } + + for( int i = 0; i < path.PointCount(); i++ ) + { + FractureEdge* fe = new FractureEdge( first, &path, index++ ); + + if( !root ) + root = fe; + + if( !first_edge ) + first_edge = fe; + + if( prev ) + prev->m_next = fe; + + if( i == path.PointCount() - 1 ) + fe->m_next = first_edge; + + prev = fe; + edges.push_back( fe ); + + if( !first ) + { + if( fe->m_p1.x == x_min ) + border_edges.push_back( fe ); + } + + if( !fe->m_connected ) + num_unconnected++; + } + first = false; // first path is always the outline + } + + // keep connecting holes to the main outline, until there's no holes left... + while( num_unconnected > 0 ) + { + int x_min = std::numeric_limits<int>::max(); + + FractureEdge* smallestX = NULL; + + // find the left-most hole edge and merge with the outline + for( FractureEdgeSet::iterator i = border_edges.begin(); i != border_edges.end(); ++i ) + { + int xt = (*i)->m_p1.x; + + if( ( xt < x_min ) && ! (*i)->m_connected ) + { + x_min = xt; + smallestX = *i; + } + } + + num_unconnected -= processEdge( edges, smallestX ); + } + + paths.clear(); + SHAPE_LINE_CHAIN newPath; + + newPath.SetClosed( true ); + + FractureEdge* e; + + for( e = root; e->m_next != root; e = e->m_next ) + newPath.Append( e->m_p1 ); + + newPath.Append( e->m_p1 ); + + for( FractureEdgeSet::iterator i = edges.begin(); i != edges.end(); ++i ) + delete *i; + + paths.push_back( newPath ); +} + + +void SHAPE_POLY_SET::Fracture( bool aFastMode ) +{ + Simplify( aFastMode ); // remove overlapping holes/degeneracy + + BOOST_FOREACH( POLYGON& paths, m_polys ) + { + fractureSingle( paths ); + } +} + + +void SHAPE_POLY_SET::Simplify( bool aFastMode ) +{ + SHAPE_POLY_SET empty; + + booleanOp( ctUnion, empty, aFastMode ); +} + + +const std::string SHAPE_POLY_SET::Format() const +{ + std::stringstream ss; + + ss << "polyset " << m_polys.size() << "\n"; + + for( unsigned i = 0; i < m_polys.size(); i++ ) + { + ss << "poly " << m_polys[i].size() << "\n"; + for( unsigned j = 0; j < m_polys[i].size(); j++) + { + ss << m_polys[i][j].PointCount() << "\n"; + for( int v = 0; v < m_polys[i][j].PointCount(); v++) + ss << m_polys[i][j].CPoint( v ).x << " " << m_polys[i][j].CPoint( v ).y << "\n"; + } + ss << "\n"; + } + + return ss.str(); +} + + +bool SHAPE_POLY_SET::Parse( std::stringstream& aStream ) +{ + std::string tmp; + + aStream >> tmp; + + if( tmp != "polyset" ) + return false; + + aStream >> tmp; + + int n_polys = atoi( tmp.c_str() ); + + if( n_polys < 0 ) + return false; + + for( int i = 0; i < n_polys; i++ ) + { + POLYGON paths; + + aStream >> tmp; + + if( tmp != "poly" ) + return false; + + aStream >> tmp; + int n_outlines = atoi( tmp.c_str() ); + + if( n_outlines < 0 ) + return false; + + for( int j = 0; j < n_outlines; j++ ) + { + SHAPE_LINE_CHAIN outline; + + outline.SetClosed( true ); + + aStream >> tmp; + int n_vertices = atoi( tmp.c_str() ); + for( int v = 0; v < n_vertices; v++ ) + { + VECTOR2I p; + + aStream >> tmp; p.x = atoi( tmp.c_str() ); + aStream >> tmp; p.y = atoi( tmp.c_str() ); + outline.Append( p ); + } + + paths.push_back( outline ); + } + + m_polys.push_back( paths ); + } + return true; +} + + +const BOX2I SHAPE_POLY_SET::BBox( int aClearance ) const +{ + BOX2I bb; + + for( unsigned i = 0; i < m_polys.size(); i++ ) + { + if( i == 0 ) + bb = m_polys[i][0].BBox(); + else + bb.Merge( m_polys[i][0].BBox() ); + } + + bb.Inflate( aClearance ); + return bb; +} + + +void SHAPE_POLY_SET::RemoveAllContours() +{ + m_polys.clear(); +} + + +void SHAPE_POLY_SET::DeletePolygon( int aIdx ) +{ + m_polys.erase( m_polys.begin() + aIdx ); +} + + +void SHAPE_POLY_SET::Append( const SHAPE_POLY_SET& aSet ) +{ + m_polys.insert( m_polys.end(), aSet.m_polys.begin(), aSet.m_polys.end() ); +} + + +void SHAPE_POLY_SET::Append( const VECTOR2I& aP, int aOutline, int aHole ) +{ + Append( aP.x, aP.y, aOutline, aHole ); +} + + +bool SHAPE_POLY_SET::Contains( const VECTOR2I& aP, int aSubpolyIndex ) const +{ + // fixme: support holes! + + if( m_polys.size() == 0 ) // empty set? + return false; + + if( aSubpolyIndex >= 0 ) + return pointInPolygon( aP, m_polys[aSubpolyIndex][0] ); + + BOOST_FOREACH ( const POLYGON& polys, m_polys ) + { + if( polys.size() == 0 ) + continue; + + if( pointInPolygon( aP, polys[0] ) ) + return true; + } + + return false; +} + + +bool SHAPE_POLY_SET::pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& aPath ) const +{ + int result = 0; + int cnt = aPath.PointCount(); + + if ( !aPath.BBox().Contains( aP ) ) // test with bounding box first + return false; + + if( cnt < 3 ) + return false; + + VECTOR2I ip = aPath.CPoint( 0 ); + + for( int i = 1; i <= cnt; ++i ) + { + VECTOR2I ipNext = ( i == cnt ? aPath.CPoint( 0 ) : aPath.CPoint( i ) ); + + if( ipNext.y == aP.y ) + { + if( ( ipNext.x == aP.x ) || ( ip.y == aP.y && + ( ( ipNext.x > aP.x ) == ( ip.x < aP.x ) ) ) ) + return true; + } + + if( ( ip.y < aP.y ) != ( ipNext.y < aP.y ) ) + { + if( ip.x >= aP.x ) + { + if( ipNext.x > aP.x ) + result = 1 - result; + else + { + int64_t d = (int64_t)( ip.x - aP.x ) * (int64_t)( ipNext.y - aP.y ) - + (int64_t)( ipNext.x - aP.x ) * (int64_t)( ip.y - aP.y ); + + if( !d ) + return true; + + if( ( d > 0 ) == ( ipNext.y > ip.y ) ) + result = 1 - result; + } + } + else + { + if( ipNext.x > aP.x ) + { + int64_t d = (int64_t)( ip.x - aP.x ) * (int64_t)( ipNext.y - aP.y ) - + (int64_t)( ipNext.x - aP.x ) * (int64_t)( ip.y - aP.y ); + + if( !d ) + return true; + + if( ( d > 0 ) == ( ipNext.y > ip.y ) ) + result = 1 - result; + } + } + } + + ip = ipNext; + } + + return result ? true : false; +} + + +void SHAPE_POLY_SET::Move( const VECTOR2I& aVector ) +{ + BOOST_FOREACH( POLYGON &poly, m_polys ) + { + BOOST_FOREACH( SHAPE_LINE_CHAIN &path, poly ) + { + path.Move( aVector ); + } + } +} + + +int SHAPE_POLY_SET::TotalVertices() const +{ + int c = 0; + + BOOST_FOREACH( const POLYGON& poly, m_polys ) + { + BOOST_FOREACH ( const SHAPE_LINE_CHAIN& path, poly ) + { + c += path.PointCount(); + } + } + + return c; +} diff --git a/common/gestfich.cpp b/common/gestfich.cpp new file mode 100644 index 0000000..3a09576 --- /dev/null +++ b/common/gestfich.cpp @@ -0,0 +1,420 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com + * Copyright (C) 2008-2011 Wayne Stambaugh <stambaughw@verizon.net> + * Copyright (C) 1992-2011 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 + */ + +/** + * @file gestfich.cpp + * @brief Functions for file management + */ + +#include <wx/mimetype.h> +#include <wx/filename.h> +#include <wx/dir.h> + +// For compilers that support precompilation, includes "wx.h". +#include <fctsys.h> +#include <pgm_base.h> +#include <confirm.h> +#include <common.h> +#include <macros.h> + +#include <gestfich.h> + +void AddDelimiterString( wxString& string ) +{ + if( !string.StartsWith( wxT( "\"" ) ) ) + { + string.Prepend ( wxT( "\"" ) ); + string.Append ( wxT( "\"" ) ); + } +} + + +bool EDA_PATH_SELECTOR( const wxString& aTitle, + wxString& aPath, + int aFlags, + wxWindow* aParent, + const wxPoint& aPosition ) +{ + int ii; + bool selected = false; + + wxDirDialog* DirFrame = new wxDirDialog( aParent, + aTitle, + aPath, + aFlags, + aPosition ); + + ii = DirFrame->ShowModal(); + + if( ii == wxID_OK ) + { + aPath = DirFrame->GetPath(); + selected = true; + } + + DirFrame->Destroy(); + return selected; +} + + +wxString EDA_FILE_SELECTOR( const wxString& aTitle, + const wxString& aPath, + const wxString& aFileName, + const wxString& aExtension, + const wxString& aWildcard, + wxWindow* aParent, + int aStyle, + const bool aKeepWorkingDirectory, + const wxPoint& aPosition, + wxString* aMruPath ) +{ + wxString fullfilename; + wxString curr_cwd = wxGetCwd(); + wxString defaultname = aFileName; + wxString defaultpath = aPath; + wxString dotted_Ext = wxT(".") + aExtension; + +#ifdef __WINDOWS__ + defaultname.Replace( wxT( "/" ), wxT( "\\" ) ); + defaultpath.Replace( wxT( "/" ), wxT( "\\" ) ); +#endif + + if( defaultpath.IsEmpty() ) + { + if( aMruPath == NULL ) + defaultpath = wxGetCwd(); + else + defaultpath = *aMruPath; + } + + wxSetWorkingDirectory( defaultpath ); + +#if 0 && defined (DEBUG) + printf( "defaultpath=\"%s\" defaultname=\"%s\" Ext=\"%s\" Mask=\"%s\" flag=%d keep_working_directory=%d\n", + TO_UTF8( defaultpath ), + TO_UTF8( defaultname ), + TO_UTF8( aExtension ), + TO_UTF8( aWildcard ), + aStyle, + aKeepWorkingDirectory ); +#endif + + fullfilename = wxFileSelector( aTitle, + defaultpath, + defaultname, + dotted_Ext, + aWildcard, + aStyle, // open mode wxFD_OPEN, wxFD_SAVE .. + aParent, + aPosition.x, aPosition.y ); + + if( aKeepWorkingDirectory ) + wxSetWorkingDirectory( curr_cwd ); + + if( !fullfilename.IsEmpty() && aMruPath ) + { + wxFileName fn = fullfilename; + *aMruPath = fn.GetPath(); + } + + return fullfilename; +} + + +wxString FindKicadFile( const wxString& shortname ) +{ + // Test the presence of the file in the directory shortname of + // the KiCad binary path. +#ifndef __WXMAC__ + wxString fullFileName = Pgm().GetExecutablePath() + shortname; +#else + wxString fullFileName = Pgm().GetExecutablePath() + wxT( "Contents/MacOS/" ) + shortname; +#endif + if( wxFileExists( fullFileName ) ) + return fullFileName; + + // Test the presence of the file in the directory shortname + // defined by the environment variable KiCad. + if( Pgm().IsKicadEnvVariableDefined() ) + { + fullFileName = Pgm().GetKicadEnvVariable() + shortname; + + if( wxFileExists( fullFileName ) ) + return fullFileName; + } + + // Path list for KiCad binary files + const static wxChar* possibilities[] = { +#if defined( __WINDOWS__ ) + wxT( "c:/kicad/bin/" ), + wxT( "d:/kicad/bin/" ), + wxT( "c:/Program Files/kicad/bin/" ), + wxT( "d:/Program Files/kicad/bin/" ), +#elif defined( __WXMAC__ ) + // all internal paths are relative to main bundle kicad.app + wxT( "Contents/Applications/pcbnew.app/Contents/MacOS/" ), + wxT( "Contents/Applications/eeschema.app/Contents/MacOS/" ), + wxT( "Contents/Applications/gerbview.app/Contents/MacOS/" ), + wxT( "Contents/Applications/bitmap2component.app/Contents/MacOS/" ), + wxT( "Contents/Applications/pcb_calculator.app/Contents/MacOS/" ), + wxT( "Contents/Applications/pl_editor.app/Contents/MacOS/" ), +#else + wxT( "/usr/bin/" ), + wxT( "/usr/local/bin/" ), + wxT( "/usr/local/kicad/bin/" ), +#endif + }; + + // find binary file from possibilities list: + for( unsigned i=0; i<DIM(possibilities); ++i ) + { +#ifndef __WXMAC__ + fullFileName = possibilities[i] + shortname; +#else + // make relative paths absolute + fullFileName = Pgm().GetExecutablePath() + possibilities[i] + shortname; +#endif + + if( wxFileExists( fullFileName ) ) + return fullFileName; + } + + return shortname; +} + + +int ExecuteFile( wxWindow* frame, const wxString& ExecFile, const wxString& param, + wxProcess *callback ) +{ + wxString fullFileName = FindKicadFile( ExecFile ); + + if( wxFileExists( fullFileName ) ) + { + if( !param.IsEmpty() ) + fullFileName += wxT( " " ) + param; + + return ProcessExecute( fullFileName, wxEXEC_ASYNC, callback ); + } +#ifdef __WXMAC__ + else + { + AddDelimiterString( fullFileName ); + + if( !param.IsEmpty() ) + fullFileName += wxT( " " ) + param; + + return ProcessExecute( wxT( "/usr/bin/open -a " ) + fullFileName, wxEXEC_ASYNC, callback ); + } +#endif + + wxString msg; + msg.Printf( _( "Command <%s> could not found" ), GetChars( fullFileName ) ); + DisplayError( frame, msg, 20 ); + return -1; +} + + +wxString KicadDatasPath() +{ + bool found = false; + wxString data_path; + + if( Pgm().IsKicadEnvVariableDefined() ) // Path defined by the KICAD environment variable. + { + data_path = Pgm().GetKicadEnvVariable(); + found = true; + } + else // Path of executables. + { +#ifndef __WXMAC__ + wxString tmp = Pgm().GetExecutablePath(); +#ifdef __WINDOWS__ + tmp.MakeLower(); +#endif + if( tmp.Contains( wxT( "kicad" ) ) ) + { +#ifdef __WINDOWS__ + tmp = Pgm().GetExecutablePath(); +#endif + if( tmp.Last() == '/' ) + tmp.RemoveLast(); + + data_path = tmp.BeforeLast( '/' ); // id cd ../ + data_path += UNIX_STRING_DIR_SEP; + + // Old versions of KiCad use kicad/ as default for data + // and last versions kicad/share/ + // So we search for kicad/share/ first + wxString old_path = data_path; + data_path += wxT( "share/" ); + + if( wxDirExists( data_path ) ) + { + found = true; + } + else if( wxDirExists( old_path ) ) + { + data_path = old_path; + found = true; + } + } + } + + if( !found ) + { + // find KiCad from possibilities list: + // /usr/local/kicad/ or c:/kicad/ + + const static wxChar* possibilities[] = { +#ifdef __WINDOWS__ + wxT( "c:/kicad/share/" ), + wxT( "d:/kicad/share/" ), + wxT( "c:/kicad/" ), + wxT( "d:/kicad/" ), + wxT( "c:/Program Files/kicad/share/" ), + wxT( "d:/Program Files/kicad/share/" ), + wxT( "c:/Program Files/kicad/" ), + wxT( "d:/Program Files/kicad/" ), +#else + wxT( "/usr/share/kicad/" ), + wxT( "/usr/local/share/kicad/" ), + wxT( "/usr/local/kicad/share/" ), // default data path for "universal + // tarballs" and build for a server + // (new) + wxT( "/usr/local/kicad/" ), // default data path for "universal + // tarballs" and build for a server + // (old) +#endif + }; + + for( unsigned i=0; i<DIM(possibilities); ++i ) + { + data_path = possibilities[i]; + + if( wxDirExists( data_path ) ) + { + found = true; + break; + } + } +#else + // On OSX point to Contents/SharedSupport folder of main bundle + data_path = GetOSXKicadDataDir(); + found = true; +#endif + } + + if( found ) + { + data_path.Replace( WIN_STRING_DIR_SEP, UNIX_STRING_DIR_SEP ); + + if( data_path.Last() != '/' ) + data_path += UNIX_STRING_DIR_SEP; + } + else + { + data_path.Empty(); + } + + return data_path; +} + + +bool OpenPDF( const wxString& file ) +{ + wxString command; + wxString filename = file; + + Pgm().ReadPdfBrowserInfos(); + + if( !Pgm().UseSystemPdfBrowser() ) // Run the preferred PDF Browser + { + AddDelimiterString( filename ); + command = Pgm().GetPdfBrowserName() + wxT( " " ) + filename; + } + else + { + wxFileType* filetype = wxTheMimeTypesManager->GetFileTypeFromExtension( wxT( "pdf" ) ); + + if( filetype ) + command = filetype->GetOpenCommand( filename ); + + delete filetype; + } + + if( !command.IsEmpty() ) + { + if( ProcessExecute( command ) ) + { + return true; + } + else + { + wxString msg; + msg.Printf( _( "Problem while running the PDF viewer\nCommand is '%s'" ), + GetChars( command ) ); + DisplayError( NULL, msg ); + } + } + else + { + wxString msg; + msg.Printf( _( "Unable to find a PDF viewer for <%s>" ), GetChars( filename ) ); + DisplayError( NULL, msg ); + } + + return false; +} + + +void OpenFile( const wxString& file ) +{ + wxString command; + wxString filename = file; + + wxFileName CurrentFileName( filename ); + wxString ext, type; + + ext = CurrentFileName.GetExt(); + wxFileType* filetype = wxTheMimeTypesManager->GetFileTypeFromExtension( ext ); + + bool success = false; + + wxFileType::MessageParameters params( filename, type ); + + if( filetype ) + success = filetype->GetOpenCommand( &command, params ); + + delete filetype; + + if( success && !command.IsEmpty() ) + ProcessExecute( command ); +} + + +wxString QuoteFullPath( wxFileName& fn, wxPathFormat format ) +{ + return wxT( "\"" ) + fn.GetFullPath( format ) + wxT( "\"" ); +} diff --git a/common/getrunningmicrosecs.cpp b/common/getrunningmicrosecs.cpp new file mode 100644 index 0000000..152a1d1 --- /dev/null +++ b/common/getrunningmicrosecs.cpp @@ -0,0 +1,97 @@ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2012 KiCad Developers, see change_log.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 <config.h> +#include <common.h> + +#if defined(_WIN32) + +#define WIN32_LEAN_AND_MEAN 1 +#include <windows.h> + +unsigned GetRunningMicroSecs() +{ + FILETIME now; + + GetSystemTimeAsFileTime( &now ); + + typedef unsigned long long UINT64; + + UINT64 t = (UINT64(now.dwHighDateTime) << 32) + now.dwLowDateTime; + + t /= 10; + + return unsigned( t ); +} + + +#if 0 +// test program +#include <stdio.h> +int main( int argc, char** argv ) +{ + unsigned then = GetRunningMicroSecs(); + + Sleep( 2000 ); // Windows Sleep( msecs ) + + printf( "delta: %u\n", GetRunningMicroSecs() - then ); + + return 0; +} +#endif + + +#elif defined(HAVE_CLOCK_GETTIME) + +#include <time.h> + +unsigned GetRunningMicroSecs() +{ + struct timespec now; + + clock_gettime( CLOCK_MONOTONIC, &now ); + + unsigned usecs = ((unsigned)now.tv_nsec)/1000 + ((unsigned)now.tv_sec) * 1000000; +// unsigned msecs = (now.tv_nsec / (1000*1000)) + now.tv_sec * 1000; + + return usecs; +} + + +#elif defined(HAVE_GETTIMEOFDAY_FUNC) + +#include <sys/time.h> +unsigned GetRunningMicroSecs() +{ + timeval tv; + + gettimeofday( &tv, 0 ); + + return (tv.tv_sec * 1000000) + tv.tv_usec; +} + +#endif + diff --git a/common/gr_basic.cpp b/common/gr_basic.cpp new file mode 100644 index 0000000..1e913cb --- /dev/null +++ b/common/gr_basic.cpp @@ -0,0 +1,1268 @@ +/********************************/ +/* Low level graphics routines */ +/********************************/ + + +#include <fctsys.h> +#include <gr_basic.h> +#include <common.h> +#include <trigo.h> +#include <macros.h> +#include <base_struct.h> +#include <class_base_screen.h> +#include <bezier_curves.h> +#include <math_for_graphics.h> +#include <wx/graphics.h> +#if defined(__WXMAC__) && defined(USE_WX_GRAPHICS_CONTEXT) +#include <wx/dcgraph.h> +#endif + +static const bool FILLED = true; +static const bool NOT_FILLED = false; + +/* Important Note: + * These drawing functions clip draw item before send these items to wxDC draw + * functions. For guy who asks why i did it, see a sample of problems encountered + * when pixels + * coordinates overflow 16 bits values: + * http://trac.wxwidgets.org/ticket/10446 + * Problems can be found under Windows **and** Linux (mainly when drawing arcs) + * (mainly at low zoom values (2, 1 or 0.5), in Pcbnew) + * some of these problems could be now fixed in recent distributions. + * + * Currently (feb 2009) there are overflow problems when drawing solid (filled) + * polygons under linux without clipping + * + * So before removing clipping functions, be aware these bug (they are not in + * KiCad or wxWidgets) are fixed by testing how are drawn complex lines arcs + * and solid polygons under Windows and Linux and remember users can have old + * versions with bugs + */ + + +/* Definitions for enabling and disabling debugging features in gr_basic.cpp. + * Please remember to set these back to 0 before making LAUNCHPAD commits. + */ +#define DEBUG_DUMP_CLIP_ERROR_COORDS 0 // Set to 1 to dump clip algorithm errors. +#define DEBUG_DUMP_CLIP_COORDS 0 // Set to 1 to dump clipped coordinates. + + +// For draw mode = XOR GR_XOR or GR_NXOR by background color +GR_DRAWMODE g_XorMode = GR_NXOR; + + +static void ClipAndDrawPoly( EDA_RECT * ClipBox, wxDC * DC, wxPoint Points[], + int n ); + +/* These functions are used by corresponding functions + * ( GRSCircle is called by GRCircle for instance) after mapping coordinates + * from user units to screen units(pixels coordinates) + */ +static void GRSRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, + int x2, int y2, int aWidth, EDA_COLOR_T aColor, + wxPenStyle aStyle = wxPENSTYLE_SOLID ); + +/**/ + +static int GRLastMoveToX, GRLastMoveToY; +static bool s_ForceBlackPen; /* if true: draws in black instead of + * color for printing. */ +static int xcliplo = 0, + ycliplo = 0, + xcliphi = 2000, + ycliphi = 2000; + +static EDA_COLOR_T s_DC_lastcolor = UNSPECIFIED_COLOR; +static EDA_COLOR_T s_DC_lastbrushcolor = UNSPECIFIED_COLOR; +static bool s_DC_lastbrushfill = false; +static wxDC* s_DC_lastDC = NULL; + +/*** + * Utility for the line clipping code, returns the boundary code of + * a point. Bit allocation is arbitrary + */ +static inline int clipOutCode( const EDA_RECT *aClipBox, int x, int y ) +{ + int code; + if( y < aClipBox->GetY() ) + code = 2; + else if( y > aClipBox->GetBottom() ) + code = 1; + else + code = 0; + if( x < aClipBox->GetX() ) + code |= 4; + else if( x > aClipBox->GetRight() ) + code |= 8; + return code; +} + + +/** + * Test if any part of a line falls within the bounds of a rectangle. + * + * Please note that this is only accurate for lines that are one pixel wide. + * + * @param aClipBox - The rectangle to test. + * @param x1 - X coordinate of one end of a line. + * @param y1 - Y coordinate of one end of a line. + * @param x2 - X coordinate of the other end of a line. + * @param y2 - Y coordinate of the other end of a line. + * + * @return - False if any part of the line lies within the rectangle. + */ +static bool clipLine( const EDA_RECT *aClipBox, int &x1, int &y1, int &x2, int &y2 ) +{ + // Stock Cohen-Sutherland algorithm; check *any* CG book for details + int outcode1 = clipOutCode( aClipBox, x1, y1 ); + int outcode2 = clipOutCode( aClipBox, x2, y2 ); + + while( outcode1 || outcode2 ) + { + // Fast reject + if( outcode1 & outcode2 ) + return true; + + // Choose a side to clip + int thisoutcode, x, y; + if( outcode1 ) + thisoutcode = outcode1; + else + thisoutcode = outcode2; + + /* One clip round + * Since we use the full range of 32 bit ints, the proportion + * computation has to be done in 64 bits to avoid horrible + * results */ + if( thisoutcode & 1 ) // Clip the bottom + { + y = aClipBox->GetBottom(); + x = x1 + (x2 - x1) * int64_t(y - y1) / (y2 - y1); + } + else if( thisoutcode & 2 ) // Clip the top + { + y = aClipBox->GetY(); + x = x1 + (x2 - x1) * int64_t(y - y1) / (y2 - y1); + } + else if( thisoutcode & 8 ) // Clip the right + { + x = aClipBox->GetRight(); + y = y1 + (y2 - y1) * int64_t(x - x1) / (x2 - x1); + } + else // if( thisoutcode & 4), obviously, clip the left + { + x = aClipBox->GetX(); + y = y1 + (y2 - y1) * int64_t(x - x1) / (x2 - x1); + } + + // Put the result back and update the boundary code + // No ambiguity, otherwise it would have been a fast reject + if( thisoutcode == outcode1 ) + { + x1 = x; + y1 = y; + outcode1 = clipOutCode( aClipBox, x1, y1 ); + } + else + { + x2 = x; + y2 = y; + outcode2 = clipOutCode( aClipBox, x2, y2 ); + } + } + return false; +} + +static void WinClipAndDrawLine( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width ) +{ + GRLastMoveToX = x2; + GRLastMoveToY = y2; + + if( ClipBox ) + { + EDA_RECT clipbox(*ClipBox); + clipbox.Inflate(width/2); + if( clipLine( &clipbox, x1, y1, x2, y2 ) ) + return; + } + + DC->DrawLine( x1, y1, x2, y2 ); +} + + +/* Forcing a reset of the current pen. + * Must be called after changing the graphical device before any trace. + */ +void GRResetPenAndBrush( wxDC* DC ) +{ + GRSetBrush( DC, BLACK ); // Force no fill + s_DC_lastbrushcolor = UNSPECIFIED_COLOR; + s_DC_lastcolor = UNSPECIFIED_COLOR; + s_DC_lastDC = NULL; +} + + +/** + * Function GRSetColorPen + * sets a pen style, width, color, and alpha into the given device context. + */ +void GRSetColorPen( wxDC* DC, EDA_COLOR_T Color, int width, wxPenStyle style ) +{ + // Under OSX and while printing when wxPen is set to 0, renderer follows the request drawing + // nothing & in the bitmap world the minimum is enough to light a pixel, in vectorial one not + if( width <= 1 ) + width = DC->DeviceToLogicalXRel( 1 ); + + if( s_ForceBlackPen ) + Color = BLACK; + + wxColour wx_color = MakeColour( Color ); + const wxPen& curr_pen = DC->GetPen(); + + if( !curr_pen.IsOk() || curr_pen.GetColour() != wx_color + || curr_pen.GetWidth() != width + || curr_pen.GetStyle() != style ) + { + wxPen pen; + pen.SetColour( wx_color ); + pen.SetWidth( width ); + pen.SetStyle( style ); + DC->SetPen( pen ); + } + else + // Should be not needed, but on Linux, in printing process + // the curr pen settings needs to be sometimes re-initialized + // Clearly, this is due to a bug, related to SetBrush(), + // but we have to live with it, at least on wxWidgets 3.0 + DC->SetPen( curr_pen ); +} + + +void GRSetBrush( wxDC* DC, EDA_COLOR_T Color, bool fill ) +{ + if( s_ForceBlackPen ) + Color = BLACK; + + if( s_DC_lastbrushcolor != Color + || s_DC_lastbrushfill != fill + || s_DC_lastDC != DC ) + { + wxBrush brush; + + brush.SetColour( MakeColour( Color ) ); + + if( fill ) + brush.SetStyle( wxBRUSHSTYLE_SOLID ); + else + brush.SetStyle( wxBRUSHSTYLE_TRANSPARENT ); + + DC->SetBrush( brush ); + + s_DC_lastbrushcolor = Color; + s_DC_lastbrushfill = fill; + s_DC_lastDC = DC; + } +} + + +/** + * Function GRForceBlackPen + * @param flagforce True to force a black pen whenever the asked color + */ +void GRForceBlackPen( bool flagforce ) +{ + s_ForceBlackPen = flagforce; +} + + +/** + * Function GetGRForceBlackPenState + * @return s_ForceBlackPen (True if a black pen was forced) + */ +bool GetGRForceBlackPenState( void ) +{ + return s_ForceBlackPen; +} + + +/*************************************/ +/* Set the device context draw mode. */ +/*************************************/ +void GRSetDrawMode( wxDC* DC, GR_DRAWMODE draw_mode ) +{ + if( draw_mode & GR_OR ) +#if defined(__WXMAC__) && (wxMAC_USE_CORE_GRAPHICS || wxCHECK_VERSION( 2, 9, 0 ) ) + + DC->SetLogicalFunction( wxCOPY ); +#elif defined( USE_WX_GRAPHICS_CONTEXT ) + + DC->SetLogicalFunction( wxCOPY ); +#else + + DC->SetLogicalFunction( wxOR ); +#endif + else if( draw_mode & GR_XOR ) +#if defined( USE_WX_GRAPHICS_CONTEXT ) + + DC->SetLogicalFunction( wxCOPY ); +#else + + DC->SetLogicalFunction( wxXOR ); +#endif + else if( draw_mode & GR_NXOR ) +#if defined(__WXMAC__) && (wxMAC_USE_CORE_GRAPHICS || wxCHECK_VERSION( 2, 9, 0 ) ) + + DC->SetLogicalFunction( wxXOR ); +#elif defined( USE_WX_GRAPHICS_CONTEXT ) + + DC->SetLogicalFunction( wxCOPY ); +#else + + DC->SetLogicalFunction( wxEQUIV ); +#endif + else if( draw_mode & GR_INVERT ) +#if defined( USE_WX_GRAPHICS_CONTEXT ) + + DC->SetLogicalFunction( wxCOPY ); +#else + + DC->SetLogicalFunction( wxINVERT ); +#endif + else if( draw_mode & GR_COPY ) + DC->SetLogicalFunction( wxCOPY ); + +#ifdef USE_WX_OVERLAY + DC->SetLogicalFunction( wxCOPY ); +#endif +} + + +void GRPutPixel( EDA_RECT* ClipBox, wxDC* DC, int x, int y, EDA_COLOR_T Color ) +{ + if( ClipBox && !ClipBox->Contains( x, y ) ) + return; + + GRSetColorPen( DC, Color ); + DC->DrawPoint( x, y ); +} + + +/* + * Draw a line, in object space. + */ +void GRLine( EDA_RECT* ClipBox, + wxDC* DC, + int x1, + int y1, + int x2, + int y2, + int width, + EDA_COLOR_T Color ) +{ + GRSetColorPen( DC, Color, width ); + WinClipAndDrawLine( ClipBox, DC, x1, y1, x2, y2, width ); + GRLastMoveToX = x2; + GRLastMoveToY = y2; +} + + +void GRLine( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aStart, wxPoint aEnd, int aWidth, EDA_COLOR_T aColor ) +{ + GRLine( aClipBox, aDC, aStart.x, aStart.y, aEnd.x, aEnd.y, aWidth, aColor ); +} + + +void GRDashedLine( EDA_RECT* ClipBox, wxDC* DC, + int x1, int y1, int x2, int y2, + int width, EDA_COLOR_T Color ) +{ + GRLastMoveToX = x2; + GRLastMoveToY = y2; + s_DC_lastcolor = UNSPECIFIED_COLOR; + GRSetColorPen( DC, Color, width, wxPENSTYLE_SHORT_DASH ); + WinClipAndDrawLine( ClipBox, DC, x1, y1, x2, y2, width ); + s_DC_lastcolor = UNSPECIFIED_COLOR; + GRSetColorPen( DC, Color, width ); +} + + +/* + * Move to a new position, in object space. + */ +void GRMoveTo( int x, int y ) +{ + GRLastMoveToX = x; + GRLastMoveToY = y; +} + + +/* + * Draw line to a new position, in object space. + */ +void GRLineTo( EDA_RECT* ClipBox, wxDC* DC, int x, int y, int width, EDA_COLOR_T Color ) +{ + GRLine( ClipBox, DC, GRLastMoveToX, GRLastMoveToY, x, y, width, Color ); +} + + +void GRMixedLine( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, + int width, EDA_COLOR_T Color ) +{ + GRSetColorPen( DC, Color, width, wxPENSTYLE_DOT_DASH ); + GRLine( ClipBox, DC, x1, y1, x2, y2, width, Color ); + GRSetColorPen( DC, Color, width ); +} + + + +/** + * Function GRLineArray + * draws an array of lines (not a polygon). + * @param aClipBox = the clip box + * @param aDC = the device context into which drawing should occur. + * @param aLines = a list of pair of coordinate in user space: a pair for each line. + * @param aWidth = the width of each line. + * @param aColor = an index into our color table of RGB colors. + * @see EDA_COLOR_T and colors.h + */ +void GRLineArray( EDA_RECT* aClipBox, wxDC* aDC, std::vector<wxPoint>& aLines, + int aWidth, EDA_COLOR_T aColor ) +{ + GRSetColorPen( aDC, aColor, aWidth ); + + if( aClipBox ) + aClipBox->Inflate(aWidth/2); + +#if defined( __WXMAC__ ) && defined( USE_WX_GRAPHICS_CONTEXT ) + wxGCDC *gcdc = wxDynamicCast( aDC, wxGCDC ); + if( gcdc ) + { + wxGraphicsContext *gc = gcdc->GetGraphicsContext(); + + // create path + wxGraphicsPath path = gc->CreatePath(); + for( unsigned i = 0; i < aLines.size(); i += 2 ) + { + int x1 = aLines[i].x; + int y1 = aLines[i].y; + int x2 = aLines[i+1].x; + int y2 = aLines[i+1].y; + if( ( aClipBox == NULL ) || !clipLine( aClipBox, x1, y1, x2, y2 ) ) + { + path.MoveToPoint( x1, y1 ); + path.AddLineToPoint( x2, y2 ); + } + } + // draw path + gc->StrokePath( path ); + } + else +#endif + { + for( unsigned i = 0; i < aLines.size(); i += 2 ) + { + int x1 = aLines[i].x; + int y1 = aLines[i].y; + int x2 = aLines[i+1].x; + int y2 = aLines[i+1].y; + if( ( aClipBox == NULL ) || !clipLine( aClipBox, x1, y1, x2, y2 ) ) + aDC->DrawLine( x1, y1, x2, y2 ); + } + } + GRMoveTo( aLines[aLines.size() - 1].x, aLines[aLines.size() - 1].y ); + + if( aClipBox ) + aClipBox->Inflate(-aWidth/2); +} + +// Draw the outline of a thick segment wih rounded ends +void GRCSegm( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, + int width, int aPenSize, EDA_COLOR_T Color ) +{ + GRLastMoveToX = x2; + GRLastMoveToY = y2; + + if( ClipBox ) + { + EDA_RECT clipbox(*ClipBox); + clipbox.Inflate(width/2); + + if( clipLine( &clipbox, x1, y1, x2, y2 ) ) + return; + } + + + if( width <= 2 ) /* single line or 2 pixels */ + { + GRSetColorPen( DC, Color, width ); + DC->DrawLine( x1, y1, x2, y2 ); + return; + } + + GRSetBrush( DC, Color, NOT_FILLED ); + GRSetColorPen( DC, Color, aPenSize ); + + int radius = (width + 1) >> 1; + int dx = x2 - x1; + int dy = y2 - y1; + double angle = -ArcTangente( dy, dx ); + wxPoint start; + wxPoint end; + wxPoint org( x1, y1); + int len = (int) hypot( dx, dy ); + + // We know if the DC is mirrored, to draw arcs + int slx = DC->DeviceToLogicalX( 1 ) - DC->DeviceToLogicalX( 0 ); + int sly = DC->DeviceToLogicalY( 1 ) - DC->DeviceToLogicalY( 0 ); + bool mirrored = (slx > 0 && sly < 0) || (slx < 0 && sly > 0); + + // first edge + start.x = 0; + start.y = radius; + end.x = len; + end.y = radius; + RotatePoint( &start, angle); + RotatePoint( &end, angle); + + start += org; + end += org; + + DC->DrawLine( start, end ); + + // first rounded end + end.x = 0; + end.y = -radius; + RotatePoint( &end, angle); + end += org; + + if( !mirrored ) + DC->DrawArc( end, start, org ); + else + DC->DrawArc( start, end, org ); + + + // second edge + start.x = len; + start.y = -radius; + RotatePoint( &start, angle); + start += org; + + DC->DrawLine( start, end ); + + // second rounded end + end.x = len; + end.y = radius; + RotatePoint( &end, angle); + end += org; + + if( !mirrored ) + DC->DrawArc( end.x, end.y, start.x, start.y, x2, y2 ); + else + DC->DrawArc( start.x, start.y, end.x, end.y, x2, y2 ); +} + + +void GRCSegm( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, + int width, EDA_COLOR_T Color ) +{ + GRCSegm( ClipBox, DC, x1, y1, x2, y2, width, 0, Color ); +} + + +void GRCSegm( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aStart, wxPoint aEnd, + int aWidth, EDA_COLOR_T aColor ) +{ + GRCSegm( aClipBox, aDC, aStart.x, aStart.y, aEnd.x, aEnd.y, aWidth, 0, aColor ); +} + + +/* + * Draw segment (full) with rounded ends in object space (real coords.). + */ +void GRFillCSegm( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, + int width, EDA_COLOR_T Color ) +{ + GRSetColorPen( DC, Color, width ); + WinClipAndDrawLine( ClipBox, DC, x1, y1, x2, y2, width ); +} + + +void GRFilledSegment( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aStart, wxPoint aEnd, + int aWidth, EDA_COLOR_T aColor ) +{ + GRSetColorPen( aDC, aColor, aWidth ); + WinClipAndDrawLine( aClipBox, aDC, aStart.x, aStart.y, aEnd.x, aEnd.y, aWidth ); +} + + +static bool IsGRSPolyDrawable( EDA_RECT* ClipBox, int n, wxPoint Points[] ) +{ + if( !ClipBox ) + return true; + + if( n <= 0 ) + return false; + + int Xmin, Xmax, Ymin, Ymax; + + Xmin = Xmax = Points[0].x; + Ymin = Ymax = Points[0].y; + + for( int ii = 1; ii < n; ii++ ) // calculate rectangle + { + Xmin = std::min( Xmin, Points[ii].x ); + Xmax = std::max( Xmax, Points[ii].x ); + Ymin = std::min( Ymin, Points[ii].y ); + Ymax = std::max( Ymax, Points[ii].y ); + } + + xcliplo = ClipBox->GetX(); + ycliplo = ClipBox->GetY(); + xcliphi = ClipBox->GetRight(); + ycliphi = ClipBox->GetBottom(); + + if( Xmax < xcliplo ) + return false; + if( Xmin > xcliphi ) + return false; + if( Ymax < ycliplo ) + return false; + if( Ymin > ycliphi ) + return false; + + return true; +} + + +/* + * Draw a new polyline and fill it if Fill, in screen space. + */ +static void GRSPoly( EDA_RECT* ClipBox, wxDC* DC, int n, wxPoint Points[], + bool Fill, int width, + EDA_COLOR_T Color, EDA_COLOR_T BgColor ) +{ + if( !IsGRSPolyDrawable( ClipBox, n, Points ) ) + return; + + if( Fill && ( n > 2 ) ) + { + GRSetBrush( DC, BgColor, FILLED ); + GRSetColorPen( DC, Color, width ); + + /* clip before send the filled polygon to wxDC, because under linux + * (GTK?) polygons having large coordinates are incorrectly drawn + * (integer overflow in coordinates, I am guessing) + */ + ClipAndDrawPoly( ClipBox, DC, Points, n ); + } + else + { +#if defined( __WXMAC__ ) && defined( USE_WX_GRAPHICS_CONTEXT ) + wxGCDC *gcdc = wxDynamicCast( DC, wxGCDC ); + if( gcdc ) + { + wxGraphicsContext *gc = gcdc->GetGraphicsContext(); + + // set pen + GRSetColorPen( DC, Color, width ); + + // create path + wxGraphicsPath path = gc->CreatePath(); + path.MoveToPoint( Points[0].x, Points[0].y ); + for( int i = 1; i < n; ++i ) + { + path.AddLineToPoint( Points[i].x, Points[i].y ); + } + // draw path + gc->StrokePath( path ); + + // correctly update last position + GRMoveTo( Points[n - 1].x, Points[n - 1].y ); + } + else +#endif + { + GRMoveTo( Points[0].x, Points[0].y ); + for( int i = 1; i < n; ++i ) + { + GRLineTo( ClipBox, DC, Points[i].x, Points[i].y, width, Color ); + } + } + } +} + + +/* + * Draw a new closed polyline and fill it if Fill, in screen space. + */ +static void GRSClosedPoly( EDA_RECT* aClipBox, wxDC* aDC, + int aPointCount, wxPoint aPoints[], + bool aFill, int aWidth, + EDA_COLOR_T aColor, + EDA_COLOR_T aBgColor ) +{ + if( !IsGRSPolyDrawable( aClipBox, aPointCount, aPoints ) ) + return; + + if( aFill && ( aPointCount > 2 ) ) + { + GRLastMoveToX = aPoints[aPointCount - 1].x; + GRLastMoveToY = aPoints[aPointCount - 1].y; + GRSetBrush( aDC, aBgColor, FILLED ); + GRSetColorPen( aDC, aColor, aWidth ); + ClipAndDrawPoly( aClipBox, aDC, aPoints, aPointCount ); + } + else + { +#if defined( __WXMAC__ ) && defined( USE_WX_GRAPHICS_CONTEXT ) + wxGCDC *gcdc = wxDynamicCast( aDC, wxGCDC ); + if( gcdc ) + { + wxGraphicsContext *gc = gcdc->GetGraphicsContext(); + + // set pen + GRSetColorPen( aDC, aColor, aWidth ); + + // create path + wxGraphicsPath path = gc->CreatePath(); + path.MoveToPoint( aPoints[0].x, aPoints[0].y ); + for( int i = 1; i < aPointCount; ++i ) + { + path.AddLineToPoint( aPoints[i].x, aPoints[i].y ); + } + if( aPoints[aPointCount - 1] != aPoints[0] ) + path.AddLineToPoint( aPoints[0].x, aPoints[0].y ); + // draw path + gc->StrokePath( path ); + + // correctly update last position + GRMoveTo( aPoints[aPointCount - 1].x, aPoints[aPointCount - 1].y ); + } + else +#endif + { + GRMoveTo( aPoints[0].x, aPoints[0].y ); + for( int i = 1; i < aPointCount; ++i ) + { + GRLineTo( aClipBox, aDC, aPoints[i].x, aPoints[i].y, aWidth, aColor ); + } + + int lastpt = aPointCount - 1; + + // Close the polygon + if( aPoints[lastpt] != aPoints[0] ) + { + GRLineTo( aClipBox, aDC, aPoints[0].x, aPoints[0].y, aWidth, aColor ); + } + } + } +} + + +/* + * Draw a new polyline and fill it if Fill, in drawing space. + */ +void GRPoly( EDA_RECT* ClipBox, wxDC* DC, int n, wxPoint Points[], + bool Fill, int width, EDA_COLOR_T Color, EDA_COLOR_T BgColor ) +{ + GRSPoly( ClipBox, DC, n, Points, Fill, width, Color, BgColor ); +} + + +/* + * Draw a closed polyline and fill it if Fill, in object space. + */ +void GRClosedPoly( EDA_RECT* ClipBox, wxDC* DC, int n, wxPoint Points[], + bool Fill, EDA_COLOR_T Color, EDA_COLOR_T BgColor ) +{ + GRClosedPoly( ClipBox, DC, n, Points, Fill, 0, Color, BgColor ); +} + + +void GRClosedPoly( EDA_RECT* ClipBox, wxDC* DC, int n, wxPoint Points[], + bool Fill, int width, EDA_COLOR_T Color, EDA_COLOR_T BgColor ) +{ + GRSClosedPoly( ClipBox, DC, n, Points, Fill, width, Color, BgColor ); +} + + +void GRCircle( EDA_RECT* ClipBox, wxDC* DC, int xc, int yc, int r, int width, EDA_COLOR_T Color ) +{ + /* Clip circles off screen. */ + if( ClipBox ) + { + int x0, y0, xm, ym; + x0 = ClipBox->GetX(); + y0 = ClipBox->GetY(); + xm = ClipBox->GetRight(); + ym = ClipBox->GetBottom(); + + if( xc < ( x0 - r - width ) ) + return; + + if( yc < ( y0 - r - width ) ) + return; + + if( xc > ( r + xm + width ) ) + return; + + if( yc > ( r + ym + width ) ) + return; + } + + GRSetBrush( DC, Color, NOT_FILLED ); + GRSetColorPen( DC, Color, width ); + DC->DrawEllipse( xc - r, yc - r, r + r, r + r ); +} + + +void GRCircle( EDA_RECT* ClipBox, wxDC* DC, int x, int y, int r, EDA_COLOR_T Color ) +{ + GRCircle( ClipBox, DC, x, y, r, 0, Color ); +} + + +void GRCircle( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aPos, int aRadius, int aWidth, EDA_COLOR_T aColor ) +{ + GRCircle( aClipBox, aDC, aPos.x, aPos.y, aRadius, aWidth, aColor ); +} + + +void GRFilledCircle( EDA_RECT* ClipBox, wxDC* DC, int x, int y, int r, + int width, EDA_COLOR_T Color, EDA_COLOR_T BgColor ) +{ + /* Clip circles off screen. */ + if( ClipBox ) + { + int x0, y0, xm, ym; + x0 = ClipBox->GetX(); + y0 = ClipBox->GetY(); + xm = ClipBox->GetRight(); + ym = ClipBox->GetBottom(); + + if( x < ( x0 - r ) ) + return; + + if( y < ( y0 - r ) ) + return; + + if( x > ( r + xm ) ) + return; + + if( y > ( r + ym ) ) + return; + } + + GRSetBrush( DC, BgColor, FILLED ); + GRSetColorPen( DC, Color, width ); + DC->DrawEllipse( x - r, y - r, r + r, r + r ); +} + + +void GRFilledCircle( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aPos, int aRadius, EDA_COLOR_T aColor ) +{ + GRFilledCircle( aClipBox, aDC, aPos.x, aPos.y, aRadius, 0, aColor, aColor ); +} + + +/* + * Draw an arc in user space. + */ +void GRArc1( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, + int xc, int yc, EDA_COLOR_T Color ) +{ + GRArc1( ClipBox, DC, x1, y1, x2, y2, xc, yc, 0, Color ); +} + + +/* + * Draw an arc, width = width in user space. + */ +void GRArc1( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, + int xc, int yc, int width, EDA_COLOR_T Color ) +{ + /* Clip arcs off screen. */ + if( ClipBox ) + { + int x0, y0, xm, ym, r; + x0 = ClipBox->GetX(); + y0 = ClipBox->GetY(); + xm = ClipBox->GetRight(); + ym = ClipBox->GetBottom(); + r = KiROUND( Distance( x1, y1, xc, yc ) ); + if( xc < ( x0 - r ) ) + return; + if( yc < ( y0 - r ) ) + return; + if( xc > ( r + xm ) ) + return; + if( yc > ( r + ym ) ) + return; + } + + GRSetBrush( DC, Color ); + GRSetColorPen( DC, Color, width ); + DC->DrawArc( x1, y1, x2, y2, xc, yc ); +} + + +void GRArc1( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aStart, wxPoint aEnd, + wxPoint aCenter, int aWidth, EDA_COLOR_T aColor ) +{ + GRArc1( aClipBox, aDC, aStart.x, aStart.y, aEnd.x, aEnd.y, aCenter.x, aCenter.y, + aWidth, aColor ); +} + + +/* + * Draw a filled arc in drawing space. + */ +void GRFilledArc( EDA_RECT* ClipBox, + wxDC* DC, + int x, + int y, + double StAngle, + double EndAngle, + int r, + int width, + EDA_COLOR_T Color, + EDA_COLOR_T BgColor ) +{ + int x1, y1, x2, y2; + + /* Clip arcs off screen */ + if( ClipBox ) + { + int x0, y0, xm, ym; + x0 = ClipBox->GetX(); + y0 = ClipBox->GetY(); + xm = ClipBox->GetRight(); + ym = ClipBox->GetBottom(); + + if( x < ( x0 - r - 1 ) ) + return; + + if( y < ( y0 - r - 1 ) ) + return; + + if( x > ( r + xm + 1 ) ) + return; + + if( y > ( r + ym + 1 ) ) + return; + } + + x1 = r; + y1 = 0; + RotatePoint( &x1, &y1, EndAngle ); + + x2 = r; + y2 = 0; + RotatePoint( &x2, &y2, StAngle ); + + GRSetBrush( DC, BgColor, FILLED ); + GRSetColorPen( DC, Color, width ); + DC->DrawArc( x + x1, y - y1, x + x2, y - y2, x, y ); +} + + +void GRFilledArc( EDA_RECT* ClipBox, wxDC* DC, int x, int y, + double StAngle, double EndAngle, int r, + EDA_COLOR_T Color, EDA_COLOR_T BgColor ) +{ + GRFilledArc( ClipBox, DC, x, y, StAngle, EndAngle, r, 0, Color, BgColor ); +} + + +/* + * Draw an arc in drawing space. + */ +void GRArc( EDA_RECT* ClipBox, wxDC* DC, int xc, int yc, double StAngle, + double EndAngle, int r, EDA_COLOR_T Color ) +{ + int x1, y1, x2, y2; + + /* Clip arcs off screen */ + if( ClipBox ) + { + int radius = r + 1; + int x0, y0, xm, ym, x, y; + x0 = ClipBox->GetX(); + y0 = ClipBox->GetY(); + xm = ClipBox->GetRight(); + ym = ClipBox->GetBottom(); + x = xc; + y = yc; + + if( x < ( x0 - radius ) ) + return; + if( y < ( y0 - radius ) ) + return; + if( x > ( xm + radius ) ) + return; + if( y > ( ym + radius ) ) + return; + } + + x1 = r; + y1 = 0; + RotatePoint( &x1, &y1, EndAngle ); + + x2 = r; + y2 = 0; + RotatePoint( &x2, &y2, StAngle ); + + GRSetBrush( DC, Color, NOT_FILLED ); + GRSetColorPen( DC, Color ); + DC->DrawArc( xc + x1, yc - y1, xc + x2, yc - y2, xc, yc ); +} + + +/* + * Draw an arc with width = width in drawing space. + */ +void GRArc( EDA_RECT* ClipBox, + wxDC* DC, + int x, + int y, + double StAngle, + double EndAngle, + int r, + int width, + EDA_COLOR_T Color ) +{ + int x1, y1, x2, y2; + + /* Clip arcs off screen. */ + if( ClipBox ) + { + int x0, y0, xm, ym; + x0 = ClipBox->GetX(); + y0 = ClipBox->GetY(); + xm = ClipBox->GetRight(); + ym = ClipBox->GetBottom(); + + if( x < ( x0 - r - width ) ) + return; + + if( y < ( y0 - r - width ) ) + return; + + if( x > ( r + xm + width ) ) + return; + + if( y > ( r + ym + width ) ) + return; + } + + x1 = r; + y1 = 0; + RotatePoint( &x1, &y1, EndAngle ); + + x2 = r; + y2 = 0; + RotatePoint( &x2, &y2, StAngle ); + + GRSetBrush( DC, Color ); + GRSetColorPen( DC, Color, width ); + DC->DrawArc( x + x1, y - y1, x + x2, y - y2, x, y ); +} + + +/* + * Draw a rectangle in drawing space. + */ +void GRRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, int x2, int y2, EDA_COLOR_T aColor ) +{ + GRSRect( aClipBox, aDC, x1, y1, x2, y2, 0, aColor ); +} + + +void GRRectPs( EDA_RECT* aClipBox, wxDC* aDC, const EDA_RECT& aRect, EDA_COLOR_T aColor, wxPenStyle aStyle ) +{ + int x1 = aRect.GetX(); + int y1 = aRect.GetY(); + int x2 = aRect.GetRight(); + int y2 = aRect.GetBottom(); + + GRSRect( aClipBox, aDC, x1, y1, x2, y2, 0, aColor, aStyle ); +} + + +/* + * Draw a rectangle (thick lines) in drawing space. + */ +void GRRect( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width, EDA_COLOR_T Color ) +{ + GRSRect( ClipBox, DC, x1, y1, x2, y2, width, Color ); +} + + +void GRRect( EDA_RECT* aClipBox, wxDC* aDC, const EDA_RECT& aRect, int aWidth, EDA_COLOR_T aColor ) +{ + int x1 = aRect.GetX(); + int y1 = aRect.GetY(); + int x2 = aRect.GetRight(); + int y2 = aRect.GetBottom(); + + GRSRect( aClipBox, aDC, x1, y1, x2, y2, aWidth, aColor ); +} + + +/* + * Draw a rectangle (filled with AreaColor) in drawing space. + */ +void GRFilledRect( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, + EDA_COLOR_T Color, EDA_COLOR_T BgColor ) +{ + GRSFilledRect( ClipBox, DC, x1, y1, x2, y2, 0, Color, BgColor ); +} + + +/* + * Draw a rectangle (filled with AreaColor) in drawing space. + */ +void GRFilledRect( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, + int width, EDA_COLOR_T Color, EDA_COLOR_T BgColor ) +{ + GRSFilledRect( ClipBox, DC, x1, y1, x2, y2, width, Color, BgColor ); +} + + +/* + * Draw a rectangle in screen space. + */ + +void GRSRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, int x2, int y2, + int aWidth, EDA_COLOR_T aColor, wxPenStyle aStyle ) +{ + wxPoint points[5]; + points[0] = wxPoint(x1, y1); + points[1] = wxPoint(x1, y2); + points[2] = wxPoint(x2, y2); + points[3] = wxPoint(x2, y1); + points[4] = points[0]; + GRSClosedPoly( aClipBox, aDC, 5, points, NOT_FILLED, aWidth, + aColor, aColor ); +} + + +void GRSFilledRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, int x2, int y2, + int aWidth, EDA_COLOR_T aColor, EDA_COLOR_T aBgColor ) +{ + wxPoint points[5]; + points[0] = wxPoint(x1, y1); + points[1] = wxPoint(x1, y2); + points[2] = wxPoint(x2, y2); + points[3] = wxPoint(x2, y1); + points[4] = points[0]; + + GRSetBrush( aDC, aBgColor, FILLED ); + GRSetColorPen( aDC, aBgColor, aWidth ); + + if( aClipBox && (aWidth > 0) ) + { + EDA_RECT clipbox(*aClipBox); + clipbox.Inflate(aWidth); + ClipAndDrawPoly(&clipbox, aDC, points, 5); // polygon approach is more accurate + } + else + ClipAndDrawPoly(aClipBox, aDC, points, 5 ); +} + +/** + * Function ClipAndDrawPoly + * Used to clip a polygon and draw it as Filled Polygon + * uses the Sutherland and Hodgman algo to clip the given poly against a + * rectangle. This rectangle is the drawing area this is useful under + * Linux (2009) because filled polygons are incorrectly drawn if they have + * too large coordinates (seems due to integer overflows in calculations) + * Could be removed in some years, if become unnecessary. + */ + +/* Note: aClipBox == NULL is legal, so if aClipBox == NULL, + * the polygon is drawn, but not clipped + */ +#include <SutherlandHodgmanClipPoly.h> + +void ClipAndDrawPoly( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aPoints[], int n ) +{ + if( aClipBox == NULL ) + { + aDC->DrawPolygon( n, aPoints ); + return; + } + + // A clip box exists: clip and draw the polygon. + static std::vector<wxPoint> clippedPolygon; + static pointVector inputPolygon, outputPolygon; + + inputPolygon.clear(); + outputPolygon.clear(); + clippedPolygon.clear(); + + for( int ii = 0; ii < n; ii++ ) + inputPolygon.push_back( PointF( (REAL) aPoints[ii].x, (REAL) aPoints[ii].y ) ); + + RectF window( (REAL) aClipBox->GetX(), (REAL) aClipBox->GetY(), + (REAL) aClipBox->GetWidth(), (REAL) aClipBox->GetHeight() ); + + SutherlandHodgman sh( window ); + sh.Clip( inputPolygon, outputPolygon ); + + for( cpointIterator cit = outputPolygon.begin(); cit != outputPolygon.end(); ++cit ) + { + clippedPolygon.push_back( wxPoint( KiROUND( cit->X ), KiROUND( cit->Y ) ) ); + } + + if( clippedPolygon.size() ) + aDC->DrawPolygon( clippedPolygon.size(), &clippedPolygon[0] ); +} + + +void GRBezier( EDA_RECT* ClipBox, + wxDC* DC, + int x1, + int y1, + int x2, + int y2, + int x3, + int y3, + int width, + EDA_COLOR_T Color ) +{ + std::vector<wxPoint> Points = Bezier2Poly( x1, y1, x2, y2, x3, y3 ); + GRPoly( ClipBox, DC, Points.size(), &Points[0], false, width, Color, Color ); +} + + +void GRBezier( EDA_RECT* ClipBox, + wxDC* DC, + int x1, + int y1, + int x2, + int y2, + int x3, + int y3, + int x4, + int y4, + int width, + EDA_COLOR_T Color ) +{ + std::vector<wxPoint> Points = Bezier2Poly( x1, y1, x2, y2, x3, y3, x4, y4 ); + GRPoly( ClipBox, DC, Points.size(), &Points[0], false, width, Color, Color ); +} + + +void GRDrawAnchor( EDA_RECT *aClipBox, wxDC *aDC, int x, int y, + int aSize, EDA_COLOR_T aColor ) +{ + int anchor_size = aDC->DeviceToLogicalXRel( aSize ); + + GRLine( aClipBox, aDC, + x - anchor_size, y, + x + anchor_size, y, 0, aColor ); + GRLine( aClipBox, aDC, + x, y - anchor_size, + x, y + anchor_size, 0, aColor ); +} diff --git a/common/grid_tricks.cpp b/common/grid_tricks.cpp new file mode 100644 index 0000000..c854411 --- /dev/null +++ b/common/grid_tricks.cpp @@ -0,0 +1,289 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2012 KiCad Developers, see change_log.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 <fctsys.h> +#include <grid_tricks.h> +#include <wx/tokenzr.h> +#include <wx/arrstr.h> +#include <wx/clipbrd.h> + + + // It works for table data on clipboard for an Excell spreadsheet, +// why not us too for now. +#define COL_SEP wxT( '\t' ) +#define ROW_SEP wxT( '\n' ) + + +enum +{ + MYID_FIRST = -1, + MYID_CUT, + MYID_COPY, + MYID_PASTE, + MYID_SELECT, + MYID_LAST, +}; + + +GRID_TRICKS::GRID_TRICKS( wxGrid* aGrid ): + m_grid( aGrid ) +{ + m_sel_row_start = 0; + m_sel_col_start = 0; + m_sel_row_count = 0; + m_sel_col_count = 0; + + aGrid->Connect( wxEVT_GRID_CELL_RIGHT_CLICK, wxGridEventHandler( GRID_TRICKS::onGridCellRightClick ), NULL, this ); + aGrid->Connect( MYID_FIRST, MYID_LAST, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( GRID_TRICKS::onPopupSelection ), NULL, this ); + aGrid->Connect( wxEVT_KEY_DOWN, wxKeyEventHandler( GRID_TRICKS::onKeyDown ), NULL, this ); + aGrid->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( GRID_TRICKS::onRightDown ), NULL, this ); +} + + +void GRID_TRICKS::getSelectedArea() +{ + wxGridCellCoordsArray topLeft = m_grid->GetSelectionBlockTopLeft(); + wxGridCellCoordsArray botRight = m_grid->GetSelectionBlockBottomRight(); + + wxArrayInt cols = m_grid->GetSelectedCols(); + wxArrayInt rows = m_grid->GetSelectedRows(); + + DBG(printf("topLeft.Count():%d botRight:Count():%d\n", int( topLeft.Count() ), int( botRight.Count() ) );) + + if( topLeft.Count() && botRight.Count() ) + { + m_sel_row_start = topLeft[0].GetRow(); + m_sel_col_start = topLeft[0].GetCol(); + + m_sel_row_count = botRight[0].GetRow() - m_sel_row_start + 1; + m_sel_col_count = botRight[0].GetCol() - m_sel_col_start + 1; + } + else if( cols.Count() ) + { + m_sel_col_start = cols[0]; + m_sel_col_count = cols.Count(); + m_sel_row_start = 0; + m_sel_row_count = m_grid->GetNumberRows(); + } + else if( rows.Count() ) + { + m_sel_col_start = 0; + m_sel_col_count = m_grid->GetNumberCols(); + m_sel_row_start = rows[0]; + m_sel_row_count = rows.Count(); + } + else + { + m_sel_row_start = -1; + m_sel_col_start = -1; + m_sel_row_count = 0; + m_sel_col_count = 0; + } + + //DBG(printf("m_sel_row_start:%d m_sel_col_start:%d m_sel_row_count:%d m_sel_col_count:%d\n", m_sel_row_start, m_sel_col_start, m_sel_row_count, m_sel_col_count );) +} + + +void GRID_TRICKS::showPopupMenu() +{ + wxMenu menu; + + menu.Append( MYID_CUT, _( "Cut\tCTRL+X" ), _( "Clear selected cells pasting original contents to clipboard" ) ); + menu.Append( MYID_COPY, _( "Copy\tCTRL+C" ), _( "Copy selected cells to clipboard" ) ); + menu.Append( MYID_PASTE, _( "Paste\tCTRL+V" ), _( "Paste clipboard cells to matrix at current cell" ) ); + menu.Append( MYID_SELECT, _( "Select All\tCTRL+A" ), _( "Select all cells" ) ); + + getSelectedArea(); + + // if nothing is selected, disable cut and copy. + if( !m_sel_row_count && !m_sel_col_count ) + { + menu.Enable( MYID_CUT, false ); + menu.Enable( MYID_COPY, false ); + } + + bool have_cb_text = false; + if( wxTheClipboard->Open() ) + { + if( wxTheClipboard->IsSupported( wxDF_TEXT ) ) + have_cb_text = true; + + wxTheClipboard->Close(); + } + + if( !have_cb_text ) + { + // if nothing on clipboard, disable paste. + menu.Enable( MYID_PASTE, false ); + } + + m_grid->PopupMenu( &menu ); +} + + +void GRID_TRICKS::onPopupSelection( wxCommandEvent& event ) +{ + int menu_id = event.GetId(); + + // assume getSelectedArea() was called by rightClickPopupMenu() and there's + // no way to have gotten here without that having been called. + + switch( menu_id ) + { + case MYID_CUT: + case MYID_COPY: + cutcopy( menu_id == MYID_CUT ); + break; + + case MYID_PASTE: + paste_clipboard(); + break; + + case MYID_SELECT: + m_grid->SelectAll(); + break; + + default: + ; + } +} + + +void GRID_TRICKS::onKeyDown( wxKeyEvent& ev ) +{ + if( isCtl( 'A', ev ) ) + { + m_grid->SelectAll(); + } + else if( isCtl( 'C', ev ) ) + { + getSelectedArea(); + cutcopy( false ); + } + else if( isCtl( 'V', ev ) ) + { + getSelectedArea(); + paste_clipboard(); + } + else if( isCtl( 'X', ev ) ) + { + getSelectedArea(); + cutcopy( true ); + } + else + { + ev.Skip( true ); + } +} + + +void GRID_TRICKS::paste_clipboard() +{ + if( wxTheClipboard->Open() ) + { + if( wxTheClipboard->IsSupported( wxDF_TEXT ) ) + { + wxTextDataObject data; + + wxTheClipboard->GetData( data ); + + wxString cb_text = data.GetText(); + + paste_text( cb_text ); + } + + wxTheClipboard->Close(); + m_grid->ForceRefresh(); + } +} + + +void GRID_TRICKS::paste_text( const wxString& cb_text ) +{ + wxGridTableBase* tbl = m_grid->GetTable(); + + const int cur_row = std::max( getCursorRow(), 0 ); // no -1 + const int cur_col = std::max( getCursorCol(), 0 ); + + wxStringTokenizer rows( cb_text, ROW_SEP, wxTOKEN_RET_EMPTY ); + + // if clipboard rows would extend past end of current table size... + if( int( rows.CountTokens() ) > tbl->GetNumberRows() - cur_row ) + { + int newRowsNeeded = rows.CountTokens() - ( tbl->GetNumberRows() - cur_row ); + + tbl->AppendRows( newRowsNeeded ); + } + + for( int row = cur_row; rows.HasMoreTokens(); ++row ) + { + wxString rowTxt = rows.GetNextToken(); + + wxStringTokenizer cols( rowTxt, COL_SEP, wxTOKEN_RET_EMPTY ); + + for( int col = cur_col; cols.HasMoreTokens(); ++col ) + { + wxString cellTxt = cols.GetNextToken(); + tbl->SetValue( row, col, cellTxt ); + } + } + m_grid->AutoSizeColumns( false ); +} + + +void GRID_TRICKS::cutcopy( bool doCut ) +{ + if( wxTheClipboard->Open() ) + { + wxGridTableBase* tbl = m_grid->GetTable(); + wxString txt; + + // fill txt with a format that is compatible with most spreadsheets + for( int row = m_sel_row_start; row < m_sel_row_start + m_sel_row_count; ++row ) + { + for( int col = m_sel_col_start; col < m_sel_col_start + m_sel_col_count; ++col ) + { + txt += tbl->GetValue( row, col ); + + if( col < m_sel_col_start + m_sel_col_count - 1 ) // that was not last column + txt += COL_SEP; + + if( doCut ) + tbl->SetValue( row, col, wxEmptyString ); + } + txt += ROW_SEP; + } + + wxTheClipboard->SetData( new wxTextDataObject( txt ) ); + wxTheClipboard->Close(); + + if( doCut ) + { + m_grid->AutoSizeColumns( false ); + m_grid->ForceRefresh(); + } + } +} + diff --git a/common/hotkeys_basic.cpp b/common/hotkeys_basic.cpp new file mode 100644 index 0000000..04dfd7f --- /dev/null +++ b/common/hotkeys_basic.cpp @@ -0,0 +1,846 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 Jean-Pierre Charras, j-p.charras at wanadoo.fr + * Copyright (C) 2010-2011 Wayne Stambaugh <stambaughw@verizon.net> + * Copyright (C) 1992-2015 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 + */ + +/** + * @file hotkeys_basic.cpp + * @brief Some functions to handle hotkeys in KiCad + */ + +#include <fctsys.h> +#include <kiface_i.h> +#include <hotkeys_basic.h> +#include <id.h> +#include <confirm.h> +#include <kicad_string.h> +#include <gestfich.h> +#include <wxstruct.h> +#include <macros.h> +#include <dialog_hotkeys_editor.h> +#include <menus_helpers.h> +#include <tool/tool_manager.h> + +#include <wx/apptrait.h> +#include <wx/stdpaths.h> +#include <wx/tokenzr.h> + +#define HOTKEYS_CONFIG_KEY wxT( "Keys" ) + +wxString g_CommonSectionTag( wxT( "[common]" ) ); + + +/* Class to handle hotkey commands hotkeys have a default value + * This class allows the real key code changed by user from a key code list + * file. + */ + +EDA_HOTKEY::EDA_HOTKEY( const wxChar* infomsg, int idcommand, int keycode, int idmenuevent ) +{ + m_KeyCode = keycode; // Key code (ascii value for ascii keys + + // or wxWidgets code for function key + m_InfoMsg = infomsg; // info message. + m_Idcommand = idcommand; // internal id for the corresponding + + // command (see hotkey_id_commnand list) + m_IdMenuEvent = idmenuevent; // id to call the corresponding event + // (if any) (see id.h) +} + + +EDA_HOTKEY::EDA_HOTKEY( const EDA_HOTKEY* base ) +{ + m_KeyCode = base->m_KeyCode; + m_InfoMsg = base->m_InfoMsg; + m_Idcommand = base->m_Idcommand; + m_IdMenuEvent = base->m_IdMenuEvent; +} + + +EDA_HOTKEY_CLIENT_DATA::~EDA_HOTKEY_CLIENT_DATA() +{ +} + + +/* class to handle the printable name and the keycode + */ +struct hotkey_name_descr +{ + const wxChar* m_Name; + int m_KeyCode; +}; + +/* table giving the hotkey name from the hotkey code, for special keys + * Note : when modifiers (ATL, SHIFT, CTRL) do not modify + * the code of the key, do need to enter the modified key code + * For instance wxT( "F1" ), WXK_F1 handle F1, AltF1, CtrlF1 ... + * Key names are: + * "Space","Ctrl+Space","Alt+Space" or + * "Alt+A","Ctrl+F1", ... + */ +static struct hotkey_name_descr hotkeyNameList[] = +{ + { wxT( "F1" ), WXK_F1 }, + { wxT( "F2" ), WXK_F2 }, + { wxT( "F3" ), WXK_F3 }, + { wxT( "F4" ), WXK_F4 }, + { wxT( "F5" ), WXK_F5 }, + { wxT( "F6" ), WXK_F6 }, + { wxT( "F7" ), WXK_F7 }, + { wxT( "F8" ), WXK_F8 }, + { wxT( "F9" ), WXK_F9 }, + { wxT( "F10" ), WXK_F10 }, + { wxT( "F11" ), WXK_F11 }, + { wxT( "F12" ), WXK_F12 }, + + { wxT( "Esc" ), WXK_ESCAPE }, + { wxT( "Del" ), WXK_DELETE }, + { wxT( "Tab" ), WXK_TAB }, + { wxT( "BkSp" ), WXK_BACK }, + { wxT( "Ins" ), WXK_INSERT }, + + { wxT( "Home" ), WXK_HOME }, + { wxT( "End" ), WXK_END }, + { wxT( "PgUp" ), WXK_PAGEUP }, + { wxT( "PgDn" ), WXK_PAGEDOWN }, + + { wxT( "Up" ), WXK_UP }, + { wxT( "Down" ), WXK_DOWN }, + { wxT( "Left" ), WXK_LEFT }, + { wxT( "Right" ), WXK_RIGHT }, + + { wxT( "Return" ), WXK_RETURN }, + + { wxT( "Space" ), WXK_SPACE }, + + // Do not change this line: end of list + { wxT( "" ), 0 } +}; + +// name of modifier keys. +// Note: the Ctrl key is Cmd key on Mac OS X. +// However, in wxWidgets defs, the key WXK_CONTROL is the Cmd key, +// so the code using WXK_CONTROL should be ok on any system. +// (on Mac OS X the actual Ctrl key code is WXK_RAW_CONTROL) +#ifdef __WXMAC__ +#define USING_MAC_CMD +#endif + +#ifdef USING_MAC_CMD +#define MODIFIER_CTRL wxT( "Cmd+" ) +#else +#define MODIFIER_CTRL wxT( "Ctrl+" ) +#endif +#define MODIFIER_CMD_MAC wxT( "Cmd+" ) +#define MODIFIER_CTRL_BASE wxT( "Ctrl+" ) +#define MODIFIER_ALT wxT( "Alt+" ) +#define MODIFIER_SHIFT wxT( "Shift+" ) + + +/** + * Function KeyNameFromKeyCode + * return the key name from the key code + * Only some wxWidgets key values are handled for function key ( see + * hotkeyNameList[] ) + * @param aKeycode = key code (ascii value, or wxWidgets value for function keys) + * @param aIsFound = a pointer to a bool to return true if found, or false. an be NULL default) + * @return the key name in a wxString + */ +wxString KeyNameFromKeyCode( int aKeycode, bool* aIsFound ) +{ + wxString keyname, modifier, fullkeyname; + int ii; + bool found = false; + + // Assume keycode of 0 is "unassigned" + if( aKeycode == 0 ) + return wxT( "<unassigned>"); + + if( (aKeycode & GR_KB_CTRL) != 0 ) + modifier << MODIFIER_CTRL; + + if( (aKeycode & GR_KB_ALT) != 0 ) + modifier << MODIFIER_ALT; + + if( (aKeycode & GR_KB_SHIFT) != 0 ) + modifier << MODIFIER_SHIFT; + + aKeycode &= ~( GR_KB_CTRL | GR_KB_ALT | GR_KB_SHIFT ); + + if( (aKeycode > ' ') && (aKeycode < 0x7F ) ) + { + found = true; + keyname.Append( (wxChar)aKeycode ); + } + else + { + for( ii = 0; ; ii++ ) + { + if( hotkeyNameList[ii].m_KeyCode == 0 ) // End of list + { + keyname = wxT( "<unknown>" ); + break; + } + + if( hotkeyNameList[ii].m_KeyCode == aKeycode ) + { + keyname = hotkeyNameList[ii].m_Name; + found = true; + break; + } + } + } + + if( aIsFound ) + *aIsFound = found; + + fullkeyname = modifier + keyname; + return fullkeyname; +} + + +/* + * helper function use in AddHotkeyName to calculate an accelerator string + * In some menus, accelerators do not perform exactly the same action as + * the hotkey that perform a similar action. + * this is usually the case when this action uses the current mouse position + * for instance zoom action is ran from the F1 key or the Zoom menu. + * a zoom uses the mouse position from a hot key and not from the menu + * In this case, the accelerator if Shift+<hotkey> + * But for many keys, the Shift modifier is not usable, and the accelerator is Alt+<hotkey> + */ +static void AddModifierToKey( wxString& aFullKey, const wxString & aKey ) +{ + if( (aKey.Length() == 1) && (aKey[0] >= 'A') && (aKey[0] <= 'Z')) + // We can use Shift+<key> as accelerator and <key> for hot key + aFullKey << wxT( "\t" ) << MODIFIER_SHIFT << aKey; + else + // We must use Alt+<key> as accelerator and <key> for hot key + aFullKey << wxT( "\t" ) << MODIFIER_ALT << aKey; +} + + +/* AddHotkeyName + * Add the key name from the Command id value ( m_Idcommand member value) + * aText = a wxString. returns aText + key name + * aList = pointer to a EDA_HOTKEY list of commands + * aCommandId = Command Id value + * aShortCutType = IS_HOTKEY to add <tab><keyname> (shortcuts in menus, same as hotkeys) + * IS_ACCELERATOR to add <tab><Shift+keyname> (accelerators in menus, not hotkeys) + * IS_COMMENT to add <spaces><(keyname)> mainly in tool tips + * Return a wxString (aTest + key name) if key found or aText without modification + */ +wxString AddHotkeyName( const wxString& aText, EDA_HOTKEY** aList, + int aCommandId, HOTKEY_ACTION_TYPE aShortCutType ) +{ + wxString msg = aText; + wxString keyname; + + if( aList ) + keyname = KeyNameFromCommandId( aList, aCommandId ); + + if( !keyname.IsEmpty() ) + { + switch( aShortCutType ) + { + case IS_HOTKEY: + msg << wxT( "\t" ) << keyname; + break; + + case IS_ACCELERATOR: + AddModifierToKey( msg, keyname ); + break; + + case IS_COMMENT: + msg << wxT( " (" ) << keyname << wxT( ")" ); + break; + } + } + +#ifdef USING_MAC_CMD + // On OSX, the modifier equivalent to the Ctrl key of PCs + // is the Cmd key, but in code we should use Ctrl as prefix in menus + msg.Replace( MODIFIER_CMD_MAC, MODIFIER_CTRL_BASE ); +#endif + + return msg; +} + + +/* AddHotkeyName + * Add the key name from the Command id value ( m_Idcommand member value) + * aText = a wxString. returns aText + key name + * aList = pointer to a EDA_HOTKEY_CONFIG DescrList of commands + * aCommandId = Command Id value + * aShortCutType = IS_HOTKEY to add <tab><keyname> (active shortcuts in menus) + * IS_ACCELERATOR to add <tab><Shift+keyname> (active accelerators in menus) + * IS_COMMENT to add <spaces><(keyname)> + * Return a wxString (aText + key name) if key found or aText without modification + */ +wxString AddHotkeyName( const wxString& aText, + struct EDA_HOTKEY_CONFIG* aDescList, + int aCommandId, + HOTKEY_ACTION_TYPE aShortCutType ) +{ + wxString msg = aText; + wxString keyname; + EDA_HOTKEY** list; + + if( aDescList ) + { + for( ; aDescList->m_HK_InfoList != NULL; aDescList++ ) + { + list = aDescList->m_HK_InfoList; + keyname = KeyNameFromCommandId( list, aCommandId ); + + if( !keyname.IsEmpty() ) + { + switch( aShortCutType ) + { + case IS_HOTKEY: + msg << wxT( "\t" ) << keyname; + break; + + case IS_ACCELERATOR: + AddModifierToKey( msg, keyname ); + break; + + case IS_COMMENT: + msg << wxT( " (" ) << keyname << wxT( ")" ); + break; + } + + break; + } + } + } + +#ifdef USING_MAC_CMD + // On OSX, the modifier equivalent to the Ctrl key of PCs + // is the Cmd key, but in code we should use Ctrl as prefix in menus + msg.Replace( MODIFIER_CMD_MAC, MODIFIER_CTRL_BASE ); +#endif + + return msg; +} + + +/** + * Function KeyNameFromCommandId + * return the key name from the Command id value ( m_Idcommand member value) + * @param aList = pointer to a EDA_HOTKEY list of commands + * @param aCommandId = Command Id value + * @return the key name in a wxString + */ +wxString KeyNameFromCommandId( EDA_HOTKEY** aList, int aCommandId ) +{ + wxString keyname; + + for( ; *aList != NULL; aList++ ) + { + EDA_HOTKEY* hk_decr = *aList; + + if( hk_decr->m_Idcommand == aCommandId ) + { + keyname = KeyNameFromKeyCode( hk_decr->m_KeyCode ); + break; + } + } + + return keyname; +} + + +/** + * Function KeyCodeFromKeyName + * return the key code from its key name + * Only some wxWidgets key values are handled for function key + * @param keyname = wxString key name to find in hotkeyNameList[], + * like F2 or space or an usual (ascii) char. + * @return the key code + */ +int KeyCodeFromKeyName( const wxString& keyname ) +{ + int ii, keycode = 0; + + // Search for modifiers: Ctrl+ Alt+ and Shift+ + // Note: on Mac OSX, the Cmd key is equiv here to Ctrl + wxString key = keyname; + wxString prefix; + int modifier = 0; + + while( 1 ) + { + prefix.Empty(); + + if( key.StartsWith( MODIFIER_CTRL_BASE ) ) + { + modifier |= GR_KB_CTRL; + prefix = MODIFIER_CTRL_BASE; + } + else if( key.StartsWith( MODIFIER_CMD_MAC ) ) + { + modifier |= GR_KB_CTRL; + prefix = MODIFIER_CMD_MAC; + } + else if( key.StartsWith( MODIFIER_ALT ) ) + { + modifier |= GR_KB_ALT; + prefix = MODIFIER_ALT; + } + else if( key.StartsWith( MODIFIER_SHIFT ) ) + { + modifier |= GR_KB_SHIFT; + prefix = MODIFIER_SHIFT; + } + else + { + break; + } + + if( !prefix.IsEmpty() ) + key.Remove( 0, prefix.Len() ); + } + + if( (key.length() == 1) && (key[0] > ' ') && (key[0] < 0x7F) ) + { + keycode = key[0]; + keycode += modifier; + return keycode; + } + + for( ii = 0; ; ii++ ) + { + if( hotkeyNameList[ii].m_KeyCode == 0 ) // End of list reached + break; + + if( key.CmpNoCase( hotkeyNameList[ii].m_Name ) == 0 ) + { + keycode = hotkeyNameList[ii].m_KeyCode + modifier; + break; + } + } + + return keycode; +} + + +/* DisplayHotkeyList + * Displays the current hotkey list + * aList = a EDA_HOTKEY_CONFIG list(Null terminated) + */ +#include <html_messagebox.h> + +void DisplayHotkeyList( EDA_BASE_FRAME* aFrame, struct EDA_HOTKEY_CONFIG* aDescList ) +{ + wxString keyname; + wxString keymessage; + EDA_HOTKEY** list; + + wxString msg = wxT( "<html><body bgcolor=\"#E2E2E2\">" ); + + msg += wxT( "<H3>" ); + msg += _( "Hotkeys List" ); + msg += wxT( "</H3> <table cellpadding=\"0\">" ); + + for( ; aDescList->m_HK_InfoList != NULL; aDescList++ ) + { + list = aDescList->m_HK_InfoList; + + for( ; *list != NULL; list++ ) + { + EDA_HOTKEY* hk_decr = *list; + + if( !hk_decr->m_InfoMsg.Contains( wxT( "Macros" ) ) ) + { + keyname = KeyNameFromKeyCode( hk_decr->m_KeyCode ); + keymessage = wxGetTranslation( hk_decr->m_InfoMsg ); + + // Some chars are modified, using html encoding, to be + // displayed by DisplayHtmlInfoMessage() + keyname.Replace( wxT( "<" ), wxT( "<" ) ); + keyname.Replace( wxT( ">" ), wxT( ">" ) ); + msg += wxT( "<tr><td>" ) + keymessage + wxT( "</td>" ); + msg += wxT( "<td><b> " ) + keyname + wxT( "</b></td></tr>" ); + } + } + } + + msg += wxT( "</table></html></body>" ); + +#if 0 // Set to 1 to create a modal dialog (blocking) + DisplayHtmlInfoMessage( aFrame, _( "Hotkeys List" ), msg, wxSize( 340, 750 ) ); +#else + // Create a non modal dialog, which shows the list of hotkeys until dismissed + // but does not block the parent window + HTML_MESSAGE_BOX *dlg = new HTML_MESSAGE_BOX( aFrame, _( "Hotkeys List" ), + wxDefaultPosition, wxSize( 340, 750 ) ); + dlg->AddHTML_Text( msg ); + dlg->Show( true ); +#endif +} + + +/** + * Function GetDescriptorFromHotkey + * Return a EDA_HOTKEY * pointer from a key code for OnHotKey() function + * @param aKey = key code (ascii value, or wxWidgets value for function keys + * @param aList = pointer to a EDA_HOTKEY list of commands + * @return the corresponding EDA_HOTKEY pointer from the EDA_HOTKEY List + */ +EDA_HOTKEY* GetDescriptorFromHotkey( int aKey, EDA_HOTKEY** aList ) +{ + for( ; *aList != NULL; aList++ ) + { + EDA_HOTKEY* hk_decr = *aList; + + if( hk_decr->m_KeyCode == aKey ) + return hk_decr; + } + + return NULL; +} + + +EDA_HOTKEY* GetDescriptorFromCommand( int aCommand, EDA_HOTKEY** aList ) +{ + for( ; *aList != NULL; aList++ ) + { + EDA_HOTKEY* hk_decr = *aList; + + if( hk_decr->m_Idcommand == aCommand ) + return hk_decr; + } + + return NULL; +} + + +int EDA_BASE_FRAME::WriteHotkeyConfig( struct EDA_HOTKEY_CONFIG* aDescList, + wxString* aFullFileName ) +{ + wxString msg; + wxString keyname, infokey; + + msg = wxT( "$hotkey list\n" ); + + // Print the current hotkey list + EDA_HOTKEY** list; + + for( ; aDescList->m_HK_InfoList != NULL; aDescList++ ) + { + if( aDescList->m_Title ) + { + msg += wxT( "# " ); + msg += *aDescList->m_Title; + msg += wxT( "\n" ); + } + + msg += *aDescList->m_SectionTag; + msg += wxT( "\n" ); + + list = aDescList->m_HK_InfoList; + + for( ; *list != NULL; list++ ) + { + EDA_HOTKEY* hk_decr = *list; + msg += wxT( "shortcut " ); + keyname = KeyNameFromKeyCode( hk_decr->m_KeyCode ); + AddDelimiterString( keyname ); + infokey = hk_decr->m_InfoMsg; + AddDelimiterString( infokey ); + msg += keyname + wxT( ": " ) + infokey + wxT( "\n" ); + } + } + + msg += wxT( "$Endlist\n" ); + + if( aFullFileName ) + { + FILE* file = wxFopen( *aFullFileName, wxT( "wt" ) ); + + if( file ) + { + fputs( TO_UTF8( msg ), file ); + fclose( file ); + } + else + { + msg.Printf( wxT( "Unable to write file %s" ), GetChars( *aFullFileName ) ); + return 0; + } + } + else + { + wxFileName fn( GetName() ); + fn.SetExt( DEFAULT_HOTKEY_FILENAME_EXT ); + wxConfigBase* config = GetNewConfig( fn.GetFullPath() ); + config->Write( HOTKEYS_CONFIG_KEY, msg ); + delete config; + } + + return 1; +} + + +int EDA_BASE_FRAME::ReadHotkeyConfigFile( const wxString& aFilename, + struct EDA_HOTKEY_CONFIG* aDescList ) +{ + wxFileName fn( aFilename ); + fn.SetExt( DEFAULT_HOTKEY_FILENAME_EXT ); + + wxFile cfgfile( fn.GetFullPath() ); + + if( !cfgfile.IsOpened() ) // There is a problem to open file + return 0; + + // get length + cfgfile.SeekEnd(); + wxFileOffset size = cfgfile.Tell(); + cfgfile.Seek( 0 ); + + // read data + char* buffer = new char[size]; + cfgfile.Read( buffer, size ); + + wxString data( buffer, wxConvUTF8 ); + + // parse + ParseHotkeyConfig( data, aDescList ); + + // cleanup + delete[] buffer; + cfgfile.Close(); + return 1; +} + + +void ReadHotkeyConfig( const wxString& Appname, struct EDA_HOTKEY_CONFIG* aDescList ) +{ + wxFileName fn( Appname ); + fn.SetExt( DEFAULT_HOTKEY_FILENAME_EXT ); + + wxConfigBase* config = GetNewConfig( fn.GetFullPath() ); + + if( !config->HasEntry( HOTKEYS_CONFIG_KEY ) ) + { + // assume defaults are ok + return; + } + + wxString data; + config->Read( HOTKEYS_CONFIG_KEY, &data ); + delete config; + + ParseHotkeyConfig( data, aDescList ); +} + + +/* Function ReadHotkeyConfig + * Read configuration data and fill the current hotkey list with hotkeys + * aDescList is the current hotkey list descr. to initialize. + */ +int EDA_BASE_FRAME::ReadHotkeyConfig( struct EDA_HOTKEY_CONFIG* aDescList ) +{ + ::ReadHotkeyConfig( GetName(), aDescList ); + return 1; +} + + +/* Function ParseHotkeyConfig + * the input format is: shortcut "key" "function" + * lines starting by # are ignored (comments) + * lines like [xxx] are tags (example: [common] or [libedit] which identify sections + */ +void ParseHotkeyConfig( const wxString& data, + struct EDA_HOTKEY_CONFIG* aDescList ) +{ + // Read the config + wxStringTokenizer tokenizer( data, L"\r\n", wxTOKEN_STRTOK ); + EDA_HOTKEY** CurrentHotkeyList = 0; + + while( tokenizer.HasMoreTokens() ) + { + wxString line = tokenizer.GetNextToken(); + wxStringTokenizer lineTokenizer( line ); + + wxString line_type = lineTokenizer.GetNextToken(); + + if( line_type[0] == '#' ) //comment + continue; + + if( line_type[0] == '[' ) // A tag is found. search infos in list + { + CurrentHotkeyList = 0; + EDA_HOTKEY_CONFIG* DList = aDescList; + + for( ; DList->m_HK_InfoList; DList++ ) + { + if( *DList->m_SectionTag == line_type ) + { + CurrentHotkeyList = DList->m_HK_InfoList; + break; + } + } + + continue; + } + + if( line_type == wxT( "$Endlist" ) ) + break; + + if( line_type != wxT( "shortcut" ) ) + continue; + + if( CurrentHotkeyList == NULL ) + continue; + + // Get the key name + lineTokenizer.SetString( lineTokenizer.GetString(), L"\"\r\n\t ", wxTOKEN_STRTOK ); + wxString keyname = lineTokenizer.GetNextToken(); + + wxString remainder = lineTokenizer.GetString(); + + // Get the command name + wxString fctname = remainder.AfterFirst( '\"' ).BeforeFirst( '\"' ); + + // search the hotkey in current hotkey list + for( EDA_HOTKEY** list = CurrentHotkeyList; *list != NULL; list++ ) + { + EDA_HOTKEY* hk_decr = *list; + + if( hk_decr->m_InfoMsg == fctname ) + { + hk_decr->m_KeyCode = KeyCodeFromKeyName( keyname ); + break; + } + } + } +} + + +void EDA_BASE_FRAME::ImportHotkeyConfigFromFile( EDA_HOTKEY_CONFIG* aDescList, + const wxString& aDefaultShortname ) +{ + wxString ext = DEFAULT_HOTKEY_FILENAME_EXT; + wxString mask = wxT( "*." ) + ext; + +#if 0 // pass in the project dir as an argument + wxString path = wxPathOnly( Prj().GetProjectFullName() ); +#else + wxString path = GetMruPath(); +#endif + wxFileName fn( aDefaultShortname ); + fn.SetExt( DEFAULT_HOTKEY_FILENAME_EXT ); + + wxString filename = EDA_FILE_SELECTOR( _( "Read Hotkey Configuration File:" ), + path, + fn.GetFullPath(), + ext, + mask, + this, + wxFD_OPEN, + true ); + + if( filename.IsEmpty() ) + return; + + ReadHotkeyConfigFile( filename, aDescList ); + SetMruPath( wxFileName( filename ).GetPath() ); +} + + +void EDA_BASE_FRAME::ExportHotkeyConfigToFile( EDA_HOTKEY_CONFIG* aDescList, + const wxString& aDefaultShortname ) +{ + wxString ext = DEFAULT_HOTKEY_FILENAME_EXT; + wxString mask = wxT( "*." ) + ext; + +#if 0 + wxString path = wxPathOnly( Prj().GetProjectFullName() ); +#else + wxString path = GetMruPath(); +#endif + wxFileName fn( aDefaultShortname ); + fn.SetExt( DEFAULT_HOTKEY_FILENAME_EXT ); + + wxString filename = EDA_FILE_SELECTOR( _( "Write Hotkey Configuration File:" ), + path, + fn.GetFullPath(), + ext, + mask, + this, + wxFD_SAVE, + true ); + + if( filename.IsEmpty() ) + return; + + WriteHotkeyConfig( aDescList, &filename ); + SetMruPath( wxFileName( filename ).GetPath() ); +} + + +/* add hotkey config options submenu to aMenu + */ +void AddHotkeyConfigMenu( wxMenu* aMenu ) +{ + if( aMenu == NULL ) + return; + + wxMenu* HotkeySubmenu = new wxMenu(); + + // List existing hotkey menu + AddMenuItem( HotkeySubmenu, + ID_PREFERENCES_HOTKEY_SHOW_CURRENT_LIST, + _( "&List Current Keys" ), + _( "Displays the current hotkeys list and corresponding commands" ), + KiBitmap( info_xpm ) ); + + // Call hotkeys editor + AddMenuItem( HotkeySubmenu, ID_PREFERENCES_HOTKEY_SHOW_EDITOR, + _( "&Edit Hotkeys" ), + _( "Call the hotkeys editor" ), + KiBitmap( editor_xpm ) ); + + HotkeySubmenu->AppendSeparator(); + + // create hotkey file to export current hotkeys config + AddMenuItem( HotkeySubmenu, ID_PREFERENCES_HOTKEY_EXPORT_CONFIG, + _( "E&xport Hotkeys" ), + _( "Create a hotkey configuration file to export the current hotkeys" ), + KiBitmap( save_setup_xpm ) ); + + // Reload hotkey file + AddMenuItem( HotkeySubmenu, ID_PREFERENCES_HOTKEY_IMPORT_CONFIG, + _( "&Import Hotkeys" ), + _( "Load an existing hotkey configuration file" ), + KiBitmap( reload_xpm ) ); + + // Append HotkeySubmenu to menu + AddMenuItem( aMenu, HotkeySubmenu, + wxID_ANY, _( "&Hotkeys" ), + _( "Hotkeys configuration and preferences" ), + KiBitmap( hotkeys_xpm ) ); +} diff --git a/common/html_messagebox.cpp b/common/html_messagebox.cpp new file mode 100644 index 0000000..0abc8ef --- /dev/null +++ b/common/html_messagebox.cpp @@ -0,0 +1,106 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.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 <fctsys.h> +#include <html_messagebox.h> +#include <macros.h> +#include <common.h> + + +HTML_MESSAGE_BOX::HTML_MESSAGE_BOX( wxWindow* parent, const wxString& aTitle, + wxPoint aPos, wxSize aSize) : + DIALOG_DISPLAY_HTML_TEXT_BASE( parent, wxID_ANY, aTitle, aPos, aSize ) +{ + m_htmlWindow->SetLayoutDirection( wxLayout_LeftToRight ); + ListClear(); + Center(); +} + + +void HTML_MESSAGE_BOX::OnCloseButtonClick( wxCommandEvent& event ) +{ + // the dialog can be shown modal or not modal. + // therefore, use the right way to close it. + if( IsModal() ) + EndModal( 0 ); + else + Destroy(); +} + + +void HTML_MESSAGE_BOX::ListClear() +{ + m_htmlWindow->SetPage( wxEmptyString ); +} + + +void HTML_MESSAGE_BOX::ListSet( const wxString& aList ) +{ + wxArrayString strings_list; + wxStringSplit( aList, strings_list, wxChar( '\n' ) ); + + wxString msg = wxT( "<ul>" ); + + for ( unsigned ii = 0; ii < strings_list.GetCount(); ii++ ) + { + msg += wxT( "<li>" ); + msg += strings_list.Item( ii ) + wxT( "</li>" ); + } + + msg += wxT( "</ul>" ); + + m_htmlWindow->AppendToPage( msg ); +} + + +void HTML_MESSAGE_BOX::ListSet( const wxArrayString& aList ) +{ + wxString msg = wxT( "<ul>" ); + + for( unsigned ii = 0; ii < aList.GetCount(); ii++ ) + { + msg += wxT( "<li>" ); + msg += aList.Item( ii ) + wxT( "</li>" ); + } + + msg += wxT( "</ul>" ); + + m_htmlWindow->AppendToPage( msg ); +} + + +void HTML_MESSAGE_BOX::MessageSet( const wxString& message ) +{ + wxString message_value = wxString::Format( + wxT( "<b>%s</b><br>" ), GetChars( message ) ); + + m_htmlWindow->AppendToPage( message_value ); +} + + +void HTML_MESSAGE_BOX::AddHTML_Text( const wxString& message ) +{ + m_htmlWindow->AppendToPage( message ); +} + diff --git a/common/kicad_curl/kicad_curl.cpp b/common/kicad_curl/kicad_curl.cpp new file mode 100644 index 0000000..fb4413c --- /dev/null +++ b/common/kicad_curl/kicad_curl.cpp @@ -0,0 +1,224 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 Mark Roszko <mark.roszko@gmail.com> + * Copyright (C) 2016 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2015 KiCad Developers, see CHANGELOG.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 3 + * 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 + */ + +// kicad_curl.h must be included before wx headers, to avoid +// conflicts for some defines, at least on Windows +#include <kicad_curl/kicad_curl.h> + +#include <wx/log.h> +#include <wx/dynlib.h> + +#include <macros.h> +#include <fctsys.h> +#include <ki_mutex.h> // MUTEX and MUTLOCK +#include <richio.h> + + + +// These are even more private than class members, and since there is only +// one instance of KICAD_CURL ever, these statics are hidden here to simplify the +// client (API) header file. +static volatile bool s_initialized; + +static MUTEX s_lock; // for s_initialized + +// Assume that on these platforms libcurl uses OpenSSL +#if defined(__linux__) || defined(__MINGW32__) + +#include <openssl/crypto.h> + +static MUTEX* s_crypto_locks; + +static void lock_callback( int mode, int type, const char* file, int line ) +{ + (void)file; + (void)line; + + wxASSERT( s_crypto_locks && unsigned( type ) < unsigned( CRYPTO_num_locks() ) ); + + //DBG( printf( "%s: mode=0x%x type=%d file=%s line=%d\n", __func__, mode, type, file, line );) + + if( mode & CRYPTO_LOCK ) + { + s_crypto_locks[ type ].lock(); + } + else + { + s_crypto_locks[ type ].unlock(); + } +} + + +static void init_locks() +{ + s_crypto_locks = new MUTEX[ CRYPTO_num_locks() ]; + + // From http://linux.die.net/man/3/crypto_set_id_callback: + + /* + + OpenSSL can safely be used in multi-threaded applications provided that at + least two callback functions are set, locking_function and threadid_func. + + locking_function(int mode, int n, const char *file, int line) is needed to + perform locking on shared data structures. (Note that OpenSSL uses a number + of global data structures that will be implicitly shared whenever multiple + threads use OpenSSL.) Multi-threaded applications will crash at random if it + is not set. + + threadid_func( CRYPTO_THREADID *id) is needed to record the + currently-executing thread's identifier into id. The implementation of this + callback should not fill in id directly, but should use + CRYPTO_THREADID_set_numeric() if thread IDs are numeric, or + CRYPTO_THREADID_set_pointer() if they are pointer-based. If the application + does not register such a callback using CRYPTO_THREADID_set_callback(), then + a default implementation is used - on Windows and BeOS this uses the + system's default thread identifying APIs, and on all other platforms it uses + the address of errno. The latter is satisfactory for thread-safety if and + only if the platform has a thread-local error number facility. + + Dick: "sounds like CRYPTO_THREADID_set_callback() is not mandatory on our + 2 OpenSSL platforms." + + */ + + CRYPTO_set_locking_callback( &lock_callback ); +} + + +static void kill_locks() +{ + CRYPTO_set_locking_callback( NULL ); + + delete[] s_crypto_locks; + + s_crypto_locks = NULL; +} + +#else + +inline void init_locks() { /* dummy */ } +inline void kill_locks() { /* dummy */ } + +#endif + +/// At process termination, using atexit() keeps the CURL stuff out of the +/// singletops and PGM_BASE. +static void at_terminate() +{ + KICAD_CURL::Cleanup(); +} + + +void KICAD_CURL::Init() +{ + // We test s_initialized twice in an effort to avoid + // unnecessarily locking s_lock. This understands that the common case + // will not need to lock. + if( !s_initialized ) + { + MUTLOCK lock( s_lock ); + + if( !s_initialized ) + { + if( curl_global_init( CURL_GLOBAL_ALL ) != CURLE_OK ) + { + THROW_IO_ERROR( "curl_global_init() failed." ); + } + + init_locks(); + + wxLogDebug( "Using %s", GetVersion() ); + + s_initialized = true; + } + } +} + + +void KICAD_CURL::Cleanup() +{ + /* + + Calling MUTLOCK() from a static destructor will typically be bad, since the + s_lock may already have been statically destroyed itself leading to a boost + exception. (Remember C++ does not provide certain sequencing of static + destructor invocation.) + + To prevent this we test s_initialized twice, which ensures that the MUTLOCK + is only instantiated on the first call, which should be from + PGM_BASE::destroy() which is first called earlier than static destruction. + Then when called again from the actual PGM_BASE::~PGM_BASE() function, + MUTLOCK will not be instantiated because s_initialized will be false. + + */ + + if( s_initialized ) + { + MUTLOCK lock( s_lock ); + + if( s_initialized ) + { + curl_global_cleanup(); + + kill_locks(); + + atexit( &at_terminate ); + + s_initialized = false; + } + } +} + + +std::string KICAD_CURL::GetSimpleVersion() +{ + if( !s_initialized ) + Init(); + + curl_version_info_data* info = curl_version_info( CURLVERSION_NOW ); + + std::string res; + + if( info->version ) + { + res += "libcurl version: " + std::string( info->version ); + } + + res += " ("; + + if( info->features & CURL_VERSION_SSL ) + { + res += "with SSL - "; + res += std::string( info->ssl_version ); + } + else + { + res += "without SSL"; + } + res += ")"; + + return res; +} diff --git a/common/kicad_curl/kicad_curl_easy.cpp b/common/kicad_curl/kicad_curl_easy.cpp new file mode 100644 index 0000000..1e84afe --- /dev/null +++ b/common/kicad_curl/kicad_curl_easy.cpp @@ -0,0 +1,94 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 Mark Roszko <mark.roszko@gmail.com> + * Copyright (C) 2015 KiCad Developers, see CHANGELOG.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 3 + * 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 <kicad_curl/kicad_curl_easy.h> + +#include <cstddef> +#include <exception> +#include <stdarg.h> +#include <sstream> +#include <richio.h> + + +static size_t write_callback( void* contents, size_t size, size_t nmemb, void* userp ) +{ + size_t realsize = size * nmemb; + + std::string* p = (std::string*) userp; + + p->append( (const char*) contents, realsize ); + + return realsize; +} + + +KICAD_CURL_EASY::KICAD_CURL_EASY() : + m_headers( NULL ) +{ + // Call KICAD_CURL::Init() from in here everytime, but only the first time + // will incur any overhead. This strategy ensures that libcurl is never loaded + // unless it is needed. + + KICAD_CURL::Init(); + + m_CURL = curl_easy_init(); + + if( !m_CURL ) + { + THROW_IO_ERROR( "Unable to initialize CURL session" ); + } + + curl_easy_setopt( m_CURL, CURLOPT_WRITEFUNCTION, write_callback ); + curl_easy_setopt( m_CURL, CURLOPT_WRITEDATA, (void*) &m_buffer ); +} + + +KICAD_CURL_EASY::~KICAD_CURL_EASY() +{ + if( m_headers ) + curl_slist_free_all( m_headers ); + + curl_easy_cleanup( m_CURL ); +} + + +void KICAD_CURL_EASY::Perform() +{ + if( m_headers ) + { + curl_easy_setopt( m_CURL, CURLOPT_HTTPHEADER, m_headers ); + } + + // bonus: retain worst case memory allocation, should re-use occur + m_buffer.clear(); + + CURLcode res = curl_easy_perform( m_CURL ); + + if( res != CURLE_OK ) + { + std::string msg = StrPrintf( "curl_easy_perform()=%d: %s", + res, GetErrorText( res ).c_str() ); + THROW_IO_ERROR( msg ); + } +} diff --git a/common/kiface_i.cpp b/common/kiface_i.cpp new file mode 100644 index 0000000..fdafa4e --- /dev/null +++ b/common/kiface_i.cpp @@ -0,0 +1,107 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2008-2011 Wayne Stambaugh <stambaughw@verizon.net> + * 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 <macros.h> // FROM_UTF8() +#include <wx/config.h> +#include <wx/stdpaths.h> + +#include <kiface_i.h> +#include <pgm_base.h> + +#include <common.h> + +/// Initialize aDst SEARCH_STACK with KIFACE (DSO) specific settings. +/// A non-member function so it an be moved easily, plus it's nobody's business. +static void setSearchPaths( SEARCH_STACK* aDst, KIWAY::FACE_T aId ) +{ + SEARCH_STACK bases; + + SystemDirsAppend( &bases ); + aDst->Clear(); + + for( unsigned i = 0; i < bases.GetCount(); ++i ) + { + wxFileName fn( bases[i], wxEmptyString ); + + // Add schematic library file path to search path list. + // we must add <kicad path>/library and <kicad path>/library/doc + if( aId == KIWAY::FACE_SCH ) + { + // Add schematic doc file path (library/doc) to search path list. + + fn.AppendDir( wxT( "library" ) ); + aDst->AddPaths( fn.GetPath() ); + + fn.AppendDir( wxT( "doc" ) ); + aDst->AddPaths( fn.GetPath() ); + + fn.RemoveLastDir(); + fn.RemoveLastDir(); // "../../" up twice, removing library/doc/ + } + + // Add PCB library file path to search path list. + if( aId == KIWAY::FACE_PCB || aId == KIWAY::FACE_CVPCB ) + { + fn.AppendDir( wxT( "modules" ) ); + aDst->AddPaths( fn.GetPath() ); + + // Add 3D module library file path to search path list. + fn.AppendDir( wxT( "packages3d" ) ); + aDst->AddPaths( fn.GetPath() ); + + fn.RemoveLastDir(); + fn.RemoveLastDir(); // "../../" up twice, remove modules/packages3d + } + + // Add KiCad template file path to search path list. + fn.AppendDir( wxT( "template" ) ); + aDst->AddPaths( fn.GetPath() ); + } + +#ifndef __WXMAC__ + aDst->AddPaths( wxT( "/usr/local/share" ) ); +#endif + +#if 1 && defined(DEBUG) + aDst->Show( "kiface" ); +#endif +} + + +bool KIFACE_I::start_common( int aCtlBits ) +{ + m_start_flags = aCtlBits; + m_bm.Init(); + setSearchPaths( &m_bm.m_search, m_id ); + + return true; +} + + +void KIFACE_I::end_common() +{ + m_bm.End(); +} + diff --git a/common/kiway.cpp b/common/kiway.cpp new file mode 100644 index 0000000..7919b9c --- /dev/null +++ b/common/kiway.cpp @@ -0,0 +1,432 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.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 <string.h> + +#include <macros.h> +#include <kiway.h> +#include <kiway_player.h> +#include <kiway_express.h> +#include <pgm_base.h> +#include <config.h> +#include <id.h> + +#include <wx/stdpaths.h> +#include <wx/debug.h> + + +KIFACE* KIWAY::m_kiface[KIWAY_FACE_COUNT]; +int KIWAY::m_kiface_version[KIWAY_FACE_COUNT]; + + + +KIWAY::KIWAY( PGM_BASE* aProgram, int aCtlBits, wxFrame* aTop ): + m_program( aProgram ), + m_ctl( aCtlBits ), + m_top( 0 ) +{ + SetTop( aTop ); // hook player_destroy_handler() into aTop. + + memset( m_player, 0, sizeof( m_player ) ); +} + + +// Any event types derived from wxCommandEvt, like wxWindowDestroyEvent, are +// propogated upwards to parent windows if not handled below. Therefore the +// m_top window should receive all wxWindowDestroyEvents originating from +// KIWAY_PLAYERs. It does anyways, but now player_destroy_handler eavesdrops +// on that event stream looking for KIWAY_PLAYERs being closed. + +void KIWAY::player_destroy_handler( wxWindowDestroyEvent& event ) +{ + wxWindow* w = event.GetWindow(); + + for( unsigned i=0; i<DIM(m_player); ++i ) + { + // if destroying one of our flock, then mark it as deceased. + if( (wxWindow*) m_player[i] == w ) + { + DBG(printf( "%s: m_player[%u] destroyed: %s\n", + __func__, i, TO_UTF8( m_player[i]->GetName() ) );) + + m_player[i] = 0; + } + } + + event.Skip(); // skip to who, the wxApp? I'm the top window. +} + + +void KIWAY::SetTop( wxFrame* aTop ) +{ + if( m_top ) + { + m_top->Disconnect( wxEVT_DESTROY, wxWindowDestroyEventHandler( KIWAY::player_destroy_handler ), NULL, this ); + } + + if( aTop ) + { + aTop->Connect( wxEVT_DESTROY, wxWindowDestroyEventHandler( KIWAY::player_destroy_handler ), NULL, this ); + } + + m_top = aTop; +} + + +const wxString KIWAY::dso_full_path( FACE_T aFaceId ) +{ + const wxChar* name; + + switch( aFaceId ) + { + case FACE_SCH: name = KIFACE_PREFIX wxT( "eeschema" ); break; + case FACE_PCB: name = KIFACE_PREFIX wxT( "pcbnew" ); break; + case FACE_CVPCB: name = KIFACE_PREFIX wxT( "cvpcb" ); break; + case FACE_GERBVIEW: name = KIFACE_PREFIX wxT( "gerbview" ); break; + case FACE_PL_EDITOR: name = KIFACE_PREFIX wxT( "pl_editor" ); break; + case FACE_PCB_CALCULATOR: name = KIFACE_PREFIX wxT( "pcb_calculator" ); break; + case FACE_BMP2CMP: name = KIFACE_PREFIX wxT( "bitmap2component" ); break; + + default: + wxASSERT_MSG( 0, wxT( "caller has a bug, passed a bad aFaceId" ) ); + return wxEmptyString; + } + +#ifndef __WXMAC__ + wxFileName fn = wxStandardPaths::Get().GetExecutablePath(); +#else + // we have the dso's in main OSX bundle kicad.app/Contents/PlugIns + wxFileName fn = Pgm().GetExecutablePath(); + fn.AppendDir( wxT( "Contents" ) ); + fn.AppendDir( wxT( "PlugIns" ) ); +#endif + + fn.SetName( name ); + + // Here a "suffix" == an extension with a preceding '.', + // so skip the preceding '.' to get an extension + fn.SetExt( KIFACE_SUFFIX + 1 ); // + 1 => &KIFACE_SUFFIX[1] + + return fn.GetFullPath(); +} + + +PROJECT& KIWAY::Prj() const +{ + return *(PROJECT*) &m_project; // strip const-ness, function really is const. +} + + +KIFACE* KIWAY::KiFACE( FACE_T aFaceId, bool doLoad ) +{ + // Since this will be called from python, cannot assume that code will + // not pass a bad aFaceId. + if( unsigned( aFaceId ) >= DIM( m_kiface ) ) + { + // @todo : throw an exception here for python's benefit, at least that + // way it gets some explanatory text. + + wxASSERT_MSG( 0, wxT( "caller has a bug, passed a bad aFaceId" ) ); + return NULL; + } + + // return the previously loaded KIFACE, if it was. + if( m_kiface[aFaceId] ) + return m_kiface[aFaceId]; + + // DSO with KIFACE has not been loaded yet, does caller want to load it? + if( doLoad ) + { + wxString dname = dso_full_path( aFaceId ); + + wxDynamicLibrary dso; + + void* addr = NULL; + + if( !dso.Load( dname, wxDL_VERBATIM | wxDL_NOW | wxDL_GLOBAL ) ) + { + // Failure: error reporting UI was done via wxLogSysError(). + // No further reporting required here. + } + + else if( ( addr = dso.GetSymbol( wxT( KIFACE_INSTANCE_NAME_AND_VERSION ) ) ) == NULL ) + { + // Failure: error reporting UI was done via wxLogSysError(). + // No further reporting required here. + } + + else + { + KIFACE_GETTER_FUNC* getter = (KIFACE_GETTER_FUNC*) addr; + + KIFACE* kiface = getter( &m_kiface_version[aFaceId], KIFACE_VERSION, m_program ); + + // KIFACE_GETTER_FUNC function comment (API) says the non-NULL is unconditional. + wxASSERT_MSG( kiface, + wxT( "attempted DSO has a bug, failed to return a KIFACE*" ) ); + + // Give the DSO a single chance to do its "process level" initialization. + // "Process level" specifically means stay away from any projects in there. + if( kiface->OnKifaceStart( m_program, m_ctl ) ) + { + // Tell dso's wxDynamicLibrary destructor not to Unload() the program image. + (void) dso.Detach(); + + return m_kiface[aFaceId] = kiface; + } + } + + // In any of the failure cases above, dso.Unload() should be called here + // by dso destructor. + // However: + + // There is a file installation bug. We only look for KIFACE_I's which we know + // to exist, and we did not find one. If we do not find one, this is an + // installation bug. + + wxString msg = wxString::Format( wxT( + "Fatal Installation Bug. File:\n" + "'%s'\ncould not be loaded\n" ), GetChars( dname ) ); + + if( ! wxFileExists( dname ) ) + msg << wxT( "It is missing.\n" ); + else + msg << wxT( "Perhaps a shared library (.dll or .so) file is missing.\n" ); + + msg << wxT( "From command line: argv[0]:\n'" ); + msg << wxStandardPaths::Get().GetExecutablePath() << wxT( "'\n" ); + + // This is a fatal error, one from which we cannot recover, nor do we want + // to protect against in client code which would require numerous noisy + // tests in numerous places. So we inform the user that the installation + // is bad. This exception will likely not get caught until way up in the + // wxApp derivative, at which point the process will exit gracefully. + THROW_IO_ERROR( msg ); + } + + return NULL; +} + + +KIWAY::FACE_T KIWAY::KifaceType( FRAME_T aFrameType ) +{ + switch( aFrameType ) + { + case FRAME_SCH: + case FRAME_SCH_LIB_EDITOR: + case FRAME_SCH_VIEWER: + case FRAME_SCH_VIEWER_MODAL: + return FACE_SCH; + + case FRAME_PCB: + case FRAME_PCB_MODULE_EDITOR: + case FRAME_PCB_MODULE_VIEWER: + case FRAME_PCB_MODULE_VIEWER_MODAL: + case FRAME_PCB_FOOTPRINT_WIZARD_MODAL: + case FRAME_PCB_DISPLAY3D: + return FACE_PCB; + + case FRAME_CVPCB: + case FRAME_CVPCB_DISPLAY: + return FACE_CVPCB; + + case FRAME_GERBER: + return FACE_GERBVIEW; + + case FRAME_PL_EDITOR: + return FACE_PL_EDITOR; + + case FRAME_CALC: + return FACE_PCB_CALCULATOR; + + case FRAME_BM2CMP: + return FACE_BMP2CMP; + + default: + return FACE_T( -1 ); + } +} + + +KIWAY_PLAYER* KIWAY::Player( FRAME_T aFrameType, bool doCreate ) +{ + // Since this will be called from python, cannot assume that code will + // not pass a bad aFrameType. + if( unsigned( aFrameType ) >= DIM( m_player ) ) + { + // @todo : throw an exception here for python's benefit, at least that + // way it gets some explanatory text. + + wxASSERT_MSG( 0, wxT( "caller has a bug, passed a bad aFrameType" ) ); + return NULL; + } + + // return the previously opened window + if( m_player[aFrameType] ) + return m_player[aFrameType]; + + if( doCreate ) + { + FACE_T face_type = KifaceType( aFrameType ); + wxASSERT( face_type != FACE_T(-1) ); + + KIFACE* kiface = KiFACE( face_type ); + wxASSERT( kiface ); + + if( kiface ) + { + KIWAY_PLAYER* frame = (KIWAY_PLAYER*) kiface->CreateWindow( + m_top, + aFrameType, + this, + m_ctl // questionable need, these same flags where passed to the KIFACE::OnKifaceStart() + ); + wxASSERT( frame ); + + return m_player[aFrameType] = frame; + } + } + + return NULL; +} + + +bool KIWAY::PlayerClose( FRAME_T aFrameType, bool doForce ) +{ + // Since this will be called from python, cannot assume that code will + // not pass a bad aFrameType. + if( unsigned( aFrameType ) >= DIM( m_player ) ) + { + // @todo : throw an exception here for python's benefit, at least that + // way it gets some explanatory text. + + wxASSERT_MSG( 0, wxT( "caller has a bug, passed a bad aFrameType" ) ); + return false; + } + + if( m_player[aFrameType] ) + { + if( m_player[aFrameType]->Close( doForce ) ) + { + m_player[aFrameType] = 0; + return true; + } + + return false; + } + + return true; // window is closed already. +} + + +bool KIWAY::PlayersClose( bool doForce ) +{ + bool ret = true; + + for( unsigned i=0; i < DIM( m_player ); ++i ) + { + ret = ret && PlayerClose( FRAME_T( i ), doForce ); + } + + return ret; +} + + +void KIWAY::ExpressMail( FRAME_T aDestination, + MAIL_T aCommand, const std::string& aPayload, wxWindow* aSource ) +{ + KIWAY_EXPRESS mail( aDestination, aCommand, aPayload, aSource ); + + ProcessEvent( mail ); +} + + +void KIWAY::SetLanguage( int aLanguage ) +{ + Pgm().SetLanguageIdentifier( aLanguage ); + Pgm().SetLanguage(); + +#if 1 + // This is a risky hack that goes away if we allow the language to be + // set only from the top most frame if !Kiface.IsSingle() + + // Only for the C++ project manager, and not for the python one and not for + // single_top do we look for the EDA_BASE_FRAME as the top level window. + // For single_top this is not needed because that window is registered in + // the array below. + if( m_ctl & KFCTL_CPP_PROJECT_SUITE ) + { + EDA_BASE_FRAME* top = (EDA_BASE_FRAME*) m_top; + if( top ) + top->ShowChangedLanguage(); + } +#endif + + for( unsigned i=0; i < DIM( m_player ); ++i ) + { + KIWAY_PLAYER* frame = m_player[i]; + + if( frame ) + { + frame->ShowChangedLanguage(); + } + } +} + + +bool KIWAY::ProcessEvent( wxEvent& aEvent ) +{ + KIWAY_EXPRESS* mail = dynamic_cast<KIWAY_EXPRESS*>( &aEvent ); + + if( mail ) + { + FRAME_T dest = mail->Dest(); + + // see if recipient is alive + KIWAY_PLAYER* alive = Player( dest, false ); + + if( alive ) + { +#if 1 + return alive->ProcessEvent( aEvent ); +#else + alive->KiwayMailIn( *mail ); + return true; +#endif + } + } + + return false; +} + + +void KIWAY::OnKiwayEnd() +{ + for( unsigned i=0; i < DIM( m_kiface ); ++i ) + { + if( m_kiface[i] ) + m_kiface[i]->OnKifaceEnd(); + } +} + diff --git a/common/kiway_express.cpp b/common/kiway_express.cpp new file mode 100644 index 0000000..8056f4a --- /dev/null +++ b/common/kiway_express.cpp @@ -0,0 +1,60 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.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 <kiway_express.h> + +//IMPLEMENT_DYNAMIC_CLASS( KIWAY_EXPRESS, wxEvent ) + + +#if 0 // requires that this code reside in only a single link image, rather than + // in each of kicad.exe, _pcbnew.kiface, and _eeschema.kiface as now. + // In the current case wxEVENT_ID will get a different value in each link + // image. We need to put this into a shared library for common utilization, + // I think that library should be libki.so. I am reluctant to do that now + // because the cost will be finding libki.so at runtime, and we need infrastructure + // to set our LIB_ENV_VAR to the proper place so libki.so can be reliably found. + // All things in due course. +const wxEventType KIWAY_EXPRESS::wxEVENT_ID = wxNewEventType(); +#else +const wxEventType KIWAY_EXPRESS::wxEVENT_ID = 30000; // commmon accross all link images, hopefully unique. +#endif + + +KIWAY_EXPRESS::KIWAY_EXPRESS( const KIWAY_EXPRESS& anOther ) : + wxEvent( anOther ) +{ + m_destination = anOther.m_destination; + m_payload = anOther.m_payload; +} + + +KIWAY_EXPRESS::KIWAY_EXPRESS( FRAME_T aDestination, MAIL_T aCommand, + const std::string& aPayload, wxWindow* aSource ) : + wxEvent( aCommand, wxEVENT_ID ), + m_destination( aDestination ), + m_payload( aPayload ) +{ + SetEventObject( aSource ); +} + diff --git a/common/kiway_holder.cpp b/common/kiway_holder.cpp new file mode 100644 index 0000000..a0a2dcf --- /dev/null +++ b/common/kiway_holder.cpp @@ -0,0 +1,32 @@ + +#include <kiway.h> +#include <kiway_player.h> + +#if defined(DEBUG) + #include <typeinfo> +#endif + + +PROJECT& KIWAY_HOLDER::Prj() const +{ + return Kiway().Prj(); +} + + +// this is not speed critical, hide it out of line. +void KIWAY_HOLDER::SetKiway( wxWindow* aDest, KIWAY* aKiway ) +{ +#if defined(DEBUG) + // offer a trap point for debugging most any window + wxASSERT( aDest ); + if( !strcmp( typeid(aDest).name(), "DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB" ) ) + { + int breakhere=1; + (void) breakhere; + } +#endif + + (void) aDest; + + m_kiway = aKiway; +} diff --git a/common/kiway_player.cpp b/common/kiway_player.cpp new file mode 100644 index 0000000..a33acc1 --- /dev/null +++ b/common/kiway_player.cpp @@ -0,0 +1,205 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2014-2015 KiCad Developers, see CHANGELOG.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 <kiway_player.h> +#include <kiway_express.h> +#include <kiway.h> +#include <id.h> +#include <macros.h> +#include <typeinfo> +#include <wx/utils.h> +#include <wx/evtloop.h> + + +BEGIN_EVENT_TABLE( KIWAY_PLAYER, EDA_BASE_FRAME ) + EVT_KIWAY_EXPRESS( KIWAY_PLAYER::kiway_express ) + EVT_MENU_RANGE( ID_LANGUAGE_CHOICE, ID_LANGUAGE_CHOICE_END, KIWAY_PLAYER::language_change ) +END_EVENT_TABLE() + + +KIWAY_PLAYER::KIWAY_PLAYER( KIWAY* aKiway, wxWindow* aParent, FRAME_T aFrameType, + const wxString& aTitle, const wxPoint& aPos, const wxSize& aSize, + long aStyle, const wxString& aWdoName ) : + EDA_BASE_FRAME( aParent, aFrameType, aTitle, aPos, aSize, aStyle, aWdoName ), + KIWAY_HOLDER( aKiway ), + m_modal( false ), + m_modal_loop( 0 ), m_modal_resultant_parent( 0 ) +{ + // DBG( printf("KIWAY_EXPRESS::wxEVENT_ID:%d\n", KIWAY_EXPRESS::wxEVENT_ID );) + m_modal_ret_val = 0; +} + + +KIWAY_PLAYER::KIWAY_PLAYER( wxWindow* aParent, wxWindowID aId, const wxString& aTitle, + const wxPoint& aPos, const wxSize& aSize, long aStyle, + const wxString& aWdoName ) : + EDA_BASE_FRAME( aParent, (FRAME_T) aId, aTitle, aPos, aSize, aStyle, aWdoName ), + KIWAY_HOLDER( 0 ), + m_modal( false ), + m_modal_loop( 0 ), + m_modal_resultant_parent( 0 ), + m_modal_ret_val( false ) +{ + // DBG( printf("KIWAY_EXPRESS::wxEVENT_ID:%d\n", KIWAY_EXPRESS::wxEVENT_ID );) +} + + +KIWAY_PLAYER::~KIWAY_PLAYER(){} + + +void KIWAY_PLAYER::KiwayMailIn( KIWAY_EXPRESS& aEvent ) +{ + // override this in derived classes. +} + + +bool KIWAY_PLAYER::ShowModal( wxString* aResult, wxWindow* aResultantFocusWindow ) +{ + wxASSERT_MSG( IsModal(), wxT( "ShowModal() shouldn't be called on non-modal frame" ) ); + + /* + This function has a nice interface but a necessarily unsightly implementation. + Now the implementation is encapsulated, localizing future changes. + + It works in tandem with DismissModal(). But only ShowModal() is in the + vtable and therefore cross-module capable. + */ + + // This is an exception safe way to zero a pointer before returning. + // Yes, even though DismissModal() clears this first normally, this is + // here in case there's an exception before the dialog is dismissed. + struct NULLER + { + void*& m_what; + NULLER( void*& aPtr ) : m_what( aPtr ) {} + ~NULLER() { m_what = 0; } // indeed, set it to NULL on destruction + } clear_this( (void*&) m_modal_loop ); + + + m_modal_resultant_parent = aResultantFocusWindow; + + Show( true ); + Raise(); // Needed on some Window managers to always display the frame + + SetFocus(); + + { + // We have to disable all frames but the the modal one. + // wxWindowDisabler does that, but it also disables all top level windows + // We do not want to disable top level windows which are child of the modal one, + // if they are enabled. + // An example is an aui toolbar which was moved + // or a dialog or an other frame or miniframe opened by the modal one. + wxWindowList wlist = GetChildren(); + std::vector<wxWindow*> enabledTopLevelWindows; + + for( unsigned ii = 0; ii < wlist.size(); ii++ ) + if( wlist[ii]->IsTopLevel() && wlist[ii]->IsEnabled() ) + enabledTopLevelWindows.push_back( wlist[ii] ); + + // exception safe way to disable all top level windows except the modal one, + // re-enables only those that were disabled on exit + wxWindowDisabler toggle( this ); + + for( unsigned ii = 0; ii < enabledTopLevelWindows.size(); ii++ ) + enabledTopLevelWindows[ii]->Enable( true ); + + WX_EVENT_LOOP event_loop; + m_modal_loop = &event_loop; + event_loop.Run(); + + } // End of scope for some variables. + // End nesting before setting focus below. + + if( aResult ) + *aResult = m_modal_string; + + DBG(printf( "~%s: aResult:'%s' ret:%d\n", + __func__, TO_UTF8( m_modal_string ), m_modal_ret_val );) + + if( aResultantFocusWindow ) + { + aResultantFocusWindow->Raise(); + + // have the final say, after wxWindowDisabler reenables my parent and + // the events settle down, set the focus + wxSafeYield(); + aResultantFocusWindow->SetFocus(); + } + + return m_modal_ret_val; +} + +bool KIWAY_PLAYER::Destroy() +{ + return EDA_BASE_FRAME::Destroy(); +} + +bool KIWAY_PLAYER::IsDismissed() +{ + bool ret = !m_modal_loop; + + DBG(printf( "%s: ret:%d\n", __func__, ret );) + + return ret; +} + + +void KIWAY_PLAYER::DismissModal( bool aRetVal, const wxString& aResult ) +{ + m_modal_ret_val = aRetVal; + m_modal_string = aResult; + + if( m_modal_loop ) + { + m_modal_loop->Exit(); + m_modal_loop = 0; // this marks it as dismissed. + } + + Show( false ); +} + + +void KIWAY_PLAYER::kiway_express( KIWAY_EXPRESS& aEvent ) +{ + // logging support +#if defined(DEBUG) + const char* class_name = typeid( this ).name(); + + printf( "%s: received cmd:%d pay:'%s'\n", class_name, + aEvent.Command(), aEvent.GetPayload().c_str() ); +#endif + + KiwayMailIn( aEvent ); // call the virtual, override in derived. +} + + +void KIWAY_PLAYER::language_change( wxCommandEvent& event ) +{ + int id = event.GetId(); + + // tell all the KIWAY_PLAYERs about the language change. + Kiway().SetLanguage( id ); +} diff --git a/common/lockfile.cpp b/common/lockfile.cpp new file mode 100644 index 0000000..b3c3714 --- /dev/null +++ b/common/lockfile.cpp @@ -0,0 +1,56 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014-2015 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2014-2015 KiCad Developers, see CHANGELOG.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 <wx/filename.h> +#include <wx/snglinst.h> +#include <common.h> + + +wxSingleInstanceChecker* LockFile( const wxString& aFileName ) +{ + // first make absolute and normalize, to avoid that different lock files + // for the same file can be created + wxFileName fn( aFileName ); + + fn.MakeAbsolute(); + + wxString lockFileName = fn.GetFullPath() + wxT( ".lock" ); + + lockFileName.Replace( wxT( "/" ), wxT( "_" ) ); + + // We can have filenames coming from Windows, so also convert Windows separator + lockFileName.Replace( wxT( "\\" ), wxT( "_" ) ); + + wxSingleInstanceChecker* p = new wxSingleInstanceChecker( lockFileName, + GetKicadLockFilePath() ); + + if( p->IsAnotherRunning() ) + { + delete p; + p = NULL; + } + + return p; +} + diff --git a/common/lset.cpp b/common/lset.cpp new file mode 100644 index 0000000..4e4248d --- /dev/null +++ b/common/lset.cpp @@ -0,0 +1,747 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2014 KiCad Developers, see change_log.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 <stdarg.h> +#include <assert.h> + +#include <layers_id_colors_and_visibility.h> +#include <class_board.h> + + +LSET::LSET( const LAYER_ID* aArray, unsigned aCount ) : + BASE_SET() +{ + for( unsigned i=0; i<aCount; ++i ) + set( aArray[i] ); +} + + +LSET::LSET( unsigned aIdCount, LAYER_ID aFirst, ... ) : + BASE_SET() +{ + // The constructor, without the mandatory aFirst argument, could have been confused + // by the compiler with the LSET( LAYER_ID ). With aFirst, that ambiguity is not + // present. Therefore aIdCount must always be >=1. + wxASSERT_MSG( aIdCount > 0, wxT( "aIdCount must be >= 1" ) ); + + set( aFirst ); + + if( --aIdCount ) + { + va_list ap; + + va_start( ap, aFirst ); + + for( unsigned i=0; i<aIdCount; ++i ) + { + LAYER_ID id = (LAYER_ID) va_arg( ap, int ); + + // printf( "%s: id:%d LAYER_ID_COUNT:%d\n", __func__, id, LAYER_ID_COUNT ); + + assert( unsigned( id ) < LAYER_ID_COUNT ); + + set( id ); + } + + va_end( ap ); + } +} + + +const wxChar* LSET::Name( LAYER_ID aLayerId ) +{ + const wxChar* txt; + + // using a switch to explicitly show the mapping more clearly + switch( aLayerId ) + { + case F_Cu: txt = wxT( "F.Cu" ); break; + case In1_Cu: txt = wxT( "In1.Cu" ); break; + case In2_Cu: txt = wxT( "In2.Cu" ); break; + case In3_Cu: txt = wxT( "In3.Cu" ); break; + case In4_Cu: txt = wxT( "In4.Cu" ); break; + case In5_Cu: txt = wxT( "In5.Cu" ); break; + case In6_Cu: txt = wxT( "In6.Cu" ); break; + case In7_Cu: txt = wxT( "In7.Cu" ); break; + case In8_Cu: txt = wxT( "In8.Cu" ); break; + case In9_Cu: txt = wxT( "In9.Cu" ); break; + case In10_Cu: txt = wxT( "In10.Cu" ); break; + case In11_Cu: txt = wxT( "In11.Cu" ); break; + case In12_Cu: txt = wxT( "In12.Cu" ); break; + case In13_Cu: txt = wxT( "In13.Cu" ); break; + case In14_Cu: txt = wxT( "In14.Cu" ); break; + case In15_Cu: txt = wxT( "In15.Cu" ); break; + case In16_Cu: txt = wxT( "In16.Cu" ); break; + case In17_Cu: txt = wxT( "In17.Cu" ); break; + case In18_Cu: txt = wxT( "In18.Cu" ); break; + case In19_Cu: txt = wxT( "In19.Cu" ); break; + case In20_Cu: txt = wxT( "In20.Cu" ); break; + case In21_Cu: txt = wxT( "In21.Cu" ); break; + case In22_Cu: txt = wxT( "In22.Cu" ); break; + case In23_Cu: txt = wxT( "In23.Cu" ); break; + case In24_Cu: txt = wxT( "In24.Cu" ); break; + case In25_Cu: txt = wxT( "In25.Cu" ); break; + case In26_Cu: txt = wxT( "In26.Cu" ); break; + case In27_Cu: txt = wxT( "In27.Cu" ); break; + case In28_Cu: txt = wxT( "In28.Cu" ); break; + case In29_Cu: txt = wxT( "In29.Cu" ); break; + case In30_Cu: txt = wxT( "In30.Cu" ); break; + case B_Cu: txt = wxT( "B.Cu" ); break; + + // Technicals + case B_Adhes: txt = wxT( "B.Adhes" ); break; + case F_Adhes: txt = wxT( "F.Adhes" ); break; + case B_Paste: txt = wxT( "B.Paste" ); break; + case F_Paste: txt = wxT( "F.Paste" ); break; + case B_SilkS: txt = wxT( "B.SilkS" ); break; + case F_SilkS: txt = wxT( "F.SilkS" ); break; + case B_Mask: txt = wxT( "B.Mask" ); break; + case F_Mask: txt = wxT( "F.Mask" ); break; + + // Users + case Dwgs_User: txt = wxT( "Dwgs.User" ); break; + case Cmts_User: txt = wxT( "Cmts.User" ); break; + case Eco1_User: txt = wxT( "Eco1.User" ); break; + case Eco2_User: txt = wxT( "Eco2.User" ); break; + case Edge_Cuts: txt = wxT( "Edge.Cuts" ); break; + case Margin: txt = wxT( "Margin" ); break; + + // Footprint + case F_CrtYd: txt = wxT( "F.CrtYd" ); break; + case B_CrtYd: txt = wxT( "B.CrtYd" ); break; + case F_Fab: txt = wxT( "F.Fab" ); break; + case B_Fab: txt = wxT( "B.Fab" ); break; + + default: + wxASSERT_MSG( 0, wxT( "aLayerId out of range" ) ); + txt = wxT( "BAD INDEX!" ); break; + } + + return txt; +} + + +LSEQ LSET::CuStack() const +{ + // desired sequence + static const LAYER_ID sequence[] = { + F_Cu, + In1_Cu, + In2_Cu, + In3_Cu, + In4_Cu, + In5_Cu, + In6_Cu, + In7_Cu, + In8_Cu, + In9_Cu, + In10_Cu, + In11_Cu, + In12_Cu, + In13_Cu, + In14_Cu, + In15_Cu, + In16_Cu, + In17_Cu, + In18_Cu, + In19_Cu, + In20_Cu, + In21_Cu, + In22_Cu, + In23_Cu, + In24_Cu, + In25_Cu, + In26_Cu, + In27_Cu, + In28_Cu, + In29_Cu, + In30_Cu, + B_Cu, // 31 + }; + + return Seq( sequence, DIM( sequence ) ); +} + + +LSEQ LSET::Technicals( LSET aSetToOmit ) const +{ + // desired sequence + static const LAYER_ID sequence[] = { + B_Adhes, + F_Adhes, + B_Paste, + F_Paste, + B_SilkS, + F_SilkS, + B_Mask, + F_Mask, + B_CrtYd, + F_CrtYd, + B_Fab, + F_Fab, + }; + + LSET subset = ~aSetToOmit & *this; + + return subset.Seq( sequence, DIM( sequence ) ); +} + + +LSEQ LSET::Users() const +{ + // desired + static const LAYER_ID sequence[] = { + Dwgs_User, + Cmts_User, + Eco1_User, + Eco2_User, + Edge_Cuts, + Margin, + }; + + return Seq( sequence, DIM( sequence ) ); +} + + +std::string LSET::FmtBin() const +{ + std::string ret; + + int bit_count = size(); + + for( int bit=0; bit<bit_count; ++bit ) + { + if( bit ) + { + if( !( bit % 8 ) ) + ret += '|'; + else if( !( bit % 4 ) ) + ret += '_'; + } + + ret += (*this)[bit] ? '1' : '0'; + } + + // reverse of string + return std::string( ret.rbegin(), ret.rend() ); +} + + +std::string LSET::FmtHex() const +{ + std::string ret; + + static const char hex[] = "0123456789abcdef"; + + int nibble_count = ( size() + 3 ) / 4; + + for( int nibble=0; nibble<nibble_count; ++nibble ) + { + unsigned ndx = 0; + + // test 4 consecutive bits and set ndx to 0-15: + for( int nibble_bit=0; nibble_bit<4; ++nibble_bit ) + { + if( (*this)[nibble_bit + nibble*4] ) + ndx |= (1 << nibble_bit); + } + + if( nibble && !( nibble % 8 ) ) + ret += '_'; + + assert( ndx < DIM( hex ) ); + + ret += hex[ndx]; + } + + // reverse of string + return std::string( ret.rbegin(), ret.rend() ); +} + + +int LSET::ParseHex( const char* aStart, int aCount ) +{ + LSET tmp; + + const char* rstart = aStart + aCount - 1; + const char* rend = aStart - 1; + + const int bitcount = size(); + + int nibble_ndx = 0; + + while( rstart > rend ) + { + int cc = *rstart--; + + if( cc == '_' ) + continue; + + int nibble; + + if( cc >= '0' && cc <= '9' ) + nibble = cc - '0'; + else if( cc >= 'a' && cc <= 'f' ) + nibble = cc - 'a' + 10; + else if( cc >= 'A' && cc <= 'F' ) + nibble = cc - 'A' + 10; + else + break; + + int bit = nibble_ndx * 4; + + for( int ndx=0; bit<bitcount && ndx<4; ++bit, ++ndx ) + if( nibble & (1<<ndx) ) + tmp.set( bit ); + + if( bit >= bitcount ) + break; + + ++nibble_ndx; + } + + int byte_count = aStart + aCount - 1 - rstart; + + assert( byte_count >= 0 ); + + if( byte_count > 0 ) + *this = tmp; + + return byte_count; +} + + +LSEQ LSET::Seq( const LAYER_ID* aWishListSequence, unsigned aCount ) const +{ + LSEQ ret; + +#if defined(DEBUG) && 0 + LSET dup_detector; + + for( unsigned i=0; i<aCount; ++i ) + { + LAYER_ID id = aWishListSequence[i]; + + if( test( id ) ) + { + wxASSERT_MSG( !dup_detector[id], wxT( "Duplicate in aWishListSequence" ) ); + dup_detector[id] = true; + + ret.push_back( id ); + } + } +#else + + for( unsigned i=0; i<aCount; ++i ) + { + LAYER_ID id = aWishListSequence[i]; + + if( test( id ) ) + ret.push_back( id ); + } +#endif + + return ret; +} + + +LSEQ LSET::Seq() const +{ + LSEQ ret; + + for( unsigned i=0; i<size(); ++i ) + { + if( test(i) ) + ret.push_back( LAYER_ID( i ) ); + } + + return ret; +} + + +LSEQ LSET::SeqStackupBottom2Top() const +{ + // bottom-to-top stack-up layers + static const LAYER_ID sequence[] = { + B_Fab, + B_CrtYd, + B_Adhes, + B_SilkS, + B_Paste, + B_Mask, + B_Cu, + In30_Cu, + In29_Cu, + In28_Cu, + In27_Cu, + In26_Cu, + In25_Cu, + In24_Cu, + In23_Cu, + In22_Cu, + In21_Cu, + In20_Cu, + In19_Cu, + In18_Cu, + In17_Cu, + In16_Cu, + In15_Cu, + In14_Cu, + In13_Cu, + In12_Cu, + In11_Cu, + In10_Cu, + In9_Cu, + In8_Cu, + In7_Cu, + In6_Cu, + In5_Cu, + In4_Cu, + In3_Cu, + In2_Cu, + In1_Cu, + F_Cu, + F_Mask, + F_Paste, + F_SilkS, + F_Adhes, + F_CrtYd, + F_Fab, + Dwgs_User, + Cmts_User, + Eco1_User, + Eco2_User, + Margin, + Edge_Cuts, + }; + + return Seq( sequence, DIM( sequence ) ); +} + + +LAYER_ID FlipLayer( LAYER_ID aLayerId, int aCopperLayersCount ) +{ + switch( aLayerId ) + { + case B_Cu: return F_Cu; + case F_Cu: return B_Cu; + + case B_SilkS: return F_SilkS; + case F_SilkS: return B_SilkS; + + case B_Adhes: return F_Adhes; + case F_Adhes: return B_Adhes; + + case B_Mask: return F_Mask; + case F_Mask: return B_Mask; + + case B_Paste: return F_Paste; + case F_Paste: return B_Paste; + + case B_CrtYd: return F_CrtYd; + case F_CrtYd: return B_CrtYd; + + case B_Fab: return F_Fab; + case F_Fab: return B_Fab; + + default: // change internal layer if aCopperLayersCount is >= 4 + if( IsCopperLayer( aLayerId ) && aCopperLayersCount >= 4 ) + { + // internal copper layers count is aCopperLayersCount-2 + LAYER_ID fliplayer = LAYER_ID(aCopperLayersCount - 2 - ( aLayerId - In1_Cu ) ); + // Ensure fliplayer has a value which does not crash pcbnew: + if( fliplayer < F_Cu ) + fliplayer = F_Cu; + + if( fliplayer > B_Cu ) + fliplayer = B_Cu; + + return fliplayer; + } + + // No change for the other layers + return aLayerId; + } +} + + +LSET FlipLayerMask( LSET aMask, int aCopperLayersCount ) +{ + // layers on physical outside of a board: + const static LSET and_mask( 16, // !! update count + B_Cu, F_Cu, + B_SilkS, F_SilkS, + B_Adhes, F_Adhes, + B_Mask, F_Mask, + B_Paste, F_Paste, + B_Adhes, F_Adhes, + B_CrtYd, F_CrtYd, + B_Fab, F_Fab + ); + + LSET newMask = aMask & ~and_mask; + + if( aMask[B_Cu] ) + newMask.set( F_Cu ); + + if( aMask[F_Cu] ) + newMask.set( B_Cu ); + + if( aMask[B_SilkS] ) + newMask.set( F_SilkS ); + + if( aMask[F_SilkS] ) + newMask.set( B_SilkS ); + + if( aMask[B_Adhes] ) + newMask.set( F_Adhes ); + + if( aMask[F_Adhes] ) + newMask.set( B_Adhes ); + + if( aMask[B_Mask] ) + newMask.set( F_Mask ); + + if( aMask[F_Mask] ) + newMask.set( B_Mask ); + + if( aMask[B_Paste] ) + newMask.set( F_Paste ); + + if( aMask[F_Paste] ) + newMask.set( B_Paste ); + + if( aMask[B_Adhes] ) + newMask.set( F_Adhes ); + + if( aMask[F_Adhes] ) + newMask.set( B_Adhes ); + + if( aMask[B_CrtYd] ) + newMask.set( F_CrtYd ); + + if( aMask[F_CrtYd] ) + newMask.set( B_CrtYd ); + + if( aMask[B_Fab] ) + newMask.set( F_Fab ); + + if( aMask[F_Fab] ) + newMask.set( B_Fab ); + + if( aCopperLayersCount >= 4 ) // Internal layers exist + { + LSET internalMask = aMask & ~LSET::InternalCuMask(); + + if( internalMask != LSET::InternalCuMask() ) + { // the mask does not include all internal layers. Therefore + // the flipped mask for internal copper layers must be built + int innerLayerCnt = aCopperLayersCount -2; + + for( int ii = 0; ii < innerLayerCnt; ii++ ) + { + if( internalMask[innerLayerCnt - ii + In1_Cu] ) + newMask.set( ii + In1_Cu ); + else + newMask.reset( ii + In1_Cu ); + } + } + } + + return newMask; +} + + +LAYER_ID LSET::ExtractLayer() const +{ + unsigned set_count = count(); + + if( !set_count ) + return UNSELECTED_LAYER; + else if( set_count > 1 ) + return UNDEFINED_LAYER; + + for( unsigned i=0; i < size(); ++i ) + { + if( test( i ) ) + return LAYER_ID( i ); + } + + wxASSERT( 0 ); // set_count was verified as 1 above, what did you break? + + return UNDEFINED_LAYER; +} + + +LSET LSET::InternalCuMask() +{ + static const LAYER_ID cu_internals[] = { + In1_Cu, + In2_Cu, + In3_Cu, + In4_Cu, + In5_Cu, + In6_Cu, + In7_Cu, + In8_Cu, + In9_Cu, + In10_Cu, + In11_Cu, + In12_Cu, + In13_Cu, + In14_Cu, + In15_Cu, + In16_Cu, + In17_Cu, + In18_Cu, + In19_Cu, + In20_Cu, + In21_Cu, + In22_Cu, + In23_Cu, + In24_Cu, + In25_Cu, + In26_Cu, + In27_Cu, + In28_Cu, + In29_Cu, + In30_Cu, + }; + + static const LSET saved( cu_internals, DIM( cu_internals ) ); + return saved; +} + + +LSET LSET::AllCuMask( int aCuLayerCount ) +{ + // retain all in static as the full set, which is a common case. + static const LSET all = InternalCuMask().set( F_Cu ).set( B_Cu ); + + if( aCuLayerCount == MAX_CU_LAYERS ) + return all; + + // subtract out some Cu layers not wanted in the mask. + LSET ret = all; + int clear_count = MAX_CU_LAYERS - aCuLayerCount; + + clear_count = Clamp( 0, clear_count, MAX_CU_LAYERS - 2 ); + + for( LAYER_NUM elem=In30_Cu; clear_count; --elem, --clear_count ) + { + ret.set( elem, false ); + } + + return ret; +} + + +LSET LSET::AllNonCuMask() +{ + static const LSET saved = LSET().set() & ~AllCuMask(); + return saved; +} + + +LSET LSET::AllLayersMask() +{ + static const LSET saved = LSET().set(); + return saved; +} + + +LSET LSET::BackTechMask() +{ + // (SILKSCREEN_LAYER_BACK | SOLDERMASK_LAYER_BACK | ADHESIVE_LAYER_BACK | SOLDERPASTE_LAYER_BACK) + static const LSET saved( 6, B_SilkS, B_Mask, B_Adhes, B_Paste, B_CrtYd, B_Fab ); + return saved; +} + + +LSET LSET::FrontTechMask() +{ + // (SILKSCREEN_LAYER_FRONT | SOLDERMASK_LAYER_FRONT | ADHESIVE_LAYER_FRONT | SOLDERPASTE_LAYER_FRONT) + static const LSET saved( 6, F_SilkS, F_Mask, F_Adhes, F_Paste, F_CrtYd, F_Fab ); + return saved; +} + + +LSET LSET::AllTechMask() +{ + static const LSET saved = BackTechMask() | FrontTechMask(); + return saved; +} + + +LSET LSET::UserMask() +{ + static const LSET saved( 6, + Dwgs_User, + Cmts_User, + Eco1_User, + Eco2_User, + Edge_Cuts, + Margin + ); + + return saved; +} + + +LSET LSET::FrontMask() +{ + static const LSET saved = FrontTechMask().set( F_Cu ); + return saved; +} + + +LSET LSET::BackMask() +{ + static const LSET saved = BackTechMask().set( B_Cu ); + return saved; +} + + +LSEQ LSET::UIOrder() const +{ + LAYER_ID order[LAYER_ID_COUNT]; + + // Assmuming that the LAYER_ID order is according to preferred UI order, as of + // today this is true. When that becomes not true, its easy to change the order + // in here to compensate. + + for( unsigned i=0; i<DIM(order); ++i ) + order[i] = LAYER_ID( i ); + + return Seq( order, DIM( order ) ); +} + + +LAYER_ID ToLAYER_ID( int aLayer ) +{ + wxASSERT( unsigned( aLayer ) < LAYER_ID_COUNT ); + return LAYER_ID( aLayer ); +} + diff --git a/common/math/math_util.cpp b/common/math/math_util.cpp new file mode 100644 index 0000000..604b879 --- /dev/null +++ b/common/math/math_util.cpp @@ -0,0 +1,91 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (c) 2005 Michael Niedermayer <michaelni@gmx.at> + * Copyright (C) CERN + * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * + * 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 <cmath> +#include <cstdlib> +#include <climits> +#include <math/math_util.h> + +template<> +int rescale( int aNumerator, int aValue, int aDenominator ) +{ + return (int) ( (int64_t) aNumerator * (int64_t) aValue / (int64_t) aDenominator ); +} + + +template<> +int64_t rescale( int64_t aNumerator, int64_t aValue, int64_t aDenominator ) +{ +#ifdef __x86_64__ + return ( (__int128_t) aNumerator * (__int128_t) aValue ) / aDenominator; +#else + int64_t r = 0; + int64_t sign = ( ( aNumerator < 0 ) ? -1 : 1 ) * ( aDenominator < 0 ? -1 : 1 ) * + ( aValue < 0 ? -1 : 1 ); + + int64_t a = std::abs( aNumerator ); + int64_t b = std::abs( aValue ); + int64_t c = std::abs( aDenominator ); + + r = c / 2; + + if( b <= INT_MAX && c <= INT_MAX ) + { + if( a <= INT_MAX ) + return sign * ( ( a * b + r ) / c ); + else + return sign * ( a / c * b + ( a % c * b + r ) / c); + } + else + { + uint64_t a0 = a & 0xFFFFFFFF; + uint64_t a1 = a >> 32; + uint64_t b0 = b & 0xFFFFFFFF; + uint64_t b1 = b >> 32; + uint64_t t1 = a0 * b1 + a1 * b0; + uint64_t t1a = t1 << 32; + int i; + + a0 = a0 * b0 + t1a; + a1 = a1 * b1 + ( t1 >> 32 ) + ( a0 < t1a ); + a0 += r; + a1 += a0 < (uint64_t)r; + + for( i = 63; i >= 0; i-- ) + { + a1 += a1 + ( ( a0 >> i ) & 1 ); + t1 += t1; + + if( (uint64_t) c <= a1 ) + { + a1 -= c; + t1++; + } + } + + return t1 * sign; + } +#endif +} diff --git a/common/msgpanel.cpp b/common/msgpanel.cpp new file mode 100644 index 0000000..97e8abe --- /dev/null +++ b/common/msgpanel.cpp @@ -0,0 +1,243 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com + * Copyright (C) 2011 Wayne Stambaugh <stambaughw@verizon.net> + * Copyright (C) 1992-2011 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 + */ + +/** + * @file msgpanel.cpp + * @brief Message panel implementation file. + */ + +#include <msgpanel.h> + + +BEGIN_EVENT_TABLE( EDA_MSG_PANEL, wxPanel ) + EVT_PAINT( EDA_MSG_PANEL::OnPaint ) +END_EVENT_TABLE() + + +EDA_MSG_PANEL::EDA_MSG_PANEL( wxWindow* aParent, int aId, + const wxPoint& aPosition, const wxSize& aSize, + long style, const wxString &name ) : + wxPanel( aParent, aId, aPosition, aSize, style, name ) +{ + SetFont( wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ) ); + SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + m_last_x = 0; + + m_fontSize = computeFontSize(); +} + + +EDA_MSG_PANEL::~EDA_MSG_PANEL() +{ +} + + +wxSize EDA_MSG_PANEL::computeFontSize() +{ + // Get size of the wxSYS_DEFAULT_GUI_FONT + wxSize fontSizeInPixels; + + wxScreenDC dc; + + dc.SetFont( wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ) ); + dc.GetTextExtent( wxT( "W" ), &fontSizeInPixels.x, &fontSizeInPixels.y ); + + return fontSizeInPixels; +} + + +int EDA_MSG_PANEL::GetRequiredHeight() +{ + // make space for two rows of text plus a number of pixels between them. + return 2 * computeFontSize().y + 0; +} + + +wxSize EDA_MSG_PANEL::computeTextSize( const wxString& aText ) const +{ + // Get size of the wxSYS_DEFAULT_GUI_FONT + wxSize textSizeInPixels; + + wxScreenDC dc; + + dc.SetFont( wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ) ); + dc.GetTextExtent( aText, &textSizeInPixels.x, &textSizeInPixels.y ); + + return textSizeInPixels; +} + + +void EDA_MSG_PANEL::OnPaint( wxPaintEvent& aEvent ) +{ + wxPaintDC dc( this ); + + erase( &dc ); + + dc.SetBackground( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + dc.SetBackgroundMode( wxSOLID ); + dc.SetTextBackground( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + dc.SetFont( wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ) ); + + for( unsigned i=0; i<m_Items.size(); ++i ) + showItem( dc, m_Items[i] ); + + aEvent.Skip(); +} + + +void EDA_MSG_PANEL::AppendMessage( const wxString& aUpperText, + const wxString& aLowerText, + EDA_COLOR_T aColor, int aPad ) +{ + wxString text; + wxSize drawSize = GetClientSize(); + + text = ( aUpperText.Len() > aLowerText.Len() ) ? aUpperText : aLowerText; + text.Append( ' ', aPad ); + + MSG_PANEL_ITEM item; + + /* Don't put the first message a window client position 0. Offset by + * one 'W' character width. */ + if( m_last_x == 0 ) + m_last_x = m_fontSize.x; + + item.m_X = m_last_x; + + item.m_UpperY = ( drawSize.y / 2 ) - m_fontSize.y; + item.m_LowerY = drawSize.y - m_fontSize.y; + + item.m_UpperText = aUpperText; + item.m_LowerText = aLowerText; + item.m_Color = aColor; + m_Items.push_back( item ); + m_last_x += computeTextSize( text ).x; + + // Add an extra space between texts for a better look: + m_last_x += m_fontSize.x; + + Refresh(); +} + + +void EDA_MSG_PANEL::SetMessage( int aXPosition, const wxString& aUpperText, + const wxString& aLowerText, EDA_COLOR_T aColor ) +{ + wxPoint pos; + wxSize drawSize = GetClientSize(); + + if( aXPosition >= 0 ) + m_last_x = pos.x = aXPosition * (m_fontSize.x + 2); + else + pos.x = m_last_x; + + MSG_PANEL_ITEM item; + + item.m_X = pos.x; + + item.m_UpperY = (drawSize.y / 2) - m_fontSize.y; + item.m_LowerY = drawSize.y - m_fontSize.y; + + item.m_UpperText = aUpperText; + item.m_LowerText = aLowerText; + item.m_Color = aColor; + + int ndx; + + // update the vector, which is sorted by m_X + int limit = m_Items.size(); + + for( ndx=0; ndx<limit; ++ndx ) + { + // replace any item with same X + if( m_Items[ndx].m_X == item.m_X ) + { + m_Items[ndx] = item; + break; + } + + if( m_Items[ndx].m_X > item.m_X ) + { + m_Items.insert( m_Items.begin() + ndx, item ); + break; + } + } + + if( ndx == limit ) // mutually exclusive with two above if tests + { + m_Items.push_back( item ); + } + + Refresh(); +} + + +void EDA_MSG_PANEL::showItem( wxDC& aDC, const MSG_PANEL_ITEM& aItem ) +{ + EDA_COLOR_T color = aItem.m_Color; + + if( color >= 0 ) + { + color = ColorGetBase( color ); + aDC.SetTextForeground( MakeColour( color ) ); + } + + if( !aItem.m_UpperText.IsEmpty() ) + { + aDC.DrawText( aItem.m_UpperText, aItem.m_X, aItem.m_UpperY ); + } + + if( !aItem.m_LowerText.IsEmpty() ) + { + aDC.DrawText( aItem.m_LowerText, aItem.m_X, aItem.m_LowerY ); + } +} + + +void EDA_MSG_PANEL::EraseMsgBox() +{ + m_Items.clear(); + m_last_x = 0; + Refresh(); +} + + +void EDA_MSG_PANEL::erase( wxDC* aDC ) +{ + wxPen pen; + wxBrush brush; + + wxSize size = GetClientSize(); + wxColor color = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ); + + pen.SetColour( color ); + + brush.SetColour( color ); + brush.SetStyle( wxBRUSHSTYLE_SOLID ); + + aDC->SetPen( pen ); + aDC->SetBrush( brush ); + aDC->DrawRectangle( 0, 0, size.x, size.y ); +} diff --git a/common/netlist.keywords b/common/netlist.keywords new file mode 100644 index 0000000..98e949b --- /dev/null +++ b/common/netlist.keywords @@ -0,0 +1,40 @@ +code +comp +components +datasheet +date +description +design +docs +export +field +fields +footprint +footprints +fp +lib +libpart +libparts +libraries +library +libsource +name +names +net +nets +node +num +part +pin +pins +ref +sheetpath +source +tool +tstamp +tstamps +uri +value +version +aliases +alias diff --git a/common/newstroke_font.cpp b/common/newstroke_font.cpp new file mode 100644 index 0000000..3098de6 --- /dev/null +++ b/common/newstroke_font.cpp @@ -0,0 +1,11359 @@ +/* + * newstroke_font.cpp - definitions for automatically converted font + * + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2010 vladimir uryvaev <vovanius@bk.ru> + * Copyright (C) 1992-2010 KiCad Developers, see change_log.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 <newstroke_font.h> + +const char* const newstroke_font[] = +{ + /* // BASIC LATIN (0020-007F) */ + "JZ", /* U+20 SPACE */ + "MWRYSZR[QZRYR[ RRSQGRFSGRSRF", + "JZNFNJ RVFVJ", + "H]LM[M RRDL_ RYVJV RS_YD", + "H\\LZO[T[VZWYXWXUWSVRTQPPNOMNLLLJMHNGPFUFXG RRCR^", + "F^J[ZF RMFOGPIOKMLKKJIKGMF RYZZXYVWUUVTXUZW[YZ", + "E_[[Z[XZUWPQNNMKMINGPFQFSGTITJSLRMLQKRJTJWKYLZN[Q[SZTYWUXRXP", + "MWSFQJ", + "KYVcUbS_R]QZPUPQQLRISGUDVC", + "KYNcObQ_R]SZTUTQSLRIQGODNC", + "JZRFRK RMIRKWI ROORKUO", + "E_JSZS RR[RK", + "MWSZS[R]Q^", + "E_JSZS", + "MWRYSZR[QZRYR[", + "G][EI`", + "H\\QFSFUGVHWJXNXSWWVYUZS[Q[OZNYMWLSLNMJNHOGQF", /* U+30 DIGIT_0 */ + "H\\X[L[ RR[RFPINKLL", + "H\\LHMGOFTFVGWHXJXLWOK[X[", + "H\\KFXFQNTNVOWPXRXWWYVZT[N[LZKY", + "H\\VMV[ RQELTYT", + "H\\WFMFLPMOONTNVOWPXRXWWYVZT[O[MZLY", + "H\\VFRFPGOHMKLOLWMYNZP[T[VZWYXWXRWPVOTNPNNOMPLR", + "H\\KFYFP[", + "H\\PONNMMLKLJMHNGPFTFVGWHXJXKWMVNTOPONPMQLSLWMYNZP[T[VZWYXWXSWQVPTO", + "H\\N[R[TZUYWVXRXJWHVGTFPFNGMHLJLOMQNRPSTSVRWQXO", + "MWRYSZR[QZRYR[ RRNSORPQORNRP", + "MWSZS[R]Q^ RRNSORPQORNRP", + "E_ZMJSZY", + "E_JPZP RZVJV", + "E_JMZSJY", + "I[QYRZQ[PZQYQ[ RMGOFTFVGWIWKVMUNSORPQRQS", + "D_VQUPSOQOOPNQMSMUNWOXQYSYUXVW RVOVWWXXXZW[U[PYMVKRJNKKMIPHTIXK[N]R^V]Y[", /* U+40 AT */ + "I[MUWU RK[RFY[", + "G\\SPVQWRXTXWWYVZT[L[LFSFUGVHWJWLVNUOSPLP", + "F[WYVZS[Q[NZLXKVJRJOKKLINGQFSFVGWH", + "G\\L[LFQFTGVIWKXOXRWVVXTZQ[L[", + "H[MPTP RW[M[MFWF", + "HZTPMP RM[MFWF", + "F[VGTFQFNGLIKKJOJRKVLXNZQ[S[VZWYWRSR", + "G]L[LF RLPXP RX[XF", + "MWR[RF", + "JZUFUUTXRZO[M[", + "G\\L[LF RX[OO RXFLR", + "HYW[M[MF", + "F^K[KFRUYFY[", + "G]L[LFX[XF", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF", + "G\\L[LFTFVGWHXJXMWOVPTQLQ", /* U+50 P_CAP */ + "G]Z]X\\VZSWQVOV RP[NZLXKTKMLINGPFTFVGXIYMYTXXVZT[P[", + "G\\X[QQ RL[LFTFVGWHXJXMWOVPTQLQ", + "H\\LZO[T[VZWYXWXUWSVRTQPPNOMNLLLJMHNGPFUFXG", + "JZLFXF RR[RF", + "G]LFLWMYNZP[T[VZWYXWXF", + "I[KFR[YF", + "F^IFN[RLV[[F", + "H\\KFY[ RYFK[", + "I[RQR[ RKFRQYF", + "H\\KFYFK[Y[", + "KYVbQbQDVD", + "KYID[_", + "KYNbSbSDND", + "LXNHREVH", + "JZJ]Z]", + "NVPESH", /* U+60 GRAVE */ + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR", + "H[M[MF RMNOMSMUNVOWQWWVYUZS[O[MZ", + "HZVZT[P[NZMYLWLQMONNPMTMVN", + "I\\W[WF RWZU[Q[OZNYMWMQNOONQMUMWN", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT", + "MYOMWM RR[RISGUFWF", + "I\\WMW^V`UaSbPbNa RWZU[Q[OZNYMWMQNOONQMUMWN", + "H[M[MF RV[VPUNSMPMNNMO", + "MWR[RM RRFQGRHSGRFRH", + "MWRMR_QaObNb RRFQGRHSGRFRH", + "IZN[NF RPSV[ RVMNU", + "MXU[SZRXRF", + "D`I[IM RIOJNLMOMQNRPR[ RRPSNUMXMZN[P[[", + "I\\NMN[ RNOONQMTMVNWPW[", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[", + "H[MMMb RMNOMSMUNVOWQWWVYUZS[O[MZ", /* U+70 P_SMALL */ + "I\\WMWb RWZU[Q[OZNYMWMQNOONQMUMWN", + "KXP[PM RPQQORNTMVM", + "J[NZP[T[VZWXWWVUTTQTOSNQNPONQMTMVN", + "MYOMWM RRFRXSZU[W[", + "H[VMV[ RMMMXNZP[S[UZVY", + "JZMMR[WM", + "G]JMN[RQV[ZM", + "IZL[WM RLMW[", + "JZMMR[ RWMR[P`OaMb", + "IZLMWML[W[", + "KYVcUcSbR`RVQTOSQRRPRFSDUCVC", + "H\\RbRD", + "KYNcOcQbR`RVSTUSSRRPRFQDOCNC", + "KZMSNRPQTSVRWQ", + "F^K[KFYFY[K[", + /* // LATIN-1 SUPPLEMENT (0080-00FF) */ + "F^K[KFYFY[K[", /* U+80 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+90 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "JZ", /* U+A0 SPACE */ + "MWROQNRMSNRORM RRUSaRbQaRURb", + "HZVZT[P[NZMYLWLQMONNPMTMVN RRJR^", + "H[LMTM RL[W[ RO[OIPGRFUFWG", + "H]LYOV RLLOO RVVYY RVOYL RVVTWQWOVNTNQOOQNTNVOWQWTVV", + "F^JTZT RJMZM RRQR[ RKFRQYF", + "MWRbRW RRFRQ", + "I[N]P^S^U]V[UYOSNQNPONQM RVGTFQFOGNIOKUQVSVTUVSW", + "LXNFOGNHMGNFNH RVFWGVHUGVFVH", + "@dVKTJPJNKLMKOKSLUNWPXTXVW RRCMDHGELDQEVH[M^R_W^\\[_V`Q_L\\GWDRC", + "KZOEQDSDUEVGVN RVMTNQNOMNKOIQHVH", + "H\\RMLSRY RXWTSXO", + "E_JQZQZV", + "RR", + "@dWXRR RNXNJTJVKWMWOVQTRNR RRCMDHGELDQEVH[M^R_W^\\[_V`Q_L\\GWDRC", + "LXMGWG", + "JZRFPGOIPKRLTKUITGRF", /* U+B0 DEGREE */ + "E_JOZO RRWRG RZ[J[", + "JZNAP@S@UAVCVEUGNNVN", + "JZN@V@RESEUFVHVKUMSNPNNM", + "NVTEQH", + "H^MMMb RWXXZZ[ RMXNZP[T[VZWXWM", + "F]VMV[ ROMOXNZL[ RZMMMKNJP", + "JZRRQSRTSSRRRT", + "MWR\\T]U_TaRbOb", + "JZVNNN RNCPBR@RN", + "KYQNOMNKNGOEQDSDUEVGVKUMSNQN", + "H\\RMXSRY RLWPSLO", + "G]KQYQ RVNNN RNCPBR@RN RUYUa RQSN]W]", + "G]KQYQ RVNNN RNCPBR@RN RNTPSSSUTVVVXUZNaVa", + "G]KQYQ RN@V@RESEUFVHVKUMSNPNNM RUYUa RQSN]W]", + "I[SORNSMTNSOSM RWaUbPbNaM_M]N[OZQYRXSVSU", + "I[MUWU RK[RFY[ RP>SA", /* U+C0 A_CAP GRAVE */ + "I[MUWU RK[RFY[ RT>QA", + "I[MUWU RK[RFY[ RNAR>VA", + "I[MUWU RK[RFY[ RMAN@P?TAV@W?", + "I[MUWU RK[RFY[ RN?O@NAM@N?NA RV?W@VAU@V?VA", + "I[MUWU RK[RFY[ RRFPEOCPAR@TAUCTERF", + "F`JURU RRPYP RH[OF\\F RRFR[\\[", + "F[WYVZS[Q[NZLXKVJRJOKKLINGQFSFVGWH RR\\T]U_TaRbOb", + "H[MPTP RW[M[MFWF RP>SA", + "H[MPTP RW[M[MFWF RT>QA", + "H[MPTP RW[M[MFWF RNAR>VA", + "H[MPTP RW[M[MFWF RN?O@NAM@N?NA RV?W@VAU@V?VA", + "MWR[RF RP>SA", + "MWR[RF RT>QA", + "MWR[RF RNAR>VA", + "MWR[RF RN?O@NAM@N?NA RV?W@VAU@V?VA", + "G\\L[LFQFTGVIWKXOXRWVVXTZQ[L[ RIPQP", /* U+D0 D_CAP STROKE_H */ + "G]L[LFX[XF RMAN@P?TAV@W?", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RP>SA", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RT>QA", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RNAR>VA", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RMAN@P?TAV@W?", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RN?O@NAM@N?NA RV?W@VAU@V?VA", + "E_LMXY RXMLY", + "G]ZFJ[ RP[NZLXKTKMLINGPFTFVGXIYMYTXXVZT[P[", + "G]LFLWMYNZP[T[VZWYXWXF RP>SA", + "G]LFLWMYNZP[T[VZWYXWXF RT>QA", + "G]LFLWMYNZP[T[VZWYXWXF RNAR>VA", + "G]LFLWMYNZP[T[VZWYXWXF RN?O@NAM@N?NA RV?W@VAU@V?VA", + "I[RQR[ RKFRQYF RT>QA", + "G\\LFL[ RLKTKVLWMXOXRWTVUTVLV", + "F]K[KJLHMGOFRFTGUHVJVMSMQNPPPQQSSTVTXUYWYXXZV[R[PZ", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RPESH", /* U+E0 A_SMALL GRAVE */ + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RTEQH", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RNHREVH", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RMHNGPFTHVGWF", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RNFOGNHMGNFNH RVFWGVHUGVFVH", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RRHPGOEPCRBTCUETGRH", + "D`INKMOMQNRP R[ZY[U[SZRXRPSNUMYM[N\\P\\RRSKSITHVHXIZK[O[QZRX", + "HZVZT[P[NZMYLWLQMONNPMTMVN RR\\T]U_TaRbOb", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RPESH", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RTEQH", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RNHREVH", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RNFOGNHMGNFNH RVFWGVHUGVFVH", + "MWR[RM RPESH", + "MWR[RM RTEQH", + "LXNHREVH RR[RM", + "LXNFOGNHMGNFNH RVFWGVHUGVFVH RR[RM", + "I\\SCQI RWNUMQMONNOMQMXNZP[T[VZWXWLVITGRFNE", /* U+F0 ETH_SMALL */ + "I\\NMN[ RNOONQMTMVNWPW[ RMHNGPFTHVGWF", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RPESH", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RTEQH", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RNHREVH", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RMHNGPFTHVGWF", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RNFOGNHMGNFNH RVFWGVHUGVFVH", + "E_ZSJS RRXSYRZQYRXRZ RRLSMRNQMRLRN", + "H[XMK[ RP[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[", + "H[VMV[ RMMMXNZP[S[UZVY RPESH", + "H[VMV[ RMMMXNZP[S[UZVY RTEQH", + "H[VMV[ RMMMXNZP[S[UZVY RNHREVH", + "H[VMV[ RMMMXNZP[S[UZVY RNFOGNHMGNFNH RVFWGVHUGVFVH", + "JZMMR[ RWMR[P`OaMb RTEQH", + "H[MFMb RMNOMSMUNVOWQWWVYUZS[O[MZ", + "JZMMR[ RWMR[P`OaMb RNFOGNHMGNFNH RVFWGVHUGVFVH", + /* // LATIN EXTENDED-A (0100-017F) */ + "I[MUWU RK[RFY[ RM@W@", /* U+100 A_CAP MACRON */ + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RMGWG", + "I[MUWU RK[RFY[ RN>O@QASAU@V>", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RNEOGQHSHUGVE", + "I[MUWU RK[RFY[ RY[W]V_WaYb[b", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RW[U]T_UaWbYb", + "F[WYVZS[Q[NZLXKVJRJOKKLINGQFSFVGWH RT>QA", + "HZVZT[P[NZMYLWLQMONNPMTMVN RTEQH", + "F[WYVZS[Q[NZLXKVJRJOKKLINGQFSFVGWH RNAR>VA", + "HZVZT[P[NZMYLWLQMONNPMTMVN RNHREVH", + "F[WYVZS[Q[NZLXKVJRJOKKLINGQFSFVGWH RR?Q@RAS@R?RA", + "HZVZT[P[NZMYLWLQMONNPMTMVN RRFQGRHSGRFRH", + "F[WYVZS[Q[NZLXKVJRJOKKLINGQFSFVGWH RN>RAV>", + "HZVZT[P[NZMYLWLQMONNPMTMVN RNERHVE", + "G\\L[LFQFTGVIWKXOXRWVVXTZQ[L[ RN>RAV>", + "IfW[WF RWZU[Q[OZNYMWMQNOONQMUMWN RbF`J", + "G\\L[LFQFTGVIWKXOXRWVVXTZQ[L[ RIPQP", /* U+110 D_CAP STROKE_H */ + "I\\W[WF RWZU[Q[OZNYMWMQNOONQMUMWN RRHZH", + "H[MPTP RW[M[MFWF RM@W@", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RMGWG", + "H[MPTP RW[M[MFWF RN>O@QASAU@V>", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RNEOGQHSHUGVE", + "H[MPTP RW[M[MFWF RR?Q@RAS@R?RA", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RRFQGRHSGRFRH", + "H[MPTP RW[M[MFWF RR[P]O_PaRbTb", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RR[P]O_PaRbTb", + "H[MPTP RW[M[MFWF RN>RAV>", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RNERHVE", + "F[VGTFQFNGLIKKJOJRKVLXNZQ[S[VZWYWRSR RNAR>VA", + "I\\WMW^V`UaSbPbNa RWZU[Q[OZNYMWMQNOONQMUMWN RNHREVH", + "F[VGTFQFNGLIKKJOJRKVLXNZQ[S[VZWYWRSR RN>O@QASAU@V>", + "I\\WMW^V`UaSbPbNa RWZU[Q[OZNYMWMQNOONQMUMWN RNEOGQHSHUGVE", + "F[VGTFQFNGLIKKJOJRKVLXNZQ[S[VZWYWRSR RR?Q@RAS@R?RA", /* U+120 G_CAP DOT */ + "I\\WMW^V`UaSbPbNa RWZU[Q[OZNYMWMQNOONQMUMWN RRFQGRHSGRFRH", + "F[VGTFQFNGLIKKJOJRKVLXNZQ[S[VZWYWRSR RR\\T]U_TaRbOb", + "I\\WMW^V`UaSbPbNa RWZU[Q[OZNYMWMQNOONQMUMWN RRGPFODPBRAUA", + "G]L[LF RLPXP RX[XF RNAR>VA", + "H[M[MF RV[VPUNSMPMNNMO RIAM>QA", + "G]IJ[J RL[LF RLPXP RX[XF", + "H[M[MF RV[VPUNSMPMNNMO RJHRH", + "MWR[RF RMAN@P?TAV@W?", + "MWR[RM RMHNGPFTHVGWF", + "MWR[RF RM@W@", + "MWR[RM RMGWG", + "MWR[RF RN>O@QASAU@V>", + "MWR[RM RNEOGQHSHUGVE", + "MWR[RF RR[P]O_PaRbTb", + "MWR[RM RR[P]O_PaRbTb", + "MWR[RF RR?Q@RAS@R?RA", /* U+130 I_CAP DOT */ + "MWR[RM", + "MgR[RF RbFbUaX_Z\\[Z[", + "MaR[RM RRFQGRHSGRFRH R\\M\\_[aYbXb R\\F[G\\H]G\\F\\H", + "JZUFUUTXRZO[M[ RQAU>YA", + "MWRMR_QaObNb RNHREVH", + "G\\L[LF RX[OO RXFLR RR\\T]U_TaRbOb", + "IZN[NF RPSV[ RVMNU RR\\T]U_TaRbOb", + "IZNMN[ RPSV[ RVMNU", + "HYW[M[MF RO>LA", + "MXU[SZRXRF RTEQH", + "HYW[M[MF RR\\T]U_TaRbOb", + "MXU[SZRXRF RR\\T]U_TaRbOb", + "HYW[M[MF RVHSK", + "M^U[SZRXRF RZFXJ", + "HYW[M[MF RUNTOUPVOUNUP", + "M\\U[SZRXRF RYOZPYQXPYOYQ", /* U+140 L_SMALL_MIDDOT */ + "HYW[M[MF RJQPM", + "MXU[SZRXRF ROQUM", + "G]L[LFX[XF RT>QA", + "I\\NMN[ RNOONQMTMVNWPW[ RTEQH", + "G]L[LFX[XF RR\\T]U_TaRbOb", + "I\\NMN[ RNOONQMTMVNWPW[ RR\\T]U_TaRbOb", + "G]L[LFX[XF RN>RAV>", + "I\\NMN[ RNOONQMTMVNWPW[ RNERHVE", + "MjSFQJ R\\M\\[ R\\O]N_MbMdNePe[", + "G]LFL[ RLINGPFTFVGWHXJX^W`VaTbQb", + "I\\NMN[ RNOONQMTMVNWPW_VaTbRb", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RM@W@", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RMGWG", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RN>O@QASAU@V>", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RNEOGQHSHUGVE", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RQ>NA RX>UA", /* U+150 O_CAP ACUTE_DBL */ + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RQENH RXEUH", + "E`RPYP RRFR[ R\\FNFLGJIIMITJXLZN[\\[", + "C`[ZY[U[SZRXRPSNUMYM[N\\P\\RRT RRQQOPNNMKMINHOGQGWHYIZK[N[PZQYRW", + "G\\X[QQ RL[LFTFVGWHXJXMWOVPTQLQ RT>QA", + "KXP[PM RPQQORNTMVM RTEQH", + "G\\X[QQ RL[LFTFVGWHXJXMWOVPTQLQ RR\\T]U_TaRbOb", + "KXP[PM RPQQORNTMVM RR\\T]U_TaRbOb", + "G\\X[QQ RL[LFTFVGWHXJXMWOVPTQLQ RN>RAV>", + "KXP[PM RPQQORNTMVM RNERHVE", + "H\\LZO[T[VZWYXWXUWSVRTQPPNOMNLLLJMHNGPFUFXG RT>QA", + "J[NZP[T[VZWXWWVUTTQTOSNQNPONQMTMVN RTEQH", + "H\\LZO[T[VZWYXWXUWSVRTQPPNOMNLLLJMHNGPFUFXG RNAR>VA", + "J[NZP[T[VZWXWWVUTTQTOSNQNPONQMTMVN RNHREVH", + "H\\LZO[T[VZWYXWXUWSVRTQPPNOMNLLLJMHNGPFUFXG RR\\T]U_TaRbOb", + "J[NZP[T[VZWXWWVUTTQTOSNQNPONQMTMVN RR\\T]U_TaRbOb", + "H\\LZO[T[VZWYXWXUWSVRTQPPNOMNLLLJMHNGPFUFXG RN>RAV>", /* U+160 S_CAP CARON */ + "J[NZP[T[VZWXWWVUTTQTOSNQNPONQMTMVN RNERHVE", + "JZLFXF RR[RF RR\\T]U_TaRbOb", + "MYOMWM RRFRXSZU[W[ RR\\T]U_TaRbOb", + "JZLFXF RR[RF RN>RAV>", + "M[OMWM RYFXI RRFRXSZU[W[", + "JZLFXF RR[RF RNQVQ", + "MYOMWM RRFRXSZU[W[ ROSUS", + "G]LFLWMYNZP[T[VZWYXWXF RMAN@P?TAV@W?", + "H[VMV[ RMMMXNZP[S[UZVY RMHNGPFTHVGWF", + "G]LFLWMYNZP[T[VZWYXWXF RM@W@", + "H[VMV[ RMMMXNZP[S[UZVY RMGWG", + "G]LFLWMYNZP[T[VZWYXWXF RN>O@QASAU@V>", + "H[VMV[ RMMMXNZP[S[UZVY RNEOGQHSHUGVE", + "G]LFLWMYNZP[T[VZWYXWXF RRAP@O>P<R;T<U>T@RA", + "H[VMV[ RMMMXNZP[S[UZVY RRHPGOEPCRBTCUETGRH", + "G]LFLWMYNZP[T[VZWYXWXF RQ>NA RX>UA", /* U+170 U_CAP ACUTE_DBL */ + "H[VMV[ RMMMXNZP[S[UZVY RQENH RXEUH", + "G]LFLWMYNZP[T[VZWYXWXF RR[P]O_PaRbTb", + "H[VMV[ RMMMXNZP[S[UZVY RV[T]S_TaVbXb", + "F^IFN[RLV[[F RNAR>VA", + "G]JMN[RQV[ZM RNHREVH", + "I[RQR[ RKFRQYF RNAR>VA", + "JZMMR[ RWMR[P`OaMb RNHREVH", + "JZLFXF RR[RF RN?O@NAM@N?NA RV?W@VAU@V?VA", + "H\\KFYFK[Y[ RT>QA", + "IZLMWML[W[ RTEQH", + "H\\KFYFK[Y[ RR?Q@RAS@R?RA", + "IZLMWML[W[ RRFQGRHSGRFRH", + "H\\KFYFK[Y[ RN>RAV>", + "IZLMWML[W[ RNERHVE", + "MYR[RISGUFWF", + /* // LATIN EXTENDED-B (0180-024F) */ + "H[M[MF RMNOMSMUNVOWQWWVYUZS[O[MZ RJHRH", /* U+180 B_SMALL STROKE_H */ + "C\\LFL[T[VZWYXWXTWRVQSPLP RFKFIGGIFSFUGVHWJWLVNUOSP", + "G\\VFLFL[R[UZWXXVXSWQUORNLN", + "H[WFMFM[ RMNOMSMUNVOWQWWVYUZS[O[MZ", + "H]MFM[S[VZXXYVYSXQVOSNMN", + "IZNMN[S[UZVXVUUSSRNR", + "I^MHNGQFSFVGXIYKZOZRYVXXVZS[Q[NZMY", + "F[WYVZS[Q[NZLXKVJRJOKKLINGQFSFVGWH RMHKGJEKCLB", + "HZVZT[P[NZMYLWLQMONNPMTMVN RTMTIUGWFYF", + "G\\L[LFQFTGVIWKXOXRWVVXTZQ[L[ RIPQP", + "C\\FKFIGGIFQFTGVIWKXOXRWVVXTZQ[L[LF", + "H]NFXFX[R[OZMXLVLSMQOORNXN", + "I\\MFWFW[ RWNUMQMONNOMQMWNYOZQ[U[WZ", + "I\\Q[T[VZWYXWXQWOVNTMQMONNOMQMWNYOZQ[T\\V]W_VaTbPbNa", + "I\\WPPP RM[W[WFMF", + "F^MHNGQFSFVGXIYKZOZRYVXXVZS[Q[NZLXKVJRZP", + "G[PPTP RWGUFPFNGMHLJLLMNNOPPMQLRKTKWLYMZO[U[WZ", /* U+190 EPSILON_LARGE */ + "HZTPMP RM[MFWF RM[M_LaJbHb", + "MYOMWM RR[RISGUFWF RR[R_QaObMb", + "F[VGTFQFNGLIKKJOJRKVLXNZQ[S[VZWYWRSR RMHKGJEKCLB", + "I[KFU[U_TaRbPaO_O[YF", + "D`I[IF RIOJNLMOMQNRPRXSZU[X[ZZ[Y\\W\\P[NZM", + "MZRFRWSYTZV[X[", + "MWR[RF RNPVP", + "G_L[LF RX[OO RLRWGYF[G\\I\\K", + "IZNMN[ RPSV[ RVMNU RNMNIOGQFSF", + "MXU[SZRXRF RNOVO", + "JZRMM[ RMFOFPGRMW[ RNLTH", + "Ca\\F\\[ R\\XZZX[V[TZSYRWRF RRWQYPZN[L[JZIYHWHF", + "G]L[LFX[XF RL[L_KaIbGb", + "I\\NMN[ RNOONQMTMVNWPWb", + "G]KPYP RPFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RVGXFYDXBWA", /* U+1A0 O_CAP HORN */ + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RUNWMXKWIVH", + "DaSGQFMFKGIIHMHTIXKZM[Q[SZUXVTVMUISGUFYF[G\\I\\b", + "E^RNPMMMKNJOIQIWJYKZM[P[RZSYTWTQSORNTMVMXNYPYb", + "C\\LFL[ RFKFIGGIFTFVGWHXJXMWOVPTQLQ", + "H[MMMb RMNOMSMUNVOWQWWVYUZS[O[MZ RRMRISGUFWF", + "G\\LFL[ RQVXb RLKTKVLWMXOXRWTVUTVLV", + "H\\XZU[P[NZMYLWLUMSNRPQTPVOWNXLXJWHVGTFOFLG", + "IZVZT[P[NZMXMWNUPTSTUSVQVPUNSMPMNN", + "H[W[L[SPLFWF", + "JYWbUbSaR_RIQGOFMGLIMKOLQKRI", + "MYOMWM RRFRXSZU[W[ RW[W_VaTbRb", + "HZR[RF RKKKILGNFXF", + "MYOMWM RWFUFSGRIRXSZU[W[", + "JZLFXF RR[RF RR[R_SaUbWb", + "G]LFLWMYNZP[T[VZWYXWXF RXFZE[CZAY@", + "H[VMV[ RMMMXNZP[S[UZVY RVMXLYJXHWG", /* U+1B0 U_SMALL HORN */ + "F^ZFUFUJWKYMZPZUYXWZT[P[MZKXJUJPKMMKOJOFJF", + "G]LFLWMYNZP[T[VZXXYVYIXGWF", + "I`RQR[ RKFRQXGZF\\G]I]K", + "J^MMR[ RMbOaP`R[VNXMZN[P[R", + "H\\KFYFK[Y[ RNPVP", + "IZLMWML[W[ RNTVT", + "H\\KFXFQNTNVOWPXRXWWYVZT[N[LZKY", + "H\\YFLFSNPNNOMPLRLWMYNZP[V[XZYY", + "JZWMNMUVRVPWOXNZN^O`PaRbUbWa", + "JZMMVMOTSTUUVWVXUZS[Q[O\\N^N_OaQbVb", + "H\\LHMGOFTFVGWHXJXLWOK[X[ RNSVS", + "H\\WFMFLPMOONTNVOWPXRXWWYVZT[O[MZLY", + "JZVMOMNSPRSRUSVUVXUZS[P[NZ", + "J^MZP[T[WZYXZVZSYQWOTNPNPF RLITI", + "H[MMMb RMONNPMTMVNWPWSVUM^", + "MWRFRb", /* U+1C0 LINE_V_LETTER */ + "JZOFOb RUFUb", + "MWRFRb ROWUW ROQUQ", + "MWRYSZR[QZRYR[ RRSQGRFSGRSRF", + "GpL[LFQFTGVIWKXOXRWVVXTZQ[L[ R_FmF_[m[ Rb>fAj>", + "GmL[LFQFTGVIWKXOXRWVVXTZQ[L[ R_MjM_[j[ RaEeHiE", + "ImW[WF RWZU[Q[OZNYMWMQNOONQMUMWN R_MjM_[j[ RaEeHiE", + "HiW[M[MF RdFdUcXaZ^[\\[", + "HcW[M[MF R^M^_]a[bZb R^F]G^H_G^F^H", + "MbU[SZRXRF R]M]_\\aZbYb R]F\\G]H^G]F]H", + "GmL[LFX[XF RhFhUgXeZb[`[", + "GgL[LFX[XF RbMb_aa_b^b RbFaGbHcGbFbH", + "IfNMN[ RNOONQMTMVNWPW[ RaMa_`a^b]b RaF`GaHbGaFaH", + "I[MUWU RK[RFY[ RN>RAV>", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RNERHVE", + "MWR[RF RN>RAV>", + "MWR[RM RNERHVE", /* U+1D0 I_SMALL CARON */ + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RN>RAV>", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RNERHVE", + "G]LFLWMYNZP[T[VZWYXWXF RN>RAV>", + "H[VMV[ RMMMXNZP[S[UZVY RNERHVE", + "G]LFLWMYNZP[T[VZWYXWXF RN?O@NAM@N?NA RV?W@VAU@V?VA RM;W;", + "H[VMV[ RMMMXNZP[S[UZVY RNFOGNHMGNFNH RVFWGVHUGVFVH RM@W@", + "G]LFLWMYNZP[T[VZWYXWXF RN?O@NAM@N?NA RV?W@VAU@V?VA RT9Q<", + "H[VMV[ RMMMXNZP[S[UZVY RNFOGNHMGNFNH RVFWGVHUGVFVH RT>QA", + "G]LFLWMYNZP[T[VZWYXWXF RN?O@NAM@N?NA RV?W@VAU@V?VA RN9R<V9", + "H[VMV[ RMMMXNZP[S[UZVY RNFOGNHMGNFNH RVFWGVHUGVFVH RN>RAV>", + "G]LFLWMYNZP[T[VZWYXWXF RN?O@NAM@N?NA RV?W@VAU@V?VA RP9S<", + "H[VMV[ RMMMXNZP[S[UZVY RNFOGNHMGNFNH RVFWGVHUGVFVH RP>SA", + "I[NNPMTMVNWPWXVZT[P[NZMXMVWT", + "I[MUWU RK[RFY[ RN?O@NAM@N?NA RV?W@VAU@V?VA RM;W;", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RNFOGNHMGNFNH RVFWGVHUGVFVH RM@W@", + "I[MUWU RK[RFY[ RR?Q@RAS@R?RA RM;W;", /* U+1E0 A_CAP DOT */ + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RRFQGRHSGRFRH RM@W@", + "F`JURU RRPYP RH[OF\\F RRFR[\\[ RO@Y@", + "D`INKMOMQNRP R[ZY[U[SZRXRPSNUMYM[N\\P\\RRSKSITHVHXIZK[O[QZRX RMGWG", + "F[VGTFQFNGLIKKJOJRKVLXNZQ[S[VZWYWRSR RSV[V", + "I\\WMW^V`UaSbPbNa RWZU[Q[OZNYMWMQNOONQMUMWN RS^[^", + "F[VGTFQFNGLIKKJOJRKVLXNZQ[S[VZWYWRSR RN>RAV>", + "I\\WMW^V`UaSbPbNa RWZU[Q[OZNYMWMQNOONQMUMWN RNERHVE", + "G\\L[LF RX[OO RXFLR RN>RAV>", + "IZN[NF RPSV[ RVMNU RJANDRA", + "G]R[P]O_PaRbTb RPFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF", + "H[R[P]O_PaRbTb RP[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[", + "G]R[P]O_PaRbTb RPFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RM@W@", + "H[R[P]O_PaRbTb RP[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RMGWG", + "H\\KFXFQNTNVOWPXRXWWYVZT[N[LZKY RN>RAV>", + "JZMMVMOVRVTWUXVZV^U`TaRbObMa RNERHVE", + "MWRMR_QaObNb RNERHVE", /* U+1F0 J_SMALL CARON */ + "GpL[LFQFTGVIWKXOXRWVVXTZQ[L[ R_FmF_[m[", + "GmL[LFQFTGVIWKXOXRWVVXTZQ[L[ R_MjM_[j[", + "ImW[WF RWZU[Q[OZNYMWMQNOONQMUMWN R_MjM_[j[", + "F[VGTFQFNGLIKKJOJRKVLXNZQ[S[VZWYWRSR RT>QA", + "I\\WMW^V`UaSbPbNa RWZU[Q[OZNYMWMQNOONQMUMWN RTEQH", + "CaH[HF RHPTP RTFTXUZW[Z[\\Z]X]M", + "G\\LFLb RLINGPFTFVGWHXJXOWRUUL^", + "G]L[LFX[XF RP>SA", + "I\\NMN[ RNOONQMTMVNWPW[ RPESH", + "I[MUWU RK[RFY[ RZ9X< RR;P<O>P@RAT@U>T<R;", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RZ@XC RRBPCOEPGRHTGUETCRB", + "F`JURU RRPYP RH[OF\\F RRFR[\\[ RV>SA", + "D`INKMOMQNRP R[ZY[U[SZRXRPSNUMYM[N\\P\\RRSKSITHVHXIZK[O[QZRX RTEQH", + "G]ZFJ[ RP[NZLXKTKMLINGPFTFVGXIYMYTXXVZT[P[ RT>QA", + "H[XMK[ RP[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RTEQH", + "I[MUWU RK[RFY[ ROAL> RVAS>", /* U+200 A_CAP GRAVE_DBL */ + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR ROHLE RVHSE", + "I[MUWU RK[RFY[ RNAO?Q>S>U?VA", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RNHOFQESEUFVH", + "H[MPTP RW[M[MFWF ROAL> RVAS>", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT ROHLE RVHSE", + "H[MPTP RW[M[MFWF RNAO?Q>S>U?VA", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RNHOFQESEUFVH", + "MWR[RF ROAL> RVAS>", + "MWR[RM ROHLE RVHSE", + "MWR[RF RNAO?Q>S>U?VA", + "MWR[RM RNHOFQESEUFVH", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF ROAL> RVAS>", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ ROHLE RVHSE", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RNAO?Q>S>U?VA", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RNHOFQESEUFVH", + "G\\X[QQ RL[LFTFVGWHXJXMWOVPTQLQ ROAL> RVAS>", /* U+210 R_CAP GRAVE_DBL */ + "KXP[PM RPQQORNTMVM RPHME RWHTE", + "G\\X[QQ RL[LFTFVGWHXJXMWOVPTQLQ RNAO?Q>S>U?VA", + "KXP[PM RPQQORNTMVM ROHPFRETEVFWH", + "G]LFLWMYNZP[T[VZWYXWXF ROAL> RVAS>", + "H[VMV[ RMMMXNZP[S[UZVY ROHLE RVHSE", + "G]LFLWMYNZP[T[VZWYXWXF RNAO?Q>S>U?VA", + "H[VMV[ RMMMXNZP[S[UZVY RNHOFQESEUFVH", + "H\\LZO[T[VZWYXWXUWSVRTQPPNOMNLLLJMHNGPFUFXG RS`SaRcQd", + "J[NZP[T[VZWXWWVUTTQTOSNQNPONQMTMVN RS`SaRcQd", + "JZLFXF RR[RF RS`SaRcQd", + "MYOMWM RRFRXSZU[W[ RU`UaTcSd", + "I]VRXTYVY[X]V_T`Lb RLHMGOFUFWGXHYJYNXPVRTSNU", + "J[UWVXWZW]V_U`SaMb RMNOMSMUNVOWQWTVVUWSXOY", + "G]L[LF RLPXP RX[XF RN>RAV>", + "H[M[MF RV[VPUNSMPMNNMO RI>MAQ>", + "G]L[LFX[XF RX[Xb", /* U+220 N_CAP LEG */ + "IbWFWXXZZ[\\[^Z_X^V\\UZVV^ RWNUMQMONNOMQMWNYOZQ[T[VZWX", + "G]NFLGKIKKLMMNOO RVFXGYIYKXMWNUO ROOUOWPXQYSYWXYWZU[O[MZLYKWKSLQMPOO", + "J[MJMMNORQVOWMWJ RPQTQVRWTWXVZT[P[NZMXMTNRPQ", + "H\\KFYFK[Y[ RY[Y_XaVbTb", + "IZLMWML[W[ RW[W_VaTbRb", + "I[MUWU RK[RFY[ RR?Q@RAS@R?RA", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RRFQGRHSGRFRH", + "H[MPTP RW[M[MFWF RR\\T]U_TaRbOb", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RR\\T]U_TaRbOb", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RN?O@NAM@N?NA RV?W@VAU@V?VA RM;W;", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RNFOGNHMGNFNH RVFWGVHUGVFVH RM@W@", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RMAN@P?TAV@W? RM;W;", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RMHNGPFTHVGWF RM@W@", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RR?Q@RAS@R?RA", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RRFQGRHSGRFRH", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RR?Q@RAS@R?RA RM;W;", /* U+230 O_CAP DOT */ + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RRFQGRHSGRFRH RM@W@", + "I[RQR[ RKFRQYF RM@W@", + "JZMMR[ RWMR[P`OaMb RMGWG", + "M]RFRXSZU[W[YZZXYVWUUVQ^", + "IbNMN[ RNOONQMTMVNWPWXXZZ[\\[^Z_X^V\\UZVV^", + "M]OMWM RRFRXSZU[W[YZZXYVWUUVQ^", + "MWRMR_QaObNb", + "D`R[RF RRZP[L[JZIYHWHQIOJNLMPMRN RTMXMZN[O\\Q\\W[YZZX[T[RZ", + "D`RMRb RRZP[L[JZIYHWHQIOJNLMPMRN RTMXMZN[O\\Q\\W[YZZX[T[RZ", + "I[MUWU RK[RFY[ RXCL`", + "F[WYVZS[Q[NZLXKVJRJOKKLINGQFSFVGWH RXCL`", + "HZVZT[P[NZMYLWLQMONNPMTMVN RWHM`", + "HYW[M[MF RIOQO", + "JZLFXF RR[RF RXCL`", + "J[P[R^T_W_ RNZP[T[VZWXWWVUTTQTOSNQNPONQMTMVN", + "IZLMWML[N[P\\R^T_W_", /* U+240 Z_SMALL_SWASHTAIL */ + "J^MGPFTFWGYIZKZNYPWRTSPSP[", + "J^NNPMTMVNWOXQXSWUVVTWPWP[", + "G\\SPVQWRXTXWWYVZT[L[LFSFUGVHWJWLVNUOSPLP RIUOU", + "G]IM[M RLFLWMYNZP[T[VZWYXWXF", + "I[Y[RFK[", + "H[MPTP RW[M[MFWF RXCL`", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RWHM`", + "JZUFUUTXRZO[M[ RQPYP", + "MWRMR_QaObNb ROTUT RRFQGRHSGRFRH", + "G]XFX^Y`Za\\b^b RXIVGTFPFNGLIKMKTLXNZP[T[VZXX", + "I\\WMW^X`Ya[b]b RWZU[Q[OZNYMWMQNOONQMUMWN", + "G\\X[QQ RL[LFTFVGWHXJXMWOVPTQLQ RIQOQ", + "KXP[PM RPQQORNTMVM RMTUT", + "I[KIYI RRQR[ RKFRQYF", + "JZLQXQ RMMR[ RWMR[P`OaMb", + /* // IPA EXTENSIONS (0250-02AF) */ + "H[MMMXNZP[T[VZ RMNOMTMVNWPWRVTTUOUMV", /* U+250 +A_SMALL */ + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[", + "G\\K[NQOOPNRMTMVNWOXRXVWYVZT[R[PZOYNWMPLNJM", + "H[RFPFNGMIM[ RMNOMSMUNVOWQWWVYUZS[O[MZ", + "J\\NNPMTMVNWOXQXWWYVZT[P[NZ", + "HZVNTMPMNNMOLQLWMYNZP[S[UZVXUVSUQVM^", + "I\\W[WF RWZU[Q[OZNYMWMQNOONQMUMWN RW[W_XaZb\\b", + "I\\\\FZFXGWIW[ RWZU[Q[OZNYMWMQNOONQMUMWN", + "I[NZP[T[VZWXWPVNTMPMNNMPMRWT", + "I[NNPMTMVNWPWXVZT[P[NZMXMVWT", + "IbNNPMTMVNWPWXVZT[P[NZMXMV\\S\\U]W_X`X", + "IZPTNUMWMXNZP[T[VZ RRTPTNSMQMPNNPMTMVN", + "J[TTVSWQWPVNTMPMNN RRTTTVUWWWXVZT[P[NZ", + "JaRTTTVUWWWXVZT[P[NZ RNNPMTMVNWPWQVSTT[S[U\\W^X_X", + "H[TTVSWQWPVNTMPMNNMOLRLVMYNZP[T[VZWXWWVUTTRT", + "MWRMR_QaObNb ROTUT", + "I\\WMW^V`UaSbPbNa RWZU[Q[OZNYMWMQNOONQMUMWN RWMWIXGZF\\F", /* U+260 G_SMALL +HOOK */ + "I\\WYVZT[P[NZMXMQNOONQMWMW^V`UaSbMb", + "HZUNSMPMNNMOLQLWMYNZP[T[VZVUSU", + "JZMMU[U_TaRbPaO_O[WM", + "JZMMTVUXTZR[PZOXPVWM", + "I\\WMWb RNMNXOZQ[T[VZWY", + "H[RFPFNGMIM[ RV[VPUNSMPMNNMO", + "H[RFPFNGMIM[ RV[VPUNSMPMNNMO RV[V_UaSbQb", + "MWR[RM ROTUT RRFQGRHSGRFRH", + "MXRMRXSZU[", + "MWR[RM RU[O[ RUMOM", + "MXU[SZRXRF RMONNPMTOVNWM", + "IYU[SZRXRF RRQQOONMOLQMSOTWT", + "MXRFR_SaUbWb", + "GZLFLXMZO[ RLMVMOVRVTWUXVZV^U`TaRbObMa", + "D`[M[[ R[YZZX[U[SZRXRM RRXQZO[L[JZIXIM", + "D`[M[[ R[YZZX[U[SZRXRM RRXQZO[L[JZIXIM R[[[b", /* U+270 +M_SMALL LEG */ + "D`I[IM RIOJNLMOMQNRPR[ RRPSNUMXMZN[P[[ R[[[_ZaXbVb", + "I\\NMN[ RNOONQMTMVNWPW[ RN[N_MaKbIb", + "I\\NMN[ RNOONQMTMVNWPW[ RW[W_XaZb\\b", + "H[M[MMV[VM", + "H[LTWT RP[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[", + "E]RTXT RRMR[ RZMMMKNJOIQIWJYKZM[Z[", + "G]RTRXSZU[V[XZYXYQXOWNUMOMMNLOKQKXLZN[O[QZRX", + "G]RFRb RPMTMVNXPYRYVXXVZT[P[NZLXKVKRLPNNPM", + "LYTMT[ RTWSYRZP[N[", + "LYTMT[ RTWSYRZP[N[ RTMTF", + "LYTMT[ RTWSYRZP[N[ RT[T_UaWbYb", + "KXP[PM RPQQORNTMVM RP[Pb", + "KXP[PM RPQQORNTMVM RP[P_QaSbUb", + "KXM[S[ RVMTMRNQOPRP[", + "LYW[Q[ RNMPMRNSOTRT[", + "I[RUW[ RN[NMTMVNWPWRVTTUNU", /* U+280 R_SMALLCAP */ + "I[RSWM RNMN[T[VZWXWVVTTSNS", + "J[NZP[T[VZWXWWVUTTQTOSNQNPONQMTMVN RN[N_OaQbSb", + "KYWFUFSGRIR_QaObMb", + "MWRMR_QaObNb ROTUT RRMRISGUFWF", + "KYMFOFQGRIRXSZU[W[", + "KYWFUFSGRIR_QaObMaL_M]O\\V\\", + "KWU[M[ RRbRPQNOMMM", + "MYOMWM RRFR_SaUbWb", + "H[JRYR RVMV[ RMMMXNZP[S[UZVY", + "I\\XMUMUPWRXTXWWYVZT[Q[OZNYMWMTNRPPPMMM", + "H[MMMXNZP[S[UZVYWWWPVNUM", + "JZW[RMM[", + "G]Z[VMRWNMJ[", + "JZW[RM RM[RMTHUGWF", + "KYRTR[ RMMRTWM", + "IZLMWML[W[ RW[W_XaZb\\b", /* U+290 Z_SMALL !HOOK */ + "IZLMWML[T[VZWXVVTURVN^", + "JZMMVMOVRVTWUXVZV^U`TaRbObMa", + "JZMMVMOVRVTWUXVZV^U`TaRbPbNaM_N]P\\R]Uc", + "J^MGPFTFWGYIZKZNYPWRTSPSP[", + "FZWGTFPFMGKIJKJNKPMRPSTST[", + "J^MZP[T[WZYXZVZSYQWOTNPNPF", + "F[WHVGSFQFNGLIKKJOJYK]L_NaQbSbVaW`", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RROQPRQSPRORQ", + "I[STVUWWWXVZT[N[NMSMUNVPVQUSSTNT", + "I\\PTNUMWMXNZP[T[VZWYXVXRWOVNTMPMNNMPMQNSPTRT", + "HZUNSMPMNNMOLQLWMYNZP[T[VZVUSU RUMUIVGXFZF", + "H[MTVT RMMM[ RVMV[", + "LXRMR_QaObMaL_M]O\\V\\ RRFQGRHSGRFRH", + "J[VMVb RTUNM RN[VS", + "JYOMO[V[", + "I\\WMWb RWZU[Q[OZNYMWMQNOONQMUMWN RWMWIXGZF\\F", /* U+2A0 Q_SMALL +HOOK */ + "J^MGPFTFWGYIZKZNYPWRTSPSP[ RLXTX", + "FZWGTFPFMGKIJKJNKPMRPSTST[ RPXXX", + "D`R[RF RRM]MR[][ RRZP[L[JZIYHWHQIOJNLMPMRN", + "E`RFR[ RRNPMMMKNJOIQIWJYKZM[P[RZ RRM\\MUVXVZW[X\\Z\\^[`ZaXbUbSa", + "D`R[RF RRM]MR[Z[\\Z]X\\VZUXVT^ RRZP[L[JZIYHWHQIOJNLMPMRN", + "G^IMQM RLFLXMZO[QZS[W[YZZXZWYUWTTTRSQQQPRNTMWMYN", + "I[KMTM RNFNXOZQ[T[ RYFWFUGTIT_SaQbOb", + "F^HMPM RKFKXLZN[P[RZ RZNXMTMRNQOPQPWQYRZT[W[YZZXYVWUUVQ^", + "F]HMPMP[ RK[KILGNFPF RPOQNSMVMXNYPY_XaVbTb", + "G^LFLXMZO[QZS[W[YZZXZWYUWTTTRSQQQPRNTMWMYN", + "H^MM[MP[ RMFMXNZP[[[", + "G]JSN[RUV[ZS RJFNNRHVNZF", + "G]XXXSLSLX RXKXFLFLK", + "I\\WMWb RNMNXOZQ[T[VZWY RNMNIMGKFIF", + "I\\\\bZbXaW_WM RNMNXOZQ[T[VZWY RNMNIMGKFIF", + /* // SPACING MODIFIER LETTERS (02B0-02FF) */ + "F^K[KFYFY[K[", /* U+2B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // COMBINING DIACRITICAL MARKS (0300-036F) */ + "F^K[KFYFY[K[", /* U+300 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+310 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+320 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+330 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+340 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+350 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+360 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // GREEK AND COPTIC (0370-03FF) */ + "H[MFM[ RXPMP", /* U+370 HETA_CAP */ + "IZNTVT RNMN[", + "G]R[RF RKOKFYFYO", + "I[R[RF RMOMFWFWO", + "MWSFQJ", + "MWS[Q_", + "G]LFL[XFX[", + "H\\MMM[WMW[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "NVR`RcSdTd", + "J\\NZP[T[VZWYXWXQWOVNTMPMNN", + "HZVZT[P[NZMYLWLQMONNPMTMVN RRSQTRUSTRSRU", + "J\\NZP[T[VZWYXWXQWOVNTMPMNN RRSQTRUSTRSRU", + "MWSZS[R]Q^ RRNSORPQORNRP", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+380 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "NVTEQH", + "LXNFOGNHMGNFNH RVFWGVHUGVFVH RT>QA", + "G[MUWU RK[RFY[ RMEJH", + "JZRRQSRTSSRRRT", + "B[MPTP RW[M[MFWF RHEEH", + "A]L[LF RLPXP RX[XF RGEDH", + "GWR[RF RMEJH", + "RR", + "B]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RHEEH", + "RR", + "@[RQR[ RKFRQYF RFECH", + "@^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[ RFECH", + "MXRMRXSZU[ RNFOGNHMGNFNH RVFWGVHUGVFVH RT>QA", /* U+390 IOTA_SMALL DIAERESIS */ + "I[MUWU RK[RFY[", + "G\\SPVQWRXTXWWYVZT[L[LFSFUGVHWJWLVNUOSPLP", + "HZM[MFXF", + "I[K[RFY[K[", + "H[MPTP RW[M[MFWF", + "H\\KFYFK[Y[", + "G]L[LF RLPXP RX[XF", + "F^OPUP RPFTFVGXIYKZNZSYVXXVZT[P[NZLXKVJSJNKKLINGPF", + "MWR[RF", + "G\\L[LF RX[OO RXFLR", + "I[K[RFY[", + "F^K[KFRUYFY[", + "G]L[LFX[XF", + "H[L[W[ RLFWF RUPNP", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF", + "G]L[LFXFX[", /* U+3A0 PI_CAP */ + "G\\L[LFTFVGWHXJXMWOVPTQLQ", + "RR", + "H[W[L[SPLFWF", + "JZLFXF RR[RF", + "I[RQR[ RKFRQYF", + "G]R[RF RPITIWJYLZNZRYTWVTWPWMVKTJRJNKLMJPI", + "H\\KFY[ RYFK[", + "G]R[RF RHFJGKIKNLQMROSUSWRXQYNYIZG\\F", + "F^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[", + "MWR[RF RN?O@NAM@N?NA RV?W@VAU@V?VA", + "I[RQR[ RKFRQYF RN?O@NAM@N?NA RV?W@VAU@V?VA", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RTEQH", + "IZPTNUMWMXNZP[T[VZ RRTPTNSMQMPNNPMTMVN RTEQH", + "I\\NMN[ RNOONQMTMVNWPWb RTEQH", + "MXRMRXSZU[ RTEQH", + "H[MMMXNZP[S[UZVYWWWPVNUM RNFOGNHMGNFNH RVFWGVHUGVFVH RT>QA", /* U+3B0 UPSILON_SMALL DIAERESIS */ + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[", + "H[SOUPVQWSWWVYUZS[P[NZMY RKbLaM_MINGPFSFUGVIVLUNSOQO", + "JZRYRb RLMMMNNRYWM", + "H[SMPMNNMOLQLWMYNZP[S[UZVYWWWQVOUNSMPLNKMINGPFTFVG", + "IZPTNUMWMXNZP[T[VZ RRTPTNSMQMPNNPMTMVN", + "HZMFWFPMNPMSMWNYOZQ[S[U\\V^V_UaSbRb", + "I\\NMN[ RNOONQMTMVNWPWb", + "H[LPWP RPFSFUGVHWKWVVYUZS[P[NZMYLVLKMHNGPF", + "MXRMRXSZU[", + "IZNMN[ RPSV[ RVMNU", + "JZRMM[ RMFOFPGRMW[", + "H^MMMb RWXXZZ[ RMXNZP[T[VZWXWM", + "J[MMR[WPWOVM", + "HZMFWF RQFOGNINLONQOUO RQOOPNQMSMWNYOZQ[S[U\\V^V_UaSbRb", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[", + "F]VMV[ ROMOXNZL[ RZMMMKNJP", /* U+3C0 PI_SMALL */ + "H\\MbMQNOONQMTMVNWOXQXWWYVZT[Q[OZMX", + "HZVNTMPMNNMOLQLWMYNZP[S[U\\V^V_UaSb", + "H\\YMPMNNMOLQLWMYNZP[S[UZVYWWWQVOUNSM", + "H\\LPMNOMXM RRMRXSZU[", + "H[MMMXNZP[S[UZVYWWWPVNUM", + "G]MMLNKPKVLXNZP[T[VZXXYVYPXNVMUMSNRPRb", + "IZWMLb RLMNNOPT_UaWb", + "G]RMRb RKMKVLXNZP[T[VZXXYVYM", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM", + "LXNFOGNHMGNFNH RVFWGVHUGVFVH RRMRXSZU[", + "H[MMMXNZP[S[UZVYWWWPVNUM RNFOGNHMGNFNH RVFWGVHUGVFVH", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RTEQH", + "H[MMMXNZP[S[UZVYWWWPVNUM RTEQH", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RTEQH", + "G\\L[LF RXFLR ROOX[Qb", + "H[SOUPVQWSWWVYUZS[P[NZMXMINGPFSFUGVIVLUNSOQO", /* U+3D0 BETA_SMALL_CURLED */ + "H[JPKQLSLVMYNZP[S[UZVYWVWKVHUGSFPFNGMHLJLLMNNOPPWP", + "I\\KFMFOGQIRKR[ RRKSHTGVFWFYGZI", + "NiTEQH RXFZF\\G^I_K_[ R_K`HaGcFdFfGgI", + "I\\KFMFOGQIRKR[ RRKSHTGVFWFYGZI RN?O@NAM@N?NA RV?W@VAU@V?VA", + "G]RFRb RPMTMVNXPYRYVXXVZT[P[NZLXKVKRLPNNPM", + "F^RTRX R[MIM RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM", + "IZLMNNOPOXNZM[LZLXMVVRWPWNVMUNTPTXUZW[V^U`TaRb", + "G]R[Rb RPFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF", + "H[R[Rb RP[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[", + "FZWFQFNGLIKKJOJRKVLXNZQ[R[T\\U^U_TaSbQb", + "HZVMPMNNMOLQLWMYNZP[R[T\\U^U_TaRbPb", + "HZTPMP RM[MFWF", + "MZVPRP RWFUFSGRIR_QaOb", + "H\\MFOGPILSXNTXUZW[", + "I[RFMPWPR[", + "H\\NGNL RXIULTNTW RKIMGPFTFVGXIYKZOZUYYX[", /* U+3E0 SAMPI_CAP */ + "H\\L[UR RR[WV RLMPNSPURWVXZXb", + "CaRWRR R\\XY]V`SaMa RLFJGHIGLGUHXJZL[N[PZQYRWSYTZV[X[ZZ\\X]U]L\\IZGXF", + "G]RTRX RXZW\\S`PaOa RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM", + "G]XFXb RPFNGLIKMKTLXNZP[T[VZXX", + "I\\WMWb RQMONNOMQMWNYOZQ[T[VZWY", + "F]KFK[ RKQMOPNTNVOXQYTYWXZW\\U^R`Nb", + "I[WLWMVPTRRSPSNRMPMONMPLRLTMVPWSWWVYUZS[M[", + "F]KHLGOFTFWGXHYJYLXOVQJ[N^Q_V_Y^", + "J[NNPMTMVNWPWRVTTVN[P]R^U^W]", + "G]I[[[ RIFJFLGXZZ[ R[FZFXGLZJ[", + "H[KMMNVZX[K[MZVNXM", + "G\\XEVFOFMGLHKJKWLYMZO[T[VZWYXWXPWNVMTLNLLMKN", + "H[WEVFTGPGNHMILKLWMYNZP[S[UZVYWWWQVOUNSMOMMNLO", + "G]RFRb RKQKMYMYQ", + "I[MMWM RRFRb", + "IZLMNNOPOXNZM[LZLXMVVRWPWNVMUNTPTXUZW[", /* U+3F0 KAPPA_SMALL_SCRIPT */ + "H\\WbQbOaN`M^MQNOONQMTMVNWOXQXWWYVZT[Q[OZMX", + "HZVZT[P[NZMYLWLQMONNPMTMVN", + "MWRMR_QaObNb RRFQGRHSGRFRH", + "G]KPYP RPFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF", + "HZLTST RVZT[P[NZMYLWLQMONNPMTMVN", + "J\\XTQT RNZP[T[VZWYXWXQWOVNTMPMNN", + "G\\LFL[ RLKTKVLWMXOXRWTVUTVLV", + "H[MFMb RMNOMSMUNVOWQWWVYUZS[O[MZ", + "F[WYVZS[Q[NZLXKVJRJOKKLINGQFSFVGWH", + "F^K[KFRMYFY[", + "G]LbLMRSXMX[", + "G\\J`S` RMbMQNOONQMTMVNWOXQXWWYVZT[Q[OZMX", + "I^MYNZQ[S[VZXXYVZRZOYKXIVGSFQFNGMH", + "F[WYVZS[Q[NZLXKVJRJOKKLINGQFSFVGWH RROQPRQSPRORQ", + "I^MYNZQ[S[VZXXYVZRZOYKXIVGSFQFNGMH RROQPRQSPRORQ", + /* // Cyrillic (0400-04FF) */ + "H[MPTP RW[M[MFWF RP>SA", /* U+400 E_CAP GRAVE */ + "H[MPTP RW[M[MFWF RN?O@NAM@N?NA RV?W@VAU@V?VA", + "JbLFXF RR[RF RRMXM[N]P^S^\\]_[aXbVb", + "HZM[MFXF RT>QA", + "F[JPTP RWYVZS[Q[NZLXKVJRJOKKLINGQFSFVGWH", + "H\\LZO[T[VZWYXWXUWSVRTQPPNOMNLLLJMHNGPFUFXG", + "MWR[RF", + "MWR[RF RN?O@NAM@N?NA RV?W@VAU@V?VA", + "JZUFUUTXRZO[M[", + "AbC[D[FZGXILJILGOFRFR[X[[Z]X^V^S]Q[OXNRN", + "AbF[FF RRFR[X[[Z]X^V^S]Q[OXNFN", + "JbLFXF RR[RF RRMXM[N]P^S^[", + "G\\L[LF RX[OO RXFLR RT>QA", + "G]LFL[XFX[ RP>SA", + "G[KFRT RYFPXNZL[K[ RN>O@QASAU@V>", + "G]R[R` RLFL[X[XF", + "I[MUWU RK[RFY[", /* U+410 A_CAP */ + "G\\VFLFL[R[UZWXXVXSWQUORNLN", + "G\\SPVQWRXTXWWYVZT[L[LFSFUGVHWJWLVNUOSPLP", + "HZM[MFXF", + "F^[`[[I[I` RW[WFRFPGOHNJL[", + "H[MPTP RW[M[MFWF", + "BbOOF[ RR[RF RRRFF R^[UO R^FRR", + "I]PPTP RMGOFTFVGWHXJXLWNVOTPWQXRYTYWXYWZU[O[MZ", + "G]LFL[XFX[", + "G]LFL[XFX[ RN>O@QASAU@V>", + "G\\L[LF RX[OO RXFLR", + "F\\W[WFTFQGOINLLXKZI[H[", + "F^K[KFRUYFY[", + "G]L[LF RLPXP RX[XF", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF", + "G]L[LFXFX[", + "G\\L[LFTFVGWHXJXMWOVPTQLQ", /* U+420 P_CAP */ + "F[WYVZS[Q[NZLXKVJRJOKKLINGQFSFVGWH", + "JZLFXF RR[RF", + "G[KFRT RYFPXNZL[K[", + "G]R[RF RPITIWJYLZNZRYTWVTWPWMVKTJRJNKLMJPI", + "H\\KFY[ RYFK[", + "G]XFX[ RLFL[Z[Z`", + "H\\WFW[ RLFLNMPNQPRWR", + "CaRFR[ RHFH[\\[\\F", + "CaRFR[ RHFH[\\[\\F R\\[^[^`", + "F]HFMFM[S[VZXXYVYSXQVOSNMN", + "Da\\F\\[ RIFI[O[RZTXUVUSTQROONIN", + "H]MFM[S[VZXXYVYSXQVOSNMN", + "I^ZQPQ RMHNGQFSFVGXIYKZOZRYVXXVZS[Q[NZMY", + "CaHFH[ ROPHP RTFXFZG\\I]M]T\\XZZX[T[RZPXOTOMPIRGTF", + "G\\RQK[ RW[WFOFMGLHKJKMLOMPOQWQ", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR", /* U+430 A_SMALL */ + "H[WEVFTGPGNHMILKLWMYNZP[S[UZVYWWWQVOUNSMOMMNLO", + "I[STVUWWWXVZT[N[NMSMUNVPVQUSSTNT", + "JYO[OMWM", + "H[WOVNTMPMNNMOLQLWMYNZP[S[UZVYWWWJVHUGSFOFMG", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT", + "F^QTJ[ RRUJM RRMR[ RZ[ST RZMRU", + "K[RTTT RNNPMTMVNWPWQVSTTVUWWWXVZT[P[NZ", + "H\\MMM[WMW[", + "H\\MMM[WMW[ RNEOGQHSHUGVE", + "IZNMN[ RPSV[ RVMNU", + "I[V[VMSMQNPPOXNZL[", + "G]L[LMRXXMX[", + "H[MTVT RMMM[ RVMV[", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[", + "H[M[MMVMV[", + "H[MMMb RMNOMSMUNVOWQWWVYUZS[O[MZ", /* U+440 P_SMALL */ + "HZVZT[P[NZMYLWLQMONNPMTMVN", + "KYMMWM RRMR[", + "JZMMR[ RWMR[P`OaMb", + "G]RFRb RPMTMVNXPYRYVXXVZT[P[NZLXKVKRLPNNPM", + "IZL[WM RLMW[", + "I\\WMW[ RNMN[Y[Y`", + "J\\VMV[ RNMNROTQUVU", + "F^RMR[ RKMK[Y[YM", + "F^RMR[ RKMK[Y[YM RY[[[[`", + "HZJMNMN[S[UZVXVUUSSRNR", + "F^YMY[ RKMK[P[RZSXSURSPRKR", + "IZNMN[S[UZVXVUUSSRNR", + "J\\XTQT RNNPMTMVNWOXQXWWYVZT[P[NZ", + "E_JTPT RJMJ[ RT[RZQYPWPQQORNTMWMYNZO[Q[WZYYZW[T[", + "I[RUM[ RV[VMPMNNMPMRNTPUVU", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RPESH", /* U+450 E_SMALL GRAVE */ + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RNFOGNHMGNFNH RVFWGVHUGVFVH", + "M^OKXK RRFR[ RRSSRUQWQYRZTZ[Y^WaVb", + "JYO[OMWM RTEQH", + "HZLTST RVZT[P[NZMYLWLQMONNPMTMVN", + "J[NZP[T[VZWXWWVUTTQTOSNQNPONQMTMVN", + "MWR[RM RRFQGRHSGRFRH", + "LXNFOGNHMGNFNH RVFWGVHUGVFVH RR[RM", + "MWRMR_QaObNb RRFQGRHSGRFRH", + "E^H[JZKXLPMNOMRMR[W[YZZXZUYSWRRR", + "D^IMI[ RRMR[W[YZZXZVYTWSIS", + "M^OKXK RRFR[ RRSSRUQWQYRZTZ[", + "IZNMN[ RPSV[ RVMNU RTEQH", + "H\\MMM[WMW[ RPESH", + "JZMMR[ RWMR[P`OaMb RNEOGQHSHUGVE", + "H]R[R` RMMM[W[WM", + "CaRWRR RLFJGHIGLGUHXJZL[N[PZQYRWSYTZV[X[ZZ\\X]U]L\\IZGXF", /* U+460 OMEGA_LARGE */ + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM", + "F]IIVI RMFM[S[VZXXYVYSXQVOSNMN", + "HZJMTM RNFN[S[UZVXVUUSSRNR", + "D`IFI[ RYPIP R\\Y[ZX[V[SZQXPVOROOPKQISGVFXF[G\\H", + "F^KMK[ RWTKT RZZX[T[RZQYPWPQQORNTMXMZN", + "F^LSXS RRSR[ RH[RF\\[", + "I[NUVU RRUR[ RK[RMY[", + "AbF[FF RFS\\S RVSV[ RL[VF`[", + "E_J[JM RVUV[ RZUJU RO[VM][", + "E_R[RPJFZFRP RI[IVJSLQOPUPXQZS[V[[", + "G]R[RTLMXMRT RK[KXLVMUOTUTWUXVYXY[", + "AcF[FF RFPSP RV[VPNF^FVP RM[MVNSPQSPYP\\Q^S_V_[", + "DaI[IM RITST RV[VTPM\\MVT RO[OXPVQUSTYT[U\\V]X][", + "H\\OPSP RNAQFSBTAUA RLGNFSFUGVHWJWLVNUOSPVQWRXTXWWYVZT[O[M\\L^L_MaObWb", + "J[RTTT ROHRMTIUHVH RNNPMTMVNWPWQVSTTVUWWWXVZT[Q[O\\N^N_OaQbVb", + "G]R[RF RHFJGKIKNLQMROSUSWRXQYNYIZG\\F", /* U+470 PSI_CAP */ + "G]RMRb RKMKVLXNZP[T[VZXXYVYM", + "G]KPYP RPFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF", + "H[LTWT RP[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[", + "I[KFR[YF", + "JZMMR[WM", + "I[KFR[YF ROAL> RVAS>", + "JZMMR[WM ROHLE RVHSE", + "GmPFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF R`Me[ RjMe[c`ba`b", + "HkP[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ R^Mc[ RhMc[a``a^b", + "CaRXR^ RRCRI RMFJGHIGLGUHXJZM[W[ZZ\\X]U]L\\IZGWFMF", + "G]RYR] RRKRO ROMMNLOKQKWLYMZO[U[WZXYYWYQXOWNUMOM", + "CaRWRR RLFJGHIGLGUHXJZL[N[PZQYRWSYTZV[X[ZZ\\X]U]L\\IZGXF RLBM@O?R?U@X@", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RLIMGOFRFUGXG", + "CaRWRR RLFJGHIGLGUHXJZL[N[PZQYRWSYTZV[X[ZZ\\X]U]L\\IZGXF RM<W< RR<R?", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RMEWE RRERH", + "FZWGTFPFMGKIJKJNKPMRPSTST[", /* U+480 KOPPA_CYR_CAP */ + "FZVNTMPMNNMOLQLSMUNVPWTWT[", + "H[N]UO ROQWU RT[LW", + "JZMHMFWGWE", + "JZMHUEVH", + "NVPESH", + "NVTEQH", + "KZLIMGOFRFUGXG", + ":j>R?PAOCPDR RC^D\\F[H\\I^ RCFDDFCHDIF ROcPaR`TaUc ROAP?R>T?UA R[^\\\\^[`\\a^ R[F\\D^C`DaF R`RaPcOePfR", + ":jDQ>Q RH[D_ RHGDC RR_Re RRCR= R\\[`_ R\\G`C R`QfQ", + "G]LFL[XFX[ RX[[[Ub RN>O@QASAU@V>", + "H\\MMM[WMW[ RW[Z[Tb RNEOGQHSHUGVE", + "H]MFM[S[VZXXYVYSXQVOSNMN RJIPI", + "IZKMQM RNFN[S[UZVXVUUSSRNR", + "G\\L[LFTFVGWHXJXMWOVPTQLQ RTMXS", + "H[MMMb RMNOMSMUNVOWQWWVYUZS[O[MZ RSWW]", + "HZM[MFXFXA", /* U+490 GAMMA_CAP_UPTURN */ + "JYO[OMWMWH", + "HZM[MFXF RJQRQ", + "JYO[OMWM RLTTT", + "H]M[MFXF RMMSMVNXPYSY\\X_VaSbQb", + "J\\O[OMWM ROTTTVUWVXXX[W^UaTb", + "BbOOF[ RR[RF RRRFF R^[UO R^FRR R^[`[``", + "F^QTJ[ RRUJM RRMR[ RZ[ST RZMRU RZ[\\[\\`", + "I]PPTP RMGOFTFVGWHXJXLWNVOTPWQXRYTYWXYWZU[O[MZ RR\\T]U_TaRbOb", + "K[RTTT RNNPMTMVNWPWQVSTTVUWWWXVZT[P[NZ RR\\T]U_TaRbOb", + "G\\L[LF RX[OO RXFLR RX[Z[Z`", + "IZNMN[ RPSV[ RVMNU RV[X[X`", + "G\\L[LF RX[OO RXFLR RPKPS", + "IZNMN[ RPSV[ RVMNU RRORW", + "G\\L[LF RX[OO RXFLR RIJOJ", + "IZN[NF RPSV[ RVMNU RKJQJ", + "E\\X[OO RXFLR RGFLFL[", /* U+4A0 K_CAP_BASHKIR */ + "HZPSV[ RVMNU RJMNMN[", + "G]L[LF RLPXP RX[XF RX[Z[Z`", + "H[MTVT RMMM[ RVMV[ RV[X[X`", + "GeL[LF RLPXP RX[XFcF", + "H`MTVT RMMM[ RV[VM^M", + "GhL[LFXFX[ RXM^MaNcPdSd\\c_aa^b\\b", + "HcM[MMVMV[ RVT[T]U^V_X_[^^\\a[b", + "F^QFNGLIKKJOJRKVLXNZQ[S[VZXXYVZRZMYJWIVITJSMSRTVUXWZY[[[", + "H\\QMPMNNMOLQLWMYNZP[T[VZWYXWXRWPUOSPRRRWSYTZV[Y[", + "F[WYVZS[Q[NZLXKVJRJOKKLINGQFSFVGWH RR\\T]U_TaRbOb", + "HZVZT[P[NZMYLWLQMONNPMTMVN RR\\T]U_TaRbOb", + "JZLFXF RR[RF RR[T[T`", + "KYMMWM RRMR[ RR[T[T`", + "I[RQR[ RKFRQYF", + "JZR[Rb RMMR[WM", + "I[RQR[ RKFRQYF RNUVU", /* U+4B0 Y_CAP STROKE_H */ + "JZR[Rb RMMR[WM RN]V]", + "H\\KFY[ RYFK[ RX[Z[Z`", + "IZL[WM RLMW[ RV[X[X`", + "D]FFRF RXFX[ RLFL[Z[Z`", + "G\\RMIM RWMW[ RNMN[Y[Y`", + "H\\WFW[ RLFLNMPNQPRWR RW[Y[Y`", + "J\\VMV[ RNMNROTQUVU RV[X[X`", + "H\\WFW[ RLFLNMPNQPRWR RRNRV", + "J\\VMV[ RNMNROTQUVU RRQRY", + "G]L[LF RL[ RLPRPUQWSXVX[", + "H[M[MF RV[VPUNSMPMNNMO", + "@^WYVZS[Q[NZLXKVJRJOKKLINGQFSFVGXIYKZOJQGQEPDOCMCK", + "E[VZT[P[NZMXMPNNPMTMVNWPWRMTKTISHQHO", + "@^WYVZS[Q[NZLXKVJRJOKKLINGQFSFVGXIYKZOJQGQEPDOCMCK RR[P]O_PaRbTb", + "E[VZT[P[NZMXMPNNPMTMVNWPWRMTKTISHQHO RR[P]O_PaRbTb", + "MWR[RF", /* U+4C0 I_CAP */ + "BbOOF[ RR[RF RRRFF R^[UO R^FRR RN>O@QASAU@V>", + "F^QTJ[ RRUJM RRMR[ RZ[ST RZMRU RNEOGQHSHUGVE", + "G\\L[LF RX[OO RXFLR RX[X_WaUbSb", + "IZNMN[ RPSV[ RVMNU RV[V_UaSbQb", + "F\\W[WFTFQGOINLLXKZI[H[ RW[Z[Tb", + "I[V[VMSMQNPPOXNZL[ RV[Y[Sb", + "G]L[LF RLPXP RX[XF RX[X_WaUbSb", + "H[MTVT RMMM[ RVMV[ RV[V_UaSbQb", + "G]L[LF RLPXP RX[XF RX[[[Ub", + "H[MTVT RMMM[ RVMV[ RV[Y[Sb", + "H\\WFW[ RLFLNMPNQPRWR RW[U[U`", + "J\\VMV[ RNMNROTQUVU RV[T[T`", + "F^K[KFRUYFY[ RY[\\[Vb", + "G]L[LMRXXMX[ RX[[[Ub", + "MWR[RF", + "I[MUWU RK[RFY[ RN>O@QASAU@V>", /* U+4D0 A_CAP BREVE */ + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RNEOGQHSHUGVE", + "I[MUWU RK[RFY[ RN?O@NAM@N?NA RV?W@VAU@V?VA", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RNFOGNHMGNFNH RVFWGVHUGVFVH", + "F`JURU RRPYP RH[OF\\F RRFR[\\[", + "D`INKMOMQNRP R[ZY[U[SZRXRPSNUMYM[N\\P\\RRSKSITHVHXIZK[O[QZRX", + "H[MPTP RW[M[MFWF RN>O@QASAU@V>", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RNEOGQHSHUGVE", + "F^MHNGQFSFVGXIYKZOZRYVXXVZS[Q[NZLXKVJRZP", + "I[NNPMTMVNWPWXVZT[P[NZMXMVWT", + "F^MHNGQFSFVGXIYKZOZRYVXXVZS[Q[NZLXKVJRZP RNBOCNDMCNBND RVBWCVDUCVBVD", + "I[NNPMTMVNWPWXVZT[P[NZMXMVWT RNFOGNHMGNFNH RVFWGVHUGVFVH", + "BbOOF[ RR[RF RRRFF R^[UO R^FRR RN?O@NAM@N?NA RV?W@VAU@V?VA", + "F^QTJ[ RRUJM RRMR[ RZ[ST RZMRU RNFOGNHMGNFNH RVFWGVHUGVFVH", + "I]PPTP RMGOFTFVGWHXJXLWNVOTPWQXRYTYWXYWZU[O[MZ RN?O@NAM@N?NA RV?W@VAU@V?VA", + "K[RTTT RNNPMTMVNWPWQVSTTVUWWWXVZT[P[NZ RNFOGNHMGNFNH RVFWGVHUGVFVH", + "H\\KFXFQNTNVOWPXRXWWYVZT[N[LZKY", /* U+4E0 DIGIT_3 */ + "JZMMVMOVRVTWUXVZV^U`TaRbObMa", + "G]LFL[XFX[ RM@W@", + "H\\MMM[WMW[ RMGWG", + "G]LFL[XFX[ RN?O@NAM@N?NA RV?W@VAU@V?VA", + "H\\MMM[WMW[ RNFOGNHMGNFNH RVFWGVHUGVFVH", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RN?O@NAM@N?NA RV?W@VAU@V?VA", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RNFOGNHMGNFNH RVFWGVHUGVFVH", + "G]KPYP RPFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF", + "H[LTWT RP[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[", + "G]KPYP RPFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RN?O@NAM@N?NA RV?W@VAU@V?VA", + "H[LTWT RP[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RNFOGNHMGNFNH RVFWGVHUGVFVH", + "I^ZPPP RMYNZQ[S[VZXXYVZRZOYKXIVGSFQFNGMH RN?O@NAM@N?NA RV?W@VAU@V?VA", + "J\\XTQT RNZP[T[VZWYXWXQWOVNTMPMNN RNFOGNHMGNFNH RVFWGVHUGVFVH", + "G[KFRT RYFPXNZL[K[ RM@W@", + "JZMMR[ RWMR[P`OaMb RMGWG", + "G[KFRT RYFPXNZL[K[ RN?O@NAM@N?NA RV?W@VAU@V?VA", /* U+4F0 Y_LARGE DIAERESIS */ + "JZMMR[ RWMR[P`OaMb RNFOGNHMGNFNH RVFWGVHUGVFVH", + "G[KFRT RYFPXNZL[K[ RQ>NA RX>UA", + "JZMMR[ RWMR[P`OaMb RQENH RXEUH", + "H\\WFW[ RLFLNMPNQPRWR RN?O@NAM@N?NA RV?W@VAU@V?VA", + "J\\VMV[ RNMNROTQUVU RNFOGNHMGNFNH RVFWGVHUGVFVH", + "HZM[MFXF RM[O[O`", + "JYO[OMWM RO[Q[Q`", + "Da\\F\\[ RIFI[O[RZTXUVUSTQROONIN RN?O@NAM@N?NA RV?W@VAU@V?VA", + "F^YMY[ RKMK[P[RZSXSURSPRKR RNFOGNHMGNFNH RVFWGVHUGVFVH", + "HZWFMFM[Q[Q_PaNbLb RJQRQ", + "JYWMOMO[S[S_RaPbNb RLTTT", + "H\\KFY[ RYFK[ RX[X_WaUbSb", + "IZL[WM RLMW[ RV[V_UaSbQb", + "H\\KFY[ RYFK[ RNPVP", + "IZL[WM RLMW[ RNTVT", + /* // Cyrillic Supplement (500-52F) */ + "G\\WFW[Q[NZLXKVKSLQNOQNWN", /* U+500 !SOFT_CAP */ + "J[VMV[Q[OZNXNUOSQRVR", + "B_RXSZU[X[ZZ[X[M RRFRXQZO[L[IZGXFVFSGQIOLNRN", + "E]RXSZU[V[XZYXYQ RRMRXQZO[M[KZJXJUKSMRRR", + "IePPTP RMGOFTFVGWHXJXLWNVOTPVQWRXTXXYZ[[^[`ZaXaM", + "KbRTTT RNNPMTMVNWPWQVSTTVUWWWXXZZ[[[]Z^X^Q", + "I\\PPTP RMGOFTFVGWHXJXLWNVOTPVQWRXTX[Z[Z`", + "K[RTTT RNNPMTMVNWPWQVSTTVUWWW[Y[Y`", + "FdH[I[KZLXNLOIQGTFWFWXXZZ[][_Z`X`M", + "IaL[NZOXPPQNSMVMVXWZY[Z[\\Z]X]Q", + "CaH[HF RHPTP RTFTXUZW[Z[\\Z]X]M", + "F^KTTT RKMK[ RTMTXUZW[X[ZZ[X[R", + "F[VGTFQFNGLIKKJOJRKVLXNZQ[S[VZWYWRSR", + "HZUNSMPMNNMOLQLWMYNZP[T[VZVUSU", + "J_LFXF RRFRXSZU[X[ZZ[X[M", + "K]MMWM RRMRXSZU[V[XZYXYS", + "G[PPTP RWGUFPFNGMHLJLLMNNOPPMQLRKTKWLYMZO[U[WZ", /* U+510 EPSILON_LARGE */ + "IZPTNUMWMXNZP[T[VZ RRTPTNSMQMPNNPMTMVN", + "F\\W[WFTFQGOINLLXKZI[H[ RW[W_VaTbRb", + "I[V[VMSMQNPPOXNZL[ RV[V_UaSbQb", + "BaP[^F RD[E[GZHXJLKIMGPF^[", + "E^[MO[ RH[JZKXLPMNOM[[", + "E_\\FUO\\[ RJ[JFRFTGUHVJVMUOTPRQJQ", + "F^KMKb R[MUT[[ RKNMMQMSNTOUQUWTYSZQ[M[KZ", + "DaOQH[ RTFT[^[ R[QLQJPIOHMHJIHJGLF^F", + "D`H[MU RRPRMKMINHPHRITKURU R[ZY[U[SZRXRPSNUMYM[N\\P\\RRT", + "G]Z]X\\VZSWQVOV RP[NZLXKTKMLINGPFTFVGXIYMYTXXVZT[P[", + "I\\WMWb RWZU[Q[OZNYMWMQNOONQMUMWN", + "F^IFN[RLV[[F", + "G]JMN[RQV[ZM", + "G\\L[LF RX[OO RXFLR RXKRG", + "IZNMN[ RPSV[ RVMNU RWQQM", + "FgW[WFTFQGOINLLXKZI[H[ RWM]M`NbPcSc\\b_`a]b[b", /* U+520 LYUDI_CAP MIDDLE_HOOK_CAP */ + "IcV[VMSMQNPPOXNZL[ RVT[T]U^V_X_[^^\\a[b", + "GhL[LF RLPXP RX[XF RXM^MaNcPdSd\\c_aa^b\\b", + "HcMTVT RMMM[ RVMV[ RVT[T]U^V_X_[^^\\a[b", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Armenian (530-58F) */ + "F^K[KFYFY[K[", /* U+530 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+540 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+550 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+560 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+570 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+580 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Hebrew (590-5FF) */ + "F^K[KFYFY[K[", /* U+590 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+5A0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+5B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+5C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+5D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+5E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+5F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Arabic (600-6FF) */ + "F^K[KFYFY[K[", /* U+600 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+610 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+620 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+630 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+640 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+650 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+660 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+670 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+680 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+690 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+6A0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+6B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+6C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+6D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+6E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+6F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Syriac (700-74F) */ + "F^K[KFYFY[K[", /* U+700 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+710 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+720 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+730 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+740 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Arabic Supplement (750-77F) */ + "F^K[KFYFY[K[", /* U+750 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+760 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+770 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Taana (780-7BF) */ + "F^K[KFYFY[K[", /* U+780 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+790 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+7A0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+7B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // N'Ko (7C0-7FF) */ + "F^K[KFYFY[K[", /* U+7C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+7D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+7E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+7F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // - (800-8FF) */ + "F^K[KFYFY[K[", /* U+800 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+810 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+820 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+830 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+840 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+850 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+860 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+870 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+880 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+890 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+8A0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+8B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+8C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+8D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+8E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+8F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Devanagari (900-97F) */ + "F^K[KFYFY[K[", /* U+900 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+910 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+920 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+930 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+940 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+950 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+960 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+970 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Bengali (980-9FF) */ + "F^K[KFYFY[K[", /* U+980 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+990 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+9A0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+9B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+9C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+9D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+9E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+9F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Gurmukhi (A00-A7F) */ + "F^K[KFYFY[K[", /* U+A00 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+A10 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+A20 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+A30 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+A40 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+A50 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+A60 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+A70 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Gujarati (A80-AFF) */ + "F^K[KFYFY[K[", /* U+A80 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+A90 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+AA0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+AB0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+AC0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+AD0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+AE0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+AF0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Oriya (B00-B7F) */ + "F^K[KFYFY[K[", /* U+B00 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+B10 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+B20 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+B30 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+B40 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+B50 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+B60 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+B70 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Tamil (B80-BFF) */ + "F^K[KFYFY[K[", /* U+B80 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+B90 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+BA0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+BB0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+BC0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+BD0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+BE0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+BF0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Telugu (C00-C7F) */ + "F^K[KFYFY[K[", /* U+C00 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+C10 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+C20 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+C30 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+C40 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+C50 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+C60 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+C70 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Kannada (C80-CFF) */ + "F^K[KFYFY[K[", /* U+C80 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+C90 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+CA0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+CB0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+CC0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+CD0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+CE0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+CF0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Malayalam (D00-D7F) */ + "F^K[KFYFY[K[", /* U+D00 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+D10 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+D20 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+D30 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+D40 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+D50 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+D60 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+D70 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Simhala (D80-DFF) */ + "F^K[KFYFY[K[", /* U+D80 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+D90 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+DA0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+DB0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+DC0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+DD0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+DE0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+DF0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Thai (E00-E7F) */ + "F^K[KFYFY[K[", /* U+E00 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+E10 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+E20 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+E30 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+E40 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+E50 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+E60 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+E70 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Lao (E80-EFF) */ + "F^K[KFYFY[K[", /* U+E80 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+E90 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+EA0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+EB0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+EC0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+ED0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+EE0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+EF0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Tibetan (F00-FFF) */ + "F^K[KFYFY[K[", /* U+F00 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+F10 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+F20 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+F30 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+F40 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+F50 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+F60 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+F70 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+F80 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+F90 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+FA0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+FB0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+FC0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+FD0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+FE0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+FF0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Myanmar (1000-109F) */ + "F^K[KFYFY[K[", /* U+1000 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1010 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1020 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1030 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1040 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1050 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1060 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1070 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1080 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1090 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Geogian (10A0-10FF) */ + "F^K[KFYFY[K[", /* U+10A0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+10B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+10C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+10D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+10E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+10F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Hangul Jamo (1100-11FF) */ + "F^K[KFYFY[K[", /* U+1100 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1110 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1120 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1130 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1140 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1150 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1160 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1170 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1180 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1190 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+11A0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+11B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+11C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+11D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+11E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+11F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Ethiopic (1200-137F) */ + "F^K[KFYFY[K[", /* U+1200 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1210 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1220 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1230 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1240 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1250 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1260 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1270 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1280 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1290 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+12A0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+12B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+12C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+12D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+12E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+12F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1300 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1310 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1320 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1330 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1340 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1350 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1360 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1370 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Ethiopic Supplement (1380-139F) */ + "F^K[KFYFY[K[", /* U+1380 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1390 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Cherokee (13A0-13FF) */ + "F^K[KFYFY[K[", /* U+13A0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+13B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+13C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+13D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+13E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+13F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Unified Canadian Aboriginal Syllabics (1400-167F) */ + "F^K[KFYFY[K[", /* U+1400 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1410 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1420 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1430 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1440 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1450 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1460 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1470 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1480 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1490 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+14A0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+14B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+14C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+14D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+14E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+14F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1500 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1510 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1520 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1530 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1540 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1550 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1560 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1570 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1580 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1590 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+15A0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+15B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+15C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+15D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+15E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+15F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1600 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1610 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1620 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1630 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1640 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1650 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1660 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1670 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Ogham (1680-169F) */ + "F^K[KFYFY[K[", /* U+1680 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1690 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Runic (16A0-16FF) */ + "F^K[KFYFY[K[", /* U+16A0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+16B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+16C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+16D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+16E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+16F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Tagalog (1700-171F) */ + "F^K[KFYFY[K[", /* U+1700 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1710 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Hanunoo (1720-173F) */ + "F^K[KFYFY[K[", /* U+1720 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1730 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Buhid (1740-175F) */ + "F^K[KFYFY[K[", /* U+1740 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1750 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Tagbanwa (1760-177F) */ + "F^K[KFYFY[K[", /* U+1760 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1770 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Khmer (1780-17FF) */ + "F^K[KFYFY[K[", /* U+1780 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1790 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+17A0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+17B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+17C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+17D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+17E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+17F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Mongolian (1800-18AF) */ + "F^K[KFYFY[K[", /* U+1800 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1810 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1820 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1830 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1840 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1850 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1860 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1870 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1880 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1890 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+18A0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // - (18B0-18FF) */ + "F^K[KFYFY[K[", /* U+18B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+18C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+18D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+18E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+18F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Limbu (1900-194F) */ + "F^K[KFYFY[K[", /* U+1900 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1910 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1920 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1930 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1940 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Tai Le (1950-197F) */ + "F^K[KFYFY[K[", /* U+1950 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1960 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1970 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // New Tai Lue (1980-19DF) */ + "F^K[KFYFY[K[", /* U+1980 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1990 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+19A0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+19B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+19C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+19D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Khmer Symbols (19E0-19FF) */ + "F^K[KFYFY[K[", /* U+19E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+19F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Buginese (1A00-1A1F) */ + "F^K[KFYFY[K[", /* U+1A00 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1A10 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // - (1A20-1AFF) */ + "F^K[KFYFY[K[", /* U+1A20 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1A30 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1A40 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1A50 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1A60 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1A70 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1A80 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1A90 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1AA0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1AB0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1AC0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1AD0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1AE0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1AF0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Balinese (1B00-1B7F) */ + "F^K[KFYFY[K[", /* U+1B00 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1B10 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1B20 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1B30 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1B40 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1B50 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1B60 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1B70 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Sudanese (1B80-1BBF) */ + "F^K[KFYFY[K[", /* U+1B80 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1B90 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1BA0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1BB0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // - (1BC0-1BFF) */ + "F^K[KFYFY[K[", /* U+1BC0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1BD0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1BE0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1BF0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Lepcha (1C00-1C4F) */ + "F^K[KFYFY[K[", /* U+1C00 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1C10 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1C20 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1C30 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1C40 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Ol Chiki (1C50-1C7F) */ + "F^K[KFYFY[K[", /* U+1C50 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1C60 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1C70 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // - (1C80-1CFF) */ + "F^K[KFYFY[K[", /* U+1C80 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1C90 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1CA0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1CB0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1CC0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1CD0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1CE0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1CF0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Phonetic Extensions (1D00-1D7F) */ + "JZNXVX RM[RMW[", /* U+1D00 A_SMALLCAP */ + "H\\LXRX RRTWT RRMR[Y[ RYMPMK[", + "D`[ZY[U[SZRX RINKMOMQNRPRXQZO[K[IZHXHVRUYU[T\\R\\P[NYMUMSNRP", + "I[STVUWWWXVZT[N[NMSMUNVPVQUSSTNT RKWQW", + "HZVZT[P[NZMYLWLQMONNPMTMVN", + "J[SMOMO[S[UZVYWVWRVOUNSM", + "J[SMOMO[S[UZVYWVWRVOUNSM RLTRT", + "JYOTTT RVMOMO[V[", + "J[TTVSWQWPVNTMPMNN RRTTTVUWWWXVZT[P[NZ", + "MWRMR[ RRbSaR`QaRbR`", + "LYTMTWSYRZP[O[", + "IZNMN[ RPSV[ RVMNU", + "JYOMO[V[ RLVRR", + "G]L[LMRXXMX[", + "I\\W[WMN[NM", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[", + "J\\NNPMTMVNWOXQXWWYVZT[P[NZ", /* U+1D10 +C_SMALL */ + "G]YSYVXXWYUZOZMYLXKVKSLQMPOOUOWPXQYS", + "G]XYYWYSXQWPUOOOMPLQKSKWLY", + "G]YNK[ RYSYVXXWYUZOZMYLXKVKSLQMPOOUOWPXQYS", + "DaINKMOMQNRPRXQZO[K[IZHXHVRT RRWSYTZV[Y[[Z\\Y]W]Q\\O[NYMVMTNSORQ", + "G]OMNNMPNRPS RTSVRWPVNUM RPSTSVTWVWXVZT[P[NZMXMVNTPS", + "I\\XTXQWOVNTMQMONNOMQMT", + "H[LTLWMYNZP[S[UZVYWWWT", + "I[N[NMTMVNWPWRVTTUNU", + "I[RUM[ RV[VMPMNNMPMRNTPUVU", + "I[RSMM RVMV[P[NZMXMVNTPSVS", + "KYMMWM RRMR[", + "H[MMMXNZP[S[UZVXVM", + "G]KPYP RKYVYXXYVYSXQWP", + "@]KPYP RKYVYXXYVYSXQWP REWFXEYDXEWEY REOFPEQDPEOEQ", + "G]KKYK RWKXLYNYQXSVTKT RVTXUYWYZX\\V]K]", + "JZMMR[WM", /* U+1D20 V_SMALL */ + "G]JMN[RQV[ZM", + "IZLMWML[W[", + "JZNMVMRRSRUSVUVXUZS[P[NZ", + "H\\XNUMPMNNMOLQLSMUNVPWTXVYWZX\\X^W`VaTbObLa RRTR\\", + "JZW[PROPPNRMTNUPTRM[", + "JYO[OMWM", + "JZM[RMW[", + "H[M[MMVMV[", + "I[N[NMTMVNWPWRVTTUNU", + "I[RMR[ RLMMNMRNTPUTUVTWRWNXM", + "I[V[VMSMQNPPOXNZL[", + "JZNKVK RMNR@WN", + "H\\LKRK RRGWG RR@RNYN RY@P@KN", + "I[SGVHWJWKVMTNNNN@S@UAVCVDUFSGNG", + "I[SGVHWJWKVMTNNNN@S@UAVCVDUFSGNG RKGQG", + "J[S@O@ONSNUMVLWIWEVBUAS@", /* U+1D30 ^D_SMALLCAP */ + "JYOGTG RV@O@ONVN", + "KZUGPG RN@U@UNNN", + "HZUAS@P@NAMBLDLJMLNMPNTNVMVHSH", + "H[MGVG RM@MN RV@VN", + "MWRNR@ RUNON RU@O@", + "LYT@TJSLRMPNON", + "IZN@NN RPFVN RV@NH", + "JYO@ONVN", + "G]LNL@RKX@XN", + "H[MNM@VNV@", + "I\\WNW@NNN@", + "H[PNNMMLLJLDMBNAP@S@UAVBWDWJVLUMSNPN", + "G]O@NAMCNEPF RTFVEWCVAU@ RPFTFVGWIWKVMTNPNNMMKMINGPF", + "I[NNN@T@VAWCWEVGTHNH", + "I[RHWN RNNN@T@VAWCWEVGTHNH", + "KYM@W@ RR@RN", /* U+1D40 ^T_SMALLCAP */ + "H[M@MKNMPNSNUMVKV@", + "G]J@NNRDVNZ@", + "KZOEQDSDUEVGVN RVMTNQNOMNKOIQHVH", + "JYNDNKOMQNSNUM RNEPDSDUEVGUISJNJ", + "H]WDUKTMRNPNNMMKMGNEPDRDTEVMWN", + "H\\XMVNUNSMRK RLDODQERHRKQMONNNLMKKKJVJXIYGXEVDUDSERH", + "KYO@ON ROMQNSNUMVKVGUESDQDOE", + "KYU@UN RUESDQDOENGNKOMQNSNUM", + "LYVMTNRNPMOKOGPERDSDUEVGVHOI", + "LYOEQDSDUEVGVKUMSNRNPMOKOJVI", + "LXPIRI RUETDPDOEOHPIOJOMPNTNUM", + "LXRITI ROEPDTDUEUHTIUJUMTNPNOM", + "KYUDUPTRRSOS RUESDQDOENGNKOMQNSNUM", + "NVRDRN RRUSTRSQTRURS", + "IZO@ON RUNQH RUDOJ", + "G]KNKD RKEMDODQERGRN RRGSEUDVDXEYGYN", /* U+1D50 ^M_TINY */ + "KZODON ROEQDSDUEVGVPURSSRS", + "KYQNOMNKNGOEQDSDUEVGVKUMSNQN", + "LYOEQDSDUEVGVKUMSNQNOM", + "KYNINGOEQDSDUEVGVI", + "KYNINKOMQNSNUMVKVI", + "KYOSOD ROEQDSDUEVGVKUMSNQNOM", + "NXPDVD RR@RKSMUNVN", + "KYUDUN RNDNKOMQNSNUM", + "I[MFWF RMMTMVLWJWHVF", + "G]YDYN RYMWNUNSMRKRD RRKQMONNNLMKKKD", + "LXNDRNVD", + "LXVNPGPEQDSDTETGNN", + "KYSFRF RNSOQOCPAR@S@UAVCUESFUGVIVKUMSNQNOM", + "KXRMRS RMDOERMVD", + "KYSDQDOENGNKOMQNSNUMVKVGUESDPCOBOAP@U@", + "I[MDLFLJMLNMPNTNVMWLXJXGWEUDSERGRS", /* U+1D60 ^PHI_TINY */ + "LXVDNS RNDPETRVS", + "NVRWRa RRPQQRRSQRPRR", + "LWPWPa RPZQXSWUW", + "KYUWUa RNWN^O`QaSaU`", + "LXNWRaVW", + "KYSYRY RNfOdOVPTRSSSUTVVUXSYUZV\\V^U`SaQaO`", + "KXR`Rf RMWOXR`VW", + "KYOfOZPXRWSWUXVZV^U`SaQaO`", + "I[MWLYL]M_N`PaTaV`W_X]XZWXUWSXRZRf", + "LXVWNf RNWPXTeVf", + "D`IMIXJZL[O[QZRX R[ZY[U[SZRXRPSNUMYM[N\\P\\RRT", + "H[M[MF RMNOMSMUNVOWQWWVYUZS[O[MZ RIHJGLFPHRGSF", + "I\\W[WF RWZU[Q[OZNYMWMQNOONQMUMWN RQHRGTFXHZG[F", + "MYOMWM RR[RISGUFWF RMTNSPRTTVSWR", + "D`I[IM RIOJNLMOMQNRPR[ RRPSNUMXMZN[P[[ RMTNSPRTTVSWR", + "I\\NMN[ RNOONQMTMVNWPW[ RMTNSPRTTVSWR", /* U+1D70 N_SMALL TILDE */ + "H[MMMb RMNOMSMUNVOWQWWVYUZS[O[MZ RI`J_L^P`R_S^", + "KXP[PM RPQQORNTMVM RLTMSORSTUSVR", + "KXM[S[ RVMTMRNQOPRP[ RLTMSORSTUSVR", + "J[NZP[T[VZWXWWVUTTQTOSNQNPONQMTMVN RNTOSQRUTWSXR", + "MYOMWM RRFRXSZU[W[ RMSNRPQTSVRWQ", + "IZLMWML[W[ RMTNSPRTTVSWR", + "H[M[MJNHOGQFTFVG RMNOMSMUNVOWQWWVYUZS[O[MZ", + "H[MGVG RM@MN RV@VN", + "JZMMVMOURUTVUWVYV^U`TaRbPbNaM_M^N\\P[V[", + "MlOMWM RRFRXSZU[W[ R^[^F Rg[gPfNdMaM_N^O RiC]`", + "MWR[RM RU[O[ RUMOM ROTUT", + "MXRMRXSZU[ ROTUT", + "H[MMMb RMNOMSMUNVOWQWWVYUZS[O[MZ RHT\\T", + "H[MMMXNZP[S[UZVXVM RHT\\T", + "I\\XMUMUPWRXTXWWYVZT[Q[OZNYMWMTNRPPPMMM RHU\\U", + /* // Phonetic Extensions Supplement (1D80-1DBF) */ + "F^K[KFYFY[K[", /* U+1D80 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1D90 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1DA0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1DB0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Combining Diacritical Marks Supplement (1DC0-1DFF) */ + "F^K[KFYFY[K[", /* U+1DC0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1DD0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1DE0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+1DF0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Latin Extended Additional (1E00-1EFF) */ + "I[MUWU RK[RFY[ RR`TaUcTeRfPeOcPaR`", /* U+1E00 A_CAP +RING */ + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RR`TaUcTeRfPeOcPaR`", + "G\\SPVQWRXTXWWYVZT[L[LFSFUGVHWJWLVNUOSPLP RR?Q@RAS@R?RA", + "H[M[MF RMNOMSMUNVOWQWWVYUZS[O[MZ RN?M@NAO@N?NA", + "G\\SPVQWRXTXWWYVZT[L[LFSFUGVHWJWLVNUOSPLP RRbSaR`QaRbR`", + "H[M[MF RMNOMSMUNVOWQWWVYUZS[O[MZ RRbSaR`QaRbR`", + "G\\SPVQWRXTXWWYVZT[L[LFSFUGVHWJWLVNUOSPLP RWaMa", + "H[M[MF RMNOMSMUNVOWQWWVYUZS[O[MZ RWaMa", + "F[WYVZS[Q[NZLXKVJRJOKKLINGQFSFVGWH RR\\T]U_TaRbOb RT>QA", + "HZVZT[P[NZMYLWLQMONNPMTMVN RR\\T]U_TaRbOb RTEQH", + "G\\L[LFQFTGVIWKXOXRWVVXTZQ[L[ RR?Q@RAS@R?RA", + "I\\W[WF RWZU[Q[OZNYMWMQNOONQMUMWN RV?U@VAW@V?VA", + "G\\L[LFQFTGVIWKXOXRWVVXTZQ[L[ RRbSaR`QaRbR`", + "I\\W[WF RWZU[Q[OZNYMWMQNOONQMUMWN RSbTaS`RaSbS`", + "G\\L[LFQFTGVIWKXOXRWVVXTZQ[L[ RWaMa", + "I\\W[WF RWZU[Q[OZNYMWMQNOONQMUMWN RXaNa", + "G\\L[LFQFTGVIWKXOXRWVVXTZQ[L[ RQ\\S]T_SaQbNb", /* U+1E10 D_CAP CEDILLA */ + "I\\W[WF RWZU[Q[OZNYMWMQNOONQMUMWN RS\\U]V_UaSbPb", + "G\\L[LFQFTGVIWKXOXRWVVXTZQ[L[ RVcR`Nc", + "I\\W[WF RWZU[Q[OZNYMWMQNOONQMUMWN RWcS`Oc", + "H[MPTP RW[M[MFWF RM@W@ RP9S<", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RMGWG RP>SA", + "H[MPTP RW[M[MFWF RM@W@ RT9Q<", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RMGWG RT>QA", + "H[MPTP RW[M[MFWF RVcR`Nc", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RVcR`Nc", + "H[MPTP RW[M[MFWF RW`VaTbP`NaMb", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RW`VaTbP`NaMb", + "H[MPTP RW[M[MFWF RR\\T]U_TaRbOb RN>O@QASAU@V>", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RR\\T]U_TaRbOb RNEOGQHSHUGVE", + "HZTPMP RM[MFWF RR?Q@RAS@R?RA", + "MYOMWM RR[RISGUFWF RT?S@TAU@T?TA", + "F[VGTFQFNGLIKKJOJRKVLXNZQ[S[VZWYWRSR RM@W@", /* U+1E20 G_CAP MACRON */ + "I\\WMW^V`UaSbPbNa RWZU[Q[OZNYMWMQNOONQMUMWN RMGWG", + "G]L[LF RLPXP RX[XF RR?Q@RAS@R?RA", + "H[M[MF RV[VPUNSMPMNNMO RM?L@MAN@M?MA", + "G]L[LF RLPXP RX[XF RRbSaR`QaRbR`", + "H[M[MF RV[VPUNSMPMNNMO RRbSaR`QaRbR`", + "G]L[LF RLPXP RX[XF RN?O@NAM@N?NA RV?W@VAU@V?VA", + "H[M[MF RV[VPUNSMPMNNMO RI?J@IAH@I?IA RQ?R@QAP@Q?QA", + "G]L[LF RLPXP RX[XF RL\\N]O_NaLbIb", + "H[M[MF RV[VPUNSMPMNNMO RM\\O]P_OaMbJb", + "G]L[LF RLPXP RX[XF RV`UbScQcObN`", + "H[M[MF RV[VPUNSMPMNNMO RV`UbScQcObN`", + "MWR[RF RW`VaTbP`NaMb", + "MWR[RM RRFQGRHSGRFRH RW`VaTbP`NaMb", + "MWR[RF RN?O@NAM@N?NA RV?W@VAU@V?VA RT9Q<", + "MWR[RM RNFOGNHMGNFNH RVFWGVHUGVFVH RT>QA", + "G\\L[LF RX[OO RXFLR RT>QA", /* U+1E30 K_CAP ACUTE */ + "IZN[NF RPSV[ RVMNU RPAMD", + "G\\L[LF RX[OO RXFLR RRbSaR`QaRbR`", + "IZN[NF RPSV[ RVMNU RRbSaR`QaRbR`", + "G\\L[LF RX[OO RXFLR RWaMa", + "IZN[NF RPSV[ RVMNU RWaMa", + "HYW[M[MF RRbSaR`QaRbR`", + "MXU[SZRXRF RSbTaS`RaSbS`", + "HYW[M[MF RH@R@ RRbSaR`QaRbR`", + "MXU[SZRXRF RM@W@ RSbTaS`RaSbS`", + "HYW[M[MF RWaMa", + "MXU[SZRXRF RXaNa", + "HYW[M[MF RVcR`Nc", + "MXU[SZRXRF RWcS`Oc", + "F^K[KFRUYFY[ RT>QA", + "D`I[IM RIOJNLMOMQNRPR[ RRPSNUMXMZN[P[[ RTEQH", + "F^K[KFRUYFY[ RR?Q@RAS@R?RA", /* U+1E40 M_CAP DOT */ + "D`I[IM RIOJNLMOMQNRPR[ RRPSNUMXMZN[P[[ RRFQGRHSGRFRH", + "F^K[KFRUYFY[ RRbSaR`QaRbR`", + "D`I[IM RIOJNLMOMQNRPR[ RRPSNUMXMZN[P[[ RRbSaR`QaRbR`", + "G]L[LFX[XF RR?Q@RAS@R?RA", + "I\\NMN[ RNOONQMTMVNWPW[ RRFQGRHSGRFRH", + "G]L[LFX[XF RRbSaR`QaRbR`", + "I\\NMN[ RNOONQMTMVNWPW[ RRbSaR`QaRbR`", + "G]L[LFX[XF RWaMa", + "I\\NMN[ RNOONQMTMVNWPW[ RWaMa", + "G]L[LFX[XF RVcR`Nc", + "I\\NMN[ RNOONQMTMVNWPW[ RVcR`Nc", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RMAN@P?TAV@W? RT9Q<", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RMHNGPFTHVGWF RT>QA", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RMAN@P?TAV@W? RN:O;N<M;N:N< RV:W;V<U;V:V<", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RMHNGPFTHVGWF RN?O@NAM@N?NA RV?W@VAU@V?VA", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RM@W@ RP9S<", /* U+1E50 O_CAP MACRON */ + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RMGWG RP>SA", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RM@W@ RT9Q<", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RMGWG RT>QA", + "G\\L[LFTFVGWHXJXMWOVPTQLQ RT>QA", + "H[MMMb RMNOMSMUNVOWQWWVYUZS[O[MZ RTEQH", + "G\\L[LFTFVGWHXJXMWOVPTQLQ RR?Q@RAS@R?RA", + "H[MMMb RMNOMSMUNVOWQWWVYUZS[O[MZ RRFQGRHSGRFRH", + "G\\X[QQ RL[LFTFVGWHXJXMWOVPTQLQ RR?Q@RAS@R?RA", + "KXP[PM RPQQORNTMVM RSFRGSHTGSFSH", + "G\\X[QQ RL[LFTFVGWHXJXMWOVPTQLQ RRbSaR`QaRbR`", + "KXP[PM RPQQORNTMVM RPbQaP`OaPbP`", + "G\\X[QQ RL[LFTFVGWHXJXMWOVPTQLQ RM@W@ RRbSaR`QaRbR`", + "KXP[PM RPQQORNTMVM RNGXG RPbQaP`OaPbP`", + "G\\X[QQ RL[LFTFVGWHXJXMWOVPTQLQ RWaMa", + "KXP[PM RPQQORNTMVM RUaKa", + "H\\LZO[T[VZWYXWXUWSVRTQPPNOMNLLLJMHNGPFUFXG RR?Q@RAS@R?RA", /* U+1E60 S_CAP DOT */ + "J[NZP[T[VZWXWWVUTTQTOSNQNPONQMTMVN RRFQGRHSGRFRH", + "H\\LZO[T[VZWYXWXUWSVRTQPPNOMNLLLJMHNGPFUFXG RRbSaR`QaRbR`", + "J[NZP[T[VZWXWWVUTTQTOSNQNPONQMTMVN RRbSaR`QaRbR`", + "H\\LZO[T[VZWYXWXUWSVRTQPPNOMNLLLJMHNGPFUFXG RU>RA RM>N?M@L?M>M@", + "J[NZP[T[VZWXWWVUTTQTOSNQNPONQMTMVN RUERH RMENFMGLFMEMG", + "H\\LZO[T[VZWYXWXUWSVRTQPPNOMNLLLJMHNGPFUFXG RN>RAV> RR:Q;R<S;R:R<", + "J[NZP[T[VZWXWWVUTTQTOSNQNPONQMTMVN RNERHVE RR?Q@RAS@R?RA", + "H\\LZO[T[VZWYXWXUWSVRTQPPNOMNLLLJMHNGPFUFXG RR?Q@RAS@R?RA RRbSaR`QaRbR`", + "J[NZP[T[VZWXWWVUTTQTOSNQNPONQMTMVN RRFQGRHSGRFRH RRbSaR`QaRbR`", + "JZLFXF RR[RF RR?Q@RAS@R?RA", + "MYOMWM RRFRXSZU[W[ RR?Q@RAS@R?RA", + "JZLFXF RR[RF RRbSaR`QaRbR`", + "MYOMWM RRFRXSZU[W[ RTbUaT`SaTbT`", + "JZLFXF RR[RF RWaMa", + "MYOMWM RRFRXSZU[W[ RYaOa", + "JZLFXF RR[RF RVcR`Nc", /* U+1E70 T_CAP +CARON */ + "MYOMWM RRFRXSZU[W[ RXcT`Pc", + "G]LFLWMYNZP[T[VZWYXWXF RVbUaV`WaVbV` RNbMaN`OaNbN`", + "H[VMV[ RMMMXNZP[S[UZVY RVbUaV`WaVbV` RNbMaN`OaNbN`", + "G]LFLWMYNZP[T[VZWYXWXF RW`VaTbP`NaMb", + "H[VMV[ RMMMXNZP[S[UZVY RW`VaTbP`NaMb", + "G]LFLWMYNZP[T[VZWYXWXF RVcR`Nc", + "H[VMV[ RMMMXNZP[S[UZVY RVcR`Nc", + "G]LFLWMYNZP[T[VZWYXWXF RMAN@P?TAV@W? RT9Q<", + "H[VMV[ RMMMXNZP[S[UZVY RMHNGPFTHVGWF RT>QA", + "G]LFLWMYNZP[T[VZWYXWXF RM@W@ RN:O;N<M;N:N< RV:W;V<U;V:V<", + "H[VMV[ RMMMXNZP[S[UZVY RMGWG RN?O@NAM@N?NA RV?W@VAU@V?VA", + "I[KFR[YF RMAN@P?TAV@W?", + "JZMMR[WM RMHNGPFTHVGWF", + "I[KFR[YF RRbSaR`QaRbR`", + "JZMMR[WM RRbSaR`QaRbR`", + "F^IFN[RLV[[F RP>SA", /* U+1E80 W_CAP GRAVE */ + "G]JMN[RQV[ZM RPESH", + "F^IFN[RLV[[F RT>QA", + "G]JMN[RQV[ZM RTEQH", + "F^IFN[RLV[[F RN?O@NAM@N?NA RV?W@VAU@V?VA", + "G]JMN[RQV[ZM RNFOGNHMGNFNH RVFWGVHUGVFVH", + "F^IFN[RLV[[F RR?Q@RAS@R?RA", + "G]JMN[RQV[ZM RRFQGRHSGRFRH", + "F^IFN[RLV[[F RRbSaR`QaRbR`", + "G]JMN[RQV[ZM RRbSaR`QaRbR`", + "H\\KFY[ RYFK[ RR?Q@RAS@R?RA", + "IZL[WM RLMW[ RRFQGRHSGRFRH", + "H\\KFY[ RYFK[ RN?O@NAM@N?NA RV?W@VAU@V?VA", + "IZL[WM RLMW[ RNFOGNHMGNFNH RVFWGVHUGVFVH", + "I[RQR[ RKFRQYF RR?Q@RAS@R?RA", + "JZMMR[ RWMR[P`OaMb RRFQGRHSGRFRH", + "H\\KFYFK[Y[ RNAR>VA", /* U+1E90 Z_CAP CIRCUMFLEX */ + "IZLMWML[W[ RNHREVH", + "H\\KFYFK[Y[ RRbSaR`QaRbR`", + "IZLMWML[W[ RRbSaR`QaRbR`", + "H\\KFYFK[Y[ RWaMa", + "IZLMWML[W[ RWaMa", + "H[M[MF RV[VPUNSMPMNNMO RWaMa", + "MYOMWM RRFRXSZU[W[ RN?O@NAM@N?NA RV?W@VAU@V?VA", + "G]JMN[RQV[ZM RRHPGOEPCRBTCUETGRH", + "JZMMR[ RWMR[P`OaMb RRHPGOEPCRBTCUETGRH", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RWJYIZGYEWD", + "MYR[RISGUFWF RT?S@TAU@T?TA", + "MYR[RISGUFWF ROSUO", + "MYR[RISGUFWF ROLUL", + "E^J[JLKIMGPFZFSNVNXOYPZRZWYYXZV[R[PZOY", + "H[SMPMNNMOLQLWMYNZP[S[UZVYWWWQVOUNSMPLNKMINGPFTFVG", + "I[MUWU RK[RFY[ RRbSaR`QaRbR`", /* U+1EA0 A_CAP +DOT */ + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RRbSaR`QaRbR`", + "I[MUWU RK[RFY[ RRAT?U=T;R:P:", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RRHTFUDTBRAPA", + "I[MUWU RK[RFY[ RU>X; RNAR>VA", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RUEXB RNHREVH", + "I[MUWU RK[RFY[ RO>L; RNAR>VA", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR ROELB RNHREVH", + "I[MUWU RK[RFY[ RNAR>VA RXAZ?[=Z;X:V:", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RNHREVH RXHZF[DZBXAVA", + "I[MUWU RK[RFY[ RNAR>VA RM<N;P:T<V;W:", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RNHREVH RMAN@P?TAV@W?", + "I[MUWU RK[RFY[ RNAR>VA RRbSaR`QaRbR`", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RNHREVH RRbSaR`QaRbR`", + "I[MUWU RK[RFY[ RN>O@QASAU@V> RT9Q<", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RNEOGQHSHUGVE RT>QA", + "I[MUWU RK[RFY[ RN>O@QASAU@V> RP9S<", /* U+1EB0 A_CAP BREVE */ + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RNEOGQHSHUGVE RP>SA", + "I[MUWU RK[RFY[ RN>O@QASAU@V> RP>R<S:R8P7N7", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RNEOGQHSHUGVE RPERCSAR?P>N>", + "I[MUWU RK[RFY[ RN>O@QASAU@V> RM<N;P:T<V;W:", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RNEOGQHSHUGVE RMAN@P?TAV@W?", + "I[MUWU RK[RFY[ RN>O@QASAU@V> RRbSaR`QaRbR`", + "I\\W[WPVNTMPMNN RWZU[P[NZMXMVNTPSUSWR RNEOGQHSHUGVE RRbSaR`QaRbR`", + "H[MPTP RW[M[MFWF RRbSaR`QaRbR`", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RRbSaR`QaRbR`", + "H[MPTP RW[M[MFWF RRAT?U=T;R:P:", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RRHTFUDTBRAPA", + "H[MPTP RW[M[MFWF RMAN@P?TAV@W?", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RMHNGPFTHVGWF", + "H[MPTP RW[M[MFWF RU>X; RNAR>VA", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RUEXB RNHREVH", + "H[MPTP RW[M[MFWF RO>L; RNAR>VA", /* U+1EC0 E_CAP CIRCUMFLEX_GRAVE */ + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT ROELB RNHREVH", + "H[MPTP RW[M[MFWF RNAR>VA RXAZ?[=Z;X:V:", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RNHREVH RXHZF[DZBXAVA", + "H[MPTP RW[M[MFWF RNAR>VA RM<N;P:T<V;W:", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RNHREVH RMAN@P?TAV@W?", + "H[MPTP RW[M[MFWF RNAR>VA RRbSaR`QaRbR`", + "I[VZT[P[NZMXMPNNPMTMVNWPWRMT RNHREVH RRbSaR`QaRbR`", + "MWR[RF RRAT?U=T;R:P:", + "MWR[RM RRHTFUDTBRAPA", + "MWR[RF RRbSaR`QaRbR`", + "MWR[RM RRFQGRHSGRFRH RRbSaR`QaRbR`", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RRbSaR`QaRbR`", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RRbSaR`QaRbR`", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RRAT?U=T;R:P:", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RRHTFUDTBRAPA", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RU>X; RNAR>VA", /* U+1ED0 O_CAP CIRCUMFLEX_ACUTE */ + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RUEXB RNHREVH", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RO>L; RNAR>VA", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ ROELB RNHREVH", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RNAR>VA RXAZ?[=Z;X:V:", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RNHREVH RXHZF[DZBXAVA", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RNAR>VA RM<N;P:T<V;W:", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RNHREVH RMAN@P?TAV@W?", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RNAR>VA RRbSaR`QaRbR`", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RNHREVH RRbSaR`QaRbR`", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RVGXFYDXBWA RT>QA", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RUNWMXKWIVH RTEQH", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RVGXFYDXBWA RP>SA", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RUNWMXKWIVH RPESH", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RVGXFYDXBWA RRAT?U=T;R:P:", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RUNWMXKWIVH RRHTFUDTBRAPA", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RVGXFYDXBWA RWAVBTCPANBMC", /* U+1EE0 O_CAP HORN */ + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RUNWMXKWIVH RWHVITJPHNIMJ", + "G]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RVGXFYDXBWA RRbSaR`QaRbR`", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RUNWMXKWIVH RRbSaR`QaRbR`", + "G]LFLWMYNZP[T[VZWYXWXF RRbSaR`QaRbR`", + "H[VMV[ RMMMXNZP[S[UZVY RRbSaR`QaRbR`", + "G]LFLWMYNZP[T[VZWYXWXF RRAT?U=T;R:P:", + "H[VMV[ RMMMXNZP[S[UZVY RRHTFUDTBRAPA", + "G]LFLWMYNZP[T[VZWYXWXF RXFZE[CZAY@ RT>QA", + "H[VMV[ RMMMXNZP[S[UZVY RVMXLYJXHWG RTEQH", + "G]LFLWMYNZP[T[VZWYXWXF RXFZE[CZAY@ RP>SA", + "H[VMV[ RMMMXNZP[S[UZVY RVMXLYJXHWG RPESH", + "G]LFLWMYNZP[T[VZWYXWXF RXFZE[CZAY@ RRAT?U=T;R:P:", + "H[VMV[ RMMMXNZP[S[UZVY RVMXLYJXHWG RRHTFUDTBRAPA", + "G]LFLWMYNZP[T[VZWYXWXF RXFZE[CZAY@ RWAVBTCPANBMC", + "H[VMV[ RMMMXNZP[S[UZVY RVMXLYJXHWG RWHVITJPHNIMJ", + "G]LFLWMYNZP[T[VZWYXWXF RXFZE[CZAY@ RRbSaR`QaRbR`", /* U+1EF0 U_CAP HORN */ + "H[VMV[ RMMMXNZP[S[UZVY RVMXLYJXHWG RRbSaR`QaRbR`", + "I[RQR[ RKFRQYF RP>SA", + "JZMMR[ RWMR[P`OaMb RPESH", + "I[RQR[ RKFRQYF RRbSaR`QaRbR`", + "JZMMR[ RWMR[P`OaMb RVbWaV`UaVbV`", + "I[RQR[ RKFRQYF RRAT?U=T;R:P:", + "JZMMR[ RWMR[P`OaMb RRHTFUDTBRAPA", + "I[RQR[ RKFRQYF RMAN@P?TAV@W?", + "JZMMR[ RWMR[P`OaMb RMHNGPFTHVGWF", + "E\\PFP[ RJFJ[Z[", + "J[MMWM ROFOXPZR[ RX[VZUXUF", + "G]QFOGMJLMLWMYNZP[T[VZXXYVYTXPVMUL", + "H[QMONNOMQMWNYOZQ[S[UZVYWWWUVSURSQ", + "G[KFRT RYFRTPXOZM[KZJXKVMUOVPX", + "JZMMR[ RWMR[Q_PaNbLaK_L]N\\P]Q_", + /* // Greek Extended (1F00-1FFF) */ + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RQHRHSGSE", /* U+1F00 ALPHA_SMALL PSILI */ + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RQEQGRHSH", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RTEWH RMHNHOGOE", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RTEWH RMEMGNHOH", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RXEUH RMHNHOGOE", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RXEUH RMEMGNHOH", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RQHRHSGSE RMAN@P?TAV@W?", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RQEQGRHSH RMAN@P?TAV@W?", + "G[MUWU RK[RFY[ RJHKHLGLE", + "G[MUWU RK[RFY[ RJEJGKHLH", + "?[MUWU RK[RFY[ RIELH RBHCHDGDE", + "?[MUWU RK[RFY[ RIELH RBEBGCHDH", + "?[MUWU RK[RFY[ RMEJH RBHCHDGDE", + "?[MUWU RK[RFY[ RMEJH RBEBGCHDH", + "D[MUWU RK[RFY[ RFAG@I?MAO@P? RJHKHLGLE", + "D[MUWU RK[RFY[ RFAG@I?MAO@P? RJEJGKHLH", + "IZPTNUMWMXNZP[T[VZ RRTPTNSMQMPNNPMTMVN RQHRHSGSE", /* U+1F10 EPSILON_SMALL PSILI */ + "IZPTNUMWMXNZP[T[VZ RRTPTNSMQMPNNPMTMVN RQEQGRHSH", + "IZPTNUMWMXNZP[T[VZ RRTPTNSMQMPNNPMTMVN RTEWH RMHNHOGOE", + "IZPTNUMWMXNZP[T[VZ RRTPTNSMQMPNNPMTMVN RTEWH RMEMGNHOH", + "IZPTNUMWMXNZP[T[VZ RRTPTNSMQMPNNPMTMVN RXEUH RMHNHOGOE", + "IZPTNUMWMXNZP[T[VZ RRTPTNSMQMPNNPMTMVN RXEUH RMEMGNHOH", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "B[MPTP RW[M[MFWF REHFHGGGE", + "B[MPTP RW[M[MFWF REEEGFHGH", + ":[MPTP RW[M[MFWF RDEGH R=H>H?G?E", + ":[MPTP RW[M[MFWF RDEGH R=E=G>H?H", + ":[MPTP RW[M[MFWF RHEEH R=H>H?G?E", + ":[MPTP RW[M[MFWF RHEEH R=E=G>H?H", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "I\\NMN[ RNOONQMTMVNWPWb RQHRHSGSE", /* U+1F20 ETA_SMALL PSILI */ + "I\\NMN[ RNOONQMTMVNWPWb RQEQGRHSH", + "I\\NMN[ RNOONQMTMVNWPWb RTEWH RMHNHOGOE", + "I\\NMN[ RNOONQMTMVNWPWb RTEWH RMEMGNHOH", + "I\\NMN[ RNOONQMTMVNWPWb RXEUH RMHNHOGOE", + "I\\NMN[ RNOONQMTMVNWPWb RXEUH RMEMGNHOH", + "I\\NMN[ RNOONQMTMVNWPWb RQHRHSGSE RMAN@P?TAV@W?", + "I\\NMN[ RNOONQMTMVNWPWb RQEQGRHSH RMAN@P?TAV@W?", + "A]L[LF RLPXP RX[XF RDHEHFGFE", + "A]L[LF RLPXP RX[XF RDEDGEHFH", + "9]L[LF RLPXP RX[XF RCEFH R<H=H>G>E", + "9]L[LF RLPXP RX[XF RCEFH R<E<G=H>H", + "9]L[LF RLPXP RX[XF RGEDH R<H=H>G>E", + "9]L[LF RLPXP RX[XF RGEDH R<E<G=H>H", + ">]L[LF RLPXP RX[XF R@AA@C?GAI@J? RDHEHFGFE", + ">]L[LF RLPXP RX[XF R@AA@C?GAI@J? RDEDGEHFH", + "MXRMRXSZU[ RQHRHSGSE", /* U+1F30 IOTA_SMALL PSILI */ + "MXRMRXSZU[ RQEQGRHSH", + "MXRMRXSZU[ RTEWH RMHNHOGOE", + "MXRMRXSZU[ RTEWH RMEMGNHOH", + "MXRMRXSZU[ RXEUH RMHNHOGOE", + "MXRMRXSZU[ RXEUH RMEMGNHOH", + "MXRMRXSZU[ RQHRHSGSE RMAN@P?TAV@W?", + "MXRMRXSZU[ RQEQGRHSH RMAN@P?TAV@W?", + "GWR[RF RJHKHLGLE", + "GWR[RF RJEJGKHLH", + "?WR[RF RIELH RBHCHDGDE", + "?WR[RF RIELH RBEBGCHDH", + "?WR[RF RMEJH RBHCHDGDE", + "?WR[RF RMEJH RBEBGCHDH", + "DWR[RF RFAG@I?MAO@P? RJHKHLGLE", + "DWR[RF RFAG@I?MAO@P? RJEJGKHLH", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RQHRHSGSE", /* U+1F40 O_SMALL PSILI */ + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RQEQGRHSH", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RTEWH RMHNHOGOE", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RTEWH RMEMGNHOH", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RXEUH RMHNHOGOE", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RXEUH RMEMGNHOH", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "B]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF REHFHGGGE", + "B]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF REEEGFHGH", + ":]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RDEGH R=H>H?G?E", + ":]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RDEGH R=E=G>H?H", + ":]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RHEEH R=H>H?G?E", + ":]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RHEEH R=E=G>H?H", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "H[MMMXNZP[S[UZVYWWWPVNUM RQHRHSGSE", /* U+1F50 UPSILON_SMALL PSILI */ + "H[MMMXNZP[S[UZVYWWWPVNUM RQEQGRHSH", + "H[MMMXNZP[S[UZVYWWWPVNUM RTEWH RMHNHOGOE", + "H[MMMXNZP[S[UZVYWWWPVNUM RTEWH RMEMGNHOH", + "H[MMMXNZP[S[UZVYWWWPVNUM RXEUH RMHNHOGOE", + "H[MMMXNZP[S[UZVYWWWPVNUM RXEUH RMEMGNHOH", + "H[MMMXNZP[S[UZVYWWWPVNUM RQHRHSGSE RMAN@P?TAV@W?", + "H[MMMXNZP[S[UZVYWWWPVNUM RQEQGRHSH RMAN@P?TAV@W?", + "F^K[KFYFY[K[", + "@[RQR[ RKFRQYF RCECGDHEH", + "F^K[KFYFY[K[", + "8[RQR[ RKFRQYF RBEEH R;E;G<H=H", + "F^K[KFYFY[K[", + "8[RQR[ RKFRQYF RFECH R;E;G<H=H", + "F^K[KFYFY[K[", + "=[RQR[ RKFRQYF R?A@@B?FAH@I? RCECGDHEH", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RQHRHSGSE", /* U+1F60 OMEGA_SMALL PSILI */ + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RQEQGRHSH", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RTEWH RMHNHOGOE", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RTEWH RMEMGNHOH", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RXEUH RMHNHOGOE", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RXEUH RMEMGNHOH", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RQHRHSGSE RMAN@P?TAV@W?", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RQEQGRHSH RMAN@P?TAV@W?", + "@^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[ RCHDHEGEE", + "@^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[ RCECGDHEH", + "8^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[ RBEEH R;H<H=G=E", + "8^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[ RBEEH R;E;G<H=H", + "8^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[ RFECH R;H<H=G=E", + "8^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[ RFECH R;E;G<H=H", + "=^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[ R?A@@B?FAH@I? RCHDHEGEE", + "=^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[ R?A@@B?FAH@I? RCECGDHEH", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RTEQH", /* U+1F70 ALPHA_SMALL ACUTE */ + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RTEQH", + "IZPTNUMWMXNZP[T[VZ RRTPTNSMQMPNNPMTMVN RTEQH", + "IZPTNUMWMXNZP[T[VZ RRTPTNSMQMPNNPMTMVN RTEQH", + "I\\NMN[ RNOONQMTMVNWPWb RTEQH", + "I\\NMN[ RNOONQMTMVNWPWb RTEQH", + "MXRMRXSZU[ RTEQH", + "MXRMRXSZU[ RTEQH", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RTEQH", + "H[P[NZMYLWLQMONNPMSMUNVOWQWWVYUZS[P[ RTEQH", + "H[MMMXNZP[S[UZVYWWWPVNUM RTEQH", + "H[MMMXNZP[S[UZVYWWWPVNUM RTEQH", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RTEQH", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RTEQH", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RQHRHSGSE RR`RcSdTd", /* U+1F80 ALPHA_SMALL PSILI */ + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RQEQGRHSH RR`RcSdTd", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RTEWH RMHNHOGOE RR`RcSdTd", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RTEWH RMEMGNHOH RR`RcSdTd", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RXEUH RMHNHOGOE RR`RcSdTd", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RXEUH RMEMGNHOH RR`RcSdTd", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RQHRHSGSE RMAN@P?TAV@W? RR`RcSdTd", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RQEQGRHSH RMAN@P?TAV@W? RR`RcSdTd", + "G[MUWU RK[RFY[ RJHKHLGLE RR`RcSdTd", + "G[MUWU RK[RFY[ RJEJGKHLH RR`RcSdTd", + "?[MUWU RK[RFY[ RIELH RBHCHDGDE RR`RcSdTd", + "?[MUWU RK[RFY[ RIELH RBEBGCHDH RR`RcSdTd", + "?[MUWU RK[RFY[ RMEJH RBHCHDGDE RR`RcSdTd", + "?[MUWU RK[RFY[ RMEJH RBEBGCHDH RR`RcSdTd", + "D[MUWU RK[RFY[ RFAG@I?MAO@P? RJHKHLGLE RR`RcSdTd", + "D[MUWU RK[RFY[ RFAG@I?MAO@P? RJEJGKHLH RR`RcSdTd", + "I\\NMN[ RNOONQMTMVNWPWb RQHRHSGSE RN`NcOdPd", /* U+1F90 ETA_SMALL PSILI */ + "I\\NMN[ RNOONQMTMVNWPWb RQEQGRHSH RN`NcOdPd", + "I\\NMN[ RNOONQMTMVNWPWb RTEWH RMHNHOGOE RN`NcOdPd", + "I\\NMN[ RNOONQMTMVNWPWb RTEWH RMEMGNHOH RN`NcOdPd", + "I\\NMN[ RNOONQMTMVNWPWb RXEUH RMHNHOGOE RN`NcOdPd", + "I\\NMN[ RNOONQMTMVNWPWb RXEUH RMEMGNHOH RN`NcOdPd", + "I\\NMN[ RNOONQMTMVNWPWb RQHRHSGSE RMAN@P?TAV@W? RN`NcOdPd", + "I\\NMN[ RNOONQMTMVNWPWb RQEQGRHSH RMAN@P?TAV@W? RN`NcOdPd", + "N]L[LF RLPXP RX[XF RR`RcSdTd", + "A]L[LF RLPXP RX[XF RDEDGEHFH RR`RcSdTd", + "9]L[LF RLPXP RX[XF RCEFH R<H=H>G>E RR`RcSdTd", + "9]L[LF RLPXP RX[XF RCEFH R<E<G=H>H RR`RcSdTd", + "9]L[LF RLPXP RX[XF RGEDH R<H=H>G>E RR`RcSdTd", + "9]L[LF RLPXP RX[XF RGEDH R<E<G=H>H RR`RcSdTd", + ">]L[LF RLPXP RX[XF R@AA@C?GAI@J? RDHEHFGFE RR`RcSdTd", + ">]L[LF RLPXP RX[XF R@AA@C?GAI@J? RDEDGEHFH RR`RcSdTd", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RQHRHSGSE RR`RcSdTd", /* U+1FA0 OMEGA_SMALL PSILI */ + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RQEQGRHSH RR`RcSdTd", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RTEWH RMHNHOGOE RR`RcSdTd", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RTEWH RMEMGNHOH RR`RcSdTd", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RXEUH RMHNHOGOE RR`RcSdTd", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RXEUH RMEMGNHOH RR`RcSdTd", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RQHRHSGSE RMAN@P?TAV@W? RR`RcSdTd", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RQEQGRHSH RMAN@P?TAV@W? RR`RcSdTd", + "@^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[ RCHDHEGEE RR`RcSdTd", + "@^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[ RCECGDHEH RR`RcSdTd", + "8^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[ RBEEH R;H<H=G=E RR`RcSdTd", + "8^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[ RBEEH R;E;G<H=H RR`RcSdTd", + "8^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[ RFECH R;H<H=G=E RR`RcSdTd", + "8^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[ RFECH R;E;G<H=H RR`RcSdTd", + "=^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[ R?A@@B?FAH@I? RCHDHEGEE RR`RcSdTd", + "=^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[ R?A@@B?FAH@I? RCECGDHEH RR`RcSdTd", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RNEOGQHSHUGVE", /* U+1FB0 ALPHA_SMALL BREVE */ + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RMGWG", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RPESH RR`RcSdTd", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RR`RcSdTd", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RTEQH RR`RcSdTd", + "F^K[KFYFY[K[", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RMHNGPFTHVGWF", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RMHNGPFTHVGWF RR`RcSdTd", + "I[MUWU RK[RFY[ RN>O@QASAU@V>", + "I[MUWU RK[RFY[ RM@W@", + "G[MUWU RK[RFY[ RIELH", + "G[MUWU RK[RFY[ RMEJH", + "I[MUWU RK[RFY[ RR`RcSdTd", + "NVQHRHSGSE", + "NVR`RcSdTd", + "NVQHRHSGSE", + "KZMHNGPFTHVGWF", /* U+1FC0 TILDE */ + "LXMCNBPATCVBWA RNFOGNHMGNFNH RVFWGVHUGVFVH", + "I\\NMN[ RNOONQMTMVNWPWb RPESH RN`NcOdPd", + "I\\NMN[ RNOONQMTMVNWPWb RN`NcOdPd", + "I\\NMN[ RNOONQMTMVNWPWb RTEQH RN`NcOdPd", + "F^K[KFYFY[K[", + "I\\NMN[ RNOONQMTMVNWPWb RMHNGPFTHVGWF", + "I\\NMN[ RNOONQMTMVNWPWb RMHNGPFTHVGWF RN`NcOdPd", + "B[MPTP RW[M[MFWF RDEGH", + "B[MPTP RW[M[MFWF RHEEH", + "A]L[LF RLPXP RX[XF RCEFH", + "A]L[LF RLPXP RX[XF RGEDH", + "G]L[LF RLPXP RX[XF RR`RcSdTd", + "JZTEWH RMHNHOGOE", + "JZXEUH RMHNHOGOE", + "NVQHRHSGSE RMAN@P?TAV@W?", + "MXRMRXSZU[ RNEOGQHSHUGVE", /* U+1FD0 IOTA_SMALL BREVE */ + "MXRMRXSZU[ RMGWG", + "MXRMRXSZU[ RNFOGNHMGNFNH RVFWGVHUGVFVH RP>SA", + "MXRMRXSZU[ RNFOGNHMGNFNH RVFWGVHUGVFVH RT>QA", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "MXRMRXSZU[ RMHNGPFTHVGWF", + "MXRMRXSZU[ RMCNBPATCVBWA RNFOGNHMGNFNH RVFWGVHUGVFVH", + "MWR[RF RN>O@QASAU@V>", + "MWR[RF RM@W@", + "GWR[RF RIELH", + "GWR[RF RMEJH", + "F^K[KFYFY[K[", + "JZTEWH RMEMGNHOH", + "JZXEUH RMEMGNHOH", + "NVQEQGRHSH RMAN@P?TAV@W?", + "H[MMMXNZP[S[UZVYWWWPVNUM RNEOGQHSHUGVE", /* U+1FE0 UPSILON_SMALL BREVE */ + "H[MMMXNZP[S[UZVYWWWPVNUM RMGWG", + "H[MMMXNZP[S[UZVYWWWPVNUM RNFOGNHMGNFNH RVFWGVHUGVFVH RP>SA", + "H[MMMXNZP[S[UZVYWWWPVNUM RNFOGNHMGNFNH RVFWGVHUGVFVH RT>QA", + "H\\MbMQNOONQMTMVNWOXQXWWYVZT[Q[OZMX RQHRHSGSE", + "H\\MbMQNOONQMTMVNWOXQXWWYVZT[Q[OZMX RQEQGRHSH", + "H[MMMXNZP[S[UZVYWWWPVNUM RMHNGPFTHVGWF", + "H[MMMXNZP[S[UZVYWWWPVNUM RMCNBPATCVBWA RNFOGNHMGNFNH RVFWGVHUGVFVH", + "I[RQR[ RKFRQYF RN>O@QASAU@V>", + "I[RQR[ RKFRQYF RM@W@", + "@[RQR[ RKFRQYF RBEEH", + "@[RQR[ RKFRQYF RFECH", + "A\\L[LFTFVGWHXJXMWOVPTQLQ RDEDGEHFH", + "LXNFOGNHMGNFNH RVFWGVHUGVFVH RP>SA", + "LXNFOGNHMGNFNH RVFWGVHUGVFVH RT>QA", + "NVPESH", + "F^K[KFYFY[K[", /* U+1FF0 */ + "F^K[KFYFY[K[", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RPESH RR`RcSdTd", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RR`RcSdTd", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RTEQH RR`RcSdTd", + "F^K[KFYFY[K[", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RMHNGPFTHVGWF", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RMHNGPFTHVGWF RR`RcSdTd", + "B]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RDEGH", + "B]PFTFVGXIYMYTXXVZT[P[NZLXKTKMLINGPF RHEEH", + "@^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[ RBEEH", + "@^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[ RFECH", + "F^J[O[OWMVKTJQJLKIMGPFTFWGYIZLZQYTWVUWU[Z[ RR`RcSdTd", + "NVTEQH", + "NVQEQGRHSH", + "F^K[KFYFY[K[", + /* // General Punctuation (2000-206F) */ + "F^", /* U+2000 SPACE_EM */ + "LX", + "F^", + "LX", + "NV", + "OU", + "PT", + "H\\", + "MW", + "PT", + "QS", + "RR", + "RR", + "RR", + "RR", + "RR", + "LXOTUT", /* U+2010 HYPHEN */ + "LXOTUT", + "H\\JRZR", + "LXVTNT", + "F^IT[T", + "F^IT[T", + "H\\ODOb RUDUb", + "JZJbZb RJ]Z]", + "MWQGQFRDSC", + "MWSFSGRIQJ", + "MWSZS[R]Q^", + "MWQFQGRISJ", + "JZUGUFVDWC RMGMFNDOC", + "JZOFOGNIMJ RWFWGVIUJ", + "JZOZO[N]M^ RWZW[V]U^", + "JZUFUGVIWJ RMFMGNIOJ", + "I[MMWM RRFRb", /* U+2020 DEI_SMALL */ + "I[M[W[ RMMWM RRFRb", + "E_PQPU RQUQQ RRPRV RSUSQ RTQTU RPTRVTT RPRRPTR RPQRPTQUSTURVPUOSPQ", + "E_PPPV RQQQU RRQRU RSSUS RSRST ROPUSOV RVSOWOOVS", + "MWRYSZR[QZRYR[", + "MaRYSZR[QZRYR[ R\\Y]Z\\[[Z\\Y\\[", + "MkRYSZR[QZRYR[ R\\Y]Z\\[[Z\\Y\\[ RfYgZf[eZfYf[", + "JZRRQSRTSSRRRT", + "RR", + "RR", + "RR", + "RR", + "RR", + "RR", + "RR", + "RR", + "FjJ[ZF RMFOGPIOKMLKKJIKGMF RcUeVfXeZc[aZ`XaVcU RYZZXYVWUUVTXUZW[YZ", /* U+2030 PERMILLE */ + "FvJ[ZF RMFOGPIOKMLKKJIKGMF RcUeVfXeZc[aZ`XaVcU RoUqVrXqZo[mZlXmVoU RYZZXYVWUUVTXUZW[YZ", + "MWTFQL", + "JZQFNL RWFTL", + "G]NFKL RTFQL RZFWL", + "MWPFSL", + "JZSFVL RMFPL", + "G]VFYL RPFSL RJFML", + "LXVcR`Nc", + "KYUMOSUY", + "KYOMUSOY", + "E_LMXY RXMLY RKRLSKTJSKRKT RRYSZR[QZRYR[ RRKSLRMQLRKRM RYRZSYTXSYRYT", + "MaRYSZR[QZRYR[ RRSQGRFSGRSRF R\\Y]Z\\[[Z\\Y\\[ R\\S[G\\F]G\\S\\F", + "I[QFQS RQYRZQ[PZQYQ[ RQYRZQ[PZQYQ[ RMGOFTFVGWIWKVMUNSORPQRQS RMGOFTFVGWIWKVMUNSORPQRQS", + "E_JGZG", + "OUb`aa^c\\dYeTfPfKeHdFcCaB`", + "OUBFCEFCHBKAP@T@YA\\B^CaEbF", /* U+2040 TIE */ + "E_N_VW RV_R[", + "CaKRKW RRFRK RYRYW RFUKWPU RH[KWN[ RMIRKWI ROORKUO RTUYW^U RV[YW\\[", + "LXOTUT", + "G][EI`", + "KYQSVS RVbQbQDVD", + "KYSSNS RNbSbSDND", + "ImQYRZQ[PZQYQ[ RMGOFTFVGWIWKVMUNSORPQRQS RcYdZc[bZcYc[ R_GaFfFhGiIiKhMgNeOdPcRcS", + "IeQYRZQ[PZQYQ[ RMGOFTFVGWIWKVMUNSORPQRQS R`YaZ`[_Z`Y`[ R`S_G`FaG`S`F", + "MiRYSZR[QZRYR[ RRSQGRFSGRSRF R_Y`Z_[^Z_Y_[ R[G]FbFdGeIeKdMcNaO`P_R_S", + "KYNMVMPb", + "G^NMN[ RUMUXVZX[ RJMWMYNZP", + "H\\NQNU RWPWV RPVPPOQOUPV RQPPPNQMSNUPVQVQP", + "H\\VQVU RMPMV RTVTPUQUUTV RSPTPVQWSVUTVSVSP", + "JZR[RV RWXRVMX RURRVOR", + "MWQZQ[R]S^ RRNQORPSORNRP", + "OUBFCEFCHBKAP@T@YA\\B^CaEbF Rb`aa^c\\dYeTfPfKeHdFcCaB`", /* U+2050 TIE *TIE */ + "JZRFRK RMIRKWI ROORKUO RRFRK RWIRKMI RUORKOO", + "JZM^WB RNFOGNHMGNFNH RVYWZV[UZVYV[", + "E_JSKRNQQRSTVUYTZS", + ">fB^B]C[EZOZQYRWSYUZ_Za[b]b^", + "E_JSZS RR[RK RLMXY RXMLY", + "E_LRMSLTKSLRLT RXYYZX[WZXYX[ RXKYLXMWLXKXM", + "D`KFHL RQFNL RWFTL R]FZL", + "E_KRLSKTJSKRKT RRYSZR[QZRYR[ RRKSLRMQLRKRM RYRZSYTXSYRYT", + "E_LXMYLZKYLXLZ RLLMMLNKMLLLN RRRSSRTQSRRRT RXXYYXZWYXXXZ RXLYMXNWMXLXN", + "MWRYSZR[QZRYR[ RRNSORPQORNRP", + "E_KRLSKTJSKRKT RRYSZR[QZRYR[ RRKSLRMQLRKRM RYRZSYTXSYRYT", + "E_JSZS RR[RK RLXMYLZKYLXLZ RLLMMLNKMLLLN RXXYYXZWYXXXZ RXLYMXNWMXLXN", + "CaR\\S]R^Q]R\\R^ RRRSSRTQSRRRT RRHSIRJQIRHRJ", + "CaR^S_R`Q_R^R` RRVSWRXQWRVRX RRNSORPQORNRP RRFSGRHQGRFRH", + "OU", + "RR", /* U+2060 0 */ + "RR", + "RR", + "RR", + "RR", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "RR", + "RR", + "RR", + "RR", + "RR", + "RR", + /* // Subscripts and Superscripts (2070-209F) */ + "JZQ@S@UAVDVJUMSNQNOMNJNDOAQ@", /* U+2070 ^DIGIT_0_SMALL */ + "NVRDRN RR=Q>R?S>R=R?", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "JZUFUN RQ@NJWJ", + "JZV@O@NFPESEUFVHVKUMSNPNNM", + "JZNHOFQESEUFVHVKUMSNQNOMNKNFOCPAR@U@", + "JZM@W@PN", + "JZQFOENCOAQ@S@UAVCUESFQFOGNINKOMQNSNUMVKVIUGSF", + "JZVFUHSIQIOHNFNCOAQ@S@UAVCVHUKTMRNON", + "I[LHXH RRBRN", + "I[LHXH", + "I[LJXJ RLFXF", + "MWT=S>RAQFQJROSRTS", + "MWP=Q>RASFSJROQRPS", + "KZODON ROEQDSDUEVGVN", + "JZQSSSUTVWV]U`SaQaO`N]NWOTQS", /* U+2080 .DIGIT_0_SMALL */ + "JZVaNa RNVPURSRa", + "JZNTPSSSUTVVVXUZNaVa", + "JZNSVSRXSXUYV[V^U`SaPaN`", + "JZUYUa RQSN]W]", + "JZVSOSNYPXSXUYV[V^U`SaPaN`", + "JZN[OYQXSXUYV[V^U`SaQaO`N^NYOVPTRSUS", + "JZMSWSPa", + "JZQYOXNVOTQSSSUTVVUXSYQYOZN\\N^O`QaSaU`V^V\\UZSY", + "JZVYU[S\\Q\\O[NYNVOTQSSSUTVVV[U^T`RaOa", + "I[L[X[ RRURa", + "I[L[X[", + "I[L]X] RLYXY", + "MWTPSQRTQYQ]RbSeTf", + "MWPPQQRTSYS]RbQePf", + "RR", + "KZOXQWSWUXVZVa RV`TaQaO`N^O\\Q[V[", /* U+2090 .A_TINY */ + "LYV`TaRaP`O^OZPXRWSWUXVZV[O\\", + "KYQaO`N^NZOXQWSWUXVZV^U`SaQa", + "KYNWVa RVWNa", + "LYOXQWSWUXVZV^U`SaRaP`O^O]V\\", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Currency Symbols (20A0-20CF) */ + "F[XMPMP[X[ RTGRFNFLGKHJJJPKRLSNTUT", /* U+20A0 ECU */ + "F[WYVZS[Q[NZLXKVJRJOKKLINGQFSFVGWH RSBG_ RZBN_", + "F[WYVZS[Q[NZLXKVJRJOKKLINGQFSFVGWH RR[RM RRQSOTNVMXM", + "HZTPMP RM[MFWF RJVRV", + "H[LMTM RL[W[ RO[OIPGRFUFWG RLSTS", + "D`I[IM RIOJNLMOMQNRPR[ RRPSNUMXMZN[P[[ RWHM`", + "G]L[LFX[XF RHV\\V RHP\\P", + "GyL[LFTFVGWHXJXMWOVPTQLQ R^MfM RaFaXbZd[f[ RlZn[r[tZuXuWtUrToTmSlQlPmNoMrMtN", + "GmX[QQ RL[LFTFVGWHXJXMWOVPTQLQ R`Zb[f[hZiXiWhUfTcTaS`Q`PaNcMfMhN", + "F^IFN[RLV[[F RHV\\V RHP\\P", + "D`I[IFOFRGTIULUR RONOUPXRZU[[[[F", + "I\\W[WF RWZU[Q[OZNYMWMQNOONQMUMWN RRHZH RXaNa", + "F[HSQS RHNTN RWYVZS[Q[NZLXKVJRJOKKLINGQFSFVGWH", + "G\\L[LF RX[OO RXFLR RLOTO", + "JZLFXF RR[RF ROVUR ROPUL", + "IoK[RFY[K[ R`b`QaObNdMgMiNjOkQkWjYiZg[d[bZ`X", + "G]ITJSLRNSOTQUSTXOYLYIXGVFUFSGRIRLSOXTYVYWXYWZT[", /* U+20B0 PENNY_GERMAN */ + "G\\L[LFTFVGWHXJXMWOVPTQLQ RHL\\L", + "F[VGTFQFNGLIKKJOJRKVLXNZQ[S[VZWYWRSR RRCR^", + "I[K[RFY[ RHV\\V RHP\\P", + "H\\XZU[P[NZMYLWLUMSNRPQTPVOWNXLXJWHVGTFOFLG RRCR^", + "HZVZT[P[NZMYLWLQMONNPMTMVN RRJR^", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+20C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Combining Diacritical Marks For Symbols (20D0-20FF) */ + "F^K[KFYFY[K[", /* U+20D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+20E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+20F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Letterlike Symbols (2100-214F) */ + "F^K[KFYFY[K[", /* U+2100 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2110 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2120 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2130 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2140 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Number Forms (2150-218F) */ + "F^K[KFYFY[K[", /* U+2150 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2160 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2170 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2180 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Arrows (2190-21FF) */ + "E_ZSJS RNWJSNO", /* U+2190 !ARROW_E */ + "E_R[RK RNORKVO", + "E_JSZS RVWZSVO", + "E_RKR[ RVWR[NW", + "E_JSZS RVWZSVO RNOJSNW", + "E_R[RK RNORKVO RVWR[NW", + "E_KLYZ RRLKLKS", + "E_YLKZ RRLYLYS", + "E_YZKL RRZYZYS", + "E_KZYL RRZKZKS", + "E_ZSJS RRWVO RNOJSNW", + "E_JSZS RRONW RVWZSVO", + "E_JWJQPQ RJQMTOUQTSRUQWRZU", + "E_ZWZQTQ RZQWTUUSTQROQMRJU", + "E_ZSJS RTOPSTW RNWJSNO", + "E_R[RK RNURQVU RNORKVO", + "E_JSZS RPOTSPW RVWZSVO", /* U+21A0 ARROW_DBL_E */ + "E_RKR[ RVQRUNQ RVWR[NW", + "E_JSVS RZOVSZW RNWJSNO", + "E_ZSNS RJONSJW RVWZSVO", + "E_ZOZW RJSZS RNWJSNO", + "E_R[RK RV[N[ RNORKVO", + "E_JOJW RZSJS RVWZSVO", + "E_RKR[ RNKVK RVWR[NW", + "E_N[V[ RR[RK RNWR[VW RNORKVO", + "E_NWJSNO RJSWSYRZPYNWM", + "E_VWZSVO RZSMSKRJPKNMM", + "E_NWJSNO RJSWSYRZPYNWMUNTPTW", + "E_VWZSVO RZSMSKRJPKNMMONPPPW", + "E_PUJUJO RZWZQTQ RZQWTUUSTQROQMRJU", + "E_JSZS RTOPW RNOJSNW RVWZSVO", + "E_PWR[VY ROKLTVOR[", + "E_V[VOJO RNSJONK", /* U+21B0 !ARROW_E_N */ + "E_N[NOZO RVSZOVK", + "E_VKVWJW RNSJWN[", + "E_NKNWZW RVSZWV[", + "E_JOVOV[ RZWV[RW", + "E_VKVWJW RNSJWN[", + "E_OQKUGQ RYRYQXNVLSKQKNLLNKQKU", + "E_UQYU]Q RKRKQLNNLQKSKVLXNYQYU", + "E_KLYZ RKHYH RRLKLKS", + "E_JWZW RJKJS RZSZ[ RZOJO RNSJONK RV[ZWVS", + "E_[KUKUQ RMMLNKQKSLVNXQYSYVXXVYSYQXNUK", + "E_IKOKOQ RWMXNYQYSXVVXSYQYNXLVKSKQLNOK", + "E_ZSJSNO", + "E_ZSJSNW", + "E_R[RKVO", + "E_R[RKNO", + "E_JSZSVO", /* U+21C0 HARPOON_UP_E */ + "E_JSZSVW", + "E_RKR[VW", + "E_RKR[NW", + "E_ZWJW RJOZO RVSZOVK RN[JWNS", + "E_N[NK RVKV[ RJONKRO RRWV[ZW", + "E_JWZW RZOJO RNSJONK RV[ZWVS", + "E_ZWJW RJOZO RN[JWNSJONK", + "E_N[NK RVKV[ RJONKROVKZO", + "E_JWZW RZOJO RV[ZWVSZOVK", + "E_VKV[ RN[NK RZWV[RWN[JW", + "E_JVZVVZ RZPJPNL", + "E_ZVJVNZ RJPZPVL", + "E_ZPMP RZVMV RRXVN ROXJSON", + "E_MVWV RMPWP RSNQX ROXJSON RUNZSUX", + "E_JVWV RJPWP RRNNX RUNZSUX", + "E_ZPMP RZVMV ROXJSON", /* U+21D0 *ARROW_E_DBL */ + "E_ONO[ RUNU[ RWPRKMP", + "E_JVWV RJPWP RUNZSUX", + "E_UXUK ROXOK RMVR[WV", + "E_MVWV RMPWP ROXJSON RUNZSUX", + "E_OXON RUXUN RMVR[WV RWPRKMP", + "E_[XOL RW\\KP RSLKLKT", + "E_IXUL RM\\YP RQLYLYT", + "E_INUZ RMJYV RQZYZYR", + "E_[NOZ RWJKV RSZKZKR", + "E_ZXOX RZSJS RZNON RQLJSQZ", + "E_JXUX RJSZS RJNUN RSLZSSZ", + "E_NWJSNO RZUWQTUQQNULSJS", + "E_VWZSVO RJUMQPUSQVUXSZS", + "E_NXVX RNSVS RR[RK RNORKVO", + "E_VNNN RVSNS RRKR[ RVWR[NW", + "E_ZSWS RSSQS RMSJS RNOJSNW", /* U+21E0 *ARROW_E_DASHED */ + "E_R[RX RRTRR RRNRK RNORKVO", + "E_JSMS RQSSS RWSZS RVWZSVO", + "E_RKRN RRRRT RRXR[ RVWR[NW", + "E_ZSJS RJWJO RNOJSNW", + "E_JSZS RZOZW RVWZSVO", + "E_ZPZVOVOXJSONOPZP", + "E_U[O[OPMPRKWPUPU[", + "E_JVJPUPUNZSUXUVJV", + "E_OKUKUVWVR[MVOVOK", + "E_U[O[OWUWU[ RUSOSOPMPRKWPUPUS", + "E_W[M[MWOWOPMPRKWPUPUWWWW[", + "E_ONUN RW[M[MWOWOPMPRKWPUPUWWWW[", + "E_RKR[ RW[M[MWOWOPMPRKWPUPUWWWW[", + "E_PPMPRKWPTP RU[O[OSMSRNWSUSU[", + "E_PPMPRKWPTP RW[M[MWOWOSMSRNWSUSUWWWW[", + "E_JNNNNPUPUNZSUXUVNVNXJXJN", /* U+21F0 ARROW_E_WALL_WHITE */ + "E_Z[NO RZKJKJ[ RUONONV", + "E_JKVW RJ[Z[ZK ROWVWVP", + "E_MPRKWPUPUVWVR[MVOVOPMP", + "E_JSZS RVWZSVO RTRTTSVQWOWMVLTLRMPOOQOSPTR", + "E_V[VK RNKN[ RZOVKRO RRWN[JW", + "E_J[Z[ RJKZK RZSJS RVGZKVOZSVWZ[V_", + "E_ZSJS RTWTO RNOJSNW", + "E_JSZS RPOPW RVWZSVO", + "E_JSZS RRORW RNOJSNW RVWZSVO", + "E_ZSJS RWWWO RRWRO RNOJSNW", + "E_JSZS RMOMW RRORW RVWZSVO", + "E_JSZS RPOPW RTOTW RNWJSNO RVWZSVO", + "E_NSZS RNWNOJSNW", + "E_VSJS RVWVOZSVW", + "E_NSVS RNWJSNONW RVWVOZSVW", + /* // Mathematical Operators (2200-22FF) */ + "I[MLWL RKFR[YF", /* U+2200 =A_CAP */ + "HZVHUGSFPFNGMHLKLVMYNZP[S[UZVY", + "H[WOVNTMPMNNMOLQLWMYNZP[S[UZVYWWWJVHUGSFOFMG", + "I\\WPPP RM[W[WFMF", + "I\\WQPQ RMFWFW[M[ RXCL`", + "C`G[\\F ROFTFXHZJ\\N\\SZWXYT[O[KYIWGSGNIJKHOF", + "I[K[RFY[K[", + "I[YFR[KFYF", + "C`\\QGQ R\\GOGKIIKGOGSIWKYO[\\[", + "C`[CH^ R\\QGQ R\\GOGKIIKGOGSIWKYO[\\[", + "E_JSZS RZZPZMYKWJTJRKOMMPLZL", + "DaHP]P RHZUZYX[V]R]N[JYHUFHF", + "DaI^\\C RHP]P RHZUZYX[V]R]N[JYHUFHF", + "E_ZSJS RJZTZWYYWZTZRYOWMTLJL", + "E_M[WQ RMZWP RMYWO RMXWN RMWWM RMVWL RMUWK RMTVK RMSUK RMRTK RMQSK RMPRK RMOQK RMNPK RMMOK RMLNK RN[WR RO[WS RP[WT RQ[WU RR[WV RS[WW RT[WX RU[WY RV[WZ RM[MKWKW[M[", + "E_Z`ZFJFJ`", + "E_ZFZ`J`JF", /* U+2210 ~PI_CAP_HUGE */ + "E_Z`I`TSIF[F", + "E_JSZS", + "E_ZWJW RROR_ RJKZK", + "E_JSZS RR[RK RRDQERFSERDRF", + "G][EI`", + "KYID[_", + "E_KOYW RR[RK RYOKW", + "E_PQRPTQUSTURVPUOSPQ", + "E_PQPU RQUQQ RRPRV RSUSQ RTQTU RPTRVTT RPRRPTR RPQRPTQUSTURVPUOSPQ", + "IbMTQSS[bB", + "IbMTQSS[bB RN@V@RESEUFVHVKUMSNPNNM", + "IbMTQSS[bB RUFUN RQ@NJWJ", + "E_XPWPUQQUOVMULSMQOPQQUUWVXV", + "E_TQVPXQYSXUVVTUPQNPLQKSLUNVPUTQ", + "E_JKJ[Z[", + "E_ZKJ[Z[", /* U+2220 ANGLE */ + "E_ZKJ[Z[ RPSRUTZT]", + "E_Z[JSZK RSYTWUSTOSM", + "H\\RbRD", + "H\\NUVQ RRDRb", + "H\\ODOb RUDUb", + "H\\LVXP RODOb RUDUb", + "E_[[RKI[", + "E_IKR[[K", + "E_Z[ZQXMTKPKLMJQJ[", + "E_JKJULYP[T[XYZUZK", + "H\\L]M_O`Q_R]RISGUFWGXI", + "D`H]I_K`M_N]NIOGQFSGTI RP]Q_S`U_V]VIWGYF[G\\I", + "@dD]E_G`I_J]JIKGMFOGPI RL]M_O`Q_R]RISGUFWGXI RT]U_W`Y_Z]ZI[G]F_G`I", + "H\\L]M_O`Q_R]RISGUFWGXI RRMUNWPXSWVUXRYOXMVLSMPONRM", + "D`H]I_K`M_N]NIOGQFSGTI RP]Q_S`U_V]VIWGYF[G\\I RVMYN[P\\S[VYXVYNYKXIVHSIPKNNMVM", + "@dD]E_G`I_J]JIKGMFOGPI RL]M_O`Q_R]RISGUFWGXI RT]U_W`Y_Z]ZI[G]F_G`I RZM]N_P`S_V]XZYJYGXEVDSEPGNJMZM", /* U+2230 INTEGRAL_TPL_CONTOUR */ + "H\\URXU[R RLSMPONRMUNWPXSXU RL]M_O`Q_R]RISGUFWGXI", + "H\\UQXT[Q RL]M_O`Q_R]RISGUFWGXI RLSMPONRMUNWPXSWVUXRYOXMVLS", + "H\\UUXR[U RL]M_O`Q_R]RISGUFWGXI RLSMPONRMUNWPXSWVUXRYOXMVLS", + "E_KXLYKZJYKXKZ RRLSMRNQMRLRN RYXZYYZXYYXYZ", + "E_YNXMYLZMYNYL RRZQYRXSYRZRX RKNJMKLLMKNKL", + "JZRXSYRZQYRXRZ RRLSMRNQMRLRN", + "E_LXMYLZKYLXLZ RLLMMLNKMLLLN RXXYYXZWYXXXZ RXLYMXNWMXLXN", + "E_JSZS RRFQGRHSGRFRH", + "E_JSTS RYXZYYZXYYXYZ RYLZMYNXMYLYN", + "E_JSZS RLXMYLZKYLXLZ RLLMMLNKMLLLN RXXYYXZWYXXXZ RXLYMXNWMXLXN", + "E_JSKRNQQRSTVUYTZS RRXSYRZQYRXRZ RRLSMRNQMRLRN", + "E_JSKRNQQRSTVUYTZS", + "E_ZSYRVQSRQTNUKTJS", + "E_WPYQZSYUWVTUPQMPKQJSKUMV", + "E_JSKNLLNKPLQNSXTZV[XZYXZS", + "E_RKSLTOSRQTPWQZR[", /* U+2240 WREATH_PRODUCT */ + "E_JSKRNQQRSTVUYTZS RVKN[", + "E_ZPJP RZVYWVXSWQUNTKUJV", + "E_JVZV RJPKONNQOSQVRYQZP", + "E_JVZV RJPKONNQOSQVRYQZP RVKN[", + "E_JYZY RJSZS RJMKLNKQLSNVOYNZM", + "E_JYZY RJSZS RUPO\\ RJMKLNKQLSNVOYNZM", + "E_JYZY RJSZS RJMKLNKQLSNVOYNZM RXGL_", + "E_JVKUNTQUSWVXYWZV RJPKONNQOSQVRYQZP", + "E_JVKUNTQUSWVXYWZV RJPKONNQOSQVRYQZP RVKN[", + "E_JYZY RJSKRNQQRSTVUYTZS RJMKLNKQLSNVOYNZM", + "E_JYKXNWQXSZV[YZZY RJSKRNQQRSTVUYTZS RJMKLNKQLSNVOYNZM", + "E_ZYJY RZSJS RZMYLVKSLQNNOKNJM", + "E_JXLWPVTVXWZX RJNLOPPTPXOZN", + "E_JVNVNWOYQZSZUYVWVVZV RJPNPNOOMQLSLUMVOVPZP", + "E_ZVJV RJPNPNOOMQLSLUMVOVPZP", + "E_JPZP RZVJV RRHQIRJSIRHRJ", /* U+2250 EQUAL DOT */ + "E_JPZP RZVJV RRXSYRZQYRXRZ RRLSMRNQMRLRN", + "E_JPZP RZVJV RKJLKKLJKKJKL RYZZ[Y\\X[YZY\\", + "E_ZPJP RJVZV RYJXKYLZKYJYL RKZJ[K\\L[KZK\\", + "AcNP^P R^VNV RGVHWGXFWGVGX RGNHOGPFOGNGP", + "AcVPFP RFVVV R]V\\W]X^W]V]X R]N\\O]P^O]N]P", + "E_JPZP RZVJV RPQRPTQUSTURVPUOSPQ", + "E_JPZP RZVJV RRJPIOGPERDTEUGTIRJ", + "E_JPZP RZVJV RNJOHQGSGUHVJ", + "E_JPZP RZVJV RNJRGVJ", + "E_JPZP RZVJV RNGRJVG", + "E_JPZP RZVJV RRATGOCUCPGRA", + "E_JPZP RZVJV RR?NJVJR?", + "E_JPZP RYC]C RZVJV R]?[@ZBZJ RM?MJKJIIHGHEICKBMB RQFVFVCUBRBQCQIRJUJ", + "E_JPZP RZVJV RMBMJ RMCNBQBRCRJ RRCSBVBWCWJ", + "E_JPZP RZVJV RRHSIRJQIRHRJ RN@P?S?U@VBUDSE", + "E_JPZP RTMPY RZVJV", /* U+2260 EQUAL_SLASH */ + "E_JYZY RJSZS RJMZM", + "E_JYZY RJSZS RJMZM RXGL_", + "E_J\\Z\\ RJPZP RJJZJ RZVJV", + "E_ZZJZ RZVJPZJ", + "E_JZZZ RJVZPJJ", + "E_J]Z] RZWJW RZSJMZG", + "E_Z]J] RJWZW RJSZMJG", + "E_J]Z] RTTP` RZWJW RZSJMZG", + "E_JWZW RTTP` RZ]J] RJSZMJG", + "=gRMBSRY RbMRSbY", + "=gRMbSRY RBMRSBY", + "I[OCPDRGSITLUQUUTZS]R_PbOc RUcTbR_Q]PZOUOQPLQIRGTDUC", + "E_JXLWPVTVXWZX RJNLOPPTPXOZN RVKN[", + "E_ZMJSZY RVKN[", + "E_JMZSJY RVKN[", + "E_ZZJZ RZVJPZJ RXGL_", /* U+2270 LESS_MINUS SLASH_OP */ + "E_JZZZ RJVZPJJ RXGL_", + "E_ZVJPZJ RJZKYNXQYS[V\\Y[ZZ", + "E_JVZPJJ RJZKYNXQYS[V\\Y[ZZ", + "E_ZVJPZJ RJZKYNXQYS[V\\Y[ZZ RXGL_", + "E_JVZPJJ RJZKYNXQYS[V\\Y[ZZ RXGL_", + "E_JSZYJ_ RZSJMZG", + "E_ZSJYZ_ RJSZMJG", + "E_JSZYJ_ RZSJMZG RXGL_", + "E_ZSJYZ_ RJSZMJG RXGL_", + "E_ZKXNVPRRJSRTVVXXZ[", + "E_JKLNNPRRZSRTNVLXJ[", + "E_JVRWVYX[Z^ RZHXKVMROJPRQVSXUZX", + "E_ZVRWNYL[J^ RJHLKNMROZPRQNSLUJX", + "E_J[KZNYQZS\\V]Y\\Z[ RZHXKVMROJPRQVSXUZX", + "E_J[KZNYQZS\\V]Y\\Z[ RJXLUNSRQZPRONMLKJH", + "E_ZKXNVPRRJSRTVVXXZ[ RVKN[", /* U+2280 PRECEDES SLASH_REL */ + "E_JKLNNPRRZSRTNVLXJ[ RVKN[", + "E_ZMNMLNKOJQJUKWLXNYZY", + "E_JMVMXNYOZQZUYWXXVYJY", + "E_ZMNMLNKOJQJUKWLXNYZY RVKN[", + "E_JMVMXNYOZQZUYWXXVYJY RVKN[", + "E_J\\Z\\ RZJNJLKKLJNJRKTLUNVZV", + "E_Z\\J\\ RJJVJXKYLZNZRYTXUVVJV", + "E_J\\Z\\ RZJNJLKKLJNJRKTLUNVZV RXGL_", + "E_Z\\J\\ RJJVJXKYLZNZRYTXUVVJV RXGL_", + "E_J\\Z\\ RZJNJLKKLJNJRKTLUNVZV RSYQ_", + "E_Z\\J\\ RJJVJXKYLZNZRYTXUVVJV RSYQ_", + "E_JKJULYP[T[XYZUZK ROSUS RSUUSSQ", + "E_JKJULYP[T[XYZUZK RRRQSRTSSRRRT", + "E_JKJULYP[T[XYZUZK RLSXS RRMRY", + "E_ZYJYJMZM", + "E_JYZYZMJM", /* U+2290 !SQIMAGE */ + "E_Z\\J\\ RZVJVJJZJ", + "E_J\\Z\\ RJVZVZJJJ", + "E_Z[ZKJKJ[", + "E_JKJ[Z[ZK", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RLSXS RRMRY", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RLSXS", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RMNWX RWNMX", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RWFM^", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RRRQSRTSSRRRT", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RPQRPTQUSTURVPUOSPQ", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RRNRS RMQRSWQ ROWRSUW", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RLUXU RLQXQ", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RNSVS", + "E_JKZKZ[J[JK RLSXS RRMRY", + "E_JKZKZ[J[JK RLSXS", + "E_JKZKZ[J[JK RMNWX RWNMX", /* U+22A0 SQUARE MULTIPLY_SMALL */ + "E_JKZKZ[J[JK RRRQSRTSSRRRT", + "E_J[JK RJSZS", + "E_Z[ZK RZSJS", + "E_ZKJK RRKR[", + "E_J[Z[ RR[RK", + "I[NSVS RNKN[", + "I[NVVV RNPVP RNKN[", + "E_JVZV RJPZP RJKJ[", + "E_JKJ[ RPSZS RPKP[", + "E_JKJ[ ROKO[ RTKT[ RYSTS", + "E_JKJ[ RPVYV RPPYP RPKP[", + "E_J[JK RJSZS RXGL_", + "E_JVZV RJPZP RJKJ[ RXGL_", + "E_JKJ[ RPSZS RPKP[ RXGL_", + "E_JKJ[ RPVYV RPPYP RPKP[ RXGL_", + "E_VKXLYNXPVQRRJSRTVUXVYXXZV[", /* U+22B0 PRECEDESREL */ + "E_NKLLKNLPNQRRZSRTNULVKXLZN[", + "E_JSZYZMJS", + "E_ZSJYJMZS", + "E_Z[J[ RJQZWZKJQ", + "E_J[Z[ RZQJWJKZQ", + "BbXQXU RYQYU RZPZV R[Q[U R\\Q\\U RMSLQJPHQGSHUJVLUMSWSXUZV\\U]S\\QZPXQWS", + "BbLQLU RKQKU RJPJV RIQIU RHQHU RWSXQZP\\Q]S\\UZVXUWSMSLUJVHUGSHQJPLQMS", + "E_JSTSUUWVYUZSYQWPUQTS", + "E_JSNS RR[RW RRKRO RZSVS", + "I[NFVF RRFR[", + "E_J[Z[ RZKRVJK", + "E_ZKJK RJ[RPZ[", + "E_JKZK RZPR[JP", + "E_JKJ[Z[ RJOLOQQTTVYV[", + "E_Z[ZKJ[Z[", + "Bb_`REE`", /* U+22C0 *OR_HUGE */ + "BbEFRa_F", + "Bb]`]O\\KZHWFSEQEMFJHHKGOG`", + "BbGFGWH[J^M`QaSaW`Z^\\[]W]F", + "E_RaJSRFZSRa", + "JZRRQSRTSSRRRT", + "I[RRTXOTUTPXRR", + "E_ZSJS RRXSYRZQYRXRZ RRLSMRNQMRLRN RLMXY RXMLY", + "E_JKZ[ZKJ[JK", + "E_ZKJ[JKZ[", + "E_JKZ[ZKJ[", + "E_JKZ[ RRSJ[", + "E_ZKJ[ RRSZ[", + "E_ZVJV RZPYOVNSOQQNRKQJP", + "E_JKMMOOQSR[SSUOWMZK", + "E_Z[WYUWSSRKQSOWMYJ[", + "E_ZPSPQQPSQUSVZV RZ\\Q\\N[KXJUJQKNNKQJZJ", /* U+22D0 SUBSET_DBL */ + "E_JPQPSQTSSUQVJV RJ\\S\\V[YXZUZQYNVKSJJJ", + "E_U[UTTRRQPROTO[ R[[[RZOWLTKPKMLJOIRI[", + "E_OKORPTRUTTURUK RIKITJWMZP[T[WZZW[T[K", + "E_RKR[ RL[LSMPNOQNSNVOWPXSX[", + "E_JPZP RZVJV RODOb RUDUb", + "E_ZMJSZY RYRXSYTZSYRYT", + "E_JMZSJY RKRJSKTLSKRKT", + "5oJM:SJY RZMJSZY RjMZSjY", + "5oZMjSZY RJMZSJY R:MJS:Y", + "E_ZSJS RJWZ[J_ RZOJKZG", + "E_JSZS RZWJ[Z_ RJOZKJG", + "E_ZLJL RZPJVZ\\", + "E_JLZL RJPZVJ\\", + "E_JPROVMXKZH RZ^X[VYRWJVRUVSXQZN", + "E_ZPRONMLKJH RJ^L[NYRWZVRUNSLQJN", + "E_JPROVMXKZH RZ^X[VYRWJVRUVSXQZN RXGL_", /* U+22E0 ~PRECEDESEQ SLASH_OP */ + "E_ZPRONMLKJH RJ^L[NYRWZVRUNSLQJN RXGL_", + "E_Z\\J\\ RZVJVJJZJ RXGL_", + "E_J\\Z\\ RJVZVZJJJ RXGL_", + "E_Z\\J\\ RZVJVJJZJ RSYQ_", + "E_J\\Z\\ RJVZVZJJJ RSYQ_", + "E_ZVJPZJ RJZKYNXQYS[V\\Y[ZZ RSWQ]", + "E_JVZPJJ RJZKYNXQYS[V\\Y[ZZ RSWQ]", + "E_J[KZNYQZS\\V]Y\\Z[ RZHXKVMROJPRQVSXUZX RSXQ^", + "E_J[KZNYQZS\\V]Y\\Z[ RJXLUNSRQZPRONMLKJH RSXQ^", + "E_JSZYZMJS RXGL_", + "E_ZSJYJMZS RXGL_", + "E_Z[J[ RJQZWZKJQ RXGL_", + "E_J[Z[ RZQJWJKZQ RXGL_", + "CaR\\S]R^Q]R\\R^ RRRSSRTQSRRRT RRHSIRJQIRHRJ", + "CaHRISHTGSHRHT RRRSSRTQSRRRT R\\R]S\\T[S\\R\\T", + "Ca\\H[I\\J]I\\H\\J RRRQSRTSSRRRT RH\\G]H^I]H\\H^", /* U+22F0 !ELLIPSIS_DIAG */ + "CaHHIIHJGIHHHJ RRRSSRTQSRRRT R\\\\]]\\^[]\\\\\\^", + ">`BQ\\Q R\\GOGKIIKGOGSIWKYO[\\[", + ">`GQ\\Q R\\M\\U R\\GOGKIIKGOGSIWKYO[\\[", + "E_JSZS RZPZV RZZPZMYKWJTJRKOMMPLZL", + "C`\\QGQ R\\GOGKIIKGOGSIWKYO[\\[ RR@QARBSAR@RB", + "C`GA\\A R\\QGQ R\\[O[KYIWGSGOIKKIOG\\G", + "E_JSZS RZGJG RZLPLMMKOJRJTKWMYPZZZ", + "C`G`\\` R\\PGP R\\FOFKHIJGNGRIVKXOZ\\Z", + "C`HT\\T RHN\\N R\\GOGKIIKGOGSIWKYO[\\[", + "DfbQHQ RHGUGYI[K]O]S[WYYU[H[", + "Df]QHQ RHMHU RHGUGYI[K]O]S[WYYU[H[", + "E_ZSJS RJPJV RJZTZWYYWZTZRYOWMTLJL", + "Da]AHA RHQ]Q RH[U[YY[W]S]O[KYIUGHG", + "E_ZSJS RJGZG RJLTLWMYOZRZTYWWYTZJZ", + "C`GQ\\Q R\\GGGG[\\[", + /* // Miscellaneous Technical (2300-23FF) */ + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RZKJ[", /* U+2300 CIRCLE SLASH_SYM */ + "E_JQRWROZU", + "E_J[JORGZOZ[J[", + "E_NORKVO", + "E_VWR[NW", + "E_ZKJK RJ[RPZ[", + "E_JNZN RJHZH RJ[RSZ[", + "H\\RDSETGSIRJQLRNSOTQSSRTQVRXSYT[S]R^Q`Rb", + "KYQbQDVD", + "KYSbSDND", + "KYQDQbVb", + "KYSDSbNb", + "E_RWR[ RVSZS", + "E_RWR[ RNSJS", + "E_RORK RVSZS", + "E_RORK RNSJS", + "E_ZQJQJV", /* U+2310 !NOT */ + "D`[JZLYPYVZZ[\\Y[UZOZK[I\\JZKVKPJLIJKKOLULYK[J", + "E_JSJQLMPKTKXMZQZS", + "E_JSJQLMPKTKXMZQZS RJSZS", + "E_JMLLPKTKXLZMR[JM", + "E_PUJ[ RTKWLYNZQYTWVTWQVOTNQONQLTK", + "E_JSZS RR[RK RVRUPSOQOOPNRNTOVQWSWUVVTVR", + "E_JWZW RJOZO RNKN[ RVKV[", + "E_LPXPZO[MZKXJVKUMUYV[X\\Z[[YZWXVLVJWIYJ[L\\N[OYOMNKLJJKIMJOLP", + "E_ZUJUJP", + "E_RORSUS RPKTKXMZQZUXYT[P[LYJUJQLMPK", + "E_M[RVW[ RN[RWV[ RP[RYT[ RS[RZQ[ RU[RXO[ RYMRPKMROYM RJFZFZKYMKTJVJ[Z[ZVYTKMJJJF", + "JZVFNFNM", + "JZNFVFVM", + "JZV[N[NT", + "JZN[V[VT", + "H\\RbRMSITGVFXGYI", /* U+2320 INTEGRAL_TOP */ + "H\\RDRYQ]P_N`L_K]", + "E_JUKTMSRRWSYTZU", + "E_ZQYRWSRTMSKRJQ", + "E_LKHK RXK\\K RNORKVO", + "@dXK^K RFKLKX[^[", + "AfJKZ[ RZKJ[ RFKZKbSZ[F[FK", + "AcJKZ[ RZKJ[ RFK^K^[F[FK", + "9k>VfV R>LfL RCQCL RD[DV REVEQ RFLFG RHQHL RJVJQ RK[KV RKLKG RMQML ROVOQ RPLPG RRQRL RTVTQ RULUG RWQWL RYVYQ RZ[ZV RZLZG R\\Q\\L R^V^Q R_L_G R`[`V R>QaQaL R>[>GfGf[>[", + "KYUcOSUC", + "KYOcUSOC", + ">cZKJ[ RJKZ[ R^KJKBSJ[^[^K", + "AcKOKW RR[YW RRKYO RRE^L^ZRaFZFLRE", + "H\\PNKX RYNTX RVRUPSOQOOPNRNTOVQWSWUVVTVR", + "E_N[J[JW RZSRSJ[ RVRUPSOQOOPNRNTOVQWSWUVVTVR", + "E_JSZS RNYVY RVMNM", + "E_RPRKNN RZPZKVN RRKJ[R[ZK", /* U+2330 TOTAL_RUNOUT */ + "H\\LS[S RRMRY RXP[SXV RVRUPSOQOOPNRNTOVQWSWUVVTVR", + "E_ZSJ\\JJZS RJSZS", + "E_J[JRZ[J[", + "E_JWJ[Z[ZW", + "E_VWR[NW", + "D`JaZa RJFZF RRFRa", + "D`MFWFWaMaMF", + "D`IF[F[aIaIF RJPZP RZVJV", + "D`IF[F[aIaIF RZSJS RRXSYRZQYRXRZ RRLSMRNQMRLRN", + "D`IF[F[aIaIF RRJ[SR\\ISRJ", + "D`IF[F[aIaIF RPQRPTQUSTURVPUOSPQ", + "D`IF[F[aIaIF RPKTKXMZQZUXYT[P[LYJUJQLMPK", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RRbRD", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RPQRPTQUSTURVPUOSPQ", + "E_JSZS RZKJ[", + "E_JSZS RJKZ[", /* U+2340 MINUS !SLASH_SYM */ + "D`IaIF[F[aIa[F", + "D`[a[FIFIa[aIF", + "D`IF[F[aIaIF RZMJSZY", + "D`IF[F[aIaIF RJMZSJY", + "E_ZSJS RNWJSNO RR[RK", + "E_JSZS RVWZSVO RR[RK", + "D`IF[F[aIaIF RZSJS RNWJSNO", + "D`IF[F[aIaIF RJSZS RVWZSVO", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RLGX_", + "E_J[Z[ RR[RK RZaJa", + "E_RKX[L[RK RRbRD", + "D`IF[F[aIaIF RIKR[[K", + "D`IF[F[aIaIF RRKX[L[RK", + "E_ZKJK RRKR[ RVRUPSOQOOPNRNTOVQWSWUVVTVR", + "E_R[RK RNORKVO RJSZS", + "D`IF[F[aIaIF RR[RK RNORKVO", /* U+2350 RECTANGLE_V_HUGE ARROW_N */ + "E_ZKJK RRKR[ RMEWE", + "E_R[LKXKR[ RRbRD", + "D`IF[F[aIaIF R[[RKI[", + "D`IF[F[aIaIF RR[LKXKR[", + "E_J[Z[ RR[RK RPQRPTQUSTURVPUOSPQ", + "E_RKR[ RVWR[NW RJSZS", + "D`IF[F[aIaIF RRKR[ RVWR[NW", + "JZJ]Z] RSFQJ", + "E_RKX[L[RK RJ]Z]", + "E_RJ[SR\\ISRJ RJ]Z]", + "E_PQRPTQUSTURVPUOSPQ RJ]Z]", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RJ]Z]", + "E_Z[ZQXMTKPKLMJQJ[ RPQRPTQUSTURVPUOSPQ", + "D`IF[F[aIaIF RSFQJ", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RRPTVORURPVRP", + "D`IF[F[aIaIF RRYSZR[QZRYR[ RRNSORPQORNRP", /* U+2360 RECTANGLE_V_HUGE COLON */ + "E_ZKJK RRKR[ RNDOENFMENDNF RVDWEVFUEVDVF", + "E_R[LKXKR[ RNFOGNHMGNFNH RVFWGVHUGVFVH", + "E_RKWZJQZQMZRK RNDOENFMENDNF RVDWEVFUEVDVF", + "E_PQRPTQUSTURVPUOSPQ RNIOJNKMJNINK RVIWJVKUJVIVK", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RNDOENFMENDNF RVDWEVFUEVDVF", + "E_JKJULYP[T[XYZUZK RRbRD", + "E_ZMNMLNKOJQJUKWLXNYZY RRbRD", + "E_JSKRNQQRSTVUYTZS RNFOGNHMGNFNH RVFWGVHUGVFVH", + "E_JMZSJY RNFOGNHMGNFNH RVFWGVHUGVFVH", + "E_JSZS RSZS[R]Q^", + "E_R[LKXKR[ RJSKRNQQRSTVUYTZS", + "H\\QFSFUGVHWJXNXSWWVYUZS[Q[OZNYMWLSLNMJNHOGQF RJPKONNQOSQVRYQZP", + "E_JSKRNQQRSTVUYTZS RRbRD", + "MWSZS[R]Q^ RRNSORPQORNRP RJ]Z]", + "D`IF[F[aIaIF RJPZP RTMPY RZVJV", + "D`IF[F[aIaIF RQYRZQ[PZQYQ[ RMGOFTFVGWIWKVMUNSORPQRQS", /* U+2370 RECTANGLE_V_HUGE QUESTION */ + "E_IKR[[K RJSKRNQQRSTVUYTZS", + "E_[[RKI[ RJSKRNQQRSTVUYTZS", + "MXRMRXSZU[", + "H\\MbMQNOONQMTMVNWOXQXWWYVZT[Q[OZMX", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[ RJ]Z]", + "HZLTST RVZT[P[NZMYLWLQMONNPMTMVN RJ]Z]", + "MXRMRXSZU[ RJ]Z]", + "G]RTRX RMMLNKPKXLZN[O[QZRXSZU[V[XZYXYPXNWM RJ]Z]", + "H]YMVWUYTZR[P[NZMYLVLRMONNPMRMTNUOVQWXXZZ[", + "IbMTQSS[bB RXL`L", + "A_J_F_F[ RJKJ[Z[ RF_OVEQOG", + "E_JWNWN[V[VWZW", + "E_NSN[J[ RVSV[Z[ RJSJQLMPKTKXMZQZSJS", + "E_PQPU RQUQQ RRPRV RSUSQ RTQTU RPTRVTT RPRRPTR RPQRPTQUSTURVPUOSPQ RRbRD", + "E_VWR[NW ROEQDSDUEVGVN RVMTNQNOMNKOIQHVH", /* U+2380 *ARROWHEAD_N ^A_TINY */ + "BbF[^[ RGLIKKKMLNNNU RUSVTUUTTUSUU R]S^T]U\\T]S]U RNTLUIUGTFRGPIONO", + "BbF[N[ RV[^[ RGLIKKKMLNNNU RWLYK[K]L^N^U RNTLUIUGTFRGPIONO R^T\\UYUWTVRWPYO^O", + "BbHPDP RJUFX RJKFH R^XZU R^HZK R`P\\P RTTRUPUNTMRMQNNPLRKVKTU", + "=_RKR[B[BKRK RPKTKXMZQZUXYT[P[LYJUJQLMPK", + "E_JKZKZ[J[JK RRbRD", + "C_ESUS RQWUSQO RJWJ[Z[ZKJKJO", + "@dX[^[ RZO^KZG RF[L[XK^K", + "E_KOYW RR[RK RYOKW RRMONMPLSMVOXRYUXWVXSWPUNRM", + "E_JSOSR[USZS RPKTKXMZQZUXYT[P[LYJUJQLMPK", + "E_R[KOYOR[ RPKTKXMZQZUXYT[P[LYJUJQLMPK", + "E_STJK RJOJKNK RSKTKXMZQZUXYT[P[LYJUJT", + "D`KNKROR RYRWPTOPOMPKR RNXMVKUIVHXIZK[MZNX RVXWZY[[Z\\X[VYUWVVX", + "E_I[N[NKVKV[[[", + "E_I[V[VK RN[NK[K", + "E_JKZK RJSRKZSR[JS", + "E_Z[J[ RZSR[JSRKZS", /* U+2390 *OPEN_H */ + "E_JKZK RJSRKZSR[JS RJSZS", + "E_Z[J[ RZSR[JSRKZS RJSZS", + "E_JVLV RJPZP RQVSV RXVZV", + "BbL[FQLGXG^QX[L[", + "D`IF[F[aIaIF", + "MWTFQL", + "AcZSJS RRORK RR[RW RNOJSNW R^[F[FK^K^[", + "AcJSZS RRWR[ RRKRO RVWZSVO RFK^K^[F[FK", + "BbLHQHQC RLSLHQCXCXSLS RLKJKHLGNGXHZJ[Z[\\Z]X]N\\LZKXK", + "BbROJW RZORW RGXGNHLJKZK\\L]N]X\\ZZ[J[HZGX", + "H\\XDVGUITLSQR[Rb", + "H\\RbRD", + "H\\XbV_U]TZSURKRD", + "H\\LDNGOIPLQQR[Rb", + "H\\RbRD", + "H\\LbN_O]PZQURKRD", /* U+23A0 *PAREN_TOP */ + "H\\XGRGRb", + "H\\RbRD", + "H\\X_R_RD", + "H\\LGRGRb", + "H\\RbRD", + "H\\L_R_RD", + "H\\XDTHSJRNRb", + "H\\RDRIQMPOLSPWQYR]Rb", + "H\\XbT^S\\RXRD", + "H\\RbRD", + "H\\LDPHQJRNRb", + "H\\RDRISMTOXSTWSYR]Rb", + "H\\LbP^Q\\RXRD", + "H\\RbRD", + "H\\HS\\S", + "H\\WDSHRKR[Q^Mb", /* U+23B0 MOUSTACHE */ + "H\\MDQHRKR[S^Wb", + "E_VbIF\\F", + "E_VDI`\\`", + ">fC^CYaYa^", + ">fCHCMaMaH", + ">fC^CYaYa^ RaHaMCMCH", + "IbMTQSS[bB", + "H\\RbRD", + "H\\RbRD", + "H\\HG\\G", + "H\\HM\\M", + "H\\\\YHY", + "H\\\\_H_", + "E_UFOFO[", + "E_U[O[OF", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RRbRD", /* U+23C0 CIRCLE LINE_V */ + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RZEJE RRERa", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RJaZa RRaRE", + "E_RK[[I[RK RRbRD", + "E_RK[[I[RK RZEJE RRERa", + "E_RK[[I[RK RJaZa RRaRE", + "E_JSKRNQQRSTVUYTZS RRbRD", + "E_JSKRNQQRSTVUYTZS RZEJE RRERa", + "E_JSKRNQQRSTVUYTZS RJaZa RRaRE", + "E_JaZa RRaRE", + "E_ZEJE RRERa", + "E_OFUFU[", + "E_O[U[UF", + "D`TFQL RMKJKJ[Z[ZKWK", + "E_IWN\\NZZZZKTKTTNTNRIW", + "E_Z[J[ RJVRKZV", + "H\\RbRD", /* U+23D0 LINE_V */ + "H\\NQNROTQUSUUTVRVQ", + "H\\NQNROTQUSUUTVRVQ RMKWK", + "H\\NQNROTQUSUUTVRVQ RW[M[", + "CaGQGRHTJULUNTOROQ RUQURVTXUZU\\T]R]Q RGK]K", + "CaGQGRHTJULUNTOROQ RUQURVTXUZU\\T]R]Q R][G[", + "E_JQJRKTMUOUQTRRRQ RRRSTUUWUYTZRZQ", + "E_JUZUZP", + "E_JPJUZUZP", + "E_RPRU RJPJUZUZP", + "E_HO\\O RLUXU RRFRO RT[P[", + "E_HS\\S RJMZMZYJYJM", + ">fB]C\\FZHYKXPWTWYX\\Y^Za\\b]", + ">fbIaJ^L\\MYNTOPOKNHMFLCJBI", + ">fB^B]C[EZOZQYRWSYUZ_Za[b]b^", + ">fbHbIaK_LULSMROQMOLELCKBIBH", + ">fB^FY^Yb^", /* U+23E0 TOR_BRACKET_OVER */ + ">fbH^MFMBH", + "E_I[NKVK[[I[", + "AcRE^L^ZRaFZFLRE RQLSLVMXOYRYTXWVYSZQZNYLWKTKRLONMQL", + "E_JSZS", + "E_HXMN\\NWXHX", + "E_JSZS RJSKNLLNKPLQNSXTZV[XZYXZS", + "E_LMXY RXMLY RPQRPTQUSTURVPUOSPQ", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+23F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Control Pictures (2400-243F) */ + "F^K[KFYFY[K[", /* U+2400 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2410 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2420 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2430 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Optical Character Recognition (2440-245F) */ + "F^K[KFYFY[K[", /* U+2440 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2450 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Enclosed Alphanumerics (2460-24FF) */ + "F^K[KFYFY[K[", /* U+2460 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2470 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2480 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2490 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+24A0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+24B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+24C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+24D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+24E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+24F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Box Drawing (2500-257F) */ + "F^K[KFYFY[K[", /* U+2500 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2510 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2520 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2530 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2540 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2550 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2560 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2570 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Block Elements (2580-259F) */ + "F^K[KFYFY[K[", /* U+2580 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2590 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Geometric Shapes (25A0-25FF) */ + "E_KKK[ RL[LK RMKM[ RN[NK ROKO[ RP[PK RQKQ[ RR[RK RSKS[ RT[TK RUKU[ RV[VK RWKW[ RX[XK RYKY[ RJKZKZ[J[JK", /* U+25A0 SQUARE_BLACK */ + "E_JKZKZ[J[JK", + "E_KLMKWKYLZNZXYZW[M[KZJXJNKL", + "E_JKZKZ[J[JK RPPPV RQVQP RRPRV RSVSP RTPTV ROVOPUPUVOV", + "E_JWZW RJSZS RJOZO RJKZKZ[J[JK", + "E_NKN[ RRKR[ RVKV[ RJKZKZ[J[JK", + "E_JWZW RJSZS RJOZO RNKN[ RRKR[ RVKV[ RJKZKZ[J[JK", + "E_JKZ[ RN[JW RT[JQ RZUPK RZOVK RJKZKZ[J[JK", + "E_J[ZK RJUTK RJONK RP[ZQ RV[ZW RJKZKZ[J[JK", + "E_J[ZK RJUTK RJONK RJKZ[ RN[JW RP[ZQ RT[JQ RV[ZW RZUPK RZOVK RJKZKZ[J[JK", + "E_PPPV RQVQP RRPRV RSVSP RTPTV ROVOPUPUVOV", + "E_OVOPUPUVOV", + "E_JXTN RJWSN RJVRN RJUQN RJTPN RJSON RJRNN RJQMN RJPLN RJOKN RKXUN RLXVN RMXWN RNXXN ROXYN RPXZN RQXZO RRXZP RSXZQ RTXZR RUXZS RVXZT RWXZU RXXZV RYXZW RJNZNZXJXJN", + "E_JNZNZXJXJN", + "E_M[WQ RMZWP RMYWO RMXWN RMWWM RMVWL RMUWK RMTVK RMSUK RMRTK RMQSK RMPRK RMOQK RMNPK RMMOK RMLNK RN[WR RO[WS RP[WT RQ[WU RR[WV RS[WW RT[WX RU[WY RV[WZ RM[MKWKW[M[", + "E_M[MKWKW[M[", + "E_NNLP RONKR RPNJT RQNIV RRNHX RSNIX RTNJX RUNKX RVNLX RWNMX RXVVX RXNNX RYTUX RYNOX RZRTX RZNPX R[PSX R[NQX R\\NRX RHXMN\\NWXHX", /* U+25B0 PARALLELOGRAM_BLACK */ + "E_HXMN\\NWXHX", + "E_JZJ[ RKXK[ RLVL[ RMTM[ RNSN[ ROQO[ RPOP[ RQMQ[ RRKR[ RSMS[ RTOT[ RUQU[ RVSV[ RWTW[ RXVX[ RYXY[ RZ[RLJ[ RZZZ[ RRK[[I[RK", + "E_RK[[I[RK", + "E_OUOV RPSPV RQQQV RRORV RSQSV RTSTV RUUUV ROVRPUV RROVVNVRO", + "E_ROVVNVRO", + "E_KKK[ RLLLZ RMLMZ RNMNY ROMOY RPNPX RQNQX RRORW RSPSV RTPTV RUQUU RVQVU RWSXS RWRWT RJKYSJ[ RZSJ\\JJZS", + "E_ZSJ\\JJZS", + "E_PPPV RQQQU RRQRU RSSUS RSRST ROPUSOV RVSOWOOVS", + "E_VSOWOOVS", + "E_KNKX RLNLX RMOMW RNONW ROOOW RPPPV RQPQV RRPRV RSQSU RTQTU RURUT RVRVT RWRWT RXSWS RJNYSJX RZSJYJMZS", + "E_ZSJYJMZS", + "E_ZLZK RYNYK RXPXK RWRWK RVSVK RUUUK RTWTK RSYSK RR[RK RQYQK RPWPK ROUOK RNSNK RMRMK RLPLK RKNKK RJKRZZK RJLJK RR[IK[KR[", + "E_R[IK[KR[", + "E_UQUP RTSTP RSUSP RRWRP RQUQP RPSPP ROQOP RUPRVOP RRWNPVPRW", + "E_RWNPVPRW", + "E_Y[YK RXZXL RWZWL RVYVM RUYUM RTXTN RSXSN RRWRO RQVQP RPVPP ROUOQ RNUNQ RMSLS RMTMR RZ[KSZK RJSZJZ\\JS", /* U+25C0 *TRIANGLE_E_BLACK */ + "E_JSZJZ\\JS", + "E_TVTP RSUSQ RRURQ RQSOS RQTQR RUVOSUP RNSUOUWNS", + "E_NSUOUWNS", + "E_YXYN RXXXN RWWWO RVWVO RUWUO RTVTP RSVSP RRVRP RQUQQ RPUPQ ROTOR RNTNR RMTMR RLSMS RZXKSZN RJSZMZYJS", + "E_JSZMZYJS", + "E_JRJT RKUKQ RLPLV RMWMO RNNNX ROYOM RPLPZ RQ[QK RRJR\\ RS[SK RTLTZ RUYUM RVNVX RWWWO RXPXV RYUYQ RZRZT RRJ[SR\\ISRJ", + "E_RJ[SR\\ISRJ", + "E_RJ[SR\\ISRJ RPRPT RQUQQ RRPRV RSUSQ RTRTT RRPUSRVOSRP", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RPQPU RQUQQ RRPRV RSUSQ RTQTU RPTRVTT RPRRPTR RPQRPTQUSTURVPUOSPQ", + "E_RaJSRFZSRa", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK", + "E_JQKO RKWJU RNLPK RP[NZ RTKVL RVZT[ RYOZQ RZUYW", + "E_NLNZ RRKR[ RVLVZ RPKTKXMZQZUXYT[P[LYJUJQLMPK", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RPQRPTQUSTURVPUOSPQ", + "E_KOKW RLXP[ RLNPK RLMLY RMYMM RNLNZ ROZOL RPKP[ RQ[QK RRKR[ RS[SK RT[XX RTKT[ RTKXN RUZUL RVLVZ RWYWM RXMXY RYWYO RPKTKXMZQZUXYT[P[LYJUJQLMPK", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RKOKW RLYLM RMMMY RNZNL ROLOZ RP[LX RP[PK RLN RQKQ[ RR[P[LYJUJQLMPKRKR[", /* U+25D0 CIRCLE CIRCLE_LEFTHALF_BLACK */ + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RYWYO RXMXY RWYWM RVLVZ RUZUL RTKXN RTKT[ RXX RS[SK RRKTKXMZQZUXYT[R[RK", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RKOKS RLMLS RMSMM RNLNS ROSOL RPKLN RPKPS RQKQS RRKRS RSKSS RTSTK RXN RULUS RVSVL RWMWS RXMXS RYOYS RJSJQLMPKTKXMZQZSJS", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RYWYS RXYXS RWSWY RVZVS RUSUZ RT[XX RT[TS RS[SS RR[RS RQ[QS RPSP[ RLX ROZOS RNSNZ RMYMS RLYLS RKWKS RZSZUXYT[P[LYJUJSZS", + "E_SSSK RTKTS RTKXN RUSUL RVLVS RWSWM RXMXS RYSYO RZSRSRK RPKTKXMZQZUXYT[P[LYJUJQLMPK", + "E_QSQ[ RP[PS RP[LX ROSOZ RNZNS RMSMY RLYLS RKSKW RJSRSR[ RT[P[LYJUJQLMPKTKXMZQZUXYT[ RYWYO RXMXY RWYWM RVLVZ RUZUL RTKXN RTKT[ RXX RS[SK RRKTKXMZQZUXYT[R[RK", + "E_KOKW RLYLM RMMMY RNZNL ROLOZ RP[LX RP[PK RLN RQKQ[ RR[P[LYJUJQLMPKRKR[", + "E_YWYO RXMXY RWYWM RVLVZ RUZUL RTKXN RTKT[ RXX RS[SK RRKTKXMZQZUXYT[R[RK", + "E_FDFb RGbGD RHDHb RIbID RJDJb RKbKD RLbLW RLDLO RMXMb RMNMD RNbNY RNDNM ROZOb ROLOD RPbPZ RPDPL RQZQb RQLQD RRbRZ RRDRL RSZSb RSLSD RTbTZ RTDTL RUZUb RULUD RVbVY RVDVM RWXWb RWNWD RXbXW RXDXO RYbYD RZDZb R[b[D R\\D\\b R]b]D R^D^b R_bEbED_D_b RKTKRLONMQLSLVMXOYRYTXWVYSZQZNYLWKT", + "E_FRFD RGNIJ RGDGN RHLHD RIDIK RJJJD RJJMG RKDKI RLHLD RMHQF RMDMH RNGND ROPOS RODOG RPSPP RPGPD RQPQS RQDQG RRSRO RRGRD RSPSS RSFWH RSDSG RTSTP RTGTD RUPUS RUDUG RVGVD RWGZJ RWDWH RXHXD RYDYI RZJZD R[J]N R[D[K R\\L\\D R]D]N R^R^D ROQROUQ RNSOPROUPVSNS RFSFRGNIKJJMHQGSGWHZJ[K]N^R^S_S_DEDESFS R^T^b R]X[\\ R]b]X R\\Z\\b R[b[[ RZ\\Zb RZ\\W_ RYbY] RX^Xb RW^S` RWbW^ RV_Vb RUVUS RUbU_ RTSTV RT_Tb RSVSS RSbS_ RRSRW RR_Rb RQVQS RQ`M^ RQbQ_ RPSPV RP_Pb ROVOS RObO_ RN_Nb RM_J\\ RMbM^ RL^Lb RKbK] RJ\\Jb RI\\GX RIbI[ RHZHb RGbGX RFTFb RUURWOU RVSUVRWOVNSVS R^S^T]X[[Z\\W^S_Q_M^J\\I[GXFTFSESEb_b_S^S", + "E_FRFD RGNIJ RGDGN RHLHD RIDIK RJJJD RJJMG RKDKI RLHLD RMHQF RMDMH RNGND ROPOS RODOG RPSPP RPGPD RQPQS RQDQG RRSRO RRGRD RSPSS RSFWH RSDSG RTSTP RTGTD RUPUS RUDUG RVGVD RWGZJ RWDWH RXHXD RYDYI RZJZD R[J]N R[D[K R\\L\\D R]D]N R^R^D ROQROUQ RNSOPROUPVSNS RFSFRGNIKJJMHQGSGWHZJ[K]N^R^S_S_DEDESFS", + "E_^T^b R]X[\\ R]b]X R\\Z\\b R[b[[ RZ\\Zb RZ\\W_ RYbY] RX^Xb RW^S` RWbW^ RV_Vb RUVUS RUbU_ RTSTV RT_Tb RSVSS RSbS_ RRSRW RR_Rb RQVQS RQ`M^ RQbQ_ RPSPV RP_Pb ROVOS RObO_ RN_Nb RM_J\\ RMbM^ RL^Lb RKbK] RJ\\Jb RI\\GX RIbI[ RHZHb RGbGX RFTFb RUURWOU RVSUVRWOVNSVS R^S^T]X[[Z\\W^S_Q_M^J\\I[GXFTFSESEb_b_S^S", + "E_JSJQLMPKRK", + "E_ZSZQXMTKRK", + "E_ZSZUXYT[R[", + "E_JSJULYP[R[", + "E_JSJQLMPKTKXMZQZS", /* U+25E0 CIRCLE_TOPHALF */ + "E_ZSZUXYT[P[LYJUJS", + "E_KZK[ RLYL[ RMXM[ RNWN[ ROVO[ RPUP[ RQTQ[ RRSR[ RSRS[ RTQT[ RUPU[ RVOV[ RWNW[ RXMX[ RYLY[ RZ[ZKJ[Z[", + "E_YZY[ RXYX[ RWXW[ RVWV[ RUVU[ RTUT[ RSTS[ RRSR[ RQRQ[ RPQP[ ROPO[ RNON[ RMNM[ RLML[ RKLK[ RJ[JKZ[J[", + "E_YLYK RXMXK RWNWK RVOVK RUPUK RTQTK RSRSK RRSRK RQTQK RPUPK ROVOK RNWNK RMXMK RLYLK RKZKK RJKJ[ZKJK", + "E_KLKK RLMLK RMNMK RNONK ROPOK RPQPK RQRQK RRSRK RSTSK RTUTK RUVUK RVWVK RWXWK RXYXK RYZYK RZKZ[JKZK", + "E_PQRPTQUSTURVPUOSPQ", + "E_JKZKZ[J[JK RK[KK RLKL[ RM[MK RNKN[ RO[OK RPKP[ RQ[QK RJ[JKRKR[J[", + "E_JKZKZ[J[JK RYKY[ RX[XK RWKW[ RV[VK RUKU[ RT[TK RSKS[ RZKZ[R[RKZK", + "E_JKZKZ[J[JK RYLYK RXMXK RWNWK RVOVK RUPUK RTQTK RSRSK RRSRK RQTQK RPUPK ROVOK RNWNK RMXMK RLYLK RKZKK RJKJ[ZKJK", + "E_JKZKZ[J[JK RKZK[ RLYL[ RMXM[ RNWN[ ROVO[ RPUP[ RQTQ[ RRSR[ RSRS[ RTQT[ RUPU[ RVOV[ RWNW[ RXMX[ RYLY[ RZ[ZKJ[Z[", + "E_JKZKZ[J[JK RR[RK", + "E_RK[[I[RK RRUQVRWSVRURW", + "E_J[RL RJZJ[ RKXK[ RLVL[ RMTM[ RNSN[ ROQO[ RPOP[ RQMQ[ RRKR[ RRK[[I[RK", + "E_Z[RL RZZZ[ RYXY[ RXVX[ RWTW[ RVSV[ RUQU[ RTOT[ RSMS[ RRKR[ RRKI[[[RK", + "C`OFTFXHZJ\\N\\SZWXYT[O[KYIWGSGNIJKHOF", + "E_JKZKZ[J[JK RRKRSJS", /* U+25F0 SQUARE !QUADRANT */ + "E_JKZKZ[J[JK RR[RSJS", + "E_JKZKZ[J[JK RR[RSZS", + "E_JKZKZ[J[JK RRKRSZS", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RRKRSJS", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RR[RSJS", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RR[RSZS", + "E_PKTKXMZQZUXYT[P[LYJUJQLMPK RRKRSZS", + "E_JKJ[ZKJK", + "E_ZKZ[JKZK", + "E_J[JKZ[J[", + "E_JKZKZ[J[JK", + "E_KKK[ RL[LK RMKM[ RN[NK ROKO[ RP[PK RQKQ[ RR[RK RSKS[ RT[TK RUKU[ RV[VK RWKW[ RX[XK RYKY[ RJKZKZ[J[JK", + "E_OVOPUPUVOV", + "E_PPPV RQVQP RRPRV RSVSP RTPTV ROVOPUPUVOV", + "E_Z[ZKJ[Z[", + /* // Miscellaneous Symbols (2600-26FF) */ + "F^K[KFYFY[K[", /* U+2600 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2610 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2620 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2630 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2640 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2650 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2660 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2670 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2680 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2690 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+26A0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+26B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+26C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+26D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+26E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+26F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Dingbats (2700-27BF) */ + "F^K[KFYFY[K[", /* U+2700 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2710 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2720 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2730 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2740 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2750 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2760 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2770 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2780 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2790 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+27A0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+27B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Miscellaneous Mathematical Symbols A (27C0-27EF) */ + "F^K[KFYFY[K[", /* U+27C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+27D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+27E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Supplemental Arrows A (27F0-27FF) */ + "F^K[KFYFY[K[", /* U+27F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Braille Patterns (2800-28FF) */ + "F^K[KFYFY[K[", /* U+2800 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2810 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2820 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2830 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2840 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2850 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2860 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2870 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2880 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2890 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+28A0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+28B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+28C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+28D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+28E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+28F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Supplemental Arrows B (2900-297F) */ + "F^K[KFYFY[K[", /* U+2900 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2910 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2920 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2930 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2940 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2950 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2960 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2970 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Miscellaneous Mathematical Symbols B (2980-29FF) */ + "F^K[KFYFY[K[", /* U+2980 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2990 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+29A0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+29B0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+29C0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+29D0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+29E0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+29F0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Supplemental Mathematical Operators (2A00-2AFF) */ + "F^K[KFYFY[K[", /* U+2A00 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2A10 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2A20 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2A30 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2A40 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2A50 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2A60 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2A70 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2A80 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2A90 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2AA0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2AB0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2AC0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2AD0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2AE0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2AF0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + /* // Miscellaneous Symbols and Arrows (2B00-2BFF) */ + "F^K[KFYFY[K[", /* U+2B00 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2B10 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2B20 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2B30 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2B40 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2B50 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2B60 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2B70 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2B80 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2B90 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2BA0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2BB0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2BC0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2BD0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2BE0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", /* U+2BF0 */ + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", + "F^K[KFYFY[K[", +}; + +const int newstroke_font_bufsize = sizeof(newstroke_font)/sizeof(newstroke_font[0]); + +/* --- unused glyphs --- */ +/* RECTANGLE_V_BLACK_SMALL */ +/* TRIANGLE_TALL_BLACK */ +/* COMBINING */ +/* DIAMOND_SMALL */ diff --git a/common/origin_viewitem.cpp b/common/origin_viewitem.cpp new file mode 100644 index 0000000..6454816 --- /dev/null +++ b/common/origin_viewitem.cpp @@ -0,0 +1,88 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 CERN + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 <origin_viewitem.h> +#include <gal/graphics_abstraction_layer.h> +#include <class_track.h> + +using namespace KIGFX; + +ORIGIN_VIEWITEM::ORIGIN_VIEWITEM( const COLOR4D& aColor, MARKER_STYLE aStyle, int aSize, const VECTOR2D& aPosition ) : + EDA_ITEM( NOT_USED ), // this item is never added to a BOARD so it needs no type + m_position( aPosition ), m_size( aSize ), m_color( aColor ), m_style( aStyle ), m_drawAtZero( false ) +{ +} + + +const BOX2I ORIGIN_VIEWITEM::ViewBBox() const +{ + BOX2I bbox; + bbox.SetMaximum(); + return bbox; +} + + +void ORIGIN_VIEWITEM::ViewDraw( int, GAL* aGal ) const +{ + // Nothing to do if the target shouldn't be drawn at 0,0 and that's where the target is. This + // mimics the Legacy canvas that doesn't display most targets at 0,0 + if( !m_drawAtZero && ( m_position.x == 0 ) && ( m_position.y == 0 ) ) + return; + + aGal->SetIsStroke( true ); + aGal->SetIsFill( false ); + aGal->SetLineWidth( 1 ); + aGal->SetStrokeColor( m_color ); + VECTOR2D scaledSize = m_view->ToWorld( VECTOR2D( m_size, m_size ), false ); + + // Draw a circle around the marker's centre point if the style demands it + if( ( m_style == CIRCLE_CROSS ) || ( m_style == CIRCLE_DOT ) || ( m_style == CIRCLE_X ) ) + aGal->DrawCircle( m_position, scaledSize.x ); + + switch( m_style ) + { + case NONE: + break; + + case CROSS: + case CIRCLE_CROSS: + aGal->DrawLine( m_position - VECTOR2D( scaledSize.x, 0 ), + m_position + VECTOR2D( scaledSize.x, 0 ) ); + aGal->DrawLine( m_position - VECTOR2D( 0, scaledSize.y ), + m_position + VECTOR2D( 0, scaledSize.y ) ); + break; + + case X: + case CIRCLE_X: + aGal->DrawLine( m_position - scaledSize, m_position + scaledSize ); + scaledSize.y = -scaledSize.y; + aGal->DrawLine( m_position - scaledSize, m_position + scaledSize ); + break; + + case DOT: + case CIRCLE_DOT: + aGal->DrawCircle( m_position, scaledSize.x / 4 ); + break; + } +} diff --git a/common/page_layout/class_worksheet_dataitem.cpp b/common/page_layout/class_worksheet_dataitem.cpp new file mode 100644 index 0000000..b5e673f --- /dev/null +++ b/common/page_layout/class_worksheet_dataitem.cpp @@ -0,0 +1,577 @@ +/** + * @file class_worksheet_dataitem.cpp + * @brief description of graphic items and texts to build a title block + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 1992-2013 Jean-Pierre Charras <jp.charras at wanadoo.fr>. + * Copyright (C) 1992-2013 KiCad Developers, see change_log.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 + */ + + +/* + * the class WORKSHEET_DATAITEM (and derived) defines + * a basic shape of a page layout ( frame references and title block ) + * Basic shapes are line, rect and texts + * the WORKSHEET_DATAITEM coordinates units is the mm, and are relative to + * one of 4 page corners. + * + * These items cannot be drawn or plot "as this". they should be converted + * to a "draw list" (WS_DRAW_ITEM_BASE and derived items) + + * The list of these items is stored in a WORKSHEET_LAYOUT instance. + * + * When building the draw list: + * the WORKSHEET_LAYOUT is used to create a WS_DRAW_ITEM_LIST + * coordinates are converted to draw/plot coordinates. + * texts are expanded if they contain format symbols. + * Items with m_RepeatCount > 1 are created m_RepeatCount times + * + * the WORKSHEET_LAYOUT is created only once. + * the WS_DRAW_ITEM_LIST is created each time the page layout is plot/drawn + * + * the WORKSHEET_LAYOUT instance is created from a S expression which + * describes the page layout (can be the default page layout or a custom file). + */ + +#include <fctsys.h> +#include <drawtxt.h> +#include <worksheet.h> +#include <class_title_block.h> +#include <worksheet_shape_builder.h> +#include <class_worksheet_dataitem.h> + + +// Static members of class WORKSHEET_DATAITEM: +double WORKSHEET_DATAITEM::m_WSunits2Iu = 1.0; +DPOINT WORKSHEET_DATAITEM::m_RB_Corner; +DPOINT WORKSHEET_DATAITEM::m_LT_Corner; +double WORKSHEET_DATAITEM::m_DefaultLineWidth = 0.0; +DSIZE WORKSHEET_DATAITEM::m_DefaultTextSize( TB_DEFAULT_TEXTSIZE, TB_DEFAULT_TEXTSIZE ); +double WORKSHEET_DATAITEM::m_DefaultTextThickness = 0.0; +bool WORKSHEET_DATAITEM::m_SpecialMode = false; +EDA_COLOR_T WORKSHEET_DATAITEM::m_Color = RED; // the default color to draw items +EDA_COLOR_T WORKSHEET_DATAITEM::m_AltColor = RED; // an alternate color to draw items +EDA_COLOR_T WORKSHEET_DATAITEM::m_SelectedColor = BROWN; // the color to draw selected items + +// The constructor: +WORKSHEET_DATAITEM::WORKSHEET_DATAITEM( WS_ItemType aType ) +{ + m_type = aType; + m_flags = 0; + m_RepeatCount = 1; + m_IncrementLabel = 1; + m_LineWidth = 0; +} + +// move item to aPosition +// starting point is moved to aPosition +// the Ending point is moved to a position which keeps the item size +// (if both coordinates have the same corner reference) +// MoveToUi and MoveTo takes the graphic position (i.e relative to the left top +// paper corner +void WORKSHEET_DATAITEM::MoveToUi( wxPoint aPosition ) +{ + DPOINT pos_mm; + pos_mm.x = aPosition.x / m_WSunits2Iu; + pos_mm.y = aPosition.y / m_WSunits2Iu; + + MoveTo( pos_mm ); +} + +void WORKSHEET_DATAITEM::MoveTo( DPOINT aPosition ) +{ + DPOINT vector = aPosition - GetStartPos(); + DPOINT endpos = vector + GetEndPos(); + + MoveStartPointTo( aPosition ); + MoveEndPointTo( endpos ); +} + +/* move the starting point of the item to a new position + * aPosition = the new position of the starting point, in mm + */ +void WORKSHEET_DATAITEM::MoveStartPointTo( DPOINT aPosition ) +{ + DPOINT position; + + // Calculate the position of the starting point + // relative to the reference corner + // aPosition is the position relative to the right top paper corner + switch( m_Pos.m_Anchor ) + { + case RB_CORNER: + position = m_RB_Corner - aPosition; + break; + + case RT_CORNER: + position.x = m_RB_Corner.x - aPosition.x; + position.y = aPosition.y - m_LT_Corner.y; + break; + + case LB_CORNER: + position.x = aPosition.x - m_LT_Corner.x; + position.y = m_RB_Corner.y - aPosition.y; + break; + + case LT_CORNER: + position = aPosition - m_LT_Corner; + break; + } + + m_Pos.m_Pos = position; +} + +/* move the starting point of the item to a new position + * aPosition = the new position of the starting point in graphic units + */ +void WORKSHEET_DATAITEM::MoveStartPointToUi( wxPoint aPosition ) +{ + DPOINT pos_mm; + pos_mm.x = aPosition.x / m_WSunits2Iu; + pos_mm.y = aPosition.y / m_WSunits2Iu; + + MoveStartPointTo( pos_mm ); +} + +/** + * move the ending point of the item to a new position + * has meaning only for items defined by 2 points + * (segments and rectangles) + * aPosition = the new position of the ending point, in mm + */ +void WORKSHEET_DATAITEM::MoveEndPointTo( DPOINT aPosition ) +{ + DPOINT position; + + // Calculate the position of the starting point + // relative to the reference corner + // aPosition is the position relative to the right top paper corner + switch( m_End.m_Anchor ) + { + case RB_CORNER: + position = m_RB_Corner - aPosition; + break; + + case RT_CORNER: + position.x = m_RB_Corner.x - aPosition.x; + position.y = aPosition.y - m_LT_Corner.y; + break; + + case LB_CORNER: + position.x = aPosition.x - m_LT_Corner.x; + position.y = m_RB_Corner.y - aPosition.y; + break; + + case LT_CORNER: + position = aPosition - m_LT_Corner; + break; + } + + // Modify m_End only for items having 2 coordinates + switch( GetType() ) + { + case WS_SEGMENT: + case WS_RECT: + m_End.m_Pos = position; + break; + + default: + break; + } +} + +/* move the ending point of the item to a new position + * has meaning only for items defined by 2 points + * (segments and rectangles) + * aPosition = the new position of the ending point in graphic units + */ +void WORKSHEET_DATAITEM::MoveEndPointToUi( wxPoint aPosition ) +{ + DPOINT pos_mm; + pos_mm.x = aPosition.x / m_WSunits2Iu; + pos_mm.y = aPosition.y / m_WSunits2Iu; + + MoveEndPointTo( pos_mm ); +} + +const DPOINT WORKSHEET_DATAITEM::GetStartPos( int ii ) const +{ + DPOINT pos; + pos.x = m_Pos.m_Pos.x + ( m_IncrementVector.x * ii ); + pos.y = m_Pos.m_Pos.y + ( m_IncrementVector.y * ii ); + + switch( m_Pos.m_Anchor ) + { + case RB_CORNER: // right bottom corner + pos = m_RB_Corner - pos; + break; + + case RT_CORNER: // right top corner + pos.x = m_RB_Corner.x - pos.x; + pos.y = m_LT_Corner.y + pos.y; + break; + + case LB_CORNER: // left bottom corner + pos.x = m_LT_Corner.x + pos.x; + pos.y = m_RB_Corner.y - pos.y; + break; + + case LT_CORNER: // left top corner + pos = m_LT_Corner + pos; + break; + } + + return pos; +} + +const wxPoint WORKSHEET_DATAITEM::GetStartPosUi( int ii ) const +{ + DPOINT pos = GetStartPos( ii ); + pos = pos * m_WSunits2Iu; + return wxPoint( KiROUND(pos.x), KiROUND(pos.y) ); +} + +const DPOINT WORKSHEET_DATAITEM::GetEndPos( int ii ) const +{ + DPOINT pos; + pos.x = m_End.m_Pos.x + ( m_IncrementVector.x * ii ); + pos.y = m_End.m_Pos.y + ( m_IncrementVector.y * ii ); + switch( m_End.m_Anchor ) + { + case RB_CORNER: // right bottom corner + pos = m_RB_Corner - pos; + break; + + case RT_CORNER: // right top corner + pos.x = m_RB_Corner.x - pos.x; + pos.y = m_LT_Corner.y + pos.y; + break; + + case LB_CORNER: // left bottom corner + pos.x = m_LT_Corner.x + pos.x; + pos.y = m_RB_Corner.y - pos.y; + break; + + case LT_CORNER: // left top corner + pos = m_LT_Corner + pos; + break; + } + + return pos; +} + +const wxPoint WORKSHEET_DATAITEM::GetEndPosUi( int ii ) const +{ + DPOINT pos = GetEndPos( ii ); + pos = pos * m_WSunits2Iu; + return wxPoint( KiROUND(pos.x), KiROUND(pos.y) ); +} + + +bool WORKSHEET_DATAITEM::IsInsidePage( int ii ) const +{ + DPOINT pos = GetStartPos( ii ); + + for( int kk = 0; kk < 1; kk++ ) + { + if( m_RB_Corner.x < pos.x || m_LT_Corner.x > pos.x ) + return false; + + if( m_RB_Corner.y < pos.y || m_LT_Corner.y > pos.y ) + return false; + + pos = GetEndPos( ii ); + } + + return true; +} + +const wxString WORKSHEET_DATAITEM::GetClassName() const +{ + wxString name; + switch( GetType() ) + { + case WS_TEXT: name = wxT("Text"); break; + case WS_SEGMENT: name = wxT("Line"); break; + case WS_RECT: name = wxT("Rect"); break; + case WS_POLYPOLYGON: name = wxT("Poly"); break; + case WS_BITMAP: name = wxT("Bitmap"); break; + } + + return name; +} + +/* return 0 if the item has no specific option for page 1 + * 1 if the item is only on page 1 + * -1 if the item is not on page 1 + */ +int WORKSHEET_DATAITEM::GetPage1Option() +{ + if(( m_flags & PAGE1OPTION) == PAGE1OPTION_NOTONPAGE1 ) + return -1; + + if(( m_flags & PAGE1OPTION) == PAGE1OPTION_PAGE1ONLY ) + return 1; + + return 0; +} + +/* Set the option for page 1 + * aChoice = 0 if the item has no specific option for page 1 + * > 0 if the item is only on page 1 + * < 0 if the item is not on page 1 + */ +void WORKSHEET_DATAITEM::SetPage1Option( int aChoice ) +{ + ClearFlags( PAGE1OPTION ); + + if( aChoice > 0) + SetFlags( PAGE1OPTION_PAGE1ONLY ); + + else if( aChoice < 0) + SetFlags( PAGE1OPTION_NOTONPAGE1 ); + +} + + +WORKSHEET_DATAITEM_POLYPOLYGON::WORKSHEET_DATAITEM_POLYPOLYGON() : + WORKSHEET_DATAITEM( WS_POLYPOLYGON ) +{ + m_Orient = 0.0; +} + +const DPOINT WORKSHEET_DATAITEM_POLYPOLYGON::GetCornerPosition( unsigned aIdx, + int aRepeat ) const +{ + DPOINT pos = m_Corners[aIdx]; + + // Rotation: + RotatePoint( &pos.x, &pos.y, m_Orient * 10 ); + pos += GetStartPos( aRepeat ); + return pos; +} + +void WORKSHEET_DATAITEM_POLYPOLYGON::SetBoundingBox() +{ + if( m_Corners.size() == 0 ) + { + m_minCoord.x = m_maxCoord.x = 0.0; + m_minCoord.y = m_maxCoord.y = 0.0; + return; + } + + DPOINT pos; + pos = m_Corners[0]; + RotatePoint( &pos.x, &pos.y, m_Orient * 10 ); + m_minCoord = m_maxCoord = pos; + + for( unsigned ii = 1; ii < m_Corners.size(); ii++ ) + { + pos = m_Corners[ii]; + RotatePoint( &pos.x, &pos.y, m_Orient * 10 ); + + if( m_minCoord.x > pos.x ) + m_minCoord.x = pos.x; + + if( m_minCoord.y > pos.y ) + m_minCoord.y = pos.y; + + if( m_maxCoord.x < pos.x ) + m_maxCoord.x = pos.x; + + if( m_maxCoord.y < pos.y ) + m_maxCoord.y = pos.y; + } +} + +bool WORKSHEET_DATAITEM_POLYPOLYGON::IsInsidePage( int ii ) const +{ + DPOINT pos = GetStartPos( ii ); + pos += m_minCoord; // left top pos of bounding box + + if( m_LT_Corner.x > pos.x || m_LT_Corner.y > pos.y ) + return false; + + pos = GetStartPos( ii ); + pos += m_maxCoord; // rignt bottom pos of bounding box + + if( m_RB_Corner.x < pos.x || m_RB_Corner.y < pos.y ) + return false; + + return true; +} + +const wxPoint WORKSHEET_DATAITEM_POLYPOLYGON::GetCornerPositionUi( unsigned aIdx, + int aRepeat ) const +{ + DPOINT pos = GetCornerPosition( aIdx, aRepeat ); + pos = pos * m_WSunits2Iu; + return wxPoint( int(pos.x), int(pos.y) ); +} + +WORKSHEET_DATAITEM_TEXT::WORKSHEET_DATAITEM_TEXT( const wxString& aTextBase ) : + WORKSHEET_DATAITEM( WS_TEXT ) +{ + m_TextBase = aTextBase; + m_IncrementLabel = 1; + m_Hjustify = GR_TEXT_HJUSTIFY_LEFT; + m_Vjustify = GR_TEXT_VJUSTIFY_CENTER; + m_Orient = 0.0; + m_LineWidth = 0.0; // 0.0 means use default value +} + +void WORKSHEET_DATAITEM_TEXT::TransfertSetupToGraphicText( WS_DRAW_ITEM_TEXT* aGText ) +{ + aGText->SetHorizJustify( m_Hjustify ) ; + aGText->SetVertJustify( m_Vjustify ); + aGText->SetOrientation( m_Orient * 10 ); // graphic text orient unit = 0.1 degree +} + +void WORKSHEET_DATAITEM_TEXT::IncrementLabel( int aIncr ) +{ + int last = m_TextBase.Len() -1; + + wxChar lbchar = m_TextBase[last]; + m_FullText = m_TextBase; + m_FullText.RemoveLast(); + + if( lbchar >= '0' && lbchar <= '9' ) + // A number is expected: + m_FullText << (int)( aIncr + lbchar - '0' ); + else + m_FullText << (wxChar) ( aIncr + lbchar ); +} + +// Replace the '\''n' sequence by EOL +// and the sequence '\''\' by only one '\' in m_FullText +// if m_FullText is a multiline text (i.e.contains '\n') return true +bool WORKSHEET_DATAITEM_TEXT::ReplaceAntiSlashSequence() +{ + bool multiline = false; + + for( unsigned ii = 0; ii < m_FullText.Len(); ii++ ) + { + if( m_FullText[ii] == '\n' ) + multiline = true; + + else if( m_FullText[ii] == '\\' ) + { + if( ++ii >= m_FullText.Len() ) + break; + + if( m_FullText[ii] == '\\' ) + { + // a double \\ sequence is replaced by a single \ char + m_FullText.Remove(ii, 1); + ii--; + } + else if( m_FullText[ii] == 'n' ) + { + // Replace the "\n" sequence by a EOL char + multiline = true; + m_FullText[ii] = '\n'; + m_FullText.Remove(ii-1, 1); + ii--; + } + } + } + + return multiline; +} + +void WORKSHEET_DATAITEM_TEXT::SetConstrainedTextSize() +{ + m_ConstrainedTextSize = m_TextSize; + + if( m_ConstrainedTextSize.x == 0 ) + m_ConstrainedTextSize.x = m_DefaultTextSize.x; + + if( m_ConstrainedTextSize.y == 0 ) + m_ConstrainedTextSize.y = m_DefaultTextSize.y; + + if( m_BoundingBoxSize.x || m_BoundingBoxSize.y ) + { + int linewidth = 0; + // to know the X and Y size of the line, we should use + // EDA_TEXT::GetTextBox() + // but this function uses integers + // So, to avoid truncations with our unit in mm, use microns. + wxSize size_micron; + size_micron.x = KiROUND( m_ConstrainedTextSize.x * 1000.0 ); + size_micron.y = KiROUND( m_ConstrainedTextSize.y * 1000.0 ); + WS_DRAW_ITEM_TEXT dummy( WS_DRAW_ITEM_TEXT( this, this->m_FullText, + wxPoint(0,0), + size_micron, + linewidth, BLACK, + IsItalic(), IsBold() ) ); + dummy.SetMultilineAllowed( true ); + TransfertSetupToGraphicText( &dummy ); + EDA_RECT rect = dummy.GetTextBox(); + DSIZE size; + size.x = rect.GetWidth() / 1000.0; + size.y = rect.GetHeight() / 1000.0; + + if( m_BoundingBoxSize.x && size.x > m_BoundingBoxSize.x ) + m_ConstrainedTextSize.x *= m_BoundingBoxSize.x / size.x; + + if( m_BoundingBoxSize.y && size.y > m_BoundingBoxSize.y ) + m_ConstrainedTextSize.y *= m_BoundingBoxSize.y / size.y; + } +} + +/* set the pixel scale factor of the bitmap + * this factor depend on the application internal unit + * and the PPI bitmap factor + * the pixel scale factor should be initialized before drawing the bitmap + */ +void WORKSHEET_DATAITEM_BITMAP::SetPixelScaleFactor() +{ + if( m_ImageBitmap ) + { + // m_WSunits2Iu is the page layout unit to application internal unit + // i.e. the mm to to application internal unit + // however the bitmap definition is always known in pixels per inches + double scale = m_WSunits2Iu * 25.4 / m_ImageBitmap->GetPPI(); + m_ImageBitmap->SetPixelScaleFactor( scale ); + } +} + +/* return the PPI of the bitmap + */ +int WORKSHEET_DATAITEM_BITMAP::GetPPI() const +{ + if( m_ImageBitmap ) + return m_ImageBitmap->GetPPI() / m_ImageBitmap->m_Scale; + + return 300; +} + +/*adjust the PPI of the bitmap + */ +void WORKSHEET_DATAITEM_BITMAP::SetPPI( int aBitmapPPI ) +{ + if( m_ImageBitmap ) + m_ImageBitmap->m_Scale = (double) m_ImageBitmap->GetPPI() / aBitmapPPI; +} + diff --git a/common/page_layout/class_worksheet_layout.cpp b/common/page_layout/class_worksheet_layout.cpp new file mode 100644 index 0000000..94c11a1 --- /dev/null +++ b/common/page_layout/class_worksheet_layout.cpp @@ -0,0 +1,248 @@ +/** + * @file class_worksheet_layout.cpp + * @brief description of graphic items and texts to build a title block + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 1992-2013 Jean-Pierre Charras <jp.charras at wanadoo.fr>. + * Copyright (C) 1992-2013 KiCad Developers, see change_log.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 + */ + + +/* + * the class WORKSHEET_DATAITEM (and derived ) defines + * a basic shape of a page layout ( frame references and title block ) + * The list of these items is stored in a WORKSHEET_LAYOUT instance. + * + * + * These items cannot be drawn or plot "as this". they should be converted + * to a "draw list". When building the draw list: + * the WORKSHEET_LAYOUT is used to create a WS_DRAW_ITEM_LIST + * coordinates are converted to draw/plot coordinates. + * texts are expanded if they contain format symbols. + * Items with m_RepeatCount > 1 are created m_RepeatCount times + * + * the WORKSHEET_LAYOUT is created only once. + * the WS_DRAW_ITEM_LIST is created each time the page layout is plot/drawn + * + * the WORKSHEET_LAYOUT instance is created from a S expression which + * describes the page layout (can be the default page layout or a custom file). + */ + +#include <fctsys.h> +#include <kiface_i.h> +#include <drawtxt.h> +#include <worksheet.h> +#include <class_title_block.h> +#include <worksheet_shape_builder.h> +#include <class_worksheet_dataitem.h> + + +// The layout shape used in the application +// It is accessible by WORKSHEET_LAYOUT::GetTheInstance() +static WORKSHEET_LAYOUT wksTheInstance; +static WORKSHEET_LAYOUT* wksAltInstance; + +WORKSHEET_LAYOUT::WORKSHEET_LAYOUT() +{ + m_allowVoidList = false; + m_leftMargin = 10.0; // the left page margin in mm + m_rightMargin = 10.0; // the right page margin in mm + m_topMargin = 10.0; // the top page margin in mm + m_bottomMargin = 10.0; // the bottom page margin in mm +} + +/* static function: returns the instance of WORKSHEET_LAYOUT + * used in the application + */ +WORKSHEET_LAYOUT& WORKSHEET_LAYOUT::GetTheInstance() +{ + if( wksAltInstance ) + return *wksAltInstance; + else + return wksTheInstance; +} + +/** + * static function: Set an alternate instance of WORKSHEET_LAYOUT + * mainly used in page setting dialog + * @param aLayout = the alternate page layout. + * if null, restore the basic page layout + */ +void WORKSHEET_LAYOUT::SetAltInstance( WORKSHEET_LAYOUT* aLayout ) +{ + wksAltInstance = aLayout; +} + + +void WORKSHEET_LAYOUT::SetLeftMargin( double aMargin ) +{ + m_leftMargin = aMargin; // the left page margin in mm +} + + +void WORKSHEET_LAYOUT::SetRightMargin( double aMargin ) +{ + m_rightMargin = aMargin; // the right page margin in mm +} + + +void WORKSHEET_LAYOUT::SetTopMargin( double aMargin ) +{ + m_topMargin = aMargin; // the top page margin in mm +} + + +void WORKSHEET_LAYOUT::SetBottomMargin( double aMargin ) +{ + m_bottomMargin = aMargin; // the bottom page margin in mm +} + + +void WORKSHEET_LAYOUT::ClearList() +{ + for( unsigned ii = 0; ii < m_list.size(); ii++ ) + delete m_list[ii]; + m_list.clear(); +} + + +void WORKSHEET_LAYOUT::Insert( WORKSHEET_DATAITEM* aItem, unsigned aIdx ) +{ + if ( aIdx >= GetCount() ) + Append( aItem ); + else + m_list.insert( m_list.begin() + aIdx, aItem ); +} + + +bool WORKSHEET_LAYOUT::Remove( unsigned aIdx ) +{ + if ( aIdx >= GetCount() ) + return false; + m_list.erase( m_list.begin() + aIdx ); + return true; +} + + +bool WORKSHEET_LAYOUT::Remove( WORKSHEET_DATAITEM* aItem ) +{ + unsigned idx = 0; + + while( idx < m_list.size() ) + { + if( m_list[idx] == aItem ) + break; + + idx++; + } + + return Remove( idx ); +} + + +int WORKSHEET_LAYOUT::GetItemIndex( WORKSHEET_DATAITEM* aItem ) const +{ + unsigned idx = 0; + while( idx < m_list.size() ) + { + if( m_list[idx] == aItem ) + return (int) idx; + + idx++; + } + + return -1; +} + +/* return the item from its index aIdx, or NULL if does not exist + */ +WORKSHEET_DATAITEM* WORKSHEET_LAYOUT::GetItem( unsigned aIdx ) const +{ + if( aIdx < m_list.size() ) + return m_list[aIdx]; + else + return NULL; +} + + +const wxString WORKSHEET_LAYOUT::MakeShortFileName( const wxString& aFullFileName, + const wxString& aProjectPath ) +{ + wxString shortFileName = aFullFileName; + wxFileName fn = aFullFileName; + + if( fn.IsRelative() ) + return shortFileName; + + if( ! aProjectPath.IsEmpty() && aFullFileName.StartsWith( aProjectPath ) ) + { + fn.MakeRelativeTo( aProjectPath ); + shortFileName = fn.GetFullPath(); + return shortFileName; + } + + wxString fileName = Kiface().KifaceSearch().FindValidPath( fn.GetFullName() ); + + if( !fileName.IsEmpty() ) + { + fn = fileName; + shortFileName = fn.GetFullName(); + return shortFileName; + } + + return shortFileName; +} + + +const wxString WORKSHEET_LAYOUT::MakeFullFileName( const wxString& aShortFileName, + const wxString& aProjectPath ) +{ + wxString fullFileName = ExpandEnvVarSubstitutions( aShortFileName ); + + if( fullFileName.IsEmpty() ) + return fullFileName; + + wxFileName fn = fullFileName; + + if( fn.IsAbsolute() ) + return fullFileName; + + // the path is not absolute: search it in project path, and then in + // kicad valid paths + if( !aProjectPath.IsEmpty() ) + { + fn.MakeAbsolute( aProjectPath ); + + if( wxFileExists( fn.GetFullPath() ) ) + return fn.GetFullPath(); + } + + fn = fullFileName; + wxString name = Kiface().KifaceSearch().FindValidPath( fn.GetFullName() ); + + if( !name.IsEmpty() ) + fullFileName = name; + + return fullFileName; +} diff --git a/common/page_layout/page_layout_default_description.cpp b/common/page_layout/page_layout_default_description.cpp new file mode 100644 index 0000000..fd8ba46 --- /dev/null +++ b/common/page_layout/page_layout_default_description.cpp @@ -0,0 +1,164 @@ +/** + * @file common/page_layout/page_layout_default_description.cpp + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 1992-2013 Jean-Pierre Charras <jp.charras at wanadoo.fr>. + * Copyright (C) 1992-2013 KiCad Developers, see change_log.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 + */ + +/* keyword used in page layout description are listed + * in page_layout_reader.keywords file + */ + +/* + * Items use coordinates. + * A coordinate is defined relative to a page corner + * A coordinate is the X pos, the Y pos, and the corner which is the coordinate origin + * the default is the bottom right corner + * example: (start 10 0 ltcorner) or (start 20 10) + * The direction depends on the corner: a positive coordinate define a point + * from the corner origin, to the opposite corner. + * + * Items are defined by a name, coordinates in mm and some attributes, + * and can be repeated. + * for instance (repeat 2) (incrx 2) (incry 2) repeat the item 2 times, + * and coordinates are incremented by 2 on X direction, and 2 on Y direction + * Comments are allowed. they are inside (), and start by the keyword comment + * example: + * (comment rect around the title block) + * + * Lines and rect are defined by 2 coordinates start and end, and attributes. + * Attributes are linewidth and repeat parameters. + * example: + * (line (start 50 2 ltcorner) (end 50 0 ltcorner) (repeat 30) (incrx 50) ) + * (rect (comment rect around the title block) (linewidth 0.15) (start 110 34) (end 2 2) ) + * + * Texts are defined by the text (between quotes), the position, and attributes + * example + * "(tbtext \"1\" (pos 25 1 lbcorner) (font (size 1.3 1.3)) (repeat 100) (incrx 50) )" + * the text can be rotated by (rotation <value>) with value = rot angle in degrees + * (font (size 1.3 1.3) bold italic) defines a specific size, + * with bold and italic options + * (justify <justif keyword>) controls the text justification (the default is left) + * justif keyword is center, left, right, top and bottom + * (justify center top) is a text centered on X axis and top aligned on vertical axis + * The text size can be constrained: + * (maxlen <value>) and (maxheight <value>) force the actual text x and/or y size to be + * reduced to limit the text height to the maxheight value, + * and the full text x size to the maxlen value. + * If the actual text size is smaller than limits, its size is not modified. + * + * Texts can include a format symbol, a la printf. + * At run time these format symbols will be replaced by their actual value. + * + * format symbols are: + * + * %% = replaced by % + * %K = Kicad version + * %Z = paper format name (A4, USLetter ...) + * %Y = company name + * %D = date + * %R = revision + * %S = sheet number + * %N = number of sheets + * %Cx = comment (x = 0 to 9 to identify the comment) + * %F = filename + * %P = sheet path (sheet full name) + * %T = title + * + * example: + * (tbtext \"Size: %Z\" ...) displays "Size A4" or Size USLetter" + * + * Poly Polygons + * Set of filled polygons are supported. + * + * The main purpose is to allow logos, or complex shapes + * They support the repeat and rotation options + * They are defined by + * (polygon (position ..) <rotation> <linewidth> + * the parameter linewidth defines the pen size used to draw/plot + * the polygon outlines (default = 0) + * example: + * (polygon (pos 134 18 rbcorner) (rotate 20) (linewidth 0.00254) + * + * and a list of corners like + * (pts (xy 20.574 8.382) (xy 19.9009 8.382) (xy 19.9009 6.26364) (xy 19.7485 5.98932) + * .... ) + * + * each sequence like + * (pts (xy 20.574 8.382) (xy 19.9009 8.382) (xy 19.9009 6.26364) (xy 19.7485 5.98932) + * .... ) + * defines a polygon. + * Each coordinate is relative to the polygon position. + * Therefore a "polygon" is in fact a set of polygons, of a poly polygon + * + */ + +#include <worksheet.h> // defaultPageLayout + + +// height of the band reference grid 2.0 mm +// worksheet frame reference text size 1.3 mm +// default text size 1.5 mm +// default line width 0.15 mm +// frame ref pitch 50 mm + +// export defaultPageLayout: +extern const char defaultPageLayout[]; + +// Default page layout (sizes are in mm) +const char defaultPageLayout[] = "( page_layout\n" + "(setup (textsize 1.5 1.5) (linewidth 0.15) (textlinewidth 0.15)\n" + "(left_margin 10)(right_margin 10)(top_margin 10)(bottom_margin 10))\n" + "(rect (comment \"rect around the title block\") (linewidth 0.15) (start 110 34) (end 2 2) )\n" + "(rect (start 0 0 ltcorner) (end 0 0 rbcorner) (repeat 2) (incrx 2) (incry 2) )\n" + "(line (start 50 2 ltcorner) (end 50 0 ltcorner) (repeat 30) (incrx 50) )\n" + "(tbtext \"1\" (pos 25 1 ltcorner) (font (size 1.3 1.3))(repeat 100) (incrx 50) )\n" + "(line (start 50 2 lbcorner) (end 50 0 lbcorner) (repeat 30) (incrx 50) )\n" + "(tbtext \"1\" (pos 25 1 lbcorner) (font (size 1.3 1.3)) (repeat 100) (incrx 50) )\n" + "(line (start 0 50 ltcorner) (end 2 50 ltcorner) (repeat 30) (incry 50) )\n" + "(tbtext \"A\" (pos 1 25 ltcorner) (font (size 1.3 1.3)) (justify center)(repeat 100) (incry 50) )\n" + "(line (start 0 50 rtcorner) (end 2 50 rtcorner) (repeat 30) (incry 50) )\n" + "(tbtext \"A\" (pos 1 25 rtcorner) (font (size 1.3 1.3)) (justify center) (repeat 100) (incry 50) )\n" + "(tbtext \"Date: %D\" (pos 87 6.9) )\n" + "(line (start 110 5.5) (end 2 5.5) )\n" + "(tbtext \"%K\" (pos 109 4.1) (comment \"Kicad version\" ) )\n" + "(line (start 110 8.5) (end 2 8.5) )\n" + "(tbtext \"Rev: %R\" (pos 24 6.9)(font bold)(justify left) )\n" + "(tbtext \"Size: %Z\" (comment \"Paper format name\")(pos 109 6.9) )\n" + "(tbtext \"Id: %S/%N\" (comment \"Sheet id\")(pos 24 4.1) )\n" + "(line (start 110 12.5) (end 2 12.5) )\n" + "(tbtext \"Title: %T\" (pos 109 10.7)(font bold italic (size 2 2)) )\n" + "(tbtext \"File: %F\" (pos 109 14.3) )\n" + "(line (start 110 18.5) (end 2 18.5) )\n" + "(tbtext \"Sheet: %P\" (pos 109 17) )\n" + "(tbtext \"%Y\" (comment \"Company name\") (pos 109 20)(font bold) )\n" + "(tbtext \"%C0\" (comment \"Comment 0\") (pos 109 23) )\n" + "(tbtext \"%C1\" (comment \"Comment 1\") (pos 109 26) )\n" + "(tbtext \"%C2\" (comment \"Comment 2\") (pos 109 29) )\n" + "(tbtext \"%C3\" (comment \"Comment 3\") (pos 109 32) )\n" + "(line (start 90 8.5) (end 90 5.5) )\n" + "(line (start 26 8.5) (end 26 2) )\n" + ")\n" +; diff --git a/common/page_layout/page_layout_graphic_items.cpp b/common/page_layout/page_layout_graphic_items.cpp new file mode 100644 index 0000000..c927e93 --- /dev/null +++ b/common/page_layout/page_layout_graphic_items.cpp @@ -0,0 +1,423 @@ +/** + * @file page_layout_graphic_items.cpp + * @brief description of graphic items and texts to build a title block + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 1992-2013 Jean-Pierre Charras <jp.charras at wanadoo.fr>. + * Copyright (C) 1992-2013 KiCad Developers, see change_log.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 + */ + + +/* + * the class WORKSHEET_DATAITEM (and WORKSHEET_DATAITEM_TEXT) defines + * a basic shape of a page layout ( frame references and title block ) + * Basic shapes are line, rect and texts + * the WORKSHEET_DATAITEM coordinates units is the mm, and are relative to + * one of 4 page corners. + * + * These items cannot be drawn or plot "as this". they should be converted + * to a "draw list" (WS_DRAW_ITEM_BASE and derived items) + + * The list of these items is stored in a WORKSHEET_LAYOUT instance. + * + * When building the draw list: + * the WORKSHEET_LAYOUT is used to create a WS_DRAW_ITEM_LIST + * coordinates are converted to draw/plot coordinates. + * texts are expanded if they contain format symbols. + * Items with m_RepeatCount > 1 are created m_RepeatCount times + * + * the WORKSHEET_LAYOUT is created only once. + * the WS_DRAW_ITEM_LIST is created each time the page layout is plotted/drawn + * + * the WORKSHEET_LAYOUT instance is created from a S expression which + * describes the page layout (can be the default page layout or a custom file). + */ + +#include <fctsys.h> +#include <drawtxt.h> +#include <worksheet.h> +#include <class_title_block.h> +#include <worksheet_shape_builder.h> +#include <class_worksheet_dataitem.h> + +/* a helper function to draw graphic symbols at start point or end point of + * an item. + * The start point symbol is a filled rectangle + * The start point symbol is a filled circle + */ +inline void drawMarker( EDA_RECT* aClipBox, wxDC* aDC, + const wxPoint& aPos, int aSize, bool aEndPointShape = false ) +{ + int markerHalfSize = aSize/2; + + if( aEndPointShape ) + GRFilledCircle( aClipBox, aDC, aPos.x, aPos.y, markerHalfSize, + 0, GREEN, GREEN ); + else + GRFilledRect( aClipBox, aDC, + aPos.x - markerHalfSize, aPos.y - markerHalfSize, + aPos.x + markerHalfSize, aPos.y + markerHalfSize, + 0, GREEN, GREEN ); +} + +/* Draws the item list created by BuildWorkSheetGraphicList + * aClipBox = the clipping rect, or NULL if no clipping + * aDC = the current Device Context + * The not selected items are drawn first (most of items) + * The selected items are drawn after (usually 0 or 1) + * to be sure they are seen, even for overlapping items + */ +void WS_DRAW_ITEM_LIST::Draw( EDA_RECT* aClipBox, wxDC* aDC ) +{ + // The not selected items are drawn first (most of items) + for( WS_DRAW_ITEM_BASE* item = GetFirst(); item; item = GetNext() ) + { + if( item->GetParent() && item->GetParent()->IsSelected() ) + continue; + + item->DrawWsItem( aClipBox, aDC ); + } + + // The selected items are drawn after (usually 0 or 1) + int markerSize = WORKSHEET_DATAITEM::GetMarkerSizeUi(); + + for( WS_DRAW_ITEM_BASE* item = GetFirst(); item; item = GetNext() ) + { + if( !item->GetParent() || !item->GetParent()->IsSelected() ) + continue; + + item->DrawWsItem( aClipBox, aDC ); + + switch( item->GetType() ) + { + case WS_DRAW_ITEM_BASE::wsg_line: + { + WS_DRAW_ITEM_LINE* line = (WS_DRAW_ITEM_LINE*) item; + + if( markerSize ) + { + drawMarker( aClipBox, aDC, line->GetStart(), markerSize ); + drawMarker( aClipBox, aDC, line->GetEnd(), markerSize, true ); + } + } + break; + + case WS_DRAW_ITEM_BASE::wsg_rect: + { + WS_DRAW_ITEM_RECT* rect = (WS_DRAW_ITEM_RECT*) item; + + if( markerSize ) + { + drawMarker( aClipBox, aDC, rect->GetStart(), markerSize ); + drawMarker( aClipBox, aDC, rect->GetEnd(), markerSize, true ); + } + } + break; + + case WS_DRAW_ITEM_BASE::wsg_text: + { + WS_DRAW_ITEM_TEXT* text = (WS_DRAW_ITEM_TEXT*) item; + + if( markerSize ) + drawMarker( aClipBox, aDC, text->GetTextPosition(), + markerSize ); + } + break; + + case WS_DRAW_ITEM_BASE::wsg_poly: + { + WS_DRAW_ITEM_POLYGON* poly = (WS_DRAW_ITEM_POLYGON*) item; + + if( markerSize ) + { + drawMarker( aClipBox, aDC, poly->GetPosition(), + markerSize ); + } + } + break; + + case WS_DRAW_ITEM_BASE::wsg_bitmap: + { + WS_DRAW_ITEM_BITMAP* bitmap = (WS_DRAW_ITEM_BITMAP*) item; + + if( markerSize ) + { + drawMarker( aClipBox, aDC, bitmap->GetPosition(), + markerSize ); + } + } + break; + } + } +} + +WS_DRAW_ITEM_TEXT::WS_DRAW_ITEM_TEXT( WORKSHEET_DATAITEM* aParent, + wxString& aText, wxPoint aPos, wxSize aSize, + int aPenWidth, EDA_COLOR_T aColor, + bool aItalic, bool aBold ) : + WS_DRAW_ITEM_BASE( aParent, wsg_text, aColor ), EDA_TEXT( aText ) +{ + SetTextPosition( aPos ); + SetSize( aSize ); + SetThickness( aPenWidth ); + SetItalic( aItalic ); + SetBold( aBold ); +} + +void WS_DRAW_ITEM_TEXT::DrawWsItem( EDA_RECT* aClipBox, wxDC* aDC ) +{ + Draw( aClipBox, aDC, wxPoint(0,0), + GetColor(), UNSPECIFIED_DRAWMODE, FILLED, UNSPECIFIED_COLOR ); +} + +// return true if the point aPosition is on the text +bool WS_DRAW_ITEM_TEXT::HitTest( const wxPoint& aPosition) const +{ + return EDA_TEXT::TextHitTest( aPosition, 0 ); +} + +/* return true if the point aPosition is on the starting point of this item + */ +bool WS_DRAW_ITEM_TEXT::HitTestStartPoint( const wxPoint& aPosition) +{ + wxPoint pos = GetTextPosition(); + + if( std::abs( pos.x - aPosition.x) <= WORKSHEET_DATAITEM::GetMarkerSizeUi()/2 && + std::abs( pos.y - aPosition.y) <= WORKSHEET_DATAITEM::GetMarkerSizeUi()/2 ) + return true; + + return false; +} + +void WS_DRAW_ITEM_POLYGON::DrawWsItem( EDA_RECT* aClipBox, wxDC* aDC ) +{ + GRPoly( aClipBox, aDC, + m_Corners.size(), &m_Corners[0], + IsFilled() ? FILLED_SHAPE : NO_FILL, + GetPenWidth(), + GetColor(), GetColor() ); +} + +// return true if the point aPosition is inside one of polygons +#include <polygon_test_point_inside.h> +bool WS_DRAW_ITEM_POLYGON::HitTest( const wxPoint& aPosition) const +{ + return TestPointInsidePolygon( &m_Corners[0], + m_Corners.size(), aPosition ); +} + +/* return true if the point aPosition is on the starting point of this item + */ +bool WS_DRAW_ITEM_POLYGON::HitTestStartPoint( const wxPoint& aPosition) +{ + wxPoint pos = GetPosition(); + + if( std::abs( pos.x - aPosition.x) <= WORKSHEET_DATAITEM::GetMarkerSizeUi()/2 && + std::abs( pos.y - aPosition.y) <= WORKSHEET_DATAITEM::GetMarkerSizeUi()/2 ) + return true; + + return false; +} + +void WS_DRAW_ITEM_RECT::DrawWsItem( EDA_RECT* aClipBox, wxDC* aDC ) +{ + GRRect( aClipBox, aDC, + GetStart().x, GetStart().y, + GetEnd().x, GetEnd().y, + GetPenWidth(), GetColor() ); +} + +// return true if the point aPosition is on the rect outline +bool WS_DRAW_ITEM_RECT::HitTest( const wxPoint& aPosition) const +{ + int dist = GetPenWidth()/2; + wxPoint start = GetStart(); + wxPoint end; + end.x = GetEnd().x; + end.y = start.y; + + // Upper line + if( TestSegmentHit( aPosition, start, end, dist ) ) + return true; + + // Right line + start = end; + end.y = GetEnd().y; + if( TestSegmentHit( aPosition, start, end, dist ) ) + return true; + + // lower line + start = end; + end.x = GetStart().x; + if( TestSegmentHit( aPosition, start, end, dist ) ) + return true; + + // left line + start = end; + end = GetStart(); + if( TestSegmentHit( aPosition, start, end, dist ) ) + return true; + + return false; +} + +/* return true if the point aPosition is on the starting point of this item + */ +bool WS_DRAW_ITEM_RECT::HitTestStartPoint( const wxPoint& aPosition) +{ + wxPoint dist = GetStart() - aPosition; + + if( std::abs( dist.x) <= WORKSHEET_DATAITEM::GetMarkerSizeUi()/2 && + std::abs( dist.y) <= WORKSHEET_DATAITEM::GetMarkerSizeUi()/2 ) + return true; + + return false; +} + +/* return true if the point aPosition is on the ending point of this item + */ +bool WS_DRAW_ITEM_RECT::HitTestEndPoint( const wxPoint& aPosition) +{ + wxPoint pos = GetEnd(); + + int dist = (int) hypot( pos.x - aPosition.x, pos.y - aPosition.y ); + + if( dist <= WORKSHEET_DATAITEM::GetMarkerSizeUi()/2 ) + return true; + + return false; +} + +void WS_DRAW_ITEM_LINE::DrawWsItem( EDA_RECT* aClipBox, wxDC* aDC ) +{ + GRLine( aClipBox, aDC, GetStart(), GetEnd(), + GetPenWidth(), GetColor() ); +} + +// return true if the point aPosition is on the text +bool WS_DRAW_ITEM_LINE::HitTest( const wxPoint& aPosition) const +{ + return TestSegmentHit( aPosition, GetStart(), GetEnd(), GetPenWidth()/2 ); +} + +/* return true if the point aPosition is on the starting point of this item + */ +bool WS_DRAW_ITEM_LINE::HitTestStartPoint( const wxPoint& aPosition) +{ + wxPoint dist = GetStart() - aPosition; + + if( std::abs( dist.x) <= WORKSHEET_DATAITEM::GetMarkerSizeUi()/2 && + std::abs( dist.y) <= WORKSHEET_DATAITEM::GetMarkerSizeUi()/2 ) + return true; + + return false; +} + +/* return true if the point aPosition is on the ending point of this item + */ +bool WS_DRAW_ITEM_LINE::HitTestEndPoint( const wxPoint& aPosition) +{ + wxPoint dist = GetEnd() - aPosition; + + if( std::abs( dist.x) <= WORKSHEET_DATAITEM::GetMarkerSizeUi()/2 && + std::abs( dist.y) <= WORKSHEET_DATAITEM::GetMarkerSizeUi()/2 ) + return true; + + return false; +} + +/* Locate graphic items in m_graphicList at location aPosition + * aList = the list of items found + * aPosition is the position (in user units) to locate items + */ +void WS_DRAW_ITEM_LIST::Locate( std::vector <WS_DRAW_ITEM_BASE*>& aList, + const wxPoint& aPosition) +{ + for( WS_DRAW_ITEM_BASE* item = GetFirst(); item; item = GetNext() ) + { + item->m_Flags &= ~(LOCATE_STARTPOINT|LOCATE_ENDPOINT); + bool found = false; + + if( item->HitTestStartPoint ( aPosition ) ) + { + item->m_Flags |= LOCATE_STARTPOINT; + found = true; + } + + if( item->HitTestEndPoint ( aPosition ) ) + { + item->m_Flags |= LOCATE_ENDPOINT; + found = true; + } + + if( found || item->HitTest( aPosition ) ) + { + aList.push_back( item ); + } + } +} + +/** The function to draw a WS_DRAW_ITEM_BITMAP + */ +void WS_DRAW_ITEM_BITMAP::DrawWsItem( EDA_RECT* aClipBox, wxDC* aDC ) +{ + WORKSHEET_DATAITEM_BITMAP* parent = (WORKSHEET_DATAITEM_BITMAP*)GetParent(); + + if( parent->m_ImageBitmap ) + { + parent->m_ImageBitmap->DrawBitmap( NULL, aDC, m_pos ); + } +} + +/** + * Virtual function + * return true if the point aPosition is on bitmap + */ +bool WS_DRAW_ITEM_BITMAP::HitTest( const wxPoint& aPosition) const +{ + const WORKSHEET_DATAITEM_BITMAP* parent = static_cast<const WORKSHEET_DATAITEM_BITMAP*>( GetParent() ); + + if( parent->m_ImageBitmap == NULL ) + return false; + + EDA_RECT rect = parent->m_ImageBitmap->GetBoundingBox(); + rect.Move( m_pos ); + return rect.Contains( aPosition ); +} + + +/** + * return true if the point aPosition is on the reference point of this item. + */ +bool WS_DRAW_ITEM_BITMAP::HitTestStartPoint( const wxPoint& aPosition) +{ + wxPoint dist = m_pos - aPosition; + + if( std::abs( dist.x) <= WORKSHEET_DATAITEM::GetMarkerSizeUi()/2 && + std::abs( dist.y) <= WORKSHEET_DATAITEM::GetMarkerSizeUi()/2 ) + return true; + + return false; +} + diff --git a/common/page_layout/page_layout_reader.cpp b/common/page_layout/page_layout_reader.cpp new file mode 100644 index 0000000..a282989 --- /dev/null +++ b/common/page_layout/page_layout_reader.cpp @@ -0,0 +1,875 @@ +/** + * @file common/page_layout/page_layout_reader.cpp + * @brief read an S expression of description of graphic items and texts + * to build a title block and page layout + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 1992-2013 Jean-Pierre Charras <jp.charras at wanadoo.fr>. + * Copyright (C) 1992-2013 KiCad Developers, see change_log.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 <fctsys.h> +#include <base_struct.h> +#include <worksheet.h> +#include <worksheet_shape_builder.h> +#include <class_worksheet_dataitem.h> +#include <page_layout_reader_lexer.h> + + +using namespace TB_READER_T; + +/** + * Class PAGE_LAYOUT_READER_PARSER + * holds data and functions pertinent to parsing a S-expression file + * for a WORKSHEET_LAYOUT. + */ +class PAGE_LAYOUT_READER_PARSER : public PAGE_LAYOUT_READER_LEXER +{ +public: + PAGE_LAYOUT_READER_PARSER( const char* aLine, const wxString& aSource ); + void Parse( WORKSHEET_LAYOUT* aLayout ) + throw( PARSE_ERROR, IO_ERROR ); + +private: + + /** + * Function parseInt + * parses an integer and constrains it between two values. + * @param aMin is the smallest return value. + * @param aMax is the largest return value. + * @return int - the parsed integer. + */ + int parseInt( int aMin, int aMax ); + + /** + * Function parseDouble + * parses a double + * @return double - the parsed double. + */ + double parseDouble(); + + void parseSetup( WORKSHEET_LAYOUT* aLayout ) throw( IO_ERROR, PARSE_ERROR ); + + /** + * parse a graphic item starting by "(line" or "(rect" and read parameters. + */ + void parseGraphic( WORKSHEET_DATAITEM * aItem ) throw( IO_ERROR, PARSE_ERROR ); + + /** + * parse a text item starting by "(tbtext" and read parameters. + */ + void parseText( WORKSHEET_DATAITEM_TEXT * aItem ) throw( IO_ERROR, PARSE_ERROR ); + + /** + * parse a polygon item starting by "( polygon" and read parameters. + * the list of corners included in this description is read by parsePolyOutline + */ + void parsePolygon( WORKSHEET_DATAITEM_POLYPOLYGON * aItem ) + throw( IO_ERROR, PARSE_ERROR ); + + /** + * parse a list of corners starting by "( pts" and read coordinates. + */ + void parsePolyOutline( WORKSHEET_DATAITEM_POLYPOLYGON * aItem ) + throw( IO_ERROR, PARSE_ERROR ); + + + /** + * parse a bitmap item starting by "( bitmap" and read parameters. + */ + void parseBitmap( WORKSHEET_DATAITEM_BITMAP * aItem ) + throw( IO_ERROR, PARSE_ERROR ); + + void parseCoordinate( POINT_COORD& aCoord) throw( IO_ERROR, PARSE_ERROR ); + void readOption( WORKSHEET_DATAITEM * aItem ) throw( IO_ERROR, PARSE_ERROR ); + void readPngdata( WORKSHEET_DATAITEM_BITMAP * aItem ) throw( IO_ERROR, PARSE_ERROR ); +}; + +// PCB_PLOT_PARAMS_PARSER + +PAGE_LAYOUT_READER_PARSER::PAGE_LAYOUT_READER_PARSER( const char* aLine, const wxString& aSource ) : + PAGE_LAYOUT_READER_LEXER( aLine, aSource ) +{ +} + + +void PAGE_LAYOUT_READER_PARSER::Parse( WORKSHEET_LAYOUT* aLayout ) + throw( PARSE_ERROR, IO_ERROR ) +{ + T token; + WORKSHEET_DATAITEM * item; + + LOCALE_IO toggle; + + while( ( token = NextTok() ) != T_RIGHT ) + { + if( token == T_EOF) + break; + + if( token == T_LEFT ) + token = NextTok(); + + if( token == T_page_layout ) + continue; + + switch( token ) + { + case T_setup: // Defines default values for graphic items + parseSetup( aLayout ); + break; + + case T_line: + item = new WORKSHEET_DATAITEM( WORKSHEET_DATAITEM::WS_SEGMENT ); + parseGraphic( item ); + aLayout->Append( item ); + break; + + case T_rect: + item = new WORKSHEET_DATAITEM( WORKSHEET_DATAITEM::WS_RECT ); + parseGraphic( item ); + aLayout->Append( item ); + break; + + case T_polygon: + item = new WORKSHEET_DATAITEM_POLYPOLYGON(); + parsePolygon( (WORKSHEET_DATAITEM_POLYPOLYGON*) item ); + aLayout->Append( item ); + break; + + case T_bitmap: + item = new WORKSHEET_DATAITEM_BITMAP( NULL ); + parseBitmap( (WORKSHEET_DATAITEM_BITMAP*) item ); + aLayout->Append( item ); + break; + + case T_tbtext: + NeedSYMBOLorNUMBER(); + item = new WORKSHEET_DATAITEM_TEXT( FromUTF8() ); + parseText( (WORKSHEET_DATAITEM_TEXT*) item ); + aLayout->Append( item ); + break; + + default: + Unexpected( CurText() ); + break; + } + } +} + +void PAGE_LAYOUT_READER_PARSER::parseSetup( WORKSHEET_LAYOUT* aLayout ) + throw( IO_ERROR, PARSE_ERROR ) +{ + T token; + while( ( token = NextTok() ) != T_RIGHT ) + { + if( token == T_EOF) + break; + + switch( token ) + { + case T_LEFT: + break; + + case T_linewidth: + WORKSHEET_DATAITEM::m_DefaultLineWidth = parseDouble(); + NeedRIGHT(); + break; + + case T_textsize: + WORKSHEET_DATAITEM::m_DefaultTextSize.x = parseDouble(); + WORKSHEET_DATAITEM::m_DefaultTextSize.y = parseDouble(); + NeedRIGHT(); + break; + + case T_textlinewidth: + WORKSHEET_DATAITEM::m_DefaultTextThickness = parseDouble(); + NeedRIGHT(); + break; + + case T_left_margin: + aLayout->SetLeftMargin( parseDouble() ); + NeedRIGHT(); + break; + + case T_right_margin: + aLayout->SetRightMargin( parseDouble() ); + NeedRIGHT(); + break; + + case T_top_margin: + aLayout->SetTopMargin( parseDouble() ); + NeedRIGHT(); + break; + + case T_bottom_margin: + aLayout->SetBottomMargin( parseDouble() ); + NeedRIGHT(); + break; + + default: + Unexpected( CurText() ); + break; + } + } +} + +void PAGE_LAYOUT_READER_PARSER::parsePolygon( WORKSHEET_DATAITEM_POLYPOLYGON * aItem ) + throw( IO_ERROR, PARSE_ERROR ) +{ + T token; + + while( ( token = NextTok() ) != T_RIGHT ) + { + if( token == T_EOF) + break; + + if( token == T_LEFT ) + token = NextTok(); + + switch( token ) + { + case T_comment: + NeedSYMBOLorNUMBER(); + aItem->m_Info = FromUTF8(); + NeedRIGHT(); + break; + + case T_pos: + parseCoordinate( aItem->m_Pos ); + break; + + case T_name: + NeedSYMBOLorNUMBER(); + aItem->m_Name = FromUTF8(); + NeedRIGHT(); + break; + + case T_option: + readOption( aItem ); + break; + + case T_pts: + parsePolyOutline( aItem ); + aItem->CloseContour(); + break; + + case T_rotate: + aItem->m_Orient = parseDouble(); + NeedRIGHT(); + break; + + case T_repeat: + aItem->m_RepeatCount = parseInt( -1, 100 ); + NeedRIGHT(); + break; + + case T_incrx: + aItem->m_IncrementVector.x = parseDouble(); + NeedRIGHT(); + break; + + case T_incry: + aItem->m_IncrementVector.y = parseDouble(); + NeedRIGHT(); + break; + + case T_linewidth: + aItem->m_LineWidth = parseDouble(); + NeedRIGHT(); + break; + + default: + Unexpected( CurText() ); + break; + } + } + + aItem->SetBoundingBox(); +} + +void PAGE_LAYOUT_READER_PARSER::parsePolyOutline( WORKSHEET_DATAITEM_POLYPOLYGON * aItem ) + throw( IO_ERROR, PARSE_ERROR ) +{ + DPOINT corner; + T token; + + while( ( token = NextTok() ) != T_RIGHT ) + { + if( token == T_EOF) + break; + + if( token == T_LEFT ) + token = NextTok(); + + switch( token ) + { + case T_xy: + corner.x = parseDouble(); + corner.y = parseDouble(); + aItem->AppendCorner( corner ); + NeedRIGHT(); + break; + + default: + Unexpected( CurText() ); + break; + } + } +} + +#include <wx/mstream.h> +void PAGE_LAYOUT_READER_PARSER::parseBitmap( WORKSHEET_DATAITEM_BITMAP * aItem ) + throw( IO_ERROR, PARSE_ERROR ) +{ + T token; + BITMAP_BASE* image = new BITMAP_BASE; + aItem->m_ImageBitmap = image; + + while( ( token = NextTok() ) != T_RIGHT ) + { + if( token == T_EOF) + break; + + if( token == T_LEFT ) + token = NextTok(); + + switch( token ) + { + case T_name: + NeedSYMBOLorNUMBER(); + aItem->m_Name = FromUTF8(); + NeedRIGHT(); + break; + + case T_pos: + parseCoordinate( aItem->m_Pos ); + break; + + case T_repeat: + aItem->m_RepeatCount = parseInt( -1, 100 ); + NeedRIGHT(); + break; + + case T_incrx: + aItem->m_IncrementVector.x = parseDouble(); + NeedRIGHT(); + break; + + case T_incry: + aItem->m_IncrementVector.y = parseDouble(); + NeedRIGHT(); + break; + + case T_linewidth: + aItem->m_LineWidth = parseDouble(); + NeedRIGHT(); + break; + + case T_scale: + aItem->m_ImageBitmap->m_Scale = parseDouble(); + NeedRIGHT(); + break; + + case T_pngdata: + readPngdata( aItem ); + break; + + case T_option: + readOption( aItem ); + break; + + default: + Unexpected( CurText() ); + break; + } + } +} + +void PAGE_LAYOUT_READER_PARSER::readPngdata( WORKSHEET_DATAITEM_BITMAP * aItem ) + throw( IO_ERROR, PARSE_ERROR ) +{ + std::string tmp; + T token; + + while( ( token = NextTok() ) != T_RIGHT ) + { + if( token == T_EOF) + break; + + if( token == T_LEFT ) + token = NextTok(); + + switch( token ) + { + case T_data: + NeedSYMBOLorNUMBER(); + tmp += CurStr(); + tmp += "\n"; + NeedRIGHT(); + break; + + default: + Unexpected( CurText() ); + break; + } + } + + tmp += "EndData"; + + wxString msg; + STRING_LINE_READER reader( tmp, wxT("Png kicad_wks data") ); + if( ! aItem->m_ImageBitmap->LoadData( reader, msg ) ) + { + wxLogMessage(msg); + } +} + + +void PAGE_LAYOUT_READER_PARSER::readOption( WORKSHEET_DATAITEM * aItem ) + throw( IO_ERROR, PARSE_ERROR ) +{ + T token; + + while( ( token = NextTok() ) != T_RIGHT ) + { + if( token == T_EOF) + break; + + switch( token ) + { + case T_page1only: + aItem->SetPage1Option( 1 ); + break; + + case T_notonpage1: + aItem->SetPage1Option( -1 ); + break; + + default: + Unexpected( CurText() ); + break; + } + } +} + + +void PAGE_LAYOUT_READER_PARSER::parseGraphic( WORKSHEET_DATAITEM * aItem ) + throw( IO_ERROR, PARSE_ERROR ) +{ + T token; + + while( ( token = NextTok() ) != T_RIGHT ) + { + if( token == T_EOF) + break; + + if( token == T_LEFT ) + token = NextTok(); + else + { + // If an other token than T_LEFT is read here, this is an error + // however, due to a old bug in kicad, the token T_end can be found + // without T_LEFT in a very few .wks files (perhaps only one in a demo). + // So this ugly hack disables the error detection. + if( token != T_end ) + Unexpected( CurText() ); + } + + switch( token ) + { + case T_comment: + NeedSYMBOLorNUMBER(); + aItem->m_Info = FromUTF8(); + NeedRIGHT(); + break; + + case T_option: + readOption( aItem ); + break; + + case T_name: + NeedSYMBOLorNUMBER(); + aItem->m_Name = FromUTF8(); + NeedRIGHT(); + break; + + case T_start: + parseCoordinate( aItem->m_Pos ); + break; + + case T_end: + parseCoordinate( aItem->m_End ); + break; + + case T_repeat: + aItem->m_RepeatCount = parseInt( -1, 100 ); + NeedRIGHT(); + break; + + case T_incrx: + aItem->m_IncrementVector.x = parseDouble(); + NeedRIGHT(); + break; + + case T_incry: + aItem->m_IncrementVector.y = parseDouble(); + NeedRIGHT(); + break; + + case T_linewidth: + aItem->m_LineWidth = parseDouble(); + NeedRIGHT(); + break; + + default: + Unexpected( CurText() ); + break; + } + } +} + + +void PAGE_LAYOUT_READER_PARSER::parseText( WORKSHEET_DATAITEM_TEXT* aItem ) + throw( IO_ERROR, PARSE_ERROR ) +{ + T token; + + while( ( token = NextTok() ) != T_RIGHT ) + { + if( token == T_EOF) + break; + + if( token == T_LEFT ) + token = NextTok(); + + switch( token ) + { + case T_comment: + NeedSYMBOLorNUMBER(); + aItem->m_Info = FromUTF8(); + NeedRIGHT(); + break; + + case T_option: + readOption( aItem ); + break; + + case T_name: + NeedSYMBOLorNUMBER(); + aItem->m_Name = FromUTF8(); + NeedRIGHT(); + break; + + case T_pos: + parseCoordinate( aItem->m_Pos ); + break; + + case T_repeat: + aItem->m_RepeatCount = parseInt( -1, 100 ); + NeedRIGHT(); + break; + + case T_incrx: + aItem->m_IncrementVector.x = parseDouble(); + NeedRIGHT(); + break; + + case T_incry: + aItem->m_IncrementVector.y = parseDouble(); + NeedRIGHT(); + break; + + case T_incrlabel: + aItem->m_IncrementLabel = parseInt(INT_MIN, INT_MAX); + NeedRIGHT(); + break; + + case T_maxlen: + aItem->m_BoundingBoxSize.x = parseDouble(); + NeedRIGHT(); + break; + + case T_maxheight: + aItem->m_BoundingBoxSize.y = parseDouble(); + NeedRIGHT(); + break; + + case T_font: + while( ( token = NextTok() ) != T_RIGHT ) + { + if( token == T_EOF) + break; + + switch( token ) + { + case T_LEFT: + break; + + case T_bold: + aItem->SetBold( true ); + break; + + case T_italic: + aItem->SetItalic( true ); + break; + + case T_size: + aItem->m_TextSize.x = parseDouble(); + aItem->m_TextSize.y = parseDouble(); + NeedRIGHT(); + break; + + case T_linewidth: + aItem->m_LineWidth = parseDouble(); + NeedRIGHT(); + break; + + default: + Unexpected( CurText() ); + break; + } + } + break; + + case T_justify: + while( ( token = NextTok() ) != T_RIGHT ) + { + if( token == T_EOF) + break; + + switch( token ) + { + case T_center: + aItem->m_Hjustify = GR_TEXT_HJUSTIFY_CENTER; + aItem->m_Vjustify = GR_TEXT_VJUSTIFY_CENTER; + break; + + case T_left: + aItem->m_Hjustify = GR_TEXT_HJUSTIFY_LEFT; + break; + + case T_right: + aItem->m_Hjustify = GR_TEXT_HJUSTIFY_RIGHT; + break; + + case T_top: + aItem->m_Vjustify = GR_TEXT_VJUSTIFY_TOP; + break; + + case T_bottom: + aItem->m_Vjustify = GR_TEXT_VJUSTIFY_BOTTOM; + break; + + default: + Unexpected( CurText() ); + break; + } + } + break; + + case T_rotate: + aItem->m_Orient = parseDouble(); + NeedRIGHT(); + break; + + default: + Unexpected( CurText() ); + break; + } + } +} + +// parse an expression like " 25 1 ltcorner)" +void PAGE_LAYOUT_READER_PARSER::parseCoordinate( POINT_COORD& aCoord) + throw( IO_ERROR, PARSE_ERROR ) +{ + T token; + + aCoord.m_Pos.x = parseDouble(); + aCoord.m_Pos.y = parseDouble(); + + while( ( token = NextTok() ) != T_RIGHT ) + { + switch( token ) + { + case T_ltcorner: + aCoord.m_Anchor = LT_CORNER; // left top corner + break; + + case T_lbcorner: + aCoord.m_Anchor = LB_CORNER; // left bottom corner + break; + + case T_rbcorner: + aCoord.m_Anchor = RB_CORNER; // right bottom corner + break; + + case T_rtcorner: + aCoord.m_Anchor = RT_CORNER; // right top corner + break; + + default: + Unexpected( CurText() ); + break; + } + } +} + +int PAGE_LAYOUT_READER_PARSER::parseInt( int aMin, int aMax ) +{ + T token = NextTok(); + + if( token != T_NUMBER ) + Expecting( T_NUMBER ); + + int val = atoi( CurText() ); + + if( val < aMin ) + val = aMin; + else if( val > aMax ) + val = aMax; + + return val; +} + + +double PAGE_LAYOUT_READER_PARSER::parseDouble() +{ + T token = NextTok(); + + if( token != T_NUMBER ) + Expecting( T_NUMBER ); + + double val = strtod( CurText(), NULL ); + + return val; +} + +// defaultPageLayout is the default page layout description +// using the S expr. +// see page_layout_default_shape.cpp +extern const char defaultPageLayout[]; + +void WORKSHEET_LAYOUT::SetDefaultLayout() +{ + ClearList(); + PAGE_LAYOUT_READER_PARSER lp_parser( defaultPageLayout, wxT( "default page" ) ); + + try + { + lp_parser.Parse( this ); + } + catch( const IO_ERROR& ioe ) + { + wxLogMessage( ioe.errorText ); + } +} + +/** + * Populates the list from a S expr description stored in a string + * @param aPageLayout = the S expr string + */ +void WORKSHEET_LAYOUT::SetPageLayout( const char* aPageLayout, bool Append ) +{ + if( ! Append ) + ClearList(); + + PAGE_LAYOUT_READER_PARSER lp_parser( aPageLayout, wxT( "Sexpr_string" ) ); + + try + { + lp_parser.Parse( this ); + } + catch( const IO_ERROR& ioe ) + { + wxLogMessage( ioe.errorText ); + } +} + +#include <wx/file.h> + +// SetLayout() try to load the aFullFileName custom layout file, +// if aFullFileName is empty, try the filename defined by the +// environment variable KICAD_WKSFILE (a *.kicad_wks filename). +// if does not exists, loads the default page layout. +void WORKSHEET_LAYOUT::SetPageLayout( const wxString& aFullFileName, bool Append ) +{ + wxString fullFileName = aFullFileName; + + if( !Append ) + { + if( fullFileName.IsEmpty() ) + wxGetEnv( wxT( "KICAD_WKSFILE" ), &fullFileName ); + + if( fullFileName.IsEmpty() || !wxFileExists( fullFileName ) ) + { + #if 0 + if( !fullFileName.IsEmpty() ) + { + wxLogMessage( wxT("Page layout file <%s> not found"), + fullFileName.GetData() ); + } + #endif + SetDefaultLayout(); + return; + } + } + + wxFile wksFile( fullFileName ); + + if( ! wksFile.IsOpened() ) + { + if( !Append ) + SetDefaultLayout(); + return; + } + + int filelen = wksFile.Length(); + char * buffer = new char[filelen+10]; + + if( wksFile.Read( buffer, filelen ) != filelen ) + wxLogMessage( _("The file <%s> was not fully read"), + fullFileName.GetData() ); + else + { + buffer[filelen]=0; + + if( ! Append ) + ClearList(); + + PAGE_LAYOUT_READER_PARSER pl_parser( buffer, fullFileName ); + + try + { + pl_parser.Parse( this ); + } + catch( const IO_ERROR& ioe ) + { + wxLogMessage( ioe.errorText ); + } + } + + delete[] buffer; +} + diff --git a/common/page_layout/page_layout_reader.keywords b/common/page_layout/page_layout_reader.keywords new file mode 100644 index 0000000..625dd59 --- /dev/null +++ b/common/page_layout/page_layout_reader.keywords @@ -0,0 +1,48 @@ +page_layout +setup +left_margin +right_margin +top_margin +bottom_margin +linewidth +textlinewidth +textsize +comment +option +page1only +notonpage1 +line +rect +polygon +bitmap +tbtext +ltcorner +lbcorner +rbcorner +rtcorner +name +pos +start +end +scale +pngdata +data +pts +xy +maxlen +maxheight +font +bold +italic +size +justify +left +center +right +top +bottom +rotate +repeat +incrx +incry +incrlabel diff --git a/common/page_layout/title_block_shapes.cpp b/common/page_layout/title_block_shapes.cpp new file mode 100644 index 0000000..c7a8385 --- /dev/null +++ b/common/page_layout/title_block_shapes.cpp @@ -0,0 +1,258 @@ +/** + * @file title_block_shapes.cpp + * @brief description of graphic items and texts to build a title block + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 1992-2013 Jean-Pierre Charras <jp.charras at wanadoo.fr>. + * Copyright (C) 1992-2013 KiCad Developers, see change_log.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 + */ + + +/* + * the class WORKSHEET_DATAITEM (and WORKSHEET_DATAITEM_TEXT) defines + * a basic shape of a page layout ( frame references and title block ) + * Basic shapes are line, rect and texts + * the WORKSHEET_DATAITEM coordinates units is the mm, and are relative to + * one of 4 page corners. + * + * These items cannot be drawn or plot "as this". they should be converted + * to a "draw list" (WS_DRAW_ITEM_BASE and derived items) + + * The list of these items is stored in a WORKSHEET_LAYOUT instance. + * + * When building the draw list: + * the WORKSHEET_LAYOUT is used to create a WS_DRAW_ITEM_LIST + * coordinates are converted to draw/plot coordinates. + * texts are expanded if they contain format symbols. + * Items with m_RepeatCount > 1 are created m_RepeatCount times + * + * the WORKSHEET_LAYOUT is created only once. + * the WS_DRAW_ITEM_LIST is created each time the page layout is plot/drawn + * + * the WORKSHEET_LAYOUT instance is created from a S expression which + * describes the page layout (can be the default page layout or a custom file). + */ + +#include <fctsys.h> +#include <drawtxt.h> +#include <class_page_info.h> +#include <worksheet.h> +#include <class_title_block.h> +#include <worksheet_shape_builder.h> +#include <class_worksheet_dataitem.h> + + +void WS_DRAW_ITEM_LIST::BuildWorkSheetGraphicList( + const PAGE_INFO& aPageInfo, + const TITLE_BLOCK& aTitleBlock, + EDA_COLOR_T aColor, EDA_COLOR_T aAltColor ) +{ + WORKSHEET_LAYOUT& pglayout = WORKSHEET_LAYOUT::GetTheInstance(); + + #define milsTomm (25.4/1000) + + m_titleBlock = &aTitleBlock; + m_paperFormat = &aPageInfo.GetType(); + + wxPoint LTmargin( Mm2mils( pglayout.GetLeftMargin() ), + Mm2mils( pglayout.GetTopMargin() ) ); + wxPoint RBmargin( Mm2mils( pglayout.GetRightMargin() ), + Mm2mils( pglayout.GetBottomMargin() ) ); + + SetMargins( LTmargin, RBmargin ); + SetPageSize( aPageInfo.GetSizeMils() ); + + // Build the basic layout shape, if the layout list is empty + if( pglayout.GetCount() == 0 && !pglayout.VoidListAllowed() ) + pglayout.SetPageLayout(); + + WORKSHEET_DATAITEM::m_WSunits2Iu = m_milsToIu / milsTomm; + WORKSHEET_DATAITEM::m_Color = aColor; // the default color to draw items + WORKSHEET_DATAITEM::m_AltColor = aAltColor; // an alternate color to draw items + + // Left top corner position + DPOINT lt_corner; + lt_corner.x = pglayout.GetLeftMargin(); + lt_corner.y = pglayout.GetTopMargin(); + WORKSHEET_DATAITEM::m_LT_Corner = lt_corner; + + // Right bottom corner position + DPOINT rb_corner; + rb_corner.x = (m_pageSize.x*milsTomm) - pglayout.GetRightMargin(); + rb_corner.y = (m_pageSize.y*milsTomm) - pglayout.GetBottomMargin(); + WORKSHEET_DATAITEM::m_RB_Corner = rb_corner; + + WS_DRAW_ITEM_TEXT* gtext; + int pensize; + + for( unsigned ii = 0; ; ii++ ) + { + WORKSHEET_DATAITEM* wsItem = pglayout.GetItem( ii ); + + if( wsItem == NULL ) + break; + + // Generate it only if the page option allows this + if( wsItem->GetPage1Option() < 0 // Not on page 1 + && m_sheetNumber <= 1 ) + continue; + + if( wsItem->GetPage1Option() > 0 // Only on page 1 + && m_sheetNumber > 1 ) + continue; + + EDA_COLOR_T color = wsItem->GetItemColor(); + + pensize = wsItem->GetPenSizeUi(); + + switch( wsItem->GetType() ) + { + case WORKSHEET_DATAITEM::WS_TEXT: + { + WORKSHEET_DATAITEM_TEXT * wsText = (WORKSHEET_DATAITEM_TEXT*)wsItem; + bool multilines = false; + + if( wsText->m_SpecialMode ) + wsText->m_FullText = wsText->m_TextBase; + else + { + wsText->m_FullText = BuildFullText( wsText->m_TextBase ); + multilines = wsText->ReplaceAntiSlashSequence(); + } + + if( wsText->m_FullText.IsEmpty() ) + break; + + if( pensize == 0 ) + pensize = m_penSize; + + wsText->SetConstrainedTextSize(); + wxSize textsize; + + textsize.x = KiROUND( wsText->m_ConstrainedTextSize.x + * WORKSHEET_DATAITEM::m_WSunits2Iu ); + textsize.y = KiROUND( wsText->m_ConstrainedTextSize.y + * WORKSHEET_DATAITEM::m_WSunits2Iu ); + + if( wsText->IsBold()) + pensize = GetPenSizeForBold( std::min( textsize.x, textsize.y ) ); + + for( int jj = 0; jj < wsText->m_RepeatCount; jj++) + { + if( jj && ! wsText->IsInsidePage( jj ) ) + continue; + + Append( gtext = new WS_DRAW_ITEM_TEXT( wsText, wsText->m_FullText, + wsText->GetStartPosUi( jj ), + textsize, + pensize, color, + wsText->IsItalic(), + wsText->IsBold() ) ); + gtext->SetMultilineAllowed( multilines ); + wsText->TransfertSetupToGraphicText( gtext ); + + // Increment label for the next text + // (has no meaning for multiline texts) + if( wsText->m_RepeatCount > 1 && !multilines ) + wsText->IncrementLabel( (jj+1)*wsText->m_IncrementLabel); + } + } + break; + + case WORKSHEET_DATAITEM::WS_SEGMENT: + if( pensize == 0 ) + pensize = m_penSize; + + for( int jj = 0; jj < wsItem->m_RepeatCount; jj++ ) + { + if( jj && ! wsItem->IsInsidePage( jj ) ) + continue; + Append( new WS_DRAW_ITEM_LINE( wsItem, wsItem->GetStartPosUi( jj ), + wsItem->GetEndPosUi( jj ), + pensize, color ) ); + } + break; + + case WORKSHEET_DATAITEM::WS_RECT: + if( pensize == 0 ) + pensize = m_penSize; + + for( int jj = 0; jj < wsItem->m_RepeatCount; jj++ ) + { + if( jj && ! wsItem->IsInsidePage( jj ) ) + break; + + Append( new WS_DRAW_ITEM_RECT( wsItem, wsItem->GetStartPosUi( jj ), + wsItem->GetEndPosUi( jj ), + pensize, color ) ); + } + break; + + case WORKSHEET_DATAITEM::WS_POLYPOLYGON: + { + WORKSHEET_DATAITEM_POLYPOLYGON * wspoly = + (WORKSHEET_DATAITEM_POLYPOLYGON*) wsItem; + for( int jj = 0; jj < wsItem->m_RepeatCount; jj++ ) + { + if( jj && ! wsItem->IsInsidePage( jj ) ) + continue; + + for( int kk = 0; kk < wspoly->GetPolyCount(); kk++ ) + { + const bool fill = true; + WS_DRAW_ITEM_POLYGON* poly = new WS_DRAW_ITEM_POLYGON( wspoly, + wspoly->GetStartPosUi( jj ), + fill, pensize, color ); + Append( poly ); + + // Create polygon outline + unsigned ist = wspoly->GetPolyIndexStart( kk ); + unsigned iend = wspoly->GetPolyIndexEnd( kk ); + while( ist <= iend ) + poly->m_Corners.push_back( + wspoly->GetCornerPositionUi( ist++, jj ) ); + + } + } + } + break; + + case WORKSHEET_DATAITEM::WS_BITMAP: + + ((WORKSHEET_DATAITEM_BITMAP*)wsItem)->SetPixelScaleFactor(); + + for( int jj = 0; jj < wsItem->m_RepeatCount; jj++ ) + { + if( jj && ! wsItem->IsInsidePage( jj ) ) + continue; + + Append( new WS_DRAW_ITEM_BITMAP( wsItem, + wsItem->GetStartPosUi( jj ) ) ); + } + break; + + } + } +} + diff --git a/common/painter.cpp b/common/painter.cpp new file mode 100644 index 0000000..0d6b1f0 --- /dev/null +++ b/common/painter.cpp @@ -0,0 +1,76 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 <painter.h> +#include <gal/graphics_abstraction_layer.h> + +using namespace KIGFX; + +RENDER_SETTINGS::RENDER_SETTINGS() +{ + // Set the default initial values + m_highlightFactor = 0.5; + m_selectFactor = 0.5; + m_layerOpacity = 0.8; + m_highlightEnabled = false; + m_hiContrastEnabled = false; + m_hiContrastFactor = 0.2; + m_highlightNetcode = -1; + m_outlineWidth = 1; + m_worksheetLineWidth = 100000; + + // Store the predefined colors used in KiCad in format used by GAL + for( int i = 0; i < NBCOLORS; i++ ) + { + m_legacyColorMap[g_ColorRefs[i].m_Numcolor] = COLOR4D( (double) g_ColorRefs[i].m_Red / 255.0, + (double) g_ColorRefs[i].m_Green / 255.0, + (double) g_ColorRefs[i].m_Blue / 255.0, + m_layerOpacity ); + } +} + + +RENDER_SETTINGS::~RENDER_SETTINGS() +{ +} + + +void RENDER_SETTINGS::update() +{ + m_hiContrastColor = COLOR4D( m_hiContrastFactor, m_hiContrastFactor, m_hiContrastFactor, + m_layerOpacity ); +} + + +PAINTER::PAINTER( GAL* aGal ) : + m_gal( aGal ), m_brightenedColor( 0.0, 1.0, 0.0, 0.9 ) +{ +} + + +PAINTER::~PAINTER() +{ +} diff --git a/common/pcb.keywords b/common/pcb.keywords new file mode 100644 index 0000000..0126f56 --- /dev/null +++ b/common/pcb.keywords @@ -0,0 +1,207 @@ +# +# This program source code file is part of KiCad, a free EDA CAD application. +# +# Copyright (C) 2012 CERN. +# +# 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 +# + +# These are the keywords for the Pcbnew s-expression file format. + +add_net +allowed +angle +arc +arc_segments +area +arrow1a +arrow1b +arrow2a +arrow2b +at +attr +autoplace_cost90 +autoplace_cost180 +aux_axis_origin +blind +blind_buried_vias_allowed +bold +bottom +center +chamfer +circle +clearance +comment +company +connect +connect_pads +copperpour +crossbar +date +descr +die_length +dimension +drawings +drill +edge +edge_width +effects +end +feature1 +feature2 +fill +fill_segments +filled_polygon +fillet +font +fp_arc +fp_circle +fp_curve +fp_line +fp_poly +fp_text +full +general +grid_origin +gr_arc +gr_circle +gr_curve +gr_line +gr_poly +gr_text +hatch +hide +italic +justify +keepout +kicad_pcb +last_trace_width +layer +layers +left +links +locked +micro +min_thickness +mirror +mod_edge_width +mod_text_size +mod_text_width +mode +model +module +net +net_class +net_name +nets +no +no_connects +none +not_allowed +np_thru_hole +offset +oval +pad +pads +pad_drill +pad_size +pad_to_mask_clearance +solder_mask_min_width +pad_to_paste_clearance +pad_to_paste_clearance_ratio +page +path +pcb_text_size +pcb_text_width +pcbplotparams +placed +plus +polygon +portrait +priority +pts +radius +rev +rect +rect_delta +reference +right +rotate +scale +segment +segment_width +setup +size +smd +smoothing +solder_mask_margin +solder_paste_margin +solder_paste_margin_ratio +solder_paste_ratio +start +status +tags +target +title +title_block +tedit +thermal_width +thermal_gap +thermal_bridge_width +thickness +top +trace_width +tracks +trace_min +trace_clearance +trapezoid +thru +thru_hole +thru_hole_only +tstamp +user +user_trace_width +user_via +uvia_dia +uvia_drill +uvia_min_drill +uvia_min_size +uvia_size +uvias_allowed +value +version +via +vias +via_dia +via_drill +via_min_drill +via_min_size +via_size +virtual +visible_elements +width +x +xy +xyz +yes +zone +zone_45_only +zone_clearance +zone_connect +zone_type +zones diff --git a/common/pcb_plot_params.keywords b/common/pcb_plot_params.keywords new file mode 100644 index 0000000..c64f70b --- /dev/null +++ b/common/pcb_plot_params.keywords @@ -0,0 +1,30 @@ +drillshape +excludeedgelayer +false +gerberprecision +hpglpendiameter +hpglpennumber +hpglpenoverlay +hpglpenspeed +layerselection +linewidth +mirror +mode +outputdirectory +outputformat +padsonsilk +pcbplotparams +plotframeref +plotinvisibletext +plotreference +plotvalue +psa4output +pscolor +psnegative +scaleselection +subtractmaskfromsilk +true +useauxorigin +usegerberextensions +viasonmask +usegerberattributes diff --git a/common/pcbcommon.cpp b/common/pcbcommon.cpp new file mode 100644 index 0000000..6147641 --- /dev/null +++ b/common/pcbcommon.cpp @@ -0,0 +1,74 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2008 Wayne Stambaugh <stambaughw@verizon.net> + * Copyright (C) 1992-2008 KiCad Developers, see change_log.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 + */ + +/* + * This file contains some functions used in the PCB + * applications Pcbnew and CvPcb. + */ + +#include <fctsys.h> +#include <pgm_base.h> +#include <kiface_i.h> + +#include <pcbcommon.h> +#include <class_board.h> +#include <3d_viewer.h> + + +wxString LayerMaskDescribe( const BOARD *aBoard, LSET aMask ) +{ + // Try the single or no- layer case (easy) + LAYER_ID layer = aMask.ExtractLayer(); + + switch( (int) layer ) + { + case UNSELECTED_LAYER: + return _( "No layers" ); + + case UNDEFINED_LAYER: + break; + + default: + return aBoard->GetLayerName( layer ); + } + + // Try to be smart and useful, starting with outer copper + // (which are more important than internal ones) + wxString layerInfo; + + if( aMask[F_Cu] ) + AccumulateDescription( layerInfo, aBoard->GetLayerName( F_Cu ) ); + + if( aMask[B_Cu] ) + AccumulateDescription( layerInfo, aBoard->GetLayerName( B_Cu ) ); + + if( ( aMask & LSET::InternalCuMask() ).any() ) + AccumulateDescription( layerInfo, _("Internal" ) ); + + if( ( aMask & LSET::AllNonCuMask() ).any() ) + AccumulateDescription( layerInfo, _("Non-copper" ) ); + + return layerInfo; +} + diff --git a/common/pgm_base.cpp b/common/pgm_base.cpp new file mode 100644 index 0000000..0728633 --- /dev/null +++ b/common/pgm_base.cpp @@ -0,0 +1,883 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2004-2015 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com + * Copyright (C) 2008-2015 Wayne Stambaugh <stambaughw@verizon.net> + * Copyright (C) 1992-2015 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 + */ + +/** + * @file pgm_base.cpp + * + * @brief For the main application: init functions, and language selection + * (locale handling) + */ + +#include <fctsys.h> +#include <wx/html/htmlwin.h> +#include <wx/fs_zip.h> +#include <wx/dir.h> +#include <wx/filename.h> +#include <wx/snglinst.h> +#include <wx/stdpaths.h> +#include <wx/sysopt.h> +#include <wx/richmsgdlg.h> + +#include <pgm_base.h> +#include <wxstruct.h> +#include <macros.h> +#include <config_params.h> +#include <id.h> +#include <build_version.h> +#include <hotkeys_basic.h> +#include <online_help.h> +#include <gestfich.h> +#include <menus_helpers.h> +#include <confirm.h> +#include <dialog_env_var_config.h> + + +#define KICAD_COMMON wxT( "kicad_common" ) + +// some key strings used to store parameters in KICAD_COMMON + +const wxChar PGM_BASE::workingDirKey[] = wxT( "WorkingDir" ); // public + +static const wxChar languageCfgKey[] = wxT( "LanguageID" ); +static const wxChar pathEnvVariables[] = wxT( "EnvironmentVariables" ); +static const wxChar showEnvVarWarningDialog[] = wxT( "ShowEnvVarWarningDialog" ); +static const wxChar traceEnvVars[] = wxT( "KIENVVARS" ); + + +/** + * A small class to handle the list of existing translations. + * The locale translation is automatic. + * The selection of languages is mainly for maintainer's convenience + * To add a support to a new translation: + * create a new icon (flag of the country) (see Lang_Fr.xpm as an example) + * add a new item to s_Languages[]. + */ +struct LANGUAGE_DESCR +{ + /// wxWidgets locale identifier (See wxWidgets doc) + int m_WX_Lang_Identifier; + + /// KiCad identifier used in menu selection (See id.h) + int m_KI_Lang_Identifier; + + /// The menu language icons + BITMAP_DEF m_Lang_Icon; + + /// Labels used in menus + wxString m_Lang_Label; + + /// Set to true if the m_Lang_Label must not be translated + bool m_DoNotTranslate; +}; + + +/** + * Variable s_Languages + * Note: because this list is not created on the fly, wxTranslation + * must be called when a language name must be displayed after translation. + * Do not change this behavior, because m_Lang_Label is also used as key in config + */ +static LANGUAGE_DESCR s_Languages[] = +{ + // Default language + { + wxLANGUAGE_DEFAULT, + ID_LANGUAGE_DEFAULT, + lang_def_xpm, + _( "Default" ) + }, + + // English language + { + wxLANGUAGE_ENGLISH, + ID_LANGUAGE_ENGLISH, + lang_en_xpm, + wxT( "English" ), + true + }, + + // French language + { + wxLANGUAGE_FRENCH, + ID_LANGUAGE_FRENCH, + lang_fr_xpm, + _( "French" ) + }, + + // Finnish language + { + wxLANGUAGE_FINNISH, + ID_LANGUAGE_FINNISH, + lang_fi_xpm, + _( "Finnish" ) + }, + + // Spanish language + { + wxLANGUAGE_SPANISH, + ID_LANGUAGE_SPANISH, + lang_es_xpm, + _( "Spanish" ) + }, + + // Portuguese language + { + wxLANGUAGE_PORTUGUESE, + ID_LANGUAGE_PORTUGUESE, + lang_pt_xpm, + _( "Portuguese" ) + }, + + // Italian language + { + wxLANGUAGE_ITALIAN, + ID_LANGUAGE_ITALIAN, + lang_it_xpm, + _( "Italian" ) + }, + + // German language + { + wxLANGUAGE_GERMAN, + ID_LANGUAGE_GERMAN, + lang_de_xpm, + _( "German" ) + }, + + // Greek language + { + wxLANGUAGE_GREEK, + ID_LANGUAGE_GREEK, + lang_gr_xpm, + _( "Greek" ) + }, + + // Slovenian language + { + wxLANGUAGE_SLOVENIAN, + ID_LANGUAGE_SLOVENIAN, + lang_sl_xpm, + _( "Slovenian" ) + }, + + // Hungarian language + { + wxLANGUAGE_HUNGARIAN, + ID_LANGUAGE_HUNGARIAN, + lang_hu_xpm, + _( "Hungarian" ) + }, + + // Polish language + { + wxLANGUAGE_POLISH, + ID_LANGUAGE_POLISH, + lang_pl_xpm, + _( "Polish" ) + }, + + // Czech language + { + wxLANGUAGE_CZECH, + ID_LANGUAGE_CZECH, + lang_cs_xpm, + _( "Czech" ) + }, + + // Russian language + { + wxLANGUAGE_RUSSIAN, + ID_LANGUAGE_RUSSIAN, + lang_ru_xpm, + _( "Russian" ) + }, + + // Korean language + { + wxLANGUAGE_KOREAN, + ID_LANGUAGE_KOREAN, + lang_ko_xpm, + _( "Korean" ) + }, + + // Chinese simplified + { + wxLANGUAGE_CHINESE_SIMPLIFIED, + ID_LANGUAGE_CHINESE_SIMPLIFIED, + lang_chinese_xpm, + _( "Chinese simplified" ) + }, + + // Catalan language + { + wxLANGUAGE_CATALAN, + ID_LANGUAGE_CATALAN, + lang_catalan_xpm, + _( "Catalan" ) + }, + + // Dutch language + { + wxLANGUAGE_DUTCH, + ID_LANGUAGE_DUTCH, + lang_nl_xpm, + _( "Dutch" ) + }, + + // Japanese language + { + wxLANGUAGE_JAPANESE, + ID_LANGUAGE_JAPANESE, + lang_jp_xpm, + _( "Japanese" ) + }, + + // Bulgarian language + { + wxLANGUAGE_BULGARIAN, + ID_LANGUAGE_BULGARIAN, + lang_bg_xpm, + _( "Bulgarian" ) + } +}; + + +PGM_BASE::PGM_BASE() +{ + m_pgm_checker = NULL; + m_locale = NULL; + m_common_settings = NULL; + + m_wx_app = NULL; + m_show_env_var_dialog = true; + + setLanguageId( wxLANGUAGE_DEFAULT ); + + ForceSystemPdfBrowser( false ); +} + + +PGM_BASE::~PGM_BASE() +{ + destroy(); +} + + +void PGM_BASE::destroy() +{ + // unlike a normal destructor, this is designed to be called more than once safely: + + delete m_common_settings; + m_common_settings = 0; + + delete m_pgm_checker; + m_pgm_checker = 0; + + delete m_locale; + m_locale = 0; +} + + +void PGM_BASE::SetEditorName( const wxString& aFileName ) +{ + m_editor_name = aFileName; + wxASSERT( m_common_settings ); + m_common_settings->Write( wxT( "Editor" ), aFileName ); +} + + +const wxString& PGM_BASE::GetEditorName( bool aCanShowFileChooser ) +{ + wxString editorname = m_editor_name; + + if( !editorname ) + { + if( !wxGetEnv( wxT( "EDITOR" ), &editorname ) ) + { + // If there is no EDITOR variable set, try the desktop default +#ifdef __WXMAC__ + editorname = "/usr/bin/open"; +#elif __WXX11__ + editorname = "/usr/bin/xdg-open"; +#endif + } + } + + // If we still don't have an editor name show a dialog asking the user to select one + if( !editorname && aCanShowFileChooser ) + { + DisplayInfoMessage( NULL, + _( "No default editor found, you must choose it" ) ); + + editorname = AskUserForPreferredEditor(); + } + + // If we finally have a new editor name request it to be copied to m_editor_name and + // saved to the preferences file. + if( !editorname.IsEmpty() ) + SetEditorName( editorname ); + + // m_editor_name already has the same value that editorname, or empty if no editor was + // found/chosen. + return m_editor_name; +} + + +const wxString PGM_BASE::AskUserForPreferredEditor( const wxString& aDefaultEditor ) +{ + // Create a mask representing the executable files in the current platform +#ifdef __WINDOWS__ + wxString mask( _( "Executable file (*.exe)|*.exe" ) ); +#else + wxString mask( _( "Executable file (*)|*" ) ); +#endif + + // Extract the path, name and extension from the default editor (even if the editor's + // name was empty, this method will succeed and return empty strings). + wxString path, name, ext; + wxFileName::SplitPath( aDefaultEditor, &path, &name, &ext ); + + // Show the modal editor and return the file chosen (may be empty if the user cancels + // the dialog). + return EDA_FILE_SELECTOR( _( "Select Preferred Editor" ), path, + name, ext, mask, + NULL, wxFD_OPEN | wxFD_FILE_MUST_EXIST, + true ); +} + + +bool PGM_BASE::initPgm() +{ + wxFileName pgm_name( App().argv[0] ); + + wxConfigBase::DontCreateOnDemand(); + + wxInitAllImageHandlers(); + + m_pgm_checker = new wxSingleInstanceChecker( pgm_name.GetName().Lower() + wxT( "-" ) + + wxGetUserId(), GetKicadLockFilePath() ); + + if( m_pgm_checker->IsAnotherRunning() ) + { + wxString quiz = wxString::Format( + _( "%s is already running, Continue?" ), + GetChars( pgm_name.GetName() ) + ); + + if( !IsOK( NULL, quiz ) ) + return false; + } + + // Init KiCad environment + // the environment variable KICAD (if exists) gives the kicad path: + // something like set KICAD=d:\kicad + bool isDefined = wxGetEnv( wxT( "KICAD" ), &m_kicad_env ); + + if( isDefined ) // ensure m_kicad_env ends by "/" + { + m_kicad_env.Replace( WIN_STRING_DIR_SEP, UNIX_STRING_DIR_SEP ); + + if( !m_kicad_env.IsEmpty() && m_kicad_env.Last() != '/' ) + m_kicad_env += UNIX_STRING_DIR_SEP; + } + + // Init parameters for configuration + App().SetVendorName( wxT( "KiCad" ) ); + App().SetAppName( pgm_name.GetName().Lower() ); + + // Install some image handlers, mainly for help + if( wxImage::FindHandler( wxBITMAP_TYPE_PNG ) == NULL ) + wxImage::AddHandler( new wxPNGHandler ); + + if( wxImage::FindHandler( wxBITMAP_TYPE_GIF ) == NULL ) + wxImage::AddHandler( new wxGIFHandler ); + + if( wxImage::FindHandler( wxBITMAP_TYPE_JPEG ) == NULL ) + wxImage::AddHandler( new wxJPEGHandler ); + + wxFileSystem::AddHandler( new wxZipFSHandler ); + + // Analyze the command line & initialize the binary path + setExecutablePath(); + + SetLanguagePath(); + + // OS specific instantiation of wxConfigBase derivative: + m_common_settings = GetNewConfig( KICAD_COMMON ); + + // Only define the default environment variable if they haven't been set in the + // .kicad_common configuration file. + if( m_common_settings && !m_common_settings->HasGroup( pathEnvVariables ) ) + { + wxString envVarName = wxT( "KIGITHUB" ); + ENV_VAR_ITEM envVarItem; + wxString envValue; + wxFileName tmpFileName; + + envVarItem.SetValue( wxString( wxT( "https://github.com/KiCad" ) ) ); + envVarItem.SetDefinedExternally( wxGetEnv( envVarName, NULL ) ); + m_local_env_vars[ envVarName ] = envVarItem; + + wxFileName baseSharePath; + baseSharePath.AssignDir( wxString( wxT( DEFAULT_INSTALL_PATH ) ) ); + +#if !defined( __WXMAC__ ) + baseSharePath.AppendDir( wxT( "share" ) ); + baseSharePath.AppendDir( wxT( "kicad" ) ); +#endif + + // KISYSMOD + envVarName = wxT( "KISYSMOD" ); + if( wxGetEnv( envVarName, &envValue ) == true && !envValue.IsEmpty() ) + { + tmpFileName.AssignDir( envValue ); + envVarItem.SetDefinedExternally( true ); + } + else + { + tmpFileName = baseSharePath; + tmpFileName.AppendDir( wxT( "modules" ) ); + envVarItem.SetDefinedExternally( false ); + } + envVarItem.SetValue( tmpFileName.GetFullPath() ); + m_local_env_vars[ envVarName ] = envVarItem; + + // KISYS3DMOD + envVarName = wxT( "KISYS3DMOD" ); + if( wxGetEnv( envVarName, &envValue ) == true && !envValue.IsEmpty() ) + { + tmpFileName.AssignDir( envValue ); + envVarItem.SetDefinedExternally( true ); + } + else + { + tmpFileName.AppendDir( wxT( "packages3d" ) ); + envVarItem.SetDefinedExternally( false ); + } + envVarItem.SetValue( tmpFileName.GetFullPath() ); + m_local_env_vars[ envVarName ] = envVarItem; + + // KICAD_PTEMPLATES + envVarName = wxT( "KICAD_PTEMPLATES" ); + if( wxGetEnv( envVarName, &envValue ) == true && !envValue.IsEmpty() ) + { + tmpFileName.AssignDir( envValue ); + envVarItem.SetDefinedExternally( true ); + } + else + { + tmpFileName = baseSharePath; + tmpFileName.AppendDir( wxT( "template" ) ); + envVarItem.SetDefinedExternally( false ); + } + envVarItem.SetValue( tmpFileName.GetFullPath() ); + m_local_env_vars[ envVarName ] = envVarItem; + } + + ReadPdfBrowserInfos(); // needs m_common_settings + + // Init user language *before* calling loadCommonSettings, because + // env vars could be incorrectly initialized on Linux + // (if the value contains some non ASCII7 chars, the env var is not initialized) + SetLanguage( true ); + + loadCommonSettings(); + +#ifdef __WXMAC__ + // Always show filters on Open dialog to be able to choose plugin + wxSystemOptions::SetOption( wxOSX_FILEDIALOG_ALWAYS_SHOW_TYPES, 1 ); +#endif + + return true; +} + + +bool PGM_BASE::setExecutablePath() +{ + m_bin_dir = wxStandardPaths::Get().GetExecutablePath(); + +#ifdef __WXMAC__ + // On OSX Pgm().GetExecutablePath() will always point to main + // bundle directory, e.g., /Applications/kicad.app/ + + wxFileName fn( m_bin_dir ); + + if( fn.GetName() == wxT( "kicad" ) ) + { + // kicad launcher, so just remove the Contents/MacOS part + fn.RemoveLastDir(); + fn.RemoveLastDir(); + } + else + { + // standalone binaries live in Contents/Applications/<standalone>.app/Contents/MacOS + fn.RemoveLastDir(); + fn.RemoveLastDir(); + fn.RemoveLastDir(); + fn.RemoveLastDir(); + fn.RemoveLastDir(); + } + + m_bin_dir = fn.GetPath() + wxT( "/" ); +#else + // Use unix notation for paths. I am not sure this is a good idea, + // but it simplifies compatibility between Windows and Unices. + // However it is a potential problem in path handling under Windows. + m_bin_dir.Replace( WIN_STRING_DIR_SEP, UNIX_STRING_DIR_SEP ); + + // Remove file name form command line: + while( m_bin_dir.Last() != '/' && !m_bin_dir.IsEmpty() ) + m_bin_dir.RemoveLast(); +#endif + + return true; +} + + +void PGM_BASE::loadCommonSettings() +{ + wxASSERT( m_common_settings ); + + m_help_size.x = 500; + m_help_size.y = 400; + + m_common_settings->Read( showEnvVarWarningDialog, &m_show_env_var_dialog ); + + m_editor_name = m_common_settings->Read( wxT( "Editor" ) ); + + wxString entry, oldPath; + wxArrayString entries; + long index = 0L; + + oldPath = m_common_settings->GetPath(); + m_common_settings->SetPath( pathEnvVariables ); + + while( m_common_settings->GetNextEntry( entry, index ) ) + { + wxLogTrace( traceEnvVars, + wxT( "Enumerating over entry %s, %ld." ), GetChars( entry ), index ); + entries.Add( entry ); + } + + for( unsigned i = 0; i < entries.GetCount(); i++ ) + { + wxString val = m_common_settings->Read( entries[i], wxEmptyString ); + m_local_env_vars[ entries[i] ] = ENV_VAR_ITEM( val, wxGetEnv( entries[i], NULL ) ); + } + + for( ENV_VAR_MAP_ITER it = m_local_env_vars.begin(); it != m_local_env_vars.end(); ++it ) + SetLocalEnvVariable( it->first, it->second.GetValue() ); + + m_common_settings->SetPath( oldPath ); +} + + +void PGM_BASE::saveCommonSettings() +{ + // m_common_settings is not initialized until fairly late in the + // process startup: initPgm(), so test before using: + if( m_common_settings ) + { + wxString cur_dir = wxGetCwd(); + + m_common_settings->Write( workingDirKey, cur_dir ); + m_common_settings->Write( showEnvVarWarningDialog, m_show_env_var_dialog ); + + // Save the local environment variables. + m_common_settings->SetPath( pathEnvVariables ); + + for( ENV_VAR_MAP_ITER it = m_local_env_vars.begin(); it != m_local_env_vars.end(); ++it ) + { + wxLogTrace( traceEnvVars, wxT( "Saving environment variable config entry %s as %s" ), + GetChars( it->first ), GetChars( it->second.GetValue() ) ); + m_common_settings->Write( it->first, it->second.GetValue() ); + } + + m_common_settings->SetPath( wxT( ".." ) ); + } +} + + +bool PGM_BASE::SetLanguage( bool first_time ) +{ + bool retv = true; + + if( first_time ) + { + setLanguageId( wxLANGUAGE_DEFAULT ); + // First time SetLanguage is called, the user selected language id is set + // from commun user config settings + wxString languageSel; + + m_common_settings->Read( languageCfgKey, &languageSel ); + + // Search for the current selection + for( unsigned ii = 0; ii < DIM( s_Languages ); ii++ ) + { + if( s_Languages[ii].m_Lang_Label == languageSel ) + { + setLanguageId( s_Languages[ii].m_WX_Lang_Identifier ); + break; + } + } + } + + // dictionary file name without extend (full name is kicad.mo) + wxString dictionaryName( wxT( "kicad" ) ); + + delete m_locale; + m_locale = new wxLocale; + + if( !m_locale->Init( m_language_id ) ) + { + wxLogDebug( wxT( "This language is not supported by the system." ) ); + + setLanguageId( wxLANGUAGE_DEFAULT ); + delete m_locale; + + m_locale = new wxLocale; + m_locale->Init(); + retv = false; + } + else if( !first_time ) + { + wxLogDebug( wxT( "Search for dictionary %s.mo in %s" ), + GetChars( dictionaryName ), GetChars( m_locale->GetName() ) ); + } + + if( !first_time ) + { + // If we are here, the user has selected an other language. + // Therefore the new prefered language name is stored in common config. + // Do NOT store the wxWidgets language Id, it can change between wxWidgets + // versions, for a given language + wxString languageSel; + + // Search for the current selection + for( unsigned ii = 0; ii < DIM( s_Languages ); ii++ ) + { + if( s_Languages[ii].m_WX_Lang_Identifier == m_language_id ) + { + languageSel = s_Languages[ii].m_Lang_Label; + break; + } + } + + m_common_settings->Write( languageCfgKey, languageSel ); + } + + // Test if floating point notation is working (bug in cross compilation, using wine) + // Make a conversion double <=> string + double dtst = 0.5; + wxString msg; + + msg << dtst; + double result; + msg.ToDouble( &result ); + + if( result != dtst ) + // string to double encode/decode does not work! Bug detected: + // Disable floating point localization: + setlocale( LC_ALL, "C" ); + + if( !m_locale->IsLoaded( dictionaryName ) ) + m_locale->AddCatalog( dictionaryName ); + + if( !retv ) + return retv; + + return m_locale->IsOk(); +} + + +void PGM_BASE::SetLanguageIdentifier( int menu_id ) +{ + wxLogDebug( wxT( "Select language ID %d from %d possible languages." ), + menu_id, DIM( s_Languages ) ); + + for( unsigned ii = 0; ii < DIM( s_Languages ); ii++ ) + { + if( menu_id == s_Languages[ii].m_KI_Lang_Identifier ) + { + setLanguageId( s_Languages[ii].m_WX_Lang_Identifier ); + break; + } + } +} + + +void PGM_BASE::SetLanguagePath() +{ + SEARCH_STACK guesses; + + SystemDirsAppend( &guesses ); + + // Add our internat dir to the wxLocale catalog of paths + for( unsigned i = 0; i < guesses.GetCount(); i++ ) + { + wxFileName fn( guesses[i], wxEmptyString ); + + // Append path for Windows and unix KiCad package install + fn.AppendDir( wxT( "share" ) ); + fn.AppendDir( wxT( "internat" ) ); + + if( fn.IsDirReadable() ) + { + wxLogDebug( wxT( "Adding locale lookup path: " ) + fn.GetPath() ); + wxLocale::AddCatalogLookupPathPrefix( fn.GetPath() ); + } + + // Append path for unix standard install + fn.RemoveLastDir(); + fn.AppendDir( wxT( "kicad" ) ); + fn.AppendDir( wxT( "internat" ) ); + + if( fn.IsDirReadable() ) + { + wxLogDebug( wxT( "Adding locale lookup path: " ) + fn.GetPath() ); + wxLocale::AddCatalogLookupPathPrefix( fn.GetPath() ); + } + } +} + + +void PGM_BASE::AddMenuLanguageList( wxMenu* MasterMenu ) +{ + wxMenu* menu = NULL; + wxMenuItem* item = MasterMenu->FindItem( ID_LANGUAGE_CHOICE ); + + if( item ) // This menu exists, do nothing + return; + + menu = new wxMenu; + + for( unsigned ii = 0; ii < DIM( s_Languages ); ii++ ) + { + wxString label; + + if( s_Languages[ii].m_DoNotTranslate ) + label = s_Languages[ii].m_Lang_Label; + else + label = wxGetTranslation( s_Languages[ii].m_Lang_Label ); + + AddMenuItem( menu, s_Languages[ii].m_KI_Lang_Identifier, + label, KiBitmap(s_Languages[ii].m_Lang_Icon ), + wxITEM_CHECK ); + } + + AddMenuItem( MasterMenu, menu, + ID_LANGUAGE_CHOICE, + _( "Language" ), + _( "Select application language (only for testing!)" ), + KiBitmap( language_xpm ) ); + + // Set Check mark on current selected language + for( unsigned ii = 0; ii < DIM( s_Languages ); ii++ ) + { + if( m_language_id == s_Languages[ii].m_WX_Lang_Identifier ) + menu->Check( s_Languages[ii].m_KI_Lang_Identifier, true ); + else + menu->Check( s_Languages[ii].m_KI_Lang_Identifier, false ); + } +} + + +bool PGM_BASE::SetLocalEnvVariable( const wxString& aName, const wxString& aValue ) +{ + wxString env; + + // Check to see if the environment variable is already set. + if( wxGetEnv( aName, &env ) ) + { + wxLogTrace( traceEnvVars, wxT( "Environment variable %s already set to %s." ), + GetChars( aName ), GetChars( env ) ); + return env == aValue; + } + + wxLogTrace( traceEnvVars, wxT( "Setting local environment variable %s to %s." ), + GetChars( aName ), GetChars( aValue ) ); + + return wxSetEnv( aName, aValue ); +} + + +void PGM_BASE::SetLocalEnvVariables( const ENV_VAR_MAP& aEnvVarMap ) +{ + m_local_env_vars.clear(); + m_local_env_vars = aEnvVarMap; + + if( m_common_settings ) + m_common_settings->DeleteGroup( pathEnvVariables ); + + saveCommonSettings(); + + // Overwrites externally defined environment variable until the next time the application + // is run. + for( ENV_VAR_MAP_ITER it = m_local_env_vars.begin(); it != m_local_env_vars.end(); ++it ) + { + wxLogTrace( traceEnvVars, wxT( "Setting local environment variable %s to %s." ), + GetChars( it->first ), GetChars( it->second.GetValue() ) ); + wxSetEnv( it->first, it->second.GetValue() ); + } +} + + +void PGM_BASE::ConfigurePaths( wxWindow* aParent ) +{ + DIALOG_ENV_VAR_CONFIG dlg( aParent, GetLocalEnvVariables() ); + + if( dlg.ShowModal() == wxID_CANCEL ) + return; + + ENV_VAR_MAP envVarMap = dlg.GetEnvVarMap(); + + for( ENV_VAR_MAP_ITER it = envVarMap.begin(); it != envVarMap.end(); ++it ) + { + wxLogTrace( traceEnvVars, wxT( "Environment variable %s=%s defined externally = %d" ), + GetChars( it->first ), GetChars( it->second.GetValue() ), + it->second.GetDefinedExternally() ); + } + + // If any of the environment variables are defined externally, warn the user that the + // next time kicad is run that the externally defined variables will be used instead of + // the user's settings. This is by design. + if( dlg.ExternalDefsChanged() && m_show_env_var_dialog ) + { + wxString msg1 = _( "Warning! Some of paths you have configured have been defined \n" + "externally to the running process and will be temporarily overwritten." ); + wxString msg2 = _( "The next time KiCad is launched, any paths that have already\n" + "been defined are honored and any settings defined in the path\n" + "configuration dialog are ignored. If you did not intend for this\n" + "behavior, either rename any conflicting entries or remove the\n" + "external environment variable definition(s) from your system." ); + wxRichMessageDialog dlg( aParent, msg1, _( "Warning" ), wxOK | wxCENTRE ); + dlg.ShowDetailedText( msg2 ); + dlg.ShowCheckBox( _( "Do not show this message again." ) ); + dlg.ShowModal(); + m_show_env_var_dialog = !dlg.IsCheckBoxChecked(); + } + + SetLocalEnvVariables( dlg.GetEnvVarMap() ); +} diff --git a/common/prependpath.cpp b/common/prependpath.cpp new file mode 100644 index 0000000..e0522dc --- /dev/null +++ b/common/prependpath.cpp @@ -0,0 +1,40 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 CERN + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.TXT for contributors. + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 <macros.h> +#include <fctsys.h> +#include <wx/filename.h> + + +/// Put aPriorityPath in front of all paths in the value of aEnvVar. +const wxString PrePendPath( const wxString& aEnvVar, const wxString& aPriorityPath ) +{ + wxPathList paths; + + paths.AddEnvList( aEnvVar ); + paths.Insert( aPriorityPath, 0 ); + + return wxJoin( paths, wxPATH_SEP[0] ); +} diff --git a/common/project.cpp b/common/project.cpp new file mode 100644 index 0000000..a6b7546 --- /dev/null +++ b/common/project.cpp @@ -0,0 +1,381 @@ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.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 <wx/stdpaths.h> + +#include <fctsys.h> +#include <macros.h> +#include <pgm_base.h> +#include <project.h> +#include <common.h> // NAMELESS_PROJECT +#include <confirm.h> +#include <kicad_string.h> +#include <config_params.h> +#include <wildcards_and_files_ext.h> + + +PROJECT::PROJECT() +{ + memset( m_elems, 0, sizeof(m_elems) ); +} + + +void PROJECT::ElemsClear() +{ + DBG( printf( "%s: clearing all _ELEMS for project %s\n", __func__, TO_UTF8( GetProjectFullName() ) );) + + // careful here, this should work, but the virtual destructor may not + // be in the same link image as PROJECT. + for( unsigned i = 0; i < DIM( m_elems ); ++i ) + { + SetElem( ELEM_T( i ), NULL ); + } +} + + +PROJECT::~PROJECT() +{ + ElemsClear(); +} + + +void PROJECT::SetProjectFullName( const wxString& aFullPathAndName ) +{ + // Compare paths, rather than inodes, to be less surprising to the user. + // Create a temporary wxFileName to normalize the path + wxFileName candidate_path( aFullPathAndName ); + + // Edge transitions only. This is what clears the project + // data using the Clear() function. + if( m_project_name.GetFullPath() != candidate_path.GetFullPath() ) + { + Clear(); // clear the data when the project changes. + + DBG(printf( "%s: old:'%s' new:'%s'\n", __func__, TO_UTF8( GetProjectFullName() ), TO_UTF8( aFullPathAndName ) );) + + m_project_name = aFullPathAndName; + + wxASSERT( m_project_name.IsAbsolute() ); + + wxASSERT( m_project_name.GetExt() == ProjectFileExtension ); + + // until multiple projects are in play, set an environment variable for the + // the project pointer. + { + wxString path = m_project_name.GetPath(); + + wxSetEnv( PROJECT_VAR_NAME, path ); + } + } +} + + +const wxString PROJECT::GetProjectFullName() const +{ + return m_project_name.GetFullPath(); +} + + +const wxString PROJECT::GetProjectPath() const +{ + return m_project_name.GetPathWithSep(); +} + + +const wxString PROJECT::GetProjectName() const +{ + return m_project_name.GetName(); +} + + +const wxString PROJECT::FootprintLibTblName() const +{ + wxFileName fn = GetProjectFullName(); + wxString path = fn.GetPath(); + + // DBG(printf( "path:'%s' fn:'%s'\n", TO_UTF8(path), TO_UTF8(fn.GetFullPath()) );) + + // if there's no path to the project name, or the name as a whole is bogus or its not + // write-able then use a template file. + if( !fn.GetDirCount() || !fn.IsOk() || !wxFileName::IsDirWritable( path ) ) + { + // return a template filename now. + + // this next line is likely a problem now, since it relies on an + // application title which is no longer constant or known. This next line needs + // to be re-thought out. + +#ifndef __WXMAC__ + fn.AssignDir( wxStandardPaths::Get().GetUserConfigDir() ); +#else + // don't pollute home folder, temp folder seems to be more appropriate + fn.AssignDir( wxStandardPaths::Get().GetTempDir() ); +#endif + +#if defined( __WINDOWS__ ) + fn.AppendDir( wxT( "kicad" ) ); +#endif + + /* + The footprint library table name used when no project file is passed + to Pcbnew or CvPcb. This is used temporarily to store the project + specific library table until the project file being edited is saved. + It is then moved to the file fp-lib-table in the folder where the + project file is saved. + */ + fn.SetName( wxT( "prj-fp-lib-table" ) ); + } + else // normal path. + { + fn.SetName( wxT( "fp-lib-table" ) ); + } + + fn.ClearExt(); + + return fn.GetFullPath(); +} + + +void PROJECT::SetRString( RSTRING_T aIndex, const wxString& aString ) +{ + unsigned ndx = unsigned( aIndex ); + + if( ndx < DIM( m_rstrings ) ) + { + m_rstrings[ndx] = aString; + } + else + { + wxASSERT( 0 ); // bad index + } +} + + +const wxString& PROJECT::GetRString( RSTRING_T aIndex ) +{ + unsigned ndx = unsigned( aIndex ); + + if( ndx < DIM( m_rstrings ) ) + { + return m_rstrings[ndx]; + } + else + { + static wxString no_cookie_for_you; + + wxASSERT( 0 ); // bad index + + return no_cookie_for_you; + } +} + + +PROJECT::_ELEM* PROJECT::GetElem( ELEM_T aIndex ) +{ + // This is virtual, so implement it out of line + + if( unsigned( aIndex ) < DIM( m_elems ) ) + { + return m_elems[aIndex]; + } + return NULL; +} + + +void PROJECT::SetElem( ELEM_T aIndex, _ELEM* aElem ) +{ + // This is virtual, so implement it out of line + + if( unsigned( aIndex ) < DIM( m_elems ) ) + { +#if defined(DEBUG) && 0 + if( aIndex == ELEM_SCH_PART_LIBS ) + { + printf( "%s: &m_elems[%i]:%p aElem:%p\n", __func__, aIndex, &m_elems[aIndex], aElem ); + } +#endif + delete m_elems[aIndex]; + m_elems[aIndex] = aElem; + } +} + + +static bool copy_pro_file_template( const SEARCH_STACK& aSearchS, const wxString& aDestination ) +{ + if( aDestination.IsEmpty() ) + { + DBG( printf( "%s: destination is empty.\n", __func__ );) + return false; + } + + wxString templateFile = wxT( "kicad." ) + ProjectFileExtension; + + wxString kicad_pro_template = aSearchS.FindValidPath( templateFile ); + + if( !kicad_pro_template ) + { + DBG( printf( "%s: template file '%s' not found using search paths.\n", __func__, TO_UTF8( templateFile ) );) + + wxFileName templ( wxStandardPaths::Get().GetDocumentsDir(), + wxT( "kicad" ), ProjectFileExtension ); + + if( !templ.IsFileReadable() ) + { + wxString msg = wxString::Format( _( + "Unable to find '%s' template config file." ), + GetChars( templateFile ) ); + + DisplayError( NULL, msg ); + + return false; + } + + kicad_pro_template = templ.GetFullPath(); + } + + DBG( printf( "%s: using template file '%s' as project file.\n", __func__, TO_UTF8( kicad_pro_template ) );) + + // Verify aDestination can be created. if this is not the case, wxCopyFile + // will generate a crappy log error message, and we *do not want* this kind + // of stupid message + wxFileName fn( aDestination ); + bool success = true; + + if( fn.IsOk() && fn.IsDirWritable() ) + success = wxCopyFile( kicad_pro_template, aDestination ); + else + { + wxLogMessage( _( "Cannot create prj file '%s' (Directory not writable)" ), + GetChars( aDestination) ); + success = false; + } + + return success; +} + + +wxConfigBase* PROJECT::configCreate( const SEARCH_STACK& aSList, + const wxString& aGroupName, const wxString& aProjectFileName ) +{ + wxConfigBase* cfg = 0; + wxString cur_pro_fn = !aProjectFileName ? GetProjectFullName() : aProjectFileName; + + if( wxFileName( cur_pro_fn ).IsFileReadable() ) + { + // Note: currently, aGroupName is not used. + // Previoulsy, the version of aGroupName was tested, but it + // was useless, and if the version is important, + // this is not the right place here, because configCreate does know anything + // about info stored in this config file. + cfg = new wxFileConfig( wxEmptyString, wxEmptyString, cur_pro_fn, wxEmptyString ); + return cfg; + } + + // No suitable pro file was found, either does not exist, or not readable. + // Use the template kicad.pro file. Find it by using caller's SEARCH_STACK. + copy_pro_file_template( aSList, cur_pro_fn ); + + cfg = new wxFileConfig( wxEmptyString, wxEmptyString, cur_pro_fn, wxEmptyString ); + + return cfg; +} + + +void PROJECT::ConfigSave( const SEARCH_STACK& aSList, const wxString& aGroupName, + const PARAM_CFG_ARRAY& aParams, const wxString& aFileName ) +{ + std::auto_ptr<wxConfigBase> cfg( configCreate( aSList, aGroupName, aFileName ) ); + + if( !cfg.get() ) + { + // could not find template + return; + } + + cfg->SetPath( wxT( "/" ) ); + + cfg->Write( wxT( "update" ), DateAndTime() ); + + // @todo: pass in aLastClient wxString: + cfg->Write( wxT( "last_client" ), Pgm().App().GetAppName() ); + + // Save parameters + cfg->DeleteGroup( aGroupName ); // Erase all data + cfg->Flush(); + + cfg->SetPath( aGroupName ); + cfg->Write( wxT( "version" ), CONFIG_VERSION ); + + cfg->SetPath( wxT( "/" ) ); + + wxConfigSaveParams( cfg.get(), aParams, aGroupName ); + + cfg->SetPath( wxT( "/" ) ); + + // cfg is deleted here by std::auto_ptr, that saves the *.pro file to disk +} + + +bool PROJECT::ConfigLoad( const SEARCH_STACK& aSList, const wxString& aGroupName, + const PARAM_CFG_ARRAY& aParams, const wxString& aForeignProjectFileName ) +{ + std::auto_ptr<wxConfigBase> cfg( configCreate( aSList, aGroupName, aForeignProjectFileName ) ); + + if( !cfg.get() ) + { + // could not find template + return false; + } + + cfg->SetPath( wxCONFIG_PATH_SEPARATOR ); + + wxString timestamp = cfg->Read( wxT( "update" ) ); + + m_pro_date_and_time = timestamp; + + // We do not want expansion of env var values when reading our project config file + bool state = cfg.get()->IsExpandingEnvVars(); + cfg.get()->SetExpandEnvVars( false ); + + wxConfigLoadParams( cfg.get(), aParams, aGroupName ); + + cfg.get()->SetExpandEnvVars( state ); + + return true; +} + + +const wxString PROJECT::AbsolutePath( const wxString& aFileName ) const +{ + wxFileName fn = aFileName; + + if( !fn.IsAbsolute() ) + { + wxString pro_dir = wxPathOnly( GetProjectFullName() ); + fn.Normalize( wxPATH_NORM_ALL, pro_dir ); + } + + return fn.GetFullPath(); +} diff --git a/common/ptree.cpp b/common/ptree.cpp new file mode 100644 index 0000000..bac47ee --- /dev/null +++ b/common/ptree.cpp @@ -0,0 +1,220 @@ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2013 KiCad Developers, see CHANGELOG.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 + */ + +// Something in either <boost/property_tree/ptree.hpp> causes a bunch of compiler +// errors in <wx/msw/winundef.h> version 2.9 on MinGW. +#include <macros.h> + +#include <boost/property_tree/ptree.hpp> + +#include <assert.h> +#include <ptree.h> + +typedef PTREE::const_iterator CITER; +typedef PTREE::iterator ITER; + +#if defined(DEBUG) + #define D(x) x +#else + #define D(x) +#endif + +#define CTL_OMIT_NL (1<<0) +#define CTL_IN_ATTRS (1<<1) + + +//-----<Scan>------------------------------------------------------------------ + +/** + * Function scanList + * reads a sexpr list from the input stream into a new node with key + * aLexer->CurText(). + */ +inline void scanList( PTREE* aTree, DSNLEXER* aLexer ) +{ + assert( aLexer->CurTok() == DSN_LEFT ); + + int tok = aLexer->NextTok(); + + const char* key = aLexer->CurText(); + + //D(printf( "%s: '%s'\n", __func__, key );) + + PTREE* list = &aTree->push_back( PTREE::value_type( key, PTREE() ) )->second; + + if( tok != DSN_RIGHT ) + { + while( ( tok = aLexer->NextTok() ) != DSN_RIGHT ) + { + if( tok == DSN_EOF ) + aLexer->Unexpected( DSN_EOF ); + + Scan( list, aLexer ); + } + } +} + + +inline void scanAtom( PTREE* aTree, DSNLEXER* aLexer ) +{ + const char* key = aLexer->CurText(); + + //D(printf( "%s: '%s'\n", __func__, key );) + + aTree->push_back( PTREE::value_type( key, PTREE() ) ); +} + + +void Scan( PTREE* aTree, DSNLEXER* aLexer ) throw( IO_ERROR ) +{ + int tok = aLexer->CurTok(); + + // conditionally read first token. + if( tok == DSN_NONE ) + tok = aLexer->NextTok(); + + if( tok == DSN_EOF ) + { + aLexer->Unexpected( DSN_EOF ); + } + + if( tok == DSN_LEFT ) + { + scanList( aTree, aLexer ); + } + else + { + scanAtom( aTree, aLexer ); + } +} + + +//-----<Format>------------------------------------------------------------------ + +inline bool isAtom( CPTREE& aTree ) +{ + return aTree.size()==0 && aTree.data().size()==0; +} + + +inline bool isLast( CPTREE& aTree, CITER it ) +{ + CITER next = it; + ++next; + return next == aTree.end(); +} + + +inline CITER next( CITER it ) +{ + CITER n = it; + return ++n; +} + + +static void formatNode( OUTPUTFORMATTER* out, int aNestLevel, int aCtl, + const std::string& aKey, CPTREE& aTree ) throw( IO_ERROR ); + + +static void formatList( OUTPUTFORMATTER* out, int aNestLevel, int aCtl, CPTREE& aTree ) + throw( IO_ERROR ) +{ + for( CITER it = aTree.begin(); it != aTree.end(); ++it ) + { + // Processing a tree which was read in with xml_parser? + if( it->first == "<xmlattr>" ) + { + formatList( out, aNestLevel, aCtl | CTL_IN_ATTRS, it->second ); + continue; + } + + int ctl = 0; + + if( isLast( aTree, it ) ) // is "it" the last one? + { + //if( !( aCtl & CTL_IN_ATTRS ) ) + ctl = CTL_OMIT_NL; + } + else if( isAtom( next( it )->second ) ) + { + /* if( !( aCtl & CTL_IN_ATTRS ) ) */ + ctl = CTL_OMIT_NL; + } + + formatNode( out, aNestLevel+1, ctl, it->first, it->second ); + } +} + + +static void formatNode( OUTPUTFORMATTER* out, int aNestLevel, int aCtl, + const std::string& aKey, CPTREE& aTree ) + throw( IO_ERROR ) +{ + if( !isAtom( aTree ) ) // is a list, not an atom + { + int ctl = CTL_OMIT_NL; + + // aTree is list and its first child is a list + if( aTree.size() && !isAtom( aTree.begin()->second ) && !aTree.data().size() ) + ctl = 0; + + out->Print( aNestLevel, "(%s%s", out->Quotes( aKey ).c_str(), ctl & CTL_OMIT_NL ? "" : "\n" ); + + if( aTree.data().size() ) // sexpr format does not use data() + { + out->Print( 0, " %s%s", + out->Quotes( aTree.data() ).c_str(), + aTree.size() ? "\n" : "" + ); + } + + formatList( out, aNestLevel, aCtl, aTree ); + + out->Print( 0, ")%s", aCtl & CTL_OMIT_NL ? "" : "\n" ); + } + + else // is an atom, not a list + { + out->Print( 0, " %s", out->Quotes( aKey ).c_str() ); + } +} + + +void Format( OUTPUTFORMATTER* out, int aNestLevel, int aCtl, CPTREE& aTree ) throw( IO_ERROR ) +{ + if( aTree.size() == 1 && !aTree.data().size() ) + { + // The topmost node is basically only a container for the document root. + // It anchors the paths which traverse the tree deeper. + CITER it = aTree.begin(); + formatNode( out, aNestLevel, aCtl, it->first, it->second ); + } + else + { + // This is not expected, neither for sexpr nor xml. + formatNode( out, aNestLevel, aCtl, "", aTree ); + } +} + diff --git a/common/reporter.cpp b/common/reporter.cpp new file mode 100644 index 0000000..2efc475 --- /dev/null +++ b/common/reporter.cpp @@ -0,0 +1,81 @@ +/** + * @file reporter.cpp + */ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 Wayne Stambaugh <stambaughw@verizon.net> + * Copyright (C) 1992-2015 KiCad Developers, see change_log.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 <macros.h> +#include <reporter.h> +#include <wx_html_report_panel.h> + +REPORTER& REPORTER::Report( const char* aText, REPORTER::SEVERITY aSeverity ) +{ + Report( FROM_UTF8( aText ) ); + return *this; +} + + +REPORTER& WX_TEXT_CTRL_REPORTER::Report( const wxString& aText, REPORTER::SEVERITY aSeverity ) +{ + wxCHECK_MSG( m_textCtrl != NULL, *this, + wxT( "No wxTextCtrl object defined in WX_TEXT_CTRL_REPORTER." ) ); + + m_textCtrl->AppendText( aText ); + return *this; +} + +REPORTER& WX_STRING_REPORTER::Report( const wxString& aText, REPORTER::SEVERITY aSeverity ) +{ + wxCHECK_MSG( m_string != NULL, *this, + wxT( "No wxString object defined in WX_STRING_REPORTER." ) ); + + *m_string << aText; + return *this; +} + +REPORTER& WX_HTML_PANEL_REPORTER::Report( const wxString& aText, SEVERITY aSeverity ) +{ + wxCHECK_MSG( m_panel != NULL, *this, + wxT( "No WX_HTML_REPORT_PANEL object defined in WX_HTML_PANEL_REPORTER." ) ); + + m_panel->Report( aText, aSeverity ); + return *this; +} + +REPORTER& NULL_REPORTER::Report( const wxString& aText, SEVERITY aSeverity ) +{ + return *this; +} + +REPORTER& NULL_REPORTER::GetInstance() +{ + static REPORTER* s_nullReporter = NULL; + + if( !s_nullReporter ) + { + s_nullReporter = new NULL_REPORTER(); + } + + return *s_nullReporter; +} diff --git a/common/richio.cpp b/common/richio.cpp new file mode 100644 index 0000000..e0684d7 --- /dev/null +++ b/common/richio.cpp @@ -0,0 +1,607 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2007-2011 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2016 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 <cstdarg> + +#include <richio.h> + + +// Fall back to getc() when getc_unlocked() is not available on the target platform. +#if !defined( HAVE_FGETC_NOLOCK ) +#define getc_unlocked getc +#endif + + +static int vprint( std::string* result, const char* format, va_list ap ) +{ + char msg[512]; + // This function can call vsnprintf twice. + // But internally, vsnprintf retrieves arguments from the va_list identified by arg as if + // va_arg was used on it, and thus the state of the va_list is likely to be altered by the call. + // see: www.cplusplus.com/reference/cstdio/vsnprintf + // we make a copy of va_list ap for the second call, if happens + va_list tmp; + va_copy( tmp, ap ); + + size_t len = vsnprintf( msg, sizeof(msg), format, ap ); + + if( len < sizeof(msg) ) // the output fit into msg + { + result->append( msg, msg + len ); + } + else + { + // output was too big, so now incur the expense of allocating + // a buf for holding suffient characters. + + std::vector<char> buf; + + buf.reserve( len+1 ); // reserve(), not resize() which writes. +1 for trailing nul. + + len = vsnprintf( &buf[0], len+1, format, tmp ); + + result->append( &buf[0], &buf[0] + len ); + } + + va_end( tmp ); // Release the temporary va_list, initialised from ap + + return len; +} + + +int StrPrintf( std::string* result, const char* format, ... ) +{ + va_list args; + + va_start( args, format ); + int ret = vprint( result, format, args ); + va_end( args ); + + return ret; +} + + +std::string StrPrintf( const char* format, ... ) +{ + std::string ret; + va_list args; + + va_start( args, format ); + int ignore = vprint( &ret, format, args ); + (void) ignore; + va_end( args ); + + return ret; +} + + +void IO_ERROR::init( const char* aThrowersFile, const char* aThrowersLoc, const wxString& aMsg ) +{ + errorText.Printf( IO_FORMAT, aMsg.GetData(), + wxString::FromUTF8( aThrowersFile ).AfterLast( '/' ).GetData(), + wxString::FromUTF8( aThrowersLoc ).GetData() ); +} + + +void PARSE_ERROR::init( const char* aThrowersFile, const char* aThrowersLoc, + const wxString& aMsg, const wxString& aSource, + const char* aInputLine, + int aLineNumber, int aByteIndex ) +{ + // save inpuLine, lineNumber, and offset for UI (.e.g. Sweet text editor) + inputLine = aInputLine; + lineNumber = aLineNumber; + byteIndex = aByteIndex; + + errorText.Printf( PARSE_FORMAT, aMsg.GetData(), aSource.GetData(), + aLineNumber, aByteIndex, + wxString::FromUTF8( aThrowersFile ).AfterLast( '/' ).GetData(), + wxString::FromUTF8( aThrowersLoc ).GetData() ); +} + + +//-----<LINE_READER>------------------------------------------------------ + +LINE_READER::LINE_READER( unsigned aMaxLineLength ) : + length( 0 ), + lineNum( 0 ), + line( NULL ), + capacity( 0 ), + maxLineLength( aMaxLineLength ) +{ + if( aMaxLineLength != 0 ) + { + // start at the INITIAL size, expand as needed up to the MAX size in maxLineLength + capacity = LINE_READER_LINE_INITIAL_SIZE; + + // but never go above user's aMaxLineLength, and leave space for trailing nul + if( capacity > aMaxLineLength+1 ) + capacity = aMaxLineLength+1; + + line = new char[capacity]; + + line[0] = '\0'; + } +} + + +LINE_READER::~LINE_READER() +{ + delete[] line; +} + + +void LINE_READER::expandCapacity( unsigned newsize ) +{ + // length can equal maxLineLength and nothing breaks, there's room for + // the terminating nul. cannot go over this. + if( newsize > maxLineLength+1 ) + newsize = maxLineLength+1; + + if( newsize > capacity ) + { + capacity = newsize; + + // resize the buffer, and copy the original data + char* bigger = new char[capacity]; + + wxASSERT( capacity >= length+1 ); + + memcpy( bigger, line, length ); + bigger[length] = 0; + + delete[] line; + line = bigger; + } +} + + +FILE_LINE_READER::FILE_LINE_READER( const wxString& aFileName, + unsigned aStartingLineNumber, + unsigned aMaxLineLength ) throw( IO_ERROR ) : + LINE_READER( aMaxLineLength ), + iOwn( true ) +{ + fp = wxFopen( aFileName, wxT( "rt" ) ); + if( !fp ) + { + wxString msg = wxString::Format( + _( "Unable to open filename '%s' for reading" ), aFileName.GetData() ); + THROW_IO_ERROR( msg ); + } + + setvbuf( fp, NULL, _IOFBF, BUFSIZ * 8 ); + + source = aFileName; + lineNum = aStartingLineNumber; +} + + +FILE_LINE_READER::FILE_LINE_READER( FILE* aFile, const wxString& aFileName, + bool doOwn, + unsigned aStartingLineNumber, + unsigned aMaxLineLength ) : + LINE_READER( aMaxLineLength ), + iOwn( doOwn ), + fp( aFile ) +{ + if( doOwn && ftell( aFile ) == 0L ) + { +#ifndef __WXMAC__ + setvbuf( fp, NULL, _IOFBF, BUFSIZ * 8 ); +#endif + } + source = aFileName; + lineNum = aStartingLineNumber; +} + + +FILE_LINE_READER::~FILE_LINE_READER() +{ + if( iOwn && fp ) + fclose( fp ); +} + + +char* FILE_LINE_READER::ReadLine() throw( IO_ERROR ) +{ + length = 0; + + for(;;) + { + if( length >= maxLineLength ) + THROW_IO_ERROR( _( "Maximum line length exceeded" ) ); + + if( length >= capacity ) + expandCapacity( capacity * 2 ); + + // faster, POSIX compatible fgetc(), no locking. + int cc = getc_unlocked( fp ); + if( cc == EOF ) + break; + + line[ length++ ] = (char) cc; + + if( cc == '\n' ) + break; + } + + line[ length ] = 0; + + // lineNum is incremented even if there was no line read, because this + // leads to better error reporting when we hit an end of file. + ++lineNum; + + return length ? line : NULL; +} + + +STRING_LINE_READER::STRING_LINE_READER( const std::string& aString, const wxString& aSource ) : + LINE_READER( LINE_READER_LINE_DEFAULT_MAX ), + lines( aString ), + ndx( 0 ) +{ + // Clipboard text should be nice and _use multiple lines_ so that + // we can report _line number_ oriented error messages when parsing. + source = aSource; +} + + +STRING_LINE_READER::STRING_LINE_READER( const STRING_LINE_READER& aStartingPoint ) : + LINE_READER( LINE_READER_LINE_DEFAULT_MAX ), + lines( aStartingPoint.lines ), + ndx( aStartingPoint.ndx ) +{ + // since we are keeping the same "source" name, for error reporting purposes + // we need to have the same notion of line number and offset. + + source = aStartingPoint.source; + lineNum = aStartingPoint.lineNum; +} + + +char* STRING_LINE_READER::ReadLine() throw( IO_ERROR ) +{ + size_t nlOffset = lines.find( '\n', ndx ); + + if( nlOffset == std::string::npos ) + length = lines.length() - ndx; + else + length = nlOffset - ndx + 1; // include the newline, so +1 + + if( length ) + { + if( length >= maxLineLength ) + THROW_IO_ERROR( _("Line length exceeded") ); + + if( length+1 > capacity ) // +1 for terminating nul + expandCapacity( length+1 ); + + wxASSERT( ndx + length <= lines.length() ); + + memcpy( line, &lines[ndx], length ); + + ndx += length; + } + + ++lineNum; // this gets incremented even if no bytes were read + + line[length] = 0; + + return length ? line : NULL; +} + + +INPUTSTREAM_LINE_READER::INPUTSTREAM_LINE_READER( wxInputStream* aStream, const wxString& aSource ) : + LINE_READER( LINE_READER_LINE_DEFAULT_MAX ), + m_stream( aStream ) +{ + source = aSource; +} + + +char* INPUTSTREAM_LINE_READER::ReadLine() throw( IO_ERROR ) +{ + length = 0; + + for(;;) + { + if( length >= maxLineLength ) + THROW_IO_ERROR( _( "Maximum line length exceeded" ) ); + + if( length + 1 > capacity ) + expandCapacity( capacity * 2 ); + + // this read may fail, docs say to test LastRead() before trusting cc. + char cc = m_stream->GetC(); + + if( !m_stream->LastRead() ) + break; + + line[ length++ ] = cc; + + if( cc == '\n' ) + break; + } + + line[ length ] = 0; + + // lineNum is incremented even if there was no line read, because this + // leads to better error reporting when we hit an end of file. + ++lineNum; + + return length ? line : NULL; +} + + +//-----<OUTPUTFORMATTER>---------------------------------------------------- + +// factor out a common GetQuoteChar + +const char* OUTPUTFORMATTER::GetQuoteChar( const char* wrapee, const char* quote_char ) +{ + // Include '#' so a symbol is not confused with a comment. We intend + // to wrap any symbol starting with a '#'. + // Our LEXER class handles comments, and comments appear to be an extension + // to the SPECCTRA DSN specification. + if( *wrapee == '#' ) + return quote_char; + + if( strlen( wrapee ) == 0 ) + return quote_char; + + bool isFirst = true; + + for( ; *wrapee; ++wrapee, isFirst = false ) + { + static const char quoteThese[] = "\t ()" + "%" // per Alfons of freerouting.net, he does not like this unquoted as of 1-Feb-2008 + "{}" // guessing that these are problems too + ; + + // if the string to be wrapped (wrapee) has a delimiter in it, + // return the quote_char so caller wraps the wrapee. + if( strchr( quoteThese, *wrapee ) ) + return quote_char; + + if( !isFirst && '-' == *wrapee ) + return quote_char; + } + + return ""; // caller does not need to wrap, can use an unwrapped string. +} + + +const char* OUTPUTFORMATTER::GetQuoteChar( const char* wrapee ) +{ + return GetQuoteChar( wrapee, quoteChar ); +} + +int OUTPUTFORMATTER::vprint( const char* fmt, va_list ap ) throw( IO_ERROR ) +{ + // This function can call vsnprintf twice. + // But internally, vsnprintf retrieves arguments from the va_list identified by arg as if + // va_arg was used on it, and thus the state of the va_list is likely to be altered by the call. + // see: www.cplusplus.com/reference/cstdio/vsnprintf + // we make a copy of va_list ap for the second call, if happens + va_list tmp; + va_copy( tmp, ap ); + int ret = vsnprintf( &buffer[0], buffer.size(), fmt, ap ); + + if( ret >= (int) buffer.size() ) + { + buffer.resize( ret + 1000 ); + ret = vsnprintf( &buffer[0], buffer.size(), fmt, tmp ); + } + + va_end( tmp ); // Release the temporary va_list, initialised from ap + + if( ret > 0 ) + write( &buffer[0], ret ); + + return ret; +} + + +int OUTPUTFORMATTER::sprint( const char* fmt, ... ) throw( IO_ERROR ) +{ + va_list args; + + va_start( args, fmt ); + int ret = vprint( fmt, args); + va_end( args ); + + return ret; +} + + +int OUTPUTFORMATTER::Print( int nestLevel, const char* fmt, ... ) throw( IO_ERROR ) +{ +#define NESTWIDTH 2 ///< how many spaces per nestLevel + + va_list args; + + va_start( args, fmt ); + + int result = 0; + int total = 0; + + for( int i=0; i<nestLevel; ++i ) + { + // no error checking needed, an exception indicates an error. + result = sprint( "%*c", NESTWIDTH, ' ' ); + + total += result; + } + + // no error checking needed, an exception indicates an error. + result = vprint( fmt, args ); + + va_end( args ); + + total += result; + return total; +} + + +std::string OUTPUTFORMATTER::Quotes( const std::string& aWrapee ) throw( IO_ERROR ) +{ + static const char quoteThese[] = "\t ()\n\r"; + + if( !aWrapee.size() || // quote null string as "" + aWrapee[0]=='#' || // quote a potential s-expression comment, so it is not a comment + aWrapee[0]=='"' || // NextTok() will travel through DSN_STRING path anyway, then must apply escapes + aWrapee.find_first_of( quoteThese ) != std::string::npos ) + { + std::string ret; + + ret.reserve( aWrapee.size()*2 + 2 ); + + ret += '"'; + + for( std::string::const_iterator it = aWrapee.begin(); it!=aWrapee.end(); ++it ) + { + switch( *it ) + { + case '\n': + ret += '\\'; + ret += 'n'; + break; + case '\r': + ret += '\\'; + ret += 'r'; + break; + case '\\': + ret += '\\'; + ret += '\\'; + break; + case '"': + ret += '\\'; + ret += '"'; + break; + default: + ret += *it; + } + } + + ret += '"'; + + return ret; + } + + return aWrapee; +} + + +std::string OUTPUTFORMATTER::Quotew( const wxString& aWrapee ) throw( IO_ERROR ) +{ + // wxStrings are always encoded as UTF-8 as we convert to a byte sequence. + // The non-virutal function calls the virtual workhorse function, and if + // a different quoting or escaping strategy is desired from the standard, + // a derived class can overload Quotes() above, but + // should never be a reason to overload this Quotew() here. + return Quotes( (const char*) aWrapee.utf8_str() ); +} + + +//-----<STRING_FORMATTER>---------------------------------------------------- + +void STRING_FORMATTER::write( const char* aOutBuf, int aCount ) throw( IO_ERROR ) +{ + mystring.append( aOutBuf, aCount ); +} + +void STRING_FORMATTER::StripUseless() +{ + std::string copy = mystring; + + mystring.clear(); + + for( std::string::iterator i=copy.begin(); i!=copy.end(); ++i ) + { + if( !isspace( *i ) && *i!=')' && *i!='(' && *i!='"' ) + { + mystring += *i; + } + } +} + +//-----<FILE_OUTPUTFORMATTER>---------------------------------------- + +FILE_OUTPUTFORMATTER::FILE_OUTPUTFORMATTER( const wxString& aFileName, + const wxChar* aMode, char aQuoteChar ) throw( IO_ERROR ) : + OUTPUTFORMATTER( OUTPUTFMTBUFZ, aQuoteChar ), + m_filename( aFileName ) +{ + m_fp = wxFopen( aFileName, aMode ); + + if( !m_fp ) + { + wxString msg = wxString::Format( + _( "cannot open or save file '%s'" ), + m_filename.GetData() ); + THROW_IO_ERROR( msg ); + } +} + + +FILE_OUTPUTFORMATTER::~FILE_OUTPUTFORMATTER() +{ + if( m_fp ) + fclose( m_fp ); +} + + +void FILE_OUTPUTFORMATTER::write( const char* aOutBuf, int aCount ) throw( IO_ERROR ) +{ + if( 1 != fwrite( aOutBuf, aCount, 1, m_fp ) ) + { + wxString msg = wxString::Format( + _( "error writing to file '%s'" ), + m_filename.GetData() ); + THROW_IO_ERROR( msg ); + } +} + + +//-----<STREAM_OUTPUTFORMATTER>-------------------------------------- + +void STREAM_OUTPUTFORMATTER::write( const char* aOutBuf, int aCount ) throw( IO_ERROR ) +{ + int lastWrite; + + // This might delay awhile if you were writing to say a socket, but for + // a file it should only go through the loop once. + for( int total = 0; total<aCount; total += lastWrite ) + { + lastWrite = os.Write( aOutBuf, aCount ).LastWrite(); + + if( !os.IsOk() ) + { + THROW_IO_ERROR( _( "OUTPUTSTREAM_OUTPUTFORMATTER write error" ) ); + } + } +} + diff --git a/common/search_stack.cpp b/common/search_stack.cpp new file mode 100644 index 0000000..9982f41 --- /dev/null +++ b/common/search_stack.cpp @@ -0,0 +1,210 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 CERN + * Copyright (C) 2014-2015 KiCad Developers, see CHANGELOG.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 <macros.h> +#include <search_stack.h> +#include <wx/tokenzr.h> + + +#if defined(__MINGW32__) + #define PATH_SEPS wxT( ";\r\n" ) +#else + #define PATH_SEPS wxT( ":;\r\n" ) // unix == linux | mac +#endif + + +int SEARCH_STACK::Split( wxArrayString* aResult, const wxString aPathString ) +{ + wxStringTokenizer tokenizer( aPathString, PATH_SEPS, wxTOKEN_STRTOK ); + + while( tokenizer.HasMoreTokens() ) + { + wxString path = tokenizer.GetNextToken(); + + aResult->Add( path ); + } + + return aResult->GetCount(); +} + + +// Convert aRelativePath to an absolute path based on aBaseDir +static wxString base_dir( const wxString& aRelativePath, const wxString& aBaseDir ) +{ + wxFileName fn = aRelativePath; + + if( !fn.IsAbsolute() && !!aBaseDir ) + { + wxASSERT_MSG( wxFileName( aBaseDir ).IsAbsolute(), wxT( "Must pass absolute path in aBaseDir" ) ); + fn.MakeRelativeTo( aBaseDir ); + } + + return fn.GetFullPath(); +} + + +wxString SEARCH_STACK::FilenameWithRelativePathInSearchList( + const wxString& aFullFilename, const wxString& aBaseDir ) +{ + wxFileName fn = aFullFilename; + wxString filename = aFullFilename; + + unsigned pathlen = fn.GetPath().Len(); // path len, used to find the better (shortest) + // subpath within defaults paths + + for( unsigned kk = 0; kk < GetCount(); kk++ ) + { + fn = aFullFilename; + + // Search for the shortest subpath within 'this': + if( fn.MakeRelativeTo( base_dir( (*this)[kk], aBaseDir ) ) ) + { + if( fn.GetPathWithSep().StartsWith( wxT("..") ) ) // Path outside kicad libs paths + continue; + + if( pathlen > fn.GetPath().Len() ) // A better (shortest) subpath is found + { + filename = fn.GetPathWithSep() + fn.GetFullName(); + pathlen = fn.GetPath().Len(); + } + } + } + + return filename; +} + + +void SEARCH_STACK::RemovePaths( const wxString& aPaths ) +{ + bool isCS = wxFileName::IsCaseSensitive(); + wxArrayString paths; + + Split( &paths, aPaths ); + + for( unsigned i=0; i<paths.GetCount(); ++i ) + { + wxString path = paths[i]; + + if( Index( path, isCS ) != wxNOT_FOUND ) + { + Remove( path ); + } + } +} + + +void SEARCH_STACK::AddPaths( const wxString& aPaths, int aIndex ) +{ + bool isCS = wxFileName::IsCaseSensitive(); + wxArrayString paths; + + Split( &paths, aPaths ); + + // appending all of them, on large or negative aIndex + if( unsigned( aIndex ) >= GetCount() ) + { + for( unsigned i=0; i<paths.GetCount(); ++i ) + { + wxString path = paths[i]; + + if( wxFileName::IsDirReadable( path ) + && Index( path, isCS ) == wxNOT_FOUND ) + { + Add( path ); + } + } + } + + // inserting all of them: + else + { + for( unsigned i=0; i<paths.GetCount(); ++i ) + { + wxString path = paths[i]; + + if( wxFileName::IsDirReadable( path ) + && Index( path, isCS ) == wxNOT_FOUND ) + { + Insert( path, aIndex ); + aIndex++; + } + } + } +} + + +#if 1 // this function is too convoluted for words. + +const wxString SEARCH_STACK::LastVisitedPath( const wxString& aSubPathToSearch ) +{ + wxString path; + + // Initialize default path to the main default lib path + // this is the second path in list (the first is the project path). + unsigned pcount = GetCount(); + + if( pcount ) + { + unsigned ipath = 0; + + if( (*this)[0] == wxGetCwd() ) + ipath = 1; + + // First choice of path: + if( ipath < pcount ) + path = (*this)[ipath]; + + // Search a sub path matching this SEARCH_PATH + if( !IsEmpty() ) + { + for( ; ipath < pcount; ipath++ ) + { + if( (*this)[ipath].Contains( aSubPathToSearch ) ) + { + path = (*this)[ipath]; + break; + } + } + } + } + + if( path.IsEmpty() ) + path = wxGetCwd(); + + return path; +} +#endif + + +#if defined(DEBUG) +void SEARCH_STACK::Show( const wxString& aPrefix ) const +{ + wxLogDebug( wxT( "%s SEARCH_STACK:" ), GetChars( aPrefix ) ); + + for( unsigned i=0; i<GetCount(); ++i ) + { + wxLogDebug( wxT( " [%2u]:%s" ), i, TO_UTF8( (*this)[i] ) ); + } +} +#endif diff --git a/common/searchhelpfilefullpath.cpp b/common/searchhelpfilefullpath.cpp new file mode 100644 index 0000000..3d16255 --- /dev/null +++ b/common/searchhelpfilefullpath.cpp @@ -0,0 +1,187 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014-2015 CERN + * Copyright (C) 2014-2015 KiCad Developers, see CHANGELOG.TXT for contributors. + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 <pgm_base.h> +#include <common.h> +#include <config.h> // to define DEFAULT_INSTALL_PATH +#include <macros.h> + + +/** + * Function FindFileInSearchPaths + * looks in "this" for \a aFilename, but first modifies every search + * path by appending a list of path fragments from aSubdirs. That modification + * is not relative. + */ +wxString FindFileInSearchPaths( const SEARCH_STACK& aStack, + const wxString& aFilename, const wxArrayString* aSubdirs ) +{ + wxPathList paths; + + for( unsigned i = 0; i < aStack.GetCount(); ++i ) + { + wxFileName fn( aStack[i], wxEmptyString ); + + if( aSubdirs ) + { + for( unsigned j = 0; j < aSubdirs->GetCount(); j++ ) + fn.AppendDir( (*aSubdirs)[j] ); + } + + wxLogDebug( wxT( " %s" ), GetChars( fn.GetFullPath() ) ); + + if( fn.DirExists() ) + { + paths.Add( fn.GetPath() ); + } + } + + return paths.FindValidPath( aFilename ); +} + + +// See also FindKicadHelpPath.cpp.notused. +wxString SearchHelpFileFullPath( const SEARCH_STACK& aSStack, const wxString& aBaseName ) +{ + wxArrayString subdirs; + wxArrayString altsubdirs; + SEARCH_STACK ss = aSStack; + + // It might already be in aSStack, but why depend on other code + // far away when it's so easy to add it again (to our copy) as the first place to look. + + // This is CMAKE_INSTALL_PREFIX unless DEFAULT_INSTALL_PATH was defined during + // build configuration: + ss.AddPaths( wxT( DEFAULT_INSTALL_PATH ), 0 ); + +#if defined(__WXMAC__) + ss.AddPaths( GetOSXKicadMachineDataDir() ); + ss.AddPaths( Pgm().GetExecutablePath(), 0 ); + + // OS X packages can have the help files in + // /Library/Application\ Support/kicad/help, + // and in Contents/SharedSupport/help inside the + // bundle. + // Below we account for an international subdirectory. + subdirs.Add( wxT( "help" ) ); + altsubdirs.Add( wxT( "Contents" ) ); + altsubdirs.Add( wxT( "SharedSupport" ) ); + altsubdirs.Add( wxT( "help" ) ); +#endif + +#if ! defined(__WXMAC__) // && defined(__linux__) + // This is the executable path minus the trailing bin directory used on Windows and Linux. + wxFileName tmp( Pgm().GetExecutablePath(), wxEmptyString ); + wxArrayString binDirs = tmp.GetDirs(); + + if( !binDirs.IsEmpty() && binDirs[ binDirs.GetCount() - 1 ].CmpNoCase( wxT( "bin" ) ) == 0 ) + tmp.RemoveLastDir(); + + ss.AddPaths( tmp.GetPath(), 0 ); + + // Based on kicad-doc.bzr/CMakeLists.txt, line 20, the help files are + // installed into "<CMAKE_INSTALL_PREFIX>/share/doc/kicad/help" for linux. + // This is ${KICAD_HELP} var in that CMakeLists.txt file. + // Below we account for an international subdirectory. + subdirs.Add( wxT( "share" ) ); + subdirs.Add( wxT( "doc" ) ); + subdirs.Add( wxT( "kicad" ) ); + subdirs.Add( wxT( "help" ) ); + + // Based on kicad-doc.bzr/CMakeLists.txt, line 35, the help files are + // installed into "<CMAKE_INSTALL_PREFIX>/doc/help" for Windows. + // This is ${KICAD_HELP} var in that CMakeLists.txt file. + // Below we account for an international subdirectory. + altsubdirs.Add( wxT( "doc" ) ); + altsubdirs.Add( wxT( "help" ) ); +#endif + + // If there's a KICAD environment variable set, always use that guy's path first. + if( !Pgm().GetKicadEnvVariable().IsEmpty() ) + ss.AddPaths( Pgm().GetKicadEnvVariable(), 0 ); + + /* Search for a help file. + * we *must* find a help file. + * so help is searched in directories in this order: + * help/<canonical name> like help/en_GB + * help/<short name> like help/en + * help/en + */ + + wxLocale* i18n = Pgm().GetLocale(); + + // We try to find help file in help/<canonical name> + // If fails, try to find help file in help/<short canonical name> + // If fails, try to find help file in help/en + wxArrayString locale_name_dirs; + locale_name_dirs.Add( i18n->GetCanonicalName() ); // canonical name like fr_FR + + // wxLocale::GetName() does not return always the short name + locale_name_dirs.Add( i18n->GetName().BeforeLast( '_' ) ); // short canonical name like fr + locale_name_dirs.Add( wxT( "en" ) ); // default (en) + +#if defined(DEBUG) && 1 + ss.Show( wxString( __func__ ) ); + wxLogDebug( wxT( "%s: m_help_file:'%s'" ), __func__, GetChars( aBaseName ) ); +#endif + + wxLogDebug( wxT( "Checking SEARCH_STACK for file %s" ), GetChars( aBaseName ) ); + + // Help files can be html (.html ext) or pdf (.pdf ext) files. + // Therefore, <BaseName>.html file is searched and if not found, + // <BaseName>.pdf file is searched in the same paths + wxString fn; + + for( unsigned ii = 0; ii < locale_name_dirs.GetCount(); ii++ ) + { + subdirs.Add( locale_name_dirs[ii] ); + altsubdirs.Add( locale_name_dirs[ii] ); + + fn = FindFileInSearchPaths( ss, aBaseName + wxT( ".html" ), &altsubdirs ); + + if( !fn.IsEmpty() ) + break; + + fn = FindFileInSearchPaths( ss, aBaseName + wxT( ".pdf" ), &altsubdirs ); + + if( !fn.IsEmpty() ) + break; + + fn = FindFileInSearchPaths( ss, aBaseName + wxT( ".html" ), &subdirs ); + + if( !fn.IsEmpty() ) + break; + + fn = FindFileInSearchPaths( ss, aBaseName + wxT( ".pdf" ), &subdirs ); + + if( !fn.IsEmpty() ) + break; + + subdirs.RemoveAt( subdirs.GetCount() - 1 ); + altsubdirs.RemoveAt( altsubdirs.GetCount() - 1 ); + } + + return fn; +} diff --git a/common/selcolor.cpp b/common/selcolor.cpp new file mode 100644 index 0000000..f3eb039 --- /dev/null +++ b/common/selcolor.cpp @@ -0,0 +1,264 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.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 + */ + + +/* Dialog for selecting color from the palette of available colors. + */ + +#include <fctsys.h> +#include <common.h> +#include <colors.h> + +#include <wx/statline.h> + + +enum colors_id { + ID_COLOR_BLACK = 2000 // ID_COLOR_ = ID_COLOR_BLACK a ID_COLOR_BLACK + 31 +}; + + +class WinEDA_SelColorFrame : public wxDialog +{ +public: + WinEDA_SelColorFrame( wxWindow* parent, + const wxPoint& framepos, int OldColor ); + ~WinEDA_SelColorFrame() {}; + +private: + void Init_Dialog( int aOldColor ); + void OnCancel( wxCommandEvent& event ); + void SelColor( wxCommandEvent& event ); + + DECLARE_EVENT_TABLE() +}; + + +BEGIN_EVENT_TABLE( WinEDA_SelColorFrame, wxDialog ) + EVT_BUTTON( wxID_CANCEL, WinEDA_SelColorFrame::OnCancel ) + EVT_COMMAND_RANGE( ID_COLOR_BLACK, ID_COLOR_BLACK + 31, + wxEVT_COMMAND_BUTTON_CLICKED, + WinEDA_SelColorFrame::SelColor ) +END_EVENT_TABLE() + + +EDA_COLOR_T DisplayColorFrame( wxWindow* parent, int OldColor ) +{ + wxPoint framepos; + EDA_COLOR_T color; + + wxGetMousePosition( &framepos.x, &framepos.y ); + + WinEDA_SelColorFrame* frame = new WinEDA_SelColorFrame( parent, + framepos, OldColor ); + color = static_cast<EDA_COLOR_T>( frame->ShowModal() ); + frame->Destroy(); + if( color > NBCOLORS ) + color = UNSPECIFIED_COLOR; + return color; +} + + +WinEDA_SelColorFrame::WinEDA_SelColorFrame( wxWindow* parent, + const wxPoint& framepos, + int OldColor ) : + wxDialog( parent, -1, _( "Colors" ), framepos, wxDefaultSize, + wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ) +{ + + Init_Dialog( OldColor ); + // Resize the dialog + GetSizer()->SetSizeHints( this ); + + // Ensure the whole frame is visible, whenever the asked position. + // Moreover with a work station having dual monitors, the asked position can be relative to a monitor + // and this frame can be displayed on the other monitor, with an "out of screen" position. + // Give also a small margin. + int margin = 10; + wxPoint windowPosition = GetPosition(); + if( framepos != wxDefaultPosition ) + { + if( windowPosition.x < margin ) + windowPosition.x = margin; + // Under MACOS, a vertical margin >= 20 is needed by the system menubar + int v_margin = std::max(20, margin); + if( windowPosition.y < v_margin ) + windowPosition.y = v_margin; + if( windowPosition != framepos ) + SetPosition(windowPosition); + } + wxPoint endCornerPosition = GetPosition(); + endCornerPosition.x += GetSize().x + margin; + endCornerPosition.y += GetSize().y + margin; + + windowPosition = GetPosition(); + wxRect freeScreenArea( wxGetClientDisplayRect( ) ); + + if( freeScreenArea.GetRight() < endCornerPosition.x ) + { + windowPosition.x += freeScreenArea.GetRight() - endCornerPosition.x; + + if( windowPosition.x < freeScreenArea.x ) + windowPosition.x = freeScreenArea.x; + + // Sligly modify the vertical position to avoid the mouse to be + // exactly on the upper side of the window + windowPosition.y +=5; + endCornerPosition.y += 5; + } + + if( freeScreenArea.GetBottom() < endCornerPosition.y ) + { + windowPosition.y += freeScreenArea.GetBottom() - endCornerPosition.y; + + if( windowPosition.y < freeScreenArea.y ) + windowPosition.y = freeScreenArea.y; + } + + SetPosition(windowPosition); +} + +void WinEDA_SelColorFrame::Init_Dialog( int aOldColor ) +{ + wxBoxSizer* OuterBoxSizer = NULL; + wxBoxSizer* MainBoxSizer = NULL; + wxFlexGridSizer* FlexColumnBoxSizer = NULL; + wxBitmapButton* BitmapButton = NULL; + wxStaticLine* Line = NULL; + wxStdDialogButtonSizer* StdDialogButtonSizer = NULL; + wxButton* Button = NULL; + + int ii, butt_ID; + int w = 20, h = 20; + bool ColorFound = false; + + SetReturnCode( -1 ); + + OuterBoxSizer = new wxBoxSizer( wxVERTICAL ); + SetSizer( OuterBoxSizer ); + + MainBoxSizer = new wxBoxSizer( wxHORIZONTAL ); + OuterBoxSizer->Add( MainBoxSizer, 1, wxGROW | wxLEFT | wxRIGHT | wxTOP, 5 ); + + for( ii = 0; ii < NBCOLORS; ++ii ) + { + // Provide a separate column for every six buttons (and their + // associated text strings), so provide a FlexGrid Sizer with + // eight rows and two columns. + if( ii % 6 == 0 ) + { + FlexColumnBoxSizer = new wxFlexGridSizer( 6, 2, 0, 0 ); + + // Specify that all of the rows can be expanded. + for( int ii = 0; ii < 6; ii++ ) + { + FlexColumnBoxSizer->AddGrowableRow( ii ); + } + + // Specify that the second column can also be expanded. + FlexColumnBoxSizer->AddGrowableCol( 1 ); + + MainBoxSizer->Add( FlexColumnBoxSizer, 1, wxGROW | wxTOP, 5 ); + } + + butt_ID = ID_COLOR_BLACK + ii; + wxMemoryDC iconDC; + wxBitmap ButtBitmap( w, h ); + wxBrush brush; + + iconDC.SelectObject( ButtBitmap ); + + EDA_COLOR_T buttcolor = g_ColorRefs[ii].m_Numcolor; + + iconDC.SetPen( *wxBLACK_PEN ); + ColorSetBrush( &brush, buttcolor ); + brush.SetStyle( wxBRUSHSTYLE_SOLID ); + + iconDC.SetBrush( brush ); + iconDC.SetBackground( *wxGREY_BRUSH ); + iconDC.Clear(); + iconDC.DrawRoundedRectangle( 0, 0, w, h, (double) h / 3 ); + + BitmapButton = new wxBitmapButton( this, butt_ID, ButtBitmap, + wxDefaultPosition, wxSize( w+8, h+6 ) ); + FlexColumnBoxSizer->Add( BitmapButton, 0, + wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | + wxLEFT | wxBOTTOM, 5 ); + + // Set focus to this button if its color matches the + // color which had been selected previously (for + // whichever layer's color is currently being edited). + if( aOldColor == buttcolor ) + { + ColorFound = true; + BitmapButton->SetFocus(); + } + + wxStaticText* label = new wxStaticText( this, -1, + wxGetTranslation( ColorGetName( buttcolor ) ), + wxDefaultPosition, wxDefaultSize, 0 ); + FlexColumnBoxSizer->Add( label, 1, + wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | + wxLEFT | wxRIGHT | wxBOTTOM, 5 ); + } + + // Provide a Cancel button as well, so that this dialog + // box can also be canceled by pressing the Esc key + // (and also provide a horizontal static line to separate + // that button from all of the other buttons). + + Line = new wxStaticLine( this, -1, wxDefaultPosition, + wxDefaultSize, wxLI_HORIZONTAL ); + OuterBoxSizer->Add( Line, 0, wxGROW | wxLEFT | wxRIGHT | wxTOP, 5 ); + + StdDialogButtonSizer = new wxStdDialogButtonSizer; + OuterBoxSizer->Add( StdDialogButtonSizer, 0, wxGROW | wxALL, 10 ); + + Button = new wxButton( this, wxID_CANCEL, _( "Cancel" ), wxDefaultPosition, + wxDefaultSize, 0 ); + StdDialogButtonSizer->AddButton( Button ); + + StdDialogButtonSizer->Realize(); + + // Set focus to the Cancel button if the currently selected color + // does not match any of the colors provided by this dialog box. + // (That shouldn't ever happen in practice though.) + if( !ColorFound ) + Button->SetFocus(); +} + +void WinEDA_SelColorFrame::OnCancel( wxCommandEvent& WXUNUSED( event ) ) +{ + // Setting the return value to -1 indicates that the + // dialog box has been canceled (and thus that the + // previously selected color is to be retained). + EndModal( -1 ); +} + + +void WinEDA_SelColorFrame::SelColor( wxCommandEvent& event ) +{ + int id = event.GetId(); + + EndModal( id - ID_COLOR_BLACK ); +} diff --git a/common/single_top.cpp b/common/single_top.cpp new file mode 100644 index 0000000..7016501 --- /dev/null +++ b/common/single_top.cpp @@ -0,0 +1,308 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.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 + */ + + +/* + + This is a program launcher for a single KIFACE DSO. It only mimics a KIWAY, + not actually implements one, since only a single DSO is supported by it. + + It is compiled multiple times, once for each standalone program and as such + gets different compiler command line supplied #defines from CMake. + +*/ + + +#include <typeinfo> +#include <macros.h> +#include <fctsys.h> +#include <wx/filename.h> +#include <wx/stdpaths.h> +#include <wx/snglinst.h> + +#include <kiway.h> +#include <pgm_base.h> +#include <kiway_player.h> +#include <confirm.h> + + +// Only a single KIWAY is supported in this single_top top level component, +// which is dedicated to loading only a single DSO. +KIWAY Kiway( &Pgm(), KFCTL_STANDALONE ); + + +// implement a PGM_BASE and a wxApp side by side: + +/** + * Struct PGM_SINGLE_TOP + * implements PGM_BASE with its own OnPgmInit() and OnPgmExit(). + */ +static struct PGM_SINGLE_TOP : public PGM_BASE +{ + bool OnPgmInit( wxApp* aWxApp ); // overload PGM_BASE virtual + void OnPgmExit(); // overload PGM_BASE virtual + void MacOpenFile( const wxString& aFileName ); // overload PGM_BASE virtual +} program; + + +PGM_BASE& Pgm() +{ + return program; +} + + +/** + * Struct APP_SINGLE_TOP + * implements a bare naked wxApp (so that we don't become dependent on + * functionality in a wxApp derivative that we cannot deliver under wxPython). + */ +struct APP_SINGLE_TOP : public wxApp +{ +#if defined (__LINUX__) + APP_SINGLE_TOP(): wxApp() + { + // Disable proxy menu in Unity window manager. Only usual menubar works with wxWidgets (at least <= 3.1) + // When the proxy menu menubar is enable, some important things for us do not work: menuitems UI events and shortcuts. + wxString wm; + + if( wxGetEnv( wxT( "XDG_CURRENT_DESKTOP" ), &wm ) && wm.CmpNoCase( wxT( "Unity" ) ) == 0 ) + { + wxSetEnv ( wxT("UBUNTU_MENUPROXY" ), wxT( "0" ) ); + } + } +#endif + + bool OnInit() // overload wxApp virtual + { + try + { + return Pgm().OnPgmInit( this ); + } + catch( const std::exception& e ) + { + wxLogError( wxT( "Unhandled exception class: %s what: %s" ), + GetChars( FROM_UTF8( typeid(e).name() )), + GetChars( FROM_UTF8( e.what() ) ) ); + } + catch( const IO_ERROR& ioe ) + { + wxLogError( GetChars( ioe.errorText ) ); + } + catch(...) + { + wxLogError( wxT( "Unhandled exception of unknown type" ) ); + } + + Pgm().OnPgmExit(); + + return false; + } + + int OnExit() // overload wxApp virtual + { + // Fixes segfault when wxPython scripting is enabled. +#if defined( KICAD_SCRIPTING_WXPYTHON ) + Pgm().OnPgmExit(); +#endif + return wxApp::OnExit(); + } + + int OnRun() // overload wxApp virtual + { + int ret = -1; + + try + { + ret = wxApp::OnRun(); + } + catch( const std::exception& e ) + { + wxLogError( wxT( "Unhandled exception class: %s what: %s" ), + GetChars( FROM_UTF8( typeid(e).name() )), + GetChars( FROM_UTF8( e.what() ) ) ); + } + catch( const IO_ERROR& ioe ) + { + wxLogError( GetChars( ioe.errorText ) ); + } + catch(...) + { + wxLogError( wxT( "Unhandled exception of unknown type" ) ); + } + + // Works properly when wxPython scripting is disabled. +#if !defined( KICAD_SCRIPTING_WXPYTHON ) + Pgm().OnPgmExit(); +#endif + + return ret; + } + + /** + * Function MacOpenFile + * is specific to MacOSX (not used under Linux or Windows). + * MacOSX requires it for file association. + * @see http://wiki.wxwidgets.org/WxMac-specific_topics + */ + void MacOpenFile( const wxString& aFileName ) // overload wxApp virtual + { + Pgm().MacOpenFile( aFileName ); + } +}; + +IMPLEMENT_APP( APP_SINGLE_TOP ); + + +bool PGM_SINGLE_TOP::OnPgmInit( wxApp* aWxApp ) +{ + // first thing: set m_wx_app + m_wx_app = aWxApp; + + wxString absoluteArgv0 = wxStandardPaths::Get().GetExecutablePath(); + + if( !wxIsAbsolutePath( absoluteArgv0 ) ) + { + wxLogError( wxT( "No meaningful argv[0]" ) ); + return false; + } + + if( !initPgm() ) + return false; + +#if !defined(BUILD_KIWAY_DLL) + // Get the getter, it is statically linked into this binary image. + KIFACE_GETTER_FUNC* getter = &KIFACE_GETTER; + + int kiface_version; + + // Get the KIFACE. + KIFACE* kiface = getter( &kiface_version, KIFACE_VERSION, this ); + + // Trick the KIWAY into thinking it loaded a KIFACE, by recording the KIFACE + // in the KIWAY. It needs to be there for KIWAY::OnKiwayEnd() anyways. + Kiway.set_kiface( KIWAY::KifaceType( TOP_FRAME ), kiface ); +#endif + + // Use KIWAY to create a top window, which registers its existence also. + // "TOP_FRAME" is a macro that is passed on compiler command line from CMake, + // and is one of the types in FRAME_T. + KIWAY_PLAYER* frame = Kiway.Player( TOP_FRAME, true ); + + Kiway.SetTop( frame ); + + App().SetTopWindow( frame ); // wxApp gets a face. + + // Open project or file specified on the command line: + int argc = App().argc; + + if( argc > 1 ) + { + /* + gerbview handles multiple project data files, i.e. gerber files on + cmd line. Others currently do not, they handle only one. For common + code simplicity we simply pass all the arguments in however, each + program module can do with them what they want, ignore, complain + whatever. We don't establish policy here, as this is a multi-purpose + launcher. + */ + + std::vector<wxString> argSet; + + for( int i=1; i<argc; ++i ) + { + argSet.push_back( App().argv[i] ); + } + + // special attention to the first argument: argv[1] (==argSet[0]) + wxFileName argv1( argSet[0] ); + + if( argc == 2 ) + { +#if defined(PGM_DATA_FILE_EXT) + // PGM_DATA_FILE_EXT, if present, may be different for each compile, + // it may come from CMake on the compiler command line, but often does not. + // This facillity is mostly useful for those program modules + // supporting a single argv[1]. + if( !argv1.GetExt() ) + argv1.SetExt( wxT( PGM_DATA_FILE_EXT ) ); + +#endif + argv1.MakeAbsolute(); + + argSet[0] = argv1.GetFullPath(); + } + + // Use the KIWAY_PLAYER::OpenProjectFiles() API function: + if( !frame->OpenProjectFiles( argSet ) ) + { + // OpenProjectFiles() API asks that it report failure to the UI. + // Nothing further to say here. + + // We've already initialized things at this point, but wx won't call OnExit if + // we fail out. Call our own cleanup routine here to ensure the relevant resources + // are freed at the right time (if they aren't, segfaults will occur). + OnPgmExit(); + + // Fail the process startup if the file could not be opened, + // although this is an optional choice, one that can be reversed + // also in the KIFACE specific OpenProjectFiles() return value. + return false; + } + } + + frame->Show(); + + return true; +} + + +void PGM_SINGLE_TOP::OnPgmExit() +{ + Kiway.OnKiwayEnd(); + + saveCommonSettings(); + + // Destroy everything in PGM_BASE, especially wxSingleInstanceCheckerImpl + // earlier than wxApp and earlier than static destruction would. + PGM_BASE::destroy(); +} + + +void PGM_SINGLE_TOP::MacOpenFile( const wxString& aFileName ) +{ + wxFileName filename( aFileName ); + + if( filename.FileExists() ) + { +#if 0 + // this pulls in EDA_DRAW_FRAME type info, which we don't want in + // the single_top link image. + KIWAY_PLAYER* frame = dynamic_cast<KIWAY_PLAYER*>( App().GetTopWindow() ); +#else + KIWAY_PLAYER* frame = (KIWAY_PLAYER*) App().GetTopWindow(); +#endif + if( frame ) + frame->OpenProjectFiles( std::vector<wxString>( 1, aFileName ) ); + } +} diff --git a/common/string.cpp b/common/string.cpp new file mode 100644 index 0000000..d0bfe33 --- /dev/null +++ b/common/string.cpp @@ -0,0 +1,485 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2004 KiCad Developers, see change_log.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 + */ + +/** + * @file string.cpp + * @brief Some useful functions to handle strings. + */ + +#include <fctsys.h> +#include <macros.h> +#include <richio.h> // StrPrintf +#include <kicad_string.h> + + +/** + * Illegal file name characters used to insure file names will be valid on all supported + * platforms. This is the list of illegal file name characters for Windows which includes + * the illegal file name characters for Linux and OSX. + */ +static const char illegalFileNameChars[] = "\\/:\"<>|"; + + +int ReadDelimitedText( wxString* aDest, const char* aSource ) +{ + std::string utf8; // utf8 but without escapes and quotes. + bool inside = false; + const char* start = aSource; + char cc; + + while( (cc = *aSource++) != 0 ) + { + if( cc == '"' ) + { + if( inside ) + break; // 2nd double quote is end of delimited text + + inside = true; // first delimiter found, make note, do not copy + } + + else if( inside ) + { + if( cc == '\\' ) + { + cc = *aSource++; + + if( !cc ) + break; + + // do no copy the escape byte if it is followed by \ or " + if( cc != '"' && cc != '\\' ) + utf8 += '\\'; + + utf8 += cc; + } + else + { + utf8 += cc; + } + } + } + + *aDest = FROM_UTF8( utf8.c_str() ); + + return aSource - start; +} + + +int ReadDelimitedText( char* aDest, const char* aSource, int aDestSize ) +{ + if( aDestSize <= 0 ) + return 0; + + bool inside = false; + const char* start = aSource; + char* limit = aDest + aDestSize - 1; + char cc; + + while( (cc = *aSource++) != 0 && aDest < limit ) + { + if( cc == '"' ) + { + if( inside ) + break; // 2nd double quote is end of delimited text + + inside = true; // first delimiter found, make note, do not copy + } + + else if( inside ) + { + if( cc == '\\' ) + { + cc = *aSource++; + + if( !cc ) + break; + + // do no copy the escape byte if it is followed by \ or " + if( cc != '"' && cc != '\\' ) + *aDest++ = '\\'; + + if( aDest < limit ) + *aDest++ = cc; + } + else + { + *aDest++ = cc; + } + } + } + + *aDest = 0; + + return aSource - start; +} + + +std::string EscapedUTF8( const wxString& aString ) +{ + std::string utf8 = TO_UTF8( aString ); + + std::string ret; + + ret += '"'; + + for( std::string::const_iterator it = utf8.begin(); it!=utf8.end(); ++it ) + { + // this escaping strategy is designed to be compatible with ReadDelimitedText(): + if( *it == '"' ) + { + ret += '\\'; + ret += '"'; + } + else if( *it == '\\' ) + { + ret += '\\'; // double it up + ret += '\\'; + } + else + { + ret += *it; + } + } + + ret += '"'; + + return ret; +} + + +char* StrPurge( char* text ) +{ + static const char whitespace[] = " \t\n\r\f\v"; + + if( text ) + { + while( *text && strchr( whitespace, *text ) ) + ++text; + + char* cp = text + strlen( text ) - 1; + + while( cp >= text && strchr( whitespace, *cp ) ) + *cp-- = '\0'; + } + + return text; +} + + +char* GetLine( FILE* File, char* Line, int* LineNum, int SizeLine ) +{ + do { + if( fgets( Line, SizeLine, File ) == NULL ) + return NULL; + + if( LineNum ) + *LineNum += 1; + + } while( Line[0] == '#' || Line[0] == '\n' || Line[0] == '\r' || Line[0] == 0 ); + + strtok( Line, "\n\r" ); + return Line; +} + + +wxString DateAndTime() +{ + wxDateTime datetime = wxDateTime::Now(); + + datetime.SetCountry( wxDateTime::Country_Default ); + return datetime.Format( wxDefaultDateTimeFormat, wxDateTime::Local ); +} + + +int StrNumCmp( const wxString& aString1, const wxString& aString2, int aLength, bool aIgnoreCase ) +{ + int i; + int nb1 = 0, nb2 = 0; + + wxString::const_iterator str1 = aString1.begin(), str2 = aString2.begin(); + + if( ( str1 == aString1.end() ) || ( str2 == aString2.end() ) ) + return 0; + + for( i = 0; i < aLength; i++ ) + { + if( isdigit( *str1 ) && isdigit( *str2 ) ) /* digit found */ + { + nb1 = 0; + nb2 = 0; + + while( isdigit( *str1 ) ) + { + nb1 = nb1 * 10 + (int) *str1 - '0'; + ++str1; + } + + while( isdigit( *str2 ) ) + { + nb2 = nb2 * 10 + (int) *str2 - '0'; + ++str2; + } + + if( nb1 < nb2 ) + return -1; + + if( nb1 > nb2 ) + return 1; + } + + if( aIgnoreCase ) + { + if( toupper( *str1 ) < toupper( *str2 ) ) + return -1; + + if( toupper( *str1 ) > toupper( *str2 ) ) + return 1; + + if( ( *str1 == 0 ) && ( *str2 == 0 ) ) + return 0; + } + else + { + if( *str1 < *str2 ) + return -1; + + if( *str1 > *str2 ) + return 1; + + if( ( str1 == aString1.end() ) && ( str2 == aString2.end() ) ) + return 0; + } + + ++str1; + ++str2; + } + + return 0; +} + + +bool WildCompareString( const wxString& pattern, const wxString& string_to_tst, + bool case_sensitive ) +{ + const wxChar* cp = NULL, * mp = NULL; + const wxChar* wild, * string; + wxString _pattern, _string_to_tst; + + if( case_sensitive ) + { + wild = pattern.GetData(); + string = string_to_tst.GetData(); + } + else + { + _pattern = pattern; + _pattern.MakeUpper(); + _string_to_tst = string_to_tst; + _string_to_tst.MakeUpper(); + wild = _pattern.GetData(); + string = _string_to_tst.GetData(); + } + + while( ( *string ) && ( *wild != '*' ) ) + { + if( ( *wild != *string ) && ( *wild != '?' ) ) + return false; + + wild++; string++; + } + + while( *string ) + { + if( *wild == '*' ) + { + if( !*++wild ) + return 1; + mp = wild; + cp = string + 1; + } + else if( ( *wild == *string ) || ( *wild == '?' ) ) + { + wild++; + string++; + } + else + { + wild = mp; + string = cp++; + } + } + + while( *wild == '*' ) + { + wild++; + } + + return !*wild; +} + + +int RefDesStringCompare( const wxString& strFWord, const wxString& strSWord ) +{ + // The different sections of the first string + wxString strFWordBeg, strFWordMid, strFWordEnd; + + // The different sections of the second string + wxString strSWordBeg, strSWordMid, strSWordEnd; + + int isEqual = 0; // The numerical results of a string compare + int iReturn = 0; // The variable that is being returned + + long lFirstDigit = 0; // The converted middle section of the first string + long lSecondDigit = 0; // The converted middle section of the second string + + // Split the two strings into separate parts + SplitString( strFWord, &strFWordBeg, &strFWordMid, &strFWordEnd ); + SplitString( strSWord, &strSWordBeg, &strSWordMid, &strSWordEnd ); + + // Compare the Beginning section of the strings + isEqual = strFWordBeg.CmpNoCase( strSWordBeg ); + + if( isEqual > 0 ) + iReturn = 1; + else if( isEqual < 0 ) + iReturn = -1; + else + { + // If the first sections are equal compare their digits + strFWordMid.ToLong( &lFirstDigit ); + strSWordMid.ToLong( &lSecondDigit ); + + if( lFirstDigit > lSecondDigit ) + iReturn = 1; + else if( lFirstDigit < lSecondDigit ) + iReturn = -1; + else + { + // If the first two sections are equal compare the endings + isEqual = strFWordEnd.CmpNoCase( strSWordEnd ); + + if( isEqual > 0 ) + iReturn = 1; + else if( isEqual < 0 ) + iReturn = -1; + else + iReturn = 0; + } + } + + return iReturn; +} + + +int SplitString( wxString strToSplit, + wxString* strBeginning, + wxString* strDigits, + wxString* strEnd ) +{ + // Clear all the return strings + strBeginning->Empty(); + strDigits->Empty(); + strEnd->Empty(); + + // There no need to do anything if the string is empty + if( strToSplit.length() == 0 ) + return 0; + + // Starting at the end of the string look for the first digit + int ii; + + for( ii = (strToSplit.length() - 1); ii >= 0; ii-- ) + { + if( isdigit( strToSplit[ii] ) ) + break; + } + + // If there were no digits then just set the single string + if( ii < 0 ) + { + *strBeginning = strToSplit; + } + else + { + // Since there is at least one digit this is the trailing string + *strEnd = strToSplit.substr( ii + 1 ); + + // Go to the end of the digits + int position = ii + 1; + + for( ; ii >= 0; ii-- ) + { + if( !isdigit( strToSplit[ii] ) ) + break; + } + + // If all that was left was digits, then just set the digits string + if( ii < 0 ) + *strDigits = strToSplit.substr( 0, position ); + + /* We were only looking for the last set of digits everything else is + * part of the preamble */ + else + { + *strDigits = strToSplit.substr( ii + 1, position - ii - 1 ); + *strBeginning = strToSplit.substr( 0, ii + 1 ); + } + } + + return 0; +} + + +wxString GetIllegalFileNameWxChars() +{ + return FROM_UTF8( illegalFileNameChars ); +} + + +bool ReplaceIllegalFileNameChars( std::string* aName, int aReplaceChar ) +{ + bool changed = false; + std::string result; + + for( std::string::iterator it = aName->begin(); it != aName->end(); ++it ) + { + if( strchr( illegalFileNameChars, *it ) ) + { + if( aReplaceChar ) + StrPrintf( &result, "%c", aReplaceChar ); + else + StrPrintf( &result, "%%%02x", *it ); + + changed = true; + } + else + { + result += *it; + } + } + + if( changed ) + *aName = result; + + return changed; +} diff --git a/common/strtok_r.c b/common/strtok_r.c new file mode 100644 index 0000000..a0b0997 --- /dev/null +++ b/common/strtok_r.c @@ -0,0 +1,35 @@ +/* + * public domain strtok_r() + */ + +#include <string.h> + +char* strtok_r( char* str, const char* delim, char** nextp ) +{ + char* ret; + + if( str == NULL ) + { + str = *nextp; + } + + str += strspn( str, delim ); + + if( *str == '\0' ) + { + return NULL; + } + + ret = str; + + str += strcspn( str, delim ); + + if( *str ) + { + *str++ = '\0'; + } + + *nextp = str; + + return ret; +} diff --git a/common/systemdirsappend.cpp b/common/systemdirsappend.cpp new file mode 100644 index 0000000..3ce86dc --- /dev/null +++ b/common/systemdirsappend.cpp @@ -0,0 +1,166 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 CERN + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.TXT for contributors. + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 <wx/stdpaths.h> + +#include <common.h> +#include <search_stack.h> +#include <pgm_base.h> +#include <config.h> // to define DEFAULT_INSTALL_PATH + + +// put your best guesses in here, send the computer on a wild goose chase, its +// got nothing else to do. + +void SystemDirsAppend( SEARCH_STACK* aSearchStack ) +{ + // No clearing is done here, the most general approach is NOT to assume that + // our appends will be the only thing in the stack. This function has no + // knowledge of caller's intentions. + + // wxPathList::AddEnvList() is broken, use SEARCH_STACK::AddPaths(). + // SEARCH_STACK::AddPaths() will verify readability and existence of + // each directory before adding. + SEARCH_STACK maybe; + + // User environment variable path is the first search path. Chances are + // if the user is savvy enough to set an environment variable they know + // what they are doing. It should take precedence over anything else. + // Otherwise don't set it. + maybe.AddPaths( wxGetenv( wxT( "KICAD" ) ) ); + +#ifdef __WXMAC__ + // Add the directory for the user-dependent, program specific data files. + maybe.AddPaths( GetOSXKicadUserDataDir() ); + + // Global machine specific application data + maybe.AddPaths( GetOSXKicadMachineDataDir() ); + + // Global application specific data files inside bundle + maybe.AddPaths( GetOSXKicadDataDir() ); +#else + // This is from CMAKE_INSTALL_PREFIX. + // Useful when KiCad is installed by `make install`. + // Use as second ranked place. + maybe.AddPaths( wxT( DEFAULT_INSTALL_PATH ) ); + + // Add the directory for the user-dependent, program specific data files. + // According to wxWidgets documentation: + // Unix: ~/.appname + // Windows: C:\Documents and Settings\username\Application Data\appname + maybe.AddPaths( wxStandardPaths::Get().GetUserDataDir() ); + + { + // Should be full path to this program executable. + wxString bin_dir = Pgm().GetExecutablePath(); + +#if defined(__MINGW32__) + // bin_dir uses unix path separator. So to parse with wxFileName + // use windows separator, especially important for server inclusion: + // like: \\myserver\local_path . + bin_dir.Replace( wxFileName::GetPathSeparator( wxPATH_UNIX ), + wxFileName::GetPathSeparator( wxPATH_WIN ) ); +#endif + + wxFileName bin_fn( bin_dir, wxEmptyString ); + + // Dir of the global (not user-specific), application specific, data files. + // From wx docs: + // Unix: prefix/share/appname + // Windows: the directory where the executable file is located + // Mac: appname.app/Contents/SharedSupport bundle subdirectory + wxString data_dir = wxStandardPaths::Get().GetDataDir(); + + if( bin_fn.GetPath() != data_dir ) + { + // add data_dir if it is different from the bin_dir + maybe.AddPaths( data_dir ); + } + + // Up one level relative to binary path with "share" appended below. + bin_fn.RemoveLastDir(); + maybe.AddPaths( bin_fn.GetPath() ); + } + + /* The normal OS program file install paths allow for a binary to be + * installed in a different path from the library files. This is + * useful for development purposes so the library and documentation + * files do not need to be installed separately. If someone can + * figure out a way to implement this without #ifdef, please do. + */ +#if defined(__MINGW32__) + maybe.AddPaths( wxGetenv( wxT( "PROGRAMFILES" ) ) ); +#else + maybe.AddPaths( wxGetenv( wxT( "PATH" ) ) ); +#endif +#endif + +#if defined(DEBUG) && 0 + maybe.Show( "maybe wish list" ); +#endif + + // Append 1) kicad, 2) kicad/share, 3) share, and 4) share/kicad to each + // possible base path in 'maybe'. Since SEARCH_STACK::AddPaths() will verify + // readability and existence of each directory, not all of these will be + // actually appended. + for( unsigned i = 0; i < maybe.GetCount(); ++i ) + { + wxFileName fn( maybe[i], wxEmptyString ); + +#ifndef __WXMAC__ + if( fn.GetPath().AfterLast( fn.GetPathSeparator() ) == wxT( "bin" ) ) + { + fn.RemoveLastDir(); + + if( !fn.GetDirCount() ) + continue; // at least on linux + } +#endif + + aSearchStack->AddPaths( fn.GetPath() ); + +#ifndef __WXMAC__ + fn.AppendDir( wxT( "kicad" ) ); + aSearchStack->AddPaths( fn.GetPath() ); // add maybe[i]/kicad + + fn.AppendDir( wxT( "share" ) ); + aSearchStack->AddPaths( fn.GetPath() ); // add maybe[i]/kicad/share + + fn.RemoveLastDir(); // ../ clear share + fn.RemoveLastDir(); // ../ clear kicad + + fn.AppendDir( wxT( "share" ) ); + aSearchStack->AddPaths( fn.GetPath() ); // add maybe[i]/share + + fn.AppendDir( wxT( "kicad" ) ); + aSearchStack->AddPaths( fn.GetPath() ); // add maybe[i]/share/kicad +#endif + } + +#if defined(DEBUG) && 0 + // final results: + aSearchStack->Show( __func__ ); +#endif +} diff --git a/common/tool/action_manager.cpp b/common/tool/action_manager.cpp new file mode 100644 index 0000000..ddc3cea --- /dev/null +++ b/common/tool/action_manager.cpp @@ -0,0 +1,269 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 <tool/action_manager.h> +#include <tool/tool_manager.h> +#include <tool/tool_action.h> +#include <draw_frame.h> + +#include <hotkeys_basic.h> +#include <boost/foreach.hpp> +#include <boost/range/adaptor/map.hpp> +#include <cassert> + +ACTION_MANAGER::ACTION_MANAGER( TOOL_MANAGER* aToolManager ) : + m_toolMgr( aToolManager ) +{ + // Register known actions + std::list<TOOL_ACTION*>& actionList = GetActionList(); + + BOOST_FOREACH( TOOL_ACTION* action, actionList ) + { + if( action->m_id == -1 ) + action->m_id = MakeActionId( action->m_name ); + + RegisterAction( new TOOL_ACTION( *action ) ); + } +} + + +ACTION_MANAGER::~ACTION_MANAGER() +{ + while( !m_actionNameIndex.empty() ) + { + TOOL_ACTION* action = m_actionNameIndex.begin()->second; + UnregisterAction( action ); + delete action; + } +} + + +void ACTION_MANAGER::RegisterAction( TOOL_ACTION* aAction ) +{ + // TOOL_ACTIONs are supposed to be named [appName.]toolName.actionName (with dots between) + // action name without specifying at least toolName is not valid + assert( aAction->GetName().find( '.', 0 ) != std::string::npos ); + + // TOOL_ACTIONs must have unique names & ids + assert( m_actionNameIndex.find( aAction->m_name ) == m_actionNameIndex.end() ); + + m_actionNameIndex[aAction->m_name] = aAction; +} + + +void ACTION_MANAGER::UnregisterAction( TOOL_ACTION* aAction ) +{ + m_actionNameIndex.erase( aAction->m_name ); + int hotkey = GetHotKey( *aAction ); + + if( hotkey ) + { + std::list<TOOL_ACTION*>& actions = m_actionHotKeys[hotkey]; + std::list<TOOL_ACTION*>::iterator action = std::find( actions.begin(), actions.end(), aAction ); + + if( action != actions.end() ) + actions.erase( action ); + else + assert( false ); + } +} + + +int ACTION_MANAGER::MakeActionId( const std::string& aActionName ) +{ + static int currentActionId = 1; + + return currentActionId++; +} + + +TOOL_ACTION* ACTION_MANAGER::FindAction( const std::string& aActionName ) const +{ + std::map<std::string, TOOL_ACTION*>::const_iterator it = m_actionNameIndex.find( aActionName ); + + if( it != m_actionNameIndex.end() ) + return it->second; + + return NULL; +} + + +bool ACTION_MANAGER::RunHotKey( int aHotKey ) const +{ + int key = aHotKey & ~MD_MODIFIER_MASK; + int mod = aHotKey & MD_MODIFIER_MASK; + + if( key >= 'a' && key <= 'z' ) + key = std::toupper( key ); + + HOTKEY_LIST::const_iterator it = m_actionHotKeys.find( key | mod ); + + // If no luck, try without Shift, to handle keys that require it + // e.g. to get ? you need to press Shift+/ without US keyboard layout + // Hardcoding ? as Shift+/ is a bad idea, as on another layout you may need to press a + // different combination + if( it == m_actionHotKeys.end() ) + { + it = m_actionHotKeys.find( key | ( mod & ~MD_SHIFT ) ); + + if( it == m_actionHotKeys.end() ) + return false; // no appropriate action found for the hotkey + } + + const std::list<TOOL_ACTION*>& actions = it->second; + + // Choose the action that has the highest priority on the active tools stack + // If there is none, run the global action associated with the hot key + int highestPriority = -1, priority = -1; + const TOOL_ACTION* context = NULL; // pointer to context action of the highest priority tool + const TOOL_ACTION* global = NULL; // pointer to global action, if there is no context action + + BOOST_FOREACH( const TOOL_ACTION* action, actions ) + { + if( action->GetScope() == AS_GLOBAL ) + { + // Store the global action for the hot key in case there was no possible + // context actions to run + assert( global == NULL ); // there should be only one global action per hot key + global = action; + continue; + } + + TOOL_BASE* tool = m_toolMgr->FindTool( action->GetToolName() ); + + if( tool ) + { + // Choose the action that goes to the tool with highest priority + // (i.e. is on the top of active tools stack) + priority = m_toolMgr->GetPriority( tool->GetId() ); + + if( priority >= 0 && priority > highestPriority ) + { + highestPriority = priority; + context = action; + } + } + } + + if( context ) + { + m_toolMgr->RunAction( *context, true ); + return true; + } + else if( global ) + { + m_toolMgr->RunAction( *global, true ); + return true; + } + + return false; +} + + +int ACTION_MANAGER::GetHotKey( const TOOL_ACTION& aAction ) const +{ + std::map<int, int>::const_iterator it = m_hotkeys.find( aAction.GetId() ); + + if( it == m_hotkeys.end() ) + return 0; + + return it->second; +} + + +void ACTION_MANAGER::UpdateHotKeys() +{ + m_actionHotKeys.clear(); + m_hotkeys.clear(); + + BOOST_FOREACH( TOOL_ACTION* action, m_actionNameIndex | boost::adaptors::map_values ) + { + int hotkey = processHotKey( action ); + + if( hotkey > 0 ) + { + m_actionHotKeys[hotkey].push_back( action ); + m_hotkeys[action->GetId()] = hotkey; + } + } + +#ifndef NDEBUG + // Check if there are two global actions assigned to the same hotkey + BOOST_FOREACH( std::list<TOOL_ACTION*>& action_list, m_actionHotKeys | boost::adaptors::map_values ) + { + int global_actions_cnt = 0; + + BOOST_FOREACH( TOOL_ACTION* action, action_list ) + { + if( action->GetScope() == AS_GLOBAL ) + ++global_actions_cnt; + } + + assert( global_actions_cnt <= 1 ); + } +#endif /* not NDEBUG */ +} + + +int ACTION_MANAGER::processHotKey( TOOL_ACTION* aAction ) +{ + int hotkey = aAction->getDefaultHotKey(); + + if( ( hotkey & TOOL_ACTION::LEGACY_HK ) ) + { + hotkey = hotkey & ~TOOL_ACTION::LEGACY_HK; // it leaves only HK_xxx identifier + EDA_DRAW_FRAME* frame = static_cast<EDA_DRAW_FRAME*>( m_toolMgr->GetEditFrame() ); + EDA_HOTKEY* hk_desc = frame->GetHotKeyDescription( hotkey ); + + if( hk_desc ) + { + hotkey = hk_desc->m_KeyCode; + + // Convert modifiers to the ones used by the Tool Framework + if( hotkey & GR_KB_CTRL ) + { + hotkey &= ~GR_KB_CTRL; + hotkey |= MD_CTRL; + } + + if( hotkey & GR_KB_ALT ) + { + hotkey &= ~GR_KB_ALT; + hotkey |= MD_ALT; + } + + if( hotkey & GR_KB_SHIFT ) + { + hotkey &= ~GR_KB_SHIFT; + hotkey |= MD_SHIFT; + } + } + else + { + hotkey = 0; + } + } + + return hotkey; +} diff --git a/common/tool/context_menu.cpp b/common/tool/context_menu.cpp new file mode 100644 index 0000000..bd316cb --- /dev/null +++ b/common/tool/context_menu.cpp @@ -0,0 +1,391 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013-2015 CERN + * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 <tool/tool_event.h> +#include <tool/tool_manager.h> +#include <tool/tool_interactive.h> +#include <tool/context_menu.h> + +#include <boost/bind.hpp> +#include <cassert> + +CONTEXT_MENU::CONTEXT_MENU() : + m_titleSet( false ), m_selected( -1 ), m_tool( NULL ), m_parent( NULL ), m_icon( NULL ), + m_menu_handler( CONTEXT_MENU::menuHandlerStub ), + m_update_handler( CONTEXT_MENU::updateHandlerStub ) +{ + setupEvents(); +} + + +CONTEXT_MENU::CONTEXT_MENU( const CONTEXT_MENU& aMenu ) +{ + copyFrom( aMenu ); + setupEvents(); +} + + +CONTEXT_MENU::~CONTEXT_MENU() +{ + // Set parent to NULL to prevent submenus from unregistering from a notexisting object + for( std::list<CONTEXT_MENU*>::iterator it = m_submenus.begin(); it != m_submenus.end(); ++it ) + (*it)->m_parent = NULL; + + if( m_parent ) + m_parent->m_submenus.remove( this ); +} + + +CONTEXT_MENU& CONTEXT_MENU::operator=( const CONTEXT_MENU& aMenu ) +{ + Clear(); + copyFrom( aMenu ); + + return *this; +} + + +void CONTEXT_MENU::setupEvents() +{ + Connect( wxEVT_MENU_HIGHLIGHT, wxMenuEventHandler( CONTEXT_MENU::onMenuEvent ), NULL, this ); + Connect( wxEVT_COMMAND_MENU_SELECTED, wxMenuEventHandler( CONTEXT_MENU::onMenuEvent ), NULL, this ); +} + + +void CONTEXT_MENU::SetTitle( const wxString& aTitle ) +{ + // TODO handle an empty string (remove title and separator) + + // Unfortunately wxMenu::SetTitle() does nothing.. (at least wxGTK) + + if( m_titleSet ) + { + FindItemByPosition( 0 )->SetItemLabel( aTitle ); + } + else + { + InsertSeparator( 0 ); + Insert( 0, new wxMenuItem( this, wxID_NONE, aTitle, wxEmptyString, wxITEM_NORMAL ) ); + m_titleSet = true; + } +} + + +wxMenuItem* CONTEXT_MENU::Add( const wxString& aLabel, int aId, const BITMAP_OPAQUE* aIcon ) +{ +#ifdef DEBUG + if( FindItem( aId ) != NULL ) + wxLogWarning( wxT( "Adding more than one menu entry with the same ID may result in" + "undefined behaviour" ) ); +#endif + + wxMenuItem* item = new wxMenuItem( this, aId, aLabel, wxEmptyString, wxITEM_NORMAL ); + + if( aIcon ) + item->SetBitmap( KiBitmap( aIcon ) ); + + return Append( item ); +} + + +wxMenuItem* CONTEXT_MENU::Add( const TOOL_ACTION& aAction ) +{ + /// ID numbers for tool actions need to have a value higher than ACTION_ID + const BITMAP_OPAQUE* icon = aAction.GetIcon(); + + wxMenuItem* item = new wxMenuItem( this, getMenuId( aAction ), aAction.GetMenuItem(), + aAction.GetDescription(), wxITEM_NORMAL ); + + if( icon ) + item->SetBitmap( KiBitmap( icon ) ); + + m_toolActions[getMenuId( aAction )] = &aAction; + + return Append( item ); +} + + +std::list<wxMenuItem*> CONTEXT_MENU::Add( CONTEXT_MENU* aMenu, const wxString& aLabel, bool aExpand ) +{ + std::list<wxMenuItem*> items; + CONTEXT_MENU* menuCopy = new CONTEXT_MENU( *aMenu ); + m_submenus.push_back( menuCopy ); + menuCopy->m_parent = this; + + if( aExpand ) + { + for( int i = 0; i < (int) aMenu->GetMenuItemCount(); ++i ) + { + wxMenuItem* item = aMenu->FindItemByPosition( i ); + items.push_back( appendCopy( item ) ); + } + } + else + { + if( aMenu->m_icon ) + { + wxMenuItem* newItem = new wxMenuItem( this, -1, aLabel, wxEmptyString, wxITEM_NORMAL ); + newItem->SetBitmap( KiBitmap( aMenu->m_icon ) ); + newItem->SetSubMenu( menuCopy ); + items.push_back( Append( newItem ) ); + } + else + { + items.push_back( AppendSubMenu( menuCopy, aLabel ) ); + } + } + + return items; +} + + +void CONTEXT_MENU::Clear() +{ + m_titleSet = false; + + for( int i = GetMenuItemCount() - 1; i >= 0; --i ) + Destroy( FindItemByPosition( i ) ); + + m_toolActions.clear(); + m_submenus.clear(); + m_parent = NULL; + + assert( GetMenuItemCount() == 0 ); +} + + +void CONTEXT_MENU::UpdateAll() +{ + try + { + m_update_handler(); + } + catch( std::exception& e ) + { + std::cerr << "CONTEXT_MENU error running update handler: " << e.what() << std::endl; + } + + if( m_tool ) + updateHotKeys(); + + runOnSubmenus( boost::bind( &CONTEXT_MENU::UpdateAll, _1 ) ); +} + + +void CONTEXT_MENU::SetTool( TOOL_INTERACTIVE* aTool ) +{ + m_tool = aTool; + + runOnSubmenus( boost::bind( &CONTEXT_MENU::SetTool, _1, aTool ) ); +} + + +TOOL_MANAGER* CONTEXT_MENU::getToolManager() +{ + assert( m_tool ); + return m_tool->GetManager(); +} + + +void CONTEXT_MENU::updateHotKeys() +{ + TOOL_MANAGER* toolMgr = getToolManager(); + + for( std::map<int, const TOOL_ACTION*>::const_iterator it = m_toolActions.begin(); + it != m_toolActions.end(); ++it ) + { + int id = it->first; + const TOOL_ACTION& action = *it->second; + int key = toolMgr->GetHotKey( action ) & ~MD_MODIFIER_MASK; + + if( key ) + { + int mod = toolMgr->GetHotKey( action ) & MD_MODIFIER_MASK; + int flags = 0; + wxMenuItem* item = FindChildItem( id ); + + if( item ) + { + flags |= ( mod & MD_ALT ) ? wxACCEL_ALT : 0; + flags |= ( mod & MD_CTRL ) ? wxACCEL_CTRL : 0; + flags |= ( mod & MD_SHIFT ) ? wxACCEL_SHIFT : 0; + + if( !flags ) + flags = wxACCEL_NORMAL; + + wxAcceleratorEntry accel( flags, key, id, item ); + item->SetAccel( &accel ); + } + } + } +} + + +void CONTEXT_MENU::onMenuEvent( wxMenuEvent& aEvent ) +{ + OPT_TOOL_EVENT evt; + + wxEventType type = aEvent.GetEventType(); + + // When the currently chosen item in the menu is changed, an update event is issued. + // For example, the selection tool can use this to dynamically highlight the current item + // from selection clarification popup. + if( type == wxEVT_MENU_HIGHLIGHT ) + evt = TOOL_EVENT( TC_COMMAND, TA_CONTEXT_MENU_UPDATE, aEvent.GetId() ); + + // One of menu entries was selected.. + else if( type == wxEVT_COMMAND_MENU_SELECTED ) + { + // Store the selected position + m_selected = aEvent.GetId(); + + // Check if there is a TOOL_ACTION for the given ID + if( m_toolActions.count( aEvent.GetId() ) == 1 ) + { + evt = m_toolActions[aEvent.GetId()]->MakeEvent(); + } + else + { + runEventHandlers( aEvent, evt ); + + // Under Linux, every submenu can have a separate event handler, under + // Windows all submenus are handled by the main menu. +#ifdef __WINDOWS__ + if( !evt ) + { + // Try to find the submenu which holds the selected item + wxMenu* menu = NULL; + FindItem( m_selected, &menu ); + + if( menu && menu != this ) + { + menu->ProcessEvent( aEvent ); + return; + } + } +#endif + + // Handling non-action menu entries (e.g. items in clarification list) + if( !evt ) + evt = TOOL_EVENT( TC_COMMAND, TA_CONTEXT_MENU_CHOICE, aEvent.GetId() ); + } + } + + assert( m_tool ); // without tool & tool manager we cannot handle events + + // forward the action/update event to the TOOL_MANAGER + if( evt && m_tool ) + m_tool->GetManager()->ProcessEvent( *evt ); +} + + +void CONTEXT_MENU::runEventHandlers( const wxMenuEvent& aMenuEvent, OPT_TOOL_EVENT& aToolEvent ) +{ + aToolEvent = m_menu_handler( aMenuEvent ); + + if( !aToolEvent ) + runOnSubmenus( boost::bind( &CONTEXT_MENU::runEventHandlers, _1, aMenuEvent, aToolEvent ) ); +} + + +void CONTEXT_MENU::runOnSubmenus( boost::function<void(CONTEXT_MENU*)> aFunction ) +{ + try + { + std::for_each( m_submenus.begin(), m_submenus.end(), aFunction ); + } + catch( std::exception& e ) + { + std::cerr << "CONTEXT_MENU runOnSubmenus error: " << e.what() << std::endl; + } +} + + +wxMenuItem* CONTEXT_MENU::appendCopy( const wxMenuItem* aSource ) +{ + wxMenuItem* newItem = new wxMenuItem( this, aSource->GetId(), aSource->GetItemLabel(), + aSource->GetHelp(), aSource->GetKind() ); + + if( aSource->GetKind() == wxITEM_NORMAL ) + newItem->SetBitmap( aSource->GetBitmap() ); + + if( aSource->IsSubMenu() ) + { +#ifdef DEBUG + // Submenus of a CONTEXT_MENU are supposed to be CONTEXT_MENUs as well + assert( dynamic_cast<CONTEXT_MENU*>( aSource->GetSubMenu() ) ); +#endif + + CONTEXT_MENU* menu = new CONTEXT_MENU( static_cast<const CONTEXT_MENU&>( *aSource->GetSubMenu() ) ); + newItem->SetSubMenu( menu ); + Append( newItem ); + + m_submenus.push_back( menu ); + menu->m_parent = this; + } + else + { + Append( newItem ); + newItem->SetKind( aSource->GetKind() ); + newItem->SetHelp( aSource->GetHelp() ); + newItem->Enable( aSource->IsEnabled() ); + + if( aSource->IsCheckable() ) + newItem->Check( aSource->IsChecked() ); + } + + return newItem; +} + + +void CONTEXT_MENU::copyFrom( const CONTEXT_MENU& aMenu ) +{ + m_icon = aMenu.m_icon; + m_titleSet = aMenu.m_titleSet; + m_selected = -1; // aMenu.m_selected; + m_tool = aMenu.m_tool; + m_toolActions = aMenu.m_toolActions; + m_parent = NULL; // aMenu.m_parent; + m_menu_handler = aMenu.m_menu_handler; + m_update_handler = aMenu.m_update_handler; + + // Copy all the menu entries + for( int i = 0; i < (int) aMenu.GetMenuItemCount(); ++i ) + { + wxMenuItem* item = aMenu.FindItemByPosition( i ); + appendCopy( item ); + } +} + + +OPT_TOOL_EVENT CONTEXT_MENU::menuHandlerStub( const wxMenuEvent& ) +{ + return boost::none; +} + + +void CONTEXT_MENU::updateHandlerStub() +{ +} diff --git a/common/tool/tool_action.cpp b/common/tool/tool_action.cpp new file mode 100644 index 0000000..c73445f --- /dev/null +++ b/common/tool/tool_action.cpp @@ -0,0 +1,65 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 <tool/tool_action.h> +#include <tool/action_manager.h> + +TOOL_ACTION::TOOL_ACTION( const std::string& aName, TOOL_ACTION_SCOPE aScope, + int aDefaultHotKey, const wxString aMenuItem, const wxString& aMenuDesc, + const BITMAP_OPAQUE* aIcon, TOOL_ACTION_FLAGS aFlags, void* aParam ) : + m_name( aName ), m_scope( aScope ), m_defaultHotKey( aDefaultHotKey ), + m_menuItem( aMenuItem ), m_menuDescription( aMenuDesc ), + m_icon( aIcon ), m_id( -1 ), m_flags( aFlags ), m_param( aParam ) +{ + ACTION_MANAGER::GetActionList().push_back( this ); +} + + +TOOL_ACTION::~TOOL_ACTION() +{ + ACTION_MANAGER::GetActionList().remove( this ); +} + + +std::string TOOL_ACTION::GetToolName() const +{ + int dotCount = std::count( m_name.begin(), m_name.end(), '.' ); + + switch( dotCount ) + { + case 0: + assert( false ); // Invalid action name format + return ""; + + case 1: + return m_name; + + case 2: + return m_name.substr( 0, m_name.rfind( '.' ) ); + + default: + assert( false ); // TODO not implemented + return ""; + } +} diff --git a/common/tool/tool_base.cpp b/common/tool/tool_base.cpp new file mode 100644 index 0000000..f9191d5 --- /dev/null +++ b/common/tool/tool_base.cpp @@ -0,0 +1,97 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * + * 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 <tool/tool_event.h> +#include <tool/tool_manager.h> + +#include <wxPcbStruct.h> // LAME! + +KIGFX::VIEW* TOOL_BASE::getView() const +{ + return m_toolMgr->GetView(); +} + + +KIGFX::VIEW_CONTROLS* TOOL_BASE::getViewControls() const +{ + return m_toolMgr->GetViewControls(); +} + + +wxWindow* TOOL_BASE::getEditFrameInt() const +{ + return m_toolMgr->GetEditFrame(); +} + + +EDA_ITEM* TOOL_BASE::getModelInt() const +{ + return m_toolMgr->GetModel(); +} + + +void TOOL_BASE::attachManager( TOOL_MANAGER* aManager ) +{ + m_toolMgr = aManager; + m_toolSettings = TOOL_SETTINGS( this ); +} + + +TOOL_SETTINGS::TOOL_SETTINGS( TOOL_BASE* aTool ) : + m_tool( aTool ) +{ +} + + +TOOL_SETTINGS::~TOOL_SETTINGS() +{ +} + + +TOOL_SETTINGS& TOOL_BASE::GetSettings() +{ + return m_toolSettings; +} + + +wxString TOOL_SETTINGS::getKeyName( const wxString& aEntryName ) const +{ + wxString key( m_tool->GetName() ); + key += wxT( "." ); + key += aEntryName; + return key; +} + + +wxConfigBase* TOOL_SETTINGS::getConfigBase() const +{ + if( !m_tool ) + return NULL; + + // fixme: make independent of pcbnew (post-stable) + if( PCB_EDIT_FRAME* frame = m_tool->getEditFrame<PCB_EDIT_FRAME>() ) + return frame->GetSettings(); + + return NULL; +} diff --git a/common/tool/tool_dispatcher.cpp b/common/tool/tool_dispatcher.cpp new file mode 100644 index 0000000..1f2f6ce --- /dev/null +++ b/common/tool/tool_dispatcher.cpp @@ -0,0 +1,363 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * + * 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 <wxPcbStruct.h> +#include <wxBasePcbFrame.h> + +#include <tool/tool_manager.h> +#include <tool/tool_dispatcher.h> +#include <tools/common_actions.h> +#include <view/view.h> +#include <view/wx_view_controls.h> + +#include <class_draw_panel_gal.h> +#include <pcbnew_id.h> + +#include <boost/optional.hpp> +#include <boost/foreach.hpp> + +///> Stores information about a mouse button state +struct TOOL_DISPATCHER::BUTTON_STATE +{ + BUTTON_STATE( TOOL_MOUSE_BUTTONS aButton, const wxEventType& aDownEvent, + const wxEventType& aUpEvent, const wxEventType& aDblClickEvent ) : + dragging( false ), + pressed( false ), + dragMaxDelta( 0.0f ), + button( aButton ), + downEvent( aDownEvent ), + upEvent( aUpEvent ), + dblClickEvent( aDblClickEvent ) + {}; + + ///> Flag indicating that dragging is active for the given button. + bool dragging; + + ///> Flag indicating that the given button is pressed. + bool pressed; + + ///> Point where dragging has started (in world coordinates). + VECTOR2D dragOrigin; + + ///> Point where click event has occurred. + VECTOR2D downPosition; + + ///> Difference between drag origin point and current mouse position (expressed as distance in + ///> pixels). + double dragMaxDelta; + + ///> Determines the mouse button for which information are stored. + TOOL_MOUSE_BUTTONS button; + + ///> The type of wxEvent that determines mouse button press. + wxEventType downEvent; + + ///> The type of wxEvent that determines mouse button release. + wxEventType upEvent; + + ///> The type of wxEvent that determines mouse button double click. + wxEventType dblClickEvent; + + ///> Time stamp for the last mouse button press event. + wxLongLong downTimestamp; + + ///> Restores initial state. + void Reset() + { + dragging = false; + pressed = false; + } + + ///> Checks the current state of the button. + bool GetState() const + { + wxMouseState mouseState = wxGetMouseState(); + + switch( button ) + { + case BUT_LEFT: + return mouseState.LeftIsDown(); + + case BUT_MIDDLE: + return mouseState.MiddleIsDown(); + + case BUT_RIGHT: + return mouseState.RightIsDown(); + + default: + assert( false ); + break; + } + + return false; + } +}; + + +TOOL_DISPATCHER::TOOL_DISPATCHER( TOOL_MANAGER* aToolMgr ) : + m_toolMgr( aToolMgr ) +{ + m_buttons.push_back( new BUTTON_STATE( BUT_LEFT, wxEVT_LEFT_DOWN, + wxEVT_LEFT_UP, wxEVT_LEFT_DCLICK ) ); + m_buttons.push_back( new BUTTON_STATE( BUT_RIGHT, wxEVT_RIGHT_DOWN, + wxEVT_RIGHT_UP, wxEVT_RIGHT_DCLICK ) ); + m_buttons.push_back( new BUTTON_STATE( BUT_MIDDLE, wxEVT_MIDDLE_DOWN, + wxEVT_MIDDLE_UP, wxEVT_MIDDLE_DCLICK ) ); + + ResetState(); +} + + +TOOL_DISPATCHER::~TOOL_DISPATCHER() +{ + BOOST_FOREACH( BUTTON_STATE* st, m_buttons ) + delete st; +} + + +void TOOL_DISPATCHER::ResetState() +{ + BOOST_FOREACH( BUTTON_STATE* st, m_buttons ) + st->Reset(); +} + + +KIGFX::VIEW* TOOL_DISPATCHER::getView() +{ + return static_cast<EDA_DRAW_FRAME*>( m_toolMgr->GetEditFrame() )->GetGalCanvas()->GetView(); +} + + +bool TOOL_DISPATCHER::handleMouseButton( wxEvent& aEvent, int aIndex, bool aMotion ) +{ + BUTTON_STATE* st = m_buttons[aIndex]; + wxEventType type = aEvent.GetEventType(); + boost::optional<TOOL_EVENT> evt; + bool isClick = false; + +// bool up = type == st->upEvent; +// bool down = type == st->downEvent; + bool up = false, down = false; + bool dblClick = type == st->dblClickEvent; + bool state = st->GetState(); + + if( !dblClick ) + { + // Sometimes the dispatcher does not receive mouse button up event, so it stays + // in the dragging mode even if the mouse button is not held anymore + if( st->pressed && !state ) + up = true; + else if( !st->pressed && state ) + down = true; + } + + int mods = decodeModifiers( static_cast<wxMouseEvent*>( &aEvent ) ); + int args = st->button | mods; + + if( down ) // Handle mouse button press + { + st->downTimestamp = wxGetLocalTimeMillis(); + st->dragOrigin = m_lastMousePos; + st->downPosition = m_lastMousePos; + st->dragMaxDelta = 0; + st->pressed = true; + evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DOWN, args ); + } + else if( up ) // Handle mouse button release + { + st->pressed = false; + + if( st->dragging ) + { + wxLongLong t = wxGetLocalTimeMillis(); + + // Determine if it was just a single click or beginning of dragging + if( t - st->downTimestamp < DragTimeThreshold && + st->dragMaxDelta < DragDistanceThreshold ) + isClick = true; + else + evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_UP, args ); + } + else + isClick = true; + + if( isClick ) + evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_CLICK, args ); + + st->dragging = false; + } + else if( dblClick ) + { + evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DBLCLICK, args ); + } + + if( st->pressed && aMotion ) + { + st->dragging = true; + double dragPixelDistance = + getView()->ToScreen( m_lastMousePos - st->dragOrigin, false ).EuclideanNorm(); + st->dragMaxDelta = std::max( st->dragMaxDelta, dragPixelDistance ); + + wxLongLong t = wxGetLocalTimeMillis(); + + if( t - st->downTimestamp > DragTimeThreshold || st->dragMaxDelta > DragDistanceThreshold ) + { + evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DRAG, args ); + evt->setMouseDragOrigin( st->dragOrigin ); + evt->setMouseDelta( m_lastMousePos - st->dragOrigin ); + } + } + + if( evt ) + { + evt->SetMousePosition( isClick ? st->downPosition : m_lastMousePos ); + m_toolMgr->ProcessEvent( *evt ); + + return true; + } + + return false; +} + + +void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent ) +{ + bool motion = false, buttonEvents = false; + boost::optional<TOOL_EVENT> evt; + + int type = aEvent.GetEventType(); + + // Mouse handling + if( type == wxEVT_MOTION || type == wxEVT_MOUSEWHEEL || +#ifdef USE_OSX_MAGNIFY_EVENT + type == wxEVT_MAGNIFY || +#endif + type == wxEVT_LEFT_DOWN || type == wxEVT_LEFT_UP || + type == wxEVT_MIDDLE_DOWN || type == wxEVT_MIDDLE_UP || + type == wxEVT_RIGHT_DOWN || type == wxEVT_RIGHT_UP || + type == wxEVT_LEFT_DCLICK || type == wxEVT_MIDDLE_DCLICK || type == wxEVT_RIGHT_DCLICK || + // Event issued whem mouse retains position in screen coordinates, + // but changes in world coordinates (e.g. autopanning) + type == KIGFX::WX_VIEW_CONTROLS::EVT_REFRESH_MOUSE ) + { + wxMouseEvent* me = static_cast<wxMouseEvent*>( &aEvent ); + int mods = decodeModifiers( me ); + + VECTOR2D screenPos = m_toolMgr->GetViewControls()->GetMousePosition(); + VECTOR2D pos = getView()->ToWorld( screenPos ); + + if( pos != m_lastMousePos ) + { + motion = true; + m_lastMousePos = pos; + } + + for( unsigned int i = 0; i < m_buttons.size(); i++ ) + buttonEvents |= handleMouseButton( aEvent, i, motion ); + + if( !buttonEvents && motion ) + { + evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_MOTION, mods ); + evt->SetMousePosition( pos ); + } + +#ifdef __APPLE__ + // TODO That's a big ugly workaround, somehow DRAWPANEL_GAL loses focus + // after second LMB click and currently I have no means to do better debugging + if( type == wxEVT_LEFT_UP ) + static_cast<PCB_BASE_FRAME*>( m_toolMgr->GetEditFrame() )->GetGalCanvas()->SetFocus(); +#endif /* __APPLE__ */ + } + + // Keyboard handling + else if( type == wxEVT_CHAR ) + { + wxKeyEvent* ke = static_cast<wxKeyEvent*>( &aEvent ); + int key = ke->GetKeyCode(); + int mods = decodeModifiers( ke ); + + if( mods & MD_CTRL ) + { +#if !wxCHECK_VERSION( 2, 9, 0 ) + // I really look forward to the day when we will use only one version of wxWidgets.. + const int WXK_CONTROL_A = 1; + const int WXK_CONTROL_Z = 26; +#endif + + // wxWidgets have a quirk related to Ctrl+letter hot keys handled by CHAR_EVT + // http://docs.wxwidgets.org/trunk/classwx_key_event.html: + // "char events for ASCII letters in this case carry codes corresponding to the ASCII + // value of Ctrl-Latter, i.e. 1 for Ctrl-A, 2 for Ctrl-B and so on until 26 for Ctrl-Z." + if( key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z ) + key += 'A' - 1; + } + + if( key == WXK_ESCAPE ) // ESC is the special key for cancelling tools + evt = TOOL_EVENT( TC_COMMAND, TA_CANCEL_TOOL ); + else + evt = TOOL_EVENT( TC_KEYBOARD, TA_KEY_PRESSED, key | mods ); + } + + if( evt ) + m_toolMgr->ProcessEvent( *evt ); + + // pass the event to the GUI, it might still be interested in it +#ifdef __APPLE__ + // On OS X, key events are always meant to be caught. An uncaught key event is assumed + // to be a user input error by OS X (as they are pressing keys in a context where nothing + // is there to catch the event). This annoyingly makes OS X beep and/or flash the screen + // in pcbnew and the footprint editor any time a hotkey is used. The correct procedure is + // to NOT pass key events to the GUI under OS X. + + if( type != wxEVT_CHAR ) + aEvent.Skip(); +#else + aEvent.Skip(); +#endif + + updateUI(); +} + + +void TOOL_DISPATCHER::DispatchWxCommand( wxCommandEvent& aEvent ) +{ + boost::optional<TOOL_EVENT> evt = COMMON_ACTIONS::TranslateLegacyId( aEvent.GetId() ); + + if( evt ) + m_toolMgr->ProcessEvent( *evt ); + else + aEvent.Skip(); + + updateUI(); +} + + +void TOOL_DISPATCHER::updateUI() +{ + // TODO I don't feel it is the right place for updating UI, + // but at the moment I cannot think of a better one.. + EDA_DRAW_FRAME* frame = static_cast<EDA_DRAW_FRAME*>( m_toolMgr->GetEditFrame() ); + frame->UpdateStatusBar(); + frame->UpdateMsgPanel(); +} diff --git a/common/tool/tool_event.cpp b/common/tool/tool_event.cpp new file mode 100644 index 0000000..68518a2 --- /dev/null +++ b/common/tool/tool_event.cpp @@ -0,0 +1,162 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * + * 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 <cstring> +#include <string> + +#include <tool/tool_event.h> +#include <tool/tool_action.h> +#include <tool/tool_manager.h> + +#include <boost/foreach.hpp> + +struct FlagString +{ + int flag; + std::string str; +}; + + +static const std::string flag2string( int aFlag, const FlagString* aExps ) +{ + std::string rv; + + for( int i = 0; aExps[i].str.length(); i++ ) + { + if( aExps[i].flag & aFlag ) + rv += aExps[i].str + " "; + } + + return rv; +} + + +bool TOOL_EVENT::IsAction( const TOOL_ACTION* aAction ) const +{ + return Matches( aAction->MakeEvent() ); +} + + +const std::string TOOL_EVENT::Format() const +{ + std::string ev; + + const FlagString categories[] = + { + { TC_MOUSE, "mouse" }, + { TC_KEYBOARD, "keyboard" }, + { TC_COMMAND, "command" }, + { TC_MESSAGE, "message" }, + { TC_VIEW, "view" }, + { 0, "" } + }; + + const FlagString actions[] = + { + { TA_MOUSE_CLICK, "click" }, + { TA_MOUSE_DBLCLICK, "double click" }, + { TA_MOUSE_UP, "button-up" }, + { TA_MOUSE_DOWN, "button-down" }, + { TA_MOUSE_DRAG, "drag" }, + { TA_MOUSE_MOTION, "motion" }, + { TA_MOUSE_WHEEL, "wheel" }, + { TA_KEY_PRESSED, "key-pressed" }, + { TA_VIEW_REFRESH, "view-refresh" }, + { TA_VIEW_ZOOM, "view-zoom" }, + { TA_VIEW_PAN, "view-pan" }, + { TA_VIEW_DIRTY, "view-dirty" }, + { TA_CHANGE_LAYER, "change-layer" }, + { TA_CANCEL_TOOL, "cancel-tool" }, + { TA_CONTEXT_MENU_UPDATE, "context-menu-update" }, + { TA_CONTEXT_MENU_CHOICE, "context-menu-choice" }, + { TA_UNDO_REDO, "undo-redo" }, + { TA_ACTION, "action" }, + { TA_ACTIVATE, "activate" }, + { 0, "" } + }; + + const FlagString buttons[] = + { + { BUT_NONE, "none" }, + { BUT_LEFT, "left" }, + { BUT_RIGHT, "right" }, + { BUT_MIDDLE, "middle" }, + { 0, "" } + }; + + const FlagString modifiers[] = + { + { MD_SHIFT, "shift" }, + { MD_CTRL, "ctrl" }, + { MD_ALT, "alt" }, + { 0, "" } + }; + + ev = "category: "; + ev += flag2string( m_category, categories ); + ev += " action: "; + ev += flag2string( m_actions, actions ); + + if( m_actions & TA_MOUSE ) + { + ev += " btns: "; + ev += flag2string( m_mouseButtons, buttons ); + } + + if( m_actions & TA_KEYBOARD ) + { + char tmp[128]; + sprintf( tmp, "key: %d", m_keyCode ); + ev += tmp; + } + + if( m_actions & ( TA_MOUSE | TA_KEYBOARD ) ) + { + ev += " mods: "; + ev += flag2string( m_modifiers, modifiers ); + } + + if( m_commandId ) + { + char tmp[128]; + sprintf( tmp, "cmd-id: %d", *m_commandId ); + ev += tmp; + } + + if( m_commandStr ) + ev += "cmd-str: " + ( *m_commandStr ); + + return ev; +} + + +const std::string TOOL_EVENT_LIST::Format() const +{ + std::string s; + + BOOST_FOREACH( TOOL_EVENT e, m_events ) + s += e.Format() + " "; + + return s; +} diff --git a/common/tool/tool_interactive.cpp b/common/tool/tool_interactive.cpp new file mode 100644 index 0000000..5f05de8 --- /dev/null +++ b/common/tool/tool_interactive.cpp @@ -0,0 +1,73 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * + * 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 <string> + +#include <tool/tool_event.h> +#include <tool/tool_manager.h> +#include <tool/tool_interactive.h> +#include <tool/context_menu.h> + +TOOL_INTERACTIVE::TOOL_INTERACTIVE( TOOL_ID aId, const std::string& aName ) : + TOOL_BASE( INTERACTIVE, aId, aName ) +{ +} + + +TOOL_INTERACTIVE::TOOL_INTERACTIVE( const std::string& aName ) : + TOOL_BASE( INTERACTIVE, TOOL_MANAGER::MakeToolId( aName ), aName ) +{ +} + + +TOOL_INTERACTIVE::~TOOL_INTERACTIVE() +{ +} + + +void TOOL_INTERACTIVE::Activate() +{ + m_toolMgr->InvokeTool( m_toolId ); +} + + +OPT_TOOL_EVENT TOOL_INTERACTIVE::Wait( const TOOL_EVENT_LIST& aEventList ) +{ + return m_toolMgr->ScheduleWait( this, aEventList ); +} + + +void TOOL_INTERACTIVE::goInternal( TOOL_STATE_FUNC& aState, const TOOL_EVENT_LIST& aConditions ) +{ + m_toolMgr->ScheduleNextState( this, aState, aConditions ); +} + + +void TOOL_INTERACTIVE::SetContextMenu( CONTEXT_MENU* aMenu, CONTEXT_MENU_TRIGGER aTrigger ) +{ + if( aMenu ) + aMenu->SetTool( this ); + + m_toolMgr->ScheduleContextMenu( this, aMenu, aTrigger ); +} diff --git a/common/tool/tool_manager.cpp b/common/tool/tool_manager.cpp new file mode 100644 index 0000000..447ab99 --- /dev/null +++ b/common/tool/tool_manager.cpp @@ -0,0 +1,777 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 <map> +#include <list> +#include <stack> +#include <algorithm> + +#include <boost/foreach.hpp> +#include <boost/scoped_ptr.hpp> +#include <boost/optional.hpp> +#include <boost/range/adaptor/map.hpp> + +#include <wx/event.h> +#include <wx/clipbrd.h> + +#include <view/view.h> +#include <view/view_controls.h> + +#include <tool/tool_base.h> +#include <tool/tool_interactive.h> +#include <tool/tool_manager.h> +#include <tool/context_menu.h> +#include <tool/coroutine.h> +#include <tool/action_manager.h> + +#include <wxPcbStruct.h> +#include <confirm.h> +#include <class_draw_panel_gal.h> + +using boost::optional; + +/// Struct describing the current execution state of a TOOL +struct TOOL_MANAGER::TOOL_STATE +{ + TOOL_STATE( TOOL_BASE* aTool ) : + theTool( aTool ) + { + clear(); + } + + TOOL_STATE( const TOOL_STATE& aState ) + { + theTool = aState.theTool; + idle = aState.idle; + pendingWait = aState.pendingWait; + pendingContextMenu = aState.pendingContextMenu; + contextMenu = aState.contextMenu; + contextMenuTrigger = aState.contextMenuTrigger; + cofunc = aState.cofunc; + wakeupEvent = aState.wakeupEvent; + waitEvents = aState.waitEvents; + transitions = aState.transitions; + // do not copy stateStack + } + + ~TOOL_STATE() + { + assert( stateStack.empty() ); + } + + /// The tool itself + TOOL_BASE* theTool; + + /// Is the tool active (pending execution) or disabled at the moment + bool idle; + + /// Flag defining if the tool is waiting for any event (i.e. if it + /// issued a Wait() call). + bool pendingWait; + + /// Is there a context menu being displayed + bool pendingContextMenu; + + /// Context menu currently used by the tool + CONTEXT_MENU* contextMenu; + + /// Defines when the context menu is opened + CONTEXT_MENU_TRIGGER contextMenuTrigger; + + /// Tool execution context + COROUTINE<int, const TOOL_EVENT&>* cofunc; + + /// The event that triggered the execution/wakeup of the tool after Wait() call + TOOL_EVENT wakeupEvent; + + /// List of events the tool is currently waiting for + TOOL_EVENT_LIST waitEvents; + + /// List of possible transitions (ie. association of events and state handlers that are executed + /// upon the event reception + std::vector<TRANSITION> transitions; + + void operator=( const TOOL_STATE& aState ) + { + theTool = aState.theTool; + idle = aState.idle; + pendingWait = aState.pendingWait; + pendingContextMenu = aState.pendingContextMenu; + contextMenu = aState.contextMenu; + contextMenuTrigger = aState.contextMenuTrigger; + cofunc = aState.cofunc; + wakeupEvent = aState.wakeupEvent; + waitEvents = aState.waitEvents; + transitions = aState.transitions; + // do not copy stateStack + } + + bool operator==( const TOOL_MANAGER::TOOL_STATE& aRhs ) const + { + return aRhs.theTool == this->theTool; + } + + bool operator!=( const TOOL_MANAGER::TOOL_STATE& aRhs ) const + { + return aRhs.theTool != this->theTool; + } + + /** + * Function Push() + * Stores the current state of the tool on stack. Stacks are stored internally and are not + * shared between different TOOL_STATE objects. + */ + void Push() + { + stateStack.push( new TOOL_STATE( *this ) ); + + clear(); + } + + /** + * Function Pop() + * Restores state of the tool from stack. Stacks are stored internally and are not + * shared between different TOOL_STATE objects. + * @return True if state was restored, false if the stack was empty. + */ + bool Pop() + { + delete cofunc; + + if( !stateStack.empty() ) + { + *this = *stateStack.top(); + delete stateStack.top(); + stateStack.pop(); + + return true; + } + else + { + cofunc = NULL; + + return false; + } + } + +private: + ///> Stack preserving previous states of a TOOL. + std::stack<TOOL_STATE*> stateStack; + + ///> Restores the initial state. + void clear() + { + idle = true; + pendingWait = false; + pendingContextMenu = false; + cofunc = NULL; + contextMenu = NULL; + contextMenuTrigger = CMENU_OFF; + transitions.clear(); + } +}; + + +TOOL_MANAGER::TOOL_MANAGER() : + m_model( NULL ), + m_view( NULL ), + m_viewControls( NULL ), + m_editFrame( NULL ), + m_passEvent( false ) +{ + m_actionMgr = new ACTION_MANAGER( this ); +} + + +TOOL_MANAGER::~TOOL_MANAGER() +{ + std::map<TOOL_BASE*, TOOL_STATE*>::iterator it, it_end; + + for( it = m_toolState.begin(), it_end = m_toolState.end(); it != it_end; ++it ) + { + delete it->second->cofunc; // delete cofunction + delete it->second; // delete TOOL_STATE + delete it->first; // delete the tool itself + } + + delete m_actionMgr; +} + + +void TOOL_MANAGER::RegisterTool( TOOL_BASE* aTool ) +{ + wxASSERT_MSG( m_toolNameIndex.find( aTool->GetName() ) == m_toolNameIndex.end(), + wxT( "Adding two tools with the same name may result in unexpected behaviour.") ); + wxASSERT_MSG( m_toolIdIndex.find( aTool->GetId() ) == m_toolIdIndex.end(), + wxT( "Adding two tools with the same ID may result in unexpected behaviour.") ); + wxASSERT_MSG( m_toolTypes.find( typeid( *aTool ).name() ) == m_toolTypes.end(), + wxT( "Adding two tools of the same type may result in unexpected behaviour.") ); + + TOOL_STATE* st = new TOOL_STATE( aTool ); + + m_toolState[aTool] = st; + m_toolNameIndex[aTool->GetName()] = st; + m_toolIdIndex[aTool->GetId()] = st; + m_toolTypes[typeid( *aTool ).name()] = st->theTool; + + aTool->attachManager( this ); + + if( !aTool->Init() ) + { + std::string msg = StrPrintf( "Initialization of the %s tool failed", + aTool->GetName().c_str() ); + + DisplayError( NULL, wxString::FromUTF8( msg.c_str() ) ); + + // Unregister the tool + m_toolState.erase( aTool ); + m_toolNameIndex.erase( aTool->GetName() ); + m_toolIdIndex.erase( aTool->GetId() ); + m_toolTypes.erase( typeid( *aTool ).name() ); + + delete st; + delete aTool; + } +} + + +bool TOOL_MANAGER::InvokeTool( TOOL_ID aToolId ) +{ + TOOL_BASE* tool = FindTool( aToolId ); + + if( tool && tool->GetType() == INTERACTIVE ) + return invokeTool( tool ); + + return false; // there is no tool with the given id +} + + +bool TOOL_MANAGER::InvokeTool( const std::string& aToolName ) +{ + TOOL_BASE* tool = FindTool( aToolName ); + + if( tool && tool->GetType() == INTERACTIVE ) + return invokeTool( tool ); + + return false; // there is no tool with the given name +} + + +void TOOL_MANAGER::RegisterAction( TOOL_ACTION* aAction ) +{ + m_actionMgr->RegisterAction( aAction ); +} + + +void TOOL_MANAGER::UnregisterAction( TOOL_ACTION* aAction ) +{ + m_actionMgr->UnregisterAction( aAction ); +} + + +bool TOOL_MANAGER::RunAction( const std::string& aActionName, bool aNow, void* aParam ) +{ + TOOL_ACTION* action = m_actionMgr->FindAction( aActionName ); + + if( action ) + { + TOOL_EVENT event = action->MakeEvent(); + + // Allow to override the action parameter + if( aParam ) + event.SetParameter( aParam ); + + if( aNow ) + ProcessEvent( event ); + else + PostEvent( event ); + + return true; + } + + wxASSERT_MSG( action != NULL, wxString::Format( wxT( "Could not find action %s." ), aActionName ) ); + + return false; +} + + +void TOOL_MANAGER::RunAction( const TOOL_ACTION& aAction, bool aNow, void* aParam ) +{ + TOOL_EVENT event = aAction.MakeEvent(); + + // Allow to override the action parameter + if( aParam ) + event.SetParameter( aParam ); + + if( aNow ) + ProcessEvent( event ); + else + PostEvent( event ); +} + + +int TOOL_MANAGER::GetHotKey( const TOOL_ACTION& aAction ) +{ + return m_actionMgr->GetHotKey( aAction ); +} + + +void TOOL_MANAGER::UpdateHotKeys() +{ + m_actionMgr->UpdateHotKeys(); +} + + +bool TOOL_MANAGER::invokeTool( TOOL_BASE* aTool ) +{ + wxASSERT( aTool != NULL ); + + TOOL_EVENT evt( TC_COMMAND, TA_ACTIVATE, aTool->GetName() ); + ProcessEvent( evt ); + + return true; +} + + +bool TOOL_MANAGER::runTool( TOOL_ID aToolId ) +{ + TOOL_BASE* tool = FindTool( aToolId ); + + if( tool && tool->GetType() == INTERACTIVE ) + return runTool( tool ); + + return false; // there is no tool with the given id +} + + +bool TOOL_MANAGER::runTool( const std::string& aToolName ) +{ + TOOL_BASE* tool = FindTool( aToolName ); + + if( tool && tool->GetType() == INTERACTIVE ) + return runTool( tool ); + + return false; // there is no tool with the given name +} + + +bool TOOL_MANAGER::runTool( TOOL_BASE* aTool ) +{ + wxASSERT( aTool != NULL ); + + if( !isRegistered( aTool ) ) + { + wxASSERT_MSG( false, wxT( "You cannot run unregistered tools" ) ); + return false; + } + + // If the tool is already active, bring it to the top of the active tools stack + if( isActive( aTool ) ) + { + m_activeTools.erase( std::find( m_activeTools.begin(), m_activeTools.end(), + aTool->GetId() ) ); + m_activeTools.push_front( aTool->GetId() ); + + return false; + } + + aTool->Reset( TOOL_INTERACTIVE::RUN ); + aTool->SetTransitions(); + + // Add the tool on the front of the processing queue (it gets events first) + m_activeTools.push_front( aTool->GetId() ); + + return true; +} + + +TOOL_BASE* TOOL_MANAGER::FindTool( int aId ) const +{ + std::map<TOOL_ID, TOOL_STATE*>::const_iterator it = m_toolIdIndex.find( aId ); + + if( it != m_toolIdIndex.end() ) + return it->second->theTool; + + return NULL; +} + + +TOOL_BASE* TOOL_MANAGER::FindTool( const std::string& aName ) const +{ + std::map<std::string, TOOL_STATE*>::const_iterator it = m_toolNameIndex.find( aName ); + + if( it != m_toolNameIndex.end() ) + return it->second->theTool; + + return NULL; +} + +void TOOL_MANAGER::DeactivateTool() +{ + TOOL_EVENT evt( TC_COMMAND, TA_ACTIVATE, "" ); // deactivate the active tool + ProcessEvent( evt ); +} + +void TOOL_MANAGER::ResetTools( TOOL_BASE::RESET_REASON aReason ) +{ + DeactivateTool(); + + BOOST_FOREACH( TOOL_BASE* tool, m_toolState | boost::adaptors::map_keys ) + { + tool->Reset( aReason ); + tool->SetTransitions(); + } +} + + +int TOOL_MANAGER::GetPriority( int aToolId ) const +{ + int priority = 0; + + for( std::list<TOOL_ID>::const_iterator it = m_activeTools.begin(), + itEnd = m_activeTools.end(); it != itEnd; ++it ) + { + if( *it == aToolId ) + return priority; + + ++priority; + } + + return -1; +} + + +void TOOL_MANAGER::ScheduleNextState( TOOL_BASE* aTool, TOOL_STATE_FUNC& aHandler, + const TOOL_EVENT_LIST& aConditions ) +{ + TOOL_STATE* st = m_toolState[aTool]; + + st->transitions.push_back( TRANSITION( aConditions, aHandler ) ); +} + + +optional<TOOL_EVENT> TOOL_MANAGER::ScheduleWait( TOOL_BASE* aTool, + const TOOL_EVENT_LIST& aConditions ) +{ + TOOL_STATE* st = m_toolState[aTool]; + + assert( !st->pendingWait ); // everything collapses on two Yield() in a row + + // indicate to the manager that we are going to sleep and we shall be + // woken up when an event matching aConditions arrive + st->pendingWait = true; + st->waitEvents = aConditions; + + // switch context back to event dispatcher loop + st->cofunc->Yield(); + + return st->wakeupEvent; +} + + +void TOOL_MANAGER::dispatchInternal( const TOOL_EVENT& aEvent ) +{ + // iterate over all registered tools + for( std::list<TOOL_ID>::iterator it = m_activeTools.begin(); + it != m_activeTools.end(); /* iteration is done inside */) + { + std::list<TOOL_ID>::iterator curIt = it; + TOOL_STATE* st = m_toolIdIndex[*it]; + ++it; // it might be overwritten, if the tool is removed the m_activeTools list + + // the tool state handler is waiting for events (i.e. called Wait() method) + if( st->pendingWait ) + { + if( st->waitEvents.Matches( aEvent ) ) + { + // By default, only messages are passed further + m_passEvent = ( aEvent.Category() == TC_MESSAGE ); + + // got matching event? clear wait list and wake up the coroutine + st->wakeupEvent = aEvent; + st->pendingWait = false; + st->waitEvents.clear(); + + if( st->cofunc && !st->cofunc->Resume() ) + { + if( finishTool( st, false ) ) // The couroutine has finished + it = m_activeTools.erase( curIt ); + } + + // If the tool did not request to propagate + // the event to other tools, we should stop it now + if( !m_passEvent ) + break; + } + } + } + + BOOST_FOREACH( TOOL_STATE* st, m_toolState | boost::adaptors::map_values ) + { + // no state handler in progress - check if there are any transitions (defined by + // Go() method that match the event. + if( !st->pendingWait && !st->transitions.empty() ) + { + BOOST_FOREACH( TRANSITION& tr, st->transitions ) + { + if( tr.first.Matches( aEvent ) ) + { + // if there is already a context, then store it + if( st->cofunc ) + st->Push(); + + // as the state changes, the transition table has to be set up again + st->transitions.clear(); + + st->cofunc = new COROUTINE<int, const TOOL_EVENT&>( tr.second ); + + // got match? Run the handler. + st->cofunc->Call( aEvent ); + + if( !st->cofunc->Running() ) + finishTool( st ); // The couroutine has finished immediately? + + // there is no point in further checking, as transitions got cleared + break; + } + } + } + } +} + + +bool TOOL_MANAGER::dispatchStandardEvents( const TOOL_EVENT& aEvent ) +{ + if( aEvent.Action() == TA_KEY_PRESSED ) + { + // Check if there is a hotkey associated + if( m_actionMgr->RunHotKey( aEvent.Modifier() | aEvent.KeyCode() ) ) + return false; // hotkey event was handled so it does not go any further + } + + return true; +} + + +bool TOOL_MANAGER::dispatchActivation( const TOOL_EVENT& aEvent ) +{ + if( aEvent.IsActivate() ) + { + std::map<std::string, TOOL_STATE*>::iterator tool = m_toolNameIndex.find( *aEvent.GetCommandStr() ); + + if( tool != m_toolNameIndex.end() ) + { + runTool( tool->second->theTool ); + return true; + } + } + + return false; +} + + +void TOOL_MANAGER::dispatchContextMenu( const TOOL_EVENT& aEvent ) +{ + BOOST_FOREACH( TOOL_ID toolId, m_activeTools ) + { + TOOL_STATE* st = m_toolIdIndex[toolId]; + + // the tool requested a context menu. The menu is activated on RMB click (CMENU_BUTTON mode) + // or immediately (CMENU_NOW) mode. The latter is used for clarification lists. + if( st->contextMenuTrigger != CMENU_OFF ) + { + if( st->contextMenuTrigger == CMENU_BUTTON && !aEvent.IsClick( BUT_RIGHT ) ) + break; + + st->pendingWait = true; + st->waitEvents = TOOL_EVENT( TC_ANY, TA_ANY ); + + // Store the menu pointer in case it is changed by the TOOL when handling menu events + CONTEXT_MENU* m = st->contextMenu; + + if( st->contextMenuTrigger == CMENU_NOW ) + st->contextMenuTrigger = CMENU_OFF; + + // Temporarily store the cursor position, so the tools could execute actions + // using the point where the user has invoked a context menu + bool forcedCursor = m_viewControls->IsCursorPositionForced(); + VECTOR2D cursorPos = m_viewControls->GetCursorPosition(); + m_viewControls->ForceCursorPosition( true, m_viewControls->GetCursorPosition() ); + + // Run update handlers + m->UpdateAll(); + + boost::scoped_ptr<CONTEXT_MENU> menu( new CONTEXT_MENU( *m ) ); + GetEditFrame()->PopupMenu( menu.get() ); + + // If nothing was chosen from the context menu, we must notify the tool as well + if( menu->GetSelected() < 0 ) + { + TOOL_EVENT evt( TC_COMMAND, TA_CONTEXT_MENU_CHOICE, -1 ); + evt.SetParameter( m ); + dispatchInternal( evt ); + } + + TOOL_EVENT evt( TC_COMMAND, TA_CONTEXT_MENU_CLOSED ); + evt.SetParameter( m ); + dispatchInternal( evt ); + + m_viewControls->ForceCursorPosition( forcedCursor, cursorPos ); + + break; + } + } +} + + +bool TOOL_MANAGER::finishTool( TOOL_STATE* aState, bool aDeactivate ) +{ + bool shouldDeactivate = false; + + // Reset VIEW_CONTROLS only if the most recent tool is finished + if( m_activeTools.empty() || m_activeTools.front() == aState->theTool->GetId() ) + m_viewControls->Reset(); + + if( !aState->Pop() ) // if there are no other contexts saved on the stack + { + // find the tool and deactivate it + std::list<TOOL_ID>::iterator tool = std::find( m_activeTools.begin(), m_activeTools.end(), + aState->theTool->GetId() ); + + if( tool != m_activeTools.end() ) + { + shouldDeactivate = true; + + if( aDeactivate ) + m_activeTools.erase( tool ); + } + } + + aState->theTool->SetTransitions(); + + return shouldDeactivate; +} + + +bool TOOL_MANAGER::ProcessEvent( const TOOL_EVENT& aEvent ) +{ + // Early dispatch of events destined for the TOOL_MANAGER + if( !dispatchStandardEvents( aEvent ) ) + return false; + + dispatchInternal( aEvent ); + dispatchActivation( aEvent ); + dispatchContextMenu( aEvent ); + + // Dispatch queue + while( !m_eventQueue.empty() ) + { + TOOL_EVENT event = m_eventQueue.front(); + m_eventQueue.pop_front(); + ProcessEvent( event ); + } + + if( m_view->IsDirty() ) + { + EDA_DRAW_FRAME* f = static_cast<EDA_DRAW_FRAME*>( GetEditFrame() ); + f->GetGalCanvas()->Refresh(); // fixme: ugly hack, provide a method in TOOL_DISPATCHER. + } + + return false; +} + + +void TOOL_MANAGER::ScheduleContextMenu( TOOL_BASE* aTool, CONTEXT_MENU* aMenu, + CONTEXT_MENU_TRIGGER aTrigger ) +{ + TOOL_STATE* st = m_toolState[aTool]; + + st->contextMenu = aMenu; + st->contextMenuTrigger = aTrigger; +} + + +bool TOOL_MANAGER::SaveClipboard( const std::string& aText ) +{ + if( wxTheClipboard->Open() ) + { + wxTheClipboard->SetData( new wxTextDataObject( wxString( aText.c_str(), wxConvUTF8 ) ) ); + wxTheClipboard->Close(); + + return true; + } + + return false; +} + + +std::string TOOL_MANAGER::GetClipboard() const +{ + std::string result; + + if( wxTheClipboard->Open() ) + { + if( wxTheClipboard->IsSupported( wxDF_TEXT ) ) + { + wxTextDataObject data; + wxTheClipboard->GetData( data ); + + result = data.GetText().mb_str(); + } + + wxTheClipboard->Close(); + } + + return result; +} + + +TOOL_ID TOOL_MANAGER::MakeToolId( const std::string& aToolName ) +{ + static int currentId; + + return currentId++; +} + + +void TOOL_MANAGER::SetEnvironment( EDA_ITEM* aModel, KIGFX::VIEW* aView, + KIGFX::VIEW_CONTROLS* aViewControls, wxWindow* aFrame ) +{ + m_model = aModel; + m_view = aView; + m_viewControls = aViewControls; + m_editFrame = aFrame; + m_actionMgr->UpdateHotKeys(); +} + + +bool TOOL_MANAGER::isActive( TOOL_BASE* aTool ) +{ + if( !isRegistered( aTool ) ) + return false; + + // 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(); +} diff --git a/common/trigo.cpp b/common/trigo.cpp new file mode 100644 index 0000000..4f29125 --- /dev/null +++ b/common/trigo.cpp @@ -0,0 +1,436 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2014 KiCad Developers, see CHANGELOG.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 + */ + +/** + * @file trigo.cpp + * @brief Trigonometric and geometric basic functions. + */ + +#include <fctsys.h> +#include <macros.h> +#include <trigo.h> +#include <common.h> +#include <math_for_graphics.h> + +// Returns true if the point P is on the segment S. +// faster than TestSegmentHit() because P should be exactly on S +// therefore works fine only for H, V and 45 deg segm (suitable for wires in eeschema) +bool IsPointOnSegment( const wxPoint& aSegStart, const wxPoint& aSegEnd, + const wxPoint& aTestPoint ) +{ + wxPoint vectSeg = aSegEnd - aSegStart; // Vector from S1 to S2 + wxPoint vectPoint = aTestPoint - aSegStart; // Vector from S1 to P + + // Use long long here to avoid overflow in calculations + if( (long long) vectSeg.x * vectPoint.y - (long long) vectSeg.y * vectPoint.x ) + return false; /* Cross product non-zero, vectors not parallel */ + + if( ( (long long) vectSeg.x * vectPoint.x + (long long) vectSeg.y * vectPoint.y ) < + ( (long long) vectPoint.x * vectPoint.x + (long long) vectPoint.y * vectPoint.y ) ) + return false; /* Point not on segment */ + + return true; +} + + +// Returns true if the segment 1 intersectd the segment 2. +bool SegmentIntersectsSegment( const wxPoint &a_p1_l1, const wxPoint &a_p2_l1, + const wxPoint &a_p1_l2, const wxPoint &a_p2_l2 ) +{ + + //We are forced to use 64bit ints because the internal units can oveflow 32bit ints when + // multiplied with each other, the alternative would be to scale the units down (i.e. divide + // by a fixed number). + long long dX_a, dY_a, dX_b, dY_b, dX_ab, dY_ab; + long long num_a, num_b, den; + + //Test for intersection within the bounds of both line segments using line equations of the + // form: + // x_k(u_k) = u_k * dX_k + x_k(0) + // y_k(u_k) = u_k * dY_k + y_k(0) + // with 0 <= u_k <= 1 and k = [ a, b ] + + dX_a = a_p2_l1.x - a_p1_l1.x; + dY_a = a_p2_l1.y - a_p1_l1.y; + dX_b = a_p2_l2.x - a_p1_l2.x; + dY_b = a_p2_l2.y - a_p1_l2.y; + dX_ab = a_p1_l2.x - a_p1_l1.x; + dY_ab = a_p1_l2.y - a_p1_l1.y; + + den = dY_a * dX_b - dY_b * dX_a ; + + //Check if lines are parallel + if( den == 0 ) + return false; + + num_a = dY_ab * dX_b - dY_b * dX_ab; + num_b = dY_ab * dX_a - dY_a * dX_ab; + + //We wont calculate directly the u_k of the intersection point to avoid floating point + // division but they could be calculated with: + // u_a = (float) num_a / (float) den; + // u_b = (float) num_b / (float) den; + + if( den < 0 ) + { + den = -den; + num_a = -num_a; + num_b = -num_b; + } + + //Test sign( u_a ) and return false if negative + if( num_a < 0 ) + return false; + + //Test sign( u_b ) and return false if negative + if( num_b < 0 ) + return false; + + //Test to ensure (u_a <= 1) + if( num_a > den ) + return false; + + //Test to ensure (u_b <= 1) + if( num_b > den ) + return false; + + return true; +} + + +/* Function TestSegmentHit + * test for hit on line segment + * i.e. a reference point is within a given distance from segment + * aRefPoint = reference point to test + * aStart, aEnd are coordinates of end points segment + * aDist = maximum distance for hit + * Note: for calculation time reasons, the distance between the ref point + * and the segment is not always exactly calculated + * (we only know if the actual dist is < aDist, not exactly know this dist. + * Because many times we have horizontal or vertical segments, + * a special calcultaion is made for them + * Note: sometimes we need to calculate the distande between 2 points + * A square root should be calculated. + * However, because we just compare 2 distnaces, to avoid calculating square root, + * the square of distances are compared. +*/ +static inline double square( int x ) // helper function to calculate x*x +{ + return (double) x * x; +} +bool TestSegmentHit( const wxPoint &aRefPoint, wxPoint aStart, + wxPoint aEnd, int aDist ) +{ + // test for vertical or horizontal segment + if( aEnd.x == aStart.x ) + { + // vertical segment + int ll = abs( aRefPoint.x - aStart.x ); + + if( ll > aDist ) + return false; + + // To have only one case to examine, ensure aEnd.y > aStart.y + if( aEnd.y < aStart.y ) + std::swap( aStart.y, aEnd.y ); + + if( aRefPoint.y <= aEnd.y && aRefPoint.y >= aStart.y ) + return true; + + // there is a special case: x,y near an end point (distance < dist ) + // the distance should be carefully calculated + if( (aStart.y - aRefPoint.y) < aDist ) + { + double dd = square( aRefPoint.x - aStart.x) + + square( aRefPoint.y - aStart.y ); + if( dd <= square( aDist ) ) + return true; + } + + if( (aRefPoint.y - aEnd.y) < aDist ) + { + double dd = square( aRefPoint.x - aEnd.x ) + + square( aRefPoint.y - aEnd.y ); + if( dd <= square( aDist ) ) + return true; + } + } + else if( aEnd.y == aStart.y ) + { + // horizontal segment + int ll = abs( aRefPoint.y - aStart.y ); + + if( ll > aDist ) + return false; + + // To have only one case to examine, ensure xf > xi + if( aEnd.x < aStart.x ) + std::swap( aStart.x, aEnd.x ); + + if( aRefPoint.x <= aEnd.x && aRefPoint.x >= aStart.x ) + return true; + + // there is a special case: x,y near an end point (distance < dist ) + // the distance should be carefully calculated + if( (aStart.x - aRefPoint.x) <= aDist ) + { + double dd = square( aRefPoint.x - aStart.x ) + + square( aRefPoint.y - aStart.y ); + if( dd <= square( aDist ) ) + return true; + } + + if( (aRefPoint.x - aEnd.x) <= aDist ) + { + double dd = square( aRefPoint.x - aEnd.x ) + + square( aRefPoint.y - aEnd.y ); + if( dd <= square( aDist ) ) + return true; + } + } + else + { + // oblique segment: + // First, we need to calculate the distance between the point + // and the line defined by aStart and aEnd + // this dist should be < dist + // + // find a,slope such that aStart and aEnd lie on y = a + slope*x + double slope = (double) (aEnd.y - aStart.y) / (aEnd.x - aStart.x); + double a = (double) aStart.y - slope * aStart.x; + // find c,orthoslope such that (x,y) lies on y = c + orthoslope*x, + // where orthoslope=(-1/slope) + // to calculate xp, yp = near point from aRefPoint + // which is on the line defined by aStart, aEnd + double orthoslope = -1.0 / slope; + double c = (double) aRefPoint.y - orthoslope * aRefPoint.x; + // find nearest point to (x,y) on line defined by aStart, aEnd + double xp = (a - c) / (orthoslope - slope); + double yp = a + slope * xp; + // find distance to line, in fact the square of dist, + // because we just know if it is > or < aDist + double dd = square( aRefPoint.x - xp ) + square( aRefPoint.y - yp ); + double dist = square( aDist ); + + if( dd > dist ) // this reference point is not a good candiadte. + return false; + + // dd is < dist, therefore we should make a fine test + if( fabs( slope ) > 0.7 ) + { + // line segment more vertical than horizontal + if( (aEnd.y > aStart.y && yp <= aEnd.y && yp >= aStart.y) || + (aEnd.y < aStart.y && yp >= aEnd.y && yp <= aStart.y) ) + return true; + } + else + { + // line segment more horizontal than vertical + if( (aEnd.x > aStart.x && xp <= aEnd.x && xp >= aStart.x) || + (aEnd.x < aStart.x && xp >= aEnd.x && xp <= aStart.x) ) + return true; + } + + // Here, the test point is still a good candidate, + // however it is not "between" the end points of the segment. + // It is "outside" the segment, but it could be near a segment end point + // Therefore, we test the dist from the test point to each segment end point + dd = square( aRefPoint.x - aEnd.x ) + square( aRefPoint.y - aEnd.y ); + if( dd <= dist ) + return true; + dd = square( aRefPoint.x - aStart.x ) + square( aRefPoint.y - aStart.y ); + if( dd <= dist ) + return true; + } + + return false; // no hit +} + + +double ArcTangente( int dy, int dx ) +{ + + /* gcc is surprisingly smart in optimizing these conditions in + a tree! */ + + if( dx == 0 && dy == 0 ) + return 0; + + if( dy == 0 ) + { + if( dx >= 0 ) + return 0; + else + return -1800; + } + + if( dx == 0 ) + { + if( dy >= 0 ) + return 900; + else + return -900; + } + + if( dx == dy ) + { + if( dx >= 0 ) + return 450; + else + return -1800 + 450; + } + + if( dx == -dy ) + { + if( dx >= 0 ) + return -450; + else + return 1800 - 450; + } + + // Of course dy and dx are treated as double + return RAD2DECIDEG( atan2( dy, dx ) ); +} + + +void RotatePoint( int* pX, int* pY, double angle ) +{ + int tmp; + + NORMALIZE_ANGLE_POS( angle ); + + // Cheap and dirty optimizations for 0, 90, 180, and 270 degrees. + if( angle == 0 ) + return; + + if( angle == 900 ) /* sin = 1, cos = 0 */ + { + tmp = *pX; + *pX = *pY; + *pY = -tmp; + } + else if( angle == 1800 ) /* sin = 0, cos = -1 */ + { + *pX = -*pX; + *pY = -*pY; + } + else if( angle == 2700 ) /* sin = -1, cos = 0 */ + { + tmp = *pX; + *pX = -*pY; + *pY = tmp; + } + else + { + double fangle = DECIDEG2RAD( angle ); + double sinus = sin( fangle ); + double cosinus = cos( fangle ); + double fpx = (*pY * sinus ) + (*pX * cosinus ); + double fpy = (*pY * cosinus ) - (*pX * sinus ); + *pX = KiROUND( fpx ); + *pY = KiROUND( fpy ); + } +} + + +void RotatePoint( int* pX, int* pY, int cx, int cy, double angle ) +{ + int ox, oy; + + ox = *pX - cx; + oy = *pY - cy; + + RotatePoint( &ox, &oy, angle ); + + *pX = ox + cx; + *pY = oy + cy; +} + + +void RotatePoint( wxPoint* point, const wxPoint& centre, double angle ) +{ + int ox, oy; + + ox = point->x - centre.x; + oy = point->y - centre.y; + + RotatePoint( &ox, &oy, angle ); + point->x = ox + centre.x; + point->y = oy + centre.y; +} + + +void RotatePoint( double* pX, double* pY, double cx, double cy, double angle ) +{ + double ox, oy; + + ox = *pX - cx; + oy = *pY - cy; + + RotatePoint( &ox, &oy, angle ); + + *pX = ox + cx; + *pY = oy + cy; +} + + +void RotatePoint( double* pX, double* pY, double angle ) +{ + double tmp; + + NORMALIZE_ANGLE_POS( angle ); + + // Cheap and dirty optimizations for 0, 90, 180, and 270 degrees. + if( angle == 0 ) + return; + + if( angle == 900 ) /* sin = 1, cos = 0 */ + { + tmp = *pX; + *pX = *pY; + *pY = -tmp; + } + else if( angle == 1800 ) /* sin = 0, cos = -1 */ + { + *pX = -*pX; + *pY = -*pY; + } + else if( angle == 2700 ) /* sin = -1, cos = 0 */ + { + tmp = *pX; + *pX = -*pY; + *pY = tmp; + } + else + { + double fangle = DECIDEG2RAD( angle ); + double sinus = sin( fangle ); + double cosinus = cos( fangle ); + + double fpx = (*pY * sinus ) + (*pX * cosinus ); + double fpy = (*pY * cosinus ) - (*pX * sinus ); + *pX = fpx; + *pY = fpy; + } +} diff --git a/common/utf8.cpp b/common/utf8.cpp new file mode 100644 index 0000000..b90e4a7 --- /dev/null +++ b/common/utf8.cpp @@ -0,0 +1,255 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2013 KiCad Developers, see CHANGELOG.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 <utf8.h> + +/* THROW_IO_ERROR needs this, but it includes this file, so until some + factoring of THROW_IO_ERROR into a separate header, defer and use the asserts. +#include <richio.h> +*/ + +#include <assert.h> + +/* + These are not inlined so that code space is saved by encapsulating the + creation of intermediate objects and the referencing of wxConvUTF8. +*/ + + +UTF8::UTF8( const wxString& o ) : + std::string( (const char*) o.utf8_str() ) +{ +} + + +UTF8::operator wxString () const +{ + return wxString( c_str(), wxConvUTF8 ); +} + + +UTF8& UTF8::operator=( const wxString& o ) +{ + std::string::operator=( (const char*) o.utf8_str() ); + return *this; +} + + +#ifndef THROW_IO_ERROR + #define THROW_IO_ERROR(x) // nothing +#endif + +// There is no wxWidgets function that does this, because wchar_t is 16 bits +// on windows and wx wants to encode the output in UTF16 for such. + +int UTF8::uni_forward( const unsigned char* aSequence, unsigned* aResult ) +{ + unsigned ch = *aSequence; + + if( ch < 0x80 ) + { + if( aResult ) + *aResult = ch; + return 1; + } + + const unsigned char* s = aSequence; + + static const unsigned char utf8_len[] = { + // Map encoded prefix byte to sequence length. Zero means + // illegal prefix. See RFC 3629 for details + /* + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 00-0F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 70-7F + */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80-8F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B0-BF + 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0-C1 + C2-CF + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // D0-DF + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // E0-EF + 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F0-F4 + F5-FF + }; + + int len = utf8_len[ *s - 0x80 /* top half of table is missing */ ]; + + switch( len ) + { + default: + case 0: + THROW_IO_ERROR( "invalid start byte" ); + break; + + case 2: + if( ( s[1] & 0xc0 ) != 0x80 ) + { + THROW_IO_ERROR( "invalid continuation byte" ); + } + + ch = ((s[0] & 0x1f) << 6) + + ((s[1] & 0x3f) << 0); + + assert( ch > 0x007F && ch <= 0x07FF ); + break; + + case 3: + if( (s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[0] == 0xE0 && s[1] < 0xA0) + // || (s[0] == 0xED && s[1] > 0x9F) + ) + { + THROW_IO_ERROR( "invalid continuation byte" ); + } + + ch = ((s[0] & 0x0f) << 12) + + ((s[1] & 0x3f) << 6 ) + + ((s[2] & 0x3f) << 0 ); + + assert( ch > 0x07FF && ch <= 0xFFFF ); + break; + + case 4: + if( (s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[3] & 0xc0) != 0x80 || + (s[0] == 0xF0 && s[1] < 0x90) || + (s[0] == 0xF4 && s[1] > 0x8F) ) + { + THROW_IO_ERROR( "invalid continuation byte" ); + } + + ch = ((s[0] & 0x7) << 18) + + ((s[1] & 0x3f) << 12) + + ((s[2] & 0x3f) << 6 ) + + ((s[3] & 0x3f) << 0 ); + + assert( ch > 0xFFFF && ch <= 0x10ffff ); + break; + } + + if( aResult ) + *aResult = ch; + + return len; +} + + +UTF8::UTF8( const wchar_t* txt ) : + // size initial string safely large enough, then shrink to known size later. + std::string( wcslen( txt ) * 4, 0 ) +{ + /* + + "this" string was sized to hold the worst case UTF8 encoded byte + sequence, and was initialized with all nul bytes. Overwrite some of + those nuls, then resize, shrinking down to actual size. + + Use the wx 2.8 function, not new FromWChar(). It knows about wchar_t + possibly being 16 bits wide on Windows and holding UTF16 input. + + */ + + int sz = wxConvUTF8.WC2MB( (char*) data(), txt, size() ); + + resize( sz ); +} + + +#if 0 // some unit tests: + +#include <stdio.h> + +wxString wxFunctionTaking_wxString( const wxString& wx ) +{ + printf( "%s:'%s'\n", __func__, (char*) UTF8( wx ) ); + printf( "%s:'%s'\n", __func__, (const char*) UTF8( wx ) ); + printf( "%s:'%s'\n", __func__, UTF8( wx ).c_str() ); + + return wx; +} + +int main() +{ + std::string str = "input"; + + UTF8 u0 = L"wide string"; + UTF8 u1 = "initial"; + wxString wx = wxT( "input2" ); + + printf( "u0:'%s'\n", u0.c_str() ); + printf( "u1:'%s'\n", u1.c_str() ); + + u1 = str; + + wxString wx2 = u1; + + // force a std::string into a UTF8, then into a wxString, then copy construct: + wxString wx3 = (UTF8&) u1; + + UTF8 u2 = wx2; + + u2 += 'X'; + + printf( "u2:'%s'\n", u2.c_str() ); + + // key accomplishments here: + // 1) passing a UTF8 to a function which normally takes a wxString. + // 2) return a wxString back into a UTF8. + UTF8 result = wxFunctionTaking_wxString( u2 ); + + printf( "result:'%s'\n", result.c_str() ); + + // test the unicode iterator: + for( UTF8::uni_iter it = u2.ubegin(); it < u2.uend(); ) + { + // test post-increment: + printf( " _%02x_", *it++ ); + } + + printf( "\n" ); + + UTF8::uni_iter it = u2.ubegin(); + + UTF8::uni_iter it2 = it++; + + printf( "post_inc:'%c' should be 'i'\n", *it2 ); + + it2 = ++it; + + printf( "pre_inc:'%c' should be 'p'\n", *it2 ); + + printf( "u[1]:'%c' should be 'n'\n", u2[1] ); + + return 0; +} + +#endif diff --git a/common/validators.cpp b/common/validators.cpp new file mode 100644 index 0000000..bb67a7e --- /dev/null +++ b/common/validators.cpp @@ -0,0 +1,85 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 Wayne Stambaugh <stambaughw@verizon.net> + * Copyright (C) 2004-2013 KiCad Developers, see change_log.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 + */ + +/** + * @file validators.cpp + * @brief Custom text control validator implementations. + */ + +#include <kicad_string.h> +#include <validators.h> + +#include <wx/textentry.h> +#include <wx/log.h> + + +FILE_NAME_CHAR_VALIDATOR::FILE_NAME_CHAR_VALIDATOR( wxString* aValue ) : + wxTextValidator( wxFILTER_EXCLUDE_CHAR_LIST, aValue ) +{ + // The Windows (DOS) file system forbidden characters already include the forbidden + // file name characters for both Posix and OSX systems. The characters \/:*?|"<> are + // illegal and filtered by the validator. + wxString illegalChars = wxFileName::GetForbiddenChars( wxPATH_DOS ); + wxTextValidator nameValidator( wxFILTER_EXCLUDE_CHAR_LIST ); + wxArrayString illegalCharList; + + for( unsigned i = 0; i < illegalChars.size(); i++ ) + illegalCharList.Add( wxString( illegalChars[i] ) ); + + SetExcludes( illegalCharList ); + } + + +FILE_NAME_WITH_PATH_CHAR_VALIDATOR::FILE_NAME_WITH_PATH_CHAR_VALIDATOR( wxString* aValue ) : + wxTextValidator( wxFILTER_EXCLUDE_CHAR_LIST | wxFILTER_EMPTY, aValue ) +{ + // The Windows (DOS) file system forbidden characters already include the forbidden + // file name characters for both Posix and OSX systems. The characters *?|"<> are + // illegal and filtered by the validator, but /\: are valid (\ and : only on Windows. + wxString illegalChars = wxFileName::GetForbiddenChars( wxPATH_DOS ); + wxTextValidator nameValidator( wxFILTER_EXCLUDE_CHAR_LIST ); + wxArrayString illegalCharList; + + for( unsigned i = 0; i < illegalChars.size(); i++ ) + { + if( illegalChars[i] == '/' ) + continue; + +#if defined (__WINDOWS__) + if( illegalChars[i] == '\\' || illegalChars[i] == ':' ) + continue; +#endif + illegalCharList.Add( wxString( illegalChars[i] ) ); + } + + SetExcludes( illegalCharList ); +} + + +ENVIRONMENT_VARIABLE_CHAR_VALIDATOR::ENVIRONMENT_VARIABLE_CHAR_VALIDATOR( wxString* aValue ) : + wxTextValidator( wxFILTER_INCLUDE_CHAR_LIST | wxFILTER_EMPTY, aValue ) +{ + wxString includeChars( wxT( "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" ) ); + SetCharIncludes( includeChars ); +} diff --git a/common/view/view.cpp b/common/view/view.cpp new file mode 100644 index 0000000..8f1933c --- /dev/null +++ b/common/view/view.cpp @@ -0,0 +1,1086 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * + * 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 <boost/foreach.hpp> + +#include <base_struct.h> +#include <layers_id_colors_and_visibility.h> + +#include <view/view.h> +#include <view/view_group.h> +#include <view/view_rtree.h> +#include <gal/definitions.h> +#include <gal/graphics_abstraction_layer.h> +#include <painter.h> + +#ifdef PROFILE +#include <profile.h> +#endif /* PROFILE */ + +using namespace KIGFX; + +VIEW::VIEW( bool aIsDynamic ) : + m_enableOrderModifier( true ), + m_scale( 4.0 ), + m_minScale( 4.0 ), m_maxScale( 15000 ), + m_painter( NULL ), + m_gal( NULL ), + m_dynamic( aIsDynamic ) +{ + m_boundary.SetMaximum(); + m_needsUpdate.reserve( 32768 ); + + // Redraw everything at the beginning + MarkDirty(); + + // View uses layers to display EDA_ITEMs (item may be displayed on several layers, for example + // pad may be shown on pad, pad hole and solder paste layers). There are usual copper layers + // (eg. F.Cu, B.Cu, internal and so on) and layers for displaying objects such as texts, + // silkscreen, pads, vias, etc. + for( int i = 0; i < VIEW_MAX_LAYERS; i++ ) + AddLayer( i ); +} + + +VIEW::~VIEW() +{ + BOOST_FOREACH( LAYER_MAP::value_type& l, m_layers ) + delete l.second.items; +} + + +void VIEW::AddLayer( int aLayer, bool aDisplayOnly ) +{ + if( m_layers.find( aLayer ) == m_layers.end() ) + { + m_layers[aLayer] = VIEW_LAYER(); + m_layers[aLayer].id = aLayer; + m_layers[aLayer].items = new VIEW_RTREE(); + m_layers[aLayer].renderingOrder = aLayer; + m_layers[aLayer].visible = true; + m_layers[aLayer].displayOnly = aDisplayOnly; + m_layers[aLayer].target = TARGET_CACHED; + } + + sortLayers(); +} + + +void VIEW::Add( VIEW_ITEM* aItem ) +{ + int layers[VIEW_MAX_LAYERS], layers_count; + + aItem->ViewGetLayers( layers, layers_count ); + aItem->saveLayers( layers, layers_count ); + + if( m_dynamic ) + aItem->viewAssign( this ); + + for( int i = 0; i < layers_count; ++i ) + { + VIEW_LAYER& l = m_layers[layers[i]]; + l.items->Insert( aItem ); + MarkTargetDirty( l.target ); + } + + aItem->ViewUpdate( VIEW_ITEM::ALL ); +} + + +void VIEW::Remove( VIEW_ITEM* aItem ) +{ + if( m_dynamic ) + aItem->m_view = NULL; + + if( aItem->viewRequiredUpdate() != VIEW_ITEM::NONE ) // prevent from updating a removed item + { + std::vector<VIEW_ITEM*>::iterator item = std::find( m_needsUpdate.begin(), + m_needsUpdate.end(), aItem ); + + if( item != m_needsUpdate.end() ) + { + m_needsUpdate.erase( item ); + aItem->clearUpdateFlags(); + } + } + + int layers[VIEW::VIEW_MAX_LAYERS], layers_count; + aItem->getLayers( layers, layers_count ); + + for( int i = 0; i < layers_count; ++i ) + { + VIEW_LAYER& l = m_layers[layers[i]]; + l.items->Remove( aItem ); + MarkTargetDirty( l.target ); + + // Clear the GAL cache + int prevGroup = aItem->getGroup( layers[i] ); + + if( prevGroup >= 0 ) + m_gal->DeleteGroup( prevGroup ); + } + + aItem->deleteGroups(); +} + + +void VIEW::SetRequired( int aLayerId, int aRequiredId, bool aRequired ) +{ + wxASSERT( (unsigned) aLayerId < m_layers.size() ); + wxASSERT( (unsigned) aRequiredId < m_layers.size() ); + + if( aRequired ) + m_layers[aLayerId].requiredLayers.insert( aRequiredId ); + else + m_layers[aLayerId].requiredLayers.erase( aRequired ); +} + + +// stupid C++... python lambda would do this in one line +template <class Container> +struct queryVisitor +{ + typedef typename Container::value_type item_type; + + queryVisitor( Container& aCont, int aLayer ) : + m_cont( aCont ), m_layer( aLayer ) + { + } + + bool operator()( VIEW_ITEM* aItem ) + { + if( aItem->ViewIsVisible() ) + m_cont.push_back( VIEW::LAYER_ITEM_PAIR( aItem, m_layer ) ); + + return true; + } + + Container& m_cont; + int m_layer; +}; + + +int VIEW::Query( const BOX2I& aRect, std::vector<LAYER_ITEM_PAIR>& aResult ) const +{ + if( m_orderedLayers.empty() ) + return 0; + + std::vector<VIEW_LAYER*>::const_reverse_iterator i; + + // execute queries in reverse direction, so that items that are on the top of + // the rendering stack are returned first. + for( i = m_orderedLayers.rbegin(); i != m_orderedLayers.rend(); ++i ) + { + // ignore layers that do not contain actual items (i.e. the selection box, menus, floats) + if( ( *i )->displayOnly ) + continue; + + queryVisitor<std::vector<LAYER_ITEM_PAIR> > visitor( aResult, ( *i )->id ); + ( *i )->items->Query( aRect, visitor ); + } + + return aResult.size(); +} + + +VECTOR2D VIEW::ToWorld( const VECTOR2D& aCoord, bool aAbsolute ) const +{ + const MATRIX3x3D& matrix = m_gal->GetScreenWorldMatrix(); + + if( aAbsolute ) + return VECTOR2D( matrix * aCoord ); + else + return VECTOR2D( matrix.GetScale().x * aCoord.x, matrix.GetScale().y * aCoord.y ); +} + + +double VIEW::ToWorld( double aSize ) const +{ + const MATRIX3x3D& matrix = m_gal->GetScreenWorldMatrix(); + + return matrix.GetScale().x * aSize; +} + + +VECTOR2D VIEW::ToScreen( const VECTOR2D& aCoord, bool aAbsolute ) const +{ + const MATRIX3x3D& matrix = m_gal->GetWorldScreenMatrix(); + + if( aAbsolute ) + return VECTOR2D( matrix * aCoord ); + else + return VECTOR2D( matrix.GetScale().x * aCoord.x, matrix.GetScale().y * aCoord.y ); +} + + +double VIEW::ToScreen( double aSize ) const +{ + const MATRIX3x3D& matrix = m_gal->GetWorldScreenMatrix(); + + return matrix.GetScale().x * aSize; +} + + +void VIEW::CopySettings( const VIEW* aOtherView ) +{ + wxASSERT_MSG( false, wxT( "This is not implemented" ) ); +} + + +void VIEW::SetGAL( GAL* aGal ) +{ + m_gal = aGal; + + // clear group numbers, so everything is going to be recached + clearGroupCache(); + + // every target has to be refreshed + MarkDirty(); + + // force the new GAL to display the current viewport. + SetCenter( m_center ); + SetScale( m_scale ); +} + + +BOX2D VIEW::GetViewport() const +{ + BOX2D rect; + VECTOR2D screenSize = m_gal->GetScreenPixelSize(); + + rect.SetOrigin( ToWorld( VECTOR2D( 0, 0 ) ) ); + rect.SetEnd( ToWorld( screenSize ) ); + + return rect.Normalize(); +} + + +void VIEW::SetViewport( const BOX2D& aViewport ) +{ + VECTOR2D ssize = ToWorld( m_gal->GetScreenPixelSize(), false ); + + wxASSERT( ssize.x > 0 && ssize.y > 0 ); + + VECTOR2D centre = aViewport.Centre(); + VECTOR2D vsize = aViewport.GetSize(); + double zoom = 1.0 / std::max( fabs( vsize.x / ssize.x ), fabs( vsize.y / ssize.y ) ); + + SetCenter( centre ); + SetScale( GetScale() * zoom ); +} + + +void VIEW::SetMirror( bool aMirrorX, bool aMirrorY ) +{ + m_gal->SetFlip( aMirrorX, aMirrorY ); +} + + +void VIEW::SetScale( double aScale, const VECTOR2D& aAnchor ) +{ + VECTOR2D a = ToScreen( aAnchor ); + + if( aScale < m_minScale ) + m_scale = m_minScale; + else if( aScale > m_maxScale ) + m_scale = m_maxScale; + else + m_scale = aScale; + + m_gal->SetZoomFactor( m_scale ); + m_gal->ComputeWorldScreenMatrix(); + + VECTOR2D delta = ToWorld( a ) - aAnchor; + + SetCenter( m_center - delta ); + + // Redraw everything after the viewport has changed + MarkDirty(); +} + + +void VIEW::SetCenter( const VECTOR2D& aCenter ) +{ + m_center = aCenter; + + if( !m_boundary.Contains( aCenter ) ) + { + if( m_center.x < m_boundary.GetLeft() ) + m_center.x = m_boundary.GetLeft(); + else if( aCenter.x > m_boundary.GetRight() ) + m_center.x = m_boundary.GetRight(); + + if( m_center.y < m_boundary.GetTop() ) + m_center.y = m_boundary.GetTop(); + else if( m_center.y > m_boundary.GetBottom() ) + m_center.y = m_boundary.GetBottom(); + } + + m_gal->SetLookAtPoint( m_center ); + m_gal->ComputeWorldScreenMatrix(); + + // Redraw everything after the viewport has changed + MarkDirty(); +} + + +void VIEW::SetLayerOrder( int aLayer, int aRenderingOrder ) +{ + m_layers[aLayer].renderingOrder = aRenderingOrder; + + sortLayers(); +} + + +int VIEW::GetLayerOrder( int aLayer ) const +{ + return m_layers.at( aLayer ).renderingOrder; +} + + +void VIEW::SortLayers( int aLayers[], int& aCount ) const +{ + int maxLay, maxOrd, maxIdx; + + for( int i = 0; i < aCount; ++i ) + { + maxLay = aLayers[i]; + maxOrd = GetLayerOrder( maxLay ); + maxIdx = i; + + // Look for the max element in the range (j..aCount) + for( int j = i; j < aCount; ++j ) + { + if( maxOrd < GetLayerOrder( aLayers[j] ) ) + { + maxLay = aLayers[j]; + maxOrd = GetLayerOrder( maxLay ); + maxIdx = j; + } + } + + // Swap elements + aLayers[maxIdx] = aLayers[i]; + aLayers[i] = maxLay; + } +} + + +struct VIEW::updateItemsColor +{ + updateItemsColor( int aLayer, PAINTER* aPainter, GAL* aGal ) : + layer( aLayer ), painter( aPainter ), gal( aGal ) + { + } + + bool operator()( VIEW_ITEM* aItem ) + { + // Obtain the color that should be used for coloring the item + const COLOR4D color = painter->GetSettings()->GetColor( aItem, layer ); + int group = aItem->getGroup( layer ); + + if( group >= 0 ) + gal->ChangeGroupColor( group, color ); + + return true; + } + + int layer; + PAINTER* painter; + GAL* gal; +}; + + +void VIEW::UpdateLayerColor( int aLayer ) +{ + // There is no point in updating non-cached layers + if( !IsCached( aLayer ) ) + return; + + BOX2I r; + + r.SetMaximum(); + + updateItemsColor visitor( aLayer, m_painter, m_gal ); + m_layers[aLayer].items->Query( r, visitor ); + MarkTargetDirty( m_layers[aLayer].target ); +} + + +void VIEW::UpdateAllLayersColor() +{ + BOX2I r; + + r.SetMaximum(); + + for( LAYER_MAP_ITER i = m_layers.begin(); i != m_layers.end(); ++i ) + { + VIEW_LAYER* l = &( ( *i ).second ); + + // There is no point in updating non-cached layers + if( !IsCached( l->id ) ) + continue; + + updateItemsColor visitor( l->id, m_painter, m_gal ); + l->items->Query( r, visitor ); + } + + MarkDirty(); +} + + +struct VIEW::changeItemsDepth +{ + changeItemsDepth( int aLayer, int aDepth, GAL* aGal ) : + layer( aLayer ), depth( aDepth ), gal( aGal ) + { + } + + bool operator()( VIEW_ITEM* aItem ) + { + int group = aItem->getGroup( layer ); + + if( group >= 0 ) + gal->ChangeGroupDepth( group, depth ); + + return true; + } + + int layer, depth; + GAL* gal; +}; + + +void VIEW::ChangeLayerDepth( int aLayer, int aDepth ) +{ + // There is no point in updating non-cached layers + if( !IsCached( aLayer ) ) + return; + + BOX2I r; + + r.SetMaximum(); + + changeItemsDepth visitor( aLayer, aDepth, m_gal ); + m_layers[aLayer].items->Query( r, visitor ); + MarkTargetDirty( m_layers[aLayer].target ); +} + + +int VIEW::GetTopLayer() const +{ + if( m_topLayers.size() == 0 ) + return 0; + + return *m_topLayers.begin(); +} + + +void VIEW::SetTopLayer( int aLayer, bool aEnabled ) +{ + if( aEnabled ) + { + if( m_topLayers.count( aLayer ) == 1 ) + return; + + m_topLayers.insert( aLayer ); + + // Move the layer closer to front + if( m_enableOrderModifier ) + m_layers[aLayer].renderingOrder += TOP_LAYER_MODIFIER; + } + else + { + if( m_topLayers.count( aLayer ) == 0 ) + return; + + m_topLayers.erase( aLayer ); + + // Restore the previous rendering order + if( m_enableOrderModifier ) + m_layers[aLayer].renderingOrder -= TOP_LAYER_MODIFIER; + } +} + + +void VIEW::EnableTopLayer( bool aEnable ) +{ + if( aEnable == m_enableOrderModifier ) + return; + + m_enableOrderModifier = aEnable; + + std::set<unsigned int>::iterator it; + + if( aEnable ) + { + for( it = m_topLayers.begin(); it != m_topLayers.end(); ++it ) + m_layers[*it].renderingOrder += TOP_LAYER_MODIFIER; + } + else + { + for( it = m_topLayers.begin(); it != m_topLayers.end(); ++it ) + m_layers[*it].renderingOrder -= TOP_LAYER_MODIFIER; + } + + UpdateAllLayersOrder(); + UpdateAllLayersColor(); +} + + +void VIEW::ClearTopLayers() +{ + std::set<unsigned int>::iterator it; + + if( m_enableOrderModifier ) + { + // Restore the previous rendering order for layers that were marked as top + for( it = m_topLayers.begin(); it != m_topLayers.end(); ++it ) + m_layers[*it].renderingOrder -= TOP_LAYER_MODIFIER; + } + + m_topLayers.clear(); +} + + +void VIEW::UpdateAllLayersOrder() +{ + sortLayers(); + + BOOST_FOREACH( LAYER_MAP::value_type& l, m_layers ) + { + ChangeLayerDepth( l.first, l.second.renderingOrder ); + } + + MarkDirty(); +} + + +struct VIEW::drawItem +{ + drawItem( VIEW* aView, int aLayer ) : + view( aView ), layer( aLayer ) + { + } + + bool operator()( VIEW_ITEM* aItem ) + { + // Conditions that have te be fulfilled for an item to be drawn + bool drawCondition = aItem->isRenderable() && + aItem->ViewGetLOD( layer ) < view->m_scale; + if( !drawCondition ) + return true; + + view->draw( aItem, layer ); + + return true; + } + + VIEW* view; + int layer, layers[VIEW_MAX_LAYERS]; +}; + + +void VIEW::redrawRect( const BOX2I& aRect ) +{ + BOOST_FOREACH( VIEW_LAYER* l, m_orderedLayers ) + { + if( l->visible && IsTargetDirty( l->target ) && areRequiredLayersEnabled( l->id ) ) + { + drawItem drawFunc( this, l->id ); + + m_gal->SetTarget( l->target ); + m_gal->SetLayerDepth( l->renderingOrder ); + l->items->Query( aRect, drawFunc ); + } + } +} + + +void VIEW::draw( VIEW_ITEM* aItem, int aLayer, bool aImmediate ) +{ + if( IsCached( aLayer ) && !aImmediate ) + { + // Draw using cached information or create one + int group = aItem->getGroup( aLayer ); + + if( group >= 0 ) + { + m_gal->DrawGroup( group ); + } + else + { + group = m_gal->BeginGroup(); + aItem->setGroup( aLayer, group ); + + if( !m_painter->Draw( aItem, aLayer ) ) + aItem->ViewDraw( aLayer, m_gal ); // Alternative drawing method + + m_gal->EndGroup(); + } + } + else + { + // Immediate mode + if( !m_painter->Draw( aItem, aLayer ) ) + aItem->ViewDraw( aLayer, m_gal ); // Alternative drawing method + } +} + + +void VIEW::draw( VIEW_ITEM* aItem, bool aImmediate ) +{ + int layers[VIEW_MAX_LAYERS], layers_count; + + aItem->ViewGetLayers( layers, layers_count ); + + // Sorting is needed for drawing order dependent GALs (like Cairo) + SortLayers( layers, layers_count ); + + for( int i = 0; i < layers_count; ++i ) + { + m_gal->SetLayerDepth( m_layers.at( layers[i] ).renderingOrder ); + draw( aItem, layers[i], aImmediate ); + } +} + + +void VIEW::draw( VIEW_GROUP* aGroup, bool aImmediate ) +{ + std::set<VIEW_ITEM*>::const_iterator it; + + for( it = aGroup->Begin(); it != aGroup->End(); ++it ) + draw( *it, aImmediate ); +} + + +struct VIEW::unlinkItem +{ + bool operator()( VIEW_ITEM* aItem ) + { + aItem->m_view = NULL; + + return true; + } +}; + + +struct VIEW::recacheItem +{ + recacheItem( VIEW* aView, GAL* aGal, int aLayer, bool aImmediately ) : + view( aView ), gal( aGal ), layer( aLayer ), immediately( aImmediately ) + { + } + + bool operator()( VIEW_ITEM* aItem ) + { + // Remove previously cached group + int group = aItem->getGroup( layer ); + + if( group >= 0 ) + gal->DeleteGroup( group ); + + if( immediately ) + { + group = gal->BeginGroup(); + aItem->setGroup( layer, group ); + + if( !view->m_painter->Draw( aItem, layer ) ) + aItem->ViewDraw( layer, gal ); // Alternative drawing method + + gal->EndGroup(); + } + else + { + aItem->ViewUpdate( VIEW_ITEM::ALL ); + aItem->setGroup( layer, -1 ); + } + + return true; + } + + VIEW* view; + GAL* gal; + int layer; + bool immediately; +}; + + +void VIEW::Clear() +{ + BOX2I r; + + r.SetMaximum(); + + BOOST_FOREACH( VIEW_ITEM* item, m_needsUpdate ) + item->clearUpdateFlags(); + + m_needsUpdate.clear(); + + for( LAYER_MAP_ITER i = m_layers.begin(); i != m_layers.end(); ++i ) + { + VIEW_LAYER* l = &( ( *i ).second ); + unlinkItem v; + + if( m_dynamic ) + l->items->Query( r, v ); + + l->items->RemoveAll(); + } + + m_gal->ClearCache(); +} + + +void VIEW::ClearTargets() +{ + if( IsTargetDirty( TARGET_CACHED ) || IsTargetDirty( TARGET_NONCACHED ) ) + { + // TARGET_CACHED and TARGET_NONCACHED have to be redrawn together, as they contain + // layers that rely on each other (eg. netnames are noncached, but tracks - are cached) + m_gal->ClearTarget( TARGET_NONCACHED ); + m_gal->ClearTarget( TARGET_CACHED ); + + MarkDirty(); + } + + if( IsTargetDirty( TARGET_OVERLAY ) ) + { + m_gal->ClearTarget( TARGET_OVERLAY ); + } +} + + +void VIEW::Redraw() +{ +#ifdef PROFILE + prof_counter totalRealTime; + prof_start( &totalRealTime ); +#endif /* PROFILE */ + + VECTOR2D screenSize = m_gal->GetScreenPixelSize(); + BOX2I rect( ToWorld( VECTOR2D( 0, 0 ) ), + ToWorld( screenSize ) - ToWorld( VECTOR2D( 0, 0 ) ) ); + rect.Normalize(); + + redrawRect( rect ); + + // All targets were redrawn, so nothing is dirty + markTargetClean( TARGET_CACHED ); + markTargetClean( TARGET_NONCACHED ); + markTargetClean( TARGET_OVERLAY ); + +#ifdef PROFILE + prof_end( &totalRealTime ); + + wxLogDebug( wxT( "Redraw: %.1f ms" ), totalRealTime.msecs() ); +#endif /* PROFILE */ +} + + +const VECTOR2I& VIEW::GetScreenPixelSize() const +{ + return m_gal->GetScreenPixelSize(); +} + + +struct VIEW::clearLayerCache +{ + clearLayerCache( VIEW* aView ) : + view( aView ) + { + } + + bool operator()( VIEW_ITEM* aItem ) + { + aItem->deleteGroups(); + + return true; + } + + VIEW* view; +}; + + +void VIEW::clearGroupCache() +{ + BOX2I r; + + r.SetMaximum(); + clearLayerCache visitor( this ); + + for( LAYER_MAP_ITER i = m_layers.begin(); i != m_layers.end(); ++i ) + { + VIEW_LAYER* l = &( ( *i ).second ); + l->items->Query( r, visitor ); + } +} + + +void VIEW::invalidateItem( VIEW_ITEM* aItem, int aUpdateFlags ) +{ + // updateLayers updates geometry too, so we do not have to update both of them at the same time + if( aUpdateFlags & VIEW_ITEM::LAYERS ) + updateLayers( aItem ); + else if( aUpdateFlags & VIEW_ITEM::GEOMETRY ) + updateBbox( aItem ); + + int layers[VIEW_MAX_LAYERS], layers_count; + aItem->ViewGetLayers( layers, layers_count ); + + // Iterate through layers used by the item and recache it immediately + for( int i = 0; i < layers_count; ++i ) + { + int layerId = layers[i]; + + if( IsCached( layerId ) ) + { + if( aUpdateFlags & ( VIEW_ITEM::GEOMETRY | VIEW_ITEM::LAYERS ) ) + updateItemGeometry( aItem, layerId ); + else if( aUpdateFlags & VIEW_ITEM::COLOR ) + updateItemColor( aItem, layerId ); + } + + // Mark those layers as dirty, so the VIEW will be refreshed + MarkTargetDirty( m_layers[layerId].target ); + } + + aItem->clearUpdateFlags(); +} + + +void VIEW::sortLayers() +{ + int n = 0; + + m_orderedLayers.resize( m_layers.size() ); + + for( LAYER_MAP_ITER i = m_layers.begin(); i != m_layers.end(); ++i ) + m_orderedLayers[n++] = &i->second; + + sort( m_orderedLayers.begin(), m_orderedLayers.end(), compareRenderingOrder ); + + MarkDirty(); +} + + +void VIEW::updateItemColor( VIEW_ITEM* aItem, int aLayer ) +{ + wxASSERT( (unsigned) aLayer < m_layers.size() ); + wxASSERT( IsCached( aLayer ) ); + + // Obtain the color that should be used for coloring the item on the specific layerId + const COLOR4D color = m_painter->GetSettings()->GetColor( aItem, aLayer ); + int group = aItem->getGroup( aLayer ); + + // Change the color, only if it has group assigned + if( group >= 0 ) + m_gal->ChangeGroupColor( group, color ); +} + + +void VIEW::updateItemGeometry( VIEW_ITEM* aItem, int aLayer ) +{ + wxASSERT( (unsigned) aLayer < m_layers.size() ); + wxASSERT( IsCached( aLayer ) ); + + VIEW_LAYER& l = m_layers.at( aLayer ); + + m_gal->SetTarget( l.target ); + m_gal->SetLayerDepth( l.renderingOrder ); + + // Redraw the item from scratch + int group = aItem->getGroup( aLayer ); + + if( group >= 0 ) + m_gal->DeleteGroup( group ); + + group = m_gal->BeginGroup(); + aItem->setGroup( aLayer, group ); + + if( !m_painter->Draw( static_cast<EDA_ITEM*>( aItem ), aLayer ) ) + aItem->ViewDraw( aLayer, m_gal ); // Alternative drawing method + + m_gal->EndGroup(); +} + + +void VIEW::updateBbox( VIEW_ITEM* aItem ) +{ + int layers[VIEW_MAX_LAYERS], layers_count; + + aItem->ViewGetLayers( layers, layers_count ); + + for( int i = 0; i < layers_count; ++i ) + { + VIEW_LAYER& l = m_layers[layers[i]]; + l.items->Remove( aItem ); + l.items->Insert( aItem ); + MarkTargetDirty( l.target ); + } +} + + +void VIEW::updateLayers( VIEW_ITEM* aItem ) +{ + int layers[VIEW_MAX_LAYERS], layers_count; + + // Remove the item from previous layer set + aItem->getLayers( layers, layers_count ); + + for( int i = 0; i < layers_count; ++i ) + { + VIEW_LAYER& l = m_layers[layers[i]]; + l.items->Remove( aItem ); + MarkTargetDirty( l.target ); + + if( IsCached( l.id ) ) + { + // Redraw the item from scratch + int prevGroup = aItem->getGroup( layers[i] ); + + if( prevGroup >= 0 ) + { + m_gal->DeleteGroup( prevGroup ); + aItem->setGroup( l.id, -1 ); + } + } + } + + // Add the item to new layer set + aItem->ViewGetLayers( layers, layers_count ); + aItem->saveLayers( layers, layers_count ); + + for( int i = 0; i < layers_count; i++ ) + { + VIEW_LAYER& l = m_layers[layers[i]]; + l.items->Insert( aItem ); + MarkTargetDirty( l.target ); + } +} + + +bool VIEW::areRequiredLayersEnabled( int aLayerId ) const +{ + wxASSERT( (unsigned) aLayerId < m_layers.size() ); + + std::set<int>::iterator it, it_end; + + for( it = m_layers.at( aLayerId ).requiredLayers.begin(), + it_end = m_layers.at( aLayerId ).requiredLayers.end(); it != it_end; ++it ) + { + // That is enough if just one layer is not enabled + if( !m_layers.at( *it ).visible || !areRequiredLayersEnabled( *it ) ) + return false; + } + + return true; +} + + +void VIEW::RecacheAllItems( bool aImmediately ) +{ + BOX2I r; + + r.SetMaximum(); + +#ifdef PROFILE + prof_counter totalRealTime; + prof_start( &totalRealTime ); +#endif /* PROFILE */ + + for( LAYER_MAP_ITER i = m_layers.begin(); i != m_layers.end(); ++i ) + { + VIEW_LAYER* l = &( ( *i ).second ); + + if( IsCached( l->id ) ) + { + m_gal->SetTarget( l->target ); + m_gal->SetLayerDepth( l->renderingOrder ); + recacheItem visitor( this, m_gal, l->id, aImmediately ); + l->items->Query( r, visitor ); + MarkTargetDirty( l->target ); + } + } + +#ifdef PROFILE + prof_end( &totalRealTime ); + + wxLogDebug( wxT( "RecacheAllItems::immediately: %u %.1f ms" ), + aImmediately, totalRealTime.msecs() ); +#endif /* PROFILE */ +} + + +void VIEW::UpdateItems() +{ + // Update items that need this + BOOST_FOREACH( VIEW_ITEM* item, m_needsUpdate ) + { + assert( item->viewRequiredUpdate() != VIEW_ITEM::NONE ); + + invalidateItem( item, item->viewRequiredUpdate() ); + } + + m_needsUpdate.clear(); +} + + +struct VIEW::extentsVisitor +{ + BOX2I extents; + bool first; + + extentsVisitor() + { + first = true; + } + + bool operator()( VIEW_ITEM* aItem ) + { + if( first ) + extents = aItem->ViewBBox(); + else + extents.Merge ( aItem->ViewBBox() ); + return false; + } +}; + + +const BOX2I VIEW::CalculateExtents() +{ + extentsVisitor v; + BOX2I fullScene; + fullScene.SetMaximum(); + + BOOST_FOREACH( VIEW_LAYER* l, m_orderedLayers ) + { + l->items->Query( fullScene, v ); + } + + return v.extents; +} + + +const int VIEW::TOP_LAYER_MODIFIER = -VIEW_MAX_LAYERS; diff --git a/common/view/view_controls.cpp b/common/view/view_controls.cpp new file mode 100644 index 0000000..850cf0b --- /dev/null +++ b/common/view/view_controls.cpp @@ -0,0 +1,47 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de + * Copyright (C) 2013-2015 CERN + * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 <view/view.h> +#include <view/view_controls.h> +#include <gal/graphics_abstraction_layer.h> + +using namespace KIGFX; + +void VIEW_CONTROLS::ShowCursor( bool aEnabled ) +{ + m_view->GetGAL()->SetCursorEnabled( aEnabled ); +} + + +void VIEW_CONTROLS::Reset() +{ + SetSnapping( false ); + SetAutoPan( false ); + ForceCursorPosition( false ); + ShowCursor( false ); + CaptureCursor( false ); + SetGrabMouse( false ); +} diff --git a/common/view/view_group.cpp b/common/view/view_group.cpp new file mode 100644 index 0000000..1459954 --- /dev/null +++ b/common/view/view_group.cpp @@ -0,0 +1,162 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 + * + */ + +/** + * @file view_group.cpp + * @brief VIEW_GROUP extends VIEW_ITEM by possibility of grouping items into a single object. + * VIEW_GROUP does not take over ownership of the held items. The main purpose of this class is + * to group items and draw them on a single layer (in particular the overlay). + */ + +#include <set> +#include <algorithm> +#include <view/view_group.h> +#include <view/view.h> +#include <painter.h> +#include <gal/graphics_abstraction_layer.h> +#include <boost/foreach.hpp> +#include <layers_id_colors_and_visibility.h> + +using namespace KIGFX; + +VIEW_GROUP::VIEW_GROUP( VIEW* aView ) : + m_layer( ITEM_GAL_LAYER( GP_OVERLAY ) ) +{ + m_view = aView; +} + + +VIEW_GROUP::~VIEW_GROUP() +{ +} + + +void VIEW_GROUP::Add( VIEW_ITEM* aItem ) +{ + m_items.insert( aItem ); +} + + +void VIEW_GROUP::Remove( VIEW_ITEM* aItem ) +{ + m_items.erase( aItem ); +} + + +void VIEW_GROUP::Clear() +{ + m_items.clear(); +} + + +unsigned int VIEW_GROUP::GetSize() const +{ + return m_items.size(); +} + + +const BOX2I VIEW_GROUP::ViewBBox() const +{ + BOX2I maxBox; + + maxBox.SetMaximum(); + return maxBox; +} + + +void VIEW_GROUP::ViewDraw( int aLayer, GAL* aGal ) const +{ + PAINTER* painter = m_view->GetPainter(); + + // Draw all items immediately (without caching) + BOOST_FOREACH( VIEW_ITEM* item, m_items ) + { + aGal->PushDepth(); + + int layers[VIEW::VIEW_MAX_LAYERS], layers_count; + item->ViewGetLayers( layers, layers_count ); + m_view->SortLayers( layers, layers_count ); + + for( int i = 0; i < layers_count; i++ ) + { + if( m_view->IsCached( layers[i] ) && m_view->IsLayerVisible( layers[i] ) ) + { + aGal->AdvanceDepth(); + + if( !painter->Draw( item, layers[i] ) ) + item->ViewDraw( layers[i], aGal ); // Alternative drawing method + } + } + + aGal->PopDepth(); + } +} + + +void VIEW_GROUP::ViewGetLayers( int aLayers[], int& aCount ) const +{ + // Everything is displayed on a single layer + aLayers[0] = m_layer; + aCount = 1; +} + + +void VIEW_GROUP::FreeItems() +{ + BOOST_FOREACH( VIEW_ITEM* item, m_items ) + { + delete item; + } + m_items.clear(); +} + + +void VIEW_GROUP::ItemsSetVisibility( bool aVisible ) +{ + std::set<VIEW_ITEM*>::const_iterator it, it_end; + + for( it = m_items.begin(), it_end = m_items.end(); it != it_end; ++it ) + (*it)->ViewSetVisible( aVisible ); +} + + +void VIEW_GROUP::ItemsViewUpdate( VIEW_ITEM::VIEW_UPDATE_FLAGS aFlags ) +{ + std::set<VIEW_ITEM*>::const_iterator it, it_end; + + for( it = m_items.begin(), it_end = m_items.end(); it != it_end; ++it ) + (*it)->ViewUpdate( aFlags ); +} + + +void VIEW_GROUP::updateBbox() +{ + // Save the used VIEW, as it used nulled during Remove() + VIEW* view = m_view; + + // Reinsert the group, so the bounding box can be updated + view->Remove( this ); + view->Add( this ); +} diff --git a/common/view/view_item.cpp b/common/view/view_item.cpp new file mode 100644 index 0000000..f1713f3 --- /dev/null +++ b/common/view/view_item.cpp @@ -0,0 +1,109 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * + * 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 <gal/definitions.h> + +#include <view/view_item.h> +#include <view/view.h> + +using namespace KIGFX; + +void VIEW_ITEM::ViewRelease() +{ + if( m_view && m_view->IsDynamic() ) + m_view->Remove( this ); +} + + +void VIEW_ITEM::getLayers( int* aLayers, int& aCount ) const +{ + int* layersPtr = aLayers; + + for( unsigned int i = 0; i < m_layers.size(); ++i ) + { + if( m_layers[i] ) + *layersPtr++ = i; + } + + aCount = m_layers.count(); +} + + +int VIEW_ITEM::getGroup( int aLayer ) const +{ + for( int i = 0; i < m_groupsSize; ++i ) + { + if( m_groups[i].first == aLayer ) + return m_groups[i].second; + } + + return -1; +} + + +std::vector<int> VIEW_ITEM::getAllGroups() const +{ + std::vector<int> groups( m_groupsSize ); + + for( int i = 0; i < m_groupsSize; ++i ) + { + groups[i] = m_groups[i].second; + } + + return groups; +} + + +void VIEW_ITEM::setGroup( int aLayer, int aId ) +{ + // Look if there is already an entry for the layer + for( int i = 0; i < m_groupsSize; ++i ) + { + if( m_groups[i].first == aLayer ) + { + m_groups[i].second = aId; + return; + } + } + + // If there was no entry for the given layer - create one + GroupPair* newGroups = new GroupPair[m_groupsSize + 1]; + + if( m_groupsSize > 0 ) + { + std::copy( m_groups, m_groups + m_groupsSize, newGroups ); + delete[] m_groups; + } + + m_groups = newGroups; + newGroups[m_groupsSize++] = GroupPair( aLayer, aId ); +} + + +void VIEW_ITEM::deleteGroups() +{ + delete[] m_groups; + m_groups = NULL; + m_groupsSize = 0; +} diff --git a/common/view/wx_view_controls.cpp b/common/view/wx_view_controls.cpp new file mode 100644 index 0000000..ac6c778 --- /dev/null +++ b/common/view/wx_view_controls.cpp @@ -0,0 +1,491 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de + * Copyright (C) 2013-2015 CERN + * Copyright (C) 2012-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 <wx/wx.h> + +#include <view/view.h> +#include <view/wx_view_controls.h> +#include <gal/graphics_abstraction_layer.h> +#include <tool/tool_dispatcher.h> + +using namespace KIGFX; + +const wxEventType WX_VIEW_CONTROLS::EVT_REFRESH_MOUSE = wxNewEventType(); + +WX_VIEW_CONTROLS::WX_VIEW_CONTROLS( VIEW* aView, wxScrolledCanvas* aParentPanel ) : + VIEW_CONTROLS( aView ), m_state( IDLE ), m_parentPanel( aParentPanel ), m_scrollScale( 1.0, 1.0 ) +{ + m_parentPanel->Connect( wxEVT_MOTION, + wxMouseEventHandler( WX_VIEW_CONTROLS::onMotion ), NULL, this ); +#ifdef USE_OSX_MAGNIFY_EVENT + m_parentPanel->Connect( wxEVT_MAGNIFY, + wxMouseEventHandler( WX_VIEW_CONTROLS::onMagnify ), NULL, this ); +#endif + m_parentPanel->Connect( wxEVT_MOUSEWHEEL, + wxMouseEventHandler( WX_VIEW_CONTROLS::onWheel ), NULL, this ); + m_parentPanel->Connect( wxEVT_MIDDLE_UP, + wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), NULL, this ); + m_parentPanel->Connect( wxEVT_MIDDLE_DOWN, + wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), NULL, this ); + m_parentPanel->Connect( wxEVT_LEFT_UP, + wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), NULL, this ); + m_parentPanel->Connect( wxEVT_LEFT_DOWN, + wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), NULL, this ); +#if defined _WIN32 || defined _WIN64 + m_parentPanel->Connect( wxEVT_ENTER_WINDOW, + wxMouseEventHandler( WX_VIEW_CONTROLS::onEnter ), NULL, this ); +#endif + m_parentPanel->Connect( wxEVT_LEAVE_WINDOW, + wxMouseEventHandler( WX_VIEW_CONTROLS::onLeave ), NULL, this ); + m_parentPanel->Connect( wxEVT_SCROLLWIN_THUMBTRACK, + wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), NULL, this ); + + m_panTimer.SetOwner( this ); + this->Connect( wxEVT_TIMER, + wxTimerEventHandler( WX_VIEW_CONTROLS::onTimer ), NULL, this ); +} + + +void WX_VIEW_CONTROLS::onMotion( wxMouseEvent& aEvent ) +{ + bool isAutoPanning = false; + + if( m_autoPanEnabled ) + isAutoPanning = handleAutoPanning( aEvent ); + + if( !isAutoPanning && aEvent.Dragging() ) + { + if( m_state == DRAG_PANNING ) + { + VECTOR2D d = m_dragStartPoint - VECTOR2D( aEvent.GetX(), aEvent.GetY() ); + VECTOR2D delta = m_view->ToWorld( d, false ); + + m_view->SetCenter( m_lookStartPoint + delta ); + aEvent.StopPropagation(); + } + } + + aEvent.Skip(); +} + + +void WX_VIEW_CONTROLS::onWheel( wxMouseEvent& aEvent ) +{ + const double wheelPanSpeed = 0.001; + + if( aEvent.ControlDown() || aEvent.ShiftDown() || m_enableMousewheelPan ) + { + // Scrolling + VECTOR2D scrollVec = m_view->ToWorld( m_view->GetScreenPixelSize(), false ) * + ( (double) aEvent.GetWheelRotation() * wheelPanSpeed ); + int axis = aEvent.GetWheelAxis(); + double scrollX = 0.0; + double scrollY = 0.0; + + if ( m_enableMousewheelPan ) + { + if ( axis == wxMOUSE_WHEEL_HORIZONTAL ) + scrollX = scrollVec.x; + else + scrollY = -scrollVec.y; + } + else + { + if ( aEvent.ControlDown() ) + scrollX = -scrollVec.x; + else + scrollY = -scrollVec.y; + } + + VECTOR2D delta( scrollX, scrollY ); + + m_view->SetCenter( m_view->GetCenter() + delta ); + } + else + { + // Zooming + wxLongLong timeStamp = wxGetLocalTimeMillis(); + double timeDiff = timeStamp.ToDouble() - m_timeStamp.ToDouble(); + int rotation = aEvent.GetWheelRotation(); + double zoomScale; + +#ifdef __WXMAC__ + // The following is to support Apple pointer devices (MagicMouse & + // Macbook touchpad), which send events more frequently, but with smaller + // wheel rotation. + // + // It should not break other platforms, but I prefer to be safe than + // sorry. If you find a device that behaves in the same way on another + // platform, feel free to remove #ifdef directives. + if( timeDiff > 0 && timeDiff < 100 && std::abs( rotation ) < 20 ) + { + aEvent.Skip(); + return; + } +#endif + + m_timeStamp = timeStamp; + + // Set scaling speed depending on scroll wheel event interval + if( timeDiff < 500 && timeDiff > 0 ) + { + zoomScale = 2.05 - timeDiff / 500; + + if( rotation < 0 ) + zoomScale = 1.0 / zoomScale; + } + else + { + zoomScale = ( rotation > 0 ) ? 1.05 : 0.95; + } + + if( IsCursorWarpingEnabled() ) + { + CenterOnCursor(); + m_view->SetScale( m_view->GetScale() * zoomScale ); + } + else + { + VECTOR2D anchor = m_view->ToWorld( VECTOR2D( aEvent.GetX(), aEvent.GetY() ) ); + m_view->SetScale( m_view->GetScale() * zoomScale, anchor ); + } + } + + aEvent.Skip(); +} + + +#ifdef USE_OSX_MAGNIFY_EVENT +void WX_VIEW_CONTROLS::onMagnify( wxMouseEvent& aEvent ) +{ + // Scale based on the magnification from our underlying magnification event. + VECTOR2D anchor = m_view->ToWorld( VECTOR2D( aEvent.GetX(), aEvent.GetY() ) ); + m_view->SetScale( m_view->GetScale() * ( aEvent.GetMagnification() + 1.0f ), anchor ); + + aEvent.Skip(); +} +#endif + + +void WX_VIEW_CONTROLS::onButton( wxMouseEvent& aEvent ) +{ + switch( m_state ) + { + case IDLE: + case AUTO_PANNING: + if( aEvent.MiddleDown() ) + { + m_dragStartPoint = VECTOR2D( aEvent.GetX(), aEvent.GetY() ); + m_lookStartPoint = m_view->GetCenter(); + m_state = DRAG_PANNING; + } + + if( aEvent.LeftUp() ) + m_state = IDLE; // Stop autopanning when user release left mouse button + + break; + + case DRAG_PANNING: + if( aEvent.MiddleUp() ) + m_state = IDLE; + + break; + } + + aEvent.Skip(); +} + + +void WX_VIEW_CONTROLS::onEnter( wxMouseEvent& aEvent ) +{ + m_parentPanel->SetFocus(); +} + + +void WX_VIEW_CONTROLS::onLeave( wxMouseEvent& aEvent ) +{ + if( m_cursorCaptured ) + { + bool warp = false; + int x = aEvent.GetX(); + int y = aEvent.GetY(); + wxSize parentSize = m_parentPanel->GetClientSize(); + + if( x < 0 ) + { + x = 0; + warp = true; + } + else if( x >= parentSize.x ) + { + x = parentSize.x - 1; + warp = true; + } + + if( y < 0 ) + { + y = 0; + warp = true; + } + else if( y >= parentSize.y ) + { + y = parentSize.y - 1; + warp = true; + } + + if( warp ) + m_parentPanel->WarpPointer( x, y ); + } +} + + +void WX_VIEW_CONTROLS::onTimer( wxTimerEvent& aEvent ) +{ + switch( m_state ) + { + case AUTO_PANNING: + { +#if wxCHECK_VERSION( 3, 0, 0 ) + if( !m_parentPanel->HasFocus() ) + break; +#endif + + double borderSize = std::min( m_autoPanMargin * m_view->GetScreenPixelSize().x, + m_autoPanMargin * m_view->GetScreenPixelSize().y ); + + VECTOR2D dir( m_panDirection ); + + if( dir.EuclideanNorm() > borderSize ) + dir = dir.Resize( borderSize ); + + dir = m_view->ToWorld( dir, false ); + m_view->SetCenter( m_view->GetCenter() + dir * m_autoPanSpeed ); + + // Notify tools that the cursor position has changed in the world coordinates + wxMouseEvent moveEvent( EVT_REFRESH_MOUSE ); + + // Set the modifiers state +#if wxCHECK_VERSION( 3, 0, 0 ) + moveEvent.SetControlDown( wxGetKeyState( WXK_CONTROL ) ); + moveEvent.SetShiftDown( wxGetKeyState( WXK_SHIFT ) ); + moveEvent.SetAltDown( wxGetKeyState( WXK_ALT ) ); +#else + // wx <3.0 do not have accessors, but the fields are exposed + moveEvent.m_controlDown = wxGetKeyState( WXK_CONTROL ); + moveEvent.m_shiftDown = wxGetKeyState( WXK_SHIFT ); + moveEvent.m_altDown = wxGetKeyState( WXK_ALT ); +#endif + + wxPostEvent( m_parentPanel, moveEvent ); + } + break; + + case IDLE: // Just remove unnecessary warnings + case DRAG_PANNING: + break; + } +} + + +void WX_VIEW_CONTROLS::onScroll( wxScrollWinEvent& aEvent ) +{ + VECTOR2D center = m_view->GetCenter(); + const BOX2I& boundary = m_view->GetBoundary(); + + if( aEvent.GetOrientation() == wxHORIZONTAL ) + center.x = (double) aEvent.GetPosition() * boundary.GetWidth() / m_scrollScale.x + boundary.GetLeft(); + else if( aEvent.GetOrientation() == wxVERTICAL ) + center.y = (double) aEvent.GetPosition() * boundary.GetHeight() / m_scrollScale.y + boundary.GetTop(); + + m_view->SetCenter( center ); + m_parentPanel->Refresh(); +} + + +void WX_VIEW_CONTROLS::SetGrabMouse( bool aEnabled ) +{ + if( aEnabled && !m_grabMouse ) + m_parentPanel->CaptureMouse(); + else if( !aEnabled && m_grabMouse ) + m_parentPanel->ReleaseMouse(); + + VIEW_CONTROLS::SetGrabMouse( aEnabled ); +} + + +VECTOR2I WX_VIEW_CONTROLS::GetMousePosition() const +{ + wxPoint msp = wxGetMousePosition(); + wxPoint winp = m_parentPanel->GetScreenPosition(); + + return VECTOR2I( msp.x - winp.x, msp.y - winp.y ); +} + + +VECTOR2D WX_VIEW_CONTROLS::GetCursorPosition() const +{ + if( m_forceCursorPosition ) + { + return m_forcedPosition; + } + else + { + VECTOR2D mousePosition = GetMousePosition(); + + if( m_snappingEnabled ) + return m_view->GetGAL()->GetGridPoint( m_view->ToWorld( mousePosition ) ); + else + return m_view->ToWorld( mousePosition ); + } +} + + +void WX_VIEW_CONTROLS::WarpCursor( const VECTOR2D& aPosition, bool aWorldCoordinates, + bool aWarpView ) const +{ + if( aWorldCoordinates ) + { + const VECTOR2I& screenSize = m_view->GetGAL()->GetScreenPixelSize(); + BOX2I screen( VECTOR2I( 0, 0 ), screenSize ); + VECTOR2D screenPos = m_view->ToScreen( aPosition ); + + if( !screen.Contains( screenPos ) ) + { + if( aWarpView ) + { + m_view->SetCenter( aPosition ); + m_parentPanel->WarpPointer( screenSize.x / 2, screenSize.y / 2 ); + } + } + else + { + m_parentPanel->WarpPointer( screenPos.x, screenPos.y ); + } + } + else + { + m_parentPanel->WarpPointer( aPosition.x, aPosition.y ); + } +} + + +void WX_VIEW_CONTROLS::CenterOnCursor() const +{ + const VECTOR2I& screenSize = m_view->GetGAL()->GetScreenPixelSize(); + VECTOR2I screenCenter( screenSize / 2 ); + + if( GetMousePosition() != screenCenter ) + { + m_view->SetCenter( GetCursorPosition() ); + m_parentPanel->WarpPointer( KiROUND( screenSize.x / 2 ), KiROUND( screenSize.y / 2 ) ); + } +} + + +bool WX_VIEW_CONTROLS::handleAutoPanning( const wxMouseEvent& aEvent ) +{ + VECTOR2D p( aEvent.GetX(), aEvent.GetY() ); + + // Compute areas where autopanning is active + double borderStart = std::min( m_autoPanMargin * m_view->GetScreenPixelSize().x, + m_autoPanMargin * m_view->GetScreenPixelSize().y ); + double borderEndX = m_view->GetScreenPixelSize().x - borderStart; + double borderEndY = m_view->GetScreenPixelSize().y - borderStart; + + if( p.x < borderStart ) + m_panDirection.x = -( borderStart - p.x ); + else if( p.x > borderEndX ) + m_panDirection.x = ( p.x - borderEndX ); + else + m_panDirection.x = 0; + + if( p.y < borderStart ) + m_panDirection.y = -( borderStart - p.y ); + else if( p.y > borderEndY ) + m_panDirection.y = ( p.y - borderEndY ); + else + m_panDirection.y = 0; + + bool borderHit = ( m_panDirection.x != 0 || m_panDirection.y != 0 ); + + switch( m_state ) + { + case AUTO_PANNING: + if( !borderHit ) + { + m_panTimer.Stop(); + m_state = IDLE; + + return false; + } + + return true; + break; + + case IDLE: + if( borderHit ) + { + m_state = AUTO_PANNING; + m_panTimer.Start( (int) ( 1000.0 / 60.0 ) ); + + return true; + } + + return false; + break; + + case DRAG_PANNING: + return false; + } + + wxASSERT_MSG( false, wxT( "This line should never be reached" ) ); + return false; // Should not be reached, just avoid the compiler warnings.. +} + + +void WX_VIEW_CONTROLS::UpdateScrollbars() +{ + const BOX2D viewport = m_view->GetViewport(); + const BOX2I& boundary = m_view->GetBoundary(); + + m_scrollScale.x = 2e3 * boundary.GetWidth() / viewport.GetWidth(); + m_scrollScale.y = 2e3 * boundary.GetHeight() / viewport.GetHeight(); + + // Another example of wxWidgets being broken by design: scroll position is determined by the + // left (or top, if vertical) edge of the slider. Fortunately, slider size seems to be constant + // (at least for wxGTK 3.0), so we have to add its size to allow user to scroll the workspace + // till the end. + m_parentPanel->SetScrollbars( 1, 1, +#ifdef __LINUX__ + m_scrollScale.x + 1623, m_scrollScale.y + 1623, +#else + m_scrollScale.x, m_scrollScale.y, +#endif + ( viewport.Centre().x - boundary.GetLeft() ) / boundary.GetWidth() * m_scrollScale.x, + ( viewport.Centre().y - boundary.GetTop() ) / boundary.GetHeight() * m_scrollScale.y ); +} diff --git a/common/wildcards_and_files_ext.cpp b/common/wildcards_and_files_ext.cpp new file mode 100644 index 0000000..80326d3 --- /dev/null +++ b/common/wildcards_and_files_ext.cpp @@ -0,0 +1,104 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com + * Copyright (C) 2008-2012 Wayne Stambaugh <stambaughw@verizon.net> + * Copyright (C) 1992-2012 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 + */ + +/** + * @file wildcards_and_files_ext.cpp + */ +#include <wildcards_and_files_ext.h> + +/** + * file extensions and wildcards used in kicad. + */ + +const wxString SchematicSymbolFileExtension( wxT( "sym" ) ); +const wxString SchematicLibraryFileExtension( wxT( "lib" ) ); +const wxString SchematicBackupFileExtension( wxT( "bak" ) ); + +const wxString VrmlFileExtension( wxT( "wrl" ) ); + +const wxString ProjectFileExtension( wxT( "pro" ) ); +const wxString SchematicFileExtension( wxT( "sch" ) ); +const wxString NetlistFileExtension( wxT( "net" ) ); +const wxString ComponentFileExtension( wxT( "cmp" ) ); +const wxString GerberFileExtension( wxT( ".((gbr|(gb|gt)[alops])|pho)" ) ); +const wxString HtmlFileExtension( wxT( "html" ) ); + +const wxString LegacyPcbFileExtension( wxT( "brd" ) ); +const wxString KiCadPcbFileExtension( wxT( "kicad_pcb" ) ); +const wxString PageLayoutDescrFileExtension( wxT( "kicad_wks" ) ); + +const wxString PdfFileExtension( wxT( "pdf" ) ); +const wxString MacrosFileExtension( wxT( "mcr" ) ); +const wxString DrillFileExtension( wxT( "drl" ) ); +const wxString SVGFileExtension( wxT( "svg" ) ); +const wxString ReportFileExtension( wxT( "rpt" ) ); +const wxString FootprintPlaceFileExtension( wxT( "pos" ) ); +const wxString KiCadLib3DShapesPathExtension( wxT( "3dshapes" ) ); ///< 3D shapes default libpath + +const wxString KiCadFootprintLibPathExtension( wxT( "pretty" ) ); ///< KICAD PLUGIN libpath +const wxString LegacyFootprintLibPathExtension( wxT( "mod" ) ); +const wxString EagleFootprintLibPathExtension( wxT( "lbr" ) ); + +const wxString KiCadFootprintFileExtension( wxT( "kicad_mod" ) ); +const wxString GedaPcbFootprintLibFileExtension( wxT( "fp" ) ); + +// These strings are wildcards for file selection dialogs. +// Because these are static, one should explicitly call wxGetTranslation +// to display them as translated. +const wxString SchematicSymbolFileWildcard( _( "KiCad drawing symbol file (*.sym)|*.sym" ) ); +const wxString SchematicLibraryFileWildcard( _( "KiCad component library file (*.lib)|*.lib" ) ); +const wxString ProjectFileWildcard( _( "KiCad project files (*.pro)|*.pro" ) ); +const wxString SchematicFileWildcard( _( "KiCad schematic files (*.sch)|*.sch" ) ); +const wxString NetlistFileWildcard( _( "KiCad netlist files (*.net)|*.net" ) ); +const wxString GerberFileWildcard( _( "Gerber files (*.pho)|*.pho" ) ); +const wxString LegacyPcbFileWildcard( _( "KiCad printed circuit board files (*.brd)|*.brd" ) ); +const wxString EaglePcbFileWildcard( _( "Eagle ver. 6.x XML PCB files (*.brd)|*.brd" ) ); +const wxString PCadPcbFileWildcard( _( "P-Cad 200x ASCII PCB files (*.pcb)|*.pcb" ) ); +const wxString PcbFileWildcard( _( "KiCad s-expr printed circuit board files (*.kicad_pcb)|*.kicad_pcb" ) ); +const wxString KiCadFootprintLibFileWildcard( _( "KiCad footprint s-expre file (*.kicad_mod)|*.kicad_mod" ) ); +const wxString KiCadFootprintLibPathWildcard( _( "KiCad footprint s-expre library path (*.pretty)|*.pretty" ) ); +const wxString LegacyFootprintLibPathWildcard( _( "Legacy footprint library file (*.mod)|*.mod" ) ); +const wxString EagleFootprintLibPathWildcard( _( "Eagle ver. 6.x XML library files (*.lbr)|*.lbr" ) ); +const wxString GedaPcbFootprintLibFileWildcard( _( "Geda PCB footprint library file (*.fp)|*.fp" ) ); +const wxString MacrosFileWildcard( _( "KiCad recorded macros (*.mcr)|*.mcr" ) ); +const wxString ComponentFileExtensionWildcard( _( "Component-footprint link file (*.cmp)|*cmp" ) ); +const wxString PageLayoutDescrFileWildcard( _( "Page layout descr file (*.kicad_wks)|*kicad_wks" ) ); +// generic: +const wxString AllFilesWildcard( _( "All files (*)|*" ) ); + +// Wildcard for cvpcb component to footprint link file +const wxString ComponentFileWildcard( _( "KiCad cmp/footprint link files (*.cmp)|*.cmp" ) ); + +// Wildcard for reports and fabrication documents +const wxString DrillFileWildcard( _( "Drill files (*.drl)|*.drl;*.DRL" ) ); +const wxString SVGFileWildcard( _( "SVG files (*.svg)|*.svg;*.SVG" ) ); +const wxString HtmlFileWildcard( _( "HTML files (*.html)|*.htm;*.html" ) ); +const wxString PdfFileWildcard( _( "Portable document format files (*.pdf)|*.pdf" ) ); +const wxString PSFileWildcard( _( "PostScript files (.ps)|*.ps" ) ); +const wxString ReportFileWildcard = _( "Report files (*.rpt)|*.rpt" ); +const wxString FootprintPlaceFileWildcard = _( "Footprint place files (*.pos)|*.pos" ); +const wxString Shapes3DFileWildcard( _( "Vrml and x3d files (*.wrl *.x3d)|*.wrl;*.x3d" ) ); +const wxString IDF3DFileWildcard( _( "IDFv3 component files (*.idf)|*.idf" ) ); +const wxString TextWildcard( _( "Text files (*.txt)|*.txt" ) ); diff --git a/common/worksheet.cpp b/common/worksheet.cpp new file mode 100644 index 0000000..36cc119 --- /dev/null +++ b/common/worksheet.cpp @@ -0,0 +1,280 @@ +/** + * @file worksheet.cpp + * @brief Common code to draw the title block and frame references + * @note it should include title_block_shape_gost.h or title_block_shape.h + * which defines most of draw shapes, and contains a part of the draw code + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 1992-2013 KiCad Developers, see change_log.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 <fctsys.h> +#include <pgm_base.h> +#include <gr_basic.h> +#include <common.h> +#include <class_drawpanel.h> +#include <class_base_screen.h> +#include <drawtxt.h> +#include <draw_frame.h> +#include <worksheet.h> +#include <class_title_block.h> +#include <build_version.h> + +#include <worksheet_shape_builder.h> + +static const wxString productName = wxT( "KiCad E.D.A. " ); + +void DrawPageLayout( wxDC* aDC, EDA_RECT* aClipBox, + const PAGE_INFO& aPageInfo, + const wxString &aFullSheetName, + const wxString& aFileName, + TITLE_BLOCK& aTitleBlock, + int aSheetCount, int aSheetNumber, + int aPenWidth, double aScalar, + EDA_COLOR_T aColor, EDA_COLOR_T aAltColor ) +{ + WS_DRAW_ITEM_LIST drawList; + + drawList.SetPenSize( aPenWidth ); + drawList.SetMilsToIUfactor( aScalar ); + drawList.SetSheetNumber( aSheetNumber ); + drawList.SetSheetCount( aSheetCount ); + drawList.SetFileName( aFileName ); + drawList.SetSheetName( aFullSheetName ); + + drawList.BuildWorkSheetGraphicList( aPageInfo, + aTitleBlock, aColor, aAltColor ); + + // Draw item list + drawList.Draw( aClipBox, aDC ); +} + + +void EDA_DRAW_FRAME::DrawWorkSheet( wxDC* aDC, BASE_SCREEN* aScreen, int aLineWidth, + double aScalar, const wxString &aFilename ) +{ + if( !m_showBorderAndTitleBlock ) + return; + + const PAGE_INFO& pageInfo = GetPageSettings(); + wxSize pageSize = pageInfo.GetSizeMils(); + + // if not printing, draw the page limits: + if( !aScreen->m_IsPrinting && m_showPageLimits ) + { + GRSetDrawMode( aDC, GR_COPY ); + GRRect( m_canvas->GetClipBox(), aDC, 0, 0, + pageSize.x * aScalar, pageSize.y * aScalar, aLineWidth, + m_drawBgColor == WHITE ? LIGHTGRAY : DARKDARKGRAY ); + } + + TITLE_BLOCK t_block = GetTitleBlock(); + EDA_COLOR_T color = RED; + + wxPoint origin = aDC->GetDeviceOrigin(); + + if( aScreen->m_IsPrinting && origin.y > 0 ) + { + aDC->SetDeviceOrigin( 0, 0 ); + aDC->SetAxisOrientation( true, false ); + } + + DrawPageLayout( aDC, m_canvas->GetClipBox(), pageInfo, + GetScreenDesc(), aFilename, t_block, + aScreen->m_NumberOfScreens, aScreen->m_ScreenNumber, + aLineWidth, aScalar, color, color ); + + if( aScreen->m_IsPrinting && origin.y > 0 ) + { + aDC->SetDeviceOrigin( origin.x, origin.y ); + aDC->SetAxisOrientation( true, true ); + } +} + + +wxString EDA_DRAW_FRAME::GetScreenDesc() const +{ + // Virtual function. In basic class, returns + // an empty string. + return wxEmptyString; +} + +// returns the full text corresponding to the aTextbase, +// after replacing format symbols by the corresponding value +wxString WS_DRAW_ITEM_LIST::BuildFullText( const wxString& aTextbase ) +{ + wxString msg; + + /* Known formats + * %% = replaced by % + * %K = Kicad version + * %Z = paper format name (A4, USLetter) + * %Y = company name + * %D = date + * %R = revision + * %S = sheet number + * %N = number of sheets + * %Cx = comment (x = 0 to 9 to identify the comment) + * %F = filename + * %P = sheet path (sheet full name) + * %T = title + */ + + for( unsigned ii = 0; ii < aTextbase.Len(); ii++ ) + { + if( aTextbase[ii] != '%' ) + { + msg << aTextbase[ii]; + continue; + } + + if( ++ii >= aTextbase.Len() ) + break; + + wxChar format = aTextbase[ii]; + switch( format ) + { + case '%': + msg += '%'; + break; + + case 'D': + msg += m_titleBlock->GetDate(); + break; + + case 'R': + msg += m_titleBlock->GetRevision(); + break; + + case 'K': + msg += productName + Pgm().App().GetAppName(); + msg += wxT( " " ) + GetBuildVersion(); + break; + + case 'Z': + msg += *m_paperFormat; + break; + + case 'S': + msg << m_sheetNumber; + break; + + case 'N': + msg << m_sheetCount; + break; + + case 'F': + { + wxFileName fn( m_fileName ); + msg += fn.GetFullName(); + } + break; + + case 'P': + msg += *m_sheetFullName; + break; + + case 'Y': + msg = m_titleBlock->GetCompany(); + break; + + case 'T': + msg += m_titleBlock->GetTitle(); + break; + + case 'C': + format = aTextbase[++ii]; + switch( format ) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + msg += m_titleBlock->GetComment( format - '0'); + break; + + default: + break; + } + + default: + break; + } + } + + return msg; +} + + +void TITLE_BLOCK::Format( OUTPUTFORMATTER* aFormatter, int aNestLevel, int aControlBits ) const + throw( IO_ERROR ) +{ + // Don't write the title block information if there is nothing to write. + bool isempty = true; + for( unsigned idx = 0; idx < m_tbTexts.GetCount(); idx++ ) + { + if( ! m_tbTexts[idx].IsEmpty() ) + { + isempty = false; + break; + } + } + + if( !isempty ) + { + aFormatter->Print( aNestLevel, "(title_block\n" ); + + if( !GetTitle().IsEmpty() ) + aFormatter->Print( aNestLevel+1, "(title %s)\n", + aFormatter->Quotew( GetTitle() ).c_str() ); + + if( !GetDate().IsEmpty() ) + aFormatter->Print( aNestLevel+1, "(date %s)\n", + aFormatter->Quotew( GetDate() ).c_str() ); + + if( !GetRevision().IsEmpty() ) + aFormatter->Print( aNestLevel+1, "(rev %s)\n", + aFormatter->Quotew( GetRevision() ).c_str() ); + + if( !GetCompany().IsEmpty() ) + aFormatter->Print( aNestLevel+1, "(company %s)\n", + aFormatter->Quotew( GetCompany() ).c_str() ); + + for( int ii = 0; ii < 4; ii++ ) + { + if( !GetComment(ii).IsEmpty() ) + aFormatter->Print( aNestLevel+1, "(comment %d %s)\n", ii+1, + aFormatter->Quotew( GetComment(ii) ).c_str() ); + } + + aFormatter->Print( aNestLevel, ")\n\n" ); + } +} diff --git a/common/worksheet_viewitem.cpp b/common/worksheet_viewitem.cpp new file mode 100644 index 0000000..cf02991 --- /dev/null +++ b/common/worksheet_viewitem.cpp @@ -0,0 +1,210 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 + */ + +/** + * @file worksheet_viewitem.cpp + * @brief Class that handles properties and drawing of worksheet layout. + */ + +#include <worksheet_viewitem.h> +#include <worksheet_shape_builder.h> +#include <gal/graphics_abstraction_layer.h> +#include <painter.h> +#include <layers_id_colors_and_visibility.h> +#include <boost/foreach.hpp> +#include <class_page_info.h> + +using namespace KIGFX; + +WORKSHEET_VIEWITEM::WORKSHEET_VIEWITEM( const PAGE_INFO* aPageInfo, const TITLE_BLOCK* aTitleBlock ) : + EDA_ITEM( NOT_USED ), // this item is never added to a BOARD so it needs no type + m_titleBlock( aTitleBlock ), m_pageInfo( aPageInfo ), m_sheetNumber( 1 ), m_sheetCount( 1 ) {} + + +void WORKSHEET_VIEWITEM::SetPageInfo( const PAGE_INFO* aPageInfo ) +{ + m_pageInfo = aPageInfo; + ViewUpdate( GEOMETRY ); +} + + +void WORKSHEET_VIEWITEM::SetTitleBlock( const TITLE_BLOCK* aTitleBlock ) +{ + m_titleBlock = aTitleBlock; + ViewUpdate( GEOMETRY ); +} + + +const BOX2I WORKSHEET_VIEWITEM::ViewBBox() const +{ + BOX2I bbox; + + if( m_pageInfo != NULL ) + { + bbox.SetOrigin( VECTOR2I( 0, 0 ) ); + bbox.SetEnd( VECTOR2I( m_pageInfo->GetWidthMils() * 25400, + m_pageInfo->GetHeightMils() * 25400 ) ); + } + else + { + bbox.SetMaximum(); + } + + return bbox; +} + + +void WORKSHEET_VIEWITEM::ViewDraw( int aLayer, GAL* aGal ) const +{ + RENDER_SETTINGS* settings = m_view->GetPainter()->GetSettings(); + wxString fileName( m_fileName.c_str(), wxConvUTF8 ); + wxString sheetName( m_sheetName.c_str(), wxConvUTF8 ); + WS_DRAW_ITEM_LIST drawList; + + drawList.SetPenSize( settings->GetWorksheetLineWidth() ); + // Sorry, but I don't get this multi #ifdef from include/convert_to_biu.h, so here goes a magic + // number. IU_PER_MILS should be 25400 (as in a different compilation unit), but somehow + // it equals 1 in this case.. + drawList.SetMilsToIUfactor( 25400 /* IU_PER_MILS */ ); + drawList.SetSheetNumber( m_sheetNumber ); + drawList.SetSheetCount( m_sheetCount ); + drawList.SetFileName( fileName ); + drawList.SetSheetName( sheetName ); + + COLOR4D color = settings->GetColor( this, aLayer ); + EDA_COLOR_T edaColor = ColorFindNearest( color.r * 255, color.g * 255, color.b * 255 ); + drawList.BuildWorkSheetGraphicList( *m_pageInfo, *m_titleBlock, edaColor, edaColor ); + + // Draw all the components that make the page layout + WS_DRAW_ITEM_BASE* item = drawList.GetFirst(); + while( item ) + { + switch( item->GetType() ) + { + case WS_DRAW_ITEM_BASE::wsg_line: + draw( static_cast<const WS_DRAW_ITEM_LINE*>( item ), aGal ); + break; + + case WS_DRAW_ITEM_BASE::wsg_rect: + draw( static_cast<const WS_DRAW_ITEM_RECT*>( item ), aGal ); + break; + + case WS_DRAW_ITEM_BASE::wsg_poly: + draw( static_cast<const WS_DRAW_ITEM_POLYGON*>( item ), aGal ); + break; + + case WS_DRAW_ITEM_BASE::wsg_text: + draw( static_cast<const WS_DRAW_ITEM_TEXT*>( item ), aGal ); + break; + + case WS_DRAW_ITEM_BASE::wsg_bitmap: + break; + } + + item = drawList.GetNext(); + } + + // Draw gray line that outlines the sheet size + drawBorder( aGal ); +} + + +void WORKSHEET_VIEWITEM::ViewGetLayers( int aLayers[], int& aCount ) const +{ + aCount = 1; + aLayers[0] = ITEM_GAL_LAYER( WORKSHEET ); +} + + +void WORKSHEET_VIEWITEM::draw( const WS_DRAW_ITEM_LINE* aItem, GAL* aGal ) const +{ + aGal->SetIsStroke( true ); + aGal->SetIsFill( false ); + aGal->SetStrokeColor( COLOR4D( aItem->GetColor() ) ); + aGal->SetLineWidth( aItem->GetPenWidth() ); + aGal->DrawLine( VECTOR2D( aItem->GetStart() ), VECTOR2D( aItem->GetEnd() ) ); +} + + +void WORKSHEET_VIEWITEM::draw( const WS_DRAW_ITEM_RECT* aItem, GAL* aGal ) const +{ + aGal->SetIsStroke( true ); + aGal->SetIsFill( false ); + aGal->SetStrokeColor( COLOR4D( aItem->GetColor() ) ); + aGal->SetLineWidth( aItem->GetPenWidth() ); + aGal->DrawRectangle( VECTOR2D( aItem->GetStart() ), VECTOR2D( aItem->GetEnd() ) ); +} + + +void WORKSHEET_VIEWITEM::draw( const WS_DRAW_ITEM_POLYGON* aItem, GAL* aGal ) const +{ + std::deque<VECTOR2D> corners; + BOOST_FOREACH( wxPoint point, aItem->m_Corners ) + { + corners.push_back( VECTOR2D( point ) ); + } + + if( aItem->IsFilled() ) + { + aGal->SetFillColor( COLOR4D( aItem->GetColor() ) ); + aGal->SetIsFill( true ); + aGal->SetIsStroke( false ); + aGal->DrawPolygon( corners ); + } + else + { + aGal->SetStrokeColor( COLOR4D( aItem->GetColor() ) ); + aGal->SetIsFill( false ); + aGal->SetIsStroke( true ); + aGal->SetLineWidth( aItem->GetPenWidth() ); + aGal->DrawPolyline( corners ); + } +} + + +void WORKSHEET_VIEWITEM::draw( const WS_DRAW_ITEM_TEXT* aItem, GAL* aGal ) const +{ + VECTOR2D position( aItem->GetTextPosition().x, aItem->GetTextPosition().y ); + + aGal->Save(); + aGal->Translate( position ); + aGal->Rotate( -aItem->GetOrientation() * M_PI / 1800.0 ); + aGal->SetStrokeColor( COLOR4D( aItem->GetColor() ) ); + aGal->SetLineWidth( aItem->GetThickness() ); + aGal->SetTextAttributes( aItem ); + aGal->StrokeText( aItem->GetShownText(), VECTOR2D( 0, 0 ), 0.0 ); + aGal->Restore(); +} + + +void WORKSHEET_VIEWITEM::drawBorder( GAL* aGal ) const +{ + VECTOR2D origin = VECTOR2D( 0.0, 0.0 ); + VECTOR2D end = VECTOR2D( m_pageInfo->GetWidthMils() * 25400, + m_pageInfo->GetHeightMils() * 25400 ); + + aGal->SetIsStroke( true ); + aGal->SetIsFill( false ); + aGal->DrawRectangle( origin, end ); +} diff --git a/common/wx_status_popup.cpp b/common/wx_status_popup.cpp new file mode 100644 index 0000000..9679b3e --- /dev/null +++ b/common/wx_status_popup.cpp @@ -0,0 +1,65 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014-2015 CERN + * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * + * 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 + */ + +/** + * Transient mouse following popup window implementation. + */ + +#include <wx_status_popup.h> +#include <wxPcbStruct.h> + +WX_STATUS_POPUP::WX_STATUS_POPUP( PCB_EDIT_FRAME* aParent ) : + wxPopupWindow( aParent ) +{ + m_panel = new wxPanel( this, wxID_ANY ); + m_panel->SetBackgroundColour( *wxLIGHT_GREY ); + + m_topSizer = new wxBoxSizer( wxVERTICAL ); + m_panel->SetSizer( m_topSizer ); +} + + +void WX_STATUS_POPUP::updateSize() +{ + m_topSizer->Fit( m_panel ); + SetClientSize( m_panel->GetSize() ); +} + + +WX_STATUS_POPUP::~WX_STATUS_POPUP() +{ +} + + +void WX_STATUS_POPUP::Popup( wxWindow* ) +{ + Show( true ); + Raise(); +} + + +void WX_STATUS_POPUP::Move( const wxPoint& aWhere ) +{ + SetPosition ( aWhere ); +} diff --git a/common/wx_unit_binder.cpp b/common/wx_unit_binder.cpp new file mode 100644 index 0000000..6a9236b --- /dev/null +++ b/common/wx_unit_binder.cpp @@ -0,0 +1,86 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014-2015 CERN + * Author: Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 <wx/stattext.h> +#include <wx/sizer.h> +#include <wx/textctrl.h> +#include <limits> +#include <base_units.h> +#include <wx/valnum.h> + +#include <boost/optional.hpp> + +#include "wx_unit_binder.h" + +WX_UNIT_BINDER::WX_UNIT_BINDER( wxWindow* aParent, wxTextCtrl* aTextInput, + wxStaticText* aUnitLabel, wxSpinButton* aSpinButton ) : + m_textCtrl( aTextInput ), + m_unitLabel( aUnitLabel ), + m_units( g_UserUnit ), + m_step( 1 ), + m_min( 0 ), + m_max( 1 ) +{ + // Use the currently selected units + m_textCtrl->SetValue( wxT( "0" ) ); + m_unitLabel->SetLabel( GetAbbreviatedUnitsLabel( m_units ) ); +} + + +WX_UNIT_BINDER::~WX_UNIT_BINDER() +{ +} + + +void WX_UNIT_BINDER::SetValue( int aValue ) +{ + wxString s = StringFromValue( m_units, aValue, false ); + + m_textCtrl->SetValue( s ); + + m_unitLabel->SetLabel( GetAbbreviatedUnitsLabel( m_units ) ); +} + + +int WX_UNIT_BINDER::GetValue() const +{ + wxString s = m_textCtrl->GetValue(); + + return ValueFromString( m_units, s ); +} + + +bool WX_UNIT_BINDER::Valid() const +{ + double dummy; + + return m_textCtrl->GetValue().ToDouble( &dummy ); +} + + +void WX_UNIT_BINDER::Enable( bool aEnable ) +{ + m_textCtrl->Enable( aEnable ); + m_unitLabel->Enable( aEnable ); +} diff --git a/common/wxunittext.cpp b/common/wxunittext.cpp new file mode 100644 index 0000000..16f649a --- /dev/null +++ b/common/wxunittext.cpp @@ -0,0 +1,195 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 CERN + * Author: Maciej Suminski <maciej.suminski@cern.ch> + * + * 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 "wxunittext.h" +#include <wx/stattext.h> +#include <wx/sizer.h> +#include <wx/textctrl.h> +#include <limits> +#include <base_units.h> +#include <wx/valnum.h> +#include <boost/optional.hpp> + +WX_UNIT_TEXT::WX_UNIT_TEXT( wxWindow* aParent, const wxString& aLabel, double aValue, double aStep ) : + wxPanel( aParent, wxID_ANY ), + m_step( aStep ) +{ + // Use the currently selected units + m_units = g_UserUnit; + + wxBoxSizer* sizer; + sizer = new wxBoxSizer( wxHORIZONTAL ); + + // Helper label + m_inputLabel = new wxStaticText( this, wxID_ANY, aLabel, + wxDefaultPosition, wxDefaultSize, 0 ); + wxSize size = m_inputLabel->GetMinSize(); + size.SetWidth( 150 ); + m_inputLabel->SetMinSize( size ); + sizer->Add( m_inputLabel, 1, wxALIGN_CENTER_VERTICAL | wxALL | wxEXPAND, 5 ); + + // Main input control + m_inputValue = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, + wxTE_PROCESS_ENTER ); + + SetValue( aValue ); + sizer->Add( m_inputValue, 0, wxALIGN_CENTER_VERTICAL | wxALL ); + + wxFloatingPointValidator<double> validator( 4, NULL, wxNUM_VAL_NO_TRAILING_ZEROES ); + validator.SetRange( 0.0, std::numeric_limits<double>::max() ); + m_inputValue->SetValidator( validator ); + + // Spin buttons for modifying values using the mouse + m_spinButton = new wxSpinButton( this, wxID_ANY ); + m_spinButton->SetRange( std::numeric_limits<int>::min(), std::numeric_limits<int>::max() ); + + m_spinButton->SetCanFocus( false ); + sizer->Add( m_spinButton, 0, wxALIGN_CENTER_VERTICAL | wxALL ); + + Connect( wxEVT_SPIN_UP, wxSpinEventHandler( WX_UNIT_TEXT::onSpinUpEvent ), NULL, this ); + Connect( wxEVT_SPIN_DOWN, wxSpinEventHandler( WX_UNIT_TEXT::onSpinDownEvent ), NULL, this ); + + sizer->AddSpacer( 5 ); + + // Create units label + m_unitLabel = new wxStaticText( this, wxID_ANY, GetUnitsLabel( g_UserUnit ), + wxDefaultPosition, wxDefaultSize, 0 ); + sizer->Add( m_unitLabel, 0, wxALIGN_CENTER_VERTICAL | wxALL ); + + SetSizer( sizer ); + Layout(); +} + + +WX_UNIT_TEXT::~WX_UNIT_TEXT() +{ +} + + +void WX_UNIT_TEXT::SetUnits( EDA_UNITS_T aUnits, bool aConvert ) +{ + assert( !aConvert ); // TODO conversion does not work yet + + m_unitLabel->SetLabel( GetUnitsLabel( g_UserUnit ) ); +} + + +void WX_UNIT_TEXT::SetValue( double aValue ) +{ + if( aValue >= 0.0 ) + { + m_inputValue->SetValue( wxString( Double2Str( aValue ).c_str(), wxConvUTF8 ) ); + m_inputValue->MarkDirty(); + } + else + { + m_inputValue->SetValue( DEFAULT_VALUE ); + } +} + + +/*boost::optional<double> WX_UNIT_TEXT::GetValue( EDA_UNITS_T aUnit ) const +{ + if( aUnit == m_units ) + return GetValue(); // no conversion needed + + switch( m_units ) + { + case MILLIMETRES: + switch( aUnit ) + { + case INCHES: + iu = Mils2iu( GetValue() * 1000.0 ); + break; + + case UNSCALED_UNITS: + iu = GetValue(); + break; + } + break; + + case INCHES: + switch( aUnit ) + { + case MILLIMETRES: + return Mils2mm( GetValue() * 1000.0 ); + break; + + case UNSCALED_UNITS: + return Mils2iu( GetValue() * 1000.0 ); + break; + } + break; + + case UNSCALED_UNITS: + switch( aUnit ) + { + case MILLIMETRES: + return Iu2Mils( GetValue() ) / 1000.0; + break; + +// case INCHES: +// return +// break; + } + break; + } + + assert( false ); // seems that there are some conversions missing + + return 0.0; +}*/ + + +boost::optional<double> WX_UNIT_TEXT::GetValue() const +{ + wxString text = m_inputValue->GetValue(); + double value; + + if( text == DEFAULT_VALUE ) + return boost::optional<double>( -1.0 ); + + if( !text.ToDouble( &value ) ) + return boost::optional<double>(); + + return boost::optional<double>( value ); +} + + +void WX_UNIT_TEXT::onSpinUpEvent( wxSpinEvent& aEvent ) +{ + SetValue( *GetValue() + m_step ); +} + + +void WX_UNIT_TEXT::onSpinDownEvent( wxSpinEvent& aEvent ) +{ + double newValue = *GetValue() - m_step; + + if( newValue >= 0.0 ) + SetValue( newValue ); +} + + +const wxString WX_UNIT_TEXT::DEFAULT_VALUE = _( "default "); diff --git a/common/wxwineda.cpp b/common/wxwineda.cpp new file mode 100644 index 0000000..8ff4c2c --- /dev/null +++ b/common/wxwineda.cpp @@ -0,0 +1,324 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com + * Copyright (C) 1992-2011 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 + */ + +/** + * @file wxwineda.cpp + */ + +#include <fctsys.h> +#include <wxstruct.h> +#include <dialog_helpers.h> +#include <base_units.h> +#include <macros.h> + + +/*******************************************************/ +/* Class to edit a graphic + text size in INCHES or MM */ +/*******************************************************/ +EDA_GRAPHIC_TEXT_CTRL::EDA_GRAPHIC_TEXT_CTRL( wxWindow* parent, + const wxString& Title, + const wxString& TextToEdit, + int textsize, + EDA_UNITS_T user_unit, + wxBoxSizer* BoxSizer, + int framelen ) +{ + m_UserUnit = user_unit; + m_Title = NULL; + + m_Title = new wxStaticText( parent, -1, Title ); + + BoxSizer->Add( m_Title, 0, wxGROW | wxLEFT | wxRIGHT | wxTOP, 5 ); + + m_FrameText = new wxTextCtrl( parent, -1, TextToEdit ); + + BoxSizer->Add( m_FrameText, 0, wxGROW | wxLEFT | wxRIGHT | wxBOTTOM, 5 ); + + if( !Title.IsEmpty() ) + { + wxString msg; + msg.Printf( _( "Size%s" ), GetChars( ReturnUnitSymbol( m_UserUnit ) ) ); + wxStaticText* text = new wxStaticText( parent, -1, msg ); + + BoxSizer->Add( text, 0, wxGROW | wxLEFT | wxRIGHT, 5 ); + } + + wxString value = FormatSize( m_UserUnit, textsize ); + + m_FrameSize = new wxTextCtrl( parent, -1, value, wxDefaultPosition, wxSize( 70, -1 ) ); + + BoxSizer->Add( m_FrameSize, 0, wxGROW | wxLEFT | wxRIGHT | wxBOTTOM, 5 ); +} + + +EDA_GRAPHIC_TEXT_CTRL::~EDA_GRAPHIC_TEXT_CTRL() +{ + /* no, these are deleted by the BoxSizer + delete m_FrameText; + delete m_Title; + */ +} + + +wxString EDA_GRAPHIC_TEXT_CTRL::FormatSize( EDA_UNITS_T aUnit, int textSize ) +{ + // Limiting the size of the text of reasonable values. + if( textSize < 10 ) + textSize = 10; + + if( textSize > 3000 ) + textSize = 3000; + + return StringFromValue( aUnit, textSize ); +} + + +void EDA_GRAPHIC_TEXT_CTRL::SetTitle( const wxString& title ) +{ + m_Title->SetLabel( title ); +} + + +void EDA_GRAPHIC_TEXT_CTRL::SetValue( const wxString& value ) +{ + m_FrameText->SetValue( value ); +} + + +void EDA_GRAPHIC_TEXT_CTRL::SetValue( int textSize ) +{ + wxString value = FormatSize( m_UserUnit, textSize ); + m_FrameSize->SetValue( value ); +} + + +const wxString EDA_GRAPHIC_TEXT_CTRL::GetText() const +{ + wxString text = m_FrameText->GetValue(); + return text; +} + + +int EDA_GRAPHIC_TEXT_CTRL::ParseSize( const wxString& sizeText, EDA_UNITS_T aUnit ) +{ + int textsize; + + textsize = ValueFromString( aUnit, sizeText ); + + // Limit to reasonable size + if( textsize < 10 ) + textsize = 10; + + if( textsize > 3000 ) + textsize = 3000; + + return textsize; +} + + +int EDA_GRAPHIC_TEXT_CTRL::GetTextSize() +{ + return ParseSize( m_FrameSize->GetValue(), m_UserUnit ); +} + + +void EDA_GRAPHIC_TEXT_CTRL::Enable( bool state ) +{ + m_FrameText->Enable( state ); +} + + +/********************************************************/ +/* Class to display and edit a coordinated INCHES or MM */ +/********************************************************/ +EDA_POSITION_CTRL::EDA_POSITION_CTRL( wxWindow* parent, + const wxString& title, + const wxPoint& pos_to_edit, + EDA_UNITS_T user_unit, + wxBoxSizer* BoxSizer ) +{ + wxString text; + + m_UserUnit = user_unit; + + if( title.IsEmpty() ) + text = _( "Pos " ); + else + text = title; + + text += _( "X" ) + ReturnUnitSymbol( m_UserUnit ); + m_TextX = new wxStaticText( parent, -1, text ); + + BoxSizer->Add( m_TextX, 0, wxGROW | wxLEFT | wxRIGHT | wxTOP, 5 ); + m_FramePosX = new wxTextCtrl( parent, -1, wxEmptyString, wxDefaultPosition ); + + BoxSizer->Add( m_FramePosX, 0, wxGROW | wxLEFT | wxRIGHT | wxBOTTOM, 5 ); + + + if( title.IsEmpty() ) + text = _( "Pos " ); + else + text = title; + text += _( "Y" ) + ReturnUnitSymbol( m_UserUnit ); + + m_TextY = new wxStaticText( parent, -1, text ); + + BoxSizer->Add( m_TextY, 0, wxGROW | wxLEFT | wxRIGHT | wxTOP, 5 ); + + m_FramePosY = new wxTextCtrl( parent, -1, wxEmptyString ); + + BoxSizer->Add( m_FramePosY, 0, wxGROW | wxLEFT | wxRIGHT | wxBOTTOM, 5 ); + + SetValue( pos_to_edit.x, pos_to_edit.y ); +} + + +EDA_POSITION_CTRL::~EDA_POSITION_CTRL() +{ + delete m_TextX; + delete m_TextY; + delete m_FramePosX; + delete m_FramePosY; +} + + +/* Returns (in internal units) to coordinate between (in user units) + */ +wxPoint EDA_POSITION_CTRL::GetValue() +{ + wxPoint coord; + + coord.x = ValueFromString( m_UserUnit, m_FramePosX->GetValue() ); + coord.y = ValueFromString( m_UserUnit, m_FramePosY->GetValue() ); + + return coord; +} + + +void EDA_POSITION_CTRL::Enable( bool x_win_on, bool y_win_on ) +{ + m_FramePosX->Enable( x_win_on ); + m_FramePosY->Enable( y_win_on ); +} + + +void EDA_POSITION_CTRL::SetValue( int x_value, int y_value ) +{ + wxString msg; + + m_Pos_To_Edit.x = x_value; + m_Pos_To_Edit.y = y_value; + + msg = StringFromValue( m_UserUnit, m_Pos_To_Edit.x ); + m_FramePosX->Clear(); + m_FramePosX->SetValue( msg ); + + msg = StringFromValue( m_UserUnit, m_Pos_To_Edit.y ); + m_FramePosY->Clear(); + m_FramePosY->SetValue( msg ); +} + + +/*******************/ +/* EDA_SIZE_CTRL */ +/*******************/ +EDA_SIZE_CTRL::EDA_SIZE_CTRL( wxWindow* parent, const wxString& title, + const wxSize& size_to_edit, EDA_UNITS_T aUnit, + wxBoxSizer* aBoxSizer ) : + EDA_POSITION_CTRL( parent, title, wxPoint( size_to_edit.x, size_to_edit.y ), + aUnit, aBoxSizer ) +{ +} + + +wxSize EDA_SIZE_CTRL::GetValue() +{ + wxPoint pos = EDA_POSITION_CTRL::GetValue(); + wxSize size; + + size.x = pos.x; + size.y = pos.y; + return size; +} + + +/**************************************************************/ +/* Class to display and edit a dimension INCHES, MM, or other */ +/**************************************************************/ +EDA_VALUE_CTRL::EDA_VALUE_CTRL( wxWindow* parent, const wxString& title, + int value, EDA_UNITS_T user_unit, wxBoxSizer* BoxSizer ) +{ + wxString label = title; + + m_UserUnit = user_unit; + m_Value = value; + label += ReturnUnitSymbol( m_UserUnit ); + + m_Text = new wxStaticText( parent, -1, label ); + + BoxSizer->Add( m_Text, 0, wxGROW | wxLEFT | wxRIGHT | wxTOP, 5 ); + + wxString stringvalue = StringFromValue( m_UserUnit, m_Value ); + m_ValueCtrl = new wxTextCtrl( parent, -1, stringvalue ); + + BoxSizer->Add( m_ValueCtrl, + 0, + wxGROW | wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT | wxBOTTOM, + 5 ); +} + + +EDA_VALUE_CTRL::~EDA_VALUE_CTRL() +{ + delete m_ValueCtrl; + delete m_Text; +} + + +int EDA_VALUE_CTRL::GetValue() +{ + int coord; + wxString txtvalue = m_ValueCtrl->GetValue(); + + coord = ValueFromString( m_UserUnit, txtvalue ); + return coord; +} + + +void EDA_VALUE_CTRL::SetValue( int new_value ) +{ + wxString buffer; + + m_Value = new_value; + + buffer = StringFromValue( m_UserUnit, m_Value ); + m_ValueCtrl->SetValue( buffer ); +} + + +void EDA_VALUE_CTRL::Enable( bool enbl ) +{ + m_ValueCtrl->Enable( enbl ); + m_Text->Enable( enbl ); +} diff --git a/common/xnode.cpp b/common/xnode.cpp new file mode 100644 index 0000000..7419f23 --- /dev/null +++ b/common/xnode.cpp @@ -0,0 +1,93 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2010 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 1992-2010 KiCad Developers, see change_log.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 <xnode.h> +#include <macros.h> + +typedef wxXmlAttribute XATTR; + + +void XNODE::Format( OUTPUTFORMATTER* out, int nestLevel ) throw( IO_ERROR ) +{ + switch( GetType() ) + { + case wxXML_ELEMENT_NODE: + out->Print( nestLevel, "(%s", out->Quotew( GetName() ).c_str() ); + FormatContents( out, nestLevel ); + if( GetNext() ) + out->Print( 0, ")\n" ); + else + out->Print( 0, ")" ); + break; + + default: + FormatContents( out, nestLevel ); + } +} + + +void XNODE::FormatContents( OUTPUTFORMATTER* out, int nestLevel ) throw( IO_ERROR ) +{ + // output attributes first if they exist + for( XATTR* attr = (XATTR*) GetAttributes(); attr; attr = (XATTR*) attr->GetNext() ) + { + out->Print( 0, " (%s %s)", + // attr names should never need quoting, no spaces, we designed the file. + out->Quotew( attr->GetName() ).c_str(), + out->Quotew( attr->GetValue() ).c_str() + ); + } + + // we only expect to have used one of two types here: + switch( GetType() ) + { + case wxXML_ELEMENT_NODE: + + // output children if they exist. + for( XNODE* kid = (XNODE*) GetChildren(); kid; kid = (XNODE*) kid->GetNext() ) + { + if( kid->GetType() != wxXML_TEXT_NODE ) + { + if( kid == GetChildren() ) + out->Print( 0, "\n" ); + kid->Format( out, nestLevel+1 ); + } + else + { + kid->Format( out, 0 ); + } + } + break; + + case wxXML_TEXT_NODE: + out->Print( 0, " %s", out->Quotew( GetContent() ).c_str() ); + break; + + default: + ; // not supported + } +} + +// EOF diff --git a/common/zoom.cpp b/common/zoom.cpp new file mode 100644 index 0000000..5107ab8 --- /dev/null +++ b/common/zoom.cpp @@ -0,0 +1,301 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com + * Copyright (C) 1992-2011 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 + */ + +/** + * @file zoom.cpp + */ + +/* + * Manage zoom, grid step, and auto crop. + */ + +#include <fctsys.h> +#include <id.h> +#include <class_drawpanel.h> +#include <view/view.h> +#include <class_base_screen.h> +#include <draw_frame.h> +#include <kicad_device_context.h> +#include <hotkeys_basic.h> +#include <menus_helpers.h> +#include <base_units.h> +#include <tool/tool_manager.h> + + +void EDA_DRAW_FRAME::RedrawScreen( const wxPoint& aCenterPoint, bool aWarpPointer ) +{ + if( IsGalCanvasActive() ) + return; + + AdjustScrollBars( aCenterPoint ); + + // Move the mouse cursor to the on grid graphic cursor position + if( aWarpPointer ) + m_canvas->MoveCursorToCrossHair(); + + m_canvas->Refresh(); + m_canvas->Update(); +} + +void EDA_DRAW_FRAME::RedrawScreen2( const wxPoint& posBefore ) +{ + if( IsGalCanvasActive() ) + return; + + wxPoint dPos = posBefore - m_canvas->GetClientSize() / 2; // relative screen position to center before zoom + wxPoint newScreenPos = m_canvas->ToDeviceXY( GetCrossHairPosition() ); // screen position of crosshair after zoom + wxPoint newCenter = m_canvas->ToLogicalXY( newScreenPos - dPos ); + + AdjustScrollBars( newCenter ); + + m_canvas->Refresh(); + m_canvas->Update(); +} + + +void EDA_DRAW_FRAME::Zoom_Automatique( bool aWarpPointer ) +{ + BASE_SCREEN* screen = GetScreen(); + + // Set the best zoom and get center point. + + // BestZoom() can compute an illegal zoom if the client window size + // is small, say because frame is not maximized. So use the clamping form + // of SetZoom(): + double bestzoom = BestZoom(); + screen->SetScalingFactor( bestzoom ); + + if( screen->m_FirstRedraw ) + SetCrossHairPosition( GetScrollCenterPosition() ); + + if( !IsGalCanvasActive() ) + RedrawScreen( GetScrollCenterPosition(), aWarpPointer ); + else + m_toolManager->RunAction( "common.Control.zoomFitScreen", true ); +} + + +/** Compute the zoom factor and the new draw offset to draw the + * selected area (Rect) in full window screen + * @param Rect = selected area to show after zooming + */ +void EDA_DRAW_FRAME::Window_Zoom( EDA_RECT& Rect ) +{ + // Compute the best zoom + Rect.Normalize(); + + wxSize size = m_canvas->GetClientSize(); + + // Use ceil to at least show the full rect + double scalex = (double) Rect.GetSize().x / size.x; + double bestscale = (double) Rect.GetSize().y / size.y; + + bestscale = std::max( bestscale, scalex ); + + GetScreen()->SetScalingFactor( bestscale ); + RedrawScreen( Rect.Centre(), true ); +} + + +/** + * Function OnZoom + * Called from any zoom event (toolbar , hotkey or popup ) + */ +void EDA_DRAW_FRAME::OnZoom( wxCommandEvent& event ) +{ + if( m_canvas == NULL ) + return; + + int id = event.GetId(); + bool zoom_at_cursor = false; + BASE_SCREEN* screen = GetScreen(); + wxPoint center = GetScrollCenterPosition(); + + switch( id ) + { + case ID_OFFCENTER_ZOOM_IN: + center = m_canvas->ToDeviceXY( GetCrossHairPosition() ); + if( screen->SetPreviousZoom() ) + RedrawScreen2( center ); + break; + + case ID_POPUP_ZOOM_IN: + zoom_at_cursor = true; + center = GetCrossHairPosition(); + + // fall thru + case ID_VIEWER_ZOOM_IN: + case ID_ZOOM_IN: + if( screen->SetPreviousZoom() ) + RedrawScreen( center, zoom_at_cursor ); + break; + + case ID_OFFCENTER_ZOOM_OUT: + center = m_canvas->ToDeviceXY( GetCrossHairPosition() ); + if( screen->SetNextZoom() ) + RedrawScreen2( center ); + break; + + case ID_POPUP_ZOOM_OUT: + zoom_at_cursor = true; + center = GetCrossHairPosition(); + + // fall thru + case ID_VIEWER_ZOOM_OUT: + case ID_ZOOM_OUT: + if( screen->SetNextZoom() ) + RedrawScreen( center, zoom_at_cursor ); + break; + + case ID_VIEWER_ZOOM_REDRAW: + case ID_POPUP_ZOOM_REDRAW: + case ID_ZOOM_REDRAW: + m_canvas->Refresh(); + break; + + case ID_POPUP_ZOOM_CENTER: + center = GetCrossHairPosition(); + RedrawScreen( center, true ); + break; + + case ID_POPUP_ZOOM_PAGE: + case ID_VIEWER_ZOOM_PAGE: + case ID_ZOOM_PAGE: + Zoom_Automatique( false ); + break; + + case ID_POPUP_ZOOM_SELECT: + break; + + case ID_POPUP_CANCEL: + m_canvas->MoveCursorToCrossHair(); + break; + + default: + SetPresetZoom( id - ID_POPUP_ZOOM_LEVEL_START ); + } + + UpdateStatusBar(); +} + + +void EDA_DRAW_FRAME::SetNextZoom() +{ + GetScreen()->SetNextZoom(); +} + + +void EDA_DRAW_FRAME::SetPrevZoom() +{ + GetScreen()->SetPreviousZoom(); +} + + +void EDA_DRAW_FRAME::SetPresetZoom( int aIndex ) +{ + BASE_SCREEN* screen = GetScreen(); + + if( aIndex >= (int) screen->m_ZoomList.size() ) + { + wxLogDebug( wxT( "%s %d: index %d is outside the bounds of the zoom list." ), + __TFILE__, __LINE__, aIndex ); + return; + } + + if( m_zoomSelectBox ) + m_zoomSelectBox->SetSelection( aIndex ); + + if( screen->SetZoom( screen->m_ZoomList[aIndex] ) ) + RedrawScreen( GetScrollCenterPosition(), true ); + + UpdateStatusBar(); +} + + +/* add the zoom list menu the the MasterMenu. + * used in OnRightClick(wxMouseEvent& event) + */ +void EDA_DRAW_FRAME::AddMenuZoomAndGrid( wxMenu* MasterMenu ) +{ + int maxZoomIds; + double zoom; + wxString msg; + BASE_SCREEN* screen = m_canvas->GetScreen(); + + msg = AddHotkeyName( _( "Center" ), m_hotkeysDescrList, HK_ZOOM_CENTER ); + AddMenuItem( MasterMenu, ID_POPUP_ZOOM_CENTER, msg, KiBitmap( zoom_center_on_screen_xpm ) ); + msg = AddHotkeyName( _( "Zoom in" ), m_hotkeysDescrList, HK_ZOOM_IN ); + AddMenuItem( MasterMenu, ID_POPUP_ZOOM_IN, msg, KiBitmap( zoom_in_xpm ) ); + msg = AddHotkeyName( _( "Zoom out" ), m_hotkeysDescrList, HK_ZOOM_OUT ); + AddMenuItem( MasterMenu, ID_POPUP_ZOOM_OUT, msg, KiBitmap( zoom_out_xpm ) ); + msg = AddHotkeyName( _( "Redraw view" ), m_hotkeysDescrList, HK_ZOOM_REDRAW ); + AddMenuItem( MasterMenu, ID_POPUP_ZOOM_REDRAW, msg, KiBitmap( zoom_redraw_xpm ) ); + msg = AddHotkeyName( _( "Zoom auto" ), m_hotkeysDescrList, HK_ZOOM_AUTO ); + AddMenuItem( MasterMenu, ID_POPUP_ZOOM_PAGE, msg, KiBitmap( zoom_fit_in_page_xpm ) ); + + + wxMenu* zoom_choice = new wxMenu; + AddMenuItem( MasterMenu, zoom_choice, + ID_POPUP_ZOOM_SELECT, _( "Zoom select" ), + KiBitmap( zoom_selection_xpm ) ); + + zoom = screen->GetZoom(); + maxZoomIds = ID_POPUP_ZOOM_LEVEL_END - ID_POPUP_ZOOM_LEVEL_START; + maxZoomIds = ( (size_t) maxZoomIds < screen->m_ZoomList.size() ) ? + maxZoomIds : screen->m_ZoomList.size(); + + // Populate zoom submenu. + for( int i = 0; i < maxZoomIds; i++ ) + { + msg.Printf( wxT( "%.2f" ), m_zoomLevelCoeff / screen->m_ZoomList[i] ); + + zoom_choice->Append( ID_POPUP_ZOOM_LEVEL_START + i, _( "Zoom: " ) + msg, + wxEmptyString, wxITEM_CHECK ); + if( zoom == screen->m_ZoomList[i] ) + zoom_choice->Check( ID_POPUP_ZOOM_LEVEL_START + i, true ); + } + + // Create grid submenu as required. + if( screen->GetGridCount() ) + { + wxMenu* gridMenu = new wxMenu; + AddMenuItem( MasterMenu, gridMenu, ID_POPUP_GRID_SELECT, + _( "Grid Select" ), KiBitmap( grid_select_xpm ) ); + + wxArrayString gridsList; + int icurr = screen->BuildGridsChoiceList( gridsList, g_UserUnit != INCHES ); + + for( unsigned i = 0; i < gridsList.GetCount(); i++ ) + { + GRID_TYPE& grid = screen->GetGrid( i ); + gridMenu->Append( grid.m_CmdId, gridsList[i], wxEmptyString, true ); + + if( (int)i == icurr ) + gridMenu->Check( grid.m_CmdId, true ); + } + } + + MasterMenu->AppendSeparator(); + AddMenuItem( MasterMenu, ID_POPUP_CANCEL, _( "Close" ), KiBitmap( cancel_xpm ) ); +} |