1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 1992-2018 Jean-Pierre Charras <jp.charras at wanadoo.fr>.
5  * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
24  */
25 
26 
27 /*
28  * The WS_DATA_ITEM_* classes define the basic shapes of a drawing sheet (frame references
29  * and title block).  The list of these items is stored in a DS_DATA_MODEL instance.
30  *
31  * These items cannot be drawn or plotted "as is".  They must be converted to WS_DRAW_*
32  * types. When building the draw list:
33  *   - the DS_DATA_MODEL is used to create a DS_DRAW_ITEM_LIST
34  *   - coordinates are converted to draw/plot coordinates.
35  *   - texts are expanded if they contain format symbols.
36  *   - items with m_RepeatCount > 1 are created m_RepeatCount times.
37  *
38  * The DS_DATA_MODEL is created only once.
39  * The WS_DRAW_ITEM_*s are created and maintained by the PlEditor, but are created each time
40  * they're needed for drawing by the clients (Eeschema, Pcbnew, etc.)
41  *
42  * The DS_DATA_MODEL instance is created from a S expression which describes the drawing sheet
43  * (can be the default drawing sheet or a custom file).  This format is also used for undo/redo
44  * storage (wrapped in a DS_PROXY_UNDO_ITEM).
45  */
46 
47 #include <kiface_base.h>
48 #include <title_block.h>
49 #include <common.h>
50 #include <drawing_sheet/ds_data_item.h>
51 #include <drawing_sheet/ds_data_model.h>
52 #include <drawing_sheet/ds_painter.h>
53 
54 
55 // The layout shape used in the application
56 // It is accessible by DS_DATA_MODEL::GetTheInstance()
57 static DS_DATA_MODEL wksTheInstance;
58 static DS_DATA_MODEL* wksAltInstance;
59 
DS_DATA_MODEL()60 DS_DATA_MODEL::DS_DATA_MODEL() :
61         m_WSunits2Iu( 1000.0 ),
62         m_DefaultLineWidth( 0.0 ),
63         m_DefaultTextSize( TB_DEFAULT_TEXTSIZE, TB_DEFAULT_TEXTSIZE ),
64         m_DefaultTextThickness( 0.0 ),
65         m_EditMode( false )
66 {
67     m_allowVoidList = false;
68     m_fileFormatVersionAtLoad = 0;
69     m_leftMargin = 10.0;    // the left page margin in mm
70     m_rightMargin = 10.0;   // the right page margin in mm
71     m_topMargin = 10.0;     // the top page margin in mm
72     m_bottomMargin = 10.0;  // the bottom page margin in mm
73 }
74 
75 /*
76  * static function: returns the instance of DS_DATA_MODEL used in the application
77  */
GetTheInstance()78 DS_DATA_MODEL& DS_DATA_MODEL::GetTheInstance()
79 {
80     if( wksAltInstance )
81         return *wksAltInstance;
82     else
83         return wksTheInstance;
84 }
85 
86 /**
87  * static function: Set an alternate instance of DS_DATA_MODEL
88  * mainly used in page setting dialog
89  * @param aLayout = the alternate drawing sheet.
90  * if null, restore the basic drawing sheet
91  */
SetAltInstance(DS_DATA_MODEL * aLayout)92 void DS_DATA_MODEL::SetAltInstance( DS_DATA_MODEL* aLayout )
93 {
94     wksAltInstance = aLayout;
95 }
96 
97 
SetupDrawEnvironment(const PAGE_INFO & aPageInfo,double aMilsToIU)98 void DS_DATA_MODEL::SetupDrawEnvironment( const PAGE_INFO& aPageInfo, double aMilsToIU )
99 {
100 #define MILS_TO_MM (25.4/1000)
101 
102     m_WSunits2Iu = aMilsToIU / MILS_TO_MM;
103 
104     // Left top corner position
105     DPOINT lt_corner;
106     lt_corner.x = GetLeftMargin();
107     lt_corner.y = GetTopMargin();
108     m_LT_Corner = lt_corner;
109 
110     // Right bottom corner position
111     DPOINT rb_corner;
112     rb_corner.x = ( aPageInfo.GetSizeMils().x * MILS_TO_MM ) - GetRightMargin();
113     rb_corner.y = ( aPageInfo.GetSizeMils().y * MILS_TO_MM ) - GetBottomMargin();
114     m_RB_Corner = rb_corner;
115 }
116 
117 
ClearList()118 void DS_DATA_MODEL::ClearList()
119 {
120     for( DS_DATA_ITEM* item : m_list )
121         delete item;
122 
123     m_list.clear();
124 }
125 
126 
Append(DS_DATA_ITEM * aItem)127 void DS_DATA_MODEL::Append( DS_DATA_ITEM* aItem )
128 {
129     m_list.push_back( aItem );
130 }
131 
132 
Remove(DS_DATA_ITEM * aItem)133 void DS_DATA_MODEL::Remove( DS_DATA_ITEM* aItem )
134 {
135     auto newEnd = std::remove( m_list.begin(), m_list.end(), aItem );
136     m_list.erase( newEnd, m_list.end() );
137 }
138 
139 
GetItemIndex(DS_DATA_ITEM * aItem) const140 int DS_DATA_MODEL::GetItemIndex( DS_DATA_ITEM* aItem ) const
141 {
142     unsigned idx = 0;
143 
144     while( idx < m_list.size() )
145     {
146         if( m_list[idx] == aItem )
147             return (int) idx;
148 
149         idx++;
150     }
151 
152     return -1;
153 }
154 
155 /* return the item from its index aIdx, or NULL if does not exist
156  */
GetItem(unsigned aIdx) const157 DS_DATA_ITEM* DS_DATA_MODEL::GetItem( unsigned aIdx ) const
158 {
159     if( aIdx < m_list.size() )
160         return m_list[aIdx];
161     else
162         return nullptr;
163 }
164 
165 
ResolvePath(const wxString & aPath,const wxString & aProjectPath)166 const wxString DS_DATA_MODEL::ResolvePath( const wxString& aPath, const wxString& aProjectPath )
167 {
168     wxString fullFileName = ExpandEnvVarSubstitutions( aPath, nullptr );
169 
170     if( fullFileName.IsEmpty() )
171         return fullFileName;
172 
173     wxFileName fn = fullFileName;
174 
175     if( fn.IsAbsolute() )
176         return fullFileName;
177 
178     // the path is not absolute: search it in project path, and then in kicad valid paths
179     if( !aProjectPath.IsEmpty() )
180     {
181         fn.MakeAbsolute( aProjectPath );
182 
183         if( wxFileExists( fn.GetFullPath() ) )
184             return fn.GetFullPath();
185     }
186 
187     fn = fullFileName;
188     wxString name = Kiface().KifaceSearch().FindValidPath( fn.GetFullName() );
189 
190     if( !name.IsEmpty() )
191         fullFileName = name;
192 
193     return fullFileName;
194 }
195