From 8110816fd1cea65b93b89d09f4bc27f3aa0ecc1e Mon Sep 17 00:00:00 2001 From: Thiadmer Riemersma Date: Tue, 18 Dec 2018 21:33:22 +0100 Subject: [PATCH] Version 1.4. Support the "rounded rectangle" pad shape (introduced with KiCad 5). Support SMD pads as reverse side (edge connectors). Allow for path recursion in collecting libraries. Automatic adjustment of zoom when switching from one footprint/symbol to the next, to always show a component at a reasonable size. Bug fix in conversion of layer set between s-expr and legacy. --- doc/LibraryFileFormats.pdf | Bin 131520 -> 129612 bytes doc/kicadlibrarian.pdf | Bin 424281 -> 429384 bytes src/cxffont.cpp | 8 +- src/libmngr_dlgnewfootprint.cpp | 300 +- src/libmngr_dlgnewfootprint.h | 122 +- src/libmngr_dlgnewsymbol.cpp | 234 +- src/libmngr_dlgnewsymbol.h | 124 +- src/libmngr_dlgoptions.cpp | 262 +- src/libmngr_dlgoptions.h | 88 +- src/libmngr_dlgremotelink.cpp | 386 +-- src/libmngr_dlgremotelink.h | 126 +- src/libmngr_dlgreport.cpp | 184 +- src/libmngr_dlgreport.h | 90 +- src/libmngr_dlgtemplate.cpp | 206 +- src/libmngr_dlgtemplate.h | 92 +- src/libmngr_frame.cpp | 930 ++++--- src/libmngr_frame.h | 19 +- src/libmngr_gui_base.cpp | 1180 ++++---- src/libmngr_gui_base.fbp | 4602 +++---------------------------- src/libmngr_gui_base.h | 174 +- src/libmngr_paths.cpp | 16 +- src/libmngr_paths.h | 104 +- src/libraryfunctions.cpp | 226 +- src/libraryfunctions.h | 6 +- src/librarymanager.cpp | 2 +- src/librarymanager.h | 44 +- src/librarymanager.rc | 8 +- src/pdfreport.cpp | 230 +- src/pdfreport.h | 11 +- src/remotelink.cpp | 1196 ++++---- src/remotelink.h | 84 +- src/rpn.cpp | 76 +- src/rpn.h | 282 +- src/svnrev.h | 12 +- src/unqlite.c | 1618 +++-------- src/unqlite.h | 83 +- src/vrmlsupport.cpp | 1030 +++---- src/vrmlsupport.h | 80 +- template/BGA.mt | 7 +- template/CAE.mt | 7 +- template/Chip.mt | 6 +- template/ChipPol.mt | 8 +- template/DIP.mt | 9 +- template/HDR1x_.mt | 7 +- template/HDR1x_SMD.mt | 7 +- template/HDR2x_.mt | 6 +- template/HDR2x_SMD.mt | 9 +- template/HDR2x_Shrouded.mt | 6 +- template/LED_Chip.mt | 5 +- template/MELF.mt | 6 +- template/MLD.mt | 7 +- template/MLDPOL.mt | 7 +- template/QFN.mt | 38 +- template/QFNX.mt | 40 +- template/QFP.mt | 9 +- template/QFPX.mt | 14 +- template/SOD.mt | 16 +- template/SON.mt | 6 +- template/SONX.mt | 12 +- template/SOP.mt | 10 +- template/SOPX.mt | 17 +- template/SOT23.mt | 6 +- template/XTAL_Chip.mt | 5 +- template/XTAL_Chip_Rev.mt | 5 +- template/XTAL_Metal_can.mt | 7 +- template/ZIF-FPC.mt | 10 +- 66 files changed, 5181 insertions(+), 9346 deletions(-) diff --git a/doc/LibraryFileFormats.pdf b/doc/LibraryFileFormats.pdf index af5b855bc0614cfb2fb418302d769b18c71de92b..d03e54224e28808068d682b7411eac5b8f51b53d 100644 GIT binary patch delta 60157 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 z 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 UuUvx-6etk{0T-8?x;(-E0U2^7&;S4c diff --git a/doc/kicadlibrarian.pdf b/doc/kicadlibrarian.pdf index 57160c5640b26892bb4d25ded6b72cd31381c02f..bd81879a087d11b02dd57533a4a26d1121ff983f 100644 GIT binary patch delta 61238 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%0D#;bS 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 aXu_FDW%7g~gyUdiX8A!*E~X&<<9`7>q(b)q delta 56102 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

  • Ey_xu%*eVG-g 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

    @{*D(80eZPoD?sUNPMi3>Z4C?kT-A#72E1TeP z;pTQ0Ck6SmpZx}ywxz(S$Y=%=DXkS?9*BzSm&wH0;$Bq&4w8uOQJ4^}Ss<5!b(xaC zDT{jH40INY@V`=YK`NPb$M&Qfr9j`@?3j7AF{bFm{q%LBsdRcEdq**jYRd_9S8dpb z?rbF@0_(hZ^FB%<#rF@{K!mt~1H}B_J;n1WntUPAwHMAlE#PFS?NIyrf*5n2#w>7( zXK$Dk)PqI<;}BrNgmjxCNrm6-A zE{WlLT;k=6D3~mXe)~*NcLfZ!%dHKhv0lG;WwJ9|oW}OZ#D?oM-g&xaK(JoXMP(jW zXy~(1;j_uzm!P<{85!Z?yFPrZI3Ja$_MTYRKg1pa2$GV*mte6Oht>qWxcmhW-RE-^ zmr5_({Bkc^OB~O6z$}~*=ak}xaR}P^Hu@#0`0i1_=j$aRkC!vX#%#tue7x=+k1|k1 z4O2^1u{}!_!;%BkT$1+va!muQ{{5BhmnCG^>-79KyZ2raSv{pqz8$knJ8EL3 z&v%OlBnCN{o2%H{xVy+Jzm1z#1+u6RtcMt2$AMTaWc`fuzwtnILf{;gJLM9Z8#7D< zkS-Q*8PZEo%sCL2lH)?T>adNqV!1CGADph17@oK~ds~i@XBo&|Mqfsz?LGCZPqx>c^ae?Qav7_b# z`18X`v!!2Q7BLI)=tk?Qfkftm$(XrlSgOjQNZ<6vO8fO(L&)YXA5zNU@~sStZYdMm z8QJVRh%}C1FVuorPGCv2UNC~frAY9vm{W%0zhOXdo zr{*?Xl6B}aQIbri))N@~o6T(nn%D^nSW&T`i_#`>ZEd#V!>kc+5%s#L?67jhn>S*J zZEHwh@>Ak8nIwZ^d##;6)JJ{sd08YY{UgP*KJ^P~^yQa+=$-8(S?v9p8i>dpjYIFr znP(AIPr9{87RdtBC7KYXu@LMwDx~yUd0qvTP&7ZRn`N<(DXUd6$K0!no9NCZpu{dA z;l`*=+USKM$6ijOkh5du0v59WD!i7>AZht_a5hAAV;aft^)V(lvZgEmGxVB#1bfWM ztva>IZ<+A+(6)yP6>rM*6x}5Qq}uLf5PIE)if3Je=DeFNi-EQEA^oAN;8j9DHp_Z3 zbhi%hfp1rqqW!!*D)2ksxgL(FYC^R6i<>UI+nG*}tEe2ZHQ&jjh<1$(9E7P!psxG>^(h_?hz`1vN_}C8Q}W`+_p4J;uVE7sLT3nZHNI(j*C| zGSp6(grpP39Y;(yPHzM1jRqF_t9&m8%pr|l57bA8?u-nl4bUEvAab}sf(90&HK!yp zQnNfhgTOGJEtbK#^C!ZtN+i*t-!DjRUV-VA0iMBWC(`^^g!4MFN<{;s-4;kqb-qJG zRSX_{om@(jk2eBKcaB1gw$fS)Qo9C50<#B#xKCmH3&qO=k5xB4q`%;!f zs6_4Gk8}{7&#{jjf)zccBf=X%jD*1p3@SRTD~Oi>QfNu(ZGFO6u3du=Fsd^41(yh? z4!TpQc)-0!8uxBHga-|wR7w;ukMt-xsxk(OM~WI&@(;)}Lc^wk zD}*P&-S{2PQXMu)Bw|A&03LcWst^!>_`VxYK0i!Vy2>d46CUv9QRp9Vsw%}xZDuDe zGVqR5LQXzCM3ot_X_@??YLVJ+A*J@t2V<&2<5r@&7 oRL4oc)aZcEDwe@MY!Th) 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

    b=d9j`GuF_esiOXD| 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

    84P$iu3@`gf+Ud*LS=mHeUx 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 diff --git a/src/cxffont.cpp b/src/cxffont.cpp index 0ab2eab..dec1e53 100644 --- a/src/cxffont.cpp +++ b/src/cxffont.cpp @@ -16,7 +16,7 @@ * License for the specific language governing permissions and limitations * under the License. * - * $Id: cxffont.cpp 5685 2017-05-23 10:35:40Z thiadmer $ + * $Id: cxffont.cpp 5686 2017-05-24 13:56:46Z thiadmer $ */ #include #include @@ -132,7 +132,7 @@ bool CXFFont::Read(const char *path) width = x2; } if (stroke.GetCount() > 0) - glyph.AddStroke(stroke); /* add the final stroke */ + glyph.AddStroke(stroke); /* add the final stroke */ glyph.SetWidth(width); m_Glyphs.push_back(glyph); } @@ -239,10 +239,10 @@ void CXFFont::DrawText(const wchar_t* text, std::vector* strokes) c ypos = 0; break; case CXF_ALIGNTOP: - ypos = m_Ascender * m_ScaleY * 1.15; /* assume 15% internal leading above the ascender */ + ypos = m_Ascender * m_ScaleY * 1.15; /* assume 15% internal leading above the ascender */ break; case CXF_ALIGNBOTTOM: - ypos = m_Descender * m_ScaleY * 1.1; /* assume 10% internal leading below the descender */ + ypos = m_Descender * m_ScaleY * 1.1; /* assume 10% internal leading below the descender */ break; case CXF_ALIGNCENTRE: ypos = (m_Ascender + m_Descender) * m_ScaleY; diff --git a/src/libmngr_dlgnewfootprint.cpp b/src/libmngr_dlgnewfootprint.cpp index 1f9c370..586de64 100644 --- a/src/libmngr_dlgnewfootprint.cpp +++ b/src/libmngr_dlgnewfootprint.cpp @@ -1,150 +1,150 @@ -/* - * Librarian for KiCad, a free EDA CAD application. - * The dialog with the templates for new components. - * - * Copyright (C) 2013-2016 CompuPhase - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * $Id: libmngr_dlgnewfootprint.cpp 5580 2016-10-03 09:21:56Z thiadmer $ - */ - -#include "libmngr_dlgnewfootprint.h" -#include "librarymanager.h" -#include "libraryfunctions.h" -#include -#include -#include - -libmngrDlgNewFootprint::libmngrDlgNewFootprint(wxWindow* parent) - : DlgNewFootprint(parent) -{ - m_libraryname = wxEmptyString; - m_templatename = wxEmptyString; - m_partname = wxEmptyString; - m_previews.Clear(); - m_currentpreview = 0; - - /* list all templates */ - wxDir dir(theApp->GetTemplatePath()); - if (!dir.IsOpened()) - return; /* error message already given */ - wxArrayString list; - dir.GetAllFiles(theApp->GetTemplatePath(), &list, wxT("*.mt"), wxDIR_FILES); - - /* remove the paths and extensions on all filenames, but add the brief description */ - for (unsigned idx = 0; idx < list.Count(); idx++) { - wxFileName fname(list[idx]); - wxString name = fname.GetName(); - wxString line = GetTemplateHeaderField(name, wxT("brief"), false); - if (line.length() > 0) - name += wxT(" - ") + line; - list[idx] = name; - } - list.Sort(); - m_lstTemplates->InsertItems(list, 0); -} - -void libmngrDlgNewFootprint::OnTemplateSelect(wxCommandEvent& /*event*/) -{ - int idx = m_lstTemplates->GetSelection(); - if (idx < 0) - return; - wxASSERT((unsigned)idx < m_lstTemplates->GetCount()); - - /* get the template name */ - wxString line = m_lstTemplates->GetString(idx); - wxString name = GetToken(&line); /* the template is the start of the name */ - - /* load the base image for the template */ - m_previews.Clear(); - wxString path = theApp->GetTemplatePath() + wxT(DIRSEP_STR) + name + wxT(".bmp"); - wxImage bitmap(path, wxBITMAP_TYPE_BMP); - if (bitmap.IsOk()) { - m_previews.Add(path); - m_bmpExample->SetBitmap(bitmap); - } - - /* get the prefix (which is the name if absent) */ - wxString prefix = GetTemplateHeaderField(name, wxT("prefix"), false); - if (prefix.length() == 0) - prefix = name; - m_txtName->SetValue(prefix); - - /* load the description */ - wxString note = GetTemplateHeaderField(name, wxT("note"), false); - m_txtDescription->SetValue(note); - - /* count the number of additional images */ - wxString field = GetTemplateHeaderField(name, wxT("model"), false); - wxArrayString models = wxSplit(field, wxT(' ')); - for (idx = 0; idx < (int)models.Count(); idx++) { - path = theApp->GetTemplatePath() + wxT(DIRSEP_STR) + wxT("model_") + models[idx] + wxT(".bmp"); - if (wxFileExists(path)) - m_previews.Add(path); - } - if (m_currentpreview >= (int)m_previews.Count()) - m_currentpreview = m_previews.Count() - 1; - m_spinPreview->Enable(m_previews.Count() > 1); -} - -void libmngrDlgNewFootprint::OnOk(wxCommandEvent& event) -{ - /* save the selected template & part names */ - int idx = m_lstTemplates->GetSelection(); - if (idx >= 0) { - wxASSERT((unsigned)idx < m_lstTemplates->GetCount()); - wxString line = m_lstTemplates->GetString(idx); - m_templatename = GetToken(&line); - } - m_partname = m_txtName->GetValue(); - - if (m_templatename.length() == 0 || m_partname.length() == 0) { - wxMessageBox(wxT("Please select a template and specify a name for the footprint.")); - return; - } - wxString prefix = GetTemplateHeaderField(m_templatename, wxT("prefix"), false); - if (m_templatename.CmpNoCase(m_partname) == 0 || prefix.CmpNoCase(m_partname) == 0) { - if (wxMessageBox(wxT("The footprint name is the same as the template name or the prefix.\nIs this what you want?"), wxT("Confirm footprint name"), wxYES_NO | wxICON_QUESTION) != wxYES) - return; - } - - /* optionally verify whether the footprint already exists in the library */ - if (m_libraryname.length() > 0 && ExistFootprint(m_libraryname, m_partname)) { - wxString msg = wxString::Format(wxT("Footprint %s already exists.\nOverwrite?"), m_partname.c_str()); - if (wxMessageBox(msg, wxT("Confirm overwrite"), wxYES_NO | wxICON_QUESTION) != wxYES) - return; - } - - event.Skip(); -} - -void libmngrDlgNewFootprint::OnNextImage(wxSpinEvent& /*event*/) -{ - if (m_previews.Count() > 1) { - m_currentpreview = (m_currentpreview < (int)m_previews.Count() - 1) ? m_currentpreview + 1 : 0; - wxImage bitmap(m_previews[m_currentpreview], wxBITMAP_TYPE_BMP); - if (bitmap.IsOk()) - m_bmpExample->SetBitmap(bitmap); - } -} - -void libmngrDlgNewFootprint::OnPrevImage(wxSpinEvent& /*event*/) -{ - if (m_previews.Count() > 1) { - m_currentpreview = (m_currentpreview > 0) ? m_currentpreview - 1 : m_previews.Count() - 1; - wxImage bitmap(m_previews[m_currentpreview], wxBITMAP_TYPE_BMP); - if (bitmap.IsOk()) - m_bmpExample->SetBitmap(bitmap); - } -} +/* + * Librarian for KiCad, a free EDA CAD application. + * The dialog with the templates for new components. + * + * Copyright (C) 2013-2016 CompuPhase + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * $Id: libmngr_dlgnewfootprint.cpp 5686 2017-05-24 13:56:46Z thiadmer $ + */ + +#include "libmngr_dlgnewfootprint.h" +#include "librarymanager.h" +#include "libraryfunctions.h" +#include +#include +#include + +libmngrDlgNewFootprint::libmngrDlgNewFootprint(wxWindow* parent) + : DlgNewFootprint(parent) +{ + m_libraryname = wxEmptyString; + m_templatename = wxEmptyString; + m_partname = wxEmptyString; + m_previews.Clear(); + m_currentpreview = 0; + + /* list all templates */ + wxDir dir(theApp->GetTemplatePath()); + if (!dir.IsOpened()) + return; /* error message already given */ + wxArrayString list; + dir.GetAllFiles(theApp->GetTemplatePath(), &list, wxT("*.mt"), wxDIR_FILES); + + /* remove the paths and extensions on all filenames, but add the brief description */ + for (unsigned idx = 0; idx < list.Count(); idx++) { + wxFileName fname(list[idx]); + wxString name = fname.GetName(); + wxString line = GetTemplateHeaderField(name, wxT("brief"), false); + if (line.length() > 0) + name += wxT(" - ") + line; + list[idx] = name; + } + list.Sort(); + m_lstTemplates->InsertItems(list, 0); +} + +void libmngrDlgNewFootprint::OnTemplateSelect(wxCommandEvent& /*event*/) +{ + int idx = m_lstTemplates->GetSelection(); + if (idx < 0) + return; + wxASSERT((unsigned)idx < m_lstTemplates->GetCount()); + + /* get the template name */ + wxString line = m_lstTemplates->GetString(idx); + wxString name = GetToken(&line); /* the template is the start of the name */ + + /* load the base image for the template */ + m_previews.Clear(); + wxString path = theApp->GetTemplatePath() + wxT(DIRSEP_STR) + name + wxT(".bmp"); + wxImage bitmap(path, wxBITMAP_TYPE_BMP); + if (bitmap.IsOk()) { + m_previews.Add(path); + m_bmpExample->SetBitmap(bitmap); + } + + /* get the prefix (which is the name if absent) */ + wxString prefix = GetTemplateHeaderField(name, wxT("prefix"), false); + if (prefix.length() == 0) + prefix = name; + m_txtName->SetValue(prefix); + + /* load the description */ + wxString note = GetTemplateHeaderField(name, wxT("note"), false); + m_txtDescription->SetValue(note); + + /* count the number of additional images */ + wxString field = GetTemplateHeaderField(name, wxT("model"), false); + wxArrayString models = wxSplit(field, wxT(' ')); + for (idx = 0; idx < (int)models.Count(); idx++) { + path = theApp->GetTemplatePath() + wxT(DIRSEP_STR) + wxT("model_") + models[idx] + wxT(".bmp"); + if (wxFileExists(path)) + m_previews.Add(path); + } + if (m_currentpreview >= (int)m_previews.Count()) + m_currentpreview = m_previews.Count() - 1; + m_spinPreview->Enable(m_previews.Count() > 1); +} + +void libmngrDlgNewFootprint::OnOk(wxCommandEvent& event) +{ + /* save the selected template & part names */ + int idx = m_lstTemplates->GetSelection(); + if (idx >= 0) { + wxASSERT((unsigned)idx < m_lstTemplates->GetCount()); + wxString line = m_lstTemplates->GetString(idx); + m_templatename = GetToken(&line); + } + m_partname = m_txtName->GetValue(); + + if (m_templatename.length() == 0 || m_partname.length() == 0) { + wxMessageBox(wxT("Please select a template and specify a name for the footprint.")); + return; + } + wxString prefix = GetTemplateHeaderField(m_templatename, wxT("prefix"), false); + if (m_templatename.CmpNoCase(m_partname) == 0 || prefix.CmpNoCase(m_partname) == 0) { + if (wxMessageBox(wxT("The footprint name is the same as the template name or the prefix.\nIs this what you want?"), wxT("Confirm footprint name"), wxYES_NO | wxICON_QUESTION) != wxYES) + return; + } + + /* optionally verify whether the footprint already exists in the library */ + if (m_libraryname.length() > 0 && ExistFootprint(m_libraryname, m_partname)) { + wxString msg = wxString::Format(wxT("Footprint %s already exists.\nOverwrite?"), m_partname.c_str()); + if (wxMessageBox(msg, wxT("Confirm overwrite"), wxYES_NO | wxICON_QUESTION) != wxYES) + return; + } + + event.Skip(); +} + +void libmngrDlgNewFootprint::OnNextImage(wxSpinEvent& /*event*/) +{ + if (m_previews.Count() > 1) { + m_currentpreview = (m_currentpreview < (int)m_previews.Count() - 1) ? m_currentpreview + 1 : 0; + wxImage bitmap(m_previews[m_currentpreview], wxBITMAP_TYPE_BMP); + if (bitmap.IsOk()) + m_bmpExample->SetBitmap(bitmap); + } +} + +void libmngrDlgNewFootprint::OnPrevImage(wxSpinEvent& /*event*/) +{ + if (m_previews.Count() > 1) { + m_currentpreview = (m_currentpreview > 0) ? m_currentpreview - 1 : m_previews.Count() - 1; + wxImage bitmap(m_previews[m_currentpreview], wxBITMAP_TYPE_BMP); + if (bitmap.IsOk()) + m_bmpExample->SetBitmap(bitmap); + } +} diff --git a/src/libmngr_dlgnewfootprint.h b/src/libmngr_dlgnewfootprint.h index ce476e4..01b493e 100644 --- a/src/libmngr_dlgnewfootprint.h +++ b/src/libmngr_dlgnewfootprint.h @@ -1,61 +1,61 @@ -/* - * Librarian for KiCad, a free EDA CAD application. - * The dialog with the templates for new footprints. - * - * Copyright (C) 2013-2015 CompuPhase - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * $Id: libmngr_dlgnewfootprint.h 5435 2016-02-15 12:20:33Z thiadmer $ - */ -#ifndef __libmngr_dlgnewfootprint__ -#define __libmngr_dlgnewfootprint__ - -/** -@file -Subclass of DlgNewFootprint, which is generated by wxFormBuilder. -*/ - -#include "libmngr_gui_base.h" - -//// end generated include - -/** Implementing DlgNewFootprint */ -class libmngrDlgNewFootprint : public DlgNewFootprint -{ - public: - /** Constructor */ - explicit libmngrDlgNewFootprint(wxWindow* parent); - //// end generated class members - - void OnTemplateSelect(wxCommandEvent& event); - void OnNextImage(wxSpinEvent& event); - void OnPrevImage(wxSpinEvent& event); - void OnOk(wxCommandEvent& event); - - wxString GetTemplateName() const { return m_templatename; } - wxString GetFootprintName() const { return m_partname; } - - /** SetLibraryName() to test whether a footprint exists in the library, - * set the full path to the library before calling ShowModal() */ - void SetLibraryName(const wxString& filename) { m_libraryname = filename; } - - private: - wxString m_templatename; /* just the "base" name, without path or extension*/ - wxString m_partname; /* name for the footprint (output of the dialog) */ - wxString m_libraryname; /* name of the library where the new footprint must be stored in */ - wxArrayString m_previews; /* paths to bitmaps with preview images */ - int m_currentpreview; -}; - -#endif // __libmngr_dlgnewfootprint__ +/* + * Librarian for KiCad, a free EDA CAD application. + * The dialog with the templates for new footprints. + * + * Copyright (C) 2013-2015 CompuPhase + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * $Id: libmngr_dlgnewfootprint.h 5686 2017-05-24 13:56:46Z thiadmer $ + */ +#ifndef __libmngr_dlgnewfootprint__ +#define __libmngr_dlgnewfootprint__ + +/** +@file +Subclass of DlgNewFootprint, which is generated by wxFormBuilder. +*/ + +#include "libmngr_gui_base.h" + +//// end generated include + +/** Implementing DlgNewFootprint */ +class libmngrDlgNewFootprint : public DlgNewFootprint +{ + public: + /** Constructor */ + explicit libmngrDlgNewFootprint(wxWindow* parent); + //// end generated class members + + void OnTemplateSelect(wxCommandEvent& event); + void OnNextImage(wxSpinEvent& event); + void OnPrevImage(wxSpinEvent& event); + void OnOk(wxCommandEvent& event); + + wxString GetTemplateName() const { return m_templatename; } + wxString GetFootprintName() const { return m_partname; } + + /** SetLibraryName() to test whether a footprint exists in the library, + * set the full path to the library before calling ShowModal() */ + void SetLibraryName(const wxString& filename) { m_libraryname = filename; } + + private: + wxString m_templatename; /* just the "base" name, without path or extension*/ + wxString m_partname; /* name for the footprint (output of the dialog) */ + wxString m_libraryname; /* name of the library where the new footprint must be stored in */ + wxArrayString m_previews; /* paths to bitmaps with preview images */ + int m_currentpreview; +}; + +#endif // __libmngr_dlgnewfootprint__ diff --git a/src/libmngr_dlgnewsymbol.cpp b/src/libmngr_dlgnewsymbol.cpp index 217b915..8e3e2ba 100644 --- a/src/libmngr_dlgnewsymbol.cpp +++ b/src/libmngr_dlgnewsymbol.cpp @@ -1,117 +1,117 @@ -/* - * Librarian for KiCad, a free EDA CAD application. - * The dialog with the templates for new symbols. - * - * Copyright (C) 2013-2015 CompuPhase - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * $Id: libmngr_dlgnewsymbol.cpp 5387 2015-10-22 19:31:30Z thiadmer $ - */ -#include "libmngr_dlgnewsymbol.h" -#include "librarymanager.h" -#include "libraryfunctions.h" -#include -#include -#include - -libmngrDlgNewSymbol::libmngrDlgNewSymbol( wxWindow* parent ) -: -DlgNewSymbol( parent ) -{ - m_libraryname = wxEmptyString; - m_templatename = wxEmptyString; - m_partname = wxEmptyString; - m_partref = wxEmptyString; - - /* list all templates */ - wxDir dir(theApp->GetTemplatePath()); - if (!dir.IsOpened()) - return; /* error message already given */ - wxArrayString list; - dir.GetAllFiles(theApp->GetTemplatePath(), &list, wxT("*.st"), wxDIR_FILES); - - /* remove the paths and extensions on all filenames, but add the brief description */ - for (unsigned idx = 0; idx < list.Count(); idx++) { - wxFileName fname(list[idx]); - wxString name = fname.GetName(); - wxString line = GetTemplateHeaderField(name, wxT("brief"), true); - if (line.length() > 0) - name += wxT(" - ") + line; - list[idx] = name; - } - list.Sort(); - m_lstTemplates->InsertItems(list, 0); -} - -void libmngrDlgNewSymbol::OnTemplateSelect( wxCommandEvent& /*event*/ ) -{ - int idx = m_lstTemplates->GetSelection(); - if (idx < 0) - return; - wxASSERT((unsigned)idx < m_lstTemplates->GetCount()); - - /* get the template name */ - wxString line = m_lstTemplates->GetString(idx); - wxString name = GetToken(&line); /* the template is the start of the name */ - - /* load the image for the template */ - wxString path = theApp->GetTemplatePath() + wxT(DIRSEP_STR) + name + wxT(".bmp"); - wxImage bitmap(path, wxBITMAP_TYPE_BMP); - if (bitmap.IsOk()) - m_bmpExample->SetBitmap(bitmap); - - /* get the prefix */ - wxString prefix = GetTemplateHeaderField(name, wxT("prefix"), true); - if (prefix.length() == 0) - prefix = wxT("U"); - m_txtPrefix->SetValue(prefix); - - /* load the description */ - wxString note = GetTemplateHeaderField(name, wxT("note"), true); - m_txtDescription->SetValue(note); -} - -void libmngrDlgNewSymbol::OnOk( wxCommandEvent& event ) -{ - /* save the selected template & part names */ - int idx = m_lstTemplates->GetSelection(); - if (idx >= 0) { - wxASSERT((unsigned)idx < m_lstTemplates->GetCount()); - wxString line = m_lstTemplates->GetString(idx); - m_templatename = GetToken(&line); - } - - m_partname = m_txtName->GetValue(); - if (m_templatename.length() == 0 || m_partname.length() == 0) { - wxMessageBox(wxT("Please select a template and specify a name for the symbol.")); - return; - } - if (m_templatename.CmpNoCase(m_partname) == 0) { - if (wxMessageBox(wxT("The symbol name is the same as the template name.\nIs this what you want?"), wxT("Confirm symbol name"), wxYES_NO | wxICON_QUESTION) != wxYES) - return; - } - - m_partref = m_txtPrefix->GetValue(); - if (m_partref.length() == 0) - m_partref = wxT("U"); - - /* optionally verify whether the symol already exists in the library */ - if (m_libraryname.length() > 0 && ExistSymbol(m_libraryname, m_partname)) { - wxString msg = wxString::Format(wxT("Symbol %s already exists.\nOverwrite?"), m_partname.c_str()); - if (wxMessageBox(msg, wxT("Confirm overwrite"), wxYES_NO | wxICON_QUESTION) != wxYES) - return; - } - - event.Skip(); -} +/* + * Librarian for KiCad, a free EDA CAD application. + * The dialog with the templates for new symbols. + * + * Copyright (C) 2013-2015 CompuPhase + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * $Id: libmngr_dlgnewsymbol.cpp 5686 2017-05-24 13:56:46Z thiadmer $ + */ +#include "libmngr_dlgnewsymbol.h" +#include "librarymanager.h" +#include "libraryfunctions.h" +#include +#include +#include + +libmngrDlgNewSymbol::libmngrDlgNewSymbol( wxWindow* parent ) +: +DlgNewSymbol( parent ) +{ + m_libraryname = wxEmptyString; + m_templatename = wxEmptyString; + m_partname = wxEmptyString; + m_partref = wxEmptyString; + + /* list all templates */ + wxDir dir(theApp->GetTemplatePath()); + if (!dir.IsOpened()) + return; /* error message already given */ + wxArrayString list; + dir.GetAllFiles(theApp->GetTemplatePath(), &list, wxT("*.st"), wxDIR_FILES); + + /* remove the paths and extensions on all filenames, but add the brief description */ + for (unsigned idx = 0; idx < list.Count(); idx++) { + wxFileName fname(list[idx]); + wxString name = fname.GetName(); + wxString line = GetTemplateHeaderField(name, wxT("brief"), true); + if (line.length() > 0) + name += wxT(" - ") + line; + list[idx] = name; + } + list.Sort(); + m_lstTemplates->InsertItems(list, 0); +} + +void libmngrDlgNewSymbol::OnTemplateSelect( wxCommandEvent& /*event*/ ) +{ + int idx = m_lstTemplates->GetSelection(); + if (idx < 0) + return; + wxASSERT((unsigned)idx < m_lstTemplates->GetCount()); + + /* get the template name */ + wxString line = m_lstTemplates->GetString(idx); + wxString name = GetToken(&line); /* the template is the start of the name */ + + /* load the image for the template */ + wxString path = theApp->GetTemplatePath() + wxT(DIRSEP_STR) + name + wxT(".bmp"); + wxImage bitmap(path, wxBITMAP_TYPE_BMP); + if (bitmap.IsOk()) + m_bmpExample->SetBitmap(bitmap); + + /* get the prefix */ + wxString prefix = GetTemplateHeaderField(name, wxT("prefix"), true); + if (prefix.length() == 0) + prefix = wxT("U"); + m_txtPrefix->SetValue(prefix); + + /* load the description */ + wxString note = GetTemplateHeaderField(name, wxT("note"), true); + m_txtDescription->SetValue(note); +} + +void libmngrDlgNewSymbol::OnOk( wxCommandEvent& event ) +{ + /* save the selected template & part names */ + int idx = m_lstTemplates->GetSelection(); + if (idx >= 0) { + wxASSERT((unsigned)idx < m_lstTemplates->GetCount()); + wxString line = m_lstTemplates->GetString(idx); + m_templatename = GetToken(&line); + } + + m_partname = m_txtName->GetValue(); + if (m_templatename.length() == 0 || m_partname.length() == 0) { + wxMessageBox(wxT("Please select a template and specify a name for the symbol.")); + return; + } + if (m_templatename.CmpNoCase(m_partname) == 0) { + if (wxMessageBox(wxT("The symbol name is the same as the template name.\nIs this what you want?"), wxT("Confirm symbol name"), wxYES_NO | wxICON_QUESTION) != wxYES) + return; + } + + m_partref = m_txtPrefix->GetValue(); + if (m_partref.length() == 0) + m_partref = wxT("U"); + + /* optionally verify whether the symol already exists in the library */ + if (m_libraryname.length() > 0 && ExistSymbol(m_libraryname, m_partname)) { + wxString msg = wxString::Format(wxT("Symbol %s already exists.\nOverwrite?"), m_partname.c_str()); + if (wxMessageBox(msg, wxT("Confirm overwrite"), wxYES_NO | wxICON_QUESTION) != wxYES) + return; + } + + event.Skip(); +} diff --git a/src/libmngr_dlgnewsymbol.h b/src/libmngr_dlgnewsymbol.h index 9bfdba6..0655bcd 100644 --- a/src/libmngr_dlgnewsymbol.h +++ b/src/libmngr_dlgnewsymbol.h @@ -1,62 +1,62 @@ -/* - * Librarian for KiCad, a free EDA CAD application. - * The dialog with the templates for new symbols. - * - * Copyright (C) 2013-2014=5 CompuPhase - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * $Id: libmngr_dlgnewsymbol.h 5387 2015-10-22 19:31:30Z thiadmer $ - */ -#ifndef __libmngr_dlgnewsymbol__ -#define __libmngr_dlgnewsymbol__ - -/** -@file -Subclass of DlgNewSymbol, which is generated by wxFormBuilder. -*/ - -#include "libmngr_gui_base.h" - -//// end generated include - -/** Implementing DlgNewSymbol */ -class libmngrDlgNewSymbol : public DlgNewSymbol -{ - public: - /** Constructor */ - explicit libmngrDlgNewSymbol(wxWindow* parent); - - protected: - // Handlers for DlgNewSymbol events. - void OnTemplateSelect(wxCommandEvent& event); - void OnOk(wxCommandEvent& event); - //// end generated class members - - public: - wxString GetTemplateName() const { return m_templatename; } - wxString GetSymbolRef() const { return m_partref; } - wxString GetSymbolName() const { return m_partname; } - - /** SetLibraryName() to test whether a symbol exists in the library, set the - * full path to the library before calling ShowModal() */ - void SetLibraryName(const wxString& filename) { m_libraryname = filename; } - - private: - wxString m_templatename; /* just the "base" name, without path or extension */ - wxString m_partname; /* name for the symbol (output of the dialog) */ - wxString m_partref; /* reference prefix for the symbol (output of the dialog) */ - wxString m_libraryname; /* name of the library where the new symbol must be stored in */ -}; - -#endif // __libmngr_dlgnewsymbol__ +/* + * Librarian for KiCad, a free EDA CAD application. + * The dialog with the templates for new symbols. + * + * Copyright (C) 2013-2014=5 CompuPhase + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * $Id: libmngr_dlgnewsymbol.h 5686 2017-05-24 13:56:46Z thiadmer $ + */ +#ifndef __libmngr_dlgnewsymbol__ +#define __libmngr_dlgnewsymbol__ + +/** +@file +Subclass of DlgNewSymbol, which is generated by wxFormBuilder. +*/ + +#include "libmngr_gui_base.h" + +//// end generated include + +/** Implementing DlgNewSymbol */ +class libmngrDlgNewSymbol : public DlgNewSymbol +{ + public: + /** Constructor */ + explicit libmngrDlgNewSymbol(wxWindow* parent); + + protected: + // Handlers for DlgNewSymbol events. + void OnTemplateSelect(wxCommandEvent& event); + void OnOk(wxCommandEvent& event); + //// end generated class members + + public: + wxString GetTemplateName() const { return m_templatename; } + wxString GetSymbolRef() const { return m_partref; } + wxString GetSymbolName() const { return m_partname; } + + /** SetLibraryName() to test whether a symbol exists in the library, set the + * full path to the library before calling ShowModal() */ + void SetLibraryName(const wxString& filename) { m_libraryname = filename; } + + private: + wxString m_templatename; /* just the "base" name, without path or extension */ + wxString m_partname; /* name for the symbol (output of the dialog) */ + wxString m_partref; /* reference prefix for the symbol (output of the dialog) */ + wxString m_libraryname; /* name of the library where the new symbol must be stored in */ +}; + +#endif // __libmngr_dlgnewsymbol__ diff --git a/src/libmngr_dlgoptions.cpp b/src/libmngr_dlgoptions.cpp index 0b16af7..2633950 100644 --- a/src/libmngr_dlgoptions.cpp +++ b/src/libmngr_dlgoptions.cpp @@ -1,131 +1,131 @@ -/* - * Librarian for KiCad, a free EDA CAD application. - * The dialog for the user-interface settings. - * - * Copyright (C) 2013-2017 CompuPhase - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * $Id: libmngr_dlgoptions.cpp 5405 2015-11-20 09:40:55Z thiadmer $ - */ -#include "librarymanager.h" -#include "libmngr_dlgoptions.h" -#include - -libmngrDlgOptions::libmngrDlgOptions( wxWindow* parent ) -: -DlgOptions( parent ) -{ - wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); - long idx; - - idx = config->Read(wxT("display/fontsize"), 8L); - m_spinFontSize->SetValue(idx); - - idx = config->Read(wxT("display/dimoffset"), 50L); - m_spinDimOffset->SetValue(idx); - - wxColour clrFootprintMode; - config->Read(wxT("display/footprintbkgnd"), &clrFootprintMode, wxColour(0, 0, 0)); - m_colourFootprint->SetColour(clrFootprintMode); - - wxColour clrSchematicMode; - config->Read(wxT("display/schematicbkgnd"), &clrSchematicMode, wxColour(255, 255, 255)); - m_colourSchematic->SetColour(clrSchematicMode); - - wxColour clr3DModelMode; - config->Read(wxT("display/3dmodelbkgnd"), &clr3DModelMode, wxColour(0, 64, 0)); - m_colour3DModel->SetColour(clr3DModelMode); - - bool showlabels; - config->Read(wxT("display/showlabels"), &showlabels, true); - m_chkDrawLabels->SetValue(showlabels); - - bool centrecross; - config->Read(wxT("display/centrecross"), ¢recross, true); - m_chkDrawCentreCross->SetValue(centrecross); - - bool fullpaths; - config->Read(wxT("display/fullpath"), &fullpaths, false); - m_chkFullPaths->SetValue(fullpaths); - - bool copyvrml; - config->Read(wxT("settings/copyvrml"), ©vrml, true); - m_chkCopyVRML->SetValue(copyvrml); - - bool disabletemplate; - config->Read(wxT("settings/disabletemplate"), &disabletemplate, false); - m_chkDisableTemplates->SetValue(disabletemplate); - - bool confirmoverwrite; - config->Read(wxT("settings/confirmoverwrite"), &confirmoverwrite, true); - m_chkConfirmOverwrite->SetValue(confirmoverwrite); - - bool confirmdelete; - config->Read(wxT("settings/confirmdelete"), &confirmdelete, true); - m_chkConfirmDelete->SetValue(confirmdelete); - - bool reloadsession; - config->Read(wxT("settings/reloadsession"), &reloadsession, true); - m_chkReloadSession->SetValue(reloadsession); - - delete config; -} - -void libmngrDlgOptions::OnOK( wxCommandEvent& event ) -{ - wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); - int idx; - - idx = m_spinFontSize->GetValue(); - config->Write(wxT("display/fontsize"), idx); - - idx = m_spinDimOffset->GetValue(); - config->Write(wxT("display/dimoffset"), idx); - - wxColour clrFootprintMode = m_colourFootprint->GetColour(); - config->Write(wxT("display/footprintbkgnd"), clrFootprintMode); - - wxColour clrSchematicMode = m_colourSchematic->GetColour(); - config->Write(wxT("display/schematicbkgnd"), clrSchematicMode); - - wxColour clr3DModelMode = m_colour3DModel->GetColour(); - config->Write(wxT("display/3dmodelbkgnd"), clr3DModelMode); - - bool showlabels = m_chkDrawLabels->GetValue(); - config->Write(wxT("display/showlabels"), showlabels); - - bool centrecross = m_chkDrawCentreCross->GetValue(); - config->Write(wxT("display/centrecross"), centrecross); - - bool fullpaths = m_chkFullPaths->GetValue(); - config->Write(wxT("display/fullpath"), fullpaths); - - bool copyvrml = m_chkCopyVRML->GetValue(); - config->Write(wxT("settings/copyvrml"), copyvrml); - - bool disabletemplate = m_chkDisableTemplates->GetValue(); - config->Write(wxT("settings/disabletemplate"), disabletemplate); - - bool confirmoverwrite = m_chkConfirmOverwrite->GetValue(); - config->Write(wxT("settings/confirmoverwrite"), confirmoverwrite); - - bool confirmdelete = m_chkConfirmDelete->GetValue(); - config->Write(wxT("settings/confirmdelete"), confirmdelete); - - bool reloadsession = m_chkReloadSession->GetValue(); - config->Write(wxT("settings/reloadsession"), reloadsession); - - delete config; - event.Skip(); -} +/* + * Librarian for KiCad, a free EDA CAD application. + * The dialog for the user-interface settings. + * + * Copyright (C) 2013-2017 CompuPhase + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * $Id: libmngr_dlgoptions.cpp 5686 2017-05-24 13:56:46Z thiadmer $ + */ +#include "librarymanager.h" +#include "libmngr_dlgoptions.h" +#include + +libmngrDlgOptions::libmngrDlgOptions( wxWindow* parent ) +: +DlgOptions( parent ) +{ + wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); + long idx; + + idx = config->Read(wxT("display/fontsize"), 8L); + m_spinFontSize->SetValue(idx); + + idx = config->Read(wxT("display/dimoffset"), 50L); + m_spinDimOffset->SetValue(idx); + + wxColour clrFootprintMode; + config->Read(wxT("display/footprintbkgnd"), &clrFootprintMode, wxColour(0, 0, 0)); + m_colourFootprint->SetColour(clrFootprintMode); + + wxColour clrSchematicMode; + config->Read(wxT("display/schematicbkgnd"), &clrSchematicMode, wxColour(255, 255, 255)); + m_colourSchematic->SetColour(clrSchematicMode); + + wxColour clr3DModelMode; + config->Read(wxT("display/3dmodelbkgnd"), &clr3DModelMode, wxColour(0, 64, 0)); + m_colour3DModel->SetColour(clr3DModelMode); + + bool showlabels; + config->Read(wxT("display/showlabels"), &showlabels, true); + m_chkDrawLabels->SetValue(showlabels); + + bool centrecross; + config->Read(wxT("display/centrecross"), ¢recross, true); + m_chkDrawCentreCross->SetValue(centrecross); + + bool fullpaths; + config->Read(wxT("display/fullpath"), &fullpaths, false); + m_chkFullPaths->SetValue(fullpaths); + + bool copyvrml; + config->Read(wxT("settings/copyvrml"), ©vrml, true); + m_chkCopyVRML->SetValue(copyvrml); + + bool disabletemplate; + config->Read(wxT("settings/disabletemplate"), &disabletemplate, false); + m_chkDisableTemplates->SetValue(disabletemplate); + + bool confirmoverwrite; + config->Read(wxT("settings/confirmoverwrite"), &confirmoverwrite, true); + m_chkConfirmOverwrite->SetValue(confirmoverwrite); + + bool confirmdelete; + config->Read(wxT("settings/confirmdelete"), &confirmdelete, true); + m_chkConfirmDelete->SetValue(confirmdelete); + + bool reloadsession; + config->Read(wxT("settings/reloadsession"), &reloadsession, true); + m_chkReloadSession->SetValue(reloadsession); + + delete config; +} + +void libmngrDlgOptions::OnOK( wxCommandEvent& event ) +{ + wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); + int idx; + + idx = m_spinFontSize->GetValue(); + config->Write(wxT("display/fontsize"), idx); + + idx = m_spinDimOffset->GetValue(); + config->Write(wxT("display/dimoffset"), idx); + + wxColour clrFootprintMode = m_colourFootprint->GetColour(); + config->Write(wxT("display/footprintbkgnd"), clrFootprintMode); + + wxColour clrSchematicMode = m_colourSchematic->GetColour(); + config->Write(wxT("display/schematicbkgnd"), clrSchematicMode); + + wxColour clr3DModelMode = m_colour3DModel->GetColour(); + config->Write(wxT("display/3dmodelbkgnd"), clr3DModelMode); + + bool showlabels = m_chkDrawLabels->GetValue(); + config->Write(wxT("display/showlabels"), showlabels); + + bool centrecross = m_chkDrawCentreCross->GetValue(); + config->Write(wxT("display/centrecross"), centrecross); + + bool fullpaths = m_chkFullPaths->GetValue(); + config->Write(wxT("display/fullpath"), fullpaths); + + bool copyvrml = m_chkCopyVRML->GetValue(); + config->Write(wxT("settings/copyvrml"), copyvrml); + + bool disabletemplate = m_chkDisableTemplates->GetValue(); + config->Write(wxT("settings/disabletemplate"), disabletemplate); + + bool confirmoverwrite = m_chkConfirmOverwrite->GetValue(); + config->Write(wxT("settings/confirmoverwrite"), confirmoverwrite); + + bool confirmdelete = m_chkConfirmDelete->GetValue(); + config->Write(wxT("settings/confirmdelete"), confirmdelete); + + bool reloadsession = m_chkReloadSession->GetValue(); + config->Write(wxT("settings/reloadsession"), reloadsession); + + delete config; + event.Skip(); +} diff --git a/src/libmngr_dlgoptions.h b/src/libmngr_dlgoptions.h index 1a54bd3..0ca781f 100644 --- a/src/libmngr_dlgoptions.h +++ b/src/libmngr_dlgoptions.h @@ -1,44 +1,44 @@ -/* - * Librarian for KiCad, a free EDA CAD application. - * The dialog for the user-interface settings. - * - * Copyright (C) 2013-2015 CompuPhase - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * $Id: libmngr_dlgoptions.h 5387 2015-10-22 19:31:30Z thiadmer $ - */ -#ifndef __libmngr_dlgoptions__ -#define __libmngr_dlgoptions__ - -/** -@file -Subclass of DlgOptions, which is generated by wxFormBuilder. -*/ - -#include "libmngr_gui_base.h" - -//// end generated include - -/** Implementing DlgOptions */ -class libmngrDlgOptions : public DlgOptions -{ - public: - /** Constructor */ - explicit libmngrDlgOptions(wxWindow* parent); - //// end generated class members - - void OnOK(wxCommandEvent& event); -}; - -#endif // __libmngr_dlgoptions__ +/* + * Librarian for KiCad, a free EDA CAD application. + * The dialog for the user-interface settings. + * + * Copyright (C) 2013-2015 CompuPhase + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * $Id: libmngr_dlgoptions.h 5686 2017-05-24 13:56:46Z thiadmer $ + */ +#ifndef __libmngr_dlgoptions__ +#define __libmngr_dlgoptions__ + +/** +@file +Subclass of DlgOptions, which is generated by wxFormBuilder. +*/ + +#include "libmngr_gui_base.h" + +//// end generated include + +/** Implementing DlgOptions */ +class libmngrDlgOptions : public DlgOptions +{ + public: + /** Constructor */ + explicit libmngrDlgOptions(wxWindow* parent); + //// end generated class members + + void OnOK(wxCommandEvent& event); +}; + +#endif // __libmngr_dlgoptions__ diff --git a/src/libmngr_dlgremotelink.cpp b/src/libmngr_dlgremotelink.cpp index 3d80c37..2328ec7 100644 --- a/src/libmngr_dlgremotelink.cpp +++ b/src/libmngr_dlgremotelink.cpp @@ -1,193 +1,193 @@ -/* - * Librarian for KiCad, a free EDA CAD application. - * The dialog for the link to a remote repository. - * - * Copyright (C) 2013-2015 CompuPhase - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * $Id: libmngr_dlgremotelink.cpp 5387 2015-10-22 19:31:30Z thiadmer $ - */ -#include "librarymanager.h" -#include "libmngr_dlgremotelink.h" -#include -#include "remotelink.h" - -libmngrDlgRemoteLink::libmngrDlgRemoteLink( wxWindow* parent ) -: -DlgRemoteLink( parent ) -{ - ReadFields(); -} - -void libmngrDlgRemoteLink::OnOK( wxCommandEvent& /*event*/ ) -{ - WriteFields(); - curlReset(); - this->EndDialog(wxID_OK); -} - -void libmngrDlgRemoteLink::OnCancel( wxCommandEvent& /*event*/ ) -{ - this->EndDialog(wxID_CANCEL); -} - -void libmngrDlgRemoteLink::OnSignUp( wxCommandEvent& /*event*/ ) -{ - WriteFields(); - libmngrDlgRemoteSignUp dlg(this); - dlg.ShowModal(); - ReadFields(); -} - -void libmngrDlgRemoteLink::ReadFields() -{ - wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); - wxString field; - - field = config->Read(wxT("repository/url")); - m_txtURL->SetValue(field); - - field = config->Read(wxT("repository/user")); - m_txtUserName->SetValue(field); - - field = config->Read(wxT("repository/pwd")); - field = Scramble(field); /* this un-scrambles the string */ - m_txtPassword->SetValue(field); - - field = config->Read(wxT("repository/hostuser")); - m_txtAuthUser->SetValue(field); - - field = config->Read(wxT("repository/hostpwd")); - field = Scramble(field); /* this un-scrambles the string */ - m_txtAuthPWD->SetValue(field); - - long flags; - config->Read(wxT("repository/hostverify"), &flags, 0x03); - m_checkVerifyPeer->SetValue((flags & 0x01) != 0); - m_checkVerifyHost->SetValue((flags & 0x02) != 0); - - delete config; -} - -void libmngrDlgRemoteLink::WriteFields() -{ - wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); - wxString field; - - field = m_txtURL->GetValue(); - if (field.Find(wxT(".php")) < 0) { - /* no page specified at the end, concatenate default repository name */ - size_t len = field.length(); - if (len > 0 && field[len - 1] != '/') - field += wxT("/"); - field += wxT("repository.php"); - } - config->Write(wxT("repository/url"), field); - - field = m_txtUserName->GetValue(); - config->Write(wxT("repository/user"), field); - - field = m_txtPassword->GetValue(); - field = Scramble(field); - config->Write(wxT("repository/pwd"), field); - - field = m_txtAuthUser->GetValue(); - config->Write(wxT("repository/hostuser"), field); - - field = m_txtAuthPWD->GetValue(); - field = Scramble(field); - config->Write(wxT("repository/hostpwd"), field); - - long flags = 0; - if (m_checkVerifyPeer->GetValue()) - flags |= 0x01; - if (m_checkVerifyHost->GetValue()) - flags |= 0x02; - config->Write(wxT("repository/hostverify"), flags); - - delete config; -} - - - -libmngrDlgRemoteSignUp::libmngrDlgRemoteSignUp( wxWindow* parent ) -: -DlgRemoteSignUp( parent ) -{ - wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); - wxString field; - - field = config->Read(wxT("repository/url")); - m_txtURL->SetValue(field); - - field = config->Read(wxT("repository/user")); - m_txtUserName->SetValue(field); - - field = config->Read(wxT("repository/email")); - m_txtEmail->SetValue(field); - - field = config->Read(wxT("repository/hostuser")); - m_txtAuthUser->SetValue(field); - - field = config->Read(wxT("repository/hostpwd")); - field = Scramble(field); /* this un-scrambles the string */ - m_txtAuthPWD->SetValue(field); - - long flags; - config->Read(wxT("repository/hostverify"), &flags, 0x03); - m_checkVerifyPeer->SetValue((flags & 0x01) != 0); - m_checkVerifyHost->SetValue((flags & 0x02) != 0); - - delete config; -} - -void libmngrDlgRemoteSignUp::OnOK( wxCommandEvent& event ) -{ - wxString url = m_txtURL->GetValue(); - wxString name = m_txtUserName->GetValue(); - wxString email = m_txtEmail->GetValue(); - wxString hostuser = m_txtAuthUser->GetValue(); - wxString hostpwd = m_txtAuthPWD->GetValue(); - long hostverify = 0; - - if (url.Find(wxT(".php")) < 0) { - /* no page specified at the end, concatenate default repository name */ - size_t len = url.length(); - if (len > 0 && url[len - 1] != '/') - url += wxT("/"); - url += wxT("repository.php"); - } - if (m_checkVerifyPeer->GetValue()) - hostverify |= 0x01; - if (m_checkVerifyHost->GetValue()) - hostverify |= 0x02; - - wxString msg = curlAddUser(url, name, email, hostuser, hostpwd, hostverify); - if (msg.length() == 0) { - wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); - config->Write(wxT("repository/url"), url); - config->Write(wxT("repository/user"), name); - config->Write(wxT("repository/email"), email); - config->Write(wxT("repository/hostuser"), hostuser); - config->Write(wxT("repository/hostpwd"), Scramble(hostpwd)); - config->Write(wxT("repository/hostverify"), hostverify); - delete config; - - wxMessageBox(wxT("Sign-up succeeded\nYou will receive a password on the supplied e-mail address.")); - event.Skip(); - } else { - wxMessageBox(wxT("Sign-up failure\nServer message: ") + msg); - } -} - +/* + * Librarian for KiCad, a free EDA CAD application. + * The dialog for the link to a remote repository. + * + * Copyright (C) 2013-2015 CompuPhase + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * $Id: libmngr_dlgremotelink.cpp 5686 2017-05-24 13:56:46Z thiadmer $ + */ +#include "librarymanager.h" +#include "libmngr_dlgremotelink.h" +#include +#include "remotelink.h" + +libmngrDlgRemoteLink::libmngrDlgRemoteLink( wxWindow* parent ) +: +DlgRemoteLink( parent ) +{ + ReadFields(); +} + +void libmngrDlgRemoteLink::OnOK( wxCommandEvent& /*event*/ ) +{ + WriteFields(); + curlReset(); + this->EndDialog(wxID_OK); +} + +void libmngrDlgRemoteLink::OnCancel( wxCommandEvent& /*event*/ ) +{ + this->EndDialog(wxID_CANCEL); +} + +void libmngrDlgRemoteLink::OnSignUp( wxCommandEvent& /*event*/ ) +{ + WriteFields(); + libmngrDlgRemoteSignUp dlg(this); + dlg.ShowModal(); + ReadFields(); +} + +void libmngrDlgRemoteLink::ReadFields() +{ + wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); + wxString field; + + field = config->Read(wxT("repository/url")); + m_txtURL->SetValue(field); + + field = config->Read(wxT("repository/user")); + m_txtUserName->SetValue(field); + + field = config->Read(wxT("repository/pwd")); + field = Scramble(field); /* this un-scrambles the string */ + m_txtPassword->SetValue(field); + + field = config->Read(wxT("repository/hostuser")); + m_txtAuthUser->SetValue(field); + + field = config->Read(wxT("repository/hostpwd")); + field = Scramble(field); /* this un-scrambles the string */ + m_txtAuthPWD->SetValue(field); + + long flags; + config->Read(wxT("repository/hostverify"), &flags, 0x03); + m_checkVerifyPeer->SetValue((flags & 0x01) != 0); + m_checkVerifyHost->SetValue((flags & 0x02) != 0); + + delete config; +} + +void libmngrDlgRemoteLink::WriteFields() +{ + wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); + wxString field; + + field = m_txtURL->GetValue(); + if (field.Find(wxT(".php")) < 0) { + /* no page specified at the end, concatenate default repository name */ + size_t len = field.length(); + if (len > 0 && field[len - 1] != '/') + field += wxT("/"); + field += wxT("repository.php"); + } + config->Write(wxT("repository/url"), field); + + field = m_txtUserName->GetValue(); + config->Write(wxT("repository/user"), field); + + field = m_txtPassword->GetValue(); + field = Scramble(field); + config->Write(wxT("repository/pwd"), field); + + field = m_txtAuthUser->GetValue(); + config->Write(wxT("repository/hostuser"), field); + + field = m_txtAuthPWD->GetValue(); + field = Scramble(field); + config->Write(wxT("repository/hostpwd"), field); + + long flags = 0; + if (m_checkVerifyPeer->GetValue()) + flags |= 0x01; + if (m_checkVerifyHost->GetValue()) + flags |= 0x02; + config->Write(wxT("repository/hostverify"), flags); + + delete config; +} + + + +libmngrDlgRemoteSignUp::libmngrDlgRemoteSignUp( wxWindow* parent ) +: +DlgRemoteSignUp( parent ) +{ + wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); + wxString field; + + field = config->Read(wxT("repository/url")); + m_txtURL->SetValue(field); + + field = config->Read(wxT("repository/user")); + m_txtUserName->SetValue(field); + + field = config->Read(wxT("repository/email")); + m_txtEmail->SetValue(field); + + field = config->Read(wxT("repository/hostuser")); + m_txtAuthUser->SetValue(field); + + field = config->Read(wxT("repository/hostpwd")); + field = Scramble(field); /* this un-scrambles the string */ + m_txtAuthPWD->SetValue(field); + + long flags; + config->Read(wxT("repository/hostverify"), &flags, 0x03); + m_checkVerifyPeer->SetValue((flags & 0x01) != 0); + m_checkVerifyHost->SetValue((flags & 0x02) != 0); + + delete config; +} + +void libmngrDlgRemoteSignUp::OnOK( wxCommandEvent& event ) +{ + wxString url = m_txtURL->GetValue(); + wxString name = m_txtUserName->GetValue(); + wxString email = m_txtEmail->GetValue(); + wxString hostuser = m_txtAuthUser->GetValue(); + wxString hostpwd = m_txtAuthPWD->GetValue(); + long hostverify = 0; + + if (url.Find(wxT(".php")) < 0) { + /* no page specified at the end, concatenate default repository name */ + size_t len = url.length(); + if (len > 0 && url[len - 1] != '/') + url += wxT("/"); + url += wxT("repository.php"); + } + if (m_checkVerifyPeer->GetValue()) + hostverify |= 0x01; + if (m_checkVerifyHost->GetValue()) + hostverify |= 0x02; + + wxString msg = curlAddUser(url, name, email, hostuser, hostpwd, hostverify); + if (msg.length() == 0) { + wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); + config->Write(wxT("repository/url"), url); + config->Write(wxT("repository/user"), name); + config->Write(wxT("repository/email"), email); + config->Write(wxT("repository/hostuser"), hostuser); + config->Write(wxT("repository/hostpwd"), Scramble(hostpwd)); + config->Write(wxT("repository/hostverify"), hostverify); + delete config; + + wxMessageBox(wxT("Sign-up succeeded\nYou will receive a password on the supplied e-mail address.")); + event.Skip(); + } else { + wxMessageBox(wxT("Sign-up failure\nServer message: ") + msg); + } +} + diff --git a/src/libmngr_dlgremotelink.h b/src/libmngr_dlgremotelink.h index 0583178..4f51386 100644 --- a/src/libmngr_dlgremotelink.h +++ b/src/libmngr_dlgremotelink.h @@ -1,63 +1,63 @@ -/* - * Librarian for KiCad, a free EDA CAD application. - * The dialog for the link to a remote repository. - * - * Copyright (C) 2013-2015 CompuPhase - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * $Id: libmngr_dlgremotelink.h 5387 2015-10-22 19:31:30Z thiadmer $ - */ -#ifndef __libmngr_dlgremotelink__ -#define __libmngr_dlgremotelink__ - -/** -@file -Subclass of DlgRemoteLink, which is generated by wxFormBuilder. -*/ - -#include "libmngr_gui_base.h" - -//// end generated include - -/** Implementing DlgRemoteLink */ -class libmngrDlgRemoteLink : public DlgRemoteLink -{ - public: - /** Constructor */ - explicit libmngrDlgRemoteLink(wxWindow* parent); - protected: - void OnOK(wxCommandEvent& event); - void OnCancel(wxCommandEvent& event); - void OnSignUp(wxCommandEvent& event); - //// end generated class members - - private: - void ReadFields(); - void WriteFields(); -}; - -/** Implementing DlgRemoteSignUp */ -class libmngrDlgRemoteSignUp : public DlgRemoteSignUp -{ - public: - /** Constructor */ - explicit libmngrDlgRemoteSignUp(wxWindow* parent); - protected: - // Handlers for DlgRemoteSignUp events. - void OnOK(wxCommandEvent& event); - //// end generated class members - -}; - -#endif // __libmngr_dlgremotelink__ +/* + * Librarian for KiCad, a free EDA CAD application. + * The dialog for the link to a remote repository. + * + * Copyright (C) 2013-2015 CompuPhase + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * $Id: libmngr_dlgremotelink.h 5686 2017-05-24 13:56:46Z thiadmer $ + */ +#ifndef __libmngr_dlgremotelink__ +#define __libmngr_dlgremotelink__ + +/** +@file +Subclass of DlgRemoteLink, which is generated by wxFormBuilder. +*/ + +#include "libmngr_gui_base.h" + +//// end generated include + +/** Implementing DlgRemoteLink */ +class libmngrDlgRemoteLink : public DlgRemoteLink +{ + public: + /** Constructor */ + explicit libmngrDlgRemoteLink(wxWindow* parent); + protected: + void OnOK(wxCommandEvent& event); + void OnCancel(wxCommandEvent& event); + void OnSignUp(wxCommandEvent& event); + //// end generated class members + + private: + void ReadFields(); + void WriteFields(); +}; + +/** Implementing DlgRemoteSignUp */ +class libmngrDlgRemoteSignUp : public DlgRemoteSignUp +{ + public: + /** Constructor */ + explicit libmngrDlgRemoteSignUp(wxWindow* parent); + protected: + // Handlers for DlgRemoteSignUp events. + void OnOK(wxCommandEvent& event); + //// end generated class members + +}; + +#endif // __libmngr_dlgremotelink__ diff --git a/src/libmngr_dlgreport.cpp b/src/libmngr_dlgreport.cpp index 1c5cadb..c899d06 100644 --- a/src/libmngr_dlgreport.cpp +++ b/src/libmngr_dlgreport.cpp @@ -1,89 +1,95 @@ -/* - * Librarian for KiCad, a free EDA CAD application. - * The dialog for the report settings (footprint sheet). - * - * Copyright (C) 2013-2015 CompuPhase - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * $Id: libmngr_dlgreport.cpp 5387 2015-10-22 19:31:30Z thiadmer $ - */ -#include "librarymanager.h" -#include "libmngr_dlgreport.h" -#include - -libmngrDlgReport::libmngrDlgReport( wxWindow* parent ) -: -DlgReport( parent ) -{ - wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); - - wxString format = config->Read(wxT("report/paper"), wxT("Letter")); - long idx = m_choicePageSize->FindString(format); - if (idx >= 0) - m_choicePageSize->Select(idx); - - idx = config->Read(wxT("report/layout"), 0L); - m_radioLayout->SetSelection(idx); - - idx = config->Read(wxT("report/includedescription"), 1L); - m_chkDescription->SetValue(idx != 0); - - idx = config->Read(wxT("report/drawlabels"), 0L); - m_chkValueLabels->SetValue(idx != 0); - - idx = config->Read(wxT("report/includepadinfo"), 1L); - m_chkPadInfo->SetValue(idx != 0); - - idx = config->Read(wxT("report/fplist"), 1L); - m_chkFPList->SetValue(idx != 0); - - idx = config->Read(wxT("report/fontsize"), 8L); - m_spinFontSize->SetValue(idx); - - delete config; -} - -void libmngrDlgReport::OnOK( wxCommandEvent& event ) -{ - wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); - - int idx = m_choicePageSize->GetSelection(); - wxString format; - if (idx >= 0) - format = m_choicePageSize->GetString(idx); - else - format = wxT("Letter"); - config->Write(wxT("report/paper"), format); - - idx = m_chkDescription->GetValue(); - config->Write(wxT("report/includedescription"), idx); - - idx = m_radioLayout->GetSelection(); - config->Write(wxT("report/layout"), idx); - - idx = m_chkValueLabels->GetValue(); - config->Write(wxT("report/drawlabels"), idx); - - idx = m_chkPadInfo->GetValue(); - config->Write(wxT("report/includepadinfo"), idx); - - idx = m_chkFPList->GetValue(); - config->Write(wxT("report/fplist"), idx); - - idx = m_spinFontSize->GetValue(); - config->Write(wxT("report/fontsize"), idx); - - delete config; - event.Skip(); -} +/* + * Librarian for KiCad, a free EDA CAD application. + * The dialog for the report settings (footprint sheet). + * + * Copyright (C) 2013-2018 CompuPhase + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * $Id: libmngr_dlgreport.cpp 5907 2018-12-14 22:05:40Z thiadmer $ + */ +#include "librarymanager.h" +#include "libmngr_dlgreport.h" +#include + +libmngrDlgReport::libmngrDlgReport( wxWindow* parent ) +: +DlgReport( parent ) +{ + wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); + + wxString format = config->Read(wxT("report/paper"), wxT("Letter")); + long idx = m_choicePageSize->FindString(format); + if (idx >= 0) + m_choicePageSize->Select(idx); + + idx = config->Read(wxT("report/layout"), 0L); + m_radioLayout->SetSelection(idx); + + idx = config->Read(wxT("report/includedescription"), 1L); + m_chkDescription->SetValue(idx != 0); + + idx = config->Read(wxT("report/drawlabels"), 0L); + m_chkValueLabels->SetValue(idx != 0); + + idx = config->Read(wxT("report/includepadinfo"), 1L); + m_chkPadInfo->SetValue(idx != 0); + + idx = config->Read(wxT("report/fplist"), 1L); + m_chkFPList->SetValue(idx != 0); + + idx = config->Read(wxT("report/index"), 1L); + m_chkIndex->SetValue(idx != 0); + + idx = config->Read(wxT("report/fontsize"), 8L); + m_spinFontSize->SetValue(idx); + + delete config; +} + +void libmngrDlgReport::OnOK( wxCommandEvent& event ) +{ + wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); + + int idx = m_choicePageSize->GetSelection(); + wxString format; + if (idx >= 0) + format = m_choicePageSize->GetString(idx); + else + format = wxT("Letter"); + config->Write(wxT("report/paper"), format); + + idx = m_chkDescription->GetValue(); + config->Write(wxT("report/includedescription"), idx); + + idx = m_radioLayout->GetSelection(); + config->Write(wxT("report/layout"), idx); + + idx = m_chkValueLabels->GetValue(); + config->Write(wxT("report/drawlabels"), idx); + + idx = m_chkPadInfo->GetValue(); + config->Write(wxT("report/includepadinfo"), idx); + + idx = m_chkFPList->GetValue(); + config->Write(wxT("report/fplist"), idx); + + idx = m_chkIndex->GetValue(); + config->Write(wxT("report/index"), idx); + + idx = m_spinFontSize->GetValue(); + config->Write(wxT("report/fontsize"), idx); + + delete config; + event.Skip(); +} diff --git a/src/libmngr_dlgreport.h b/src/libmngr_dlgreport.h index 05223a0..e16e968 100644 --- a/src/libmngr_dlgreport.h +++ b/src/libmngr_dlgreport.h @@ -1,45 +1,45 @@ -/* - * Librarian for KiCad, a free EDA CAD application. - * The dialog for the report settings (footprint sheet). - * - * Copyright (C) 2013-2015 CompuPhase - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * $Id: libmngr_dlgreport.h 5387 2015-10-22 19:31:30Z thiadmer $ - */ -#ifndef __libmngr_dlgreport__ -#define __libmngr_dlgreport__ - -/** -@file -Subclass of DlgReport, which is generated by wxFormBuilder. -*/ - -#include "libmngr_gui_base.h" - -//// end generated include - -/** Implementing DlgReport */ -class libmngrDlgReport : public DlgReport -{ - public: - /** Constructor */ - explicit libmngrDlgReport(wxWindow* parent); - - protected: - // Handlers for DlgReport events. - void OnOK(wxCommandEvent& event); -}; - -#endif // __libmngr_dlgreport__ +/* + * Librarian for KiCad, a free EDA CAD application. + * The dialog for the report settings (footprint sheet). + * + * Copyright (C) 2013-2015 CompuPhase + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * $Id: libmngr_dlgreport.h 5686 2017-05-24 13:56:46Z thiadmer $ + */ +#ifndef __libmngr_dlgreport__ +#define __libmngr_dlgreport__ + +/** +@file +Subclass of DlgReport, which is generated by wxFormBuilder. +*/ + +#include "libmngr_gui_base.h" + +//// end generated include + +/** Implementing DlgReport */ +class libmngrDlgReport : public DlgReport +{ + public: + /** Constructor */ + explicit libmngrDlgReport(wxWindow* parent); + + protected: + // Handlers for DlgReport events. + void OnOK(wxCommandEvent& event); +}; + +#endif // __libmngr_dlgreport__ diff --git a/src/libmngr_dlgtemplate.cpp b/src/libmngr_dlgtemplate.cpp index 20657d4..1e92c24 100644 --- a/src/libmngr_dlgtemplate.cpp +++ b/src/libmngr_dlgtemplate.cpp @@ -1,103 +1,103 @@ -/* - * Librarian for KiCad, a free EDA CAD application. - * The dialog for adjusting the template variables. - * - * Copyright (C) 2013-2015 CompuPhase - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * $Id: libmngr_dlgtemplate.cpp 5387 2015-10-22 19:31:30Z thiadmer $ - */ -#include "librarymanager.h" -#include "libmngr_dlgtemplate.h" -#include - - -#if !defined sizearray - #define sizearray(a) (sizeof(a) / sizeof((a)[0])) -#endif - -struct TemplateDefInfo { - const wxChar* descr; - const wxChar* name; - bool percent; -}; - -static TemplateDefInfo TemplateDefValue[] = { - { wxT("Body Pen thickness (mm)"), wxT("BP"), false }, - { wxT("Text Size for Reference label (mm)"), wxT("TSR"), false }, - { wxT("Text Size for Value label (mm)"), wxT("TSV"), false }, - { wxT("Text Weight (%)"), wxT("TW"), true }, - { wxT("Solder-To-Pad clearance (mm)"), wxT("STP"), false }, - { wxT("Aux. Pad Solder Reduction (%)"), wxT("PSRA"), true }, -}; - - -libmngrDlgTemplateOpts::libmngrDlgTemplateOpts( wxWindow* parent ) -: -DlgTemplateOpts( parent ) -{ - int count = 0; - while (TemplateDefValue[count].descr) - count++; - m_gridTemplateVars->ClearGrid(); - if (m_gridTemplateVars->GetNumberRows() != count) { - m_gridTemplateVars->DeleteRows(0, m_gridTemplateVars->GetNumberRows()); - m_gridTemplateVars->InsertRows(0, count); - } - m_gridTemplateVars->EnableEditing(true); - - wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); - wxString field; - double value; - for (int idx = 0; idx < sizearray(TemplateDefValue); idx++) { - m_gridTemplateVars->SetCellValue((int)idx, 0, TemplateDefValue[idx].descr); - m_gridTemplateVars->SetCellValue((int)idx, 1, TemplateDefValue[idx].name); - m_gridTemplateVars->SetReadOnly(idx, 0, true); - m_gridTemplateVars->SetReadOnly(idx, 1, true); - field = wxT("template/"); - field += TemplateDefValue[idx].name; - if (config->Read(field, &value)) { - if (TemplateDefValue[idx].percent) - field = wxString::Format(wxT("%d"), (int)value); - else - field = wxString::Format(wxT("%.2f"), value); - m_gridTemplateVars->SetCellValue((int)idx, 2, field); - } - m_gridTemplateVars->SetReadOnly(idx, 2, false); - } - delete config; - - m_gridTemplateVars->AutoSize(); - m_gridTemplateVars->Fit(); -} - -void libmngrDlgTemplateOpts::OnOK( wxCommandEvent& event ) -{ - wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); - /* clear the existing search settings */ - config->DeleteGroup(wxT("template")); - - wxString field; - double value; - for (int idx = 0; idx < sizearray(TemplateDefValue); idx++) { - field = m_gridTemplateVars->GetCellValue(idx, 2); - if (field.length() > 0 && field.ToDouble(&value)) { - field = wxT("template/"); - field += TemplateDefValue[idx].name; - config->Write(field, value); - } - } - delete config; - event.Skip(); -} +/* + * Librarian for KiCad, a free EDA CAD application. + * The dialog for adjusting the template variables. + * + * Copyright (C) 2013-2015 CompuPhase + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * $Id: libmngr_dlgtemplate.cpp 5686 2017-05-24 13:56:46Z thiadmer $ + */ +#include "librarymanager.h" +#include "libmngr_dlgtemplate.h" +#include + + +#if !defined sizearray + #define sizearray(a) (sizeof(a) / sizeof((a)[0])) +#endif + +struct TemplateDefInfo { + const wxChar* descr; + const wxChar* name; + bool percent; +}; + +static TemplateDefInfo TemplateDefValue[] = { + { wxT("Body Pen thickness (mm)"), wxT("BP"), false }, + { wxT("Text Size for Reference label (mm)"), wxT("TSR"), false }, + { wxT("Text Size for Value label (mm)"), wxT("TSV"), false }, + { wxT("Text Weight (%)"), wxT("TW"), true }, + { wxT("Solder-To-Pad clearance (mm)"), wxT("STP"), false }, + { wxT("Aux. Pad Solder Reduction (%)"), wxT("PSRA"), true }, +}; + + +libmngrDlgTemplateOpts::libmngrDlgTemplateOpts( wxWindow* parent ) +: +DlgTemplateOpts( parent ) +{ + int count = 0; + while (TemplateDefValue[count].descr) + count++; + m_gridTemplateVars->ClearGrid(); + if (m_gridTemplateVars->GetNumberRows() != count) { + m_gridTemplateVars->DeleteRows(0, m_gridTemplateVars->GetNumberRows()); + m_gridTemplateVars->InsertRows(0, count); + } + m_gridTemplateVars->EnableEditing(true); + + wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); + wxString field; + double value; + for (int idx = 0; idx < sizearray(TemplateDefValue); idx++) { + m_gridTemplateVars->SetCellValue((int)idx, 0, TemplateDefValue[idx].descr); + m_gridTemplateVars->SetCellValue((int)idx, 1, TemplateDefValue[idx].name); + m_gridTemplateVars->SetReadOnly(idx, 0, true); + m_gridTemplateVars->SetReadOnly(idx, 1, true); + field = wxT("template/"); + field += TemplateDefValue[idx].name; + if (config->Read(field, &value)) { + if (TemplateDefValue[idx].percent) + field = wxString::Format(wxT("%d"), (int)value); + else + field = wxString::Format(wxT("%.2f"), value); + m_gridTemplateVars->SetCellValue((int)idx, 2, field); + } + m_gridTemplateVars->SetReadOnly(idx, 2, false); + } + delete config; + + m_gridTemplateVars->AutoSize(); + m_gridTemplateVars->Fit(); +} + +void libmngrDlgTemplateOpts::OnOK( wxCommandEvent& event ) +{ + wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); + /* clear the existing search settings */ + config->DeleteGroup(wxT("template")); + + wxString field; + double value; + for (int idx = 0; idx < sizearray(TemplateDefValue); idx++) { + field = m_gridTemplateVars->GetCellValue(idx, 2); + if (field.length() > 0 && field.ToDouble(&value)) { + field = wxT("template/"); + field += TemplateDefValue[idx].name; + config->Write(field, value); + } + } + delete config; + event.Skip(); +} diff --git a/src/libmngr_dlgtemplate.h b/src/libmngr_dlgtemplate.h index 2be58b4..5eb6ea6 100644 --- a/src/libmngr_dlgtemplate.h +++ b/src/libmngr_dlgtemplate.h @@ -1,46 +1,46 @@ -/* - * Librarian for KiCad, a free EDA CAD application. - * The dialog for adjusting the template variables. - * - * Copyright (C) 2013-2015 CompuPhase - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * $Id: libmngr_dlgtemplate.h 5387 2015-10-22 19:31:30Z thiadmer $ - */ -#ifndef __libmngr_dlgtemplate__ -#define __libmngr_dlgtemplate__ - -/** -@file -Subclass of DlgTemplateOpts, which is generated by wxFormBuilder. -*/ - -#include "libmngr_gui_base.h" - -//// end generated include - - -/** Implementing DlgTemplateOpts */ -class libmngrDlgTemplateOpts : public DlgTemplateOpts -{ - public: - /** Constructor */ - explicit libmngrDlgTemplateOpts(wxWindow* parent); - - protected: - // Handlers for DlgTemplateOpts events. - void OnOK(wxCommandEvent& event); -}; - -#endif // __libmngr_dlgtemplate__ +/* + * Librarian for KiCad, a free EDA CAD application. + * The dialog for adjusting the template variables. + * + * Copyright (C) 2013-2015 CompuPhase + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * $Id: libmngr_dlgtemplate.h 5686 2017-05-24 13:56:46Z thiadmer $ + */ +#ifndef __libmngr_dlgtemplate__ +#define __libmngr_dlgtemplate__ + +/** +@file +Subclass of DlgTemplateOpts, which is generated by wxFormBuilder. +*/ + +#include "libmngr_gui_base.h" + +//// end generated include + + +/** Implementing DlgTemplateOpts */ +class libmngrDlgTemplateOpts : public DlgTemplateOpts +{ + public: + /** Constructor */ + explicit libmngrDlgTemplateOpts(wxWindow* parent); + + protected: + // Handlers for DlgTemplateOpts events. + void OnOK(wxCommandEvent& event); +}; + +#endif // __libmngr_dlgtemplate__ diff --git a/src/libmngr_frame.cpp b/src/libmngr_frame.cpp index 14cd882..768d5b6 100644 --- a/src/libmngr_frame.cpp +++ b/src/libmngr_frame.cpp @@ -3,7 +3,7 @@ * This file contains the code for the main frame, which is almost all of the * user-interface code. * - * Copyright (C) 2013-2017 CompuPhase + * Copyright (C) 2013-2018 CompuPhase * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy @@ -17,7 +17,7 @@ * License for the specific language governing permissions and limitations * under the License. * - * $Id: libmngr_frame.cpp 5784 2017-12-26 14:12:22Z thiadmer $ + * $Id: libmngr_frame.cpp 5907 2018-12-14 22:05:40Z thiadmer $ */ #include "librarymanager.h" #include "libmngr_frame.h" @@ -78,6 +78,7 @@ extern "C" { #define SCALE_MIN 0.8 #define SCALE_DEFAULT 8 #define SCALE_MAX 80 +#define SCALE_FACTOR 1.189207 /* pow(2, 0.25) */ #if defined _WIN32 #define PANEL_WIDTH 200 /* default width of the sidebar, in pixels */ #define EDITF_WIDTH 150 @@ -97,9 +98,9 @@ extern "C" { #define RIGHTPANEL 1 #define BOTHPANELS 0 -#define PROTECTED wxColour(224,224,224) -#define ENABLED *wxWHITE -#define CHANGED wxColour(255,192,192) +const wxColour ENABLED(*wxWHITE); +const wxColour PROTECTED(224,224,224); +const wxColour CHANGED(255,192,192); libmngrFrame::libmngrFrame(wxWindow* parent) @@ -279,7 +280,7 @@ void libmngrFrame::OnTimer(wxTimerEvent& /*event*/) if (ok_count == 2) { /* all sashes already at the good position, wait a while and check whether to clear the timer */ if (busy == NULL) - busy = new wxBusyInfo("Initializeing lay-out. Please wait..."); + busy = new wxBusyInfo("Initializing lay-out. Please wait..."); if (++idle_count >= 3) { /* position has stayed good for 1.5 seconds */ m_Timer->Stop(); delete busy; @@ -709,6 +710,7 @@ void libmngrFrame::OnNewFootprint(wxCommandEvent& /*event*/) LoadPart(idx, list, 0, 0); UpdateDetails(0); Update3DModel(PartData[0]); + AutoZoom(0); m_panelView->Refresh(); m_statusBar->SetStatusText(wxT("New footprint")); /* toggle the details panel on, if it was off */ @@ -923,11 +925,12 @@ void libmngrFrame::OnFootprintReport(wxCommandEvent& /*event*/) long opt_labels = config->Read(wxT("report/drawlabels"), 0L); long opt_description = config->Read(wxT("report/includedescription"), 1L); long opt_padinfo = config->Read(wxT("report/includepadinfo"), 1L); + long opt_index = config->Read(wxT("report/index"), 1L); long fontsize = config->Read(wxT("report/fontsize"), 8L); delete config; PdfReport report; report.SetPage(format, (landscape != 0)); - report.FootprintOptions(opt_description != 0, opt_padinfo != 0, opt_labels != 0); + report.FootprintOptions(opt_description != 0, opt_index != 0, opt_padinfo != 0, opt_labels != 0); report.SetFont(fontsize); report.FootprintReport(this, library, modules, reportfile); m_statusBar->SetStatusText(wxT("Finished report")); @@ -961,11 +964,12 @@ void libmngrFrame::OnSymbolReport(wxCommandEvent& /*event*/) long landscape = config->Read(wxT("report/layout"), 0L); long fontsize = config->Read(wxT("report/fontsize"), 8L); long opt_description = config->Read(wxT("report/includedescription"), 1L); + long opt_index = config->Read(wxT("report/index"), 1L); long opt_fplist = config->Read(wxT("report/fplist"), 1L); delete config; PdfReport report; report.SetPage(format, (landscape != 0)); - report.SymbolOptions(opt_description != 0, opt_fplist != 0); + report.SymbolOptions(opt_description != 0, opt_index != 0, opt_fplist != 0); report.SetFont(fontsize); report.SymbolReport(this, library, symbols, reportfile); m_statusBar->SetStatusText(wxT("Finished report")); @@ -1360,7 +1364,7 @@ void libmngrFrame::OnAbout(wxCommandEvent& /*event*/) info.SetName(wxT("KiCad Librarian")); info.SetVersion(wxT(SVN_REVSTR)); info.SetDescription(description); - info.SetCopyright(wxT("(C) 2013-2017 ITB CompuPhase")); + info.SetCopyright(wxT("(C) 2013-2018 ITB CompuPhase")); info.SetIcon(icon); info.SetWebSite(wxT("http://www.compuphase.com/")); info.AddArtist(wxT("The logo of KiCad Librarian is designed by http://icons8.com/")); @@ -1711,6 +1715,7 @@ void libmngrFrame::OnDeletePart(wxCommandEvent& /*event*/) result = RemoveSymbol(filename, modname); else result = RemoveFootprint(filename, modname); + PartEdited = false; if (result) { PartData[0].Clear(); PartData[1].Clear(); /* should already be clear (delete operation is inactive in compare mode) */ @@ -2026,6 +2031,7 @@ void libmngrFrame::OnLeftModSelect(wxListEvent& event) m_radioViewRight->SetValue(false); UpdateDetails(0); Update3DModel(PartData[0]); + AutoZoom(0); m_panelView->Refresh(); } @@ -2049,6 +2055,7 @@ void libmngrFrame::OnRightModSelect(wxListEvent& event) m_radioViewRight->SetValue(true); UpdateDetails(CompareMode ? 1 : 0); Update3DModel(PartData[0]); + AutoZoom(0); m_panelView->Refresh(); } @@ -2183,17 +2190,8 @@ wxString libmngrFrame::ExportFootprintBitmap(const wxString& modname, bool blues int ImgWidth = IMG_WIDTH_FP; int ImgHeight = IMG_HEIGHT_FP; - double padsize = Footprint[0].PadSize[0].GetX() <= Footprint[0].PadSize[0].GetY() ? Footprint[0].PadSize[0].GetX() : Footprint[0].PadSize[0].GetY(); - double hsize = BodySize[0].BodyWidth; - double vsize = BodySize[0].BodyLength; - if (hsize < Footprint[0].Pitch + padsize) - hsize = Footprint[0].Pitch + padsize; - if (hsize < Footprint[0].SpanHor + padsize) - hsize = Footprint[0].SpanHor + padsize; - if (vsize < Footprint[0].Pitch + padsize) - vsize = Footprint[0].Pitch + padsize; - if (vsize < Footprint[0].SpanVer + padsize) - vsize = Footprint[0].SpanVer + padsize; + double hsize, vsize; + EstimateFootprintSize(&hsize, &vsize, Footprint[0], BodySize[0]); if (dpi == 0) { /* estimate scale from body size */ if (hsize > vsize) @@ -2301,6 +2299,31 @@ void libmngrFrame::DrawStrokeText(wxGraphicsContext *gc, float x, float y, const } } +void libmngrFrame::EstimateSymbolSize(double *cx, double *cy, const BodyInfo& Body) +{ + const int PINLENGTH = 6; /* assume pins are 6 mm long */ + const double SCALEFACTOR = 0.25; + wxASSERT(cx != NULL && cy != NULL); + *cx = (Body.BodyWidth + 2 * PINLENGTH) * SCALEFACTOR; + *cy = (Body.BodyLength + 2 * PINLENGTH) * SCALEFACTOR; +} + +void libmngrFrame::EstimateFootprintSize(double *cx, double *cy, const FootprintInfo& Footprint, const BodyInfo& Body) +{ + wxASSERT(cx != NULL && cy != NULL); + *cx = Body.BodyWidth; + *cy = Body.BodyLength; + double padsize = Footprint.PadSize[0].GetX() <= Footprint.PadSize[0].GetY() ? Footprint.PadSize[0].GetX() : Footprint.PadSize[0].GetY(); + if (*cx < Footprint.Pitch + padsize) + *cx = Footprint.Pitch + padsize; + if (*cx < Footprint.SpanHor + padsize) + *cx = Footprint.SpanHor + padsize; + if (*cy < Footprint.Pitch + padsize) + *cy = Footprint.Pitch + padsize; + if (*cy < Footprint.SpanVer + padsize) + *cy = Footprint.SpanVer + padsize; +} + void libmngrFrame::UpdateBoundingBox(CoordSize* bbox, double x, double y) { wxASSERT(bbox); @@ -2314,6 +2337,39 @@ void libmngrFrame::UpdateBoundingBox(CoordSize* bbox, double x, double y) bbox->SetBottom(y); } +void libmngrFrame::AutoZoom(int part) +{ + /* estimate footprint size */ + wxASSERT(part == 0 || part == 1); + double cx, cy; + if (SymbolMode) + EstimateSymbolSize(&cx, &cy, BodySize[part]); + else + EstimateFootprintSize(&cx, &cy, Footprint[part], BodySize[part]); + + /* get viewport size */ + wxSize size = m_panelView->GetSize(); + + /* check whether the shape is too large to fit in the viewport */ + if (cx * Scale > size.GetWidth() * 0.8) + Scale = size.GetWidth() * 0.8 / cx; + if (cy * Scale > size.GetHeight() * 0.8) + Scale = size.GetHeight() * 0.8 / cy; + + /* check whether the shape is very small in the viewport */ + if (cx * Scale < size.GetWidth() / 4 && cy * Scale < size.GetHeight() /4) { + Scale = size.GetWidth() * 0.8 / cx; + if (cy * Scale > size.GetHeight() * 0.8) + Scale = size.GetHeight() * 0.8 / cy; + } + + /* check the bounds */ + if (Scale > SCALE_MAX) + Scale = SCALE_MAX; + else if (Scale < SCALE_MIN) + Scale = SCALE_MIN; +} + void libmngrFrame::DrawSymbols(wxGraphicsContext *gc, int midx, int midy, const int transp[]) { #define DEFAULTPEN 1 @@ -2757,10 +2813,172 @@ void libmngrFrame::DrawSymbols(wxGraphicsContext *gc, int midx, int midy, const } } +void libmngrFrame::DrawPad(wxGraphicsContext *gc, double midx, double midy, + const wxString& padshape, const wxString& padpin, + double padx, double pady, double padwidth, double padheight, + double padrratio, double paddeltax, double paddeltay, + double pasteratio, double padrot, + double drillx, double drilly, double drillwidth, double drillheight, + const wxPen& penPad, const wxBrush& brushPad, const wxBrush& brushHole, + CoordSize *bbox) +{ + /* make the pad smaller in outline mode */ + if (OutlineMode) { + if (padwidth > 0.3) + padwidth -= 0.15; + if (padheight > 0.3) + padheight -= 0.15; + } + + /* draw the pad */ + wxPoint2DDouble points[5]; + points[0].m_x = -padwidth/2 * Scale; + points[0].m_y = -padheight/2 * Scale; + points[1].m_x = padwidth/2 * Scale; + points[1].m_y = -padheight/2 * Scale; + points[2].m_x = padwidth/2 * Scale; + points[2].m_y = padheight/2 * Scale; + points[3].m_x = -padwidth/2 * Scale; + points[3].m_y = padheight/2 * Scale; + if (padshape.CmpNoCase(wxT("T")) == 0 || padshape.Cmp(wxT("trapezoid")) == 0) { + if (!Equal(paddeltax, 0.0)) { + points[0].m_y -= paddeltax * Scale / 2; + points[1].m_y += paddeltax * Scale / 2; + points[2].m_y -= paddeltax * Scale / 2; + points[3].m_y += paddeltax * Scale / 2; + } + if (!Equal(paddeltay, 0.0)) { + points[0].m_x += paddeltay * Scale / 2; + points[1].m_x -= paddeltay * Scale / 2; + points[2].m_x += paddeltay * Scale / 2; + points[3].m_x -= paddeltay * Scale / 2; + } + } + points[4] = points[0]; + + /* make scaled pad for paste */ + wxPoint2DDouble pastepoints[5]; + memcpy(pastepoints, points, sizeof pastepoints); + if (pasteratio < -EPSILON && pasteratio > -0.5 && !OutlineMode) { + double d; + d = points[1].m_x - points[0].m_x; + pastepoints[0].m_x = points[0].m_x - d * pasteratio; + pastepoints[1].m_x = points[1].m_x + d * pasteratio; + d = points[2].m_x - points[3].m_x; + pastepoints[2].m_x = points[2].m_x + d * pasteratio; + pastepoints[3].m_x = points[3].m_x - d * pasteratio; + d = points[3].m_y - points[0].m_y; + pastepoints[0].m_y = points[0].m_y - d * pasteratio; + pastepoints[3].m_y = points[3].m_y + d * pasteratio; + d = points[2].m_y - points[1].m_y; + pastepoints[1].m_y = points[1].m_y - d * pasteratio; + pastepoints[2].m_y = points[2].m_y + d * pasteratio; + pastepoints[4] = pastepoints[0]; + } + + /* apply rotation */ + if (padrot > EPSILON) { + double angle = (padrot * M_PI / 180.0); + for (int idx = 0; idx < 5; idx++) { + wxDouble nx = points[idx].m_x * cos(angle) - points[idx].m_y * sin(angle); + wxDouble ny = points[idx].m_x * sin(angle) + points[idx].m_y * cos(angle); + points[idx].m_x = nx; + points[idx].m_y = ny; + /* same for paste aperture */ + nx = pastepoints[idx].m_x * cos(angle) - pastepoints[idx].m_y * sin(angle); + ny = pastepoints[idx].m_x * sin(angle) + pastepoints[idx].m_y * cos(angle); + pastepoints[idx].m_x = nx; + pastepoints[idx].m_y = ny; + } + } + + /* move pad relative to footprint origin */ + for (int idx = 0; idx < 5; idx++) { + points[idx].m_x += padx * Scale + midx; + points[idx].m_y += pady * Scale + midy; + UpdateBoundingBox(bbox, padx, pady); + pastepoints[idx].m_x += padx * Scale + midx; + pastepoints[idx].m_y += pady * Scale + midy; + } + + gc->SetBrush(brushPad); + gc->SetPen(penPad); + + /* avoid negative width/height for ellipses or obrounds */ + CoordSize cs(points[0].m_x, points[0].m_y, points[2].m_x - points[0].m_x, points[2].m_y - points[0].m_y); + if (padshape.CmpNoCase(wxT("C")) == 0 || padshape.Cmp(wxT("circle")) == 0) { + gc->DrawEllipse(cs.GetX(), cs.GetY(), cs.GetWidth(), cs.GetHeight()); + } else if (padshape.CmpNoCase(wxT("O")) == 0 || padshape.Cmp(wxT("oval")) == 0) { + double dim = (cs.GetWidth() < cs.GetHeight()) ? cs.GetWidth() : cs.GetHeight(); + gc->DrawRoundedRectangle(cs.GetX(), cs.GetY(), cs.GetWidth(), cs.GetHeight(), dim / 2); + } else if (padshape.CmpNoCase(wxT("D")) == 0 || padshape.Cmp(wxT("roundrect")) == 0) { + wxASSERT(padrratio >= 0 && padrratio <= 0.5); + double dim = (cs.GetWidth() < cs.GetHeight()) ? cs.GetWidth() : cs.GetHeight(); + gc->DrawRoundedRectangle(cs.GetX(), cs.GetY(), cs.GetWidth(), cs.GetHeight(), dim * padrratio); + } else { + gc->DrawLines(5, points); + } + + /* draw solder paste ratio, if set */ + if (pasteratio < -EPSILON && pasteratio > -0.5 && !OutlineMode) { + wxPen pastepen(wxColour(160,160,80), 1, wxPENSTYLE_DOT); + gc->SetPen(pastepen); + wxBrush pastebrush(wxColour(160,160,80), wxBRUSHSTYLE_FDIAGONAL_HATCH); + gc->SetBrush(pastebrush); + /* avoid negative width/height for ellipses or obrounds */ + cs.Set(pastepoints[0].m_x, pastepoints[0].m_y, pastepoints[2].m_x - pastepoints[0].m_x, pastepoints[2].m_y - pastepoints[0].m_y); + if (padshape.CmpNoCase(wxT("C")) == 0 || padshape.Cmp(wxT("circle")) == 0) { + gc->DrawEllipse(cs.GetX(), cs.GetY(), cs.GetWidth(), cs.GetHeight()); + } else if (padshape.CmpNoCase(wxT("O")) == 0 || padshape.Cmp(wxT("oval")) == 0) { + double dim = (cs.GetWidth() < cs.GetHeight()) ? cs.GetWidth() : cs.GetHeight(); + gc->DrawRoundedRectangle(cs.GetX(), cs.GetY(), cs.GetWidth(), cs.GetHeight(), dim / 2); + } else if (padshape.CmpNoCase(wxT("D")) == 0 || padshape.Cmp(wxT("roundrect")) == 0) { + wxASSERT(padrratio >= 0 && padrratio <= 0.5); + double dim = (cs.GetWidth() < cs.GetHeight()) ? cs.GetWidth() : cs.GetHeight(); + gc->DrawRoundedRectangle(cs.GetX(), cs.GetY(), cs.GetWidth(), cs.GetHeight(), dim * padrratio); + } else { + gc->DrawLines(5, pastepoints); + } + gc->SetPen(penPad); + gc->SetBrush(brushPad); + } + + /* optionally the hole in the pad */ + if (drillwidth > EPSILON) { + if ((padshape == wxT('C') || padshape.Cmp(wxT("circle")) == 0) && padwidth - drillwidth < 0.05) + gc->SetBrush(brushHole); + else + gc->SetBrush(*wxBLACK_BRUSH); + if (drillheight > EPSILON) { + if ((padrot > 45 && padrot < 135) || (padrot > 225 && padrot < 315)) { + double t = drillwidth; + drillwidth = drillheight; + drillheight = t; + } + cs.Set((padx + drillx - drillwidth/2) * Scale + midx, + (pady + drilly - drillheight/2) * Scale + midy, + drillwidth * Scale, drillheight * Scale); + gc->DrawRoundedRectangle(cs.GetX(), cs.GetY(), cs.GetWidth(), cs.GetHeight(), + ((cs.GetWidth() < cs.GetHeight()) ? cs.GetWidth() : cs.GetHeight()) / 2); + } else { + gc->DrawEllipse((padx + drillx - drillwidth/2) * Scale + midx, + (pady + drilly - drillwidth/2) * Scale + midy, + drillwidth * Scale, drillwidth * Scale); + } + } + + /* draw the pin name inside the pad */ + if (ShowPinNumbers) { + wxDouble tw, th, td, tex; + gc->GetTextExtent(padpin, &tw, &th, &td, &tex); + gc->DrawText(padpin, padx * Scale + midx - tw/2, pady * Scale + midy - th/2); + } +} + void libmngrFrame::DrawFootprints(wxGraphicsContext *gc, int midx, int midy, const int transp[]) { CoordSize bbox; - wxColour clrBody, clrPad, clrPadFill, clrText, clrHiddenText; + wxColour clrBody, clrText, clrHiddenText, clrPad, clrPadFill, clrPadRev, clrPadRevFill; wxPoint2DDouble points[5]; wxPen pen; @@ -2789,12 +3007,16 @@ void libmngrFrame::DrawFootprints(wxGraphicsContext *gc, int midx, int midy, con clrBody.Set(192, 192, 96, transp[fp]); clrPad.Set(160, 0, 0, transp[fp]); clrPadFill.Set(160, 48, 48, transp[fp]); + clrPadRev.Set(160, 0, 128, transp[fp]); + clrPadRevFill.Set(160, 48, 128, transp[fp]); clrText.Set(240, 240, 64, transp[fp]); clrHiddenText.Set(160, 160, 50, transp[fp]); } else { clrBody.Set(192, 96, 192, transp[fp]); clrPad.Set(0, 160, 0, transp[fp]); clrPadFill.Set(48, 160, 48, transp[fp]); + clrPadRev.Set(128, 160, 0, transp[fp]); + clrPadRevFill.Set(128, 160, 48, transp[fp]); clrText.Set(240, 64, 240, transp[fp]); clrHiddenText.Set(160, 50, 160, transp[fp]); } @@ -3131,20 +3353,27 @@ void libmngrFrame::DrawFootprints(wxGraphicsContext *gc, int midx, int midy, con } /* draw the pads */ - wxBrush brush; - wxPen pen; + wxBrush brushStd, brushRev; + wxPen penStd, penRev; if (OutlineMode) { - brush = *wxTRANSPARENT_BRUSH; - pen.SetColour(clrPad); - pen.SetWidth(0.2 * Scale); + brushStd = *wxTRANSPARENT_BRUSH; + brushRev = *wxTRANSPARENT_BRUSH; + penStd.SetColour(clrPad); + penStd.SetWidth(0.2 * Scale); + penRev.SetColour(clrPadRev); + penRev.SetWidth(0.2 * Scale); } else { - brush.SetColour(clrPadFill); - brush.SetStyle(wxBRUSHSTYLE_SOLID); - pen.SetColour(clrPad); - pen.SetWidth(1); + brushStd.SetColour(clrPadFill); + brushStd.SetStyle(wxBRUSHSTYLE_SOLID); + brushRev.SetColour(clrPadRevFill); + brushRev.SetStyle(wxBRUSHSTYLE_SOLID); + penStd.SetColour(clrPad); + penStd.SetWidth(1); + penRev.SetColour(clrPadRev); + penRev.SetWidth(1); } - gc->SetBrush(brush); - gc->SetPen(pen); + gc->SetBrush(brushStd); + gc->SetPen(penStd); wxBrush brushHole; brushHole.SetColour(wxColour(224, 224, 0, transp[fp])); @@ -3158,6 +3387,8 @@ void libmngrFrame::DrawFootprints(wxGraphicsContext *gc, int midx, int midy, con double paddeltax = 0, paddeltay = 0; double drillx = 0, drilly = 0, drillwidth = 0, drillheight = 0; double pasteratio = 0.0; + double padrratio = 0.5; + bool padsmd = false, padbottomside = false; wxPoint2DDouble pastepoints[5]; wxString padpin, padshape; for (int idx = 0; idx < (int)PartData[fp].Count(); idx++) { @@ -3168,143 +3399,21 @@ void libmngrFrame::DrawFootprints(wxGraphicsContext *gc, int midx, int midy, con drillwidth = drillheight = 0; pasteratio = 0.0; } else if (line.CmpNoCase(wxT("$EndPAD")) == 0) { - /* make the pad smaller in outline mode */ - if (OutlineMode) { - if (padwidth > 0.3) - padwidth -= 0.15; - if (padheight > 0.3) - padheight -= 0.15; - } - /* draw the pad */ - points[0].m_x = -padwidth/2 * Scale; - points[0].m_y = -padheight/2 * Scale; - points[1].m_x = padwidth/2 * Scale; - points[1].m_y = -padheight/2 * Scale; - points[2].m_x = padwidth/2 * Scale; - points[2].m_y = padheight/2 * Scale; - points[3].m_x = -padwidth/2 * Scale; - points[3].m_y = padheight/2 * Scale; - points[4] = points[0]; - if (padshape.CmpNoCase(wxT("T")) == 0) { - if (!Equal(paddeltax, 0.0)) { - points[0].m_y -= paddeltax * Scale / 2; - points[1].m_y += paddeltax * Scale / 2; - points[2].m_y -= paddeltax * Scale / 2; - points[3].m_y += paddeltax * Scale / 2; - } - if (!Equal(paddeltay, 0.0)) { - points[0].m_x += paddeltay * Scale / 2; - points[1].m_x -= paddeltay * Scale / 2; - points[2].m_x += paddeltay * Scale / 2; - points[3].m_x -= paddeltay * Scale / 2; - } - } - points[4] = points[0]; - /* make scaled pad for paste */ - memcpy(pastepoints, points, sizeof pastepoints); - if (pasteratio < -EPSILON && pasteratio > -0.5 && !OutlineMode) { - double d; - d = points[1].m_x - points[0].m_x; - pastepoints[0].m_x = points[0].m_x - d * pasteratio; - pastepoints[1].m_x = points[1].m_x + d * pasteratio; - d = points[2].m_x - points[3].m_x; - pastepoints[2].m_x = points[2].m_x + d * pasteratio; - pastepoints[3].m_x = points[3].m_x - d * pasteratio; - d = points[3].m_y - points[0].m_y; - pastepoints[0].m_y = points[0].m_y - d * pasteratio; - pastepoints[3].m_y = points[3].m_y + d * pasteratio; - d = points[2].m_y - points[1].m_y; - pastepoints[1].m_y = points[1].m_y - d * pasteratio; - pastepoints[2].m_y = points[2].m_y + d * pasteratio; - pastepoints[4] = pastepoints[0]; - } - /* apply rotation */ - if (padrot > EPSILON) { - double angle = (padrot * M_PI / 180.0); - for (int idx = 0; idx < 5; idx++) { - wxDouble nx = points[idx].m_x * cos(angle) - points[idx].m_y * sin(angle); - wxDouble ny = points[idx].m_x * sin(angle) + points[idx].m_y * cos(angle); - points[idx].m_x = nx; - points[idx].m_y = ny; - /* same for paste aperture */ - nx = pastepoints[idx].m_x * cos(angle) - pastepoints[idx].m_y * sin(angle); - ny = pastepoints[idx].m_x * sin(angle) + pastepoints[idx].m_y * cos(angle); - pastepoints[idx].m_x = nx; - pastepoints[idx].m_y = ny; - } - } - /* move pad relative to footprint origin */ - for (int idx = 0; idx < 5; idx++) { - points[idx].m_x += padx * Scale + midx; - points[idx].m_y += pady * Scale + midy; - UpdateBoundingBox(&bbox, padx, pady); - pastepoints[idx].m_x += padx * Scale + midx; - pastepoints[idx].m_y += pady * Scale + midy; - } - gc->SetBrush(brush); - /* avoid negative width/height for ellipses or obrounds */ - CoordSize cs(points[0].m_x, points[0].m_y, points[2].m_x - points[0].m_x, points[2].m_y - points[0].m_y); - if (padshape.CmpNoCase(wxT("C")) == 0) - gc->DrawEllipse(cs.GetX(), cs.GetY(), cs.GetWidth(), cs.GetHeight()); - else if (padshape.CmpNoCase(wxT("O")) == 0) - gc->DrawRoundedRectangle(cs.GetX(), cs.GetY(), cs.GetWidth(), cs.GetHeight(), - ((cs.GetWidth() < cs.GetHeight()) ? cs.GetWidth() : cs.GetHeight()) / 2); - else - gc->DrawLines(5, points); - /* draw solder paste ratio, if set */ - if (pasteratio < -EPSILON && pasteratio > -0.5 && !OutlineMode) { - wxPen pastepen(wxColour(160,160,80), 1, wxPENSTYLE_DOT); - gc->SetPen(pastepen); - wxBrush pastebrush(wxColour(160,160,80), wxBRUSHSTYLE_FDIAGONAL_HATCH); - gc->SetBrush(pastebrush); - /* avoid negative width/height for ellipses or obrounds */ - cs.Set(pastepoints[0].m_x, pastepoints[0].m_y, pastepoints[2].m_x - pastepoints[0].m_x, pastepoints[2].m_y - pastepoints[0].m_y); - if (padshape.CmpNoCase(wxT("C")) == 0) - gc->DrawEllipse(cs.GetX(), cs.GetY(), cs.GetWidth(), cs.GetHeight()); - else if (padshape.CmpNoCase(wxT("O")) == 0) - gc->DrawRoundedRectangle(cs.GetX(), cs.GetY(), cs.GetWidth(), cs.GetHeight(), - ((cs.GetWidth() < cs.GetHeight()) ? cs.GetWidth() : cs.GetHeight()) / 2); - else - gc->DrawLines(5, pastepoints); - gc->SetPen(pen); - gc->SetBrush(brush); - } - /* optionally the hole in the pad */ - if (drillwidth > EPSILON) { - if (padshape == wxT('C') && padwidth - drillwidth < 0.05) - gc->SetBrush(brushHole); - else - gc->SetBrush(*wxBLACK_BRUSH); - if (drillheight > EPSILON) { - if ((padrot > 45 && padrot < 135) || (padrot > 225 && padrot < 315)) { - double t = drillwidth; - drillwidth = drillheight; - drillheight = t; - } - cs.Set((padx + drillx - drillwidth/2) * Scale + midx, - (pady + drilly - drillheight/2) * Scale + midy, - drillwidth * Scale, drillheight * Scale); - gc->DrawRoundedRectangle(cs.GetX(), cs.GetY(), cs.GetWidth(), cs.GetHeight(), - ((cs.GetWidth() < cs.GetHeight()) ? cs.GetWidth() : cs.GetHeight()) / 2); - } else { - gc->DrawEllipse((padx + drillx - drillwidth/2) * Scale + midx, - (pady + drilly - drillwidth/2) * Scale + midy, - drillwidth * Scale, drillwidth * Scale); - } - } - /* draw the pin name inside the pad */ - if (ShowPinNumbers) { - wxDouble tw, th, td, tex; - gc->GetTextExtent(padpin, &tw, &th, &td, &tex); - gc->DrawText(padpin, padx * Scale + midx - tw/2, pady * Scale + midy - th/2); - } + wxBrush *brushPad = padbottomside ? &brushRev : &brushStd; + wxPen *penPad = padbottomside ? &penRev : &penStd; + DrawPad(gc, midx, midy, padshape, padpin, + padx, pady, padwidth, padheight, padrratio, + paddeltax, paddeltay, pasteratio, padrot, + drillx, drilly, drillwidth, drillheight, + *penPad, *brushPad, brushHole, &bbox); inpad = false; } continue; } else if (line[0] == wxT('(') && line.Left(4).Cmp(wxT("(pad")) == 0) { GetToken(&line); /* ignore "(pad" */ padpin = GetToken(&line); - GetToken(&line); /* ignore smd/thru_hole/np_thru_hole type */ + wxString tmp = GetToken(&line); + padsmd = (tmp.CmpNoCase(wxT("smd")) == 0); padshape = GetToken(&line); padrot = 0; /* preset pad rotation (frequently omitted) */ wxString section = GetSection(line, wxT("at")); @@ -3341,138 +3450,32 @@ void libmngrFrame::DrawFootprints(wxGraphicsContext *gc, int midx, int midy, con pasteratio = GetTokenDouble(§ion); else pasteratio = 0.0; - if (OutlineMode) { - if (padwidth > 0.3) - padwidth -= 0.15; - if (padheight > 0.3) - padheight -= 0.15; + if (padsmd) { + /* for SMD pads, check the side (for through-hole, don't + care because these go through all sides) */ + section = GetSection(line, wxT("layers")); + if (section.length() > 0 && section.Find(wxT("B.Cu")) >= 0 && section.Find(wxT("F.Cu")) < 0) + padbottomside = true; } - /* draw the pad */ - points[0].m_x = -padwidth/2 * Scale; - points[0].m_y = -padheight/2 * Scale; - points[1].m_x = padwidth/2 * Scale; - points[1].m_y = -padheight/2 * Scale; - points[2].m_x = padwidth/2 * Scale; - points[2].m_y = padheight/2 * Scale; - points[3].m_x = -padwidth/2 * Scale; - points[3].m_y = padheight/2 * Scale; if (padshape.Cmp(wxT("trapezoid")) == 0) { section = GetSection(line, wxT("rect_delta")); if (section.length() > 0) { paddeltax = GetTokenDim(§ion, true); paddeltay = GetTokenDim(§ion, true); - if (!Equal(paddeltax, 0.0)) { - points[0].m_y -= paddeltax * Scale / 2; - points[1].m_y += paddeltax * Scale / 2; - points[2].m_y -= paddeltax * Scale / 2; - points[3].m_y += paddeltax * Scale / 2; - } - if (!Equal(paddeltay, 0.0)) { - points[0].m_x += paddeltay * Scale / 2; - points[1].m_x -= paddeltay * Scale / 2; - points[2].m_x += paddeltay * Scale / 2; - points[3].m_x -= paddeltay * Scale / 2; - } - } - } - points[4] = points[0]; - /* make scaled pad for paste */ - memcpy(pastepoints, points, sizeof pastepoints); - if (pasteratio < -EPSILON && pasteratio > -0.5 && !OutlineMode) { - double d; - d = points[1].m_x - points[0].m_x; - pastepoints[0].m_x = points[0].m_x - d * pasteratio; - pastepoints[1].m_x = points[1].m_x + d * pasteratio; - d = points[2].m_x - points[3].m_x; - pastepoints[2].m_x = points[2].m_x + d * pasteratio; - pastepoints[3].m_x = points[3].m_x - d * pasteratio; - d = points[3].m_y - points[0].m_y; - pastepoints[0].m_y = points[0].m_y - d * pasteratio; - pastepoints[3].m_y = points[3].m_y + d * pasteratio; - d = points[2].m_y - points[1].m_y; - pastepoints[1].m_y = points[1].m_y - d * pasteratio; - pastepoints[2].m_y = points[2].m_y + d * pasteratio; - pastepoints[4] = pastepoints[0]; - } - /* apply rotation */ - if (padrot != 0) { - double angle = (padrot * M_PI / 180.0); - for (int idx = 0; idx < 5; idx++) { - wxDouble nx = points[idx].m_x * cos(angle) - points[idx].m_y * sin(angle); - wxDouble ny = points[idx].m_x * sin(angle) + points[idx].m_y * cos(angle); - points[idx].m_x = nx; - points[idx].m_y = ny; - /* same for paste aperture */ - nx = pastepoints[idx].m_x * cos(angle) - pastepoints[idx].m_y * sin(angle); - ny = pastepoints[idx].m_x * sin(angle) + pastepoints[idx].m_y * cos(angle); - pastepoints[idx].m_x = nx; - pastepoints[idx].m_y = ny; - } - } - /* move pad relative to footprint origin */ - for (int idx = 0; idx < 5; idx++) { - points[idx].m_x += padx * Scale + midx; - points[idx].m_y += pady * Scale + midy; - UpdateBoundingBox(&bbox, padx, pady); - pastepoints[idx].m_x += padx * Scale + midx; - pastepoints[idx].m_y += pady * Scale + midy; - } - gc->SetBrush(brush); - CoordSize cs(points[0].m_x, points[0].m_y, points[2].m_x - points[0].m_x, points[2].m_y - points[0].m_y); - if (padshape.Cmp(wxT("circle")) == 0) - gc->DrawEllipse(cs.GetX(), cs.GetY(), cs.GetWidth(), cs.GetHeight()); - else if (padshape.Cmp(wxT("oval")) == 0) - gc->DrawRoundedRectangle(cs.GetX(), cs.GetY(), cs.GetWidth(), cs.GetHeight(), - ((cs.GetWidth() < cs.GetHeight()) ? cs.GetWidth() : cs.GetHeight()) / 2); - else - gc->DrawLines(5, points); - /* draw solder paste ratio, if set */ - if (pasteratio < -EPSILON && pasteratio > -0.5 && !OutlineMode) { - wxPen pastepen(wxColour(160,160,80), 1, wxPENSTYLE_DOT); - gc->SetPen(pastepen); - wxBrush pastebrush(wxColour(160,160,80), wxBRUSHSTYLE_FDIAGONAL_HATCH); - gc->SetBrush(pastebrush); - /* avoid negative width/height for ellipses or obrounds */ - cs.Set(pastepoints[0].m_x, pastepoints[0].m_y, pastepoints[2].m_x - pastepoints[0].m_x, pastepoints[2].m_y - pastepoints[0].m_y); - if (padshape.CmpNoCase(wxT("C")) == 0) - gc->DrawEllipse(cs.GetX(), cs.GetY(), cs.GetWidth(), cs.GetHeight()); - else if (padshape.CmpNoCase(wxT("O")) == 0) - gc->DrawRoundedRectangle(cs.GetX(), cs.GetY(), cs.GetWidth(), cs.GetHeight(), - ((cs.GetWidth() < cs.GetHeight()) ? cs.GetWidth() : cs.GetHeight()) / 2); - else - gc->DrawLines(5, pastepoints); - gc->SetPen(pen); - gc->SetBrush(brush); - } - /* optionally the hole in the pad */ - if (drillwidth > EPSILON) { - if (padshape.Cmp(wxT("circle")) == 0 && padwidth - drillwidth < 0.05) - gc->SetBrush(brushHole); - else - gc->SetBrush(*wxBLACK_BRUSH); - if (drillheight > EPSILON) { - if ((padrot > 45 && padrot < 135) || (padrot > 225 && padrot < 315)) { - double t = drillwidth; - drillwidth = drillheight; - drillheight = t; - } - cs.Set((padx + drillx - drillwidth/2) * Scale + midx, - (pady + drilly - drillheight/2) * Scale + midy, - drillwidth * Scale, drillheight * Scale); - gc->DrawRoundedRectangle(cs.GetX(), cs.GetY(), cs.GetWidth(), cs.GetHeight(), - ((cs.GetWidth() < cs.GetHeight()) ? cs.GetWidth() : cs.GetHeight()) / 2); - } else { - gc->DrawEllipse((padx + drillx - drillwidth/2) * Scale + midx, - (pady + drilly -drillwidth/2) * Scale + midy, - drillwidth * Scale, drillwidth * Scale); } + } else if (padshape.Cmp(wxT("roundrect")) == 0) { + section = GetSection(line, wxT("roundrect_rratio")); + if (section.length() > 0) + padrratio = GetTokenDouble(§ion); } - /* draw the pin name inside the pad */ - if (ShowPinNumbers) { - wxDouble tw, th, td, tex; - gc->GetTextExtent(padpin, &tw, &th, &td, &tex); - gc->DrawText(padpin, padx * Scale + midx - tw/2, pady * Scale + midy - th/2); - } + /* draw the pad */ + wxBrush *brushPad = padbottomside ? &brushRev : &brushStd; + wxPen *penPad = padbottomside ? &penRev : &penStd; + DrawPad(gc, midx, midy, padshape, padpin, + padx, pady, padwidth, padheight, padrratio, + paddeltax, paddeltay, pasteratio, padrot, + drillx, drilly, drillwidth, drillheight, + *penPad, *brushPad, brushHole, &bbox); continue; } if (!inpad) @@ -3489,6 +3492,17 @@ void libmngrFrame::DrawFootprints(wxGraphicsContext *gc, int midx, int midy, con paddeltax = GetTokenDim(&line, unit_mm); paddeltay = GetTokenDim(&line, unit_mm); padrot = NormalizeAngle(GetTokenLong(&line) / 10.0 - module_angle); + padrratio = (line.Length() > 0) ? GetTokenDouble(&line) : 0; + if (padshape == 'R' && padrratio > EPSILON) + padshape = 'D'; + } else if (token.CmpNoCase(wxT("At")) == 0) { + token = GetToken(&line); + padsmd = (token.CmpNoCase(wxT("SMD")) == 0); + GetToken(&line); /* ignore legacy field */ + token = GetToken(&line); + long mask; + if (token.ToLong(&mask, 16) && (mask & 0xffff) == 1 && padsmd) + padbottomside = true; } else if (token.CmpNoCase(wxT("Dr")) == 0) { drillwidth = GetTokenDim(&line, unit_mm); drillx = GetTokenDim(&line, unit_mm); /* this is relative to the pad position */ @@ -3884,7 +3898,7 @@ void libmngrFrame::OnSizeViewport(wxSizeEvent& /*event*/) void libmngrFrame::OnZoomIn(wxCommandEvent& /*event*/) { if (Scale < SCALE_MAX) { - Scale *= 1.1892; + Scale *= SCALE_FACTOR; if (ModelMode) ResizeModelViewport(); m_panelView->Refresh(); @@ -3896,7 +3910,7 @@ void libmngrFrame::OnZoomIn(wxCommandEvent& /*event*/) void libmngrFrame::OnZoomOut(wxCommandEvent& /*event*/) { if (Scale > SCALE_MIN) { - Scale /= 1.1892; + Scale /= SCALE_FACTOR; if (ModelMode) ResizeModelViewport(); m_panelView->Refresh(); @@ -4182,18 +4196,39 @@ void libmngrFrame::CollectLibraries(const wxString &path, wxArrayString *list) } } +class wxDirTraverserTree : public wxDirTraverser +{ +public: + wxDirTraverserTree(wxArrayString* pathlist) : m_pathlist(pathlist) { } + + virtual wxDirTraverseResult OnFile(const wxString& /*filename*/) wxOVERRIDE { + return wxDIR_CONTINUE; + } + virtual wxDirTraverseResult OnDir(const wxString& dirname) wxOVERRIDE { + m_pathlist->Add(dirname); + return wxDIR_CONTINUE; + } +private: + wxArrayString* m_pathlist; + wxDECLARE_NO_COPY_CLASS(wxDirTraverserTree); +}; + void libmngrFrame::CollectAllLibraries(bool eraselists) { #if defined _MSC_VER _CrtCheckMemory(); #endif - wxArrayString list; - wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); + + bool recurse = false; + config->Read(wxT("path/recurse"), &recurse); + + /* get the list of paths (straightforward without recursion) */ + wxArrayString pathlist; wxString path; wxString key; - int idx = 1; + unsigned idx = 1; for ( ;; ) { if (SymbolMode) key = wxString::Format(wxT("paths/symbols%d"), idx); @@ -4201,31 +4236,43 @@ void libmngrFrame::CollectAllLibraries(bool eraselists) key = wxString::Format(wxT("paths/footprints%d"), idx); if (!config->Read(key, &path)) break; - CollectLibraries(path, &list); + pathlist.Add(path); + if (recurse) { + wxDir dir(path); + if (dir.IsOpened()) { + wxDirTraverserTree traverser(&pathlist); + dir.Traverse(traverser, wxEmptyString, wxDIR_DIRS); + } + } idx++; } delete config; - list.Sort(CompareStringNoCase); + /* get the library list */ + wxArrayString liblist; + for (idx = 0; idx < pathlist.Count(); idx++) + CollectLibraries(pathlist[idx], &liblist); + + liblist.Sort(CompareStringNoCase); m_choiceModuleLeft->Clear(); m_choiceModuleLeft->Append(LIB_NONE); - if (list.Count() > 0) + if (liblist.Count() > 0) m_choiceModuleLeft->Append(LIB_ALL); #if !defined NO_CURL m_choiceModuleLeft->Append(LIB_REPOS); #endif - if (list.Count() > 0) - m_choiceModuleLeft->Append(list); + if (liblist.Count() > 0) + m_choiceModuleLeft->Append(liblist); m_choiceModuleLeft->SetSelection(0); m_choiceModuleRight->Clear(); m_choiceModuleRight->Append(LIB_NONE); - if (list.Count() > 0) + if (liblist.Count() > 0) m_choiceModuleRight->Append(LIB_ALL); #if !defined NO_CURL m_choiceModuleRight->Append(LIB_REPOS); #endif - if (list.Count() > 0) - m_choiceModuleRight->Append(list); + if (liblist.Count() > 0) + m_choiceModuleRight->Append(liblist); m_choiceModuleRight->SetSelection(0); if (eraselists) { @@ -4457,12 +4504,14 @@ void libmngrFrame::HandleLibrarySelect(wxChoice* choice, wxListCtrl* list, int s LoadPart(SelectedPartLeft, m_listModulesLeft, m_choiceModuleLeft, 0); UpdateDetails(0); Update3DModel(PartData[0]); + AutoZoom(0); m_panelView->Refresh(); } else if (side == LEFTPANEL && SelectedPartRight >= 0) { LoadPart(SelectedPartRight, m_listModulesRight, m_choiceModuleRight, CompareMode ? 1 : 0); UpdateDetails(CompareMode ? 1 : 0); if (!CompareMode) Update3DModel(PartData[0]); + AutoZoom(0); m_panelView->Refresh(); } } @@ -5690,6 +5739,16 @@ void libmngrFrame::ChangePadInfo(wxControl* ctrl) adjusted.PadShape = 'S'; else if (field.CmpNoCase(wxT("Trapezoid")) == 0) adjusted.PadShape = 'T'; + else if (field.CmpNoCase(wxT("Rounded rectangle")) == 0) + adjusted.PadShape = 'D'; + + /* some fields are disabled depending on the pad shape */ + SetTextField(m_txtPadLength, m_txtPadLength->GetValue(), (adjusted.PadShape == 'C' || adjusted.PadShape == 'S') ? PROTECTED : ENABLED); + field = m_txtPadRadius->GetValue(); + if (adjusted.PadShape == 'D' && field.Length() == 0) + SetTextField(m_txtPadRadius, wxT("25"), CHANGED); + else + SetTextField(m_txtPadRadius, field, (adjusted.PadShape == 'D') ? ENABLED : PROTECTED); /* for circular and square+round pads, the width & length should be the same */ if (adjusted.PadShape == 'C' || adjusted.PadShape == 'S') { @@ -5715,6 +5774,16 @@ void libmngrFrame::ChangePadInfo(wxControl* ctrl) dim = adjusted.PadSize[0].GetY(); adjusted.PadSize[0].Set(dim, dim); } + /* if the pad is rounded rectangle, read the relevant field; otherwise + force the rounding to be zero */ + if (adjusted.PadShape == 'D') { + long percent; + field = m_txtPadRadius->GetValue(); + if (field.length() > 0 && field.ToLong(&percent)) + adjusted.PadRRatio = percent; + } else { + adjusted.PadRRatio = 0; + } field = m_txtAuxPadWidth->GetValue(); if (field.length() > 0 && field.ToDouble(&dim) && dim > 0.02) @@ -6187,6 +6256,13 @@ bool libmngrFrame::CacheMetadata(const wxString& libname, const wxString& symnam data += wxString::Format(wxT("pad%d=%f %f\n"), pinnr + 1, pad.GetMidX(), pad.GetMidY()); } + CoordPair stdpad = footprint.PadSize[0]; + if (stdpad.GetX() > EPSILON) { + if (footprint.PadRightAngle[0]) + stdpad.Set(stdpad.GetY(), stdpad.GetX()); + data += wxString::Format(wxT("padsize=%f %f\n"), stdpad.GetX(), stdpad.GetY()); + } + if (xmax - xmin > EPSILON && ymax - ymin > EPSILON) data += wxString::Format(wxT("courtyard=%f %f\n"), xmax - xmin, ymax - ymin); @@ -6381,6 +6457,14 @@ void libmngrFrame::OnRevertPart(wxCommandEvent& /*event*/) m_statusBar->SetStatusText(wxT("Changes reverted")); } +void libmngrFrame::SetTextField(wxTextCtrl* ctrl, const wxString& value, const wxColour& status) +{ + wxASSERT(ctrl != NULL); + ctrl->SetValue(value); + ctrl->SetEditable(status != PROTECTED); + ctrl->SetBackgroundColour(status); +} + bool libmngrFrame::CheckTemplateVar(const wxString& varname) { wxASSERT(PartData[0].Count() > 0); @@ -6410,6 +6494,13 @@ bool libmngrFrame::SetVarDefaults(RPNexpression *rpn, const wxString& templatena if (err != RPN_EMPTY && !silent) wxMessageBox(wxT("The '#param' line in the template has an error.")); } + /* copy PSH and PRR in #param to $PSH and $PRR */ + rpn->Set("PSH"); + const char* shape = (rpn->Parse() == RPN_OK) ? rpn->Value().Text() : ""; + rpn->SetVariable(RPNvariable("$PSH", shape)); + rpn->Set("PRR"); + double rratio = (rpn->Parse() == RPN_OK) ? rpn->Value().Double() : 0.25; + rpn->SetVariable(RPNvariable("$PRR", rratio)); /* then set the defaults from the user settings (possibly overriding those of the #param line) */ @@ -6472,12 +6563,37 @@ bool libmngrFrame::SetVarsFromFields(RPNexpression *rpn, bool SymbolMode) } if (!SymbolMode) { + val = m_choicePadShape->GetSelection(); + if (val < 0) + val = 0; + wxString shape = m_choicePadShape->GetString(val); + if (shape.CmpNoCase(wxT("Round")) == 0) + shape = wxT("circle"); + else if (shape.CmpNoCase(wxT("Obround")) == 0) + shape = wxT("oval"); + else if (shape.CmpNoCase(wxT("Rectangular")) == 0) + shape = wxT("rect"); + else if (shape.CmpNoCase(wxT("Round + square")) == 0) + shape = wxT("sqcircle"); + else if (shape.CmpNoCase(wxT("Trapezoid")) == 0) + shape = wxT("trapezoid"); + else if (shape.CmpNoCase(wxT("Rounded rectangle")) == 0) + shape = wxT("roundrect"); + rpn->SetVariable(RPNvariable("$PSH", shape)); + field = m_txtPadRadius->GetValue(); + if (field.length() > 0 && field.ToLong(&val)) + rpn->SetVariable(RPNvariable("$PRR", val / 100.0)); field = m_txtPadWidth->GetValue(); if (field.length() > 0 && field.ToDouble(&dim) && dim > 0.02) rpn->SetVariable(RPNvariable("PW", dim)); - field = m_txtPadLength->GetValue(); - if (field.length() > 0 && field.ToDouble(&dim) && dim > 0.02) + if (shape.CmpNoCase(wxT("circle")) == 0 || shape.CmpNoCase(wxT("sqcircle")) == 0) { + /* for circle and square+circle, set pad length to the same value as the pad width */ rpn->SetVariable(RPNvariable("PL", dim)); + } else { + field = m_txtPadLength->GetValue(); + if (field.length() > 0 && field.ToDouble(&dim) && dim > 0.02) + rpn->SetVariable(RPNvariable("PL", dim)); + } field = m_txtAuxPadLength->GetValue(); if (field.length() > 0 && field.ToDouble(&dim) && dim > 0.02) rpn->SetVariable(RPNvariable("PLA", dim)); @@ -6658,110 +6774,67 @@ void libmngrFrame::UpdateDetails(int fp) { /* reset all fields and colours */ m_txtDescription->SetToolTip(wxEmptyString); - m_txtDescription->SetValue(wxEmptyString); - m_txtAlias->SetValue(wxEmptyString); - m_txtDescription->SetEditable(false); - m_txtAlias->SetEditable(false); - m_txtDescription->SetBackgroundColour(PROTECTED); - m_txtAlias->SetBackgroundColour(PROTECTED); + SetTextField(m_txtDescription, wxEmptyString, PROTECTED); + SetTextField(m_txtAlias, wxEmptyString, PROTECTED); if (SymbolMode) { m_spinUnitSelect->SetRange(1,1); m_spinUnitSelect->SetValue(1); - m_txtFootprintFilter->SetValue(wxEmptyString); - m_txtPadCount->SetValue(wxEmptyString); + SetTextField(m_txtFootprintFilter, wxEmptyString, PROTECTED); + SetTextField(m_txtPadCount, wxEmptyString, PROTECTED); m_gridPinNames->ClearGrid(); m_gridPinNames->SetColLabelSize(0); if (m_gridPinNames->GetNumberRows() > 0) m_gridPinNames->DeleteRows(0, m_gridPinNames->GetNumberRows()); wxSizer* sizer = m_gridPinNames->GetContainingSizer(); - m_txtBodyLength->SetValue(wxEmptyString); - m_txtBodyWidth->SetValue(wxEmptyString); - m_txtRefLabel->SetValue(wxEmptyString); + SetTextField(m_txtBodyLength, wxEmptyString, PROTECTED); + SetTextField(m_txtBodyWidth, wxEmptyString, PROTECTED); + SetTextField(m_txtRefLabel, wxEmptyString, PROTECTED); m_chkRefLabelVisible->SetValue(false); - m_txtValueLabel->SetValue(wxEmptyString); + SetTextField(m_txtValueLabel, wxEmptyString, PROTECTED); m_chkValueLabelVisible->SetValue(false); wxASSERT(sizer != 0); sizer->Layout(); m_lblUnitSelect->Enable(false); m_spinUnitSelect->Enable(false); - m_txtFootprintFilter->SetEditable(false); - m_txtPadCount->SetEditable(false); m_gridPinNames->EnableEditing(false); - m_txtBodyLength->SetEditable(false); - m_txtBodyWidth->SetEditable(false); - m_txtRefLabel->SetEditable(false); m_chkRefLabelVisible->Enable(false); - m_txtValueLabel->SetEditable(false); m_chkValueLabelVisible->Enable(false); - m_txtFootprintFilter->SetBackgroundColour(PROTECTED); - m_txtPadCount->SetBackgroundColour(PROTECTED); m_gridPinNames->SetBackgroundColour(PROTECTED); - m_txtBodyLength->SetBackgroundColour(PROTECTED); - m_txtBodyWidth->SetBackgroundColour(PROTECTED); - m_txtRefLabel->SetBackgroundColour(PROTECTED); m_chkRefLabelVisible->SetBackgroundColour(wxNullColour); - m_txtValueLabel->SetBackgroundColour(PROTECTED); m_chkValueLabelVisible->SetBackgroundColour(wxNullColour); } else { m_choiceShape->Clear(); - m_txtPadCount->SetValue(wxEmptyString); + SetTextField(m_txtPadCount, wxEmptyString, PROTECTED); m_choicePadShape->SetSelection(0); - m_txtPadWidth->SetValue(wxEmptyString); - m_txtPadLength->SetValue(wxEmptyString); - m_txtPitch->SetValue(wxEmptyString); - m_txtPadSpanX->SetValue(wxEmptyString); - m_txtPadSpanY->SetValue(wxEmptyString); - m_txtDrillSize->SetValue(wxEmptyString); - m_txtAuxPadLength->SetValue(wxEmptyString); - m_txtAuxPadWidth->SetValue(wxEmptyString); - m_txtBodyLength->SetValue(wxEmptyString); - m_txtBodyWidth->SetValue(wxEmptyString); - m_txtRefLabel->SetValue(wxEmptyString); + SetTextField(m_txtPadWidth, wxEmptyString, PROTECTED); + SetTextField(m_txtPadLength, wxEmptyString, PROTECTED); + SetTextField(m_txtPadRadius, wxEmptyString, PROTECTED); + SetTextField(m_txtPitch, wxEmptyString, PROTECTED); + SetTextField(m_txtPadSpanX, wxEmptyString, PROTECTED); + SetTextField(m_txtPadSpanY, wxEmptyString, PROTECTED); + SetTextField(m_txtDrillSize, wxEmptyString, PROTECTED); + SetTextField(m_txtAuxPadLength, wxEmptyString, PROTECTED); + SetTextField(m_txtAuxPadWidth, wxEmptyString, PROTECTED); + SetTextField(m_txtBodyLength, wxEmptyString, PROTECTED); + SetTextField(m_txtBodyWidth, wxEmptyString, PROTECTED); + SetTextField(m_txtRefLabel, wxEmptyString, PROTECTED); m_chkRefLabelVisible->SetValue(false); - m_txtValueLabel->SetValue(wxEmptyString); + SetTextField(m_txtValueLabel, wxEmptyString, PROTECTED); m_chkValueLabelVisible->SetValue(false); m_choiceShape->SetSelection(0); - m_txtShapeHeight->SetValue(wxEmptyString); + SetTextField(m_txtShapeHeight, wxEmptyString, PROTECTED); - m_txtPadCount->SetEditable(false); m_choicePadShape->Enable(false); - m_txtPadWidth->SetEditable(false); - m_txtPadLength->SetEditable(false); - m_txtPitch->SetEditable(false); - m_txtPadSpanX->SetEditable(false); - m_txtPadSpanY->SetEditable(false); - m_txtDrillSize->SetEditable(false); - m_txtAuxPadLength->SetEditable(false); - m_txtAuxPadWidth->SetEditable(false); - m_txtBodyLength->SetEditable(false); - m_txtBodyWidth->SetEditable(false); - m_txtRefLabel->SetEditable(false); m_chkRefLabelVisible->Enable(false); - m_txtValueLabel->SetEditable(false); m_chkValueLabelVisible->Enable(false); m_choiceShape->Enable(false); - m_txtShapeHeight->SetEditable(false); - m_txtPadCount->SetBackgroundColour(PROTECTED); m_choicePadShape->SetBackgroundColour(wxNullColour); - m_txtPadWidth->SetBackgroundColour(PROTECTED); - m_txtPadLength->SetBackgroundColour(PROTECTED); - m_txtPitch->SetBackgroundColour(PROTECTED); - m_txtPadSpanX->SetBackgroundColour(PROTECTED); - m_txtPadSpanY->SetBackgroundColour(PROTECTED); - m_txtDrillSize->SetBackgroundColour(PROTECTED); - m_txtAuxPadLength->SetBackgroundColour(PROTECTED); - m_txtAuxPadWidth->SetBackgroundColour(PROTECTED); - m_txtBodyLength->SetBackgroundColour(PROTECTED); - m_txtBodyWidth->SetBackgroundColour(PROTECTED); - m_txtRefLabel->SetBackgroundColour(PROTECTED); m_chkRefLabelVisible->SetBackgroundColour(wxNullColour); - m_txtValueLabel->SetBackgroundColour(PROTECTED); m_chkValueLabelVisible->SetBackgroundColour(wxNullColour); m_choiceShape->SetBackgroundColour(wxNullColour); - m_txtShapeHeight->SetBackgroundColour(PROTECTED); } m_btnSavePart->Enable(false); @@ -6773,25 +6846,18 @@ void libmngrFrame::UpdateDetails(int fp) wxString templatename = GetTemplateName(PartData[fp]); wxString field = GetDescription(PartData[fp], SymbolMode); - m_txtDescription->SetValue(field); + SetTextField(m_txtDescription, field, DefEnable ? ENABLED : PROTECTED); m_txtDescription->SetToolTip(field); - m_txtDescription->SetEditable(DefEnable); - m_txtDescription->SetBackgroundColour(DefEnable ? ENABLED : PROTECTED); if (SymbolMode) { /* schematic mode */ field = GetAliases(PartData[fp]); - m_txtAlias->SetValue(field); - m_txtAlias->SetEditable(DefEnable); - m_txtAlias->SetBackgroundColour(DefEnable ? ENABLED : PROTECTED); + SetTextField(m_txtAlias, field, DefEnable ? ENABLED : PROTECTED); field = GetFootprints(PartData[fp]); - m_txtFootprintFilter->SetValue(field); - m_txtFootprintFilter->SetEditable(DefEnable); - m_txtFootprintFilter->SetBackgroundColour(DefEnable ? ENABLED : PROTECTED); + SetTextField(m_txtFootprintFilter, field, DefEnable ? ENABLED : PROTECTED); - m_txtPadCount->SetValue(wxString::Format(wxT("%d"), PinDataCount[fp])); bool enable = templatename.length() > 0 && DefEnable; if (enable) { /* check whether the template allows multiple pin counts (many 2-pin @@ -6802,8 +6868,7 @@ void libmngrFrame::UpdateDetails(int fp) trimmed, when more white-space exists, it must be as a separator */ } - m_txtPadCount->SetEditable(enable); - m_txtPadCount->SetBackgroundColour(enable ? ENABLED : PROTECTED); + SetTextField(m_txtPadCount, wxString::Format(wxT("%d"), PinDataCount[fp]), enable ? ENABLED : PROTECTED); int unitcount = GetUnitCount(PartData[fp]); enable = unitcount > 1 && DefEnable; @@ -6872,11 +6937,8 @@ void libmngrFrame::UpdateDetails(int fp) /* footprint mode */ field = GetKeywords(PartData[fp], SymbolMode); - m_txtAlias->SetValue(field); - m_txtAlias->SetEditable(DefEnable); - m_txtAlias->SetBackgroundColour(DefEnable ? ENABLED : PROTECTED); + SetTextField(m_txtAlias, field, DefEnable ? ENABLED : PROTECTED); - m_txtPadCount->SetValue(wxString::Format(wxT("%d"), Footprint[fp].PadCount)); bool enable = templatename.length() > 0 && DefEnable; if (enable) { /* check whether the template allows multiple pin counts (many 2-pin @@ -6887,8 +6949,7 @@ void libmngrFrame::UpdateDetails(int fp) trimmed, when more white-space exists, it must be as a separator */ } - m_txtPadCount->SetEditable(enable); - m_txtPadCount->SetBackgroundColour(enable ? ENABLED : PROTECTED); + SetTextField(m_txtPadCount, wxString::Format(wxT("%d"), Footprint[fp].PadCount), enable ? ENABLED : PROTECTED); enable = DefEnable; int idx; @@ -6908,6 +6969,9 @@ void libmngrFrame::UpdateDetails(int fp) case 'T': idx = m_choicePadShape->FindString(wxT("Trapezoid")); break; + case 'D': + idx = m_choicePadShape->FindString(wxT("Rounded rectangle")); + break; default: idx = m_choicePadShape->FindString(wxT("(varies)")); enable = false; @@ -6918,54 +6982,34 @@ void libmngrFrame::UpdateDetails(int fp) m_choicePadShape->SetBackgroundColour(enable ? ENABLED : PROTECTED); const CoordPair& padsize = Footprint[fp].PadSize[0]; - if (padsize.GetX() > EPSILON) { - m_txtPadWidth->SetValue(wxString::Format(wxT("%.3f"), padsize.GetX())); - m_txtPadWidth->SetEditable(DefEnable); - m_txtPadWidth->SetBackgroundColour(DefEnable ? ENABLED : PROTECTED); - } - if (padsize.GetY() > EPSILON) { - m_txtPadLength->SetValue(wxString::Format(wxT("%.3f"), padsize.GetY())); - m_txtPadLength->SetEditable(DefEnable); - m_txtPadLength->SetBackgroundColour(DefEnable ? ENABLED : PROTECTED); - } + if (padsize.GetX() > EPSILON) + SetTextField(m_txtPadWidth, wxString::Format(wxT("%.3f"), padsize.GetX()), DefEnable ? ENABLED : PROTECTED); + if (padsize.GetY() > EPSILON) + SetTextField(m_txtPadLength, wxString::Format(wxT("%.3f"), padsize.GetY()), DefEnable ? ENABLED : PROTECTED); + int radius = Footprint[fp].PadRRatio; + if (radius > 0) + SetTextField(m_txtPadRadius, wxString::Format(wxT("%d"), radius), DefEnable ? ENABLED : PROTECTED); const CoordPair& auxpadsize = Footprint[fp].PadSize[1]; - if (auxpadsize.GetX() > EPSILON) { - m_txtAuxPadWidth->SetValue(wxString::Format(wxT("%.3f"), auxpadsize.GetX())); - m_txtAuxPadWidth->SetEditable(DefEnable); - m_txtAuxPadWidth->SetBackgroundColour(DefEnable ? ENABLED : PROTECTED); - } - if (auxpadsize.GetY() > EPSILON) { - m_txtAuxPadLength->SetValue(wxString::Format(wxT("%.3f"), auxpadsize.GetY())); - m_txtAuxPadLength->SetEditable(DefEnable); - m_txtAuxPadLength->SetBackgroundColour(DefEnable ? ENABLED : PROTECTED); - } + if (auxpadsize.GetX() > EPSILON) + SetTextField(m_txtAuxPadWidth, wxString::Format(wxT("%.3f"), auxpadsize.GetX()), DefEnable ? ENABLED : PROTECTED); + if (auxpadsize.GetY() > EPSILON) + SetTextField(m_txtAuxPadLength, wxString::Format(wxT("%.3f"), auxpadsize.GetY()), DefEnable ? ENABLED : PROTECTED); if (Footprint[fp].Pitch > EPSILON) { - m_txtPitch->SetValue(wxString::Format(wxT("%.3f"), Footprint[fp].Pitch)); enable = DefEnable && Footprint[fp].RegPadCount > 0 && Footprint[fp].PadLines > 0 && Footprint[fp].OriginCentred; - m_txtPitch->SetEditable(enable); - m_txtPitch->SetBackgroundColour(enable ? ENABLED : PROTECTED); + SetTextField(m_txtPitch, wxString::Format(wxT("%.3f"), Footprint[fp].Pitch), enable ? ENABLED : PROTECTED); } - if (Footprint[fp].SpanHor > EPSILON) { - m_txtPadSpanX->SetValue(wxString::Format(wxT("%.3f"), Footprint[fp].SpanHor)); - m_txtPadSpanX->SetEditable(DefEnable); - m_txtPadSpanX->SetBackgroundColour(DefEnable ? ENABLED : PROTECTED); - } - if (Footprint[fp].SpanVer > EPSILON) { - m_txtPadSpanY->SetValue(wxString::Format(wxT("%.3f"), Footprint[fp].SpanVer)); - m_txtPadSpanY->SetEditable(DefEnable); - m_txtPadSpanY->SetBackgroundColour(DefEnable ? ENABLED : PROTECTED); - } + if (Footprint[fp].SpanHor > EPSILON) + SetTextField(m_txtPadSpanX, wxString::Format(wxT("%.3f"), Footprint[fp].SpanHor), DefEnable ? ENABLED : PROTECTED); + if (Footprint[fp].SpanVer > EPSILON) + SetTextField(m_txtPadSpanY, wxString::Format(wxT("%.3f"), Footprint[fp].SpanVer), DefEnable ? ENABLED : PROTECTED); - if (Footprint[fp].DrillSize > EPSILON) { - m_txtDrillSize->SetValue(wxString::Format(wxT("%.3f"), Footprint[fp].DrillSize)); - m_txtDrillSize->SetEditable(DefEnable); - m_txtDrillSize->SetBackgroundColour(DefEnable ? ENABLED : PROTECTED); - } + if (Footprint[fp].DrillSize > EPSILON) + SetTextField(m_txtDrillSize, wxString::Format(wxT("%.3f"), Footprint[fp].DrillSize), DefEnable ? ENABLED : PROTECTED); if (templatename.length() > 0) { /* fill the 3D model list */ @@ -6986,20 +7030,20 @@ void libmngrFrame::UpdateDetails(int fp) /* check what model was used */ wxString vrmlpath = GetVRMLPath(library, PartData[fp]); if (vrmlpath.length() > 0) { - field = GetFileHeaderField(vrmlpath, wxT("model")); - wxString modelname = field.BeforeFirst(wxT('{')); + field = wxEmptyString; + wxString spec = GetFileHeaderField(vrmlpath, wxT("model")); + wxString modelname = spec.BeforeFirst(wxT('{')); modelname.Trim(true); idx = m_choiceShape->FindString(modelname); - field = field.AfterFirst(wxT('{')).BeforeFirst(wxT('}')); - field.Trim(true); - if (field.length() > 0) { - field.Trim(false); - wxArrayString params = wxSplit(field, wxT(' ')); + spec = spec.AfterFirst(wxT('{')).BeforeFirst(wxT('}')); + spec.Trim(true); + if (spec.length() > 0) { + spec.Trim(false); + wxArrayString params = wxSplit(spec, wxT(' ')); if (params.Count() >= 2) - m_txtShapeHeight->SetValue(params[1]); + field = params[1]; } - m_txtShapeHeight->SetEditable(DefEnable); - m_txtShapeHeight->SetBackgroundColour(DefEnable ? ENABLED : PROTECTED); + SetTextField(m_txtShapeHeight, field, DefEnable ? ENABLED : PROTECTED); } m_choiceShape->SetSelection(idx); enable = (DefEnable && models.Count() > 1); @@ -7018,29 +7062,21 @@ void libmngrFrame::UpdateDetails(int fp) setlength = (field.Find(wxT("@BL")) >= 0); } if (BodySize[fp].BodyLength > EPSILON) { - m_txtBodyLength->SetValue(wxString::Format(wxT("%.3f"), BodySize[fp].BodyLength)); bool enable = setlength && DefEnable; - m_txtBodyLength->SetEditable(enable); - m_txtBodyLength->SetBackgroundColour(enable ? ENABLED : PROTECTED); + SetTextField(m_txtBodyLength, wxString::Format(wxT("%.3f"), BodySize[fp].BodyLength), enable ? ENABLED : PROTECTED); } if (BodySize[fp].BodyWidth > EPSILON) { - m_txtBodyWidth->SetValue(wxString::Format(wxT("%.3f"), BodySize[fp].BodyWidth)); bool enable = setwidth && DefEnable; - m_txtBodyWidth->SetEditable(enable); - m_txtBodyWidth->SetBackgroundColour(enable ? ENABLED : PROTECTED); + SetTextField(m_txtBodyWidth, wxString::Format(wxT("%.3f"), BodySize[fp].BodyWidth), enable ? ENABLED : PROTECTED); } - m_txtRefLabel->SetValue(wxString::Format(wxT("%.2f"), LabelData[fp].RefLabelSize)); - m_txtRefLabel->SetEditable(DefEnable); - m_txtRefLabel->SetBackgroundColour(DefEnable ? ENABLED : PROTECTED); + SetTextField(m_txtRefLabel, wxString::Format(wxT("%.2f"), LabelData[fp].RefLabelSize), DefEnable ? ENABLED : PROTECTED); m_chkRefLabelVisible->SetValue(LabelData[fp].RefLabelVisible); m_chkRefLabelVisible->Enable(DefEnable); - m_txtValueLabel->SetValue(wxString::Format(wxT("%.2f"), LabelData[fp].ValueLabelSize)); - m_txtValueLabel->SetEditable(DefEnable); - m_txtValueLabel->SetBackgroundColour(DefEnable ? ENABLED : PROTECTED); + SetTextField(m_txtValueLabel, wxString::Format(wxT("%.2f"), LabelData[fp].ValueLabelSize), DefEnable ? ENABLED : PROTECTED); m_chkValueLabelVisible->SetValue(LabelData[fp].ValueLabelVisible); m_chkValueLabelVisible->Enable(DefEnable); FieldEdited = false; /* clear this flag for all implicit changes */ -} \ No newline at end of file +} diff --git a/src/libmngr_frame.h b/src/libmngr_frame.h index 1dcd3f7..1e2102c 100644 --- a/src/libmngr_frame.h +++ b/src/libmngr_frame.h @@ -3,7 +3,7 @@ * This file contains the code for the main frame, which is almost all of the * user-interface code. * - * Copyright (C) 2013-2017 CompuPhase + * Copyright (C) 2013-2018 CompuPhase * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy @@ -17,7 +17,7 @@ * License for the specific language governing permissions and limitations * under the License. * - * $Id: libmngr_frame.h 5784 2017-12-26 14:12:22Z thiadmer $ + * $Id: libmngr_frame.h 5907 2018-12-14 22:05:40Z thiadmer $ */ #ifndef __libmngr_frame__ #define __libmngr_frame__ @@ -94,7 +94,7 @@ class libmngrFrame : public AppFrame virtual void OnSizeViewport(wxSizeEvent& event); virtual void OnStatusBarDblClk(wxMouseEvent& event); virtual void OnUnitSelect(wxSpinEvent& event); - + void OnPasteGeneral(wxCommandEvent& event); void OnExportGeneral(wxCommandEvent& event); @@ -146,6 +146,18 @@ class libmngrFrame : public AppFrame void DrawModels(float xangle, float yangle); void DrawDimension(wxGraphicsContext *gc, int midx, int midy, int orientation, int stack, double value, const CoordPair pins[2], const CoordSize& bbox); + void DrawPad(wxGraphicsContext *gc, double midx, double midy, + const wxString& padshape, const wxString& padpin, + double padx, double pady, double padwidth, double padheight, + double padrratio, double paddeltax, double paddeltay, + double pasteratio, double padrot, + double drillx, double drilly, double drillwidth, double drillheight, + const wxPen& penPad, const wxBrush& brushPad, const wxBrush& brushHole, + CoordSize *bbox); + + void EstimateSymbolSize(double *cx, double *cy, const BodyInfo& Body); + void EstimateFootprintSize(double *cx, double *cy, const FootprintInfo& Footprint, const BodyInfo& Body); + void AutoZoom(int part); void UpdateBoundingBox(CoordSize* bbox, double x, double y); void UpdateBoundingBox(CoordSize* bbox, double x, double y, double width, double height) { UpdateBoundingBox(bbox, x, y); @@ -192,6 +204,7 @@ class libmngrFrame : public AppFrame void UpdateDetails(int fp); bool CacheMetadata(const wxString& libname, const wxString& symname, bool force_export, const wxArrayString& module, const FootprintInfo& footprint); + void SetTextField(wxTextCtrl* ctrl, const wxString& value, const wxColour& status); bool SavePart(int index, wxListCtrl* list); bool CheckSavePart(); diff --git a/src/libmngr_gui_base.cpp b/src/libmngr_gui_base.cpp index 07d4dbc..1db6162 100644 --- a/src/libmngr_gui_base.cpp +++ b/src/libmngr_gui_base.cpp @@ -1,8 +1,8 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Feb 14 2017) +// C++ code generated with wxFormBuilder (version Oct 26 2018) // http://www.wxformbuilder.org/ // -// PLEASE DO "NOT" EDIT THIS FILE! +// PLEASE DO *NOT* EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// #include "libmngr_gui_base.h" @@ -21,323 +21,323 @@ AppFrame::AppFrame( wxWindow* parent, wxWindowID id, const wxString& title, cons { this->SetSizeHints( wxDefaultSize, wxDefaultSize ); this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_MENU ) ); - + m_menubar = new wxMenuBar( 0 ); m_mnuFile = new wxMenu(); wxMenuItem* m_mnuNew; m_mnuNew = new wxMenuItem( m_mnuFile, wxID_NEW, wxString( wxT("New &Library...") ) , wxT("Create a new (empty) module library"), wxITEM_NORMAL ); m_mnuFile->Append( m_mnuNew ); - + wxMenuItem* m_mnuRenameLibrary; m_mnuRenameLibrary = new wxMenuItem( m_mnuFile, IDM_RENAMELIBRAY, wxString( wxT("R&ename library...") ) , wxT("Rename the current library"), wxITEM_NORMAL ); m_mnuFile->Append( m_mnuRenameLibrary ); - + m_mnuFile->AppendSeparator(); - + wxMenuItem* m_mnuNewFootprint; m_mnuNewFootprint = new wxMenuItem( m_mnuFile, IDM_NEWFOOTPRINT, wxString( wxT("New &Footprint..") ) , wxEmptyString, wxITEM_NORMAL ); m_mnuFile->Append( m_mnuNewFootprint ); - + wxMenuItem* m_mnuNewSymbol; m_mnuNewSymbol = new wxMenuItem( m_mnuFile, IDM_NEWSYMBOL, wxString( wxT("New &Symbol...") ) , wxEmptyString, wxITEM_NORMAL ); m_mnuFile->Append( m_mnuNewSymbol ); - + m_mnuFile->AppendSeparator(); - + wxMenuItem* m_mnuFootprintSheet; m_mnuFootprintSheet = new wxMenuItem( m_mnuFile, IDM_REPORTFOOTPRINT, wxString( wxT("Create footprint &report") ) , wxEmptyString, wxITEM_NORMAL ); m_mnuFile->Append( m_mnuFootprintSheet ); - + wxMenuItem* m_mnuSymbolSheet; m_mnuSymbolSheet = new wxMenuItem( m_mnuFile, IDM_REPORTSYMBOL, wxString( wxT("Create s&ymbol report") ) , wxEmptyString, wxITEM_NORMAL ); m_mnuFile->Append( m_mnuSymbolSheet ); - + m_mnuFile->AppendSeparator(); - + wxMenuItem* m_mnuQuit; m_mnuQuit = new wxMenuItem( m_mnuFile, wxID_EXIT, wxString( wxT("&Quit") ) + wxT('\t') + wxT("Alt+F4"), wxT("Exit the application"), wxITEM_NORMAL ); m_mnuFile->Append( m_mnuQuit ); - - m_menubar->Append( m_mnuFile, wxT("&File") ); - + + m_menubar->Append( m_mnuFile, wxT("&File") ); + m_mnuView = new wxMenu(); wxMenuItem* m_mnuFootprintMode; m_mnuFootprintMode = new wxMenuItem( m_mnuView, IDM_FOOTPRINTMODE, wxString( wxT("&Footprint mode") ) , wxT("In footprint mode, you can view, adjust, move and copy footprints bewteen libraries"), wxITEM_RADIO ); m_mnuView->Append( m_mnuFootprintMode ); m_mnuFootprintMode->Check( true ); - + wxMenuItem* m_mnuSymbolMode; m_mnuSymbolMode = new wxMenuItem( m_mnuView, IDM_SCHEMATICMODE, wxString( wxT("&Schematic mode") ) , wxT("In schematic mode, you can view, move and copy symbols between schematic libraries"), wxITEM_RADIO ); m_mnuView->Append( m_mnuSymbolMode ); - + m_mnuView->AppendSeparator(); - + wxMenuItem* m_mnuCompareMode; m_mnuCompareMode = new wxMenuItem( m_mnuView, IDM_COMPAREMODE, wxString( wxT("&Compare mode") ) + wxT('\t') + wxT("Ctrl+C"), wxT("In compare mode, you can compare two footprints"), wxITEM_CHECK ); m_mnuView->Append( m_mnuCompareMode ); - + wxMenuItem* m_mnuDetailsPanel; m_mnuDetailsPanel = new wxMenuItem( m_mnuView, IDM_DETAILSPANEL, wxString( wxT("Details panel") ) + wxT('\t') + wxT("Ctrl+D"), wxT("The details panel allows you to see properties of the footprint, and adjust them"), wxITEM_CHECK ); m_mnuView->Append( m_mnuDetailsPanel ); - + wxMenuItem* m_mnuSyncMode; m_mnuSyncMode = new wxMenuItem( m_mnuView, IDM_SYNCMODE, wxString( wxT("Synchronize lists") ) + wxT('\t') + wxT("Ctrl+Y"), wxEmptyString, wxITEM_CHECK ); m_mnuView->Append( m_mnuSyncMode ); - + m_mnuView->AppendSeparator(); - + wxMenuItem* m_mnuFilter; m_mnuFilter = new wxMenuItem( m_mnuView, IDM_FILTER, wxString( wxT("Filter...") ) + wxT('\t') + wxT("Ctrl+F"), wxT("Show only footprints/symbols that match the keywords"), wxITEM_CHECK ); m_mnuView->Append( m_mnuFilter ); - - m_menubar->Append( m_mnuView, wxT("&View") ); - + + m_menubar->Append( m_mnuView, wxT("&View") ); + m_mnuPreferences = new wxMenu(); wxMenuItem* m_mnuSearchPaths; m_mnuSearchPaths = new wxMenuItem( m_mnuPreferences, wxID_PREFERENCES, wxString( wxT("Search &Paths...") ) , wxT("Add or remove paths to search for footprint or symbol libraries"), wxITEM_NORMAL ); m_mnuPreferences->Append( m_mnuSearchPaths ); - + wxMenuItem* m_mnuRemoteLink; m_mnuRemoteLink = new wxMenuItem( m_mnuPreferences, IDM_DLGREMOTE, wxString( wxT("Remote Repository...") ) , wxEmptyString, wxITEM_NORMAL ); m_mnuPreferences->Append( m_mnuRemoteLink ); - + wxMenuItem* m_mnuReport; m_mnuReport = new wxMenuItem( m_mnuPreferences, IDM_DLGREPORT, wxString( wxT("&Report options...") ) , wxT("Configuration of the footprint report"), wxITEM_NORMAL ); m_mnuPreferences->Append( m_mnuReport ); - + wxMenuItem* m_mnuTemplate; m_mnuTemplate = new wxMenuItem( m_mnuPreferences, IDM_TEMPLATEOPTIONS, wxString( wxT("&Template variables...") ) , wxT("Change parameters for templates for new footprints"), wxITEM_NORMAL ); m_mnuPreferences->Append( m_mnuTemplate ); - + m_mnuPreferences->AppendSeparator(); - + wxMenuItem* m_mnuUI; m_mnuUI = new wxMenuItem( m_mnuPreferences, IDM_DLGOPTIONS, wxString( wxT("&Application settings...") ) , wxT("User interface settings"), wxITEM_NORMAL ); m_mnuPreferences->Append( m_mnuUI ); - - m_menubar->Append( m_mnuPreferences, wxT("&Preferences") ); - + + m_menubar->Append( m_mnuPreferences, wxT("&Preferences") ); + m_mnuHelp = new wxMenu(); wxMenuItem* m_mnuHelpManual; m_mnuHelpManual = new wxMenuItem( m_mnuHelp, wxID_HELP, wxString( wxT("&Manual") ) + wxT('\t') + wxT("F1"), wxEmptyString, wxITEM_NORMAL ); m_mnuHelp->Append( m_mnuHelpManual ); - + wxMenuItem* m_mnuAbout; m_mnuAbout = new wxMenuItem( m_mnuHelp, wxID_ABOUT, wxString( wxT("&About") ) , wxEmptyString, wxITEM_NORMAL ); m_mnuHelp->Append( m_mnuAbout ); - - m_menubar->Append( m_mnuHelp, wxT("&Help") ); - + + m_menubar->Append( m_mnuHelp, wxT("&Help") ); + this->SetMenuBar( m_menubar ); - + wxBoxSizer* bMainSizer; bMainSizer = new wxBoxSizer( wxVERTICAL ); - + m_splitter = new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_3D ); m_splitter->Connect( wxEVT_IDLE, wxIdleEventHandler( AppFrame::m_splitterOnIdle ), NULL, this ); m_splitter->SetMinimumPaneSize( 5 ); - + m_panelTop = new wxPanel( m_splitter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); m_panelTop->Hide(); - + wxBoxSizer* bHorSplitter1; bHorSplitter1 = new wxBoxSizer( wxHORIZONTAL ); - + wxBoxSizer* bSizerLeft; bSizerLeft = new wxBoxSizer( wxVERTICAL ); - + wxArrayString m_choiceModuleLeftChoices; m_choiceModuleLeft = new wxChoice( m_panelTop, IDM_LEFT_LIB, wxDefaultPosition, wxDefaultSize, m_choiceModuleLeftChoices, 0 ); m_choiceModuleLeft->SetSelection( 0 ); bSizerLeft->Add( m_choiceModuleLeft, 0, wxEXPAND|wxTOP|wxBOTTOM|wxLEFT, 5 ); - + m_listModulesLeft = new wxListCtrl( m_panelTop, IDM_LEFT_MOD, wxDefaultPosition, wxDefaultSize, wxLC_NO_HEADER|wxLC_REPORT|wxLC_SINGLE_SEL ); bSizerLeft->Add( m_listModulesLeft, 1, wxEXPAND|wxBOTTOM|wxLEFT, 5 ); - - + + bHorSplitter1->Add( bSizerLeft, 1, wxEXPAND, 5 ); - + wxBoxSizer* bSizerMid; bSizerMid = new wxBoxSizer( wxVERTICAL ); - - + + bSizerMid->Add( 0, 0, 1, wxEXPAND, 5 ); - + m_btnMove = new wxButton( m_panelTop, IDM_MOVEMODULE, wxT("&Move"), wxDefaultPosition, wxDefaultSize, 0 ); m_btnMove->SetToolTip( wxT("Move the footprint / symbol to the other library.") ); - + bSizerMid->Add( m_btnMove, 0, wxALL, 5 ); - + m_btnCopy = new wxButton( m_panelTop, IDM_COPYMODULE, wxT("&Copy"), wxDefaultPosition, wxDefaultSize, 0 ); m_btnCopy->SetToolTip( wxT("Copy the footprint / symbol to the other library.") ); - + bSizerMid->Add( m_btnCopy, 0, wxALL, 5 ); - + m_btnDelete = new wxButton( m_panelTop, IDM_DELETEMODULE, wxT("&Delete"), wxDefaultPosition, wxDefaultSize, 0 ); m_btnDelete->SetToolTip( wxT("Remove the selected footprint / symbol.") ); - + bSizerMid->Add( m_btnDelete, 0, wxALL, 5 ); - + m_btnRename = new wxButton( m_panelTop, IDM_RENAMEMODULE, wxT("&Rename"), wxDefaultPosition, wxDefaultSize, 0 ); m_btnRename->SetToolTip( wxT("Rename the selected footprint / symbol.") ); - + bSizerMid->Add( m_btnRename, 0, wxALL, 5 ); - + m_btnDuplicate = new wxButton( m_panelTop, IDM_RENAMEMODULE, wxT("D&uplicate"), wxDefaultPosition, wxDefaultSize, 0 ); m_btnDuplicate->SetToolTip( wxT("Copy the selected footprint / symbol to the same library under a different name.") ); - + bSizerMid->Add( m_btnDuplicate, 0, wxALL, 5 ); - - + + bSizerMid->Add( 0, 0, 1, wxEXPAND, 5 ); - - + + bHorSplitter1->Add( bSizerMid, 0, wxEXPAND, 5 ); - + wxBoxSizer* bSizerRight; bSizerRight = new wxBoxSizer( wxVERTICAL ); - + wxArrayString m_choiceModuleRightChoices; m_choiceModuleRight = new wxChoice( m_panelTop, IDM_RIGHT_LIB, wxDefaultPosition, wxDefaultSize, m_choiceModuleRightChoices, 0 ); m_choiceModuleRight->SetSelection( 0 ); bSizerRight->Add( m_choiceModuleRight, 0, wxEXPAND|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - + m_listModulesRight = new wxListCtrl( m_panelTop, IDM_RIGHT_MOD, wxDefaultPosition, wxDefaultSize, wxLC_NO_HEADER|wxLC_REPORT|wxLC_SINGLE_SEL ); bSizerRight->Add( m_listModulesRight, 1, wxBOTTOM|wxRIGHT|wxEXPAND, 5 ); - - + + bHorSplitter1->Add( bSizerRight, 1, wxEXPAND, 5 ); - - + + m_panelTop->SetSizer( bHorSplitter1 ); m_panelTop->Layout(); bHorSplitter1->Fit( m_panelTop ); m_panelBottom = new wxPanel( m_splitter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); m_panelBottom->Hide(); - + wxBoxSizer* bSizerBottom; bSizerBottom = new wxBoxSizer( wxHORIZONTAL ); - - m_toolBar = new wxAuiToolBar( m_panelBottom, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxAUI_TB_PLAIN_BACKGROUND|wxAUI_TB_VERTICAL ); - m_toolZoomIn = m_toolBar->AddTool( IDT_ZOOMIN, wxT("Zoom in"), ZoomIn_png_to_wx_bitmap(), wxNullBitmap, wxITEM_NORMAL, wxT("Zoom in (Ctrl +)."), wxEmptyString, NULL ); - - m_toolZoomOut = m_toolBar->AddTool( IDT_ZOOMOUT, wxT("Zoom out"), ZoomOut_png_to_wx_bitmap(), wxNullBitmap, wxITEM_NORMAL, wxT("Zoom out (Ctrl -)."), wxEmptyString, NULL ); - - m_tool3DView = m_toolBar->AddTool( IDT_3DVIEW, wxT("tool"), View3D_png_to_wx_bitmap(), wxNullBitmap, wxITEM_CHECK, wxEmptyString, wxEmptyString, NULL ); - - m_toolMeasure = m_toolBar->AddTool( IDT_MEASUREMENTS, wxT("Measure"), Measure_png_to_wx_bitmap(), wxNullBitmap, wxITEM_CHECK, wxT("Show pitch and pad measurements."), wxEmptyString, NULL ); - - m_toolDetailsPanel = m_toolBar->AddTool( IDT_DETAILSPANEL, wxT("Details panel"), Information_png_to_wx_bitmap(), wxNullBitmap, wxITEM_CHECK, wxT("Show/hide the side panel with parameters."), wxEmptyString, NULL ); - - m_toolLeftFootprint = m_toolBar->AddTool( IDT_LEFTFOOTPRINT, wxT("Left"), ArrowLeft_png_to_wx_bitmap(), wxNullBitmap, wxITEM_CHECK, wxT("Show left footprint."), wxEmptyString, NULL ); - - m_toolRightFootprint = m_toolBar->AddTool( IDT_RIGHTFOOTPRINT, wxT("Right"), ArrowRight_png_to_wx_bitmap(), wxNullBitmap, wxITEM_CHECK, wxT("Show right footprint."), wxEmptyString, NULL ); - - m_toolBar->Realize(); - + + m_toolBar = new wxAuiToolBar( m_panelBottom, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxAUI_TB_PLAIN_BACKGROUND|wxAUI_TB_VERTICAL ); + m_toolZoomIn = m_toolBar->AddTool( IDT_ZOOMIN, wxT("Zoom in"), ZoomIn_png_to_wx_bitmap(), wxNullBitmap, wxITEM_NORMAL, wxT("Zoom in (Ctrl +)."), wxEmptyString, NULL ); + + m_toolZoomOut = m_toolBar->AddTool( IDT_ZOOMOUT, wxT("Zoom out"), ZoomOut_png_to_wx_bitmap(), wxNullBitmap, wxITEM_NORMAL, wxT("Zoom out (Ctrl -)."), wxEmptyString, NULL ); + + m_tool3DView = m_toolBar->AddTool( IDT_3DVIEW, wxT("tool"), View3D_png_to_wx_bitmap(), wxNullBitmap, wxITEM_CHECK, wxEmptyString, wxEmptyString, NULL ); + + m_toolMeasure = m_toolBar->AddTool( IDT_MEASUREMENTS, wxT("Measure"), Measure_png_to_wx_bitmap(), wxNullBitmap, wxITEM_CHECK, wxT("Show pitch and pad measurements."), wxEmptyString, NULL ); + + m_toolDetailsPanel = m_toolBar->AddTool( IDT_DETAILSPANEL, wxT("Details panel"), Information_png_to_wx_bitmap(), wxNullBitmap, wxITEM_CHECK, wxT("Show/hide the side panel with parameters."), wxEmptyString, NULL ); + + m_toolLeftFootprint = m_toolBar->AddTool( IDT_LEFTFOOTPRINT, wxT("Left"), ArrowLeft_png_to_wx_bitmap(), wxNullBitmap, wxITEM_CHECK, wxT("Show left footprint."), wxEmptyString, NULL ); + + m_toolRightFootprint = m_toolBar->AddTool( IDT_RIGHTFOOTPRINT, wxT("Right"), ArrowRight_png_to_wx_bitmap(), wxNullBitmap, wxITEM_CHECK, wxT("Show right footprint."), wxEmptyString, NULL ); + + m_toolBar->Realize(); + bSizerBottom->Add( m_toolBar, 0, wxALL, 5 ); - + m_splitterViewPanel = new wxSplitterWindow( m_panelBottom, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_3DSASH ); m_splitterViewPanel->SetSashGravity( 1 ); m_splitterViewPanel->Connect( wxEVT_IDLE, wxIdleEventHandler( AppFrame::m_splitterViewPanelOnIdle ), NULL, this ); - + m_panelView = new wxPanel( m_splitterViewPanel, IDM_PANELVIEW, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); m_panelView->SetBackgroundColour( wxColour( 0, 0, 0 ) ); - + m_panelSettings = new wxScrolledWindow( m_splitterViewPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL|wxVSCROLL ); m_panelSettings->SetScrollRate( 5, 5 ); m_panelSettings->Hide(); - + wxFlexGridSizer* fgSidePanel; fgSidePanel = new wxFlexGridSizer( 0, 2, 0, 0 ); fgSidePanel->AddGrowableCol( 1 ); fgSidePanel->SetFlexibleDirection( wxBOTH ); fgSidePanel->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - + m_lblViewSide = new wxStaticText( m_panelSettings, wxID_ANY, wxT("View"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblViewSide->Wrap( -1 ); fgSidePanel->Add( m_lblViewSide, 0, wxALL, 5 ); - + wxBoxSizer* bSizerViewSide; bSizerViewSide = new wxBoxSizer( wxHORIZONTAL ); - + m_radioViewLeft = new wxRadioButton( m_panelSettings, wxID_ANY, wxT("Left"), wxDefaultPosition, wxDefaultSize, 0 ); m_radioViewLeft->SetToolTip( wxT("Show the symbol from the right symbol/footprint list.") ); - + bSizerViewSide->Add( m_radioViewLeft, 0, wxALL, 5 ); - + m_radioViewRight = new wxRadioButton( m_panelSettings, wxID_ANY, wxT("Right"), wxDefaultPosition, wxDefaultSize, 0 ); m_radioViewRight->SetToolTip( wxT("Show the symbol from the right symbol/footprint list.") ); - + bSizerViewSide->Add( m_radioViewRight, 0, wxALL, 5 ); - + m_lblUnitSelect = new wxStaticText( m_panelSettings, wxID_ANY, wxT("Unit"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblUnitSelect->Wrap( -1 ); bSizerViewSide->Add( m_lblUnitSelect, 0, wxALL, 5 ); - + m_spinUnitSelect = new wxSpinCtrl( m_panelSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 1, 2, 0 ); m_spinUnitSelect->SetMinSize( wxSize( 50,-1 ) ); - + bSizerViewSide->Add( m_spinUnitSelect, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - + + fgSidePanel->Add( bSizerViewSide, 1, 0, 5 ); - + m_lblDescription = new wxStaticText( m_panelSettings, wxID_ANY, wxT("Description"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblDescription->Wrap( -1 ); fgSidePanel->Add( m_lblDescription, 0, wxALL, 5 ); - + m_txtDescription = new wxTextCtrl( m_panelSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); m_txtDescription->SetToolTip( wxT("A general description of the part.") ); - + fgSidePanel->Add( m_txtDescription, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 3 ); - + m_lblAlias = new wxStaticText( m_panelSettings, wxID_ANY, wxT("Alias"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblAlias->Wrap( -1 ); fgSidePanel->Add( m_lblAlias, 0, wxALL, 5 ); - + m_txtAlias = new wxTextCtrl( m_panelSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); m_txtAlias->SetToolTip( wxT("Other names for the symbol or keywords for the footprint, separated with spaces.") ); - + fgSidePanel->Add( m_txtAlias, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 3 ); - + m_lblFootprintFilter = new wxStaticText( m_panelSettings, wxID_ANY, wxT("Footprints"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblFootprintFilter->Wrap( -1 ); m_lblFootprintFilter->Hide(); - + fgSidePanel->Add( m_lblFootprintFilter, 0, wxALL, 5 ); - + m_txtFootprintFilter = new wxTextCtrl( m_panelSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); m_txtFootprintFilter->Hide(); m_txtFootprintFilter->SetToolTip( wxT("Applicable footprints, separated by spaces; the footprint names may have wildcard characters.") ); - + fgSidePanel->Add( m_txtFootprintFilter, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 3 ); - + m_lblPadCount = new wxStaticText( m_panelSettings, wxID_ANY, wxT("Pad count"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblPadCount->Wrap( -1 ); fgSidePanel->Add( m_lblPadCount, 0, wxALL, 5 ); - + m_txtPadCount = new wxTextCtrl( m_panelSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); m_txtPadCount->SetToolTip( wxT("The total number of pads or pins of the footprint or symbol\nincluding an exposed pad or non-standard pads.\nIf this field is read-only, either\n1) the part was not based on a template\n2) rebuilding the part from the template is disabled in the preferences\n3) this part only exists with one valid pin count.") ); m_txtPadCount->SetMinSize( wxSize( 55,-1 ) ); - + fgSidePanel->Add( m_txtPadCount, 0, wxBOTTOM|wxRIGHT|wxLEFT, 3 ); - + m_lblPinNames = new wxStaticText( m_panelSettings, wxID_ANY, wxT("Pins"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblPinNames->Wrap( -1 ); m_lblPinNames->Hide(); - + fgSidePanel->Add( m_lblPinNames, 0, wxALL, 5 ); - - m_gridPinNames = new wxGrid( m_panelSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER ); - + + m_gridPinNames = new wxGrid( m_panelSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN ); + // Grid m_gridPinNames->CreateGrid( 2, 5 ); m_gridPinNames->EnableEditing( true ); m_gridPinNames->EnableGridLines( true ); m_gridPinNames->EnableDragGridSize( false ); m_gridPinNames->SetMargins( 0, 0 ); - + // Columns m_gridPinNames->AutoSizeColumns(); m_gridPinNames->EnableDragColMove( false ); @@ -348,269 +348,280 @@ AppFrame::AppFrame( wxWindow* parent, wxWindowID id, const wxString& title, cons m_gridPinNames->SetColLabelValue( 2, wxT("type") ); m_gridPinNames->SetColLabelValue( 3, wxT("style") ); m_gridPinNames->SetColLabelValue( 4, wxT("side") ); - m_gridPinNames->SetColLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE ); - + m_gridPinNames->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER ); + // Rows m_gridPinNames->EnableDragRowSize( false ); m_gridPinNames->SetRowLabelSize( 0 ); - m_gridPinNames->SetRowLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE ); - + m_gridPinNames->SetRowLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER ); + // Label Appearance - + // Cell Defaults m_gridPinNames->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_TOP ); m_gridPinNames->Hide(); m_gridPinNames->SetToolTip( wxT("Pin numbers, labels and properties.\nThe \"type\" is the electrical type.\nThe \"style\" is the graphic shape.\nThe position can only be changed for symbols that are based on a template.") ); - + fgSidePanel->Add( m_gridPinNames, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 3 ); - + m_lblPadShape = new wxStaticText( m_panelSettings, wxID_ANY, wxT("Pad shape"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblPadShape->Wrap( -1 ); fgSidePanel->Add( m_lblPadShape, 0, wxALL, 5 ); - - wxString m_choicePadShapeChoices[] = { wxT("(varies)"), wxT("Rectangular"), wxT("Round"), wxT("Round + square"), wxT("Obround"), wxT("Trapezoid") }; + + wxString m_choicePadShapeChoices[] = { wxT("(varies)"), wxT("Round"), wxT("Round + square"), wxT("Rectangular"), wxT("Rounded rectangle"), wxT("Obround"), wxT("Trapezoid") }; int m_choicePadShapeNChoices = sizeof( m_choicePadShapeChoices ) / sizeof( wxString ); m_choicePadShape = new wxChoice( m_panelSettings, IDC_PADSHAPE, wxDefaultPosition, wxDefaultSize, m_choicePadShapeNChoices, m_choicePadShapeChoices, 0 ); m_choicePadShape->SetSelection( 0 ); m_choicePadShape->SetToolTip( wxT("The shape of the pads\n(except any secondary pad, such as a thermal pad).") ); - + fgSidePanel->Add( m_choicePadShape, 0, wxBOTTOM|wxRIGHT|wxLEFT, 3 ); - + m_lblPadSize = new wxStaticText( m_panelSettings, wxID_ANY, wxT("Pad size"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblPadSize->Wrap( -1 ); fgSidePanel->Add( m_lblPadSize, 0, wxALL, 5 ); - + wxBoxSizer* bSizerPadSize; bSizerPadSize = new wxBoxSizer( wxHORIZONTAL ); - + m_txtPadWidth = new wxTextCtrl( m_panelSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); m_txtPadWidth->SetToolTip( wxT("The width of the pads, in mm.") ); m_txtPadWidth->SetMinSize( wxSize( 55,-1 ) ); - + bSizerPadSize->Add( m_txtPadWidth, 0, wxBOTTOM|wxLEFT, 3 ); - + m_txtPadLength = new wxTextCtrl( m_panelSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); m_txtPadLength->SetToolTip( wxT("The length of the pads, in mm.") ); m_txtPadLength->SetMinSize( wxSize( 55,-1 ) ); - + bSizerPadSize->Add( m_txtPadLength, 0, wxBOTTOM|wxRIGHT, 3 ); - - + + m_lblPadRadius = new wxStaticText( m_panelSettings, wxID_ANY, wxT("radius"), wxDefaultPosition, wxDefaultSize, 0 ); + m_lblPadRadius->Wrap( -1 ); + bSizerPadSize->Add( m_lblPadRadius, 0, wxALL, 5 ); + + m_txtPadRadius = new wxTextCtrl( m_panelSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); + m_txtPadRadius->Hide(); + m_txtPadRadius->SetToolTip( wxT("Rounded corner size, in percent of the pad size (range: 1..50).") ); + m_txtPadRadius->SetMinSize( wxSize( 25,-1 ) ); + + bSizerPadSize->Add( m_txtPadRadius, 0, wxBOTTOM|wxRIGHT, 5 ); + + fgSidePanel->Add( bSizerPadSize, 1, wxEXPAND, 5 ); - + m_lblPitch = new wxStaticText( m_panelSettings, wxID_ANY, wxT("Pitch"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblPitch->Wrap( -1 ); fgSidePanel->Add( m_lblPitch, 0, wxALL, 5 ); - + m_txtPitch = new wxTextCtrl( m_panelSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); m_txtPitch->SetToolTip( wxT("Pad pitch, in mm.") ); m_txtPitch->SetMinSize( wxSize( 55,-1 ) ); - + fgSidePanel->Add( m_txtPitch, 0, wxBOTTOM|wxRIGHT|wxLEFT, 3 ); - + m_lblPadSpan = new wxStaticText( m_panelSettings, wxID_ANY, wxT("Span"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblPadSpan->Wrap( -1 ); fgSidePanel->Add( m_lblPadSpan, 0, wxALL, 5 ); - + wxBoxSizer* bSizerPadSpan; bSizerPadSpan = new wxBoxSizer( wxHORIZONTAL ); - + m_txtPadSpanX = new wxTextCtrl( m_panelSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); m_txtPadSpanX->SetToolTip( wxT("The distance between two rows of pads, in the horizontal direction, in mm.") ); m_txtPadSpanX->SetMinSize( wxSize( 55,-1 ) ); - + bSizerPadSpan->Add( m_txtPadSpanX, 0, wxBOTTOM|wxLEFT, 3 ); - + m_txtPadSpanY = new wxTextCtrl( m_panelSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); m_txtPadSpanY->SetToolTip( wxT("The distance between two rows of pads, in the vertical direction, in mm.") ); m_txtPadSpanY->SetMinSize( wxSize( 55,-1 ) ); - + bSizerPadSpan->Add( m_txtPadSpanY, 0, wxBOTTOM|wxRIGHT, 3 ); - - + + fgSidePanel->Add( bSizerPadSpan, 1, wxEXPAND, 5 ); - + m_lblDrillSize = new wxStaticText( m_panelSettings, wxID_ANY, wxT("Drill size"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblDrillSize->Wrap( -1 ); fgSidePanel->Add( m_lblDrillSize, 0, wxALL, 5 ); - + m_txtDrillSize = new wxTextCtrl( m_panelSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); m_txtDrillSize->SetToolTip( wxT("The drill size in mm, or zero for no hole.") ); m_txtDrillSize->SetMinSize( wxSize( 55,-1 ) ); - + fgSidePanel->Add( m_txtDrillSize, 0, wxBOTTOM|wxRIGHT|wxLEFT, 3 ); - + m_lblAuxPad = new wxStaticText( m_panelSettings, wxID_ANY, wxT("Aux. pad"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblAuxPad->Wrap( -1 ); fgSidePanel->Add( m_lblAuxPad, 0, wxALL, 5 ); - + wxBoxSizer* bSizerAuxPad; bSizerAuxPad = new wxBoxSizer( wxHORIZONTAL ); - + m_txtAuxPadWidth = new wxTextCtrl( m_panelSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); m_txtAuxPadWidth->SetToolTip( wxT("The width of an auxiliary pad, for example, for a thermal pad.") ); m_txtAuxPadWidth->SetMinSize( wxSize( 55,-1 ) ); - + bSizerAuxPad->Add( m_txtAuxPadWidth, 0, wxBOTTOM|wxLEFT, 3 ); - + m_txtAuxPadLength = new wxTextCtrl( m_panelSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); m_txtAuxPadLength->SetToolTip( wxT("The length of an auxiliary pad, for example, for a thermal pad.") ); m_txtAuxPadLength->SetMinSize( wxSize( 55,-1 ) ); - + bSizerAuxPad->Add( m_txtAuxPadLength, 0, wxBOTTOM|wxRIGHT, 3 ); - - + + fgSidePanel->Add( bSizerAuxPad, 1, wxEXPAND, 5 ); - + m_lblBodySize = new wxStaticText( m_panelSettings, wxID_ANY, wxT("Body size"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblBodySize->Wrap( -1 ); fgSidePanel->Add( m_lblBodySize, 0, wxALL, 5 ); - + wxBoxSizer* bSizerBodySize; bSizerBodySize = new wxBoxSizer( wxHORIZONTAL ); - + m_txtBodyWidth = new wxTextCtrl( m_panelSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); m_txtBodyWidth->SetToolTip( wxT("The width of the body of the component, in mm.\nIf this field is disabled, either\n1) the footprint/symbol is not based on a template,\n2) changing the body size is disabled in the template, or\n3) rebuilding the footprint/symbol from the template is disabled in the preferences.") ); m_txtBodyWidth->SetMinSize( wxSize( 55,-1 ) ); - + bSizerBodySize->Add( m_txtBodyWidth, 0, wxBOTTOM|wxLEFT, 3 ); - + m_txtBodyLength = new wxTextCtrl( m_panelSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); m_txtBodyLength->SetToolTip( wxT("The length of the body of the component, in mm.\n\nIf this field is disabled, either\n1) the footprint/symbol is not based on a template,\n2) changing the body size is disabled in the template, or\n3) rebuilding the footprint/symbol from the template is disabled in the preferences.") ); m_txtBodyLength->SetMinSize( wxSize( 55,-1 ) ); - + bSizerBodySize->Add( m_txtBodyLength, 0, wxBOTTOM|wxRIGHT, 3 ); - - + + fgSidePanel->Add( bSizerBodySize, 1, wxEXPAND, 5 ); - + m_lblRefLabel = new wxStaticText( m_panelSettings, wxID_ANY, wxT("Ref. label"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblRefLabel->Wrap( -1 ); fgSidePanel->Add( m_lblRefLabel, 0, wxALL, 5 ); - + wxBoxSizer* bSizerRefLabel; bSizerRefLabel = new wxBoxSizer( wxHORIZONTAL ); - + m_txtRefLabel = new wxTextCtrl( m_panelSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); m_txtRefLabel->SetToolTip( wxT("The text size, in mm.") ); m_txtRefLabel->SetMinSize( wxSize( 55,-1 ) ); - + bSizerRefLabel->Add( m_txtRefLabel, 0, wxBOTTOM|wxLEFT, 3 ); - + m_chkRefLabelVisible = new wxCheckBox( m_panelSettings, wxID_ANY, wxT("visible"), wxDefaultPosition, wxDefaultSize, 0 ); m_chkRefLabelVisible->SetToolTip( wxT("Whether the reference label is visible by default.") ); - + bSizerRefLabel->Add( m_chkRefLabelVisible, 0, wxALL, 5 ); - - + + fgSidePanel->Add( bSizerRefLabel, 1, wxEXPAND, 5 ); - + m_lblValueLabel = new wxStaticText( m_panelSettings, wxID_ANY, wxT("Value label"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblValueLabel->Wrap( -1 ); fgSidePanel->Add( m_lblValueLabel, 0, wxALL, 5 ); - + wxBoxSizer* bSizerValueLabel; bSizerValueLabel = new wxBoxSizer( wxHORIZONTAL ); - + m_txtValueLabel = new wxTextCtrl( m_panelSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); m_txtValueLabel->SetToolTip( wxT("The text size, in mm.") ); m_txtValueLabel->SetMinSize( wxSize( 55,-1 ) ); - + bSizerValueLabel->Add( m_txtValueLabel, 0, wxBOTTOM|wxLEFT, 3 ); - + m_chkValueLabelVisible = new wxCheckBox( m_panelSettings, wxID_ANY, wxT("visible"), wxDefaultPosition, wxDefaultSize, 0 ); m_chkValueLabelVisible->SetToolTip( wxT("Whether the value label is visible by default.") ); - + bSizerValueLabel->Add( m_chkValueLabelVisible, 0, wxALL, 5 ); - - + + fgSidePanel->Add( bSizerValueLabel, 1, wxEXPAND, 5 ); - + m_lblShape = new wxStaticText( m_panelSettings, wxID_ANY, wxT("Shape"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblShape->Wrap( -1 ); fgSidePanel->Add( m_lblShape, 0, wxALL, 5 ); - + wxBoxSizer* bSizerShape; bSizerShape = new wxBoxSizer( wxHORIZONTAL ); - + wxArrayString m_choiceShapeChoices; m_choiceShape = new wxChoice( m_panelSettings, IDC_BODYSHAPE, wxDefaultPosition, wxDefaultSize, m_choiceShapeChoices, 0 ); m_choiceShape->SetSelection( 0 ); m_choiceShape->SetToolTip( wxT("The model for the 3D package.") ); - + bSizerShape->Add( m_choiceShape, 1, wxLEFT, 3 ); - + m_txtShapeHeight = new wxTextCtrl( m_panelSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); m_txtShapeHeight->SetToolTip( wxT("The height of the 3D package, in mm.") ); m_txtShapeHeight->SetMinSize( wxSize( 45,-1 ) ); - + bSizerShape->Add( m_txtShapeHeight, 0, wxBOTTOM|wxRIGHT|wxLEFT, 3 ); - - + + fgSidePanel->Add( bSizerShape, 1, wxEXPAND, 5 ); - - + + fgSidePanel->Add( 0, 0, 1, wxEXPAND, 5 ); - + wxBoxSizer* bSizerSaveRevert; bSizerSaveRevert = new wxBoxSizer( wxHORIZONTAL ); - + m_btnSavePart = new wxButton( m_panelSettings, IDT_SAVE, wxT("Save"), wxDefaultPosition, wxDefaultSize, 0 ); m_btnSavePart->SetToolTip( wxT("Save the modified data (Ctrl + S).") ); m_btnSavePart->SetMinSize( wxSize( 54,-1 ) ); - + bSizerSaveRevert->Add( m_btnSavePart, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); - + m_btnRevertPart = new wxButton( m_panelSettings, IDT_REVERT, wxT("Revert"), wxDefaultPosition, wxDefaultSize, 0 ); m_btnRevertPart->SetToolTip( wxT("Undo modifications and restore to the last saved state (Ctrl + Z).") ); m_btnRevertPart->SetMinSize( wxSize( 54,-1 ) ); - + bSizerSaveRevert->Add( m_btnRevertPart, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); - - + + fgSidePanel->Add( bSizerSaveRevert, 1, wxEXPAND, 5 ); - - + + m_panelSettings->SetSizer( fgSidePanel ); m_panelSettings->Layout(); fgSidePanel->Fit( m_panelSettings ); m_splitterViewPanel->SplitVertically( m_panelView, m_panelSettings, -200 ); bSizerBottom->Add( m_splitterViewPanel, 1, wxEXPAND, 5 ); - - + + m_panelBottom->SetSizer( bSizerBottom ); m_panelBottom->Layout(); bSizerBottom->Fit( m_panelBottom ); m_splitter->SplitHorizontally( m_panelTop, m_panelBottom, 185 ); bMainSizer->Add( m_splitter, 1, wxEXPAND, 5 ); - - + + this->SetSizer( bMainSizer ); this->Layout(); - m_statusBar = this->CreateStatusBar( 1, wxST_SIZEGRIP, wxID_ANY ); - + m_statusBar = this->CreateStatusBar( 1, wxSTB_SIZEGRIP, wxID_ANY ); + // Connect Events this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( AppFrame::OnCloseApp ) ); - this->Connect( m_mnuNew->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnNewLibrary ) ); - this->Connect( m_mnuRenameLibrary->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnRenameLibrary ) ); - this->Connect( m_mnuNewFootprint->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnNewFootprint ) ); - this->Connect( m_mnuNewSymbol->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnNewSymbol ) ); - this->Connect( m_mnuFootprintSheet->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnFootprintReport ) ); - this->Connect( m_mnuSymbolSheet->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnSymbolReport ) ); - this->Connect( m_mnuQuit->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnQuit ) ); - this->Connect( m_mnuFootprintMode->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnFootprintMode ) ); - this->Connect( m_mnuSymbolMode->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnSymbolMode ) ); - this->Connect( m_mnuCompareMode->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnCompareMode ) ); - this->Connect( m_mnuDetailsPanel->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnDetailsPanel ) ); - this->Connect( m_mnuSyncMode->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnSyncMode ) ); - this->Connect( m_mnuFilter->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnFilterToggle ) ); - this->Connect( m_mnuSearchPaths->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnSearchPaths ) ); - this->Connect( m_mnuRemoteLink->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnRemoteLink ) ); - this->Connect( m_mnuReport->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnReportSettings ) ); - this->Connect( m_mnuTemplate->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnTemplateOptions ) ); - this->Connect( m_mnuUI->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnUIOptions ) ); - this->Connect( m_mnuHelpManual->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnHelp ) ); - this->Connect( m_mnuAbout->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnAbout ) ); + m_mnuFile->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnNewLibrary ), this, m_mnuNew->GetId()); + m_mnuFile->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnRenameLibrary ), this, m_mnuRenameLibrary->GetId()); + m_mnuFile->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnNewFootprint ), this, m_mnuNewFootprint->GetId()); + m_mnuFile->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnNewSymbol ), this, m_mnuNewSymbol->GetId()); + m_mnuFile->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnFootprintReport ), this, m_mnuFootprintSheet->GetId()); + m_mnuFile->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnSymbolReport ), this, m_mnuSymbolSheet->GetId()); + m_mnuFile->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnQuit ), this, m_mnuQuit->GetId()); + m_mnuView->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnFootprintMode ), this, m_mnuFootprintMode->GetId()); + m_mnuView->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnSymbolMode ), this, m_mnuSymbolMode->GetId()); + m_mnuView->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnCompareMode ), this, m_mnuCompareMode->GetId()); + m_mnuView->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnDetailsPanel ), this, m_mnuDetailsPanel->GetId()); + m_mnuView->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnSyncMode ), this, m_mnuSyncMode->GetId()); + m_mnuView->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnFilterToggle ), this, m_mnuFilter->GetId()); + m_mnuPreferences->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnSearchPaths ), this, m_mnuSearchPaths->GetId()); + m_mnuPreferences->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnRemoteLink ), this, m_mnuRemoteLink->GetId()); + m_mnuPreferences->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnReportSettings ), this, m_mnuReport->GetId()); + m_mnuPreferences->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnTemplateOptions ), this, m_mnuTemplate->GetId()); + m_mnuPreferences->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnUIOptions ), this, m_mnuUI->GetId()); + m_mnuHelp->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnHelp ), this, m_mnuHelpManual->GetId()); + m_mnuHelp->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnAbout ), this, m_mnuAbout->GetId()); m_choiceModuleLeft->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( AppFrame::OnLeftLibSelect ), NULL, this ); m_listModulesLeft->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( AppFrame::OnLeftModSelect ), NULL, this ); m_btnMove->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AppFrame::OnMovePart ), NULL, this ); @@ -658,6 +669,8 @@ AppFrame::AppFrame( wxWindow* parent, wxWindowID id, const wxString& title, cons m_txtPadLength->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( AppFrame::OnKillFocusPadInfo ), NULL, this ); m_txtPadLength->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( AppFrame::OnTextFieldChange ), NULL, this ); m_txtPadLength->Connect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( AppFrame::OnEnterPadInfo ), NULL, this ); + m_txtPadRadius->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( AppFrame::OnTextFieldChange ), NULL, this ); + m_txtPadRadius->Connect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( AppFrame::OnEnterPadInfo ), NULL, this ); m_txtPitch->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( AppFrame::OnKillFocusPitchInfo ), NULL, this ); m_txtPitch->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( AppFrame::OnTextFieldChange ), NULL, this ); m_txtPitch->Connect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( AppFrame::OnEnterPitchInfo ), NULL, this ); @@ -703,26 +716,6 @@ AppFrame::~AppFrame() { // Disconnect Events this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( AppFrame::OnCloseApp ) ); - this->Disconnect( wxID_NEW, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnNewLibrary ) ); - this->Disconnect( IDM_RENAMELIBRAY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnRenameLibrary ) ); - this->Disconnect( IDM_NEWFOOTPRINT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnNewFootprint ) ); - this->Disconnect( IDM_NEWSYMBOL, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnNewSymbol ) ); - this->Disconnect( IDM_REPORTFOOTPRINT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnFootprintReport ) ); - this->Disconnect( IDM_REPORTSYMBOL, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnSymbolReport ) ); - this->Disconnect( wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnQuit ) ); - this->Disconnect( IDM_FOOTPRINTMODE, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnFootprintMode ) ); - this->Disconnect( IDM_SCHEMATICMODE, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnSymbolMode ) ); - this->Disconnect( IDM_COMPAREMODE, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnCompareMode ) ); - this->Disconnect( IDM_DETAILSPANEL, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnDetailsPanel ) ); - this->Disconnect( IDM_SYNCMODE, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnSyncMode ) ); - this->Disconnect( IDM_FILTER, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnFilterToggle ) ); - this->Disconnect( wxID_PREFERENCES, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnSearchPaths ) ); - this->Disconnect( IDM_DLGREMOTE, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnRemoteLink ) ); - this->Disconnect( IDM_DLGREPORT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnReportSettings ) ); - this->Disconnect( IDM_TEMPLATEOPTIONS, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnTemplateOptions ) ); - this->Disconnect( IDM_DLGOPTIONS, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnUIOptions ) ); - this->Disconnect( wxID_HELP, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnHelp ) ); - this->Disconnect( wxID_ABOUT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( AppFrame::OnAbout ) ); m_choiceModuleLeft->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( AppFrame::OnLeftLibSelect ), NULL, this ); m_listModulesLeft->Disconnect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( AppFrame::OnLeftModSelect ), NULL, this ); m_btnMove->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AppFrame::OnMovePart ), NULL, this ); @@ -770,6 +763,8 @@ AppFrame::~AppFrame() m_txtPadLength->Disconnect( wxEVT_KILL_FOCUS, wxFocusEventHandler( AppFrame::OnKillFocusPadInfo ), NULL, this ); m_txtPadLength->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( AppFrame::OnTextFieldChange ), NULL, this ); m_txtPadLength->Disconnect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( AppFrame::OnEnterPadInfo ), NULL, this ); + m_txtPadRadius->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( AppFrame::OnTextFieldChange ), NULL, this ); + m_txtPadRadius->Disconnect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( AppFrame::OnEnterPadInfo ), NULL, this ); m_txtPitch->Disconnect( wxEVT_KILL_FOCUS, wxFocusEventHandler( AppFrame::OnKillFocusPitchInfo ), NULL, this ); m_txtPitch->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( AppFrame::OnTextFieldChange ), NULL, this ); m_txtPitch->Disconnect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( AppFrame::OnEnterPitchInfo ), NULL, this ); @@ -809,95 +804,100 @@ AppFrame::~AppFrame() m_btnSavePart->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AppFrame::OnSavePart ), NULL, this ); m_btnRevertPart->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AppFrame::OnRevertPart ), NULL, this ); m_statusBar->Disconnect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( AppFrame::OnStatusBarDblClk ), NULL, this ); - + } DlgPaths::DlgPaths( 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( 400,300 ), wxDefaultSize ); - + this->SetSizeHints( wxSize( 400,400 ), wxDefaultSize ); + wxBoxSizer* bSizerMain; bSizerMain = new wxBoxSizer( wxVERTICAL ); - + m_lblFootprintPaths = new wxStaticText( this, wxID_ANY, wxT("Footprint libraries search paths"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblFootprintPaths->Wrap( -1 ); bSizerMain->Add( m_lblFootprintPaths, 0, wxTOP|wxRIGHT|wxLEFT, 8 ); - + wxBoxSizer* bSizerFootprints; bSizerFootprints = new wxBoxSizer( wxHORIZONTAL ); - - m_lstFootprints = new wxListBox( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_HSCROLL|wxLB_SINGLE|wxLB_SORT ); + + m_lstFootprints = new wxListBox( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_HSCROLL|wxLB_SINGLE|wxLB_SORT ); m_lstFootprints->SetToolTip( wxT("The librarian searches for footprint libraries in these paths.\n(Note that the librarian does not recurse into sub-paths.)") ); m_lstFootprints->SetMinSize( wxSize( 200,-1 ) ); - + bSizerFootprints->Add( m_lstFootprints, 1, wxALL|wxEXPAND, 5 ); - + wxBoxSizer* bSizerButtonsFootprint; bSizerButtonsFootprint = new wxBoxSizer( wxVERTICAL ); - + m_btnAddFootprint = new wxButton( this, wxID_ADD, wxT("Add"), wxDefaultPosition, wxDefaultSize, 0 ); m_btnAddFootprint->SetToolTip( wxT("Add a search path for footprint libraries.") ); - + bSizerButtonsFootprint->Add( m_btnAddFootprint, 0, wxALL, 5 ); - + m_btnRemoveFootprint = new wxButton( this, wxID_REMOVE, wxT("Remove"), wxDefaultPosition, wxDefaultSize, 0 ); m_btnRemoveFootprint->SetToolTip( wxT("Remove the currently selected path.") ); - + bSizerButtonsFootprint->Add( m_btnRemoveFootprint, 0, wxALL, 5 ); - - + + bSizerFootprints->Add( bSizerButtonsFootprint, 0, wxEXPAND, 5 ); - - + + bSizerMain->Add( bSizerFootprints, 1, wxEXPAND, 5 ); - + m_lblSchematicPath = new wxStaticText( this, wxID_ANY, wxT("Schematic symbol libraries search paths"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblSchematicPath->Wrap( -1 ); bSizerMain->Add( m_lblSchematicPath, 0, wxTOP|wxRIGHT|wxLEFT, 8 ); - + wxBoxSizer* bSizerSymbol; bSizerSymbol = new wxBoxSizer( wxHORIZONTAL ); - - m_lstSymbols = new wxListBox( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_HSCROLL|wxLB_SINGLE|wxLB_SORT ); + + m_lstSymbols = new wxListBox( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_HSCROLL|wxLB_SINGLE|wxLB_SORT ); m_lstSymbols->SetToolTip( wxT("The librarian searches for symbol libraries in these paths.\n(Note that the librarian does not recurse into sub-paths.)") ); - + bSizerSymbol->Add( m_lstSymbols, 1, wxALL|wxEXPAND, 5 ); - + wxBoxSizer* bSizerButtonsSchematic; bSizerButtonsSchematic = new wxBoxSizer( wxVERTICAL ); - + m_btnAddSymbol = new wxButton( this, wxID_ANY, wxT("Add"), wxDefaultPosition, wxDefaultSize, 0 ); m_btnAddSymbol->SetToolTip( wxT("Add a search path for symbol libraries.") ); - + bSizerButtonsSchematic->Add( m_btnAddSymbol, 0, wxALL, 5 ); - + m_btnRemoveSymbol = new wxButton( this, wxID_ANY, wxT("Remove"), wxDefaultPosition, wxDefaultSize, 0 ); m_btnRemoveSymbol->SetToolTip( wxT("Remove the currently selected path.") ); - + bSizerButtonsSchematic->Add( m_btnRemoveSymbol, 0, wxALL, 5 ); - - + + bSizerSymbol->Add( bSizerButtonsSchematic, 0, wxEXPAND, 5 ); - - + + bSizerMain->Add( bSizerSymbol, 1, wxEXPAND, 5 ); - + + m_chkRecurseDirectories = new wxCheckBox( this, wxID_ANY, wxT("Collect libraries in folders and subfolders recursively"), wxDefaultPosition, wxDefaultSize, 0 ); + m_chkRecurseDirectories->SetToolTip( wxT("Collect libraries (for footprints and symbols) in the listed search paths, plus in any sub-paths below the listed search paths.") ); + + bSizerMain->Add( m_chkRecurseDirectories, 0, 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_sdbSizer->Realize(); - + bSizerMain->Add( m_sdbSizer, 0, wxEXPAND|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - + + this->SetSizer( bSizerMain ); this->Layout(); bSizerMain->Fit( this ); - + this->Centre( wxBOTH ); - + // Connect Events m_lstFootprints->Connect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( DlgPaths::OnFootprintPathSelect ), NULL, this ); m_btnAddFootprint->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DlgPaths::OnAddFootprintPath ), NULL, this ); @@ -918,121 +918,126 @@ DlgPaths::~DlgPaths() m_btnAddSymbol->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DlgPaths::OnAddSymbolPath ), NULL, this ); m_btnRemoveSymbol->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DlgPaths::OnRemoveSymbolPath ), NULL, this ); m_sdbSizerOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DlgPaths::OnOK ), NULL, this ); - + } DlgReport::DlgReport( 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* bSizerColumns; bSizerColumns = new wxBoxSizer( wxHORIZONTAL ); - + wxBoxSizer* bSizerPage; bSizerPage = new wxBoxSizer( wxVERTICAL ); - + wxFlexGridSizer* fgSizerPage; fgSizerPage = new wxFlexGridSizer( 0, 2, 0, 0 ); fgSizerPage->SetFlexibleDirection( wxBOTH ); fgSizerPage->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - + m_lblPageSize = new wxStaticText( this, wxID_ANY, wxT("Page size"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblPageSize->Wrap( -1 ); fgSizerPage->Add( m_lblPageSize, 0, wxALL, 5 ); - + wxString m_choicePageSizeChoices[] = { wxT("A3"), wxT("A4"), wxT("Executive"), wxT("Legal"), wxT("Letter") }; int m_choicePageSizeNChoices = sizeof( m_choicePageSizeChoices ) / sizeof( wxString ); m_choicePageSize = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choicePageSizeNChoices, m_choicePageSizeChoices, 0 ); m_choicePageSize->SetSelection( 0 ); m_choicePageSize->SetToolTip( wxT("The paper size for the PDF report.") ); - + fgSizerPage->Add( m_choicePageSize, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 ); - - + + bSizerPage->Add( fgSizerPage, 0, wxTOP|wxEXPAND, 5 ); - + wxString m_radioLayoutChoices[] = { wxT("Portrait"), wxT("Landscape") }; int m_radioLayoutNChoices = sizeof( m_radioLayoutChoices ) / sizeof( wxString ); m_radioLayout = new wxRadioBox( this, wxID_ANY, wxT("Lay-out"), wxDefaultPosition, wxDefaultSize, m_radioLayoutNChoices, m_radioLayoutChoices, 2, wxRA_SPECIFY_COLS ); m_radioLayout->SetSelection( 0 ); m_radioLayout->SetToolTip( wxT("Page lay-out, portrait or landscape.") ); - + bSizerPage->Add( m_radioLayout, 0, wxALL|wxEXPAND, 5 ); - + wxFlexGridSizer* fgSizerFont; fgSizerFont = new wxFlexGridSizer( 1, 3, 0, 0 ); fgSizerFont->SetFlexibleDirection( wxBOTH ); fgSizerFont->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - + m_lblFontSize = new wxStaticText( this, wxID_ANY, wxT("Font size"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblFontSize->Wrap( -1 ); fgSizerFont->Add( m_lblFontSize, 0, wxTOP|wxLEFT, 5 ); - + m_spinFontSize = new wxSpinCtrl( this, wxID_ANY, wxT("0"), wxDefaultPosition, wxSize( -1,-1 ), wxSP_ARROW_KEYS, 8, 32, 8 ); m_spinFontSize->SetToolTip( wxT("The font size of the dimensions, in points.") ); m_spinFontSize->SetMinSize( wxSize( 50,-1 ) ); m_spinFontSize->SetMaxSize( wxSize( 100,-1 ) ); - + fgSizerFont->Add( m_spinFontSize, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - + m_lblFontUnit = new wxStaticText( this, wxID_ANY, wxT("point"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblFontUnit->Wrap( -1 ); fgSizerFont->Add( m_lblFontUnit, 0, wxTOP, 5 ); - - + + bSizerPage->Add( fgSizerFont, 0, wxEXPAND|wxBOTTOM, 5 ); - - + + bSizerColumns->Add( bSizerPage, 1, wxEXPAND|wxBOTTOM|wxRIGHT, 5 ); - + wxBoxSizer* bSizerOptions; bSizerOptions = new wxBoxSizer( wxVERTICAL ); - + m_chkDescription = new wxCheckBox( this, wxID_ANY, wxT("Include des&cription"), wxDefaultPosition, wxDefaultSize, 0 ); m_chkDescription->SetToolTip( wxT("Include the description of the component in the report.") ); - + bSizerOptions->Add( m_chkDescription, 0, wxALL, 5 ); - + m_chkValueLabels = new wxCheckBox( this, wxID_ANY, wxT("Draw &labels"), wxDefaultPosition, wxDefaultSize, 0 ); m_chkValueLabels->SetToolTip( wxT("Draw the reference and value labels in the footprint.\n(Footprint reports only.)") ); - + bSizerOptions->Add( m_chkValueLabels, 0, wxALL, 5 ); - + m_chkPadInfo = new wxCheckBox( this, wxID_ANY, wxT("Include &pad information"), wxDefaultPosition, wxDefaultSize, 0 ); m_chkPadInfo->SetToolTip( wxT("Include pad pitch and size information in the report.\n(Footprint reports only.)") ); - + bSizerOptions->Add( m_chkPadInfo, 0, wxALL, 5 ); - + m_chkFPList = new wxCheckBox( this, wxID_ANY, wxT("Include footprint list"), wxDefaultPosition, wxDefaultSize, 0 ); m_chkFPList->SetToolTip( wxT("Include the list of suitable footprints in the report.\n(Symbol reports only.)") ); - + bSizerOptions->Add( m_chkFPList, 0, wxALL, 5 ); - - + + m_chkIndex = new wxCheckBox( this, wxID_ANY, wxT("Include index"), wxDefaultPosition, wxDefaultSize, 0 ); + m_chkIndex->SetToolTip( wxT("Include a sorted index of all symbols or footprints and their aliases.") ); + + bSizerOptions->Add( m_chkIndex, 0, wxALL, 5 ); + + bSizerColumns->Add( bSizerOptions, 1, wxEXPAND|wxBOTTOM|wxLEFT, 5 ); - - + + bSizerMain->Add( bSizerColumns, 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(); - + bSizerMain->Add( m_sdbSizer, 0, wxEXPAND|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - + + this->SetSizer( bSizerMain ); this->Layout(); bSizerMain->Fit( this ); - + this->Centre( wxBOTH ); - + // Connect Events m_sdbSizerOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DlgReport::OnOK ), NULL, this ); } @@ -1041,179 +1046,179 @@ DlgReport::~DlgReport() { // Disconnect Events m_sdbSizerOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DlgReport::OnOK ), NULL, this ); - + } DlgOptions::DlgOptions( 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* bSizerOptionsMain; bSizerOptionsMain = new wxBoxSizer( wxVERTICAL ); - + wxFlexGridSizer* fgSizerFont; fgSizerFont = new wxFlexGridSizer( 3, 3, 0, 0 ); fgSizerFont->SetFlexibleDirection( wxBOTH ); fgSizerFont->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - + m_lblFontSize = new wxStaticText( this, wxID_ANY, wxT("Font size"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblFontSize->Wrap( -1 ); m_lblFontSize->SetToolTip( wxT("Font size used in the viewport for dimensions, pin numbers and other miscellaneous labels") ); - + fgSizerFont->Add( m_lblFontSize, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); - + m_spinFontSize = new wxSpinCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxSP_ARROW_KEYS, 5, 20, 8 ); m_spinFontSize->SetToolTip( wxT("The font size of the dimensions and pin numbers, in point.") ); m_spinFontSize->SetMinSize( wxSize( 50,-1 ) ); m_spinFontSize->SetMaxSize( wxSize( 100,-1 ) ); - + fgSizerFont->Add( m_spinFontSize, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - + m_lblFontUnit = new wxStaticText( this, wxID_ANY, wxT("point"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblFontUnit->Wrap( -1 ); fgSizerFont->Add( m_lblFontUnit, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); - + m_lblDimOffset = new wxStaticText( this, wxID_ANY, wxT("Dimension offset"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblDimOffset->Wrap( -1 ); m_lblDimOffset->SetToolTip( wxT("The offset (in pixels) of the dimensions from the pads in a footprint.") ); - + fgSizerFont->Add( m_lblDimOffset, 0, wxALL, 5 ); - + m_spinDimOffset = new wxSpinCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 10, 200, 49 ); m_spinDimOffset->SetToolTip( wxT("The (minumum) offset of the dimensions from the pads, in pixels.") ); m_spinDimOffset->SetMinSize( wxSize( 50,-1 ) ); m_spinDimOffset->SetMaxSize( wxSize( 100,-1 ) ); - + fgSizerFont->Add( m_spinDimOffset, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - + m_lblDimUnit = new wxStaticText( this, wxID_ANY, wxT("pixels"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblDimUnit->Wrap( -1 ); fgSizerFont->Add( m_lblDimUnit, 0, wxALL, 5 ); - - + + bSizerOptionsMain->Add( fgSizerFont, 0, wxEXPAND|wxTOP, 5 ); - + wxBoxSizer* bSizerOptions; bSizerOptions = new wxBoxSizer( wxVERTICAL ); - + m_chkDrawLabels = new wxCheckBox( this, wxID_ANY, wxT("Draw footprint/symbol labels"), wxDefaultPosition, wxDefaultSize, 0 ); m_chkDrawLabels->SetToolTip( wxT("Draw the reference and value labels in the footprint or schematic symbol.") ); - + bSizerOptions->Add( m_chkDrawLabels, 0, wxALL, 5 ); - + m_chkDrawCentreCross = new wxCheckBox( this, wxID_ANY, wxT("Draw centre cross (footprints)"), wxDefaultPosition, wxDefaultSize, 0 ); m_chkDrawCentreCross->SetToolTip( wxT("Draw a cross in the centre position, to mark the \"pick-up\" position for the package.") ); - + bSizerOptions->Add( m_chkDrawCentreCross, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - + m_chkFullPaths = new wxCheckBox( this, wxID_ANY, wxT("Show full library paths"), wxDefaultPosition, wxDefaultSize, 0 ); m_chkFullPaths->SetToolTip( wxT("If checked. shows the full path to each library in the footprint/symbol lists.\nIf not checked, only shows the base filenames.") ); - + bSizerOptions->Add( m_chkFullPaths, 0, wxRIGHT|wxLEFT, 5 ); - + m_staticline1 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); bSizerOptions->Add( m_staticline1, 0, wxEXPAND | wxALL, 5 ); - + wxFlexGridSizer* fgSizer8; fgSizer8 = new wxFlexGridSizer( 0, 2, 0, 0 ); fgSizer8->SetFlexibleDirection( wxBOTH ); fgSizer8->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - + m_lblClrFootprint = new wxStaticText( this, wxID_ANY, wxT("Footprint mode colour"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblClrFootprint->Wrap( -1 ); m_lblClrFootprint->SetToolTip( wxT("The background colour of the viewport in \"footprint\" mode.") ); - + fgSizer8->Add( m_lblClrFootprint, 0, wxALL, 5 ); - + m_colourFootprint = new wxColourPickerCtrl( this, wxID_ANY, *wxBLACK, wxDefaultPosition, wxDefaultSize, wxCLRP_DEFAULT_STYLE ); m_colourFootprint->SetToolTip( wxT("The background colour of the viewport in \"footprint\" mode.") ); - + fgSizer8->Add( m_colourFootprint, 0, wxRIGHT|wxLEFT, 5 ); - + m_lblClrSymbol = new wxStaticText( this, wxID_ANY, wxT("Schematic mode colour"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblClrSymbol->Wrap( -1 ); m_lblClrSymbol->SetToolTip( wxT("The background colour of the viewport in \"schematic\" mode.") ); - + fgSizer8->Add( m_lblClrSymbol, 0, wxALL, 5 ); - + m_colourSchematic = new wxColourPickerCtrl( this, wxID_ANY, *wxBLACK, wxDefaultPosition, wxDefaultSize, wxCLRP_DEFAULT_STYLE ); m_colourSchematic->SetToolTip( wxT("The background colour of the viewport in \"schematic\" mode.") ); - + fgSizer8->Add( m_colourSchematic, 0, wxRIGHT|wxLEFT, 5 ); - + m_lblClr3DModel = new wxStaticText( this, wxID_ANY, wxT("3D Model view colour"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblClr3DModel->Wrap( -1 ); m_lblClr3DModel->SetToolTip( wxT("The background colour of the viewport when viewing the 3D model (in \"footprint\" mode).") ); - + fgSizer8->Add( m_lblClr3DModel, 0, wxALL, 5 ); - + m_colour3DModel = new wxColourPickerCtrl( this, wxID_ANY, *wxBLACK, wxDefaultPosition, wxDefaultSize, wxCLRP_DEFAULT_STYLE ); m_colour3DModel->SetToolTip( wxT("The background colour of the viewport when viewing the 3D model (in \"footprint\" mode).") ); - + fgSizer8->Add( m_colour3DModel, 0, wxRIGHT|wxLEFT, 5 ); - - + + bSizerOptions->Add( fgSizer8, 1, wxEXPAND, 5 ); - + wxGridSizer* gSizer1; gSizer1 = new wxGridSizer( 0, 2, 0, 0 ); - - + + bSizerOptions->Add( gSizer1, 1, wxEXPAND, 5 ); - + m_staticline11 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); bSizerOptions->Add( m_staticline11, 0, wxEXPAND | wxALL, 5 ); - + m_chkCopyVRML = new wxCheckBox( this, wxID_ANY, wxT("Copy 3D models"), wxDefaultPosition, wxDefaultSize, 0 ); m_chkCopyVRML->SetToolTip( wxT("Copy the VRML models when moving/copying the footprints to other libraries.\nVRML models are not deleted when deleting footprints, and not renamed.") ); - + bSizerOptions->Add( m_chkCopyVRML, 0, wxALL, 5 ); - + m_chkDisableTemplates = new wxCheckBox( this, wxID_ANY, wxT("Disable edits via templates"), wxDefaultPosition, wxDefaultSize, 0 ); m_chkDisableTemplates->SetToolTip( wxT("By default, if you edit the parameters of a footprint or symbol and that footprint/symbol is based on a template, the Librarian rebuilds the part from the template.\nHowever, this looses changes that you may have made in the KiCad module editor.\nPutting a checkmark in this option disables rebuilding from a template.") ); - + bSizerOptions->Add( m_chkDisableTemplates, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - + m_staticline2 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); bSizerOptions->Add( m_staticline2, 0, wxEXPAND | wxALL, 5 ); - + m_chkConfirmOverwrite = new wxCheckBox( this, wxID_ANY, wxT("Confirm overwrite"), wxDefaultPosition, wxDefaultSize, 0 ); m_chkConfirmOverwrite->SetToolTip( wxT("Whether to show a confirmation box when copying/moving a symbol or footprint would overwrite an existing footprint.") ); - + bSizerOptions->Add( m_chkConfirmOverwrite, 0, wxALL, 5 ); - + m_chkConfirmDelete = new wxCheckBox( this, wxID_ANY, wxT("Confirm deletion"), wxDefaultPosition, wxDefaultSize, 0 ); m_chkConfirmDelete->SetToolTip( wxT("Whether to show a confirmation box when deleting a symbol or footprint.") ); - + bSizerOptions->Add( m_chkConfirmDelete, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - + m_staticline4 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); bSizerOptions->Add( m_staticline4, 0, wxEXPAND | wxALL, 5 ); - + m_chkReloadSession = new wxCheckBox( this, wxID_ANY, wxT("Reload recent libraries on start-up"), wxDefaultPosition, wxDefaultSize, 0 ); m_chkReloadSession->SetToolTip( wxT("Whether to load the libraries that were open on the last run of the program.") ); - + bSizerOptions->Add( m_chkReloadSession, 0, wxALL, 5 ); - - + + bSizerOptionsMain->Add( bSizerOptions, 1, wxEXPAND|wxBOTTOM, 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(); - + bSizerOptionsMain->Add( m_sdbSizer, 0, wxEXPAND|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - + + this->SetSizer( bSizerOptionsMain ); this->Layout(); bSizerOptionsMain->Fit( this ); - + this->Centre( wxBOTH ); - + // Connect Events m_sdbSizerOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DlgOptions::OnOK ), NULL, this ); } @@ -1222,31 +1227,31 @@ DlgOptions::~DlgOptions() { // Disconnect Events m_sdbSizerOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DlgOptions::OnOK ), NULL, this ); - + } DlgTemplateOpts::DlgTemplateOpts( 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( 300,300 ), wxDefaultSize ); - + wxBoxSizer* bSizerDlg; bSizerDlg = new wxBoxSizer( wxVERTICAL ); - + m_lblVariables = new wxStaticText( this, wxID_ANY, wxT("Template variables"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblVariables->Wrap( -1 ); m_lblVariables->SetToolTip( wxT("Template variables, used in footprints that are created from a template") ); - + bSizerDlg->Add( m_lblVariables, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); - + m_gridTemplateVars = new wxGrid( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); - + // Grid m_gridTemplateVars->CreateGrid( 5, 3 ); m_gridTemplateVars->EnableEditing( true ); m_gridTemplateVars->EnableGridLines( true ); m_gridTemplateVars->EnableDragGridSize( false ); m_gridTemplateVars->SetMargins( 0, 0 ); - + // Columns m_gridTemplateVars->AutoSizeColumns(); m_gridTemplateVars->EnableDragColMove( false ); @@ -1255,42 +1260,42 @@ DlgTemplateOpts::DlgTemplateOpts( wxWindow* parent, wxWindowID id, const wxStrin m_gridTemplateVars->SetColLabelValue( 0, wxT("Description") ); m_gridTemplateVars->SetColLabelValue( 1, wxT("Name") ); m_gridTemplateVars->SetColLabelValue( 2, wxT("Value") ); - m_gridTemplateVars->SetColLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE ); - + m_gridTemplateVars->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER ); + // Rows m_gridTemplateVars->EnableDragRowSize( true ); m_gridTemplateVars->SetRowLabelSize( 0 ); - m_gridTemplateVars->SetRowLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE ); - + m_gridTemplateVars->SetRowLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER ); + // Label Appearance - + // Cell Defaults m_gridTemplateVars->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_TOP ); m_gridTemplateVars->SetToolTip( wxT("Dimensions used in templates. Dimensions are in mm, except the Solder Paste Reduction (in percent).") ); - + bSizerDlg->Add( m_gridTemplateVars, 1, wxEXPAND|wxALL, 5 ); - + m_txtInfo = new wxTextCtrl( this, wxID_ANY, wxT("You can only change the values of the predefined variables in the list. When a value is set, it overrides the value that the template specifies. When a value is empty, the default from the template is used."), wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_NO_VSCROLL|wxTE_READONLY|wxTE_WORDWRAP ); m_txtInfo->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - + bSizerDlg->Add( m_txtInfo, 0, wxALL|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(); - + bSizerDlg->Add( m_sdbSizer, 0, wxEXPAND|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - + + this->SetSizer( bSizerDlg ); this->Layout(); bSizerDlg->Fit( this ); - + this->Centre( wxBOTH ); - + // Connect Events m_sdbSizerOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DlgTemplateOpts::OnOK ), NULL, this ); } @@ -1299,104 +1304,105 @@ DlgTemplateOpts::~DlgTemplateOpts() { // Disconnect Events m_sdbSizerOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DlgTemplateOpts::OnOK ), NULL, this ); - + } DlgNewFootprint::DlgNewFootprint( 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* bSizerListInfo; bSizerListInfo = new wxBoxSizer( wxHORIZONTAL ); - + wxBoxSizer* bSizerListName; bSizerListName = new wxBoxSizer( wxVERTICAL ); - - m_lstTemplates = new wxListBox( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_HSCROLL|wxLB_NEEDED_SB|wxLB_SINGLE ); + + m_lstTemplates = new wxListBox( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_HSCROLL|wxLB_NEEDED_SB|wxLB_SINGLE ); m_lstTemplates->SetToolTip( wxT("These are template files.\nChoose one that best matches the footprint that you need to make.") ); - + m_lstTemplates->SetMinSize( wxSize( 300,-1 ) ); + bSizerListName->Add( m_lstTemplates, 1, wxALL|wxEXPAND, 5 ); - + wxBoxSizer* bSizerNameInfo; bSizerNameInfo = new wxBoxSizer( wxHORIZONTAL ); - + m_lblName = new wxStaticText( this, wxID_ANY, wxT("Footprint name"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblName->Wrap( -1 ); bSizerNameInfo->Add( m_lblName, 0, wxALL, 5 ); - + m_txtName = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); m_txtName->SetToolTip( wxT("Enter the name of the new footprint.") ); - + bSizerNameInfo->Add( m_txtName, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - + + bSizerListName->Add( bSizerNameInfo, 0, wxEXPAND, 5 ); - - + + bSizerListInfo->Add( bSizerListName, 1, wxEXPAND, 5 ); - + wxBoxSizer* bSizerExample; bSizerExample = new wxBoxSizer( wxVERTICAL ); - + wxBoxSizer* bSiserPreview; bSiserPreview = new wxBoxSizer( wxHORIZONTAL ); - - m_bmpExample = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 128,128 ), wxSTATIC_BORDER ); + + m_bmpExample = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 128,128 ), wxBORDER_STATIC ); m_bmpExample->SetToolTip( wxT("Preview of the currently selected template (with default parameters).") ); - + bSiserPreview->Add( m_bmpExample, 0, wxALL, 5 ); - + wxBoxSizer* bSizerScrollButtons; bSizerScrollButtons = new wxBoxSizer( wxVERTICAL ); - - + + bSizerScrollButtons->Add( 0, 0, 1, wxEXPAND, 5 ); - + m_spinPreview = new wxSpinButton( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_VERTICAL ); m_spinPreview->SetToolTip( wxT("Browse through the preview images.") ); - + bSizerScrollButtons->Add( m_spinPreview, 0, wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - + + bSizerScrollButtons->Add( 0, 0, 1, wxEXPAND, 5 ); - - + + bSiserPreview->Add( bSizerScrollButtons, 1, wxEXPAND, 5 ); - - + + bSizerExample->Add( bSiserPreview, 0, wxEXPAND, 5 ); - + m_txtDescription = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY ); m_txtDescription->SetToolTip( wxT("Description and elements of the currently selected template.") ); - m_txtDescription->SetMinSize( wxSize( -1,50 ) ); - + m_txtDescription->SetMinSize( wxSize( -1,82 ) ); + bSizerExample->Add( m_txtDescription, 1, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 ); - - + + bSizerListInfo->Add( bSizerExample, 0, wxEXPAND, 5 ); - - + + bSizerMain->Add( bSizerListInfo, 1, wxEXPAND, 5 ); - + m_sdbSizerOkCancel = new wxStdDialogButtonSizer(); m_sdbSizerOkCancelOK = new wxButton( this, wxID_OK ); m_sdbSizerOkCancel->AddButton( m_sdbSizerOkCancelOK ); m_sdbSizerOkCancelCancel = new wxButton( this, wxID_CANCEL ); m_sdbSizerOkCancel->AddButton( m_sdbSizerOkCancelCancel ); m_sdbSizerOkCancel->Realize(); - + bSizerMain->Add( m_sdbSizerOkCancel, 0, wxEXPAND|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - + + this->SetSizer( bSizerMain ); this->Layout(); bSizerMain->Fit( this ); - + this->Centre( wxBOTH ); - + // Connect Events m_lstTemplates->Connect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( DlgNewFootprint::OnTemplateSelect ), NULL, this ); m_spinPreview->Connect( wxEVT_SCROLL_LINEDOWN, wxSpinEventHandler( DlgNewFootprint::OnNextImage ), NULL, this ); @@ -1411,136 +1417,136 @@ DlgNewFootprint::~DlgNewFootprint() m_spinPreview->Disconnect( wxEVT_SCROLL_LINEDOWN, wxSpinEventHandler( DlgNewFootprint::OnNextImage ), NULL, this ); m_spinPreview->Disconnect( wxEVT_SCROLL_LINEUP, wxSpinEventHandler( DlgNewFootprint::OnPrevImage ), NULL, this ); m_sdbSizerOkCancelOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DlgNewFootprint::OnOk ), NULL, this ); - + } DlgRemoteLink::DlgRemoteLink( 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 ); - + wxFlexGridSizer* fgSizer; fgSizer = new wxFlexGridSizer( 0, 2, 0, 0 ); fgSizer->SetFlexibleDirection( wxBOTH ); fgSizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - + m_lblURL = new wxStaticText( this, wxID_ANY, wxT("&URL"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblURL->Wrap( -1 ); fgSizer->Add( m_lblURL, 0, wxALL, 5 ); - + m_txtURL = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); m_txtURL->SetToolTip( wxT("The complete URL to the repository. (Mandatory)") ); m_txtURL->SetMinSize( wxSize( 250,-1 ) ); - + fgSizer->Add( m_txtURL, 1, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 ); - + m_lblUserName = new wxStaticText( this, wxID_ANY, wxT("User &name"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblUserName->Wrap( -1 ); fgSizer->Add( m_lblUserName, 0, wxALL, 5 ); - + m_txtUserName = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); m_txtUserName->SetToolTip( wxT("The user name to access the repository. (Mandatory)") ); - + fgSizer->Add( m_txtUserName, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - + m_lblPassword = new wxStaticText( this, wxID_ANY, wxT("&Password"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblPassword->Wrap( -1 ); fgSizer->Add( m_lblPassword, 0, wxALL, 5 ); - + m_txtPassword = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PASSWORD ); m_txtPassword->SetToolTip( wxT("The password to access the repository. (Mandatory)") ); - + fgSizer->Add( m_txtPassword, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - + m_lblAuthentication = new wxStaticText( this, wxID_ANY, wxT("Authentication"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblAuthentication->Wrap( -1 ); fgSizer->Add( m_lblAuthentication, 0, wxALL, 5 ); - + wxBoxSizer* bSizerAuth; bSizerAuth = new wxBoxSizer( wxVERTICAL ); - + wxBoxSizer* bSizerAuthUser; bSizerAuthUser = new wxBoxSizer( wxHORIZONTAL ); - + m_lblAuthUser = new wxStaticText( this, wxID_ANY, wxT("user"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblAuthUser->Wrap( -1 ); bSizerAuthUser->Add( m_lblAuthUser, 0, wxALL, 5 ); - + m_txtAuthUser = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); m_txtAuthUser->SetToolTip( wxT("A user name for access to the server; this need not be the same as the user name for the repository access. (Optional)") ); - + bSizerAuthUser->Add( m_txtAuthUser, 1, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 ); - + m_lblAuthPwd = new wxStaticText( this, wxID_ANY, wxT("password"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblAuthPwd->Wrap( -1 ); bSizerAuthUser->Add( m_lblAuthPwd, 0, wxALL, 5 ); - + m_txtAuthPWD = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PASSWORD ); m_txtAuthPWD->SetToolTip( wxT("The password for access to the server, in the case of protected connections. (Optional)") ); - + bSizerAuthUser->Add( m_txtAuthPWD, 1, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 ); - - + + bSizerAuth->Add( bSizerAuthUser, 1, wxEXPAND, 5 ); - + wxBoxSizer* bSizerAuthOpt; bSizerAuthOpt = new wxBoxSizer( wxHORIZONTAL ); - + m_checkVerifyPeer = new wxCheckBox( this, wxID_ANY, wxT("Verify certificate"), wxDefaultPosition, wxDefaultSize, 0 ); m_checkVerifyPeer->SetToolTip( wxT("Verify that the certificate is issued by a CA in the list of trusted root CAs (and that it has not expired).") ); - + bSizerAuthOpt->Add( m_checkVerifyPeer, 0, wxALL, 5 ); - + m_checkVerifyHost = new wxCheckBox( this, wxID_ANY, wxT("Verify host name"), wxDefaultPosition, wxDefaultSize, 0 ); m_checkVerifyHost->SetToolTip( wxT("Verify that the host name matches the name in the certificate.") ); - + bSizerAuthOpt->Add( m_checkVerifyHost, 0, wxALL, 5 ); - - + + bSizerAuth->Add( bSizerAuthOpt, 1, wxEXPAND, 5 ); - - + + fgSizer->Add( bSizerAuth, 1, wxEXPAND, 5 ); - - + + fgSizer->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_lblHelp = new wxStaticText( this, wxID_ANY, wxT("Fill in the above fields. If you do not have an account yet, click on the button \"Sign up\"."), wxDefaultPosition, wxDefaultSize, 0|wxSTATIC_BORDER ); + + m_lblHelp = new wxStaticText( this, wxID_ANY, wxT("Fill in the above fields. If you do not have an account yet, click on the button \"Sign up\"."), wxDefaultPosition, wxDefaultSize, 0|wxBORDER_STATIC ); m_lblHelp->Wrap( 300 ); m_lblHelp->SetMinSize( wxSize( 300,-1 ) ); - + fgSizer->Add( m_lblHelp, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - + + bSizerMain->Add( fgSizer, 1, wxEXPAND|wxTOP|wxBOTTOM, 5 ); - + wxBoxSizer* bSizerButtons; bSizerButtons = new wxBoxSizer( wxHORIZONTAL ); - - + + bSizerButtons->Add( 0, 0, 1, wxEXPAND, 5 ); - + m_btnSignUp = new wxButton( this, wxID_ANY, wxT("Sign up"), wxDefaultPosition, wxDefaultSize, 0 ); bSizerButtons->Add( m_btnSignUp, 0, wxALL, 3 ); - + m_btnOK = new wxButton( this, wxID_ANY, wxT("OK"), wxDefaultPosition, wxDefaultSize, 0 ); bSizerButtons->Add( m_btnOK, 0, wxALL, 3 ); - + m_btnCancel = new wxButton( this, wxID_ANY, wxT("Cancel"), wxDefaultPosition, wxDefaultSize, 0 ); bSizerButtons->Add( m_btnCancel, 0, wxALL, 3 ); - - + + bSizerMain->Add( bSizerButtons, 0, wxEXPAND, 5 ); - - + + this->SetSizer( bSizerMain ); this->Layout(); bSizerMain->Fit( this ); - + this->Centre( wxBOTH ); - + // Connect Events m_btnSignUp->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DlgRemoteLink::OnSignUp ), NULL, this ); m_btnOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DlgRemoteLink::OnOK ), NULL, this ); @@ -1553,127 +1559,127 @@ DlgRemoteLink::~DlgRemoteLink() m_btnSignUp->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DlgRemoteLink::OnSignUp ), NULL, this ); m_btnOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DlgRemoteLink::OnOK ), NULL, this ); m_btnCancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DlgRemoteLink::OnCancel ), NULL, this ); - + } DlgRemoteSignUp::DlgRemoteSignUp( 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 ); - + wxFlexGridSizer* fgSizer; fgSizer = new wxFlexGridSizer( 0, 2, 0, 0 ); fgSizer->SetFlexibleDirection( wxBOTH ); fgSizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - + m_lblURL = new wxStaticText( this, wxID_ANY, wxT("&URL"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblURL->Wrap( -1 ); fgSizer->Add( m_lblURL, 0, wxALL, 5 ); - + m_txtURL = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); m_txtURL->SetToolTip( wxT("The complete URL to the repository. (Mandatory)") ); m_txtURL->SetMinSize( wxSize( 250,-1 ) ); - + fgSizer->Add( m_txtURL, 1, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 ); - + m_lblUserName = new wxStaticText( this, wxID_ANY, wxT("User &name"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblUserName->Wrap( -1 ); fgSizer->Add( m_lblUserName, 0, wxALL, 5 ); - + m_txtUserName = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); m_txtUserName->SetToolTip( wxT("The user name to access the repository. (Mandatory)") ); - + fgSizer->Add( m_txtUserName, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 ); - + m_lbEmail = new wxStaticText( this, wxID_ANY, wxT("&Email"), wxDefaultPosition, wxDefaultSize, 0 ); m_lbEmail->Wrap( -1 ); fgSizer->Add( m_lbEmail, 0, wxALL, 5 ); - + m_txtEmail = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); m_txtEmail->SetToolTip( wxT("The email address linked to the account. (Mandatory)") ); - + fgSizer->Add( m_txtEmail, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 ); - + m_lblAuthentication = new wxStaticText( this, wxID_ANY, wxT("Authentication"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblAuthentication->Wrap( -1 ); fgSizer->Add( m_lblAuthentication, 0, wxALL, 5 ); - + wxBoxSizer* bSizerAuth; bSizerAuth = new wxBoxSizer( wxVERTICAL ); - + wxBoxSizer* bSizerAuthUser; bSizerAuthUser = new wxBoxSizer( wxHORIZONTAL ); - + m_lblAuthUser = new wxStaticText( this, wxID_ANY, wxT("user"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblAuthUser->Wrap( -1 ); bSizerAuthUser->Add( m_lblAuthUser, 0, wxALL, 5 ); - + m_txtAuthUser = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); m_txtAuthUser->SetToolTip( wxT("A user name for access to the server; this need not be the same as the user name for the repository access. (Optional)") ); - + bSizerAuthUser->Add( m_txtAuthUser, 1, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 ); - + m_lblAuthPwd = new wxStaticText( this, wxID_ANY, wxT("password"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblAuthPwd->Wrap( -1 ); bSizerAuthUser->Add( m_lblAuthPwd, 0, wxALL, 5 ); - + m_txtAuthPWD = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PASSWORD ); m_txtAuthPWD->SetToolTip( wxT("The password for access to the server, in the case of protected connections. (Optional)") ); - + bSizerAuthUser->Add( m_txtAuthPWD, 1, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 ); - - + + bSizerAuth->Add( bSizerAuthUser, 1, wxEXPAND, 5 ); - + wxBoxSizer* bSizerAuthOpt; bSizerAuthOpt = new wxBoxSizer( wxHORIZONTAL ); - + m_checkVerifyPeer = new wxCheckBox( this, wxID_ANY, wxT("Verify certificate"), wxDefaultPosition, wxDefaultSize, 0 ); m_checkVerifyPeer->SetToolTip( wxT("Verify that the certificate is issued by a CA in the list of trusted root CAs (and that it has not expired).") ); - + bSizerAuthOpt->Add( m_checkVerifyPeer, 0, wxALL, 5 ); - + m_checkVerifyHost = new wxCheckBox( this, wxID_ANY, wxT("Verify host name"), wxDefaultPosition, wxDefaultSize, 0 ); m_checkVerifyHost->SetToolTip( wxT("Verify that the host name matches the name in the certificate.") ); - + bSizerAuthOpt->Add( m_checkVerifyHost, 0, wxALL, 5 ); - - + + bSizerAuth->Add( bSizerAuthOpt, 1, wxEXPAND, 5 ); - - + + fgSizer->Add( bSizerAuth, 1, wxEXPAND, 5 ); - - + + fgSizer->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_lblHelp = new wxStaticText( this, wxID_ANY, wxT("Fill in the above fields. You will receive a password by email."), wxDefaultPosition, wxDefaultSize, 0|wxSTATIC_BORDER ); + + m_lblHelp = new wxStaticText( this, wxID_ANY, wxT("Fill in the above fields. You will receive a password by email."), wxDefaultPosition, wxDefaultSize, 0|wxBORDER_STATIC ); m_lblHelp->Wrap( 300 ); m_lblHelp->SetMinSize( wxSize( 300,-1 ) ); - + fgSizer->Add( m_lblHelp, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - + + bSizerMain->Add( fgSizer, 1, wxEXPAND|wxTOP|wxBOTTOM, 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, wxEXPAND|wxALL, 5 ); - - + + this->SetSizer( bSizerMain ); this->Layout(); bSizerMain->Fit( this ); - + this->Centre( wxBOTH ); - + // Connect Events m_sdbSizerOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DlgRemoteSignUp::OnOK ), NULL, this ); } @@ -1682,92 +1688,92 @@ DlgRemoteSignUp::~DlgRemoteSignUp() { // Disconnect Events m_sdbSizerOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DlgRemoteSignUp::OnOK ), NULL, this ); - + } DlgNewSymbol::DlgNewSymbol( 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* bSizerListInfo; bSizerListInfo = new wxBoxSizer( wxHORIZONTAL ); - + wxBoxSizer* bSizerListName; bSizerListName = new wxBoxSizer( wxVERTICAL ); - - m_lstTemplates = new wxListBox( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_HSCROLL|wxLB_NEEDED_SB|wxLB_SINGLE ); + + m_lstTemplates = new wxListBox( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_HSCROLL|wxLB_NEEDED_SB|wxLB_SINGLE ); m_lstTemplates->SetToolTip( wxT("These are template files.\nChoose one that best matches the symbol that you need to make.") ); - + bSizerListName->Add( m_lstTemplates, 1, wxALL|wxEXPAND, 5 ); - + wxFlexGridSizer* fgSizerNameRef; fgSizerNameRef = new wxFlexGridSizer( 2, 2, 0, 0 ); fgSizerNameRef->SetFlexibleDirection( wxHORIZONTAL ); fgSizerNameRef->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - + m_lblName = new wxStaticText( this, wxID_ANY, wxT("Symbol name"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblName->Wrap( -1 ); fgSizerNameRef->Add( m_lblName, 0, wxALL, 5 ); - + m_txtName = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); m_txtName->SetToolTip( wxT("Enter the name of the new symbol.") ); - + fgSizerNameRef->Add( m_txtName, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - + m_lblPrefix = new wxStaticText( this, wxID_ANY, wxT("Prefix"), wxDefaultPosition, wxDefaultSize, 0 ); m_lblPrefix->Wrap( -1 ); fgSizerNameRef->Add( m_lblPrefix, 0, wxALL, 5 ); - + m_txtPrefix = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); m_txtPrefix->SetToolTip( wxT("Enter the prefix for the reference.\nFor example: U for a integrated circuit, R for a resistor, ...") ); - + fgSizerNameRef->Add( m_txtPrefix, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - + + bSizerListName->Add( fgSizerNameRef, 0, wxEXPAND, 5 ); - - + + bSizerListInfo->Add( bSizerListName, 1, wxEXPAND, 5 ); - + wxBoxSizer* bSizerExample; bSizerExample = new wxBoxSizer( wxVERTICAL ); - - m_bmpExample = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 128,128 ), wxSTATIC_BORDER ); + + m_bmpExample = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 128,128 ), wxBORDER_STATIC ); m_bmpExample->SetToolTip( wxT("Preview of the currently selected template (with default parameters).") ); - + bSizerExample->Add( m_bmpExample, 0, wxALL, 5 ); - + m_txtDescription = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY ); m_txtDescription->SetToolTip( wxT("Description and elements of the currently selected template.") ); m_txtDescription->SetMinSize( wxSize( -1,50 ) ); - + bSizerExample->Add( m_txtDescription, 1, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 ); - - + + bSizerListInfo->Add( bSizerExample, 0, wxEXPAND, 5 ); - - + + bSizerMain->Add( bSizerListInfo, 1, wxEXPAND, 5 ); - + m_sdbSizerOkCancel = new wxStdDialogButtonSizer(); m_sdbSizerOkCancelOK = new wxButton( this, wxID_OK ); m_sdbSizerOkCancel->AddButton( m_sdbSizerOkCancelOK ); m_sdbSizerOkCancelCancel = new wxButton( this, wxID_CANCEL ); m_sdbSizerOkCancel->AddButton( m_sdbSizerOkCancelCancel ); m_sdbSizerOkCancel->Realize(); - + bSizerMain->Add( m_sdbSizerOkCancel, 0, wxEXPAND|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - + + this->SetSizer( bSizerMain ); this->Layout(); bSizerMain->Fit( this ); - + this->Centre( wxBOTH ); - + // Connect Events m_lstTemplates->Connect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( DlgNewSymbol::OnTemplateSelect ), NULL, this ); m_sdbSizerOkCancelOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DlgNewSymbol::OnOk ), NULL, this ); @@ -1778,5 +1784,5 @@ DlgNewSymbol::~DlgNewSymbol() // Disconnect Events m_lstTemplates->Disconnect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( DlgNewSymbol::OnTemplateSelect ), NULL, this ); m_sdbSizerOkCancelOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DlgNewSymbol::OnOk ), NULL, this ); - + } diff --git a/src/libmngr_gui_base.fbp b/src/libmngr_gui_base.fbp index 3a1f169..49e2762 100644 --- a/src/libmngr_gui_base.fbp +++ b/src/libmngr_gui_base.fbp @@ -1,6 +1,6 @@ - + C++ @@ -14,6 +14,7 @@ libmngr_gui_base 1000 none + 0 libmngr_gui @@ -53,41 +54,7 @@ wxTAB_TRAVERSAL 1 - - - - - - - - - OnCloseApp - - - - - - - - - - - - - - - - - - - - - - - - - @@ -110,29 +77,6 @@ - - - - - - - - - - - - - - - - - - - - - - - &File m_mnuFile @@ -150,7 +94,6 @@ OnNewLibrary - @@ -165,7 +108,6 @@ OnRenameLibrary - m_separator2 @@ -184,7 +126,6 @@ OnNewFootprint - @@ -199,7 +140,6 @@ OnNewSymbol - m_separator5 @@ -218,7 +158,6 @@ OnFootprintReport - @@ -233,7 +172,6 @@ OnSymbolReport - m_separator3 @@ -252,7 +190,6 @@ Alt+F4 OnQuit - @@ -272,7 +209,6 @@ OnFootprintMode - @@ -287,7 +223,6 @@ OnSymbolMode - m_separator4 @@ -306,7 +241,6 @@ Ctrl+C OnCompareMode - @@ -321,7 +255,6 @@ Ctrl+D OnDetailsPanel - @@ -336,7 +269,6 @@ Ctrl+Y OnSyncMode - m_separator6 @@ -355,7 +287,6 @@ Ctrl+F OnFilterToggle - @@ -375,7 +306,6 @@ OnSearchPaths - @@ -390,7 +320,6 @@ OnRemoteLink - @@ -405,7 +334,6 @@ OnReportSettings - @@ -420,7 +348,6 @@ OnTemplateOptions - m_separator41 @@ -439,7 +366,6 @@ OnUIOptions - @@ -459,7 +385,6 @@ F1 OnHelp - @@ -474,7 +399,6 @@ OnAbout - @@ -544,33 +468,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - 1 @@ -623,29 +520,6 @@ wxTAB_TRAVERSAL - - - - - - - - - - - - - - - - - - - - - - - bHorSplitter1 @@ -722,30 +596,7 @@ - OnLeftLibSelect - - - - - - - - - - - - - - - - - - - - - - @@ -808,49 +659,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - OnLeftModSelect - - - - - - - - - - - - - - @@ -889,25 +698,31 @@ + 1 0 1 1 + 0 0 + Dock 0 Left 1 1 + 0 0 IDM_MOVEMODULE &Move + + 0 0 @@ -922,6 +737,8 @@ protected 1 + + Resizable 1 @@ -937,29 +754,6 @@ OnMovePart - - - - - - - - - - - - - - - - - - - - - - - @@ -977,25 +771,31 @@ + 1 0 1 1 + 0 0 + Dock 0 Left 1 1 + 0 0 IDM_COPYMODULE &Copy + + 0 0 @@ -1010,6 +810,8 @@ protected 1 + + Resizable 1 @@ -1025,29 +827,6 @@ OnCopyPart - - - - - - - - - - - - - - - - - - - - - - - @@ -1065,25 +844,31 @@ + 1 0 1 1 + 0 0 + Dock 0 Left 1 1 + 0 0 IDM_DELETEMODULE &Delete + + 0 0 @@ -1098,6 +883,8 @@ protected 1 + + Resizable 1 @@ -1113,29 +900,6 @@ OnDeletePart - - - - - - - - - - - - - - - - - - - - - - - @@ -1153,25 +917,31 @@ + 1 0 1 1 + 0 0 + Dock 0 Left 1 1 + 0 0 IDM_RENAMEMODULE &Rename + + 0 0 @@ -1186,6 +956,8 @@ protected 1 + + Resizable 1 @@ -1201,29 +973,6 @@ OnRenamePart - - - - - - - - - - - - - - - - - - - - - - - @@ -1241,25 +990,31 @@ + 1 0 1 1 + 0 0 + Dock 0 Left 1 1 + 0 0 IDM_RENAMEMODULE D&uplicate + + 0 0 @@ -1274,6 +1029,8 @@ protected 1 + + Resizable 1 @@ -1289,29 +1046,6 @@ OnDuplicatePart - - - - - - - - - - - - - - - - - - - - - - - @@ -1397,30 +1131,7 @@ - OnRightLibSelect - - - - - - - - - - - - - - - - - - - - - - @@ -1483,49 +1194,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - OnRightModSelect - - - - - - - - - - - - - - @@ -1585,29 +1254,6 @@ wxTAB_TRAVERSAL - - - - - - - - - - - - - - - - - - - - - - - bSizerBottom @@ -1676,29 +1322,6 @@ - - - - - - - - - - - - - - - - - - - - - - - Load From Embedded File; res/ZoomIn.png 0 @@ -1709,16 +1332,7 @@ protected Zoom in (Ctrl +). - - - - - - OnZoomIn - - - Load From Embedded File; res/ZoomOut.png @@ -1730,16 +1344,7 @@ protected Zoom out (Ctrl -). - - - - - - OnZoomOut - - - Load From Embedded File; res/View3D.png @@ -1751,16 +1356,7 @@ protected - - - - - - On3DView - - - Load From Embedded File; res/Measure.png @@ -1772,16 +1368,7 @@ protected Show pitch and pad measurements. - - - - - - OnShowMeasurements - - - Load From Embedded File; res/Information.png @@ -1793,16 +1380,7 @@ protected Show/hide the side panel with parameters. - - - - - - OnShowDetails - - - Load From Embedded File; res/ArrowLeft.png @@ -1814,16 +1392,7 @@ protected Show left footprint. - - - - - - OnShowLeftFootprint - - - Load From Embedded File; res/ArrowRight.png @@ -1835,16 +1404,7 @@ protected Show right footprint. - - - - - - OnShowRightFootprint - - - @@ -1909,33 +1469,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - OnDetailsPanelUnsplit - 1 @@ -1988,29 +1522,11 @@ wxTAB_TRAVERSAL - - - - - - - OnViewCentre OnViewStartDrag - - - - OnViewDrag - - OnPaintViewport - - - - OnSizeViewport - @@ -2067,29 +1583,6 @@ wxTAB_TRAVERSAL|wxVSCROLL - - - - - - - - - - - - - - - - - - - - - - - 2 wxBOTH @@ -2135,6 +1628,7 @@ 0 wxID_ANY View + 0 0 @@ -2160,29 +1654,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -2256,30 +1727,7 @@ - - - - - - - - - - - - - - - - - OnShowLeftDetails - - - - - - @@ -2344,30 +1792,7 @@ - - - - - - - - - - - - - - - - - OnShowRightDetails - - - - - - @@ -2403,6 +1828,7 @@ 0 wxID_ANY Unit + 0 0 @@ -2428,36 +1854,13 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - - + 5 wxBOTTOM|wxRIGHT|wxLEFT 0 - + 1 1 1 @@ -2513,32 +1916,7 @@ - - - - - - - - - - - - - - - - - - - - - - OnUnitSelect - - - @@ -2576,6 +1954,7 @@ 0 wxID_ANY Description + 0 0 @@ -2601,29 +1980,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -2688,33 +2044,9 @@ - - - - - OnKillFocusTextInfo - - - - - - - - - - - - - - - - OnTextFieldChange OnEnterTextInfo - - - @@ -2750,6 +2082,7 @@ 0 wxID_ANY Alias + 0 0 @@ -2775,29 +2108,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -2862,33 +2172,9 @@ - - - - - OnKillFocusTextInfo - - - - - - - - - - - - - - - - OnTextFieldChange OnEnterTextInfo - - - @@ -2924,6 +2210,7 @@ 1 wxID_ANY Footprints + 0 0 @@ -2949,29 +2236,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -3036,33 +2300,9 @@ - - - - - OnKillFocusTextInfo - - - - - - - - - - - - - - - - OnTextFieldChange OnEnterTextInfo - - - @@ -3098,6 +2338,7 @@ 0 wxID_ANY Pad count + 0 0 @@ -3123,29 +2364,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -3210,33 +2428,9 @@ - - - - - OnKillFocusPadCount - - - - - - - - - - - - - - - - OnTextFieldChange OnEnterPadCount - - - @@ -3272,6 +2466,7 @@ 1 wxID_ANY Pins + 0 0 @@ -3297,29 +2492,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -3348,10 +2520,10 @@ wxALIGN_TOP 0 1 - wxALIGN_CENTRE + wxALIGN_CENTER 16 "pin" "label" "type" "style" "side" - wxALIGN_CENTRE + wxALIGN_CENTER 5 @@ -3394,10 +2566,10 @@ 1 Resizable - wxALIGN_CENTRE + wxALIGN_CENTER 0 - wxALIGN_CENTRE + wxALIGN_CENTER 2 1 @@ -3407,62 +2579,10 @@ Pin numbers, labels and properties. The "type" is the electrical type. The "style" is the graphic shape. The position can only be changed for symbols that are based on a template. - wxSUNKEN_BORDER - - - + wxBORDER_SUNKEN OnPinNameChanged - - OnPinRightClick - - - - - - - - - - - - - - - - - - - - - - - - - - - - OnPinNameRearrange - - - - - - - - - - - - - - - - - - - @@ -3498,6 +2618,7 @@ 0 wxID_ANY Pad shape + 0 0 @@ -3523,29 +2644,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -3566,7 +2664,7 @@ 1 0 - "(varies)" "Rectangular" "Round" "Round + square" "Obround" "Trapezoid" + "(varies)" "Round" "Round + square" "Rectangular" "Rounded rectangle" "Obround" "Trapezoid" 1 1 @@ -3610,30 +2708,7 @@ - OnChoiceFieldChange - - - - - - - - - - - - - - - - - - - - - - @@ -3669,6 +2744,7 @@ 0 wxID_ANY Pad size + 0 0 @@ -3694,29 +2770,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -3790,33 +2843,9 @@ - - - - - OnKillFocusPadInfo - - - - - - - - - - - - - - - - OnTextFieldChange OnEnterPadInfo - - - @@ -3881,33 +2910,136 @@ - - - - - OnKillFocusPadInfo - - - - - - - - - - - - - - - - OnTextFieldChange OnEnterPadInfo - - - + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + radius + 0 + + 0 + + + 0 + + 1 + m_lblPadRadius + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxBOTTOM|wxRIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 1 + wxID_ANY + + 0 + + + + 0 + 25,-1 + 1 + m_txtPadRadius + 1 + + + protected + 1 + + Resizable + 1 + + wxTE_PROCESS_ENTER + ; ; forward_declare + 0 + Rounded corner size, in percent of the pad size (range: 1..50). + + wxFILTER_NONE + wxDefaultValidator + + + + + + OnTextFieldChange + OnEnterPadInfo @@ -3945,6 +3077,7 @@ 0 wxID_ANY Pitch + 0 0 @@ -3970,29 +3103,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -4057,33 +3167,9 @@ - - - - - OnKillFocusPitchInfo - - - - - - - - - - - - - - - - OnTextFieldChange OnEnterPitchInfo - - - @@ -4119,6 +3205,7 @@ 0 wxID_ANY Span + 0 0 @@ -4144,29 +3231,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -4240,33 +3304,9 @@ - - - - - OnKillFocusSpanInfo - - - - - - - - - - - - - - - - OnTextFieldChange OnEnterSpanInfo - - - @@ -4331,33 +3371,9 @@ - - - - - OnKillFocusSpanInfo - - - - - - - - - - - - - - - - OnTextFieldChange OnEnterSpanInfo - - - @@ -4395,6 +3411,7 @@ 0 wxID_ANY Drill size + 0 0 @@ -4420,29 +3437,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -4507,33 +3501,9 @@ - - - - - OnKillFocusPadInfo - - - - - - - - - - - - - - - - OnTextFieldChange OnEnterPadInfo - - - @@ -4569,6 +3539,7 @@ 0 wxID_ANY Aux. pad + 0 0 @@ -4594,29 +3565,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -4690,33 +3638,9 @@ - - - - - OnKillFocusPadInfo - - - - - - - - - - - - - - - - OnTextFieldChange OnEnterPadInfo - - - @@ -4781,33 +3705,9 @@ - - - - - OnKillFocusPadInfo - - - - - - - - - - - - - - - - OnTextFieldChange OnEnterPadInfo - - - @@ -4845,6 +3745,7 @@ 0 wxID_ANY Body size + 0 0 @@ -4870,29 +3771,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -4966,33 +3844,9 @@ - - - - - OnKillFocusBodyInfo - - - - - - - - - - - - - - - - OnTextFieldChange OnEnterBodyInfo - - - @@ -5057,33 +3911,9 @@ - - - - - OnKillFocusBodyInfo - - - - - - - - - - - - - - - - OnTextFieldChange OnEnterBodyInfo - - - @@ -5121,6 +3951,7 @@ 0 wxID_ANY Ref. label + 0 0 @@ -5146,29 +3977,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -5242,33 +4050,9 @@ - - - - - OnKillFocusLabelField - - - - - - - - - - - - - - - - OnTextFieldChange OnEnterLabelField - - - @@ -5333,30 +4117,7 @@ - OnLabelShowHide - - - - - - - - - - - - - - - - - - - - - - @@ -5394,6 +4155,7 @@ 0 wxID_ANY Value label + 0 0 @@ -5419,29 +4181,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -5515,33 +4254,9 @@ - - - - - OnKillFocusLabelField - - - - - - - - - - - - - - - - OnTextFieldChange OnEnterLabelField - - - @@ -5606,30 +4321,7 @@ - OnLabelShowHide - - - - - - - - - - - - - - - - - - - - - - @@ -5667,6 +4359,7 @@ 0 wxID_ANY Shape + 0 0 @@ -5692,29 +4385,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -5788,30 +4458,7 @@ - OnChoiceFieldChange - - - - - - - - - - - - - - - - - - - - - - @@ -5876,33 +4523,9 @@ - - - - - OnKillFocusShapeHeight - - - - - - - - - - - - - - - - OnTextFieldChange OnEnterShapeHeight - - - @@ -5941,25 +4564,31 @@ + 1 0 1 1 + 0 0 + Dock 0 Left 1 1 + 0 0 IDT_SAVE Save + + 0 0 @@ -5974,6 +4603,8 @@ protected 1 + + Resizable 1 @@ -5989,29 +4620,6 @@ OnSavePart - - - - - - - - - - - - - - - - - - - - - - - @@ -6029,25 +4637,31 @@ + 1 0 1 1 + 0 0 + Dock 0 Left 1 1 + 0 0 IDT_REVERT Revert + + 0 0 @@ -6062,6 +4676,8 @@ protected 1 + + Resizable 1 @@ -6077,29 +4693,6 @@ OnRevertPart - - - - - - - - - - - - - - - - - - - - - - - @@ -6131,35 +4724,13 @@ protected - wxST_SIZEGRIP + wxSTB_SIZEGRIP - - - - - - - OnStatusBarDblClk - - - - - - - - - - - - - - - @@ -6177,7 +4748,7 @@ 0 wxID_ANY - 400,300 + 400,400 DlgPaths -1,-1 @@ -6188,42 +4759,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bSizerMain @@ -6262,6 +4797,7 @@ 0 wxID_ANY Footprint libraries search paths + 0 0 @@ -6287,29 +4823,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -6382,31 +4895,7 @@ - - - - - - - - - - OnFootprintPathSelect - - - - - - - - - - - - - - @@ -6433,25 +4922,31 @@ + 1 0 1 1 + 0 0 + Dock 0 Left 1 1 + 0 0 wxID_ADD Add + + 0 0 @@ -6466,6 +4961,8 @@ protected 1 + + Resizable 1 @@ -6481,29 +4978,6 @@ OnAddFootprintPath - - - - - - - - - - - - - - - - - - - - - - - @@ -6521,25 +4995,31 @@ + 1 0 1 1 + 0 0 + Dock 0 Left 1 1 + 0 0 wxID_REMOVE Remove + + 0 0 @@ -6554,6 +5034,8 @@ protected 1 + + Resizable 1 @@ -6569,29 +5051,6 @@ OnRemoveFootprintPath - - - - - - - - - - - - - - - - - - - - - - - @@ -6631,6 +5090,7 @@ 0 wxID_ANY Schematic symbol libraries search paths + 0 0 @@ -6656,29 +5116,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -6751,31 +5188,7 @@ - - - - - - - - - - OnSymbolPathSelect - - - - - - - - - - - - - - @@ -6802,25 +5215,31 @@ + 1 0 1 1 + 0 0 + Dock 0 Left 1 1 + 0 0 wxID_ANY Add + + 0 0 @@ -6835,6 +5254,8 @@ protected 1 + + Resizable 1 @@ -6850,29 +5271,6 @@ OnAddSymbolPath - - - - - - - - - - - - - - - - - - - - - - - @@ -6890,25 +5288,31 @@ + 1 0 1 1 + 0 0 + Dock 0 Left 1 1 + 0 0 wxID_ANY Remove + + 0 0 @@ -6923,6 +5327,8 @@ protected 1 + + Resizable 1 @@ -6938,35 +5344,76 @@ OnRemoveSymbolPath - - - - - - - - - - - - - - - - - - - - - - - + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Collect libraries in folders and subfolders recursively + + 0 + + + 0 + + 1 + m_chkRecurseDirectories + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + Collect libraries (for footprints and symbols) in the listed search paths, plus in any sub-paths below the listed search paths. + + wxFILTER_NONE + wxDefaultValidator + + + + + + 5 wxEXPAND|wxTOP|wxBOTTOM|wxRIGHT @@ -6983,14 +5430,7 @@ m_sdbSizer protected - - - - - OnOK - - @@ -7021,42 +5461,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bSizerMain @@ -7129,6 +5533,7 @@ 0 wxID_ANY Page size + 0 0 @@ -7154,29 +5559,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -7241,30 +5623,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -7333,30 +5691,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -7408,6 +5742,7 @@ 0 wxID_ANY Font size + 0 0 @@ -7433,29 +5768,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -7518,32 +5830,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -7579,6 +5865,7 @@ 0 wxID_ANY point + 0 0 @@ -7604,29 +5891,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -7704,30 +5968,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -7792,30 +6032,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -7880,30 +6096,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -7968,30 +6160,70 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Include index + + 0 + + + 0 + + 1 + m_chkIndex + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + Include a sorted index of all symbols or footprints and their aliases. + + wxFILTER_NONE + wxDefaultValidator + + + + @@ -8014,14 +6246,7 @@ m_sdbSizer protected - - - - - OnOK - - @@ -8052,42 +6277,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bSizerOptionsMain @@ -8142,6 +6331,7 @@ 0 wxID_ANY Font size + 0 0 @@ -8167,29 +6357,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -8252,32 +6419,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -8313,6 +6454,7 @@ 0 wxID_ANY point + 0 0 @@ -8338,29 +6480,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -8396,6 +6515,7 @@ 0 wxID_ANY Dimension offset + 0 0 @@ -8421,29 +6541,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -8506,32 +6603,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -8567,6 +6638,7 @@ 0 wxID_ANY pixels + 0 0 @@ -8592,29 +6664,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -8690,30 +6739,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -8778,30 +6803,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -8866,30 +6867,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -8948,29 +6925,6 @@ - - - - - - - - - - - - - - - - - - - - - - - @@ -9022,6 +6976,7 @@ 0 wxID_ANY Footprint mode colour + 0 0 @@ -9047,29 +7002,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -9133,30 +7065,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -9192,6 +7100,7 @@ 0 wxID_ANY Schematic mode colour + 0 0 @@ -9217,29 +7126,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -9303,30 +7189,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -9362,6 +7224,7 @@ 0 wxID_ANY 3D Model view colour + 0 0 @@ -9387,29 +7250,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -9473,30 +7313,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -9571,29 +7387,6 @@ - - - - - - - - - - - - - - - - - - - - - - - @@ -9658,30 +7451,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -9746,30 +7515,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -9828,29 +7573,6 @@ - - - - - - - - - - - - - - - - - - - - - - - @@ -9915,30 +7637,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -10003,30 +7701,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -10085,29 +7759,6 @@ - - - - - - - - - - - - - - - - - - - - - - - @@ -10172,30 +7823,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -10216,14 +7843,7 @@ m_sdbSizer protected - - - - - OnOK - - @@ -10254,42 +7874,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bSizerDlg @@ -10328,6 +7912,7 @@ 0 wxID_ANY Template variables + 0 0 @@ -10353,29 +7938,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -10404,10 +7966,10 @@ wxALIGN_TOP 0 1 - wxALIGN_CENTRE + wxALIGN_CENTER 20 "Description" "Name" "Value" - wxALIGN_CENTRE + wxALIGN_CENTER 3 @@ -10450,10 +8012,10 @@ 1 Resizable - wxALIGN_CENTRE + wxALIGN_CENTER 0 - wxALIGN_CENTRE + wxALIGN_CENTER 5 1 @@ -10464,61 +8026,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -10583,33 +8090,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -10628,14 +8108,7 @@ m_sdbSizer protected - - - - - OnOK - - @@ -10666,42 +8139,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bSizerMain @@ -10763,7 +8200,7 @@ 0 - + 300,-1 1 m_lstTemplates 1 @@ -10786,31 +8223,7 @@ - - - - - - - - - - OnTemplateSelect - - - - - - - - - - - - - - @@ -10855,6 +8268,7 @@ 0 wxID_ANY Footprint name + 0 0 @@ -10880,29 +8294,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -10967,33 +8358,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -11073,30 +8437,7 @@ Preview of the currently selected template (with default parameters). - wxSTATIC_BORDER - - - - - - - - - - - - - - - - - - - - - - - + wxBORDER_STATIC @@ -11174,32 +8515,8 @@ - - - - - - - - - - - - - - - - - - - - - - - OnNextImage OnPrevImage - @@ -11254,7 +8571,7 @@ 0 0 - -1,50 + -1,82 1 m_txtDescription 1 @@ -11278,33 +8595,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -11327,14 +8617,7 @@ m_sdbSizerOkCancel protected - - - - - OnOk - - @@ -11365,42 +8648,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bSizerMain @@ -11455,6 +8702,7 @@ 0 wxID_ANY &URL + 0 0 @@ -11480,29 +8728,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -11567,33 +8792,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -11629,6 +8827,7 @@ 0 wxID_ANY User &name + 0 0 @@ -11654,29 +8853,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -11741,33 +8917,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -11803,6 +8952,7 @@ 0 wxID_ANY &Password + 0 0 @@ -11828,29 +8978,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -11915,33 +9042,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -11977,6 +9077,7 @@ 0 wxID_ANY Authentication + 0 0 @@ -12002,29 +9103,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -12078,6 +9156,7 @@ 0 wxID_ANY user + 0 0 @@ -12103,29 +9182,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -12190,33 +9246,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -12252,6 +9281,7 @@ 0 wxID_ANY password + 0 0 @@ -12277,29 +9307,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -12364,33 +9371,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -12466,30 +9446,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -12554,30 +9510,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -12627,6 +9559,7 @@ 0 wxID_ANY Fill in the above fields. If you do not have an account yet, click on the button "Sign up". + 0 0 @@ -12650,31 +9583,8 @@ - wxSTATIC_BORDER + wxBORDER_STATIC 300 - - - - - - - - - - - - - - - - - - - - - - - @@ -12713,25 +9623,31 @@ + 1 0 1 1 + 0 0 + Dock 0 Left 1 1 + 0 0 wxID_ANY Sign up + + 0 0 @@ -12746,6 +9662,8 @@ protected 1 + + Resizable 1 @@ -12761,29 +9679,6 @@ OnSignUp - - - - - - - - - - - - - - - - - - - - - - - @@ -12801,25 +9696,31 @@ + 1 0 1 1 + 0 0 + Dock 0 Left 1 1 + 0 0 wxID_ANY OK + + 0 0 @@ -12834,6 +9735,8 @@ protected 1 + + Resizable 1 @@ -12849,29 +9752,6 @@ OnOK - - - - - - - - - - - - - - - - - - - - - - - @@ -12889,25 +9769,31 @@ + 1 0 1 1 + 0 0 + Dock 0 Left 1 1 + 0 0 wxID_ANY Cancel + + 0 0 @@ -12922,6 +9808,8 @@ protected 1 + + Resizable 1 @@ -12937,29 +9825,6 @@ OnCancel - - - - - - - - - - - - - - - - - - - - - - - @@ -12992,42 +9857,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bSizerMain @@ -13082,6 +9911,7 @@ 0 wxID_ANY &URL + 0 0 @@ -13107,29 +9937,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -13194,33 +10001,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -13256,6 +10036,7 @@ 0 wxID_ANY User &name + 0 0 @@ -13281,29 +10062,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -13368,33 +10126,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -13430,6 +10161,7 @@ 0 wxID_ANY &Email + 0 0 @@ -13455,29 +10187,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -13542,33 +10251,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -13604,6 +10286,7 @@ 0 wxID_ANY Authentication + 0 0 @@ -13629,29 +10312,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -13705,6 +10365,7 @@ 0 wxID_ANY user + 0 0 @@ -13730,29 +10391,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -13817,33 +10455,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -13879,6 +10490,7 @@ 0 wxID_ANY password + 0 0 @@ -13904,29 +10516,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -13991,33 +10580,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -14093,30 +10655,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -14181,30 +10719,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -14254,6 +10768,7 @@ 0 wxID_ANY Fill in the above fields. You will receive a password by email. + 0 0 @@ -14277,31 +10792,8 @@ - wxSTATIC_BORDER + wxBORDER_STATIC 300 - - - - - - - - - - - - - - - - - - - - - - - @@ -14322,14 +10814,7 @@ m_sdbSizer protected - - - - - OnOK - - @@ -14360,42 +10845,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bSizerMain @@ -14480,31 +10929,7 @@ - - - - - - - - - - OnTemplateSelect - - - - - - - - - - - - - - @@ -14556,6 +10981,7 @@ 0 wxID_ANY Symbol name + 0 0 @@ -14581,29 +11007,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -14668,33 +11071,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -14730,6 +11106,7 @@ 0 wxID_ANY Prefix + 0 0 @@ -14755,29 +11132,6 @@ -1 - - - - - - - - - - - - - - - - - - - - - - - @@ -14842,33 +11196,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -14939,30 +11266,7 @@ Preview of the currently selected template (with default parameters). - wxSTATIC_BORDER - - - - - - - - - - - - - - - - - - - - - - - + wxBORDER_STATIC @@ -15027,33 +11331,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -15076,14 +11353,7 @@ m_sdbSizerOkCancel protected - - - - - OnOk - - diff --git a/src/libmngr_gui_base.h b/src/libmngr_gui_base.h index 1d6d3fd..f54a2c3 100644 --- a/src/libmngr_gui_base.h +++ b/src/libmngr_gui_base.h @@ -1,12 +1,11 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Feb 14 2017) +// C++ code generated with wxFormBuilder (version Oct 26 2018) // http://www.wxformbuilder.org/ // -// PLEASE DO "NOT" EDIT THIS FILE! +// PLEASE DO *NOT* EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// -#ifndef __LIBMNGR_GUI_BASE_H__ -#define __LIBMNGR_GUI_BASE_H__ +#pragma once #include #include @@ -85,10 +84,10 @@ /////////////////////////////////////////////////////////////////////////////// /// Class AppFrame /////////////////////////////////////////////////////////////////////////////// -class AppFrame : public wxFrame +class AppFrame : public wxFrame { private: - + protected: wxMenuBar* m_menubar; wxMenu* m_mnuFile; @@ -108,13 +107,13 @@ class AppFrame : public wxFrame wxListCtrl* m_listModulesRight; wxPanel* m_panelBottom; wxAuiToolBar* m_toolBar; - wxAuiToolBarItem* m_toolZoomIn; - wxAuiToolBarItem* m_toolZoomOut; - wxAuiToolBarItem* m_tool3DView; - wxAuiToolBarItem* m_toolMeasure; - wxAuiToolBarItem* m_toolDetailsPanel; - wxAuiToolBarItem* m_toolLeftFootprint; - wxAuiToolBarItem* m_toolRightFootprint; + wxAuiToolBarItem* m_toolZoomIn; + wxAuiToolBarItem* m_toolZoomOut; + wxAuiToolBarItem* m_tool3DView; + wxAuiToolBarItem* m_toolMeasure; + wxAuiToolBarItem* m_toolDetailsPanel; + wxAuiToolBarItem* m_toolLeftFootprint; + wxAuiToolBarItem* m_toolRightFootprint; wxSplitterWindow* m_splitterViewPanel; wxPanel* m_panelView; wxScrolledWindow* m_panelSettings; @@ -138,6 +137,8 @@ class AppFrame : public wxFrame wxStaticText* m_lblPadSize; wxTextCtrl* m_txtPadWidth; wxTextCtrl* m_txtPadLength; + wxStaticText* m_lblPadRadius; + wxTextCtrl* m_txtPadRadius; wxStaticText* m_lblPitch; wxTextCtrl* m_txtPitch; wxStaticText* m_lblPadSpan; @@ -163,7 +164,7 @@ class AppFrame : public wxFrame wxButton* m_btnSavePart; wxButton* m_btnRevertPart; wxStatusBar* m_statusBar; - + // Virtual event handlers, overide them in your derived class virtual void OnCloseApp( wxCloseEvent& event ) { event.Skip(); } virtual void OnNewLibrary( wxCommandEvent& event ) { event.Skip(); } @@ -236,35 +237,35 @@ class AppFrame : public wxFrame virtual void OnSavePart( wxCommandEvent& event ) { event.Skip(); } virtual void OnRevertPart( wxCommandEvent& event ) { event.Skip(); } virtual void OnStatusBarDblClk( wxMouseEvent& event ) { event.Skip(); } - - + + public: - + AppFrame( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("KiCad Librarian"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 700,450 ), long style = wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER|wxTAB_TRAVERSAL ); - + ~AppFrame(); - + void m_splitterOnIdle( wxIdleEvent& ) { m_splitter->SetSashPosition( 185 ); m_splitter->Disconnect( wxEVT_IDLE, wxIdleEventHandler( AppFrame::m_splitterOnIdle ), NULL, this ); } - + void m_splitterViewPanelOnIdle( wxIdleEvent& ) { m_splitterViewPanel->SetSashPosition( -200 ); m_splitterViewPanel->Disconnect( wxEVT_IDLE, wxIdleEventHandler( AppFrame::m_splitterViewPanelOnIdle ), NULL, this ); } - + }; /////////////////////////////////////////////////////////////////////////////// /// Class DlgPaths /////////////////////////////////////////////////////////////////////////////// -class DlgPaths : public wxDialog +class DlgPaths : public wxDialog { private: - + protected: wxStaticText* m_lblFootprintPaths; wxListBox* m_lstFootprints; @@ -274,10 +275,11 @@ class DlgPaths : public wxDialog wxListBox* m_lstSymbols; wxButton* m_btnAddSymbol; wxButton* m_btnRemoveSymbol; + wxCheckBox* m_chkRecurseDirectories; wxStdDialogButtonSizer* m_sdbSizer; wxButton* m_sdbSizerOK; wxButton* m_sdbSizerCancel; - + // Virtual event handlers, overide them in your derived class virtual void OnFootprintPathSelect( wxCommandEvent& event ) { event.Skip(); } virtual void OnAddFootprintPath( wxCommandEvent& event ) { event.Skip(); } @@ -286,22 +288,22 @@ class DlgPaths : public wxDialog virtual void OnAddSymbolPath( wxCommandEvent& event ) { event.Skip(); } virtual void OnRemoveSymbolPath( wxCommandEvent& event ) { event.Skip(); } virtual void OnOK( wxCommandEvent& event ) { event.Skip(); } - - + + public: - - DlgPaths( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Search paths"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + + DlgPaths( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Search paths"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); ~DlgPaths(); - + }; /////////////////////////////////////////////////////////////////////////////// /// Class DlgReport /////////////////////////////////////////////////////////////////////////////// -class DlgReport : public wxDialog +class DlgReport : public wxDialog { private: - + protected: wxStaticText* m_lblPageSize; wxChoice* m_choicePageSize; @@ -313,28 +315,29 @@ class DlgReport : public wxDialog wxCheckBox* m_chkValueLabels; wxCheckBox* m_chkPadInfo; wxCheckBox* m_chkFPList; + wxCheckBox* m_chkIndex; wxStdDialogButtonSizer* m_sdbSizer; wxButton* m_sdbSizerOK; wxButton* m_sdbSizerCancel; - + // Virtual event handlers, overide them in your derived class virtual void OnOK( wxCommandEvent& event ) { event.Skip(); } - - + + public: - - DlgReport( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Report options"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + + DlgReport( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Report options"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); ~DlgReport(); - + }; /////////////////////////////////////////////////////////////////////////////// /// Class DlgOptions /////////////////////////////////////////////////////////////////////////////// -class DlgOptions : public wxDialog +class DlgOptions : public wxDialog { private: - + protected: wxStaticText* m_lblFontSize; wxSpinCtrl* m_spinFontSize; @@ -363,25 +366,25 @@ class DlgOptions : public wxDialog wxStdDialogButtonSizer* m_sdbSizer; wxButton* m_sdbSizerOK; wxButton* m_sdbSizerCancel; - + // Virtual event handlers, overide them in your derived class virtual void OnOK( wxCommandEvent& event ) { event.Skip(); } - - + + public: - - DlgOptions( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("User Interface"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + + DlgOptions( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("User Interface"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); ~DlgOptions(); - + }; /////////////////////////////////////////////////////////////////////////////// /// Class DlgTemplateOpts /////////////////////////////////////////////////////////////////////////////// -class DlgTemplateOpts : public wxDialog +class DlgTemplateOpts : public wxDialog { private: - + protected: wxStaticText* m_lblVariables; wxGrid* m_gridTemplateVars; @@ -389,25 +392,25 @@ class DlgTemplateOpts : public wxDialog wxStdDialogButtonSizer* m_sdbSizer; wxButton* m_sdbSizerOK; wxButton* m_sdbSizerCancel; - + // Virtual event handlers, overide them in your derived class virtual void OnOK( wxCommandEvent& event ) { event.Skip(); } - - + + public: - - DlgTemplateOpts( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Template variables"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + + DlgTemplateOpts( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Template variables"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); ~DlgTemplateOpts(); - + }; /////////////////////////////////////////////////////////////////////////////// /// Class DlgNewFootprint /////////////////////////////////////////////////////////////////////////////// -class DlgNewFootprint : public wxDialog +class DlgNewFootprint : public wxDialog { private: - + protected: wxListBox* m_lstTemplates; wxStaticText* m_lblName; @@ -418,28 +421,28 @@ class DlgNewFootprint : public wxDialog wxStdDialogButtonSizer* m_sdbSizerOkCancel; wxButton* m_sdbSizerOkCancelOK; wxButton* m_sdbSizerOkCancelCancel; - + // Virtual event handlers, overide them in your derived class virtual void OnTemplateSelect( wxCommandEvent& event ) { event.Skip(); } virtual void OnNextImage( wxSpinEvent& event ) { event.Skip(); } virtual void OnPrevImage( wxSpinEvent& event ) { event.Skip(); } virtual void OnOk( wxCommandEvent& event ) { event.Skip(); } - - + + public: - - DlgNewFootprint( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("New footprint"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + + DlgNewFootprint( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("New footprint"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); ~DlgNewFootprint(); - + }; /////////////////////////////////////////////////////////////////////////////// /// Class DlgRemoteLink /////////////////////////////////////////////////////////////////////////////// -class DlgRemoteLink : public wxDialog +class DlgRemoteLink : public wxDialog { private: - + protected: wxStaticText* m_lblURL; wxTextCtrl* m_txtURL; @@ -458,27 +461,27 @@ class DlgRemoteLink : public wxDialog wxButton* m_btnSignUp; wxButton* m_btnOK; wxButton* m_btnCancel; - + // Virtual event handlers, overide them in your derived class virtual void OnSignUp( wxCommandEvent& event ) { event.Skip(); } virtual void OnOK( wxCommandEvent& event ) { event.Skip(); } virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } - - + + public: - - DlgRemoteLink( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Repository"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + + DlgRemoteLink( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Repository"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); ~DlgRemoteLink(); - + }; /////////////////////////////////////////////////////////////////////////////// /// Class DlgRemoteSignUp /////////////////////////////////////////////////////////////////////////////// -class DlgRemoteSignUp : public wxDialog +class DlgRemoteSignUp : public wxDialog { private: - + protected: wxStaticText* m_lblURL; wxTextCtrl* m_txtURL; @@ -497,25 +500,25 @@ class DlgRemoteSignUp : public wxDialog wxStdDialogButtonSizer* m_sdbSizer; wxButton* m_sdbSizerOK; wxButton* m_sdbSizerCancel; - + // Virtual event handlers, overide them in your derived class virtual void OnOK( wxCommandEvent& event ) { event.Skip(); } - - + + public: - - DlgRemoteSignUp( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Repository - sign up"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + + DlgRemoteSignUp( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Repository - sign up"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); ~DlgRemoteSignUp(); - + }; /////////////////////////////////////////////////////////////////////////////// /// Class DlgNewSymbol /////////////////////////////////////////////////////////////////////////////// -class DlgNewSymbol : public wxDialog +class DlgNewSymbol : public wxDialog { private: - + protected: wxListBox* m_lstTemplates; wxStaticText* m_lblName; @@ -527,17 +530,16 @@ class DlgNewSymbol : public wxDialog wxStdDialogButtonSizer* m_sdbSizerOkCancel; wxButton* m_sdbSizerOkCancelOK; wxButton* m_sdbSizerOkCancelCancel; - + // Virtual event handlers, overide them in your derived class virtual void OnTemplateSelect( wxCommandEvent& event ) { event.Skip(); } virtual void OnOk( wxCommandEvent& event ) { event.Skip(); } - - + + public: - - DlgNewSymbol( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("New symbol"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + + DlgNewSymbol( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("New symbol"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); ~DlgNewSymbol(); - + }; -#endif //__LIBMNGR_GUI_BASE_H__ diff --git a/src/libmngr_paths.cpp b/src/libmngr_paths.cpp index e8f5e2a..010acea 100644 --- a/src/libmngr_paths.cpp +++ b/src/libmngr_paths.cpp @@ -2,7 +2,7 @@ * Librarian for KiCad, a free EDA CAD application. * The dialog for the search paths settings. * - * Copyright (C) 2013-2015 CompuPhase + * Copyright (C) 2013-2018 CompuPhase * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy @@ -16,16 +16,15 @@ * License for the specific language governing permissions and limitations * under the License. * - * $Id: libmngr_paths.cpp 5784 2017-12-26 14:12:22Z thiadmer $ + * $Id: libmngr_paths.cpp 5907 2018-12-14 22:05:40Z thiadmer $ */ #include "librarymanager.h" #include "libmngr_paths.h" #include #include -libmngrDlgPaths::libmngrDlgPaths( wxWindow* parent ) -: -DlgPaths( parent ) +libmngrDlgPaths::libmngrDlgPaths(wxWindow* parent) + : DlgPaths(parent) { /* fill in the list of search paths */ wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); @@ -48,6 +47,10 @@ DlgPaths( parent ) idx++; } + bool recurse = false; + config->Read(wxT("path/recurse"), &recurse); + m_chkRecurseDirectories->SetValue(recurse); + delete config; m_btnRemoveFootprint->Enable(false); @@ -136,6 +139,9 @@ void libmngrDlgPaths::OnOK( wxCommandEvent& event ) config->Write(key, path); } + bool recurse = m_chkRecurseDirectories->GetValue(); + config->Write(wxT("path/recurse"), recurse); + delete config; event.Skip(); } diff --git a/src/libmngr_paths.h b/src/libmngr_paths.h index 9835c3f..cd3e7af 100644 --- a/src/libmngr_paths.h +++ b/src/libmngr_paths.h @@ -1,52 +1,52 @@ -/* - * Librarian for KiCad, a free EDA CAD application. - * The dialog for the search paths settings. - * - * Copyright (C) 2013-2015 CompuPhase - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * $Id: libmngr_paths.h 5387 2015-10-22 19:31:30Z thiadmer $ - */ -#ifndef __libmngr_paths__ -#define __libmngr_paths__ - -/** -@file -Subclass of DlgPaths, which is generated by wxFormBuilder. -*/ - -#include "libmngr_gui_base.h" - -//// end generated include - -/** Implementing DlgPaths */ -class libmngrDlgPaths : public DlgPaths -{ - public: - /** Constructor */ - explicit libmngrDlgPaths(wxWindow* parent); - //// end generated class members - - void OnFootprintPathSelect(wxCommandEvent& event); - void OnAddFootprintPath(wxCommandEvent& event); - void OnRemoveFootprintPath(wxCommandEvent& event); - - void OnSymbolPathSelect(wxCommandEvent& event); - void OnAddSymbolPath(wxCommandEvent& event); - void OnRemoveSymbolPath(wxCommandEvent& event); - - void OnOK(wxCommandEvent& event); -}; - -#endif // __libmngr_paths__ +/* + * Librarian for KiCad, a free EDA CAD application. + * The dialog for the search paths settings. + * + * Copyright (C) 2013-2015 CompuPhase + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * $Id: libmngr_paths.h 5686 2017-05-24 13:56:46Z thiadmer $ + */ +#ifndef __libmngr_paths__ +#define __libmngr_paths__ + +/** +@file +Subclass of DlgPaths, which is generated by wxFormBuilder. +*/ + +#include "libmngr_gui_base.h" + +//// end generated include + +/** Implementing DlgPaths */ +class libmngrDlgPaths : public DlgPaths +{ + public: + /** Constructor */ + explicit libmngrDlgPaths(wxWindow* parent); + //// end generated class members + + void OnFootprintPathSelect(wxCommandEvent& event); + void OnAddFootprintPath(wxCommandEvent& event); + void OnRemoveFootprintPath(wxCommandEvent& event); + + void OnSymbolPathSelect(wxCommandEvent& event); + void OnAddSymbolPath(wxCommandEvent& event); + void OnRemoveSymbolPath(wxCommandEvent& event); + + void OnOK(wxCommandEvent& event); +}; + +#endif // __libmngr_paths__ diff --git a/src/libraryfunctions.cpp b/src/libraryfunctions.cpp index 6d79857..8979282 100644 --- a/src/libraryfunctions.cpp +++ b/src/libraryfunctions.cpp @@ -2,7 +2,7 @@ * Librarian for KiCad, a free EDA CAD application. * Utility functions for parsing and writing libraries. * - * Copyright (C) 2013-2017 CompuPhase + * Copyright (C) 2013-2018 CompuPhase * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy @@ -16,7 +16,7 @@ * License for the specific language governing permissions and limitations * under the License. * - * $Id: libraryfunctions.cpp 5784 2017-12-26 14:12:22Z thiadmer $ + * $Id: libraryfunctions.cpp 5907 2018-12-14 22:05:40Z thiadmer $ */ #include @@ -134,7 +134,7 @@ long GetTokenLong(wxString* string) return val; } -/** GetTokenDouble() returns a floating-point value in mm. +/** GetTokenDouble() returns a floating-point value. */ double GetTokenDouble(wxString* string) { @@ -246,8 +246,12 @@ bool SetSection(wxString& string, const wxString& token, const wxString& params) { wxASSERT(token[0] != wxT('(')); int pos = string.Find(wxT("(") + token + wxT(" ")); - if (pos < 0) - return false; + if (pos < 0) { + /* section does not exist yet, add an empty section just before the end */ + pos = (int)string.length() - 1; + wxASSERT(pos > 0 && string[pos] == wxT(')')); + string = string.insert(pos, wxT(" (") + token + wxT(" )")); + } wxASSERT(pos < (int)string.length()); wxASSERT(string[pos] == wxT('(')); pos += (int)token.length() + 1; /* +1 for the '(' */ @@ -332,7 +336,7 @@ static long GetPadNumber(const wxString& field, int* width, int* height) /* most footprints have numeric pin numbers, but BGA and PGA use a letter followed by digits; in this case, the matrix size must be determined as well */ - if (isalpha(field[0]) && isdigit(field[1])) { + if (field.Length() >= 2 && isalpha(field[0]) && isdigit(field[1])) { long row = toupper(field[0]) - 'A' + 1; if (row > 9) row--; /* because 'I' is not used for grid arrays */ @@ -809,6 +813,7 @@ void TranslateToSexpr(wxArrayString* output, const wxArrayString& module) output->Add(newline); } else if (keyword.CmpNoCase(wxT("$PAD")) == 0) { wxString name, type, shape; + double rratio = 0; double px = 0, py = 0, pw = 0, ph = 0, tx = 0, ty = 0; double drillx = 0, drilly = 0, drillwidth = 0, drillheight = 0; double die_length = 0, solder_mask_margin = 0, clearance = 0; @@ -835,6 +840,8 @@ void TranslateToSexpr(wxArrayString* output, const wxArrayString& module) tx = GetTokenDim(&line, true); ty = GetTokenDim(&line, true); angle = GetTokenLong(&line); + if (line.length() > 0) + rratio = GetTokenDouble(&line); /* convert naming conventions */ switch (toupper(shape[0])) { case 'C': @@ -842,6 +849,8 @@ void TranslateToSexpr(wxArrayString* output, const wxArrayString& module) break; case 'R': shape = wxT("rect"); + if (rratio > EPSILON) + shape = wxT("roundrect"); break; case 'O': shape = wxT("oval"); @@ -907,6 +916,8 @@ void TranslateToSexpr(wxArrayString* output, const wxArrayString& module) else newline += wxString::Format(wxT(" (drill oval %.4f %.4f (offset %.4f %.4f))"), drillwidth, drillheight, drillx, drilly); } + if (shape.Cmp(wxT("roundrect")) == 0) + newline += wxString::Format(wxT(" (roundrect_rratio %.2f)"), rratio); newline += wxT(" (layers"); if ((layermask & 0x0000ffff) == 0x0000ffff) { newline += wxT(" *.Cu"); @@ -1264,13 +1275,24 @@ void TranslateToLegacy(wxArrayString* output, const wxArrayString& module) if (section.Length() > 0) { double width = GetTokenDim(§ion, true); double height = GetTokenDim(§ion, true); + double rratio = 0.0; char lshape = 'C'; - if (shape.CmpNoCase(wxT("rect")) == 0) + if (shape.CmpNoCase(wxT("rect")) == 0) { lshape = 'R'; - else if (shape.CmpNoCase(wxT("oval")) == 0) + } else if (shape.CmpNoCase(wxT("oval")) == 0) { lshape = 'O'; - else if (shape.CmpNoCase(wxT("trapezoid")) == 0) + } else if (shape.CmpNoCase(wxT("trapezoid")) == 0) { lshape = 'T'; + } else if (shape.CmpNoCase(wxT("roundrect")) == 0) { + /* legacy format does not define "rounded rectangle", so it is + set to "rectangle" with a (non-standard) rounding ratio */ + section = GetSection(line, wxT("roundrect_rratio")); + if (section.length() > 0) + rratio = GetTokenDouble(§ion); + lshape = 'R'; + } else if (shape.CmpNoCase(wxT("custom")) == 0) { + lshape = 'R'; + } double dx = 0, dy = 0; section = GetSection(line, wxT("rect_delta")); if (section.Length() > 0) { @@ -1278,8 +1300,10 @@ void TranslateToLegacy(wxArrayString* output, const wxArrayString& module) dy = GetTokenDim(§ion, true); } wxString newline = wxString::Format(wxT("Sh \"%s\" %c %.4f %.4f %.4f %.4f %ld"), - name.c_str(), lshape, width, height, dx, dy, - (long)(angle * 10)); + name.c_str(), lshape, width, height, dx, dy, + (long)(angle * 10)); + if (rratio > EPSILON) + newline += wxString::Format(wxT(" %.2f"), rratio); output->Add(newline); } long mask = 0; @@ -1294,7 +1318,7 @@ void TranslateToLegacy(wxArrayString* output, const wxArrayString& module) else if (field.CmpNoCase(wxT("*.Silk")) == 0) mask |= 0x00300000; else - mask |= LayerNumber(field); + mask |= (1 << LayerNumber(field)); } } if (type.CmpNoCase(wxT("thru_hole")) == 0) @@ -1443,6 +1467,9 @@ int AdjustPad(wxArrayString& module, FootprintInfo* current, const FootprintInfo case 'T': padshape_adj = wxT("trapezoid"); break; + case 'D': + padshape_adj = wxT("roundrect"); + break; } CoordPair padsize; @@ -1487,11 +1514,16 @@ int AdjustPad(wxArrayString& module, FootprintInfo* current, const FootprintInfo padshape = adjusted.PadShape; if (padshape == 'S') padshape = (pinnr == 1) ? 'R' : 'C'; + else if (padshape == 'D') + padshape = 'R'; /* rounded rect is not supported in legacy format */ padsize.Set(adjusted.PadSize[0].GetX(), adjusted.PadSize[0].GetY()); - module[i] = wxString::Format(wxT("%s \"%s\" %c %.4f %.4f %.4f %.4f %ld"), - keyword.c_str(), name.c_str(), padshape, - MM(padsize.GetX()), MM(padsize.GetY()), - MM(xdelta), MM(ydelta), rot); + wxString line = wxString::Format(wxT("%s \"%s\" %c %.4f %.4f %.4f %.4f %ld"), + keyword.c_str(), name.c_str(), padshape, + MM(padsize.GetX()), MM(padsize.GetY()), + MM(xdelta), MM(ydelta), rot); + if (adjusted.PadShape == 'D') + line += wxString::Format(wxT(" %.2f"), adjusted.PadRRatio / 100.0); + module[i] = line; } else if (keyword.CmpNoCase(wxT("Dr")) == 0) { GetToken(&line); /* ignore drill size */ double xpos = GetTokenDim(&line, true); @@ -1556,6 +1588,11 @@ int AdjustPad(wxArrayString& module, FootprintInfo* current, const FootprintInfo double width = GetTokenDim(&line, true); double height = GetTokenDim(&line, true); padsize.Set(width, height); + GetToken(&line); /* ignore delta-x */ + GetToken(&line); /* ignore delta-y */ + GetToken(&line); /* ignore rotation */ + if (padshape == 'R' && line.Length() > 0 && GetTokenDouble(&line) > EPSILON) + padshape = 'D'; } else if (keyword.CmpNoCase(wxT("Dr")) == 0 && inpad) { drillsize = GetTokenDim(&line, true); } else if (keyword.CmpNoCase(wxT("At")) == 0 && !inpad) { @@ -1576,6 +1613,10 @@ int AdjustPad(wxArrayString& module, FootprintInfo* current, const FootprintInfo padshape = 'O'; else if (padshape_s.Cmp(wxT("trapezoid")) == 0) padshape = 'T'; + else if (padshape_s.Cmp(wxT("roundrect")) == 0) + padshape = 'D'; + else if (padshape_s.Cmp(wxT("custom")) == 0) + padshape = 'R'; wxString section = GetSection(line, wxT("size")); if (section.length() > 0) { double width = GetTokenDim(§ion, true); @@ -1599,10 +1640,19 @@ int AdjustPad(wxArrayString& module, FootprintInfo* current, const FootprintInfo SetSection(line, wxT("size"), section); if (adjusted.DrillSize > EPSILON) { section = wxString::Format(wxT("%.4f"), MM(adjusted.DrillSize)); - SetSection(line, wxT("drill"), section); //??? this fails if there is no drill section yet (a new section must be inserted) + SetSection(line, wxT("drill"), section); } else { DeleteSection(line, wxT("drill")); } + if (adjusted.PadShape == 'D') { + int percent = adjusted.PadRRatio; + if (percent > 50) + percent = 50; + section = wxString::Format(wxT("%.2f"), percent / 100.0); + SetSection(line, wxT("roundrect_rratio"), section); + } else { + DeleteSection(line, wxT("roundrect_rratio")); + } if (adjusted.PadShape == 'S' && pinnr == 1) line.Replace(padshape_s, wxT("rect"), false); else @@ -2764,6 +2814,28 @@ static const wxChar* rpn_errors[] = { wxT("(none)"), wxT("empty stack"), wxT("multiple results"), wxT("underflow"), wxT("overflow"), wxT("invalid variable"), wxT("invalid function"), wxT("invalid operator") }; +wxString TranslatePadShape(const wxString& name, int pad, bool legacy) +{ + wxString newname = name; + + if (name.Cmp(wxT("sqcircle")) == 0) + newname = (pad == 1) ? wxT("rect") : wxT("circle"); + + if (legacy) { + if (newname.Cmp(wxT("circle")) == 0) + newname = wxT("C"); + else if (newname.Cmp(wxT("rect")) == 0 || newname.Cmp(wxT("roundrect")) == 0 || newname.Cmp(wxT("custom")) == 0) + newname = wxT("R"); + else if (newname.Cmp(wxT("oval")) == 0) + newname = wxT("O"); + else if (newname.Cmp(wxT("trapezoid")) == 0) + newname = wxT("T"); + wxASSERT(newname.Length() == 1); + } + + return newname; +} + /* For footprints */ bool FootprintFromTemplate(wxArrayString* module, const wxArrayString& templat, RPNexpression& rpn, bool bodyonly) @@ -2793,12 +2865,14 @@ bool FootprintFromTemplate(wxArrayString* module, const wxArrayString& templat, unsigned tidx = 0; unsigned padstart = 0; unsigned bodyline = 0; + bool legacy = false; while (tidx < templat.Count()) { wxString line = templat[tidx]; line.Trim(); /* check for the start of a pad (pads are handled separately) */ if (line.CmpNoCase(wxT("$PAD")) == 0 || line.Left(4).Cmp(wxT("(pad")) == 0) { padstart = tidx; + legacy = (line.CmpNoCase(wxT("$PAD")) == 0); break; } /* handle skipping conditional blocks */ @@ -2881,6 +2955,18 @@ bool FootprintFromTemplate(wxArrayString* module, const wxArrayString& templat, endlabel = wxEmptyString; tidx = padstart; rpn.SetVariable(RPNvariable("PN", pad)); + /* check/set pad shape */ + rpn.Set("$PSH"); + if (rpn.Parse() == RPN_OK) { + wxString shape = rpn.Value().Text(); + rpn.SetVariable(RPNvariable("PSH", TranslatePadShape(shape, pad, legacy))); + double rratio = -1; + if (shape.Cmp(wxT("roundrect")) == 0) { + rpn.Set("$PRR"); + rratio = (rpn.Parse() == RPN_OK) ? rpn.Value().Double() : 0.25; + } + rpn.SetVariable(RPNvariable("PRR", rratio)); + } while (tidx < templat.Count()) { wxString line = templat[tidx]; line.Trim(); @@ -4489,6 +4575,7 @@ bool TranslatePadInfo(wxArrayString* module, FootprintInfo* info) double orgwidth, orgheight; /* pad width/height may change if multiple overlapping pads are combined */ long angle; char shape; + double rratio; /* rounding ratio for rounded rectangle */ double drillwidth, drillheight; /* for round holes, only drillwidth is set */ bool rotate; /* set to true if the pad must be rotated by 90 degrees relative to the anchor pad */ } *padlist = NULL; @@ -4511,6 +4598,7 @@ bool TranslatePadInfo(wxArrayString* module, FootprintInfo* info) double drillwidth = 0, drillheight = 0; char padshape = '\0'; long padangle = 0; + double rratio = 0; unsigned startidx = 0; long pinnr = PIN_UNKNOWN; for (unsigned idx = 0; idx < module->Count(); idx++) { @@ -4572,6 +4660,7 @@ bool TranslatePadInfo(wxArrayString* module, FootprintInfo* info) padlist[pinnr].height = padheight; padlist[pinnr].angle = padangle; padlist[pinnr].shape = padshape; + padlist[pinnr].rratio = rratio; padlist[pinnr].drillwidth = drillwidth; padlist[pinnr].drillheight = drillheight; padlist[pinnr].orgwidth = padwidth; @@ -4582,6 +4671,7 @@ bool TranslatePadInfo(wxArrayString* module, FootprintInfo* info) padangle = 0; drillwidth = drillheight = 0; padshape = '\0'; + rratio = 0; startidx = 0; pinnr = PIN_UNKNOWN; } @@ -4630,6 +4720,9 @@ bool TranslatePadInfo(wxArrayString* module, FootprintInfo* info) padlist[pinnr].drillheight = 0; } } + section = GetSection(line, wxT("roundrect_rratio")); + if (section.length() > 0) + padlist[pinnr].rratio = GetTokenDouble(§ion); if (shape.Cmp(wxT("circle")) == 0) padlist[pinnr].shape = 'C'; else if (shape.Cmp(wxT("rect")) == 0) @@ -4638,6 +4731,8 @@ bool TranslatePadInfo(wxArrayString* module, FootprintInfo* info) padlist[pinnr].shape = 'O'; else if (shape.Cmp(wxT("trapezoid")) == 0) padlist[pinnr].shape = 'T'; + else if (shape.Cmp(wxT("roundrect")) == 0) + padlist[pinnr].shape = 'D'; } pinnr = PIN_UNKNOWN; continue; @@ -4664,6 +4759,11 @@ bool TranslatePadInfo(wxArrayString* module, FootprintInfo* info) GetToken(&line); /* ignore x-delta */ GetToken(&line); /* ignore y-delta */ padangle = GetTokenLong(&line); + if (line.Length() > 0) { + rratio = GetTokenDouble(&line); + if (padshape == 'R') + padshape = 'D';/* rectangle -> rounded rectangle (rounding ratio is present) */ + } } else if (token.CmpNoCase(wxT("Dr")) == 0) { drillwidth = GetTokenDim(&line, unit_mm); GetToken(&line); /* ignore drill offset */ @@ -4739,7 +4839,7 @@ bool TranslatePadInfo(wxArrayString* module, FootprintInfo* info) dy = -dy; /* check horizontal pitch */ if (horlevel < 2 && dx >= MIN_PITCH) { /* deltas smaller than this are not seen as pitch */ - int newlevel = (dy < MIN_PITCH) ? 2 : 1; + int newlevel = (dy < TOL_ROWALIGN) ? 2 : 1; if (newlevel > horlevel) { pitchhor = dx; horlevel = newlevel; @@ -4748,7 +4848,7 @@ bool TranslatePadInfo(wxArrayString* module, FootprintInfo* info) } /* check vertical pitch */ if (verlevel < 2 && dy >= MIN_PITCH) { - int newlevel = (dx < MIN_PITCH) ? 2 : 1; + int newlevel = (dx < TOL_ROWALIGN) ? 2 : 1; if (newlevel > verlevel) { pitchver = dy; verlevel = newlevel; @@ -4935,16 +5035,92 @@ bool TranslatePadInfo(wxArrayString* module, FootprintInfo* info) if (!Equal(pitchhor, pitchver)) { wxASSERT(info->Pitch > EPSILON); if (info->PitchVertical) { + wxASSERT(horpin_base >= 2); /* must be >= 2 because the opposing pin (of the span) is typically the predecessor and the pin numbering starts at 1 */ + int opposite = horpin_base - 1; + if (horlevel == 1) { + /* horizontal "pitch" is detected as weak, see if we find a better match */ + int ver_span_pin = -1; + for (int pin = horpin_base - 1; horlevel == 1 && pin > 0; pin--) { + if (padlist[pin].startidx > 0 && Equal(padlist[pin].y, padlist[horpin_base].y, TOLERANCE)) { + horlevel = 2; + ver_span_pin = opposite; + opposite = pin; + } + } + for (int pin = horpin_base + 1; horlevel == 1 && pin <= padvalid; pin++) { + if (padlist[pin].startidx > 0 && Equal(padlist[pin].y, padlist[opposite].y, TOLERANCE)) { + horlevel = 2; + ver_span_pin = horpin_base; + horpin_base = pin; + } + } + if (horlevel == 2) + pitchhor = fabs(padlist[opposite].x - padlist[horpin_base].x); + /* if one of the anchor pads for the horizontal span moved, + that may indicate there is a vertical span on the original + anchor */ + if (ver_span_pin > 0) { + for (int pin = 1; pin <= padvalid; pin++) { + if (pin != ver_span_pin && padlist[pin].startidx > 0 + && Equal(padlist[pin].x, padlist[ver_span_pin].x, TOLERANCE) + && Equal(padlist[pin].width, padlist[ver_span_pin].width, TOLERANCE) + && Equal(padlist[pin].height, padlist[ver_span_pin].height, TOLERANCE)) { + info->SpanVer = fabs(padlist[pin].y - padlist[ver_span_pin].y); + wxASSERT(padlist[pin].startidx > 0 && padlist[ver_span_pin].startidx > 0); + info->SpanVerPins[0] = CoordPair(padlist[pin].x, padlist[pin].y); + info->SpanVerPins[1] = CoordPair(padlist[ver_span_pin].x, padlist[ver_span_pin].y); + break; + } + } + } + } /* horlevel == 1 (weak span) */ info->SpanHor = pitchhor; - wxASSERT(horpin_base > 1); - wxASSERT(padlist[horpin_base - 1].startidx > 0 && padlist[horpin_base].startidx > 0); - info->SpanHorPins[0] = CoordPair(padlist[horpin_base - 1].x, padlist[horpin_base - 1].y); + wxASSERT(padlist[opposite].startidx > 0 && padlist[horpin_base].startidx > 0); + info->SpanHorPins[0] = CoordPair(padlist[opposite].x, padlist[opposite].y); info->SpanHorPins[1] = CoordPair(padlist[horpin_base].x, padlist[horpin_base].y); } else { + wxASSERT(verpin_base >= 2); + int opposite = verpin_base - 1; + if (verlevel == 1) { + /* vertical "pitch" is detected as weak, see if we find a better match */ + int hor_span_pin = -1; + for (int pin = verpin_base - 1; verlevel == 1 && pin > 0; pin--) { + if (padlist[pin].startidx > 0 && Equal(padlist[pin].x, padlist[verpin_base].x, TOLERANCE)) { + verlevel = 2; + hor_span_pin = opposite; + opposite = pin; + } + } + for (int pin = verpin_base + 1; verlevel == 1 && pin <= padvalid; pin++) { + if (padlist[pin].startidx > 0 && Equal(padlist[pin].x, padlist[opposite].x, TOLERANCE)) { + verlevel = 2; + hor_span_pin = verpin_base; + verpin_base = pin; + } + } + if (verlevel == 2) + pitchver = fabs(padlist[opposite].y - padlist[verpin_base].y); + /* if one of the anchor pads for the vertical span moved, + that may indicate there is a horizontal span on the original + anchor */ + if (hor_span_pin > 0) { + for (int pin = 1; pin <= padvalid; pin++) { + if (pin != hor_span_pin && padlist[pin].startidx > 0 + && Equal(padlist[pin].y, padlist[hor_span_pin].y, TOLERANCE) + && Equal(padlist[pin].width, padlist[hor_span_pin].width, TOLERANCE) + && Equal(padlist[pin].height, padlist[hor_span_pin].height, TOLERANCE)) { + info->SpanHor = fabs(padlist[pin].x - padlist[hor_span_pin].x); + wxASSERT(padlist[pin].startidx > 0 && padlist[hor_span_pin].startidx > 0); + info->SpanHorPins[0] = CoordPair(padlist[pin].x, padlist[pin].y); + info->SpanHorPins[1] = CoordPair(padlist[hor_span_pin].x, padlist[hor_span_pin].y); + break; + } + } + } + } /* verlevel == 1 (weak span) */ info->SpanVer = pitchver; - wxASSERT(verpin_base > 0); - wxASSERT(padlist[verpin_base - 1].startidx > 0 && padlist[verpin_base].startidx > 0); - info->SpanVerPins[0] = CoordPair(padlist[verpin_base - 1].x, padlist[verpin_base - 1].y); + wxASSERT(padlist[opposite].startidx > 0 && padlist[verpin_base].startidx > 0); + info->SpanVerPins[0] = CoordPair(padlist[opposite].x, padlist[opposite].y); info->SpanVerPins[1] = CoordPair(padlist[verpin_base].x, padlist[verpin_base].y); } } else { diff --git a/src/libraryfunctions.h b/src/libraryfunctions.h index 6857fc3..1d75c1b 100644 --- a/src/libraryfunctions.h +++ b/src/libraryfunctions.h @@ -2,7 +2,7 @@ * Librarian for KiCad, a free EDA CAD application. * Utility functions for parsing and writing libraries. * - * Copyright (C) 2013-2017 CompuPhase + * Copyright (C) 2013-2018 CompuPhase * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy @@ -16,7 +16,7 @@ * License for the specific language governing permissions and limitations * under the License. * - * $Id: libraryfunctions.h 5784 2017-12-26 14:12:22Z thiadmer $ + * $Id: libraryfunctions.h 5907 2018-12-14 22:05:40Z thiadmer $ */ #ifndef LIBRARYFUNCTIONS_H #define LIBRARYFUNCTIONS_H @@ -26,6 +26,7 @@ #define TOLERANCE 0.03 /* in mm, tolerance for matching pad and drill sizes */ #define MIN_PITCH 0.2 /* in mm, deltas smaller than this are not seen as pitch */ +#define TOL_ROWALIGN 0.075 /* in mm, pads displaced by more than this are not considered to be aligned in a row */ #define EPSILON 0.000001 /* default margin for comparing floating point values */ #define LIB_NONE wxT("(None)") @@ -170,6 +171,7 @@ class FootprintInfo { CoordPair PadSize[2]; bool PadRightAngle[2]; /* true if rotated by 90 or 270 degrees */ char PadShape; + int PadRRatio; /* rounded corner size, in percentage of width/height */ double DrillSize; ArrayCoordSize Pads; /* array with pad positions (outlines) */ diff --git a/src/librarymanager.cpp b/src/librarymanager.cpp index c77be46..4e96c30 100644 --- a/src/librarymanager.cpp +++ b/src/librarymanager.cpp @@ -16,7 +16,7 @@ * License for the specific language governing permissions and limitations * under the License. * - * $Id: librarymanager.cpp 5387 2015-10-22 19:31:30Z thiadmer $ + * $Id: librarymanager.cpp 5685 2017-05-23 10:35:40Z thiadmer $ */ #include "librarymanager.h" #include "libmngr_frame.h" diff --git a/src/librarymanager.h b/src/librarymanager.h index 16d46d7..90c566f 100644 --- a/src/librarymanager.h +++ b/src/librarymanager.h @@ -16,7 +16,7 @@ * License for the specific language governing permissions and limitations * under the License. * - * $Id: librarymanager.h 5387 2015-10-22 19:31:30Z thiadmer $ + * $Id: librarymanager.h 5686 2017-05-24 13:56:46Z thiadmer $ */ #ifndef __librarymanager__ #define __librarymanager__ @@ -25,29 +25,29 @@ #include #if defined __MSDOS__ || defined __WIN32__ || defined _Windows || defined _WIN32 || defined WIN32 - #define DIRSEP_CHAR '\\' /* directory separator character */ - #define DIRSEP_STR "\\" + #define DIRSEP_CHAR '\\' /* directory separator character */ + #define DIRSEP_STR "\\" #elif defined macintosh || defined __APPLE__ - #define DIRSEP_CHAR ':' - #define DIRSEP_STR ":" + #define DIRSEP_CHAR ':' + #define DIRSEP_STR ":" #else - #define DIRSEP_CHAR '/' - #define DIRSEP_STR "/" + #define DIRSEP_CHAR '/' + #define DIRSEP_STR "/" #endif -#define APP_NAME wxT("KiCadLibrarian") -#define VENDOR_NAME wxT("CompuPhase") +#define APP_NAME wxT("KiCadLibrarian") +#define VENDOR_NAME wxT("CompuPhase") enum { - IDM_SWAPABOVE = 2000, - IDM_SWAPBELOW, - IDM_PASTEPINLIST, + IDM_SWAPABOVE = 2000, + IDM_SWAPBELOW, + IDM_PASTEPINLIST, IDM_PASTEGENERAL, - IDM_PINTYPE = 2100, - IDM_PINSHAPE = 2200, - IDM_PINSECTION = 2300, - /* ----- */ - IDC_FILTER = 3000, + IDM_PINTYPE = 2100, + IDM_PINSHAPE = 2200, + IDM_PINSECTION = 2300, + /* ----- */ + IDC_FILTER = 3000, IDC_EXPORT, }; @@ -64,12 +64,12 @@ class LibraryManagerApp : public wxApp { return strTemplates; } wxString GetDocumentationPath() const /* the path where the documentation is */ { return strDocumentationPath; } - wxString GetUserDataPath() const /* the path where user data can be stored */ - { return strUserDataPath; } + wxString GetUserDataPath() const /* the path where user data can be stored */ + { return strUserDataPath; } wxString GetFontFile() const /* the full path/filename to the font */ { return strFontFile; } - wxString GetINIPath() const /* the full path/filename to the INI file, or wxEmptyString */ - { return strINIPath; } + wxString GetINIPath() const /* the full path/filename to the INI file, or wxEmptyString */ + { return strINIPath; } private: wxString strRootPath; @@ -78,7 +78,7 @@ class LibraryManagerApp : public wxApp wxString strDocumentationPath; wxString strUserDataPath; wxString strFontFile; - wxString strINIPath; + wxString strINIPath; }; extern LibraryManagerApp* theApp; diff --git a/src/librarymanager.rc b/src/librarymanager.rc index 0b5c5c1..1712261 100644 --- a/src/librarymanager.rc +++ b/src/librarymanager.rc @@ -1,7 +1,7 @@ /* Librarian for KiCad, a free EDA CAD application. * - * Copyright 2013-2017 CompuPhase - * $Id: librarymanager.rc 5685 2017-05-23 10:35:40Z thiadmer $ + * Copyright 2013-2018 CompuPhase + * $Id: librarymanager.rc 5907 2018-12-14 22:05:40Z thiadmer $ */ #include @@ -16,14 +16,14 @@ AppIcon ICON "res/logo.ico" * for details on version information and the VERSIONINFO structure. */ #define VERSION 1 -#define REVISION 2 +#define REVISION 4 #define BUILD SVN_REV #define VERSIONSTR SVN_REVSTR "\0" #define VERSIONNAME "Librarian.exe\0" #define VERSIONDESCRIPTION "KiCad Librarian\0" #define VERSIONCOMPANYNAME "CompuPhase\0" #define VERSIONPRODUCTNAME "LibraryManager\0" -#define VERSIONCOPYRIGHT "Copyright \251 ITB CompuPhase 2013-2017\0" +#define VERSIONCOPYRIGHT "Copyright \251 ITB CompuPhase 2013-2018\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VERSION, REVISION, BUILD, 0 diff --git a/src/pdfreport.cpp b/src/pdfreport.cpp index 11d68e6..f5ec667 100644 --- a/src/pdfreport.cpp +++ b/src/pdfreport.cpp @@ -2,7 +2,7 @@ * Librarian for KiCad, a free EDA CAD application. * Report generation functions, based on libHaru. * - * Copyright (C) 2013-2017 CompuPhase + * Copyright (C) 2013-2018 CompuPhase * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy @@ -16,7 +16,7 @@ * License for the specific language governing permissions and limitations * under the License. * - * $Id: pdfreport.cpp 5685 2017-05-23 10:35:40Z thiadmer $ + * $Id: pdfreport.cpp 5907 2018-12-14 22:05:40Z thiadmer $ */ #include @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include "svnrev.h" @@ -278,7 +278,7 @@ bool PdfReport::FootprintReport(wxWindow* parent, const wxString& library, const double extent_x1=0,extent_y1=0,extent_x2=0,extent_y2=0; #define UPDATE_EXTENTS(x,y) { if ((x)extent_x2) extent_x2=(x); if ((y)extent_y2) extent_y2=(y); } double xoffs,yoffs; - double module_angle = 0; /* all angles should be corrected with the footprint angle */ + double module_angle = 0; /* all angles should be corrected with the footprint angle */ if (DryRun) { xoffs=yoffs=0; } else { @@ -300,8 +300,8 @@ bool PdfReport::FootprintReport(wxWindow* parent, const wxString& library, const wxString line=module[r]; wxString token=GetToken(&line); if (token.CmpNoCase(wxT("Po")) == 0) { - GetToken(&line); /* ignore X position */ - GetToken(&line); /* ignore Y position */ + GetToken(&line); /* ignore X position */ + GetToken(&line); /* ignore Y position */ if (line.length()>0) module_angle=GetTokenLong(&line)/10.0; } else if (token.CmpNoCase(wxT("DS"))==0) { @@ -354,7 +354,7 @@ bool PdfReport::FootprintReport(wxWindow* parent, const wxString& library, const HPDF_Page_SetRGBStroke(page,(HPDF_REAL)0.7,(HPDF_REAL)0.7,(HPDF_REAL)0.0); Arc(page,xpos+MM(x),ybase+MM(y),MM(radius),startangle,endangle,true,false); } else if (token.CmpNoCase(wxT("DP")) == 0) { - GetToken(&line); /* ignore first four unknown values */ + GetToken(&line); /* ignore first four unknown values */ GetToken(&line); GetToken(&line); GetToken(&line); @@ -362,7 +362,7 @@ bool PdfReport::FootprintReport(wxWindow* parent, const wxString& library, const wxASSERT(count>0); double penwidth=GetTokenDim(&line,unit_mm); if (penwidth<0.1) - penwidth=0.1; /* minimum pen width, as we are only drawing the outline */ + penwidth=0.1; /* minimum pen width, as we are only drawing the outline */ /* ignore layer */ /* Haru PDF has a limitation in drawing polygons, so we draw just the outline */ HPDF_Page_SetLineWidth(page,MM(penwidth)); @@ -414,8 +414,8 @@ bool PdfReport::FootprintReport(wxWindow* parent, const wxString& library, const StrokeText(page,xpos+MM(x),ybase+MM(y),token.wc_str(wxConvLibc),MM(cx),MM(cy),rot); } } else if (token.Cmp(wxT("(at")) == 0) { - GetToken(&line); /* ignore X */ - GetToken(&line); /* ignore Y */ + GetToken(&line); /* ignore X */ + GetToken(&line); /* ignore Y */ if (line.length() > 0) module_angle=GetTokenDouble(&line); } else if (token.Cmp(wxT("(fp_line"))==0) { @@ -567,8 +567,8 @@ bool PdfReport::FootprintReport(wxWindow* parent, const wxString& library, const bool inpad = false; double padx = 0, pady = 0, padwidth = 0, padheight = 0, padrot = 0; double drillx = 0, drilly = 0, drillwidth = 0, drillheight = 0; - double paddelta = 0; - int paddeltadir = 0; + double paddelta = 0; + int paddeltadir = 0; wxString padpin, padshape; for (unsigned r=0; rEPSILON) { @@ -638,18 +638,18 @@ bool PdfReport::FootprintReport(wxWindow* parent, const wxString& library, const padwidth=GetTokenDim(§ion,true); padheight=GetTokenDim(§ion,true); } - section = GetSection(line, wxT("rect_delta")); - if (section.length() > 0) { - double paddeltax = GetTokenDim(§ion, true); - double paddeltay = GetTokenDim(§ion, true); - if (!Equal(paddeltax, 0.0)) { - paddelta = paddeltax; - paddeltadir = 1; - } else { - paddelta = paddeltay; - paddeltadir = 0; - } - } + section = GetSection(line, wxT("rect_delta")); + if (section.length() > 0) { + double paddeltax = GetTokenDim(§ion, true); + double paddeltay = GetTokenDim(§ion, true); + if (!Equal(paddeltax, 0.0)) { + paddelta = paddeltax; + paddeltadir = 1; + } else { + paddelta = paddeltay; + paddeltadir = 0; + } + } drillx=drilly=0; /* preset drill parameters (as these are optional) */ drillwidth=drillheight=0; section=GetSection(line,wxT("drill")); @@ -689,7 +689,7 @@ bool PdfReport::FootprintReport(wxWindow* parent, const wxString& library, const RoundedRect(page,cs.GetX(),cs.GetY(),cs.GetX()+cs.GetWidth(),cs.GetY()+cs.GetHeight(), cs.GetWidth()first + wxT(" : ") + iter->second; - TextWrap(page,PAGEMARGIN,0,(PageWidth-PAGEMARGIN)/2,PageHeight,1.2*FontSize,-1.5*FontSize,line.utf8_str(),&numlines); - lines+=numlines; + /* optionally add the pages for the index */ + if (PrintIndex) { + long lines=0; + for (wxStringToStringHashMap::iterator iter=FootprintIndex.begin(); iter!=FootprintIndex.end(); iter++) { + int numlines=0; + wxString line=iter->first + wxT(" : ") + iter->second; + TextWrap(page,PAGEMARGIN,0,(PageWidth-PAGEMARGIN)/2,PageHeight,1.2*FontSize,-1.5*FontSize,line.utf8_str(),&numlines); + lines+=numlines; + } + long columns=(lines+IndexLines-1)/IndexLines; + pagecount+=(columns+1)/2; } - long columns=(lines+IndexLines-1)/IndexLines; - pagecount+=(columns+1)/2; } DryRun=!DryRun; } while (!DryRun); - /* sort the index */ - wxSortedArrayString SortedIndex(CompareFootprint); - for (wxStringToStringHashMap::iterator iter = FootprintIndex.begin(); iter != FootprintIndex.end(); iter++) { - wxString line = iter->first + wxT(" : ") + iter->second; - SortedIndex.Add(line); - } - FootprintIndex.clear(); - - /* print the index */ - progress.Update(++progresspos,wxT("Generating the index")); - DryRun=false; - size_t base=0; - while (basefirst + wxT(" : ") + iter->second; + SortedIndex.Add(line); + } + FootprintIndex.clear(); + + /* print the index */ + progress.Update(++progresspos,wxT("Generating the index")); + DryRun=false; + size_t base=0; + while (base -#include -#include -#include -#include - - -enum { - UNINITIALIZED = 0, - GLOBAL_INIT, - EASY_INIT, - - INIT_FAILED = -1, -}; -static int Initialized = UNINITIALIZED; -static bool Valid = false; -static CURL* curl = 0; - - -bool curlInit() -{ - if (Initialized == UNINITIALIZED) { - Valid = false; - CURLcode code; - #if defined _WIN32 - code = curl_global_init(CURL_GLOBAL_SSL | CURL_GLOBAL_WIN32); - #else - code = curl_global_init(CURL_GLOBAL_SSL); - #endif - Initialized = (code == CURLE_OK) ? GLOBAL_INIT : INIT_FAILED; - } - if (Initialized == GLOBAL_INIT) { - curl = curl_easy_init(); - if (curl) - Initialized = EASY_INIT; - } - if (Initialized == INIT_FAILED) { - Valid = false; /* should already be false */ - return false; - } - Valid = curlReset(); - return Valid; -} - -bool curlReset() -{ - Valid = false; - if (!curl) - return false; - curl_easy_reset(curl); - - curl_easy_setopt(curl, CURLOPT_UPLOAD, 0); - curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL); - curl_easy_setopt(curl, CURLOPT_READDATA, NULL); - - /* set URL, username and password */ - wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); - wxString field; - wxCharBuffer postbuffer; - - field = config->Read(wxT("repository/url")); - if (field.Length() > 0) { - postbuffer = field.mb_str(); - curl_easy_setopt(curl, CURLOPT_URL, postbuffer.data()); - Valid = true; - } - - field = config->Read(wxT("repository/hostuser")); - if (field.Length() > 0) { - postbuffer = field.mb_str(); - curl_easy_setopt(curl, CURLOPT_USERNAME, postbuffer.data()); - } - - field = config->Read(wxT("repository/hostpwd")); - if (field.Length() > 0) { - field = Scramble(field); /* this un-scrambles the field */ - postbuffer = field.mb_str(); - curl_easy_setopt(curl, CURLOPT_PASSWORD, postbuffer.data()); - curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); - } - long flags; - config->Read(wxT("repository/hostverify"), &flags, 0x03); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, (flags & 0x01) ? 1 : 0); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, (flags & 0x02) ? 2 : 0); - - delete config; - - /* other options */ - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - - return true; -} - -void curlCleanup(bool global) -{ - Valid = false; - if (curl) { - wxASSERT(Initialized == EASY_INIT); - Initialized = GLOBAL_INIT; - curl_easy_cleanup(curl); - curl = 0; - } - if (global && Initialized >= GLOBAL_INIT) { - curl_global_cleanup(); - Initialized = UNINITIALIZED; - } -} - -/* Converts a hex character to its integer value */ -static inline char from_hex(char ch) -{ - return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; -} - -/* Converts an integer value to its hex character*/ -static inline char to_hex(char code) -{ - static char hex[] = "0123456789abcdef"; - return hex[code & 15]; -} - -/* Returns a url-encoded version of the input string */ -wxString URLEncode(const wxString& string) -{ - char *buf = (char*)malloc(string.length() * 3 + 1); /* absolute worst case */ - if (!buf) - return wxEmptyString; - char *pbuf = buf; - wxCharBuffer source = string.mb_str(wxConvUTF8); - for (const char *pstr = source.data(); *pstr; pstr++) { - if (*pstr >= 0 && *pstr <= 0x7f - && (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~')) - { - *pbuf++ = *pstr; - } else if (*pstr == ' ') { - *pbuf++ = '+'; - } else { - *pbuf++ = '%'; - *pbuf++ = to_hex(*pstr >> 4); - *pbuf++ = to_hex(*pstr & 15); - } - } - *pbuf = '\0'; - wxString result = wxString::FromAscii(buf); - free((void*)buf); - return result; -} - -/* Returns a url-decoded version of the input string */ -wxString URLDecode(const wxString& string) -{ - wxCharBuffer source = string.mb_str(); - const char *pstr = source.data(); - char *buf = (char*)malloc(strlen(pstr) + 1); - if (!buf) - return wxEmptyString; - char *pbuf = buf; - while (*pstr) { - if (*pstr == '%') { - if (pstr[1] && pstr[2]) { - *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]); - pstr += 2; - } - } else if (*pstr == '+') { - *pbuf++ = ' '; - } else { - *pbuf++ = *pstr; - } - pstr++; - } - *pbuf = '\0'; - wxString result = wxString::FromAscii(buf); - free((void*)buf); - return result; -} - -static wxString Base64Encode(const unsigned char* buffer, size_t size, bool linefeeds) -{ - const static wxChar Lookup[] = wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); - const static wxChar Padding = wxT('='); - - wxString encoded; - encoded.Alloc((size / 3 + (size % 3 > 0)) * 4); - - unsigned temp; - size_t idx; - for (idx = 0; idx < size / 3; idx++) { - if (linefeeds && idx > 0 && idx % 16 == 0) - encoded += wxT("\r\n"); /* add a line break after 16 byte triplets */ - temp = (unsigned)(*buffer++) << 16; /* Convert to big endian */ - temp += (unsigned)(*buffer++) << 8; - temp += (unsigned)(*buffer++); - encoded += Lookup[(temp & 0x00FC0000) >> 18]; - encoded += Lookup[(temp & 0x0003F000) >> 12]; - encoded += Lookup[(temp & 0x00000FC0) >> 6 ]; - encoded += Lookup[(temp & 0x0000003F) ]; - } - if (linefeeds && idx > 0 && idx % 16 == 0) - encoded += wxT("\r\n"); - switch (size % 3) { - case 1: - temp = (unsigned)(*buffer++) << 16; - encoded += Lookup[(temp & 0x00FC0000) >> 18]; - encoded += Lookup[(temp & 0x0003F000) >> 12]; - encoded += Padding; - encoded += Padding; - break; - case 2: - temp = (unsigned)(*buffer++) << 16; - temp += (unsigned)(*buffer++) << 8; - encoded += Lookup[(temp & 0x00FC0000) >> 18]; - encoded += Lookup[(temp & 0x0003F000) >> 12]; - encoded += Lookup[(temp & 0x00000FC0) >> 6 ]; - encoded += Padding; - break; - } - return encoded; -} - -wxString Scramble(const wxString& source) -{ - /* this is ROT47 */ - wxString result = source; - for (size_t idx = 0; idx < source.length(); idx++) { - if (result[idx] >= wxT('!') && result[idx] <= wxT('O')) - result[idx] = (((int)result[idx] + 47) % 127); - else if (result[idx] >= wxT('P') && result[idx] <= wxT('~')) - result[idx] = (((int)result[idx] - 47) % 127); - } - return result; -} - -static size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp) -{ - wxString* data = (wxString*)userp; - wxString buf = wxString::FromUTF8((const char*)buffer, size * nmemb); - data->Append(buf); - return size * nmemb; -} - -/** The URL and other parameters must be passed in explicitly; this routine is - * called before the settings are final. - * - * \return wxEmptyString on success, error message on failure. - */ -wxString curlAddUser(const wxString& url, const wxString& user, const wxString& email, - const wxString& hostuser, const wxString& hostpwd, long flags) -{ - wxASSERT(url.length() > 0 && user.length() > 0 && email.length() > 0); - if (!curlInit()) - return wxT("Incorrect configuration for network or repository"); - wxASSERT(curl); - - wxString data; - wxCharBuffer postbuffer; - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); - postbuffer = url.mb_str(); - curl_easy_setopt(curl, CURLOPT_URL, postbuffer.data()); - if (hostuser.length() > 0) { - postbuffer = hostuser.mb_str(); - curl_easy_setopt(curl, CURLOPT_USERNAME, postbuffer.data()); - } - if (hostpwd.Length() > 0) { - postbuffer = hostpwd.mb_str(); - curl_easy_setopt(curl, CURLOPT_PASSWORD, postbuffer.data()); - curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); - } - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, (flags & 0x01) ? 1 : 0); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, (flags & 0x02) ? 2 : 0); - - wxString post = wxT("cmd=user&user=") + URLEncode(user) + wxT("&email=") + URLEncode(email); - //??? also set password, for those repositories where the user can choose his/her password - postbuffer = post.mb_str(); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postbuffer.data()); - CURLcode code = curl_easy_perform(curl); - if (code == CURLE_SSL_CACERT) - return wxT("Host certificate cannot be authenticated with known CA certificates"); - else if (code != CURLE_OK) - return wxT("Connection failure"); - - data.Trim(); - data.Trim(false); - if (data.CmpNoCase(wxT("ok")) == 0) - return wxEmptyString; - return data; -} - -wxString curlList(wxArrayString* list, const wxString& category, const wxString& filter) -{ - if (!Valid && !curlInit()) - return wxT("Incorrect configuration for network or repository"); - wxASSERT(curl); - - wxString data; - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); - - wxString post = wxT("cmd=list&cat=") + category; - if (filter.Length() > 0) - post += wxT("&filter=") + URLEncode(filter); - wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); - wxString field = config->Read(wxT("repository/user")); - if (field.Length() > 0) - post += wxT("&user=") + URLEncode(field); - field = config->Read(wxT("repository/pwd")); - if (field.Length() > 0) { - field = Scramble(field); /* this un-scrambles the field */ - post += wxT("&pwd=") + URLEncode(field); - } - delete config; - wxCharBuffer postbuffer = post.mb_str(); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postbuffer.data()); - CURLcode code = curl_easy_perform(curl); - if (code == CURLE_SSL_CACERT) - return wxT("Host certificate cannot be authenticated with known CA certificates"); - else if (code != CURLE_OK) - return wxT("Connection failure"); - - /* parse the returned string (URL-decode fields) */ - data.Trim(); - while (data.Length() > 0) { - int pos = data.Find(wxT('\n')); - wxString line; - if (pos >= 0) { - line = data.Left(pos); - data = data.Mid(pos + 1); - } else { - line = data; - data.Clear(); - } - /* get first field */ - line.Trim(false); - wxString part; - if (line[0] == wxT('"')) { - line = line.Mid(1); /* skip first '"' */ - pos = line.Find(wxT('"')); /* find next '"' */ - if (pos < 0) - break; /* invalid format */ - part = line.Left(pos); - line = line.Mid(pos + 1); - pos = line.Find(wxT(',')); /* find ',' */ - if (pos < 0) - break; /* invalid format */ - line = line.Mid(pos + 1); - } else { - pos = line.Find(wxT(',')); /* find ',' */ - if (pos < 0) - break; /* invalid format */ - part = line.Left(pos); - line = line.Mid(pos + 1); - part.Trim(); - } - /* get second field */ - line.Trim(false); - wxString name; - if (line[0] == wxT('"')) { - line = line.Mid(1); /* skip first '"' */ - pos = line.Find(wxT('"')); /* find next '"' */ - if (pos < 0) - break; /* invalid format */ - name = line.Left(pos); - } else { - name = line; - name.Trim(); - } - list->Add(part + wxT("\t") + name); - } - - if (data.length() > 0) - return data; /* likely an error message (instead of a part listing) */ - return wxEmptyString; -} - -/** curlGet() retrieves a part from the repository. - * If the author name is empty, the current user will be assumed. - * If the module parameter is NULL, the function will only return whether the symbol exists. - */ -wxString curlGet(const wxString& partname, const wxString& author, const wxString& category, wxArrayString* module) -{ - if (!Valid && !curlInit()) - return wxT("Incorrect configuration for network or repository"); - wxASSERT(curl); - - wxString data; - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); - - wxString post = wxT("cmd=get&cat=") + category + wxT("&part=") + URLEncode(partname); - wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); - wxString field = config->Read(wxT("repository/user")); - if (author.length() == 0) - post += wxT("&author=") + URLEncode(field); - else - post += wxT("&author=") + URLEncode(author); - post += wxT("&user=") + URLEncode(field); - field = config->Read(wxT("repository/pwd")); - if (field.Length() > 0) { - field = Scramble(field); /* this un-scrambles the field */ - post += wxT("&pwd=") + URLEncode(field); - } - delete config; - - wxCharBuffer postbuffer = post.mb_str(); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postbuffer.data()); - CURLcode code = curl_easy_perform(curl); - if (code != CURLE_OK) - return wxT("Connection failure"); - - data.Trim(); - data.Trim(false); - if (data.length() == 0) - return wxT("Not found"); - if (data.Left(6).CmpNoCase(wxT("ERROR:")) == 0) - return data; - - wxString copydata = data; /* save */ - int count = 0; - while (data.length() > 0) { - count++; - int pos = data.Find(wxT('\n')); - wxString line; - if (pos >= 0) { - line = data.Left(pos); - data = data.Mid(pos + 1); - } else { - line = data; - data.Clear(); - } - if (module) { - line.Trim(); - module->Add(line); - } - } - if (count <= 2) - return copydata; /* only 1 or 2 lines in the part definition, this must be an error message */ - return wxEmptyString; -} - -/** curlPut() stores a part in the repository. - * The author of the part is implicitly set to the user name. - */ -wxString curlPut(const wxString& partname, const wxString& category, const wxArrayString& module) -{ - if (!Valid && !curlInit()) - return wxT("Incorrect configuration for network or repository"); - wxASSERT(curl); - - wxString data; - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); - - wxString post = wxT("cmd=put&cat=") + category + wxT("&part=") + URLEncode(partname); - wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); - wxString field = config->Read(wxT("repository/user")); - post += wxT("&author=") + URLEncode(field) + wxT("&user=") + URLEncode(field); - field = config->Read(wxT("repository/pwd")); - if (field.Length() > 0) { - field = Scramble(field); /* this un-scrambles the field */ - post += wxT("&pwd=") + URLEncode(field); - } - delete config; - - field.Clear(); - for (size_t idx = 0; idx < module.Count(); idx++) { - if (idx > 0) - field += wxT("\n"); - field += module[idx]; - } - post += wxT("&data=") + URLEncode(field); - - wxCharBuffer postbuffer = post.mb_str(); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postbuffer.data()); - CURLcode code = curl_easy_perform(curl); - if (code != CURLE_OK) - return wxT("Connection failure"); - - data.Trim(); - data.Trim(false); - if (data.CmpNoCase(wxT("ok")) == 0) - return wxEmptyString; - return data; -} - -/** curlPutInfo() updates a part in the repository. - * The author of the part is implicitly set to the user name. - */ -wxString curlPutInfo(const wxString& partname, const wxString& category, const wxString& fields, - const wxString& imagefile) -{ - if (!Valid && !curlInit()) - return wxT("Incorrect configuration for network or repository"); - wxASSERT(curl); - - wxString data; - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); - - wxString post = wxT("cmd=putinfo&cat=") + category + wxT("&part=") + URLEncode(partname); - wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); - wxString field = config->Read(wxT("repository/user")); - post += wxT("&author=") + URLEncode(field) + wxT("&user=") + URLEncode(field); - field = config->Read(wxT("repository/pwd")); - if (field.Length() > 0) { - field = Scramble(field); /* this un-scrambles the field */ - post += wxT("&pwd=") + URLEncode(field); - } - delete config; - - post += wxT("&") + fields; - - if (imagefile.Length() > 0) { - wxFFile imgdata(imagefile, wxT("rb")); - if (imgdata.IsOpened()) { - wxFileOffset length = imgdata.Length(); - unsigned char *buffer = new unsigned char[length]; - imgdata.Read(buffer, length); - imgdata.Close(); - wxFileName fname(imagefile); - post += wxT("&thumb=data:image/") + fname.GetExt().MakeLower() + wxT(";base64,") - + Base64Encode(buffer, length, false); - delete[] buffer; - } - } - - wxCharBuffer postbuffer = post.mb_str(); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postbuffer.data()); - CURLcode code = curl_easy_perform(curl); - if (code != CURLE_OK) - return wxT("Connection failure"); - - data.Trim(); - data.Trim(false); - if (data.CmpNoCase(wxT("ok")) == 0) - return wxEmptyString; - return data; -} - -/** curlDelete() deletes a part from the repository. - * The author of the part is implicitly set to the user name. - */ -wxString curlDelete(const wxString& partname, const wxString& category) -{ - if (!Valid && !curlInit()) - return wxT("Incorrect configuration for network or repository"); - wxASSERT(curl); - - wxString data; - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); - - wxString post = wxT("cmd=del&cat=") + category + wxT("&part=") + URLEncode(partname); - wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); - wxString field = config->Read(wxT("repository/user")); - post += wxT("&author=") + URLEncode(field) + wxT("&user=") + URLEncode(field); - field = config->Read(wxT("repository/pwd")); - if (field.Length() > 0) { - field = Scramble(field); /* this un-scrambles the field */ - post += wxT("&pwd=") + URLEncode(field); - } - delete config; - - wxCharBuffer postbuffer = post.mb_str(); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postbuffer.data()); - CURLcode code = curl_easy_perform(curl); - if (code != CURLE_OK) - return wxT("Connection failure"); - - data.Trim(); - data.Trim(false); - if (data.CmpNoCase(wxT("ok")) == 0) - return wxEmptyString; - return data; -} - +/* + * Librarian for KiCad, a free EDA CAD application. + * Utility functions transferring modules or footprints to/from a repository. + * + * Copyright (C) 2013-2015 CompuPhase + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * $Id: remotelink.cpp 5686 2017-05-24 13:56:46Z thiadmer $ + */ + +#include "librarymanager.h" +#include "remotelink.h" +#include +#include +#include +#include +#include + + +enum { + UNINITIALIZED = 0, + GLOBAL_INIT, + EASY_INIT, + + INIT_FAILED = -1, +}; +static int Initialized = UNINITIALIZED; +static bool Valid = false; +static CURL* curl = 0; + + +bool curlInit() +{ + if (Initialized == UNINITIALIZED) { + Valid = false; + CURLcode code; + #if defined _WIN32 + code = curl_global_init(CURL_GLOBAL_SSL | CURL_GLOBAL_WIN32); + #else + code = curl_global_init(CURL_GLOBAL_SSL); + #endif + Initialized = (code == CURLE_OK) ? GLOBAL_INIT : INIT_FAILED; + } + if (Initialized == GLOBAL_INIT) { + curl = curl_easy_init(); + if (curl) + Initialized = EASY_INIT; + } + if (Initialized == INIT_FAILED) { + Valid = false; /* should already be false */ + return false; + } + Valid = curlReset(); + return Valid; +} + +bool curlReset() +{ + Valid = false; + if (!curl) + return false; + curl_easy_reset(curl); + + curl_easy_setopt(curl, CURLOPT_UPLOAD, 0); + curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL); + curl_easy_setopt(curl, CURLOPT_READDATA, NULL); + + /* set URL, username and password */ + wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); + wxString field; + wxCharBuffer postbuffer; + + field = config->Read(wxT("repository/url")); + if (field.Length() > 0) { + postbuffer = field.mb_str(); + curl_easy_setopt(curl, CURLOPT_URL, postbuffer.data()); + Valid = true; + } + + field = config->Read(wxT("repository/hostuser")); + if (field.Length() > 0) { + postbuffer = field.mb_str(); + curl_easy_setopt(curl, CURLOPT_USERNAME, postbuffer.data()); + } + + field = config->Read(wxT("repository/hostpwd")); + if (field.Length() > 0) { + field = Scramble(field); /* this un-scrambles the field */ + postbuffer = field.mb_str(); + curl_easy_setopt(curl, CURLOPT_PASSWORD, postbuffer.data()); + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); + } + long flags; + config->Read(wxT("repository/hostverify"), &flags, 0x03); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, (flags & 0x01) ? 1 : 0); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, (flags & 0x02) ? 2 : 0); + + delete config; + + /* other options */ + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + + return true; +} + +void curlCleanup(bool global) +{ + Valid = false; + if (curl) { + wxASSERT(Initialized == EASY_INIT); + Initialized = GLOBAL_INIT; + curl_easy_cleanup(curl); + curl = 0; + } + if (global && Initialized >= GLOBAL_INIT) { + curl_global_cleanup(); + Initialized = UNINITIALIZED; + } +} + +/* Converts a hex character to its integer value */ +static inline char from_hex(char ch) +{ + return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; +} + +/* Converts an integer value to its hex character*/ +static inline char to_hex(char code) +{ + static char hex[] = "0123456789abcdef"; + return hex[code & 15]; +} + +/* Returns a url-encoded version of the input string */ +wxString URLEncode(const wxString& string) +{ + char *buf = (char*)malloc(string.length() * 3 + 1); /* absolute worst case */ + if (!buf) + return wxEmptyString; + char *pbuf = buf; + wxCharBuffer source = string.mb_str(wxConvUTF8); + for (const char *pstr = source.data(); *pstr; pstr++) { + if (*pstr >= 0 && *pstr <= 0x7f + && (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~')) + { + *pbuf++ = *pstr; + } else if (*pstr == ' ') { + *pbuf++ = '+'; + } else { + *pbuf++ = '%'; + *pbuf++ = to_hex(*pstr >> 4); + *pbuf++ = to_hex(*pstr & 15); + } + } + *pbuf = '\0'; + wxString result = wxString::FromAscii(buf); + free((void*)buf); + return result; +} + +/* Returns a url-decoded version of the input string */ +wxString URLDecode(const wxString& string) +{ + wxCharBuffer source = string.mb_str(); + const char *pstr = source.data(); + char *buf = (char*)malloc(strlen(pstr) + 1); + if (!buf) + return wxEmptyString; + char *pbuf = buf; + while (*pstr) { + if (*pstr == '%') { + if (pstr[1] && pstr[2]) { + *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]); + pstr += 2; + } + } else if (*pstr == '+') { + *pbuf++ = ' '; + } else { + *pbuf++ = *pstr; + } + pstr++; + } + *pbuf = '\0'; + wxString result = wxString::FromAscii(buf); + free((void*)buf); + return result; +} + +static wxString Base64Encode(const unsigned char* buffer, size_t size, bool linefeeds) +{ + const static wxChar Lookup[] = wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); + const static wxChar Padding = wxT('='); + + wxString encoded; + encoded.Alloc((size / 3 + (size % 3 > 0)) * 4); + + unsigned temp; + size_t idx; + for (idx = 0; idx < size / 3; idx++) { + if (linefeeds && idx > 0 && idx % 16 == 0) + encoded += wxT("\r\n"); /* add a line break after 16 byte triplets */ + temp = (unsigned)(*buffer++) << 16; /* Convert to big endian */ + temp += (unsigned)(*buffer++) << 8; + temp += (unsigned)(*buffer++); + encoded += Lookup[(temp & 0x00FC0000) >> 18]; + encoded += Lookup[(temp & 0x0003F000) >> 12]; + encoded += Lookup[(temp & 0x00000FC0) >> 6 ]; + encoded += Lookup[(temp & 0x0000003F) ]; + } + if (linefeeds && idx > 0 && idx % 16 == 0) + encoded += wxT("\r\n"); + switch (size % 3) { + case 1: + temp = (unsigned)(*buffer++) << 16; + encoded += Lookup[(temp & 0x00FC0000) >> 18]; + encoded += Lookup[(temp & 0x0003F000) >> 12]; + encoded += Padding; + encoded += Padding; + break; + case 2: + temp = (unsigned)(*buffer++) << 16; + temp += (unsigned)(*buffer++) << 8; + encoded += Lookup[(temp & 0x00FC0000) >> 18]; + encoded += Lookup[(temp & 0x0003F000) >> 12]; + encoded += Lookup[(temp & 0x00000FC0) >> 6 ]; + encoded += Padding; + break; + } + return encoded; +} + +wxString Scramble(const wxString& source) +{ + /* this is ROT47 */ + wxString result = source; + for (size_t idx = 0; idx < source.length(); idx++) { + if (result[idx] >= wxT('!') && result[idx] <= wxT('O')) + result[idx] = (((int)result[idx] + 47) % 127); + else if (result[idx] >= wxT('P') && result[idx] <= wxT('~')) + result[idx] = (((int)result[idx] - 47) % 127); + } + return result; +} + +static size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp) +{ + wxString* data = (wxString*)userp; + wxString buf = wxString::FromUTF8((const char*)buffer, size * nmemb); + data->Append(buf); + return size * nmemb; +} + +/** The URL and other parameters must be passed in explicitly; this routine is + * called before the settings are final. + * + * \return wxEmptyString on success, error message on failure. + */ +wxString curlAddUser(const wxString& url, const wxString& user, const wxString& email, + const wxString& hostuser, const wxString& hostpwd, long flags) +{ + wxASSERT(url.length() > 0 && user.length() > 0 && email.length() > 0); + if (!curlInit()) + return wxT("Incorrect configuration for network or repository"); + wxASSERT(curl); + + wxString data; + wxCharBuffer postbuffer; + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); + postbuffer = url.mb_str(); + curl_easy_setopt(curl, CURLOPT_URL, postbuffer.data()); + if (hostuser.length() > 0) { + postbuffer = hostuser.mb_str(); + curl_easy_setopt(curl, CURLOPT_USERNAME, postbuffer.data()); + } + if (hostpwd.Length() > 0) { + postbuffer = hostpwd.mb_str(); + curl_easy_setopt(curl, CURLOPT_PASSWORD, postbuffer.data()); + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); + } + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, (flags & 0x01) ? 1 : 0); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, (flags & 0x02) ? 2 : 0); + + wxString post = wxT("cmd=user&user=") + URLEncode(user) + wxT("&email=") + URLEncode(email); + //??? also set password, for those repositories where the user can choose his/her password + postbuffer = post.mb_str(); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postbuffer.data()); + CURLcode code = curl_easy_perform(curl); + if (code == CURLE_SSL_CACERT) + return wxT("Host certificate cannot be authenticated with known CA certificates"); + else if (code != CURLE_OK) + return wxT("Connection failure"); + + data.Trim(); + data.Trim(false); + if (data.CmpNoCase(wxT("ok")) == 0) + return wxEmptyString; + return data; +} + +wxString curlList(wxArrayString* list, const wxString& category, const wxString& filter) +{ + if (!Valid && !curlInit()) + return wxT("Incorrect configuration for network or repository"); + wxASSERT(curl); + + wxString data; + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); + + wxString post = wxT("cmd=list&cat=") + category; + if (filter.Length() > 0) + post += wxT("&filter=") + URLEncode(filter); + wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); + wxString field = config->Read(wxT("repository/user")); + if (field.Length() > 0) + post += wxT("&user=") + URLEncode(field); + field = config->Read(wxT("repository/pwd")); + if (field.Length() > 0) { + field = Scramble(field); /* this un-scrambles the field */ + post += wxT("&pwd=") + URLEncode(field); + } + delete config; + wxCharBuffer postbuffer = post.mb_str(); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postbuffer.data()); + CURLcode code = curl_easy_perform(curl); + if (code == CURLE_SSL_CACERT) + return wxT("Host certificate cannot be authenticated with known CA certificates"); + else if (code != CURLE_OK) + return wxT("Connection failure"); + + /* parse the returned string (URL-decode fields) */ + data.Trim(); + while (data.Length() > 0) { + int pos = data.Find(wxT('\n')); + wxString line; + if (pos >= 0) { + line = data.Left(pos); + data = data.Mid(pos + 1); + } else { + line = data; + data.Clear(); + } + /* get first field */ + line.Trim(false); + wxString part; + if (line[0] == wxT('"')) { + line = line.Mid(1); /* skip first '"' */ + pos = line.Find(wxT('"')); /* find next '"' */ + if (pos < 0) + break; /* invalid format */ + part = line.Left(pos); + line = line.Mid(pos + 1); + pos = line.Find(wxT(',')); /* find ',' */ + if (pos < 0) + break; /* invalid format */ + line = line.Mid(pos + 1); + } else { + pos = line.Find(wxT(',')); /* find ',' */ + if (pos < 0) + break; /* invalid format */ + part = line.Left(pos); + line = line.Mid(pos + 1); + part.Trim(); + } + /* get second field */ + line.Trim(false); + wxString name; + if (line[0] == wxT('"')) { + line = line.Mid(1); /* skip first '"' */ + pos = line.Find(wxT('"')); /* find next '"' */ + if (pos < 0) + break; /* invalid format */ + name = line.Left(pos); + } else { + name = line; + name.Trim(); + } + list->Add(part + wxT("\t") + name); + } + + if (data.length() > 0) + return data; /* likely an error message (instead of a part listing) */ + return wxEmptyString; +} + +/** curlGet() retrieves a part from the repository. + * If the author name is empty, the current user will be assumed. + * If the module parameter is NULL, the function will only return whether the symbol exists. + */ +wxString curlGet(const wxString& partname, const wxString& author, const wxString& category, wxArrayString* module) +{ + if (!Valid && !curlInit()) + return wxT("Incorrect configuration for network or repository"); + wxASSERT(curl); + + wxString data; + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); + + wxString post = wxT("cmd=get&cat=") + category + wxT("&part=") + URLEncode(partname); + wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); + wxString field = config->Read(wxT("repository/user")); + if (author.length() == 0) + post += wxT("&author=") + URLEncode(field); + else + post += wxT("&author=") + URLEncode(author); + post += wxT("&user=") + URLEncode(field); + field = config->Read(wxT("repository/pwd")); + if (field.Length() > 0) { + field = Scramble(field); /* this un-scrambles the field */ + post += wxT("&pwd=") + URLEncode(field); + } + delete config; + + wxCharBuffer postbuffer = post.mb_str(); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postbuffer.data()); + CURLcode code = curl_easy_perform(curl); + if (code != CURLE_OK) + return wxT("Connection failure"); + + data.Trim(); + data.Trim(false); + if (data.length() == 0) + return wxT("Not found"); + if (data.Left(6).CmpNoCase(wxT("ERROR:")) == 0) + return data; + + wxString copydata = data; /* save */ + int count = 0; + while (data.length() > 0) { + count++; + int pos = data.Find(wxT('\n')); + wxString line; + if (pos >= 0) { + line = data.Left(pos); + data = data.Mid(pos + 1); + } else { + line = data; + data.Clear(); + } + if (module) { + line.Trim(); + module->Add(line); + } + } + if (count <= 2) + return copydata; /* only 1 or 2 lines in the part definition, this must be an error message */ + return wxEmptyString; +} + +/** curlPut() stores a part in the repository. + * The author of the part is implicitly set to the user name. + */ +wxString curlPut(const wxString& partname, const wxString& category, const wxArrayString& module) +{ + if (!Valid && !curlInit()) + return wxT("Incorrect configuration for network or repository"); + wxASSERT(curl); + + wxString data; + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); + + wxString post = wxT("cmd=put&cat=") + category + wxT("&part=") + URLEncode(partname); + wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); + wxString field = config->Read(wxT("repository/user")); + post += wxT("&author=") + URLEncode(field) + wxT("&user=") + URLEncode(field); + field = config->Read(wxT("repository/pwd")); + if (field.Length() > 0) { + field = Scramble(field); /* this un-scrambles the field */ + post += wxT("&pwd=") + URLEncode(field); + } + delete config; + + field.Clear(); + for (size_t idx = 0; idx < module.Count(); idx++) { + if (idx > 0) + field += wxT("\n"); + field += module[idx]; + } + post += wxT("&data=") + URLEncode(field); + + wxCharBuffer postbuffer = post.mb_str(); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postbuffer.data()); + CURLcode code = curl_easy_perform(curl); + if (code != CURLE_OK) + return wxT("Connection failure"); + + data.Trim(); + data.Trim(false); + if (data.CmpNoCase(wxT("ok")) == 0) + return wxEmptyString; + return data; +} + +/** curlPutInfo() updates a part in the repository. + * The author of the part is implicitly set to the user name. + */ +wxString curlPutInfo(const wxString& partname, const wxString& category, const wxString& fields, + const wxString& imagefile) +{ + if (!Valid && !curlInit()) + return wxT("Incorrect configuration for network or repository"); + wxASSERT(curl); + + wxString data; + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); + + wxString post = wxT("cmd=putinfo&cat=") + category + wxT("&part=") + URLEncode(partname); + wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); + wxString field = config->Read(wxT("repository/user")); + post += wxT("&author=") + URLEncode(field) + wxT("&user=") + URLEncode(field); + field = config->Read(wxT("repository/pwd")); + if (field.Length() > 0) { + field = Scramble(field); /* this un-scrambles the field */ + post += wxT("&pwd=") + URLEncode(field); + } + delete config; + + post += wxT("&") + fields; + + if (imagefile.Length() > 0) { + wxFFile imgdata(imagefile, wxT("rb")); + if (imgdata.IsOpened()) { + wxFileOffset length = imgdata.Length(); + unsigned char *buffer = new unsigned char[length]; + imgdata.Read(buffer, length); + imgdata.Close(); + wxFileName fname(imagefile); + post += wxT("&thumb=data:image/") + fname.GetExt().MakeLower() + wxT(";base64,") + + Base64Encode(buffer, length, false); + delete[] buffer; + } + } + + wxCharBuffer postbuffer = post.mb_str(); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postbuffer.data()); + CURLcode code = curl_easy_perform(curl); + if (code != CURLE_OK) + return wxT("Connection failure"); + + data.Trim(); + data.Trim(false); + if (data.CmpNoCase(wxT("ok")) == 0) + return wxEmptyString; + return data; +} + +/** curlDelete() deletes a part from the repository. + * The author of the part is implicitly set to the user name. + */ +wxString curlDelete(const wxString& partname, const wxString& category) +{ + if (!Valid && !curlInit()) + return wxT("Incorrect configuration for network or repository"); + wxASSERT(curl); + + wxString data; + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); + + wxString post = wxT("cmd=del&cat=") + category + wxT("&part=") + URLEncode(partname); + wxFileConfig *config = new wxFileConfig(APP_NAME, VENDOR_NAME, theApp->GetINIPath()); + wxString field = config->Read(wxT("repository/user")); + post += wxT("&author=") + URLEncode(field) + wxT("&user=") + URLEncode(field); + field = config->Read(wxT("repository/pwd")); + if (field.Length() > 0) { + field = Scramble(field); /* this un-scrambles the field */ + post += wxT("&pwd=") + URLEncode(field); + } + delete config; + + wxCharBuffer postbuffer = post.mb_str(); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postbuffer.data()); + CURLcode code = curl_easy_perform(curl); + if (code != CURLE_OK) + return wxT("Connection failure"); + + data.Trim(); + data.Trim(false); + if (data.CmpNoCase(wxT("ok")) == 0) + return wxEmptyString; + return data; +} + diff --git a/src/remotelink.h b/src/remotelink.h index e1959f9..c20f6d7 100644 --- a/src/remotelink.h +++ b/src/remotelink.h @@ -1,42 +1,42 @@ -/* - * Librarian for KiCad, a free EDA CAD application. - * Utility functions transferring modules or footprints to/from a repository. - * - * Copyright (C) 2013-2015 CompuPhase - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * $Id: remotelink.h 5405 2015-11-20 09:40:55Z thiadmer $ - */ -#ifndef REMOTELINK_H -#define REMOTELINK_H - -#include - -bool curlInit(); -bool curlReset(); -void curlCleanup(bool global = false); - -wxString curlAddUser(const wxString& url, const wxString& user, const wxString& email, - const wxString& hostuser, const wxString& hostpwd, long flags); -wxString curlList(wxArrayString* list, const wxString& category, const wxString& filter); -wxString curlGet(const wxString& partname, const wxString& author, const wxString& category, wxArrayString* module); -wxString curlPut(const wxString& partname, const wxString& category, const wxArrayString& module); -wxString curlPutInfo(const wxString& partname, const wxString& category, const wxString& fields, const wxString& imagefile); -wxString curlDelete(const wxString& partname, const wxString& category); - -wxString URLEncode(const wxString& string); -wxString URLDecode(const wxString& string); -wxString Scramble(const wxString& source); - -#endif /* REMOTELINK_H */ +/* + * Librarian for KiCad, a free EDA CAD application. + * Utility functions transferring modules or footprints to/from a repository. + * + * Copyright (C) 2013-2015 CompuPhase + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * $Id: remotelink.h 5686 2017-05-24 13:56:46Z thiadmer $ + */ +#ifndef REMOTELINK_H +#define REMOTELINK_H + +#include + +bool curlInit(); +bool curlReset(); +void curlCleanup(bool global = false); + +wxString curlAddUser(const wxString& url, const wxString& user, const wxString& email, + const wxString& hostuser, const wxString& hostpwd, long flags); +wxString curlList(wxArrayString* list, const wxString& category, const wxString& filter); +wxString curlGet(const wxString& partname, const wxString& author, const wxString& category, wxArrayString* module); +wxString curlPut(const wxString& partname, const wxString& category, const wxArrayString& module); +wxString curlPutInfo(const wxString& partname, const wxString& category, const wxString& fields, const wxString& imagefile); +wxString curlDelete(const wxString& partname, const wxString& category); + +wxString URLEncode(const wxString& string); +wxString URLDecode(const wxString& string); +wxString Scramble(const wxString& source); + +#endif /* REMOTELINK_H */ diff --git a/src/rpn.cpp b/src/rpn.cpp index 7e12730..1418acc 100644 --- a/src/rpn.cpp +++ b/src/rpn.cpp @@ -2,7 +2,7 @@ * Librarian for KiCad, a free EDA CAD application. * The RPN expression parser. * - * Copyright (C) 2013-2017 CompuPhase + * Copyright (C) 2013-2018 CompuPhase * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy @@ -16,7 +16,7 @@ * License for the specific language governing permissions and limitations * under the License. * - * $Id: rpn.cpp 5685 2017-05-23 10:35:40Z thiadmer $ + * $Id: rpn.cpp 5907 2018-12-14 22:05:40Z thiadmer $ */ #include "rpn.h" #include @@ -233,8 +233,8 @@ RPN_ERROR RPNexpression::Parse() RPNvariable v(word + 1, p1); SetVariable(v); } - } else if (isalpha(word[0])) { - if (isupper(word[0])) { + } else if (isalpha(word[0]) || word[0] == '$' && isalpha(word[1])) { + if (isupper(word[0]) || word[0] == '$' && isupper(word[1])) { /* variable reference */ int i = varindex(word); if (i >= 0) { @@ -257,9 +257,9 @@ RPN_ERROR RPNexpression::Parse() push(ceil(p1 - EPSILON)); } else if (strcmp(word, "chr") == 0) { RPNvalue p1 = pop(); - char str[2] = "?"; - str[0] = (char)(int)p1.Double(); - push(RPNvalue(str)); + char str[2] = "?"; + str[0] = (char)(int)p1.Double(); + push(RPNvalue(str)); } else if (strcmp(word, "cos") == 0) { RPNvalue p1 = pop(); push(cos(p1 * M_PI / 180.0)); @@ -282,13 +282,13 @@ RPN_ERROR RPNexpression::Parse() RPNvalue p1 = pop(); push(floor(p1 + EPSILON)); } else if (strcmp(word, "gacol") == 0) { - static const char letters[] = "ABCDEFGHJKLMNPRTUVWY"; /* no I, O, Q, S, X, Z */ + static const char letters[] = "ABCDEFGHJKLMNPRTUVWY"; /* no I, O, Q, S, X, Z */ RPNvalue p1 = pop(); - char str[2] = "?"; - int idx = (int)p1.Double(); - if (idx >= 0 && idx < sizearray(letters)) - str[0] = letters[idx]; - push(RPNvalue(str)); + char str[2] = "?"; + int idx = (int)p1.Double(); + if (idx >= 0 && idx < sizearray(letters)) + str[0] = letters[idx]; + push(RPNvalue(str)); } else if (strcmp(word, "max") == 0) { RPNvalue p2 = pop(); RPNvalue p1 = pop(); @@ -404,31 +404,31 @@ RPN_ERROR RPNexpression::Parse() push(p1); /* restore the 2nd expression */ } break; - case '(': - stackmark = m_top; - break; - case ')': - if (stackmark < 0) { - m_error = RPN_SYNTAX; - } else { - if (word[1] == '=' && stackmark > 0) { - p1 = stack[stackmark - 1]; - int i; - for (i = stackmark; i < m_top; i++) { - p2 = stack[i]; - if (p1.Double() >= p2.Double() - EPSILON && p1.Double() <= p2.Double() + EPSILON) - break; - } - p1 = (i < m_top); /* set to 1 if any of the values in the list match, 0 otherwise */ - m_top = stackmark - 1; /* pop off the list and the parameter */ - push(p1); - } else { - m_error = RPN_INV_OPER; - m_top = stackmark; /* pop off the list */ - } - } - stackmark = -1; - break; + case '(': + stackmark = m_top; + break; + case ')': + if (stackmark < 0) { + m_error = RPN_SYNTAX; + } else { + if (word[1] == '=' && stackmark > 0) { + p1 = stack[stackmark - 1]; + int i; + for (i = stackmark; i < m_top; i++) { + p2 = stack[i]; + if (p1.Double() >= p2.Double() - EPSILON && p1.Double() <= p2.Double() + EPSILON) + break; + } + p1 = (i < m_top); /* set to 1 if any of the values in the list match, 0 otherwise */ + m_top = stackmark - 1; /* pop off the list and the parameter */ + push(p1); + } else { + m_error = RPN_INV_OPER; + m_top = stackmark; /* pop off the list */ + } + } + stackmark = -1; + break; default: if (m_error == RPN_OK) m_error = RPN_INV_OPER; diff --git a/src/rpn.h b/src/rpn.h index 09322d4..fa456b8 100644 --- a/src/rpn.h +++ b/src/rpn.h @@ -1,141 +1,141 @@ -/* - * Librarian for KiCad, a free EDA CAD application. - * The RPN expression parser. - * - * Copyright (C) 2013-2015 CompuPhase - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * $Id: rpn.h 5387 2015-10-22 19:31:30Z thiadmer $ - */ -#ifndef __rpn_h -#define __rpn_h - -#include -#include -#include - -#define MAX_STACK 16 /* max. nesting level of the stack */ -#define MAX_WORD 16 /* max. length of a number, variable or function */ - -class RPNvalue { -public: - RPNvalue(); - RPNvalue(double value); - RPNvalue(const char* text); - RPNvalue(const RPNvalue& value); - ~RPNvalue(); - - RPNvalue& operator=(const RPNvalue& value); - - bool Alpha() const { return m_text != 0; } - double Double() const { return m_value; } /* returns 0.0 for alpha type */ - const char *Text() const { return m_text ? m_text : "#ERROR"; } - - operator double() { return m_value; } /* same as Double() */ - operator const char *() const { return m_text ? m_text : "#ERROR"; } - - void Set(double value); - void Set(const char* text); - void Set(const RPNvalue& value); - -private: - double m_value; - char *m_text; -}; - -class RPNvariable { -public: - RPNvariable(const char *name, double value) : m_value(value) { - assert(strlen(name) <= MAX_WORD); - strcpy(m_name, name); - } - RPNvariable(const char *name, const char *text) : m_value(text) { - assert(strlen(name) <= MAX_WORD); - strcpy(m_name, name); - } - RPNvariable(const char *name, const RPNvalue &value) : m_value(value) { - assert(strlen(name) <= MAX_WORD); - strcpy(m_name, name); - } - - const char *Name() const { return m_name; } - RPNvalue Value() const { return m_value; } - - void Set(double value) { m_value.Set(value); } - void Set(const char *text) { m_value.Set(text); } - void Set(const RPNvalue &value) { m_value.Set(value); } - -private: - char m_name[MAX_WORD + 1]; - RPNvalue m_value; -}; - -enum RPN_ERROR { - RPN_OK = 0, /* no expression error detected, single result left on the stack */ - RPN_EMPTY, /* empty stack, not necessarily an error (when the result of the - expression is stored in a variable, the stack is left empty) */ - RPN_NOTSINGLE,/* more than a single item left on the stack, must be because - of an expression error */ - RPN_UNDERFLOW,/* stack underflow, must be because of an expression error */ - RPN_OVERFLOW, /* stack overflow (expression too complex) */ - RPN_INV_VAR, /* reference to an unknown variable */ - RPN_INV_FUNC, /* calling an unknown function */ - RPN_INV_OPER, /* calling an unknown operator */ - RPN_TEXT_OPER,/* attempt to perform an arithmetic operation on a text string */ - RPN_INV_STRING,/* run-away string */ - RPN_DIV_ZERO, /* division by zero */ - RPN_SYNTAX, /* syntax error */ -}; - -/** Usage: - * 1) create an instance of RPNexpression, passing in the expression itself - * (or call Set() later) - * 2) add/set all variables - * 3) call Parse(), returns a success/failure flag - * 4) if Parse() returned RPN_OK, call Value() to get it - */ -class RPNexpression { -public: - explicit RPNexpression(const char *expression = 0); - ~RPNexpression(); - - void Set(const char *expression); - RPN_ERROR Parse(); - const RPNvalue& Value() const { return stack[0]; } - - bool ExistVariable(const char *name) { return varindex(name) >= 0; } - void SetVariable(const RPNvariable &v); - -private: - void push(const RPNvalue &v) { - if (m_top < MAX_STACK) stack[m_top++] = v; else m_error = RPN_OVERFLOW; - } - const RPNvalue &pop() { - if (m_top > 0) return stack[--m_top]; - if (m_error == RPN_OK) m_error = RPN_UNDERFLOW; - stack[0] = RPNvalue(0.0); - return stack[0]; - } - int varindex(const char *name); - - char *m_expr; - RPNvalue stack[MAX_STACK]; - int m_top; /* stack top */ - RPN_ERROR m_error; /* returns parsing error, if any */ - - std::vector variables; -}; - -#endif /* __rpn_h */ - +/* + * Librarian for KiCad, a free EDA CAD application. + * The RPN expression parser. + * + * Copyright (C) 2013-2018 CompuPhase + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * $Id: rpn.h 5907 2018-12-14 22:05:40Z thiadmer $ + */ +#ifndef __rpn_h +#define __rpn_h + +#include +#include +#include + +#define MAX_STACK 16 /* max. nesting level of the stack */ +#define MAX_WORD 16 /* max. length of a number, variable or function */ + +class RPNvalue { +public: + RPNvalue(); + RPNvalue(double value); + RPNvalue(const char* text); + RPNvalue(const RPNvalue& value); + ~RPNvalue(); + + RPNvalue& operator=(const RPNvalue& value); + + bool Alpha() const { return m_text != 0; } + double Double() const { return m_value; } /* returns 0.0 for alpha type */ + const char *Text() const { return m_text ? m_text : "#ERROR"; } + + operator double() { return m_value; } /* same as Double() */ + operator const char *() const { return m_text ? m_text : "#ERROR"; } + + void Set(double value); + void Set(const char* text); + void Set(const RPNvalue& value); + +private: + double m_value; + char *m_text; +}; + +class RPNvariable { +public: + RPNvariable(const char *name, double value) : m_value(value) { + assert(strlen(name) <= MAX_WORD); + strcpy(m_name, name); + } + RPNvariable(const char *name, const char *text) : m_value(text) { + assert(strlen(name) <= MAX_WORD); + strcpy(m_name, name); + } + RPNvariable(const char *name, const RPNvalue &value) : m_value(value) { + assert(strlen(name) <= MAX_WORD); + strcpy(m_name, name); + } + + const char *Name() const { return m_name; } + RPNvalue Value() const { return m_value; } + + void Set(double value) { m_value.Set(value); } + void Set(const char *text) { m_value.Set(text); } + void Set(const RPNvalue &value) { m_value.Set(value); } + +private: + char m_name[MAX_WORD + 1]; + RPNvalue m_value; +}; + +enum RPN_ERROR { + RPN_OK = 0, /* no expression error detected, single result left on the stack */ + RPN_EMPTY, /* empty stack, not necessarily an error (when the result of the + expression is stored in a variable, the stack is left empty) */ + RPN_NOTSINGLE,/* more than a single item left on the stack, must be because + of an expression error */ + RPN_UNDERFLOW,/* stack underflow, must be because of an expression error */ + RPN_OVERFLOW, /* stack overflow (expression too complex) */ + RPN_INV_VAR, /* reference to an unknown variable */ + RPN_INV_FUNC, /* calling an unknown function */ + RPN_INV_OPER, /* calling an unknown operator */ + RPN_TEXT_OPER,/* attempt to perform an arithmetic operation on a text string */ + RPN_INV_STRING,/* run-away string */ + RPN_DIV_ZERO, /* division by zero */ + RPN_SYNTAX, /* syntax error */ +}; + +/** Usage: + * 1) create an instance of RPNexpression, passing in the expression itself + * (or call Set() later) + * 2) add/set all variables + * 3) call Parse(), returns a success/failure flag + * 4) if Parse() returned RPN_OK, call Value() to get it + */ +class RPNexpression { +public: + explicit RPNexpression(const char *expression = 0); + ~RPNexpression(); + + void Set(const char *expression); + RPN_ERROR Parse(); + const RPNvalue& Value() const { return stack[0]; } + + bool ExistVariable(const char *name) { return varindex(name) >= 0; } + void SetVariable(const RPNvariable &v); + +private: + void push(const RPNvalue &v) { + if (m_top < MAX_STACK) stack[m_top++] = v; else m_error = RPN_OVERFLOW; + } + const RPNvalue &pop() { + if (m_top > 0) return stack[--m_top]; + if (m_error == RPN_OK) m_error = RPN_UNDERFLOW; + stack[0] = RPNvalue(0.0); + return stack[0]; + } + int varindex(const char *name); + + char *m_expr; + RPNvalue stack[MAX_STACK]; + int m_top; /* stack top */ + RPN_ERROR m_error; /* returns parsing error, if any */ + + std::vector variables; +}; + +#endif /* __rpn_h */ + diff --git a/src/svnrev.h b/src/svnrev.h index 9d197e5..5b2e4b9 100644 --- a/src/svnrev.h +++ b/src/svnrev.h @@ -2,17 +2,17 @@ * (http://www.compuphase.com/svnrev.htm). * You should not modify it manually, as it may be re-generated. * - * $Revision: 5784$ - * $Date: 2017-12-26$ + * $Revision: 5907$ + * $Date: 2018-12-14$ */ #ifndef SVN_REV_H #define SVN_REV_H -#define SVN_REV 5784 -#define SVN_REVSTR "5784" -#define SVN_REVDATE "2017-12-26" -#define SVN_REVSTAMP 20171226L +#define SVN_REV 5907 +#define SVN_REVSTR "1.4.5907" +#define SVN_REVDATE "2018-12-14" +#define SVN_REVSTAMP 20181214L #define SVN_REVMODIFIED 0 #endif /* SVN_REV_H */ diff --git a/src/unqlite.c b/src/unqlite.c index 3b49256..692509a 100644 --- a/src/unqlite.c +++ b/src/unqlite.c @@ -2,7 +2,7 @@ * Symisc UnQLite-KV: A Transactional Key/Value Store Database Engine. * Copyright (C) 2016, Symisc Systems http://unqlite.org/ * Copyright (C) 2014, Yuras Shumovich - * Version 1.1 + * Version 1.1.2 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net @@ -12,7 +12,7 @@ * http://unqlite.org/licensing.html */ /* - * Copyright (C) 2012, 2016 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine ]. + * Copyright (C) 2012, 2018 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine ]. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,13 +31,13 @@ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* - * $SymiscID: unqlite.c v1.1.7 Win10 2106-12-02 00:04:12 stable $ + * $SymiscID: unqlite.c v1.1.8 Win10 2108-01-21 00:02:12 stable $ */ /* This file is an amalgamation of many separate C source files from unqlite version 1.1.6 * By combining all the individual C code files into this single large file, the entire code @@ -46,7 +46,7 @@ * are commonly seen when unqlite is compiled as a single translation unit. * * This file is all you need to compile unqlite. To use unqlite in other programs, you need - * this file and the "unqlite.h" header file that defines the programming interface to the + * this file and the "unqlite.h" header file that defines the programming interface to the * unqlite engine.(If you do not have the "unqlite.h" header file at hand, you will find * a copy embedded within the text of this file.Search for "Header file: " to find * the start of the embedded unqlite.h header file.) Additional code files may be needed if @@ -59,795 +59,8 @@ #ifndef UNQLITE_AMALGAMATION #define UNQLITE_AMALGAMATION #endif /* UNQLITE_AMALGAMATION */ -/* - * Embedded header file for unqlite: - */ -/* - * ---------------------------------------------------------- - * File: unqlite.h - * ---------------------------------------------------------- - */ - /* This file was automatically generated. Do not edit (Except for compile time directives)! */ -/* This file was automatically generated. Do not edit (Except for compile time directives)! */ -#ifndef _UNQLITE_H_ -#define _UNQLITE_H_ -/* - * Symisc UnQLite-KV: A Transactional Key/Value Database Engine. - * Copyright (C) 2016, Symisc Systems http://unqlite.org/ - * Copyright (C) 2014, Yuras Shumovich - * Version 1.1 - * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES - * please contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - * or visit: - * http://unqlite.org/licensing.html - */ -/* - * Copyright (C) 2016 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine ]. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SYMISC SYSTEMS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - /* $SymiscID: unqlite.h v1.1 Win10 2016-12-02 00:04:12 stable $ */ -#include /* needed for the definition of va_list */ -/* - * Compile time engine version, signature, identification in the symisc source tree - * and copyright notice. - * Each macro have an equivalent C interface associated with it that provide the same - * information but are associated with the library instead of the header file. - * Refer to [unqlite_lib_version()], [unqlite_lib_signature()], [unqlite_lib_ident()] and - * [unqlite_lib_copyright()] for more information. - */ -/* - * The UNQLITE_VERSION C preprocessor macroevaluates to a string literal - * that is the unqlite version in the format "X.Y.Z" where X is the major - * version number and Y is the minor version number and Z is the release - * number. - */ -#define UNQLITE_VERSION "1.1.1" -/* - * The UNQLITE_VERSION_NUMBER C preprocessor macro resolves to an integer - * with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same - * numbers used in [UNQLITE_VERSION]. - */ -#define UNQLITE_VERSION_NUMBER 1001001 -/* - * The UNQLITE_SIG C preprocessor macro evaluates to a string - * literal which is the public signature of the unqlite engine. - * This signature could be included for example in a host-application - * generated Server MIME header as follows: - * Server: YourWebServer/x.x unqlite/x.x.x \r\n - */ -#define UNQLITE_SIG "unqlite/1.1.1" -/* - * UnQLite identification in the Symisc source tree: - * Each particular check-in of a particular software released - * by symisc systems have an unique identifier associated with it. - * This macro hold the one associated with unqlite. - */ -#define UNQLITE_IDENT "unqlite:c12ef78a5263baff9673264cdae36" -/* - * Copyright notice. - * If you have any questions about the licensing situation, please - * visit http://unqlite.org/licensing.html - * or contact Symisc Systems via: - * legal@symisc.net - * licensing@symisc.net - * contact@symisc.net - */ -#define UNQLITE_COPYRIGHT "Copyright (C) Symisc Systems, S.U.A.R.L [Mrad Chems Eddine ] 2016, http://unqlite.org/" - -/* Forward declaration to public objects */ -typedef struct unqlite_io_methods unqlite_io_methods; -typedef struct unqlite_kv_methods unqlite_kv_methods; -typedef struct unqlite_kv_engine unqlite_kv_engine; -typedef struct unqlite_vfs unqlite_vfs; -typedef struct unqlite_vm unqlite_vm; -typedef struct unqlite unqlite; -/* - * ------------------------------ - * Compile time directives - * ------------------------------ - * For most purposes, UnQLite can be built just fine using the default compilation options. - * However, if required, the compile-time options documented below can be used to omit UnQLite - * features (resulting in a smaller compiled library size) or to change the default values - * of some parameters. - * Every effort has been made to ensure that the various combinations of compilation options - * work harmoniously and produce a working library. - * - * UNQLITE_ENABLE_THREADS - * This option controls whether or not code is included in UnQLite to enable it to operate - * safely in a multithreaded environment. The default is not. All mutexing code is omitted - * and it is unsafe to use UnQLite in a multithreaded program. When compiled with the - * UNQLITE_ENABLE_THREADS directive enabled, UnQLite can be used in a multithreaded program - * and it is safe to share the same virtual machine and engine handle between two or more threads. - * The value of UNQLITE_ENABLE_THREADS can be determined at run-time using the unqlite_lib_is_threadsafe() - * interface. - * When UnQLite has been compiled with threading support then the threading mode can be altered - * at run-time using the unqlite_lib_config() interface together with one of these verbs: - * UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE - * UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI - * Platforms others than Windows and UNIX systems must install their own mutex subsystem via - * unqlite_lib_config() with a configuration verb set to UNQLITE_LIB_CONFIG_USER_MUTEX. - * Otherwise the library is not threadsafe. - * Note that you must link UnQLite with the POSIX threads library under UNIX systems (i.e: -lpthread). - */ -/* Symisc public definitions */ -#if !defined(SYMISC_STANDARD_DEFS) -#define SYMISC_STANDARD_DEFS -#if defined (_WIN32) || defined (WIN32) || defined(__MINGW32__) || defined (_MSC_VER) || defined (_WIN32_WCE) -/* Windows Systems */ -#if !defined(__WINNT__) -#define __WINNT__ -#endif -/* - * Determine if we are dealing with WindowsCE - which has a much - * reduced API. - */ -#if defined(_WIN32_WCE) -#ifndef __WIN_CE__ -#define __WIN_CE__ -#endif /* __WIN_CE__ */ -#endif /* _WIN32_WCE */ -#else -/* - * By default we will assume that we are compiling on a UNIX systems. - * Otherwise the OS_OTHER directive must be defined. - */ -#if !defined(OS_OTHER) -#if !defined(__UNIXES__) -#define __UNIXES__ -#endif /* __UNIXES__ */ -#else -#endif /* OS_OTHER */ -#endif /* __WINNT__/__UNIXES__ */ -#if defined(_MSC_VER) || defined(__BORLANDC__) -typedef signed __int64 sxi64; /* 64 bits(8 bytes) signed int64 */ -typedef unsigned __int64 sxu64; /* 64 bits(8 bytes) unsigned int64 */ -#else -typedef signed long long int sxi64; /* 64 bits(8 bytes) signed int64 */ -typedef unsigned long long int sxu64; /* 64 bits(8 bytes) unsigned int64 */ -#endif /* _MSC_VER */ -/* Signature of the consumer routine */ -typedef int (*ProcConsumer)(const void *, unsigned int, void *); -/* Forward reference */ -typedef struct SyMutexMethods SyMutexMethods; -typedef struct SyMemMethods SyMemMethods; -typedef struct SyString SyString; -typedef struct syiovec syiovec; -typedef struct SyMutex SyMutex; -typedef struct Sytm Sytm; -/* Scatter and gather array. */ -struct syiovec -{ -#if defined (__WINNT__) - /* Same fields type and offset as WSABUF structure defined one winsock2 header */ - unsigned long nLen; - char *pBase; -#else - void *pBase; - unsigned long nLen; -#endif -}; -struct SyString -{ - const char *zString; /* Raw string (may not be null terminated) */ - unsigned int nByte; /* Raw string length */ -}; -/* Time structure. */ -struct Sytm -{ - int tm_sec; /* seconds (0 - 60) */ - int tm_min; /* minutes (0 - 59) */ - int tm_hour; /* hours (0 - 23) */ - int tm_mday; /* day of month (1 - 31) */ - int tm_mon; /* month of year (0 - 11) */ - int tm_year; /* year + 1900 */ - int tm_wday; /* day of week (Sunday = 0) */ - int tm_yday; /* day of year (0 - 365) */ - int tm_isdst; /* is summer time in effect? */ - char *tm_zone; /* abbreviation of timezone name */ - long tm_gmtoff; /* offset from UTC in seconds */ -}; -/* Convert a tm structure (struct tm *) found in to a Sytm structure */ -#define STRUCT_TM_TO_SYTM(pTM, pSYTM) \ - (pSYTM)->tm_hour = (pTM)->tm_hour;\ - (pSYTM)->tm_min = (pTM)->tm_min;\ - (pSYTM)->tm_sec = (pTM)->tm_sec;\ - (pSYTM)->tm_mon = (pTM)->tm_mon;\ - (pSYTM)->tm_mday = (pTM)->tm_mday;\ - (pSYTM)->tm_year = (pTM)->tm_year + 1900;\ - (pSYTM)->tm_yday = (pTM)->tm_yday;\ - (pSYTM)->tm_wday = (pTM)->tm_wday;\ - (pSYTM)->tm_isdst = (pTM)->tm_isdst;\ - (pSYTM)->tm_gmtoff = 0;\ - (pSYTM)->tm_zone = 0; - -/* Convert a SYSTEMTIME structure (LPSYSTEMTIME: Windows Systems only ) to a Sytm structure */ -#define SYSTEMTIME_TO_SYTM(pSYSTIME, pSYTM) \ - (pSYTM)->tm_hour = (pSYSTIME)->wHour;\ - (pSYTM)->tm_min = (pSYSTIME)->wMinute;\ - (pSYTM)->tm_sec = (pSYSTIME)->wSecond;\ - (pSYTM)->tm_mon = (pSYSTIME)->wMonth - 1;\ - (pSYTM)->tm_mday = (pSYSTIME)->wDay;\ - (pSYTM)->tm_year = (pSYSTIME)->wYear;\ - (pSYTM)->tm_yday = 0;\ - (pSYTM)->tm_wday = (pSYSTIME)->wDayOfWeek;\ - (pSYTM)->tm_gmtoff = 0;\ - (pSYTM)->tm_isdst = -1;\ - (pSYTM)->tm_zone = 0; - -/* Dynamic memory allocation methods. */ -struct SyMemMethods -{ - void * (*xAlloc)(unsigned int); /* [Required:] Allocate a memory chunk */ - void * (*xRealloc)(void *, unsigned int); /* [Required:] Re-allocate a memory chunk */ - void (*xFree)(void *); /* [Required:] Release a memory chunk */ - unsigned int (*xChunkSize)(void *); /* [Optional:] Return chunk size */ - int (*xInit)(void *); /* [Optional:] Initialization callback */ - void (*xRelease)(void *); /* [Optional:] Release callback */ - void *pUserData; /* [Optional:] First argument to xInit() and xRelease() */ -}; -/* Out of memory callback signature. */ -typedef int (*ProcMemError)(void *); -/* Mutex methods. */ -struct SyMutexMethods -{ - int (*xGlobalInit)(void); /* [Optional:] Global mutex initialization */ - void (*xGlobalRelease)(void); /* [Optional:] Global Release callback () */ - SyMutex * (*xNew)(int); /* [Required:] Request a new mutex */ - void (*xRelease)(SyMutex *); /* [Optional:] Release a mutex */ - void (*xEnter)(SyMutex *); /* [Required:] Enter mutex */ - int (*xTryEnter)(SyMutex *); /* [Optional:] Try to enter a mutex */ - void (*xLeave)(SyMutex *); /* [Required:] Leave a locked mutex */ -}; -#if defined (_MSC_VER) || defined (__MINGW32__) || defined (__GNUC__) && defined (__declspec) -#define SX_APIIMPORT __declspec(dllimport) -#define SX_APIEXPORT __declspec(dllexport) -#else -#define SX_APIIMPORT -#define SX_APIEXPORT -#endif -/* Standard return values from Symisc public interfaces */ -#define SXRET_OK 0 /* Not an error */ -#define SXERR_MEM (-1) /* Out of memory */ -#define SXERR_IO (-2) /* IO error */ -#define SXERR_EMPTY (-3) /* Empty field */ -#define SXERR_LOCKED (-4) /* Locked operation */ -#define SXERR_ORANGE (-5) /* Out of range value */ -#define SXERR_NOTFOUND (-6) /* Item not found */ -#define SXERR_LIMIT (-7) /* Limit reached */ -#define SXERR_MORE (-8) /* Need more input */ -#define SXERR_INVALID (-9) /* Invalid parameter */ -#define SXERR_ABORT (-10) /* User callback request an operation abort */ -#define SXERR_EXISTS (-11) /* Item exists */ -#define SXERR_SYNTAX (-12) /* Syntax error */ -#define SXERR_UNKNOWN (-13) /* Unknown error */ -#define SXERR_BUSY (-14) /* Busy operation */ -#define SXERR_OVERFLOW (-15) /* Stack or buffer overflow */ -#define SXERR_WILLBLOCK (-16) /* Operation will block */ -#define SXERR_NOTIMPLEMENTED (-17) /* Operation not implemented */ -#define SXERR_EOF (-18) /* End of input */ -#define SXERR_PERM (-19) /* Permission error */ -#define SXERR_NOOP (-20) /* No-op */ -#define SXERR_FORMAT (-21) /* Invalid format */ -#define SXERR_NEXT (-22) /* Not an error */ -#define SXERR_OS (-23) /* System call return an error */ -#define SXERR_CORRUPT (-24) /* Corrupted pointer */ -#define SXERR_CONTINUE (-25) /* Not an error: Operation in progress */ -#define SXERR_NOMATCH (-26) /* No match */ -#define SXERR_RESET (-27) /* Operation reset */ -#define SXERR_DONE (-28) /* Not an error */ -#define SXERR_SHORT (-29) /* Buffer too short */ -#define SXERR_PATH (-30) /* Path error */ -#define SXERR_TIMEOUT (-31) /* Timeout */ -#define SXERR_BIG (-32) /* Too big for processing */ -#define SXERR_RETRY (-33) /* Retry your call */ -#define SXERR_IGNORE (-63) /* Ignore */ -#endif /* SYMISC_PUBLIC_DEFS */ -/* - * Marker for exported interfaces. - */ -#define UNQLITE_APIEXPORT SX_APIEXPORT -/* - * If compiling for a processor that lacks floating point - * support, substitute integer for floating-point. - */ -#ifdef UNQLITE_OMIT_FLOATING_POINT -typedef sxi64 uqlite_real; -#else -typedef double unqlite_real; -#endif -typedef sxi64 unqlite_int64; -/* Standard UnQLite return values */ -#define UNQLITE_OK SXRET_OK /* Successful result */ -/* Beginning of error codes */ -#define UNQLITE_NOMEM SXERR_MEM /* Out of memory */ -#define UNQLITE_ABORT SXERR_ABORT /* Another thread have released this instance */ -#define UNQLITE_IOERR SXERR_IO /* IO error */ -#define UNQLITE_CORRUPT SXERR_CORRUPT /* Corrupt pointer */ -#define UNQLITE_LOCKED SXERR_LOCKED /* Forbidden Operation */ -#define UNQLITE_BUSY SXERR_BUSY /* The database file is locked */ -#define UNQLITE_DONE SXERR_DONE /* Operation done */ -#define UNQLITE_PERM SXERR_PERM /* Permission error */ -#define UNQLITE_NOTIMPLEMENTED SXERR_NOTIMPLEMENTED /* Method not implemented by the underlying Key/Value storage engine */ -#define UNQLITE_NOTFOUND SXERR_NOTFOUND /* No such record */ -#define UNQLITE_NOOP SXERR_NOOP /* No such method */ -#define UNQLITE_INVALID SXERR_INVALID /* Invalid parameter */ -#define UNQLITE_EOF SXERR_EOF /* End Of Input */ -#define UNQLITE_UNKNOWN SXERR_UNKNOWN /* Unknown configuration option */ -#define UNQLITE_LIMIT SXERR_LIMIT /* Database limit reached */ -#define UNQLITE_EXISTS SXERR_EXISTS /* Record exists */ -#define UNQLITE_EMPTY SXERR_EMPTY /* Empty record */ -#define UNQLITE_COMPILE_ERR (-70) /* Compilation error */ -#define UNQLITE_VM_ERR (-71) /* Virtual machine error */ -#define UNQLITE_FULL (-73) /* Full database (unlikely) */ -#define UNQLITE_CANTOPEN (-74) /* Unable to open the database file */ -#define UNQLITE_READ_ONLY (-75) /* Read only Key/Value storage engine */ -#define UNQLITE_LOCKERR (-76) /* Locking protocol error */ -/* end-of-error-codes */ -/* - * Database Handle Configuration Commands. - * - * The following set of constants are the available configuration verbs that can - * be used by the host-application to configure an UnQLite database handle. - * These constants must be passed as the second argument to [unqlite_config()]. - * - * Each options require a variable number of arguments. - * The [unqlite_config()] interface will return UNQLITE_OK on success, any other - * return value indicates failure. - * For a full discussion on the configuration verbs and their expected - * parameters, please refer to this page: - * http://unqlite.org/c_api/unqlite_config.html - */ -#define UNQLITE_CONFIG_MAX_PAGE_CACHE 2 /* ONE ARGUMENT: int nMaxPage */ -#define UNQLITE_CONFIG_ERR_LOG 3 /* TWO ARGUMENTS: const char **pzBuf, int *pLen */ -#define UNQLITE_CONFIG_KV_ENGINE 4 /* ONE ARGUMENT: const char *zKvName */ -#define UNQLITE_CONFIG_DISABLE_AUTO_COMMIT 5 /* NO ARGUMENTS */ -#define UNQLITE_CONFIG_GET_KV_NAME 6 /* ONE ARGUMENT: const char **pzPtr */ -/* - * Storage engine configuration commands. - * - * The following set of constants are the available configuration verbs that can - * be used by the host-application to configure the underlying storage engine (i.e Hash, B+tree, R+tree). - * These constants must be passed as the first argument to [unqlite_kv_config()]. - * Each options require a variable number of arguments. - * The [unqlite_kv_config()] interface will return UNQLITE_OK on success, any other return - * value indicates failure. - * For a full discussion on the configuration verbs and their expected parameters, please - * refer to this page: - * http://unqlite.org/c_api/unqlite_kv_config.html - */ -#define UNQLITE_KV_CONFIG_HASH_FUNC 1 /* ONE ARGUMENT: unsigned int (*xHash)(const void *,unsigned int) */ -#define UNQLITE_KV_CONFIG_CMP_FUNC 2 /* ONE ARGUMENT: int (*xCmp)(const void *,const void *,unsigned int) */ -/* - * Global Library Configuration Commands. - * - * The following set of constants are the available configuration verbs that can - * be used by the host-application to configure the whole library. - * These constants must be passed as the first argument to [unqlite_lib_config()]. - * - * Each options require a variable number of arguments. - * The [unqlite_lib_config()] interface will return UNQLITE_OK on success, any other return - * value indicates failure. - * Notes: - * The default configuration is recommended for most applications and so the call to - * [unqlite_lib_config()] is usually not necessary. It is provided to support rare - * applications with unusual needs. - * The [unqlite_lib_config()] interface is not threadsafe. The application must insure that - * no other [unqlite_*()] interfaces are invoked by other threads while [unqlite_lib_config()] - * is running. Furthermore, [unqlite_lib_config()] may only be invoked prior to library - * initialization using [unqlite_lib_init()] or [unqlite_init()] or after shutdown - * by [unqlite_lib_shutdown()]. If [unqlite_lib_config()] is called after [unqlite_lib_init()] - * or [unqlite_init()] and before [unqlite_lib_shutdown()] then it will return UNQLITE_LOCKED. - * For a full discussion on the configuration verbs and their expected parameters, please - * refer to this page: - * http://unqlite.org/c_api/unqlite_lib.html - */ -#define UNQLITE_LIB_CONFIG_USER_MALLOC 1 /* ONE ARGUMENT: const SyMemMethods *pMemMethods */ -#define UNQLITE_LIB_CONFIG_MEM_ERR_CALLBACK 2 /* TWO ARGUMENTS: int (*xMemError)(void *), void *pUserData */ -#define UNQLITE_LIB_CONFIG_USER_MUTEX 3 /* ONE ARGUMENT: const SyMutexMethods *pMutexMethods */ -#define UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE 4 /* NO ARGUMENTS */ -#define UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI 5 /* NO ARGUMENTS */ -#define UNQLITE_LIB_CONFIG_VFS 6 /* ONE ARGUMENT: const unqlite_vfs *pVfs */ -#define UNQLITE_LIB_CONFIG_STORAGE_ENGINE 7 /* ONE ARGUMENT: unqlite_kv_methods *pStorage */ -#define UNQLITE_LIB_CONFIG_PAGE_SIZE 8 /* ONE ARGUMENT: int iPageSize */ -/* - * These bit values are intended for use in the 3rd parameter to the [unqlite_open()] interface - * and in the 4th parameter to the xOpen method of the [unqlite_vfs] object. - */ -#define UNQLITE_OPEN_READONLY 0x00000001 /* Read only mode. Ok for [unqlite_open] */ -#define UNQLITE_OPEN_READWRITE 0x00000002 /* Ok for [unqlite_open] */ -#define UNQLITE_OPEN_CREATE 0x00000004 /* Ok for [unqlite_open] */ -#define UNQLITE_OPEN_EXCLUSIVE 0x00000008 /* VFS only */ -#define UNQLITE_OPEN_TEMP_DB 0x00000010 /* VFS only */ -#define UNQLITE_OPEN_NOMUTEX 0x00000020 /* Ok for [unqlite_open] */ -#define UNQLITE_OPEN_OMIT_JOURNALING 0x00000040 /* Omit journaling for this database. Ok for [unqlite_open] */ -#define UNQLITE_OPEN_IN_MEMORY 0x00000080 /* An in memory database. Ok for [unqlite_open]*/ -#define UNQLITE_OPEN_MMAP 0x00000100 /* Obtain a memory view of the whole file. Ok for [unqlite_open] */ -/* - * Synchronization Type Flags - * - * When UnQLite invokes the xSync() method of an [unqlite_io_methods] object it uses - * a combination of these integer values as the second argument. - * - * When the UNQLITE_SYNC_DATAONLY flag is used, it means that the sync operation only - * needs to flush data to mass storage. Inode information need not be flushed. - * If the lower four bits of the flag equal UNQLITE_SYNC_NORMAL, that means to use normal - * fsync() semantics. If the lower four bits equal UNQLITE_SYNC_FULL, that means to use - * Mac OS X style fullsync instead of fsync(). - */ -#define UNQLITE_SYNC_NORMAL 0x00002 -#define UNQLITE_SYNC_FULL 0x00003 -#define UNQLITE_SYNC_DATAONLY 0x00010 -/* - * File Locking Levels - * - * UnQLite uses one of these integer values as the second - * argument to calls it makes to the xLock() and xUnlock() methods - * of an [unqlite_io_methods] object. - */ -#define UNQLITE_LOCK_NONE 0 -#define UNQLITE_LOCK_SHARED 1 -#define UNQLITE_LOCK_RESERVED 2 -#define UNQLITE_LOCK_PENDING 3 -#define UNQLITE_LOCK_EXCLUSIVE 4 -/* - * CAPIREF: OS Interface: Open File Handle - * - * An [unqlite_file] object represents an open file in the [unqlite_vfs] OS interface - * layer. - * Individual OS interface implementations will want to subclass this object by appending - * additional fields for their own use. The pMethods entry is a pointer to an - * [unqlite_io_methods] object that defines methods for performing - * I/O operations on the open file. -*/ -typedef struct unqlite_file unqlite_file; -struct unqlite_file { - const unqlite_io_methods *pMethods; /* Methods for an open file. MUST BE FIRST */ -}; -/* - * CAPIREF: OS Interface: File Methods Object - * - * Every file opened by the [unqlite_vfs] xOpen method populates an - * [unqlite_file] object (or, more commonly, a subclass of the - * [unqlite_file] object) with a pointer to an instance of this object. - * This object defines the methods used to perform various operations - * against the open file represented by the [unqlite_file] object. - * - * If the xOpen method sets the unqlite_file.pMethods element - * to a non-NULL pointer, then the unqlite_io_methods.xClose method - * may be invoked even if the xOpen reported that it failed. The - * only way to prevent a call to xClose following a failed xOpen - * is for the xOpen to set the unqlite_file.pMethods element to NULL. - * - * The flags argument to xSync may be one of [UNQLITE_SYNC_NORMAL] or - * [UNQLITE_SYNC_FULL]. The first choice is the normal fsync(). - * The second choice is a Mac OS X style fullsync. The [UNQLITE_SYNC_DATAONLY] - * flag may be ORed in to indicate that only the data of the file - * and not its inode needs to be synced. - * - * The integer values to xLock() and xUnlock() are one of - * - * UNQLITE_LOCK_NONE - * UNQLITE_LOCK_SHARED - * UNQLITE_LOCK_RESERVED - * UNQLITE_LOCK_PENDING - * UNQLITE_LOCK_EXCLUSIVE - * - * xLock() increases the lock. xUnlock() decreases the lock. - * The xCheckReservedLock() method checks whether any database connection, - * either in this process or in some other process, is holding a RESERVED, - * PENDING, or EXCLUSIVE lock on the file. It returns true if such a lock exists - * and false otherwise. - * - * The xSectorSize() method returns the sector size of the device that underlies - * the file. The sector size is the minimum write that can be performed without - * disturbing other bytes in the file. - * - */ -struct unqlite_io_methods { - int iVersion; /* Structure version number (currently 1) */ - int (*xClose)(unqlite_file*); - int (*xRead)(unqlite_file*, void*, unqlite_int64 iAmt, unqlite_int64 iOfst); - int (*xWrite)(unqlite_file*, const void*, unqlite_int64 iAmt, unqlite_int64 iOfst); - int (*xTruncate)(unqlite_file*, unqlite_int64 size); - int (*xSync)(unqlite_file*, int flags); - int (*xFileSize)(unqlite_file*, unqlite_int64 *pSize); - int (*xLock)(unqlite_file*, int); - int (*xUnlock)(unqlite_file*, int); - int (*xCheckReservedLock)(unqlite_file*, int *pResOut); - int (*xSectorSize)(unqlite_file*); -}; -/* - * CAPIREF: OS Interface Object - * - * An instance of the unqlite_vfs object defines the interface between - * the UnQLite core and the underlying operating system. The "vfs" - * in the name of the object stands for "Virtual File System". - * - * Only a single vfs can be registered within the UnQLite core. - * Vfs registration is done using the [unqlite_lib_config()] interface - * with a configuration verb set to UNQLITE_LIB_CONFIG_VFS. - * Note that Windows and UNIX (Linux, FreeBSD, Solaris, Mac OS X, etc.) users - * does not have to worry about registering and installing a vfs since UnQLite - * come with a built-in vfs for these platforms that implements most the methods - * defined below. - * - * Clients running on exotic systems (ie: Other than Windows and UNIX systems) - * must register their own vfs in order to be able to use the UnQLite library. - * - * The value of the iVersion field is initially 1 but may be larger in - * future versions of UnQLite. - * - * The szOsFile field is the size of the subclassed [unqlite_file] structure - * used by this VFS. mxPathname is the maximum length of a pathname in this VFS. - * - * At least szOsFile bytes of memory are allocated by UnQLite to hold the [unqlite_file] - * structure passed as the third argument to xOpen. The xOpen method does not have to - * allocate the structure; it should just fill it in. Note that the xOpen method must - * set the unqlite_file.pMethods to either a valid [unqlite_io_methods] object or to NULL. - * xOpen must do this even if the open fails. UnQLite expects that the unqlite_file.pMethods - * element will be valid after xOpen returns regardless of the success or failure of the - * xOpen call. - * - */ -struct unqlite_vfs { - const char *zName; /* Name of this virtual file system [i.e: Windows, UNIX, etc.] */ - int iVersion; /* Structure version number (currently 1) */ - int szOsFile; /* Size of subclassed unqlite_file */ - int mxPathname; /* Maximum file pathname length */ - int (*xOpen)(unqlite_vfs*, const char *zName, unqlite_file*,unsigned int flags); - int (*xDelete)(unqlite_vfs*, const char *zName, int syncDir); - int (*xAccess)(unqlite_vfs*, const char *zName, int flags, int *pResOut); - int (*xFullPathname)(unqlite_vfs*, const char *zName,int buf_len,char *zBuf); - int (*xTmpDir)(unqlite_vfs*,char *zBuf,int buf_len); - int (*xMmap)(const char *, void **, unqlite_int64 *); /* Read-only memory map of the whole file */ - void (*xUnmap)(void *, unqlite_int64); /* Unmap a memory view */ - int (*xSleep)(unqlite_vfs*, int microseconds); - int (*xCurrentTime)(unqlite_vfs*,Sytm *pOut); - int (*xGetLastError)(unqlite_vfs*, int, char *); -}; -/* - * Flags for the xAccess VFS method - * - * These integer constants can be used as the third parameter to - * the xAccess method of an [unqlite_vfs] object. They determine - * what kind of permissions the xAccess method is looking for. - * With UNQLITE_ACCESS_EXISTS, the xAccess method - * simply checks whether the file exists. - * With UNQLITE_ACCESS_READWRITE, the xAccess method - * checks whether the named directory is both readable and writable - * (in other words, if files can be added, removed, and renamed within - * the directory). - * The UNQLITE_ACCESS_READWRITE constant is currently used only by the - * [temp_store_directory pragma], though this could change in a future - * release of UnQLite. - * With UNQLITE_ACCESS_READ, the xAccess method - * checks whether the file is readable. The UNQLITE_ACCESS_READ constant is - * currently unused, though it might be used in a future release of - * UnQLite. - */ -#define UNQLITE_ACCESS_EXISTS 0 -#define UNQLITE_ACCESS_READWRITE 1 -#define UNQLITE_ACCESS_READ 2 -/* - * The type used to represent a page number. The first page in a file - * is called page 1. 0 is used to represent "not a page". - * A page number is an unsigned 64-bit integer. - */ -typedef sxu64 pgno; -/* - * A database disk page is represented by an instance - * of the follwoing structure. - */ -typedef struct unqlite_page unqlite_page; -struct unqlite_page -{ - unsigned char *zData; /* Content of this page */ - void *pUserData; /* Extra content */ - pgno pgno; /* Page number for this page */ -}; -/* - * UnQLite handle to the underlying Key/Value Storage Engine (See below). - */ -typedef void * unqlite_kv_handle; -/* - * UnQLite pager IO methods. - * - * An instance of the following structure define the exported methods of the UnQLite pager - * to the underlying Key/Value storage engine. - */ -typedef struct unqlite_kv_io unqlite_kv_io; -struct unqlite_kv_io -{ - unqlite_kv_handle pHandle; /* UnQLite handle passed as the first parameter to the - * method defined below. - */ - unqlite_kv_methods *pMethods; /* Underlying storage engine */ - /* Pager methods */ - int (*xGet)(unqlite_kv_handle,pgno,unqlite_page **); - int (*xLookup)(unqlite_kv_handle,pgno,unqlite_page **); - int (*xNew)(unqlite_kv_handle,unqlite_page **); - int (*xWrite)(unqlite_page *); - int (*xDontWrite)(unqlite_page *); - int (*xDontJournal)(unqlite_page *); - int (*xDontMkHot)(unqlite_page *); - int (*xPageRef)(unqlite_page *); - int (*xPageUnref)(unqlite_page *); - int (*xPageSize)(unqlite_kv_handle); - int (*xReadOnly)(unqlite_kv_handle); - unsigned char * (*xTmpPage)(unqlite_kv_handle); - void (*xSetUnpin)(unqlite_kv_handle,void (*xPageUnpin)(void *)); - void (*xSetReload)(unqlite_kv_handle,void (*xPageReload)(void *)); - void (*xErr)(unqlite_kv_handle,const char *); -}; -/* - * Key/Value Storage Engine Cursor Object - * - * An instance of a subclass of the following object defines a cursor - * used to scan through a key-value storage engine. - */ -typedef struct unqlite_kv_cursor unqlite_kv_cursor; -struct unqlite_kv_cursor -{ - unqlite_kv_engine *pStore; /* Must be first */ - /* Subclasses will typically add additional fields */ -}; -/* - * Possible seek positions. - */ -#define UNQLITE_CURSOR_MATCH_EXACT 1 -#define UNQLITE_CURSOR_MATCH_LE 2 -#define UNQLITE_CURSOR_MATCH_GE 3 -/* - * Key/Value Storage Engine. - * - * A Key-Value storage engine is defined by an instance of the following - * object. - * UnQLite works with run-time interchangeable storage engines (i.e. Hash, B+Tree, R+Tree, LSM, etc.). - * The storage engine works with key/value pairs where both the key - * and the value are byte arrays of arbitrary length and with no restrictions on content. - * UnQLite come with two built-in KV storage engine: A Virtual Linear Hash (VLH) storage - * engine is used for persistent on-disk databases with O(1) lookup time and an in-memory - * hash-table or Red-black tree storage engine is used for in-memory databases. - * Future versions of UnQLite might add other built-in storage engines (i.e. LSM). - * Registration of a Key/Value storage engine at run-time is done via [unqlite_lib_config()] - * with a configuration verb set to UNQLITE_LIB_CONFIG_STORAGE_ENGINE. - */ -struct unqlite_kv_engine -{ - const unqlite_kv_io *pIo; /* IO methods: MUST be first */ - /* Subclasses will typically add additional fields */ -}; -/* - * Key/Value Storage Engine Virtual Method Table. - * - * Key/Value storage engine methods is defined by an instance of the following - * object. - * Registration of a Key/Value storage engine at run-time is done via [unqlite_lib_config()] - * with a configuration verb set to UNQLITE_LIB_CONFIG_STORAGE_ENGINE. - */ -struct unqlite_kv_methods -{ - const char *zName; /* Storage engine name [i.e. Hash, B+tree, LSM, R-tree, Mem, etc.]*/ - int szKv; /* 'unqlite_kv_engine' subclass size */ - int szCursor; /* 'unqlite_kv_cursor' subclass size */ - int iVersion; /* Structure version, currently 1 */ - /* Storage engine methods */ - int (*xInit)(unqlite_kv_engine *,int iPageSize); - void (*xRelease)(unqlite_kv_engine *); - int (*xConfig)(unqlite_kv_engine *,int op,va_list ap); - int (*xOpen)(unqlite_kv_engine *,pgno); - int (*xReplace)( - unqlite_kv_engine *, - const void *pKey,int nKeyLen, - const void *pData,unqlite_int64 nDataLen - ); - int (*xAppend)( - unqlite_kv_engine *, - const void *pKey,int nKeyLen, - const void *pData,unqlite_int64 nDataLen - ); - void (*xCursorInit)(unqlite_kv_cursor *); - int (*xSeek)(unqlite_kv_cursor *,const void *pKey,int nByte,int iPos); /* Mandatory */ - int (*xFirst)(unqlite_kv_cursor *); - int (*xLast)(unqlite_kv_cursor *); - int (*xValid)(unqlite_kv_cursor *); - int (*xNext)(unqlite_kv_cursor *); - int (*xPrev)(unqlite_kv_cursor *); - int (*xDelete)(unqlite_kv_cursor *); - int (*xKeyLength)(unqlite_kv_cursor *,int *); - int (*xKey)(unqlite_kv_cursor *,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData); - int (*xDataLength)(unqlite_kv_cursor *,unqlite_int64 *); - int (*xData)(unqlite_kv_cursor *,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData); - void (*xReset)(unqlite_kv_cursor *); - void (*xCursorRelease)(unqlite_kv_cursor *); -}; -/* - * UnQLite journal file suffix. - */ -#ifndef UNQLITE_JOURNAL_FILE_SUFFIX -#define UNQLITE_JOURNAL_FILE_SUFFIX "_unqlite_journal" -#endif -/* - * C-API-REF: Please refer to the official documentation for interfaces - * purpose and expected parameters. - */ - -/* Database Engine Handle */ -UNQLITE_APIEXPORT int unqlite_open(unqlite **ppDB,const char *zFilename,unsigned int iMode); -UNQLITE_APIEXPORT int unqlite_config(unqlite *pDb,int nOp,...); -UNQLITE_APIEXPORT int unqlite_close(unqlite *pDb); - -/* Key/Value (KV) Store Interfaces */ -UNQLITE_APIEXPORT int unqlite_kv_store(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen); -UNQLITE_APIEXPORT int unqlite_kv_append(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen); -UNQLITE_APIEXPORT int unqlite_kv_store_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...); -UNQLITE_APIEXPORT int unqlite_kv_append_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...); -UNQLITE_APIEXPORT int unqlite_kv_fetch(unqlite *pDb,const void *pKey,int nKeyLen,void *pBuf,unqlite_int64 /* in|out */*pBufLen); -UNQLITE_APIEXPORT int unqlite_kv_fetch_callback(unqlite *pDb,const void *pKey, - int nKeyLen,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData); -UNQLITE_APIEXPORT int unqlite_kv_delete(unqlite *pDb,const void *pKey,int nKeyLen); -UNQLITE_APIEXPORT int unqlite_kv_config(unqlite *pDb,int iOp,...); - -/* Cursor Iterator Interfaces */ -UNQLITE_APIEXPORT int unqlite_kv_cursor_init(unqlite *pDb,unqlite_kv_cursor **ppOut); -UNQLITE_APIEXPORT int unqlite_kv_cursor_release(unqlite *pDb,unqlite_kv_cursor *pCur); -UNQLITE_APIEXPORT int unqlite_kv_cursor_seek(unqlite_kv_cursor *pCursor,const void *pKey,int nKeyLen,int iPos); -UNQLITE_APIEXPORT int unqlite_kv_cursor_first_entry(unqlite_kv_cursor *pCursor); -UNQLITE_APIEXPORT int unqlite_kv_cursor_last_entry(unqlite_kv_cursor *pCursor); -UNQLITE_APIEXPORT int unqlite_kv_cursor_valid_entry(unqlite_kv_cursor *pCursor); -UNQLITE_APIEXPORT int unqlite_kv_cursor_next_entry(unqlite_kv_cursor *pCursor); -UNQLITE_APIEXPORT int unqlite_kv_cursor_prev_entry(unqlite_kv_cursor *pCursor); -UNQLITE_APIEXPORT int unqlite_kv_cursor_key(unqlite_kv_cursor *pCursor,void *pBuf,int *pnByte); -UNQLITE_APIEXPORT int unqlite_kv_cursor_key_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData); -UNQLITE_APIEXPORT int unqlite_kv_cursor_data(unqlite_kv_cursor *pCursor,void *pBuf,unqlite_int64 *pnData); -UNQLITE_APIEXPORT int unqlite_kv_cursor_data_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData); -UNQLITE_APIEXPORT int unqlite_kv_cursor_delete_entry(unqlite_kv_cursor *pCursor); -UNQLITE_APIEXPORT int unqlite_kv_cursor_reset(unqlite_kv_cursor *pCursor); - -/* Manual Transaction Manager */ -UNQLITE_APIEXPORT int unqlite_begin(unqlite *pDb); -UNQLITE_APIEXPORT int unqlite_commit(unqlite *pDb); -UNQLITE_APIEXPORT int unqlite_rollback(unqlite *pDb); - -/* Utility interfaces */ -UNQLITE_APIEXPORT int unqlite_util_random_string(unqlite *pDb,char *zBuf,unsigned int buf_size); -UNQLITE_APIEXPORT unsigned int unqlite_util_random_num(unqlite *pDb); - -/* Global Library Management Interfaces */ -UNQLITE_APIEXPORT int unqlite_lib_config(int nConfigOp,...); -UNQLITE_APIEXPORT int unqlite_lib_init(void); -UNQLITE_APIEXPORT int unqlite_lib_shutdown(void); -UNQLITE_APIEXPORT int unqlite_lib_is_threadsafe(void); -UNQLITE_APIEXPORT const char * unqlite_lib_version(void); -UNQLITE_APIEXPORT const char * unqlite_lib_signature(void); -UNQLITE_APIEXPORT const char * unqlite_lib_ident(void); -UNQLITE_APIEXPORT const char * unqlite_lib_copyright(void); - -#endif /* _UNQLITE_H_ */ + +#include "unqlite.h" /* * ---------------------------------------------------------- * File: unqliteInt.h @@ -876,7 +89,7 @@ UNQLITE_APIEXPORT const char * unqlite_lib_copyright(void); #else #define UNQLITE_PRIVATE #include "unqlite.h" -#endif +#endif /* * Constants for the largest and smallest possible 64-bit signed integers. * These macros are designed to work correctly on both 32-bit and 64-bit @@ -927,7 +140,7 @@ typedef double sxreal; #define SXI32_HIGH 0x7FFFFFFF #define SXU32_HIGH 0xFFFFFFFF #define SXI64_HIGH 0x7FFFFFFFFFFFFFFF -#define SXU64_HIGH 0xFFFFFFFFFFFFFFFF +#define SXU64_HIGH 0xFFFFFFFFFFFFFFFF #if !defined(TRUE) #define TRUE 1 #endif @@ -938,13 +151,13 @@ typedef double sxreal; * The following macros are used to cast pointers to integers and * integers to pointers. */ -#if defined(__PTRDIFF_TYPE__) +#if defined(__PTRDIFF_TYPE__) # define SX_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X)) # define SX_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X)) -#elif !defined(__GNUC__) +#elif !defined(__GNUC__) # define SX_INT_TO_PTR(X) ((void*)&((char*)0)[X]) # define SX_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0)) -#else +#else # define SX_INT_TO_PTR(X) ((void*)(X)) # define SX_PTR_TO_INT(X) ((int)(X)) #endif @@ -1009,7 +222,7 @@ typedef sxi32 (*ProcHashSum)(const void *, sxu32, unsigned char *, sxu32); typedef sxi32 (*ProcSort)(void *, sxu32, sxu32, ProcCmp); #define MACRO_LIST_PUSH(Head, Item)\ Item->pNext = Head;\ - Head = Item; + Head = Item; #define MACRO_LD_PUSH(Head, Item)\ if( Head == 0 ){\ Head = Item;\ @@ -1030,18 +243,18 @@ typedef sxi32 (*ProcSort)(void *, sxu32, sxu32, ProcCmp); struct SySet { SyMemBackend *pAllocator; /* Memory backend */ - void *pBase; /* Base pointer */ + void *pBase; /* Base pointer */ sxu32 nUsed; /* Total number of used slots */ sxu32 nSize; /* Total number of available slots */ sxu32 eSize; /* Size of a single slot */ - sxu32 nCursor; /* Loop cursor */ + sxu32 nCursor; /* Loop cursor */ void *pUserData; /* User private data associated with this container */ }; #define SySetBasePtr(S) ((S)->pBase) #define SySetBasePtrJump(S, OFFT) (&((char *)(S)->pBase)[OFFT*(S)->eSize]) #define SySetUsed(S) ((S)->nUsed) #define SySetSize(S) ((S)->nSize) -#define SySetElemSize(S) ((S)->eSize) +#define SySetElemSize(S) ((S)->eSize) #define SySetCursor(S) ((S)->nCursor) #define SySetGetAllocator(S) ((S)->pAllocator) #define SySetSetUserData(S, DATA) ((S)->pUserData = DATA) @@ -1274,7 +487,7 @@ struct SyToken struct SyStream { const unsigned char *zInput; /* Complete text of the input */ - const unsigned char *zText; /* Current input we are processing */ + const unsigned char *zText; /* Current input we are processing */ const unsigned char *zEnd; /* End of input marker */ sxu32 nLine; /* Total number of processed lines */ sxu32 nIgn; /* Total number of ignored tokens */ @@ -1306,7 +519,7 @@ struct SyLex ** */ /* -** Assuming zIn points to the first byte of a UTF-8 character, +** Assuming zIn points to the first byte of a UTF-8 character, ** advance zIn to point to the first byte of the next UTF-8 character. */ #define SX_JMP_UTF8(zIn, zEnd)\ @@ -1330,8 +543,8 @@ struct SyLex } /* Rely on the standard ctype */ #include -#define SyToUpper(c) toupper(c) -#define SyToLower(c) tolower(c) +#define SyToUpper(c) toupper(c) +#define SyToLower(c) tolower(c) #define SyisUpper(c) isupper(c) #define SyisLower(c) islower(c) #define SyisSpace(c) isspace(c) @@ -1346,7 +559,7 @@ struct SyLex #define SyisAscii(c) isascii(c) #define SyisAlphaNum(c) isalnum(c) #define SyisGraph(c) isgraph(c) -#define SyDigToHex(c) "0123456789ABCDEF"[c & 0x0F] +#define SyDigToHex(c) "0123456789ABCDEF"[c & 0x0F] #define SyDigToInt(c) ((c < 0xc0 && SyisDigit(c))? (c - '0') : 0 ) #define SyCharToUpper(c) ((c < 0xc0 && SyisLower(c))? SyToUpper(c) : c) #define SyCharToLower(c) ((c < 0xc0 && SyisUpper(c))? SyToLower(c) : c) @@ -1465,7 +678,7 @@ typedef struct unqlite_db unqlite_db; * The following #defines specify the range of bytes used for locking. * SHARED_SIZE is the number of bytes available in the pool from which * a random byte is selected for a shared lock. The pool of bytes for - * shared locks begins at SHARED_FIRST. + * shared locks begins at SHARED_FIRST. * * The same locking strategy and byte ranges are used for Unix and Windows. * This leaves open the possiblity of having clients on winNT, and @@ -1481,7 +694,7 @@ typedef struct unqlite_db unqlite_db; * that all locks will fit on a single page even at the minimum page size. * PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE * is set high so that we don't have to allocate an unused page except - * for very large databases. But one should test the page skipping logic + * for very large databases. But one should test the page skipping logic * by setting PENDING_BYTE low and running the entire regression suite. * * Changing the value of PENDING_BYTE results in a subtly incompatible @@ -1531,7 +744,7 @@ struct unqlite sxu32 nMagic; /* Sanity check against misuse */ }; #define UNQLITE_FL_DISABLE_AUTO_COMMIT 0x001 /* Disable auto-commit on close */ -/* +/* * Database signature to identify a valid database image. */ #define UNQLITE_DB_SIG "unqlite" @@ -1597,8 +810,8 @@ UNQLITE_PRIVATE int unqliteOsSectorSize(unqlite_file *id); UNQLITE_PRIVATE int unqliteOsOpen( unqlite_vfs *pVfs, SyMemBackend *pAlloc, - const char *zPath, - unqlite_file **ppOut, + const char *zPath, + unqlite_file **ppOut, unsigned int flags ); UNQLITE_PRIVATE int unqliteOsCloseFree(SyMemBackend *pAlloc,unqlite_file *pId); @@ -1674,10 +887,10 @@ static struct unqlGlobal_Data #if defined(UNQLITE_ENABLE_THREADS) const SyMutexMethods *pMutexMethods; /* Mutex methods */ SyMutex *pMutex; /* Global mutex */ - sxu32 nThreadingLevel; /* Threading level: 0 == Single threaded/1 == Multi-Threaded + sxu32 nThreadingLevel; /* Threading level: 0 == Single threaded/1 == Multi-Threaded * The threading level can be set using the [unqlite_lib_config()] * interface with a configuration verb set to - * UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE or + * UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE or * UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI */ #endif @@ -1688,17 +901,17 @@ static struct unqlGlobal_Data unqlite *pDB; /* List of active DB handles */ sxu32 nMagic; /* Sanity check against library misuse */ }sUnqlMPGlobal = { - {0, 0, 0, 0, 0, 0, 0, 0, {0}}, + {0, 0, 0, 0, 0, 0, 0, 0, {0}}, #if defined(UNQLITE_ENABLE_THREADS) - 0, - 0, - 0, + 0, + 0, + 0, #endif {0, 0, 0, 0, 0, 0, 0 }, UNQLITE_DEFAULT_PAGE_SIZE, - 0, - 0, - 0, + 0, + 0, + 0, 0 }; #define UNQLITE_LIB_MAGIC 0xEA1495BA @@ -1715,7 +928,7 @@ static struct unqlGlobal_Data * are enabled so that the application is free to share the same database handle * between different threads at the same time. */ -#define UNQLITE_THREAD_LEVEL_SINGLE 1 +#define UNQLITE_THREAD_LEVEL_SINGLE 1 #define UNQLITE_THREAD_LEVEL_MULTI 2 /* * Find a Key Value storage engine from the set of installed engines. @@ -1771,7 +984,7 @@ static sxi32 unqliteCoreConfigure(sxi32 nOp, va_list ap) unqlite_kv_methods *pMethods = va_arg(ap,unqlite_kv_methods *); /* Make sure we are delaing with a valid methods */ if( pMethods == 0 || SX_EMPTY_STR(pMethods->zName) || pMethods->xSeek == 0 || pMethods->xData == 0 - || pMethods->xKey == 0 || pMethods->xDataLength == 0 || pMethods->xKeyLength == 0 + || pMethods->xKey == 0 || pMethods->xDataLength == 0 || pMethods->xKeyLength == 0 || pMethods->szKv < (int)sizeof(unqlite_kv_engine) ){ rc = UNQLITE_INVALID; break; @@ -1809,7 +1022,7 @@ static sxi32 unqliteCoreConfigure(sxi32 nOp, va_list ap) sUnqlMPGlobal.sAllocator.xMemError = xMemErr; sUnqlMPGlobal.sAllocator.pUserData = pUserData; break; - } + } case UNQLITE_LIB_CONFIG_USER_MUTEX: { #if defined(UNQLITE_ENABLE_THREADS) /* Use an alternative low-level mutex subsystem */ @@ -1853,10 +1066,10 @@ static sxi32 unqliteCoreConfigure(sxi32 nOp, va_list ap) rc = UNQLITE_CORRUPT; break; } - sUnqlMPGlobal.pMutexMethods = pMethods; + sUnqlMPGlobal.pMutexMethods = pMethods; if( sUnqlMPGlobal.nThreadingLevel == 0 ){ /* Set a default threading level */ - sUnqlMPGlobal.nThreadingLevel = UNQLITE_THREAD_LEVEL_MULTI; + sUnqlMPGlobal.nThreadingLevel = UNQLITE_THREAD_LEVEL_MULTI; } #endif break; @@ -1902,11 +1115,11 @@ int unqlite_lib_config(int nConfigOp,...) /* * Global library initialization * Refer to [unqlite_lib_init()] - * This routine must be called to initialize the memory allocation subsystem, the mutex + * This routine must be called to initialize the memory allocation subsystem, the mutex * subsystem prior to doing any serious work with the library. The first thread to call * this routine does the initialization process and set the magic number so no body later * can re-initialize the library. If subsequent threads call this routine before the first - * thread have finished the initialization process, then the subsequent threads must block + * thread have finished the initialization process, then the subsequent threads must block * until the initialization process is done. */ static sxi32 unqliteCoreInitialize(void) @@ -2039,7 +1252,7 @@ static void unqliteCoreShutdown(void) break; } pNext = pDb->pNext; - unqliteDbRelease(pDb); + unqliteDbRelease(pDb); pDb = pNext; sUnqlMPGlobal.nDB--; } @@ -2186,7 +1399,7 @@ static int unqliteInitDatabase( { int rc; - /* Initialiaze the memory subsystem */ + /* Initialize the memory subsystem */ SyMemBackendInitFromParent(&pDB->sMem,pParent); SyBlobInit(&pDB->sErr,&pDB->sMem); /* Sanityze flags */ @@ -2388,7 +1601,7 @@ int unqlite_config(unqlite *pDb,int nConfigOp,...) #if defined(UNQLITE_ENABLE_THREADS) /* Acquire DB mutex */ SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && + if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && UNQLITE_THRD_DB_RELEASE(pDb) ){ return UNQLITE_ABORT; /* Another thread have released this instance */ } @@ -2415,7 +1628,7 @@ int unqlite_close(unqlite *pDb) #if defined(UNQLITE_ENABLE_THREADS) /* Acquire DB mutex */ SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && + if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && UNQLITE_THRD_DB_RELEASE(pDb) ){ return UNQLITE_ABORT; /* Another thread have released this instance */ } @@ -2457,7 +1670,7 @@ int unqlite_kv_store(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData #if defined(UNQLITE_ENABLE_THREADS) /* Acquire DB mutex */ SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && + if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && UNQLITE_THRD_DB_RELEASE(pDb) ){ return UNQLITE_ABORT; /* Another thread have released this instance */ } @@ -2501,7 +1714,7 @@ int unqlite_kv_store_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *z #if defined(UNQLITE_ENABLE_THREADS) /* Acquire DB mutex */ SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && + if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && UNQLITE_THRD_DB_RELEASE(pDb) ){ return UNQLITE_ABORT; /* Another thread have released this instance */ } @@ -2554,7 +1767,7 @@ int unqlite_kv_append(unqlite *pDb,const void *pKey,int nKeyLen,const void *pDat #if defined(UNQLITE_ENABLE_THREADS) /* Acquire DB mutex */ SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && + if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && UNQLITE_THRD_DB_RELEASE(pDb) ){ return UNQLITE_ABORT; /* Another thread have released this instance */ } @@ -2598,7 +1811,7 @@ int unqlite_kv_append_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char * #if defined(UNQLITE_ENABLE_THREADS) /* Acquire DB mutex */ SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && + if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && UNQLITE_THRD_DB_RELEASE(pDb) ){ return UNQLITE_ABORT; /* Another thread have released this instance */ } @@ -2653,7 +1866,7 @@ int unqlite_kv_fetch(unqlite *pDb,const void *pKey,int nKeyLen,void *pBuf,unqlit #if defined(UNQLITE_ENABLE_THREADS) /* Acquire DB mutex */ SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && + if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && UNQLITE_THRD_DB_RELEASE(pDb) ){ return UNQLITE_ABORT; /* Another thread have released this instance */ } @@ -2711,7 +1924,7 @@ int unqlite_kv_fetch_callback(unqlite *pDb,const void *pKey,int nKeyLen,int (*xC #if defined(UNQLITE_ENABLE_THREADS) /* Acquire DB mutex */ SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && + if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && UNQLITE_THRD_DB_RELEASE(pDb) ){ return UNQLITE_ABORT; /* Another thread have released this instance */ } @@ -2733,7 +1946,7 @@ int unqlite_kv_fetch_callback(unqlite *pDb,const void *pKey,int nKeyLen,int (*xC } if( rc == UNQLITE_OK && xConsumer ){ /* Consume the data directly */ - rc = pMethods->xData(pCur,xConsumer,pUserData); + rc = pMethods->xData(pCur,xConsumer,pUserData); } #if defined(UNQLITE_ENABLE_THREADS) /* Leave DB mutex */ @@ -2757,7 +1970,7 @@ int unqlite_kv_delete(unqlite *pDb,const void *pKey,int nKeyLen) #if defined(UNQLITE_ENABLE_THREADS) /* Acquire DB mutex */ SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && + if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && UNQLITE_THRD_DB_RELEASE(pDb) ){ return UNQLITE_ABORT; /* Another thread have released this instance */ } @@ -2807,7 +2020,7 @@ int unqlite_kv_config(unqlite *pDb,int iOp,...) #if defined(UNQLITE_ENABLE_THREADS) /* Acquire DB mutex */ SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && + if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && UNQLITE_THRD_DB_RELEASE(pDb) ){ return UNQLITE_ABORT; /* Another thread have released this instance */ } @@ -2844,7 +2057,7 @@ int unqlite_kv_cursor_init(unqlite *pDb,unqlite_kv_cursor **ppOut) #if defined(UNQLITE_ENABLE_THREADS) /* Acquire DB mutex */ SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && + if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && UNQLITE_THRD_DB_RELEASE(pDb) ){ return UNQLITE_ABORT; /* Another thread have released this instance */ } @@ -2870,7 +2083,7 @@ int unqlite_kv_cursor_release(unqlite *pDb,unqlite_kv_cursor *pCur) #if defined(UNQLITE_ENABLE_THREADS) /* Acquire DB mutex */ SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && + if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && UNQLITE_THRD_DB_RELEASE(pDb) ){ return UNQLITE_ABORT; /* Another thread have released this instance */ } @@ -3175,7 +2388,7 @@ int unqlite_begin(unqlite *pDb) #if defined(UNQLITE_ENABLE_THREADS) /* Acquire DB mutex */ SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && + if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && UNQLITE_THRD_DB_RELEASE(pDb) ){ return UNQLITE_ABORT; /* Another thread have released this instance */ } @@ -3201,7 +2414,7 @@ int unqlite_commit(unqlite *pDb) #if defined(UNQLITE_ENABLE_THREADS) /* Acquire DB mutex */ SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && + if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && UNQLITE_THRD_DB_RELEASE(pDb) ){ return UNQLITE_ABORT; /* Another thread have released this instance */ } @@ -3227,7 +2440,7 @@ int unqlite_rollback(unqlite *pDb) #if defined(UNQLITE_ENABLE_THREADS) /* Acquire DB mutex */ SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && + if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && UNQLITE_THRD_DB_RELEASE(pDb) ){ return UNQLITE_ABORT; /* Another thread have released this instance */ } @@ -3256,7 +2469,7 @@ UNQLITE_APIEXPORT int unqlite_util_random_string(unqlite *pDb,char *zBuf,unsigne #if defined(UNQLITE_ENABLE_THREADS) /* Acquire DB mutex */ SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && + if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && UNQLITE_THRD_DB_RELEASE(pDb) ){ return UNQLITE_ABORT; /* Another thread have released this instance */ } @@ -3282,7 +2495,7 @@ UNQLITE_APIEXPORT unsigned int unqlite_util_random_num(unqlite *pDb) #if defined(UNQLITE_ENABLE_THREADS) /* Acquire DB mutex */ SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */ - if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && + if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && UNQLITE_THRD_DB_RELEASE(pDb) ){ return 0; /* Another thread have released this instance */ } @@ -3327,7 +2540,7 @@ UNQLITE_APIEXPORT unsigned int unqlite_util_random_num(unqlite *pDb) ** So the bitmap is usually sparse and has low cardinality. */ /* - * Actually, this is not a bitmap but a simple hashtable where page + * Actually, this is not a bitmap but a simple hashtable where page * number (64-bit unsigned integers) are used as the lookup keys. */ typedef struct bitvec_rec bitvec_rec; @@ -3344,14 +2557,14 @@ struct Bitvec bitvec_rec **apRec; /* Record table */ bitvec_rec *pList; /* List of records */ }; -/* +/* * Allocate a new bitvec instance. */ UNQLITE_PRIVATE Bitvec * unqliteBitvecCreate(SyMemBackend *pAlloc,pgno iSize) { bitvec_rec **apNew; Bitvec *p; - + p = (Bitvec *)SyMemBackendAlloc(pAlloc,sizeof(*p) ); if( p == 0 ){ SXUNUSED(iSize); /* cc warning */ @@ -3378,7 +2591,7 @@ UNQLITE_PRIVATE Bitvec * unqliteBitvecCreate(SyMemBackend *pAlloc,pgno iSize) * Return true if installed. False otherwise. */ UNQLITE_PRIVATE int unqliteBitvecTest(Bitvec *p,pgno i) -{ +{ bitvec_rec *pRec; /* Point to the desired bucket */ pRec = p->apRec[i & (p->nSize - 1)]; @@ -3484,7 +2697,7 @@ UNQLITE_PRIVATE void unqliteBitvecDestroy(Bitvec *p) { bitvec_rec *pNext,*pRec = p->pList; SyMemBackend *pAlloc = p->pAlloc; - + for(;;){ if( p->nRec < 1 ){ break; @@ -3539,11 +2752,11 @@ struct SyMutex }; /* Preallocated static mutex */ static SyMutex aStaticMutexes[] = { - {{0}, SXMUTEX_TYPE_STATIC_1}, - {{0}, SXMUTEX_TYPE_STATIC_2}, - {{0}, SXMUTEX_TYPE_STATIC_3}, - {{0}, SXMUTEX_TYPE_STATIC_4}, - {{0}, SXMUTEX_TYPE_STATIC_5}, + {{0}, SXMUTEX_TYPE_STATIC_1}, + {{0}, SXMUTEX_TYPE_STATIC_2}, + {{0}, SXMUTEX_TYPE_STATIC_3}, + {{0}, SXMUTEX_TYPE_STATIC_4}, + {{0}, SXMUTEX_TYPE_STATIC_5}, {{0}, SXMUTEX_TYPE_STATIC_6} }; static BOOL winMutexInit = FALSE; @@ -3656,15 +2869,15 @@ struct SyMutex static SyMutex * UnixMutexNew(int nType) { static SyMutex aStaticMutexes[] = { - {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_1}, - {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_2}, - {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_3}, - {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_4}, - {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_5}, + {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_1}, + {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_2}, + {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_3}, + {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_4}, + {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_5}, {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_6} }; SyMutex *pMutex; - + if( nType == SXMUTEX_TYPE_FAST || nType == SXMUTEX_TYPE_RECURSIVE ){ pthread_mutexattr_t sRecursiveAttr; /* Allocate a new mutex */ @@ -3688,7 +2901,7 @@ static SyMutex * UnixMutexNew(int nType) pMutex = &aStaticMutexes[nType - 3]; } pMutex->nType = nType; - + return pMutex; } static void UnixMutexRelease(SyMutex *pMutex) @@ -3780,7 +2993,7 @@ static void * SyOSHeapRealloc(void *pOld, sxu32 nByte) #else pNew = realloc(pOld, (size_t)nByte); #endif - return pNew; + return pNew; } static void SyOSHeapFree(void *pPtr) { @@ -3803,7 +3016,7 @@ UNQLITE_PRIVATE sxu32 SyStrlen(const char *zSrc) if( !zIn[0] ){ break; } zIn++; if( !zIn[0] ){ break; } zIn++; if( !zIn[0] ){ break; } zIn++; - if( !zIn[0] ){ break; } zIn++; + if( !zIn[0] ){ break; } zIn++; } return (sxu32)(zIn - zSrc); } @@ -3811,7 +3024,7 @@ UNQLITE_PRIVATE sxi32 SyStrnicmp(const char *zLeft, const char *zRight, sxu32 SL { register unsigned char *p = (unsigned char *)zLeft; register unsigned char *q = (unsigned char *)zRight; - + if( SX_EMPTY_STR(p) || SX_EMPTY_STR(q) ){ return SX_EMPTY_STR(p)? SX_EMPTY_STR(q) ? 0 : -1 :1; } @@ -3820,7 +3033,7 @@ UNQLITE_PRIVATE sxi32 SyStrnicmp(const char *zLeft, const char *zRight, sxu32 SL if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen; if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen; if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen; - + } return (sxi32)(SyCharToLower(p[0]) - SyCharToLower(q[0])); } @@ -3834,7 +3047,7 @@ UNQLITE_PRIVATE sxu32 Systrcpy(char *zDest, sxu32 nDestLen, const char *zSrc, sx return 0; } #endif - if( nLen <= 0 ){ + if( nLen == 0 ){ nLen = SyStrlen(zSrc); } zEnd = &zBuf[nDestLen - 1]; /* reserve a room for the null terminator */ @@ -3868,7 +3081,7 @@ UNQLITE_PRIVATE void SyZero(void *pSrc, sxu32 nSize) UNQLITE_PRIVATE sxi32 SyMemcmp(const void *pB1, const void *pB2, sxu32 nSize) { sxi32 rc; - if( nSize <= 0 ){ + if( nSize == 0 ){ return 0; } if( pB1 == 0 || pB2 == 0 ){ @@ -3927,12 +3140,12 @@ static sxu32 MemOSChunkSize(void *pBlock) } /* Export OS allocation methods */ static const SyMemMethods sOSAllocMethods = { - MemOSAlloc, - MemOSRealloc, - MemOSFree, - MemOSChunkSize, - 0, - 0, + MemOSAlloc, + MemOSRealloc, + MemOSFree, + MemOSChunkSize, + 0, + 0, 0 }; static void * MemBackendAlloc(SyMemBackend *pBackend, sxu32 nByte) @@ -3946,7 +3159,7 @@ static void * MemBackendAlloc(SyMemBackend *pBackend, sxu32 nByte) nByte += sizeof(SyMemBlock); for(;;){ pBlock = (SyMemBlock *)pBackend->pMethods->xAlloc(nByte); - if( pBlock != 0 || pBackend->xMemError == 0 || nRetry > SXMEM_BACKEND_RETRY + if( pBlock != 0 || pBackend->xMemError == 0 || nRetry > SXMEM_BACKEND_RETRY || SXERR_RETRY != pBackend->xMemError(pBackend->pUserData) ){ break; } @@ -4106,14 +3319,14 @@ UNQLITE_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend, const S * Memory pool allocator */ #define SXMEM_POOL_MAGIC 0xDEAD -#define SXMEM_POOL_MAXALLOC (1<<(SXMEM_POOL_NBUCKETS+SXMEM_POOL_INCR)) +#define SXMEM_POOL_MAXALLOC (1<<(SXMEM_POOL_NBUCKETS+SXMEM_POOL_INCR)) #define SXMEM_POOL_MINALLOC (1<<(SXMEM_POOL_INCR)) static sxi32 MemPoolBucketAlloc(SyMemBackend *pBackend, sxu32 nBucket) { char *zBucket, *zBucketEnd; SyMemHeader *pHeader; sxu32 nBucketSize; - + /* Allocate one big block first */ zBucket = (char *)MemBackendAlloc(&(*pBackend), SXMEM_POOL_MAXALLOC); if( zBucket == 0 ){ @@ -4130,10 +3343,10 @@ static sxi32 MemPoolBucketAlloc(SyMemBackend *pBackend, sxu32 nBucket) pHeader->pNext = (SyMemHeader *)&zBucket[nBucketSize]; /* Advance the cursor to the next available chunk */ pHeader = pHeader->pNext; - zBucket += nBucketSize; + zBucket += nBucketSize; } pHeader->pNext = 0; - + return SXRET_OK; } static void * MemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nByte) @@ -4602,7 +3815,7 @@ UNQLITE_PRIVATE sxi32 SySetPut(SySet *pSet, const void *pItem) if( pSet->pAllocator == 0 ){ return SXERR_LOCKED; } - if( pSet->nSize <= 0 ){ + if( pSet->nSize == 0 ){ pSet->nSize = 4; } pNew = SyMemBackendRealloc(pSet->pAllocator, pSet->pBase, pSet->eSize * pSet->nSize * 2); @@ -4614,7 +3827,7 @@ UNQLITE_PRIVATE sxi32 SySetPut(SySet *pSet, const void *pItem) } zbase = (unsigned char *)pSet->pBase; SX_MACRO_FAST_MEMCPY(pItem, &zbase[pSet->nUsed * pSet->eSize], pSet->eSize); - pSet->nUsed++; + pSet->nUsed++; return SXRET_OK; } UNQLITE_PRIVATE sxi32 SySetRelease(SySet *pSet) @@ -4645,7 +3858,7 @@ UNQLITE_PRIVATE sxi32 SySetRelease(SySet *pSet) #define SXFMT_ERROR 9 /* Used to indicate no such conversion type */ /* Extension by Symisc Systems */ #define SXFMT_RAWSTR 13 /* %z Pointer to raw string (SyString *) */ -#define SXFMT_UNUSED 15 +#define SXFMT_UNUSED 15 /* ** Allowed values for SyFmtInfo.flags */ @@ -4677,13 +3890,13 @@ struct SyFmtConsumer sxi32 nType; /* Type of the consumer see below */ sxi32 rc; /* Consumer return value;Abort processing if rc != SXRET_OK */ union{ - struct{ + struct{ ProcConsumer xUserConsumer; void *pUserData; - }sFunc; + }sFunc; SyBlob *pBlob; - }uConsumer; -}; + }uConsumer; +}; #ifndef SX_OMIT_FLOATINGPOINT static int getdigit(sxlongreal *val, int *cnt) { @@ -4711,27 +3924,27 @@ static sxi32 InternFormat(ProcConsumer xConsumer, void *pUserData, const char *z * used conversion types first. */ static const SyFmtInfo aFmt[] = { - { 'd', 10, SXFLAG_SIGNED, SXFMT_RADIX, "0123456789", 0 }, - { 's', 0, 0, SXFMT_STRING, 0, 0 }, - { 'c', 0, 0, SXFMT_CHARX, 0, 0 }, - { 'x', 16, 0, SXFMT_RADIX, "0123456789abcdef", "x0" }, - { 'X', 16, 0, SXFMT_RADIX, "0123456789ABCDEF", "X0" }, + { 'd', 10, SXFLAG_SIGNED, SXFMT_RADIX, "0123456789", 0 }, + { 's', 0, 0, SXFMT_STRING, 0, 0 }, + { 'c', 0, 0, SXFMT_CHARX, 0, 0 }, + { 'x', 16, 0, SXFMT_RADIX, "0123456789abcdef", "x0" }, + { 'X', 16, 0, SXFMT_RADIX, "0123456789ABCDEF", "X0" }, /* -- Extensions by Symisc Systems -- */ { 'z', 0, 0, SXFMT_RAWSTR, 0, 0 }, /* Pointer to a raw string (SyString *) */ - { 'B', 2, 0, SXFMT_RADIX, "01", "b0"}, + { 'B', 2, 0, SXFMT_RADIX, "01", "b0"}, /* -- End of Extensions -- */ - { 'o', 8, 0, SXFMT_RADIX, "01234567", "0" }, - { 'u', 10, 0, SXFMT_RADIX, "0123456789", 0 }, + { 'o', 8, 0, SXFMT_RADIX, "01234567", "0" }, + { 'u', 10, 0, SXFMT_RADIX, "0123456789", 0 }, #ifndef SX_OMIT_FLOATINGPOINT - { 'f', 0, SXFLAG_SIGNED, SXFMT_FLOAT, 0, 0 }, - { 'e', 0, SXFLAG_SIGNED, SXFMT_EXP, "e", 0 }, - { 'E', 0, SXFLAG_SIGNED, SXFMT_EXP, "E", 0 }, - { 'g', 0, SXFLAG_SIGNED, SXFMT_GENERIC, "e", 0 }, - { 'G', 0, SXFLAG_SIGNED, SXFMT_GENERIC, "E", 0 }, -#endif - { 'i', 10, SXFLAG_SIGNED, SXFMT_RADIX, "0123456789", 0 }, - { 'n', 0, 0, SXFMT_SIZE, 0, 0 }, - { '%', 0, 0, SXFMT_PERCENT, 0, 0 }, + { 'f', 0, SXFLAG_SIGNED, SXFMT_FLOAT, 0, 0 }, + { 'e', 0, SXFLAG_SIGNED, SXFMT_EXP, "e", 0 }, + { 'E', 0, SXFLAG_SIGNED, SXFMT_EXP, "E", 0 }, + { 'g', 0, SXFLAG_SIGNED, SXFMT_GENERIC, "e", 0 }, + { 'G', 0, SXFLAG_SIGNED, SXFMT_GENERIC, "E", 0 }, +#endif + { 'i', 10, SXFLAG_SIGNED, SXFMT_RADIX, "0123456789", 0 }, + { 'n', 0, 0, SXFMT_SIZE, 0, 0 }, + { '%', 0, 0, SXFMT_PERCENT, 0, 0 }, { 'p', 10, 0, SXFMT_RADIX, "0123456789", 0 } }; int c; /* Next character in the format string */ @@ -4752,7 +3965,7 @@ static const SyFmtInfo aFmt[] = { char prefix; /* Prefix character."+" or "-" or " " or '\0'.*/ sxu8 errorflag = 0; /* True if an error is encountered */ sxu8 xtype; /* Conversion paradigm */ - char *zExtra; + char *zExtra; static char spaces[] = " "; #define etSPACESIZE ((int)sizeof(spaces)-1) #ifndef SX_OMIT_FLOATINGPOINT @@ -4791,7 +4004,7 @@ static const SyFmtInfo aFmt[] = { return errorflag > 0 ? SXERR_FORMAT : SXRET_OK; } /* Find out what flags are present */ - flag_leftjustify = flag_plussign = flag_blanksign = + flag_leftjustify = flag_plussign = flag_blanksign = flag_alternateform = flag_zeropad = 0; do{ switch( c ){ @@ -4900,12 +4113,12 @@ static const SyFmtInfo aFmt[] = { ** I think this is stupid.*/ if( longvalue==0 ) flag_alternateform = 0; #else - /* More sensible: turn off the prefix for octal (to prevent "00"), + /* More sensible: turn off the prefix for octal (to prevent "00"), ** but leave the prefix for hex.*/ if( longvalue==0 && infop->base==8 ) flag_alternateform = 0; #endif if( infop->flags & SXFLAG_SIGNED ){ - if( longvalue<0 ){ + if( longvalue<0 ){ longvalue = -longvalue; /* Ticket 1433-003 */ if( longvalue < 0 ){ @@ -4941,7 +4154,7 @@ static const SyFmtInfo aFmt[] = { longvalue = longvalue/base; }while( longvalue>0 ); } - length = &buf[SXFMT_BUFSIZ-1]-bufpt; + length = (int)(&buf[SXFMT_BUFSIZ-1]-bufpt); for(idx=precision-length; idx>0; idx--){ *(--bufpt) = '0'; /* Zero pad */ } @@ -4953,7 +4166,7 @@ static const SyFmtInfo aFmt[] = { for(pre=infop->prefix; (x=(*pre))!=0; pre++) *(--bufpt) = x; } } - length = &buf[SXFMT_BUFSIZ-1]-bufpt; + length = (int)(&buf[SXFMT_BUFSIZ-1]-bufpt); break; case SXFMT_FLOAT: case SXFMT_EXP: @@ -5062,7 +4275,7 @@ static const SyFmtInfo aFmt[] = { /* The converted number is in buf[] and zero terminated.Output it. ** Note that the number is in the usual order, not reversed as with ** integer conversions.*/ - length = bufpt-buf; + length = (int)(bufpt-buf); bufpt = buf; /* Special case: Add leading zeros if the flag_zeropad flag is @@ -5190,7 +4403,7 @@ static const SyFmtInfo aFmt[] = { } }/* End for loop over the format string */ return errorflag ? SXERR_FORMAT : SXRET_OK; -} +} static sxi32 FormatConsumer(const void *pSrc, unsigned int nLen, void *pData) { SyFmtConsumer *pConsumer = (SyFmtConsumer *)pData; @@ -5204,14 +4417,14 @@ static sxi32 FormatConsumer(const void *pSrc, unsigned int nLen, void *pData) /* Blob consumer */ rc = SyBlobAppend(pConsumer->uConsumer.pBlob, pSrc, (sxu32)nLen); break; - default: + default: /* Unknown consumer */ break; } /* Update total number of bytes consumed so far */ pConsumer->nLen += nLen; pConsumer->rc = rc; - return rc; + return rc; } static sxi32 FormatMount(sxi32 nType, void *pConsumer, ProcConsumer xUserCons, void *pUserData, sxu32 *pOutLen, const char *zFormat, va_list ap) { @@ -5235,10 +4448,10 @@ static sxi32 FormatMount(sxi32 nType, void *pConsumer, ProcConsumer xUserCons, v case SXFMT_CONS_BLOB: sCons.uConsumer.pBlob = (SyBlob *)pConsumer; break; - default: + default: return SXERR_UNKNOWN; } - InternFormat(FormatConsumer, &sCons, zFormat, ap); + InternFormat(FormatConsumer, &sCons, zFormat, ap); if( pOutLen ){ *pOutLen = sCons.nLen; } @@ -5247,27 +4460,28 @@ static sxi32 FormatMount(sxi32 nType, void *pConsumer, ProcConsumer xUserCons, v UNQLITE_PRIVATE sxu32 SyBlobFormatAp(SyBlob *pBlob, const char *zFormat, va_list ap) { sxu32 n = 0; /* cc warning */ -#if defined(UNTRUST) +#if defined(UNTRUST) if( SX_EMPTY_STR(zFormat) ){ return 0; } -#endif +#endif FormatMount(SXFMT_CONS_BLOB, &(*pBlob), 0, 0, &n, zFormat, ap); return n; } +#if defined(__UNIXES__) UNQLITE_PRIVATE sxu32 SyBufferFormat(char *zBuf, sxu32 nLen, const char *zFormat, ...) { SyBlob sBlob; va_list ap; sxu32 n; -#if defined(UNTRUST) +#if defined(UNTRUST) if( SX_EMPTY_STR(zFormat) ){ return 0; } -#endif +#endif if( SXRET_OK != SyBlobInitFromBuf(&sBlob, zBuf, nLen - 1) ){ return 0; - } + } va_start(ap, zFormat); FormatMount(SXFMT_CONS_BLOB, &sBlob, 0, 0, 0, zFormat, ap); va_end(ap); @@ -5277,6 +4491,7 @@ UNQLITE_PRIVATE sxu32 SyBufferFormat(char *zBuf, sxu32 nLen, const char *zFormat SyBlobAppend(&sBlob, "\0", sizeof(char)); return n; } +#endif /* * Psuedo Random Number Generator (PRNG) * @authors: SQLite authors @@ -5346,11 +4561,11 @@ UNQLITE_PRIVATE sxi32 SyRandomnessInit(SyPRNGCtx *pCtx, ProcRandomSeed xSeed, vo if( pCtx->nMagic == SXPRNG_MAGIC ){ return SXRET_OK; /* Already initialized */ } - /* Initialize the state of the random number generator once, + /* Initialize the state of the random number generator once, ** the first time this routine is called.The seed value does ** not need to contain a lot of randomness since we are not ** trying to do secure encryption or anything like that... - */ + */ if( xSeed == 0 ){ xSeed = SyOSUtilRandomSeed; } @@ -5369,7 +4584,7 @@ UNQLITE_PRIVATE sxi32 SyRandomnessInit(SyPRNGCtx *pCtx, ProcRandomSeed xSeed, vo pCtx->s[i] = t; } pCtx->nMagic = SXPRNG_MAGIC; - + return SXRET_OK; } /* @@ -5378,7 +4593,7 @@ UNQLITE_PRIVATE sxi32 SyRandomnessInit(SyPRNGCtx *pCtx, ProcRandomSeed xSeed, vo static sxu8 randomByte(SyPRNGCtx *pCtx) { sxu8 t; - + /* Generate and return single random byte */ pCtx->i++; t = pCtx->s[pCtx->i]; @@ -5401,12 +4616,12 @@ UNQLITE_PRIVATE sxi32 SyRandomness(SyPRNGCtx *pCtx, void *pBuf, sxu32 nLen) return SXERR_CORRUPT; } for(;;){ - if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++; - if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++; - if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++; - if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++; + if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++; + if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++; + if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++; + if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++; } - return SXRET_OK; + return SXRET_OK; } UNQLITE_PRIVATE void SyBigEndianPack32(unsigned char *buf,sxu32 nb) { @@ -5437,7 +4652,7 @@ UNQLITE_PRIVATE void SyBigEndianPack64(unsigned char *buf,sxu64 n64) buf[3] = n64 & 0xFF; n64 >>=8; buf[2] = n64 & 0xFF; n64 >>=8; buf[1] = n64 & 0xFF; n64 >>=8; - buf[0] = (sxu8)n64 ; + buf[0] = (sxu8)n64 ; } UNQLITE_PRIVATE void SyBigEndianUnpack64(const unsigned char *buf,sxu64 *n64) { @@ -5489,7 +4704,7 @@ UNQLITE_PRIVATE void SyDosTimeFormat(sxu32 nDosDate, Sytm *pOut) #ifndef UNQLITE_AMALGAMATION #include "unqliteInt.h" #endif -/* +/* * This file implements disk based hashtable using the linear hashing algorithm. * This implementation is the one decribed in the paper: * LINEAR HASHING : A NEW TOOL FOR FILE AND TABLE ADDRESSING. Witold Litwin. I. N. Ft. I. A.. 78 150 Le Chesnay, France. @@ -5502,7 +4717,7 @@ UNQLITE_PRIVATE void SyDosTimeFormat(sxu32 nDosDate, Sytm *pOut) */ #define L_HASH_WORD "chm@symisc" /* - * Cell size on disk. + * Cell size on disk. */ #define L_HASH_CELL_SZ (4/*Hash*/+4/*Key*/+8/*Data*/+2/* Offset of the next cell */+8/*Overflow*/) /* @@ -5553,7 +4768,7 @@ struct lhcell ** structure. */ typedef struct lhphdr lhphdr; -struct lhphdr +struct lhphdr { sxu16 iOfft; /* Offset of the first cell */ sxu16 iFree; /* Offset of the first free block*/ @@ -5587,7 +4802,7 @@ struct lhash_bmap_rec { pgno iLogic; /* Logical bucket number */ pgno iReal; /* Real bucket number */ - lhash_bmap_rec *pNext,*pPrev; /* Link to other bucket map */ + lhash_bmap_rec *pNext,*pPrev; /* Link to other bucket map */ lhash_bmap_rec *pNextCol,*pPrevCol; /* Collision links */ }; typedef struct lhash_bmap_page lhash_bmap_page; @@ -5683,7 +4898,7 @@ static int lhMapInstallBucket(lhash_kv_engine *pEngine,pgno iLogic,pgno iReal) lhash_bmap_rec *pEntry; lhash_bmap_rec **apNew; sxu32 n; - + apNew = (lhash_bmap_rec **)SyMemBackendAlloc(&pEngine->sAllocator, nNewSize * sizeof(lhash_bmap_rec *)); if( apNew ){ /* Zero the new table */ @@ -5756,7 +4971,7 @@ static int lhMapLoadPage(lhash_kv_engine *pEngine,lhash_bmap_page *pMap,const un /* All done */ return UNQLITE_OK; } -/* +/* * Allocate a new cell instance. */ static lhcell * lhNewCell(lhash_kv_engine *pEngine,lhpage *pPage) @@ -5778,8 +4993,8 @@ static lhcell * lhNewCell(lhash_kv_engine *pEngine,lhpage *pPage) */ static void lhCellDiscard(lhcell *pCell) { - lhpage *pPage = pCell->pPage->pMaster; - + lhpage *pPage = pCell->pPage->pMaster; + if( pCell->pPrevCol ){ pCell->pPrevCol->pNextCol = pCell->pNextCol; }else{ @@ -5836,7 +5051,7 @@ static int lhInstallCell(lhcell *pCell) lhcell *pEntry; lhcell **apNew; sxu32 n; - + apNew = (lhcell **)SyMemBackendAlloc(&pPage->pHash->sAllocator, nNewSize * sizeof(lhcell *)); if( apNew ){ /* Zero the new table */ @@ -5964,10 +5179,10 @@ static int lhParseOneCell(lhpage *pPage,const unsigned char *zRaw,const unsigned iOfft = (sxu16)(zRaw - (const unsigned char *)pPage->pRaw->zData); /* 4 byte hash number */ SyBigEndianUnpack32(zRaw,&iHash); - zRaw += 4; + zRaw += 4; /* 4 byte key length */ SyBigEndianUnpack32(zRaw,&nKey); - zRaw += 4; + zRaw += 4; /* 8 byte data length */ SyBigEndianUnpack64(zRaw,&nData); zRaw += 8; @@ -6383,7 +5598,7 @@ static int lhash_read_header(lhash_kv_engine *pEngine,unqlite_page *pHeader) zRaw += 8; /* Next generation */ pEngine->nmax_split_nucket = pEngine->max_split_bucket << 1; - /* Initialiaze the bucket map */ + /* Initialize the bucket map */ pMap = &pEngine->sPageMap; /* Fill in the structure */ pMap->iNum = pHeader->pgno; @@ -6440,7 +5655,7 @@ static int lhRecordLookup( pgno iBucket; sxu32 nHash; int rc; - /* Acquire the first page (hash Header) so that everything gets loaded autmatically */ + /* Acquire the first page (hash Header) so that everything gets loaded automatically */ rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0); if( rc != UNQLITE_OK ){ return rc; @@ -6607,8 +5822,8 @@ static int lhPageDefragment(lhpage *pPage) lhcell *pCell; /* Get a temporary page from the pager. This opertaion never fail */ zTmp = pEngine->pIo->xTmpPage(pEngine->pIo->pHandle); - /* Move the target cells to the begining */ - pCell = pPage->pList; + /* Move the target cells to the beginning */ + pCell = pPage->pMaster->pList; /* Write the slave page number */ SyBigEndianPack64(&zTmp[2/*Offset of the first cell */+2/*Offset of the first free block */],pPage->sHdr.iSlave); zPtr = &zTmp[L_HASH_PAGE_HDR_SZ]; /* Offset to start writing from */ @@ -6686,7 +5901,7 @@ static int lhPageDefragment(lhpage *pPage) ** ** If the page contains nBytes of free space but does not contain ** nBytes of contiguous free space, then this routine automatically -** calls defragementPage() to consolidate all free space before +** calls defragementPage() to consolidate all free space before ** allocating the new chunk. */ static int lhAllocateSpace(lhpage *pPage,sxu64 nAmount,sxu16 *pOfft) @@ -7074,14 +6289,14 @@ static lhcell * lhFindSibeling(lhcell *pCell) { lhpage *pPage = pCell->pPage->pMaster; lhcell *pEntry; - pEntry = pPage->pFirst; + pEntry = pPage->pFirst; while( pEntry ){ if( pEntry->pPage == pCell->pPage && pEntry->iNext == pCell->iStart ){ /* Sibeling found */ return pEntry; } /* Point to the previous entry */ - pEntry = pEntry->pPrev; + pEntry = pEntry->pPrev; } /* Last inserted cell */ return 0; @@ -7601,7 +6816,7 @@ static int lhFindSlavePage(lhpage *pPage,sxu64 nAmount,sxu16 *pOfft,lhpage **ppS if( UNQLITE_OK != lhAllocateSpace(pNew,L_HASH_CELL_SZ+nAmount,&iOfft) ){ /* Cell header only */ lhAllocateSpace(pNew,L_HASH_CELL_SZ,&iOfft); /* Never fail */ - } + } *pOfft = iOfft; } /* Link this page to the previous slave page */ @@ -7695,7 +6910,7 @@ static int lhPageSplit( lhcell *pCell,*pNext; SyBlob sWorker; pgno iBucket; - int rc; + int rc; SyBlobInit(&sWorker,&pOld->pHash->sAllocator); /* Perform the split */ pCell = pOld->pList; @@ -7880,7 +7095,7 @@ static int lh_record_insert( int iCnt; int rc; - /* Acquire the first page (DB hash Header) so that everything gets loaded autmatically */ + /* Acquire the first page (DB hash Header) so that everything gets loaded automatically */ rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0); if( rc != UNQLITE_OK ){ return rc; @@ -8003,7 +7218,7 @@ static int lhash_write_header(lhash_kv_engine *pEngine,unqlite_page *pHeader) /* Maximum split bucket */ SyBigEndianPack64(zRaw,pEngine->max_split_bucket); zRaw += 8; - /* Initialiaze the bucket map */ + /* Initialize the bucket map */ pMap = &pEngine->sPageMap; /* Fill in the structure */ pMap->iNum = pHeader->pgno; @@ -8099,7 +7314,7 @@ static sxu32 lhash_bin_hash(const void *pSrc,sxu32 nLen) if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; - } + } return nH; } /* @@ -8201,7 +7416,7 @@ struct lhash_kv_cursor unqlite_page *pRaw; /* Raw disk page */ lhash_bmap_rec *pRec; /* Logical to real bucket map */ }; -/* +/* * Possible state of the cursor */ #define L_HASH_CURSOR_STATE_NEXT_PAGE 1 /* Next page in the list */ @@ -8409,7 +7624,7 @@ static int lhCursorKeyLength(unqlite_kv_cursor *pCursor,int *pLen) { lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor; lhcell *pCell; - + if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){ /* Invalid state */ return UNQLITE_INVALID; @@ -8427,7 +7642,7 @@ static int lhCursorDataLength(unqlite_kv_cursor *pCursor,unqlite_int64 *pLen) { lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor; lhcell *pCell; - + if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){ /* Invalid state */ return UNQLITE_INVALID; @@ -8546,7 +7761,7 @@ UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportDiskKvStorage(void) lhCursorDataLength, /* xDataLength */ lhCursorData, /* xData */ lhCursorReset, /* xReset */ - 0 /* xRelease */ + 0 /* xRelease */ }; return &sDiskStore; } @@ -8572,7 +7787,7 @@ UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportDiskKvStorage(void) #ifndef UNQLITE_AMALGAMATION #include "unqliteInt.h" #endif -/* +/* * This file implements an in-memory key value storage engine for unQLite. * Note that this storage engine does not support transactions. * @@ -8632,7 +7847,7 @@ static mem_hash_record * MemHashNewRecord( void *pDupData; sxu32 nByte; char *zPtr; - + /* Total number of bytes to alloc */ nByte = sizeof(mem_hash_record) + nKey; /* Allocate a new instance */ @@ -8721,7 +7936,7 @@ static mem_hash_record * MemHashGetEntry( if( pEntry == 0 ){ break; } - if( pEntry->nHash == nHash && pEntry->nKeyLen == (sxu32)nKeyLen && + if( pEntry->nHash == nHash && pEntry->nKeyLen == (sxu32)nKeyLen && pEngine->xCmp(pEntry->pKey,pKey,pEntry->nKeyLen) == 0 ){ return pEntry; } @@ -8751,7 +7966,7 @@ static int MemHashGrowTable(mem_hash_kv_engine *pEngine) n = 0; pEntry = pEngine->pLast; for(;;){ - + /* Loop one */ if( n >= pEngine->nRecord ){ break; @@ -9009,7 +8224,7 @@ static sxu32 MemHashFunc(const void *pSrc,sxu32 nLen) if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; - } + } return nH; } /* Default bucket size */ @@ -9022,7 +8237,7 @@ static sxu32 MemHashFunc(const void *pSrc,sxu32 nLen) static int MemHashInit(unqlite_kv_engine *pKvEngine,int iPageSize) { mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKvEngine; - /* Note that this instance is already zeroed */ + /* Note that this instance is already zeroed */ /* Memory backend */ SyMemBackendInitFromParent(&pEngine->sAlloc,unqliteExportMemBackend()); /* Default hash & comparison function */ @@ -9181,7 +8396,7 @@ static int MemHashAppend( /* Append data to the existing record */ if( nNew > SXU32_HIGH ){ /* Overflow */ - pEngine->pIo->xErr(pEngine->pIo->pHandle,"Append operation will cause data overflow"); + pEngine->pIo->xErr(pEngine->pIo->pHandle,"Append operation will cause data overflow"); return UNQLITE_LIMIT; } nData = (sxu32)nNew; @@ -9226,7 +8441,7 @@ UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportMemKvStorage(void) MemHashCursorDataLength, /* xDataLength */ MemHashCursorData, /* xData */ MemHashCursorReset, /* xReset */ - 0 /* xRelease */ + 0 /* xRelease */ }; return &sMemStore; } @@ -9305,9 +8520,9 @@ UNQLITE_PRIVATE int unqliteOsSectorSize(unqlite_file *id) UNQLITE_PRIVATE int unqliteOsOpen( unqlite_vfs *pVfs, SyMemBackend *pAlloc, - const char *zPath, - unqlite_file **ppOut, - unsigned int flags + const char *zPath, + unqlite_file **ppOut, + unsigned int flags ) { unqlite_file *pFile; @@ -9346,9 +8561,9 @@ UNQLITE_PRIVATE int unqliteOsDelete(unqlite_vfs *pVfs, const char *zPath, int di return pVfs->xDelete(pVfs, zPath, dirSync); } UNQLITE_PRIVATE int unqliteOsAccess( - unqlite_vfs *pVfs, - const char *zPath, - int flags, + unqlite_vfs *pVfs, + const char *zPath, + int flags, int *pResOut ){ return pVfs->xAccess(pVfs, zPath, flags, pResOut); @@ -9375,7 +8590,7 @@ UNQLITE_PRIVATE int unqliteOsAccess( #ifndef UNQLITE_AMALGAMATION #include "unqliteInt.h" #endif -/* +/* * Omit the whole layer from the build if compiling for platforms other than Unix (Linux, BSD, Solaris, OS X, etc.). * Note: Mostly SQLite3 source tree. */ @@ -9408,7 +8623,7 @@ UNQLITE_PRIVATE int unqliteOsAccess( #include #include #include -#if defined(__APPLE__) +#if defined(__APPLE__) # include #endif /* @@ -9433,7 +8648,7 @@ UNQLITE_PRIVATE int unqliteOsAccess( */ #define MAX_PATHNAME 512 /* -** Only set the lastErrno if the error code is a real error and not +** Only set the lastErrno if the error code is a real error and not ** a normal expected return code of UNQLITE_BUSY or UNQLITE_OK */ #define IS_LOCK_ERROR(x) ((x != UNQLITE_OK) && (x != UNQLITE_BUSY)) @@ -9488,11 +8703,11 @@ struct unixFile { /* ** Helper functions to obtain and relinquish the global mutex. The ** global mutex is used to protect the unixInodeInfo and -** vxworksFileId objects used by this file, all of which may be +** vxworksFileId objects used by this file, all of which may be ** shared by multiple threads. ** -** Function unixMutexHeld() is used to assert() that the global mutex -** is held when required. This function is only used as part of assert() +** Function unixMutexHeld() is used to assert() that the global mutex +** is held when required. This function is only used as part of assert() ** statements. e.g. ** ** unixEnterMutex() @@ -9521,44 +8736,44 @@ static void unixLeaveMutex(void){ ** This routine translates a standard POSIX errno code into something ** useful to the clients of the unqlite3 functions. Specifically, it is ** intended to translate a variety of "try again" errors into UNQLITE_BUSY -** and a variety of "please close the file descriptor NOW" errors into +** and a variety of "please close the file descriptor NOW" errors into ** UNQLITE_IOERR -** +** ** Errors during initialization of locks, or file system support for locks, ** should handle ENOLCK, ENOTSUP, EOPNOTSUPP separately. */ static int unqliteErrorFromPosixError(int posixError, int unqliteIOErr) { switch (posixError) { - case 0: + case 0: return UNQLITE_OK; - + case EAGAIN: case ETIMEDOUT: case EBUSY: case EINTR: - case ENOLCK: - /* random NFS retry error, unless during file system support + case ENOLCK: + /* random NFS retry error, unless during file system support * introspection, in which it actually means what it says */ return UNQLITE_BUSY; - - case EACCES: + + case EACCES: /* EACCES is like EAGAIN during locking operations, but not any other time*/ return UNQLITE_BUSY; - - case EPERM: + + case EPERM: return UNQLITE_PERM; - + case EDEADLK: return UNQLITE_IOERR; - + #if EOPNOTSUPP!=ENOTSUP - case EOPNOTSUPP: - /* something went terribly awry, unless during file system support + case EOPNOTSUPP: + /* something went terribly awry, unless during file system support * introspection, in which it actually means what it says */ #endif #ifdef ENOTSUP - case ENOTSUP: - /* invalid fd, unless during file system support introspection, in which + case ENOTSUP: + /* invalid fd, unless during file system support introspection, in which * it actually means what it says */ #endif case EIO: @@ -9571,8 +8786,8 @@ static int unqliteErrorFromPosixError(int posixError, int unqliteIOErr) { case ESTALE: case ENOSYS: /* these should force the client to close the file and reconnect */ - - default: + + default: return unqliteIOErr; } } @@ -9625,7 +8840,7 @@ static int unqliteErrorFromPosixError(int posixError, int unqliteIOErr) { ** cnt>0 means there are cnt shared locks on the file. ** ** Any attempt to lock or unlock a file first checks the locking -** structure. The fcntl() system call is only invoked to set a +** structure. The fcntl() system call is only invoked to set a ** POSIX lock if the internal lock structure transitions between ** a locked and an unlocked state. ** @@ -9710,9 +8925,9 @@ void unqlite_free(void *p) ** cleared and UNQLITE_OK returned. ** ** Otherwise, if an error occurs, then successfully closed file descriptor -** entries are removed from the list, and UNQLITE_IOERR_CLOSE returned. +** entries are removed from the list, and UNQLITE_IOERR_CLOSE returned. ** not deleted and UNQLITE_IOERR_CLOSE returned. -*/ +*/ static int closePendingFds(unixFile *pFile){ int rc = UNQLITE_OK; unixInodeInfo *pInode = pFile->pInode; @@ -9850,7 +9065,7 @@ static int unixCheckReservedLock(unqlite_file *id, int *pResOut){ int reserved = 0; unixFile *pFile = (unixFile*)id; - + unixEnterMutex(); /* Because pFile->pInode is shared across threads */ /* Check if a thread in this process holds such a lock */ @@ -9874,9 +9089,9 @@ static int unixCheckReservedLock(unqlite_file *id, int *pResOut){ reserved = 1; } } - + unixLeaveMutex(); - + *pResOut = reserved; return rc; } @@ -9922,7 +9137,7 @@ static int unixLock(unqlite_file *id, int eFileLock){ ** ** A process may only obtain a RESERVED lock after it has a SHARED lock. ** A RESERVED lock is implemented by grabbing a write-lock on the - ** 'reserved byte'. + ** 'reserved byte'. ** ** A process may only obtain a PENDING lock after it has obtained a ** SHARED lock. A PENDING lock is implemented by obtaining a write-lock @@ -9936,7 +9151,7 @@ static int unixLock(unqlite_file *id, int eFileLock){ ** implemented by obtaining a write-lock on the entire 'shared byte ** range'. Since all other locks require a read-lock on one of the bytes ** within this range, this ensures that no other locks are held on the - ** database. + ** database. ** ** The reason a single byte cannot be used instead of the 'shared byte ** range' is that some versions of unixdows do not support read-locks. By @@ -9965,7 +9180,7 @@ static int unixLock(unqlite_file *id, int eFileLock){ /* If some thread using this PID has a lock via a different unixFile* ** handle that precludes the requested lock, return BUSY. */ - if( (pFile->eFileLock!=pInode->eFileLock && + if( (pFile->eFileLock!=pInode->eFileLock && (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK)) ){ rc = UNQLITE_BUSY; @@ -9976,7 +9191,7 @@ static int unixLock(unqlite_file *id, int eFileLock){ ** has a SHARED or RESERVED lock, then increment reference counts and ** return UNQLITE_OK. */ - if( eFileLock==SHARED_LOCK && + if( eFileLock==SHARED_LOCK && (pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){ pFile->eFileLock = SHARED_LOCK; pInode->nShared++; @@ -9989,7 +9204,7 @@ static int unixLock(unqlite_file *id, int eFileLock){ */ lock.l_len = 1L; lock.l_whence = SEEK_SET; - if( eFileLock==SHARED_LOCK + if( eFileLock==SHARED_LOCK || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLockh, F_SETLK, &lock)!=0 ){ if( s != -1 ){ /* This could happen with a network mount */ - tErrno = errno; - rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR); + tErrno = errno; + rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } @@ -10099,11 +9314,11 @@ static void setPendingFd(unixFile *pFile){ ** ** If the locking level of the file descriptor is already at or below ** the requested locking level, this routine is a no-op. -** +** ** If handleNFSUnlock is true, then on downgrading an EXCLUSIVE_LOCK to SHARED ** the byte range is divided into 2 parts and the first part is unlocked then -** set to a read lock, then the other part is simply unlocked. This works -** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to +** set to a read lock, then the other part is simply unlocked. This works +** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to ** remove the write lock on a region when a read lock is set. */ static int _posixUnlock(unqlite_file *id, int eFileLock, int handleNFSUnlock){ @@ -10118,14 +9333,14 @@ static int _posixUnlock(unqlite_file *id, int eFileLock, int handleNFSUnlock){ return UNQLITE_OK; } unixEnterMutex(); - + h = pFile->h; pInode = pFile->pInode; - + if( pFile->eFileLock>SHARED_LOCK ){ /* downgrading to a shared lock on NFS involves clearing the write lock ** before establishing the readlock - to avoid a race condition we downgrade - ** the lock in 2 blocks, so that part of the range will be covered by a + ** the lock in 2 blocks, so that part of the range will be covered by a ** write lock until the rest is covered by a read lock: ** 1: [WWWWW] ** 2: [....W] @@ -10135,7 +9350,7 @@ static int _posixUnlock(unqlite_file *id, int eFileLock, int handleNFSUnlock){ if( eFileLock==SHARED_LOCK ){ if( handleNFSUnlock ){ off_t divSize = SHARED_SIZE - 1; - + lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST; @@ -10212,7 +9427,7 @@ static int _posixUnlock(unqlite_file *id, int eFileLock, int handleNFSUnlock){ lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = lock.l_len = 0L; - + if( fcntl(h, F_SETLK, &lock)!=(-1) ){ pInode->eFileLock = NO_LOCK; }else{ @@ -10231,7 +9446,7 @@ static int _posixUnlock(unqlite_file *id, int eFileLock, int handleNFSUnlock){ ** was deferred because of outstanding locks. */ pInode->nLock--; - + if( pInode->nLock==0 ){ int rc2 = closePendingFds(pFile); if( rc==UNQLITE_OK ){ @@ -10239,11 +9454,11 @@ static int _posixUnlock(unqlite_file *id, int eFileLock, int handleNFSUnlock){ } } } - + end_unlock: unixLeaveMutex(); - + if( rc==UNQLITE_OK ) pFile->eFileLock = eFileLock; return rc; } @@ -10258,7 +9473,7 @@ static int unixUnlock(unqlite_file *id, int eFileLock){ return _posixUnlock(id, eFileLock, 0); } /* -** This function performs the parts of the "close file" operation +** This function performs the parts of the "close file" operation ** common to all locking schemes. It closes the directory and file ** handles, if they are valid, and sets all fields of the unixFile ** structure to 0. @@ -10300,7 +9515,7 @@ static int unixClose(unqlite_file *id){ if( pFile->pInode && pFile->pInode->nLock ){ /* If there are outstanding locks, do not actually close the file just ** yet because that would clear those locks. Instead, add the file - ** descriptor to pInode->pUnused list. It will be automatically closed + ** descriptor to pInode->pUnused list. It will be automatically closed ** when the last lock is cleared. */ setPendingFd(pFile); @@ -10315,14 +9530,14 @@ static int unixClose(unqlite_file *id){ ******************************************************************************/ /* ** -** The next division contains implementations for all methods of the +** The next division contains implementations for all methods of the ** unqlite_file object other than the locking methods. The locking ** methods were defined in divisions above (one locking method per ** division). Those methods that are common to all locking modes ** are gather together into this division. */ /* -** Seek to the offset passed as the second argument, then read cnt +** Seek to the offset passed as the second argument, then read cnt ** bytes into pBuf. Return the number of bytes actually read. ** ** NB: If you define USE_PREAD or USE_PREAD64, then it might also @@ -10339,19 +9554,19 @@ static int seekAndRead(unixFile *id, unqlite_int64 offset, void *pBuf, int cnt){ #if (!defined(USE_PREAD) && !defined(USE_PREAD64)) unqlite_int64 newOffset; #endif - + #if defined(USE_PREAD) got = pread(id->h, pBuf, cnt, offset); #elif defined(USE_PREAD64) got = pread64(id->h, pBuf, cnt, offset); #else newOffset = lseek(id->h, offset, SEEK_SET); - + if( newOffset!=offset ){ if( newOffset == -1 ){ ((unixFile*)id)->lastErrno = errno; }else{ - ((unixFile*)id)->lastErrno = 0; + ((unixFile*)id)->lastErrno = 0; } return -1; } @@ -10368,14 +9583,14 @@ static int seekAndRead(unixFile *id, unqlite_int64 offset, void *pBuf, int cnt){ ** wrong. */ static int unixRead( - unqlite_file *id, - void *pBuf, + unqlite_file *id, + void *pBuf, unqlite_int64 amt, unqlite_int64 offset ){ unixFile *pFile = (unixFile *)id; int got; - + got = seekAndRead(pFile, offset, pBuf, (int)amt); if( got==(int)amt ){ return UNQLITE_OK; @@ -10401,7 +9616,7 @@ static int seekAndWrite(unixFile *id, unqlite_int64 offset, const void *pBuf, un #if (!defined(USE_PREAD) && !defined(USE_PREAD64)) unqlite_int64 newOffset; #endif - + #if defined(USE_PREAD) got = pwrite(id->h, pBuf, cnt, offset); #elif defined(USE_PREAD64) @@ -10412,7 +9627,7 @@ static int seekAndWrite(unixFile *id, unqlite_int64 offset, const void *pBuf, un if( newOffset == -1 ){ ((unixFile*)id)->lastErrno = errno; }else{ - ((unixFile*)id)->lastErrno = 0; + ((unixFile*)id)->lastErrno = 0; } return -1; } @@ -10428,10 +9643,10 @@ static int seekAndWrite(unixFile *id, unqlite_int64 offset, const void *pBuf, un ** or some other error code on failure. */ static int unixWrite( - unqlite_file *id, - const void *pBuf, + unqlite_file *id, + const void *pBuf, unqlite_int64 amt, - unqlite_int64 offset + unqlite_int64 offset ){ unixFile *pFile = (unixFile*)id; int wrote = 0; @@ -10441,7 +9656,7 @@ static int unixWrite( offset += wrote; pBuf = &((char*)pBuf)[wrote]; } - + if( amt>0 ){ if( wrote<0 ){ /* lastErrno set by seekAndWrite */ @@ -10481,8 +9696,8 @@ static int unixWrite( ** ** SQLite sets the dataOnly flag if the size of the file is unchanged. ** The idea behind dataOnly is that it should only write the file content -** to disk, not the inode. We only set dataOnly if the file size is -** unchanged since the file size is part of the inode. However, +** to disk, not the inode. We only set dataOnly if the file size is +** unchanged since the file size is part of the inode. However, ** Ted Ts'o tells us that fdatasync() will also write the inode if the ** file size has changed. The only real difference between fdatasync() ** and fsync(), Ted tells us, is that fdatasync() will not flush the @@ -10511,11 +9726,11 @@ static int full_fsync(int fd, int fullSync, int dataOnly){ rc = 1; } /* If the FULLFSYNC failed, fall back to attempting an fsync(). - ** It shouldn't be possible for fullfsync to fail on the local + ** It shouldn't be possible for fullfsync to fail on the local ** file system (on OSX), so failure indicates that FULLFSYNC - ** isn't supported for this file system. So, attempt an fsync - ** and (for now) ignore the overhead of a superfluous fcntl call. - ** It'd be better to detect fullfsync support once and avoid + ** isn't supported for this file system. So, attempt an fsync + ** and (for now) ignore the overhead of a superfluous fcntl call. + ** It'd be better to detect fullfsync support once and avoid ** the fcntl call every time sync is called. */ if( rc ) rc = fsync(fd); @@ -10525,7 +9740,7 @@ static int full_fsync(int fd, int fullSync, int dataOnly){ ** so currently we default to the macro that redefines fdatasync to fsync */ rc = fsync(fd); -#else +#else rc = fdatasync(fd); #endif /* ifdef UNQLITE_NO_SYNC elif HAVE_FULLFSYNC */ if( rc!= -1 ){ @@ -10610,9 +9825,9 @@ static int unixTruncate(unqlite_file *id, sxi64 nByte){ static int unixFileSize(unqlite_file *id,sxi64 *pSize){ int rc; struct stat buf; - + rc = fstat(((unixFile*)id)->h, &buf); - + if( rc!=0 ){ ((unixFile*)id)->lastErrno = errno; return UNQLITE_IOERR; @@ -10682,7 +9897,7 @@ static int fillInUnixFile( unixFile *pNew = (unixFile *)pId; int rc = UNQLITE_OK; - /* Parameter isDelete is only used on vxworks. Express this explicitly + /* Parameter isDelete is only used on vxworks. Express this explicitly ** here to prevent compiler warnings about unused parameters. */ SXUNUSED(isDelete); @@ -10693,7 +9908,7 @@ static int fillInUnixFile( pNew->dirfd = dirfd; pNew->fileFlags = 0; pNew->zPath = zFilename; - + unixEnterMutex(); rc = findInodeInfo(pNew, &pNew->pInode); if( rc!=UNQLITE_OK ){ @@ -10719,7 +9934,7 @@ static int fillInUnixFile( h = -1; } unixLeaveMutex(); - + pNew->lastErrno = 0; if( rc!=UNQLITE_OK ){ if( dirfd>=0 ) close(dirfd); /* silent leak if fail, already in error */ @@ -10759,7 +9974,7 @@ static int openDirectory(const char *zFilename, int *pFd){ return (fd>=0?UNQLITE_OK: UNQLITE_IOERR ); } /* -** Search for an unused file descriptor that was opened on the database +** Search for an unused file descriptor that was opened on the database ** file (not a journal or master-journal file) identified by pathname ** zPath with UNQLITE_OPEN_XXX flags matching those passed as the second ** argument to this function. @@ -10768,7 +9983,7 @@ static int openDirectory(const char *zFilename, int *pFd){ ** but the associated file descriptor could not be closed because some ** other file descriptor open on the same file is holding a file-lock. ** Refer to comments in the unixClose() function and the lengthy comment -** describing "Posix Advisory Locking" at the start of this file for +** describing "Posix Advisory Locking" at the start of this file for ** further details. Also, ticket #4018. ** ** If a suitable file descriptor is found, then it is returned. If no @@ -10810,18 +10025,18 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){ ** This function is called by unixOpen() to determine the unix permissions ** to create new files with. If no error occurs, then UNQLITE_OK is returned ** and a value suitable for passing as the third argument to open(2) is -** written to *pMode. If an IO error occurs, an SQLite error code is +** written to *pMode. If an IO error occurs, an SQLite error code is ** returned and the value of *pMode is not modified. ** ** If the file being opened is a temporary file, it is always created with ** the octal permissions 0600 (read/writable by owner only). If the file -** is a database or master journal file, it is created with the permissions +** is a database or master journal file, it is created with the permissions ** mask UNQLITE_DEFAULT_FILE_PERMISSIONS. ** -** Finally, if the file being opened is a WAL or regular journal file, then -** this function queries the file-system for the permissions on the -** corresponding database file and sets *pMode to this value. Whenever -** possible, WAL and journal files are created using the same permissions +** Finally, if the file being opened is a WAL or regular journal file, then +** this function queries the file-system for the permissions on the +** corresponding database file and sets *pMode to this value. Whenever +** possible, WAL and journal files are created using the same permissions ** as the associated database file. */ static int findCreateFileMode( @@ -10840,7 +10055,7 @@ static int findCreateFileMode( } /* ** Open the file zPath. -** +** ** Previously, the SQLite OS layer used three functions in place of this ** one: ** @@ -10851,13 +10066,13 @@ static int findCreateFileMode( ** These calls correspond to the following combinations of flags: ** ** ReadWrite() -> (READWRITE | CREATE) -** ReadOnly() -> (READONLY) +** ReadOnly() -> (READONLY) ** OpenExclusive() -> (READWRITE | CREATE | EXCLUSIVE) ** ** The old OpenExclusive() accepted a boolean argument - "delFlag". If ** true, the file was configured to be automatically deleted when the -** file handle closed. To achieve the same effect using this new -** interface, add the DELETEONCLOSE flag to those specified above for +** file handle closed. To achieve the same effect using this new +** interface, add the DELETEONCLOSE flag to those specified above for ** OpenExclusive(). */ static int unixOpen( @@ -10886,7 +10101,7 @@ static int unixOpen( const char *zName = zPath; SyZero(p,sizeof(unixFile)); - + pUnused = findReusableFd(zName, flags); if( pUnused ){ fd = pUnused->fd; @@ -10897,10 +10112,10 @@ static int unixOpen( } } p->pUnused = pUnused; - + /* Determine the value of the flags parameter passed to POSIX function ** open(). These must be calculated even if open() is not called, as - ** they may be stored as part of the file handle and used by the + ** they may be stored as part of the file handle and used by the ** 'conch file' locking functions later on. */ if( isReadonly ) openFlags |= O_RDONLY; if( isReadWrite ) openFlags |= O_RDWR; @@ -10920,7 +10135,7 @@ static int unixOpen( goto open_finished; } } - + if( p->pUnused ){ p->pUnused->fd = fd; p->pUnused->flags = flags; @@ -10948,7 +10163,7 @@ static int unixOpen( noLock = 0; -#if defined(__APPLE__) +#if defined(__APPLE__) struct statfs fsInfo; if( fstatfs(fd, &fsInfo) == -1 ){ ((unixFile*)pFile)->lastErrno = errno; @@ -10960,7 +10175,7 @@ static int unixOpen( ((unixFile*)pFile)->fsFlags |= UNQLITE_FSFLAGS_IS_MSDOS; } #endif - + rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete); open_finished: if( rc!=UNQLITE_OK ){ @@ -10979,7 +10194,7 @@ static int unixDelete( ){ int rc = UNQLITE_OK; SXUNUSED(NotUsed); - + if( unlink(zPath)==(-1) && errno!=ENOENT ){ return UNQLITE_IOERR; } @@ -11080,9 +10295,9 @@ static int unixAccess( /* ** Turn a relative pathname into a full pathname. The relative path ** is stored as a nul-terminated string in the buffer pointed to by -** zPath. +** zPath. ** -** zOut points to a buffer of at least unqlite_vfs.mxPathname bytes +** zOut points to a buffer of at least unqlite_vfs.mxPathname bytes ** (in this case, MAX_PATHNAME bytes). The full-path is written to ** this buffer before returning. */ @@ -11194,7 +10409,7 @@ UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void) ** Some microsoft compilers lack this definition. */ #ifndef INVALID_FILE_ATTRIBUTES -# define INVALID_FILE_ATTRIBUTES ((DWORD)-1) +# define INVALID_FILE_ATTRIBUTES ((DWORD)-1) #endif /* ** WinCE lacks native support for file locking so we have to fake it @@ -11227,14 +10442,14 @@ struct winFile { int szChunk; /* Chunk size */ #ifdef __WIN_CE__ WCHAR *zDeleteOnClose; /* Name of file to delete when closing */ - HANDLE hMutex; /* Mutex used to control access to shared lock */ + HANDLE hMutex; /* Mutex used to control access to shared lock */ HANDLE hShared; /* Shared memory segment used for locking */ winceLock local; /* Locks obtained by this instance of winFile */ winceLock *shared; /* Global shared lock memory for the file */ #endif }; /* -** Convert a UTF-8 string to microsoft unicode (UTF-16?). +** Convert a UTF-8 string to microsoft unicode (UTF-16?). ** ** Space to hold the returned string is obtained from HeapAlloc(). */ @@ -11280,7 +10495,7 @@ static char *unicodeToUtf8(const WCHAR *zWideFilename){ /* ** Convert an ansi string to microsoft unicode, based on the ** current codepage settings for file apis. -** +** ** Space to hold the returned string is obtained ** from malloc. */ @@ -11325,8 +10540,8 @@ char *unqlite_win32_mbcs_to_utf8(const char *zFilename){ #endif /* -** Move the current position of the file handle passed as the first -** argument to offset iOffset within the file. If successful, return 0. +** Move the current position of the file handle passed as the first +** argument to offset iOffset within the file. If successful, return 0. ** Otherwise, set pFile->lastErrno and return non-zero. */ static int seekWinFile(winFile *pFile, unqlite_int64 iOffset){ @@ -11337,11 +10552,11 @@ static int seekWinFile(winFile *pFile, unqlite_int64 iOffset){ upperBits = (LONG)((iOffset>>32) & 0x7fffffff); lowerBits = (LONG)(iOffset & 0xffffffff); - /* API oddity: If successful, SetFilePointer() returns a dword + /* API oddity: If successful, SetFilePointer() returns a dword ** containing the lower 32-bits of the new file-offset. Or, if it fails, - ** it returns INVALID_SET_FILE_POINTER. However according to MSDN, - ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine - ** whether an error has actually occured, it is also necessary to call + ** it returns INVALID_SET_FILE_POINTER. However according to MSDN, + ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine + ** whether an error has actually occured, it is also necessary to call ** GetLastError(). */ dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); @@ -11796,8 +11011,8 @@ static int winDelete( && (Sleep(100), 1) ); HeapFree(GetProcessHeap(),0,zConverted); - - return ( (rc == INVALID_FILE_ATTRIBUTES) + + return ( (rc == INVALID_FILE_ATTRIBUTES) && (error == ERROR_FILE_NOT_FOUND)) ? UNQLITE_OK : UNQLITE_IOERR; } /* @@ -11821,13 +11036,13 @@ static int winAccess( } SyZero(&sAttrData,sizeof(sAttrData)); if( GetFileAttributesExW((WCHAR*)zConverted, - GetFileExInfoStandard, + GetFileExInfoStandard, &sAttrData) ){ /* For an UNQLITE_ACCESS_EXISTS query, treat a zero-length file ** as if it does not exist. */ if( flags==UNQLITE_ACCESS_EXISTS - && sAttrData.nFileSizeHigh==0 + && sAttrData.nFileSizeHigh==0 && sAttrData.nFileSizeLow==0 ){ attr = INVALID_FILE_ATTRIBUTES; }else{ @@ -11934,7 +11149,7 @@ static int getSectorSize( bytesPerSector = UNQLITE_DEFAULT_SECTOR_SIZE; } } - return (int) bytesPerSector; + return (int) bytesPerSector; } /* ** Sleep for a little while. Return the amount of time slept. @@ -12042,7 +11257,7 @@ static int winOpen( }else{ dwDesiredAccess = GENERIC_READ; } - /* UNQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is + /* UNQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is ** created. */ if( isExclusive ){ @@ -12094,7 +11309,7 @@ static HANDLE OpenReadOnly(LPCWSTR pPath) DWORD dwType = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS; DWORD dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE; DWORD dwAccess = GENERIC_READ; - DWORD dwCreate = OPEN_EXISTING; + DWORD dwCreate = OPEN_EXISTING; HANDLE pHandle; pHandle = CreateFileW(pPath, dwAccess, dwShare, 0, dwCreate, dwType, 0); if( pHandle == INVALID_HANDLE_VALUE){ @@ -12205,8 +11420,8 @@ UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void) ** +---------> READER-------+ | ** | | | ** | V | -** |<-------WRITER_LOCKED--------->| -** | | | +** |<-------WRITER_LOCKED--------->| +** | | | ** | V | ** |<------WRITER_CACHEMOD-------->| ** | | | @@ -12215,7 +11430,7 @@ UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void) ** | | | ** | V | ** +<------WRITER_FINISHED-------->+ -** +** ** OPEN: ** ** The pager starts up in this state. Nothing is guaranteed in this @@ -12228,35 +11443,35 @@ UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void) ** ** READER: ** -** In this state all the requirements for reading the database in +** In this state all the requirements for reading the database in ** rollback mode are met. Unless the pager is (or recently -** was) in exclusive-locking mode, a user-level read transaction is +** was) in exclusive-locking mode, a user-level read transaction is ** open. The database size is known in this state. -** +** ** * A read transaction may be active (but a write-transaction cannot). ** * A SHARED or greater lock is held on the database file. -** * The dbSize variable may be trusted (even if a user-level read +** * The dbSize variable may be trusted (even if a user-level read ** transaction is not active). The dbOrigSize variables ** may not be trusted at this point. -** * Even if a read-transaction is not open, it is guaranteed that +** * Even if a read-transaction is not open, it is guaranteed that ** there is no hot-journal in the file-system. ** ** WRITER_LOCKED: ** ** The pager moves to this state from READER when a write-transaction -** is first opened on the database. In WRITER_LOCKED state, all locks -** required to start a write-transaction are held, but no actual +** is first opened on the database. In WRITER_LOCKED state, all locks +** required to start a write-transaction are held, but no actual ** modifications to the cache or database have taken place. ** -** In rollback mode, a RESERVED or (if the transaction was opened with +** In rollback mode, a RESERVED or (if the transaction was opened with ** EXCLUSIVE flag) EXCLUSIVE lock is obtained on the database file when -** moving to this state, but the journal file is not written to or opened -** to in this state. If the transaction is committed or rolled back while -** in WRITER_LOCKED state, all that is required is to unlock the database +** moving to this state, but the journal file is not written to or opened +** to in this state. If the transaction is committed or rolled back while +** in WRITER_LOCKED state, all that is required is to unlock the database ** file. ** ** * A write transaction is active. -** * If the connection is open in rollback-mode, a RESERVED or greater +** * If the connection is open in rollback-mode, a RESERVED or greater ** lock is held on the database file. ** * The dbSize and dbOrigSize variables are all valid. ** * The contents of the pager cache have not been modified. @@ -12272,7 +11487,7 @@ UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void) ** ** * A write transaction is active. ** * A RESERVED or greater lock is held on the database file. -** * The journal file is open and the first header has been written +** * The journal file is open and the first header has been written ** to it, but the header has not been synced to disk. ** * The contents of the page cache have been modified. ** @@ -12283,7 +11498,7 @@ UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void) ** ** * A write transaction is active. ** * An EXCLUSIVE or greater lock is held on the database file. -** * The journal file is open and the first header has been written +** * The journal file is open and the first header has been written ** and synced to disk. ** * The contents of the page cache have been modified (and possibly ** written to disk). @@ -12293,8 +11508,8 @@ UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void) ** A rollback-mode pager changes to WRITER_FINISHED state from WRITER_DBMOD ** state after the entire transaction has been successfully written into the ** database file. In this state the transaction may be committed simply -** by finalizing the journal file. Once in WRITER_FINISHED state, it is -** not possible to modify the database further. At this point, the upper +** by finalizing the journal file. Once in WRITER_FINISHED state, it is +** not possible to modify the database further. At this point, the upper ** layer must either commit or rollback the transaction. ** ** * A write transaction is active. @@ -12302,8 +11517,8 @@ UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void) ** * All writing and syncing of journal and database data has finished. ** If no error occured, all that remains is to finalize the journal to ** commit the transaction. If an error did occur, the caller will need -** to rollback the transaction. -** +** to rollback the transaction. +** ** */ #define PAGER_OPEN 0 @@ -12324,7 +11539,7 @@ static const unsigned char aJournalMagic[] = { 0xa6, 0xe8, 0xcd, 0x2b, 0x1c, 0x92, 0xdb, 0x9f, }; /* -** The journal header size for this pager. This is usually the same +** The journal header size for this pager. This is usually the same ** size as a single disk sector. See also setSectorSize(). */ #define JOURNAL_HDR_SZ(pPager) (pPager->iSectorSize) @@ -12415,9 +11630,9 @@ struct Pager }; /* Control flags */ #define PAGER_CTRL_COMMIT_ERR 0x001 /* Commit error */ -#define PAGER_CTRL_DIRTY_COMMIT 0x002 /* Dirty commit has been applied */ +#define PAGER_CTRL_DIRTY_COMMIT 0x002 /* Dirty commit has been applied */ /* -** Read a 32-bit integer from the given file descriptor. +** Read a 32-bit integer from the given file descriptor. ** All values are stored on disk as big-endian. */ static int ReadInt32(unqlite_file *pFd,sxu32 *pOut,sxi64 iOfft) @@ -12432,7 +11647,7 @@ static int ReadInt32(unqlite_file *pFd,sxu32 *pOut,sxi64 iOfft) return UNQLITE_OK; } /* -** Read a 64-bit integer from the given file descriptor. +** Read a 64-bit integer from the given file descriptor. ** All values are stored on disk as big-endian. */ static int ReadInt64(unqlite_file *pFd,sxu64 *pOut,sxi64 iOfft) @@ -12469,7 +11684,7 @@ static int WriteInt64(unqlite_file *pFd,sxu64 iNum,sxi64 iOfft) return rc; } /* -** The maximum allowed sector size. 64KiB. If the xSectorsize() method +** The maximum allowed sector size. 64KiB. If the xSectorsize() method ** returns a value larger than this, then MAX_SECTOR_SIZE is used instead. ** This could conceivably cause corruption following a power failure on ** such a system. This is currently an undocumented limit. @@ -12529,7 +11744,7 @@ static Page * pager_fetch_page(Pager *pPager,pgno page_num) static Page * pager_alloc_page(Pager *pPager,pgno num_page) { Page *pNew; - + pNew = (Page *)SyMemBackendPoolAlloc(pPager->pAllocator,sizeof(Page)+pPager->iPageSize); if( pNew == 0 ){ return 0; @@ -12942,7 +12157,7 @@ static Page * pager_get_hot_pages(Pager *pPager) ** - 8 bytes: Initial database page count. ** - 4 bytes: Sector size used by the process that wrote this journal. ** - 4 bytes: Database page size. -** +** ** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space. */ /* @@ -13020,21 +12235,21 @@ static int pager_read_journal_header( } /* Check that the values read from the page-size and sector-size fields ** are within range. To be 'in range', both values need to be a power - ** of two greater than or equal to 512 or 32, and not greater than their + ** of two greater than or equal to 512 or 32, and not greater than their ** respective compile time maximum limits. */ if( iPageSize < UNQLITE_MIN_PAGE_SIZE || iSectorSize<32 || iPageSize > UNQLITE_MAX_PAGE_SIZE || iSectorSize>MAX_SECTOR_SIZE - || ((iPageSize-1)&iPageSize)!=0 || ((iSectorSize-1)&iSectorSize)!=0 + || ((iPageSize-1)&iPageSize)!=0 || ((iSectorSize-1)&iSectorSize)!=0 ){ - /* If the either the page-size or sector-size in the journal-header is - ** invalid, then the process that wrote the journal-header must have - ** crashed before the header was synced. In this case stop reading + /* If the either the page-size or sector-size in the journal-header is + ** invalid, then the process that wrote the journal-header must have + ** crashed before the header was synced. In this case stop reading ** the journal file here. */ return UNQLITE_DONE; } - /* Update the assumed sector-size to match the value used by + /* Update the assumed sector-size to match the value used by ** the process that created this journal. If this journal was ** created by a process other than this one, then this routine ** is being called from within pager_playback(). The local value @@ -13075,10 +12290,10 @@ static int pager_write_journal_header(Pager *pPager,unsigned char *zBuf) } /* ** Parameter aData must point to a buffer of pPager->pageSize bytes -** of data. Compute and return a checksum based ont the contents of the +** of data. Compute and return a checksum based ont the contents of the ** page of data and the current value of pPager->cksumInit. ** -** This is not a real checksum. It is really just the sum of the +** This is not a real checksum. It is really just the sum of the ** random initial value (pPager->cksumInit) and every 200th byte ** of the page data, starting with byte offset (pPager->pageSize%200). ** Each byte is interpreted as an 8-bit unsigned integer. @@ -13086,8 +12301,8 @@ static int pager_write_journal_header(Pager *pPager,unsigned char *zBuf) ** Changing the formula used to compute this checksum results in an ** incompatible journal file format. ** -** If journal corruption occurs due to a power failure, the most likely -** scenario is that one end or the other of the record will be changed. +** If journal corruption occurs due to a power failure, the most likely +** scenario is that one end or the other of the record will be changed. ** It is much less likely that the two ends of the journal record will be ** correct and the middle be corrupt. Thus, this "checksum" scheme, ** though fast and simple, catches the mostly likely kind of corruption. @@ -13148,14 +12363,14 @@ static int pager_play_back_one_page(Pager *pPager,sxi64 *pOfft,unsigned char *zT } /* ** Playback the journal and thus restore the database file to -** the state it was in before we started making changes. +** the state it was in before we started making changes. ** -** The journal file format is as follows: +** The journal file format is as follows: ** ** (1) 8 byte prefix. A copy of aJournalMagic[]. ** (2) 4 byte big-endian integer which is the number of valid page records -** in the journal. -** (3) 4 byte big-endian integer which is the initial value for the +** in the journal. +** (3) 4 byte big-endian integer which is the initial value for the ** sanity checksum. ** (4) 8 byte integer which is the number of pages to truncate the ** database to during a rollback. @@ -13218,7 +12433,7 @@ static int pager_playback(Pager *pPager) return UNQLITE_NOMEM; } SyZero((void *)zTmp,(sxu32)pPager->iPageSize); - /* Copy original pages out of the journal and back into the + /* Copy original pages out of the journal and back into the ** database file and/or page cache. */ iOfft = pPager->iJournalOfft; @@ -13250,7 +12465,7 @@ static int pager_playback(Pager *pPager) ** succeeds, set the Pager.iLock variable to match the (attempted) new lock. ** ** Except, if Pager.iLock is set to NO_LOCK when this function is -** called, do not modify it. See the comment above the #define of +** called, do not modify it. See the comment above the #define of ** NO_LOCK for an explanation of this. */ static int pager_unlock_db(Pager *pPager, int eLock) @@ -13265,11 +12480,11 @@ static int pager_unlock_db(Pager *pPager, int eLock) /* ** Lock the database file to level eLock, which must be either SHARED_LOCK, ** RESERVED_LOCK or EXCLUSIVE_LOCK. If the caller is successful, set the -** Pager.eLock variable to the new locking state. +** Pager.eLock variable to the new locking state. ** -** Except, if Pager.eLock is set to NO_LOCK when this function is -** called, do not modify it unless the new locking state is EXCLUSIVE_LOCK. -** See the comment above the #define of NO_LOCK for an explanation +** Except, if Pager.eLock is set to NO_LOCK when this function is +** called, do not modify it unless the new locking state is EXCLUSIVE_LOCK. +** See the comment above the #define of NO_LOCK for an explanation ** of this. */ static int pager_lock_db(Pager *pPager, int eLock){ @@ -13291,13 +12506,13 @@ static int pager_lock_db(Pager *pPager, int eLock){ ** a similar or greater lock is already held, this function is a no-op ** (returning UNQLITE_OK immediately). ** -** Otherwise, attempt to obtain the lock using unqliteOsLock(). Invoke -** the busy callback if the lock is currently not available. Repeat -** until the busy callback returns false or until the attempt to +** Otherwise, attempt to obtain the lock using unqliteOsLock(). Invoke +** the busy callback if the lock is currently not available. Repeat +** until the busy callback returns false or until the attempt to ** obtain the lock succeeds. ** ** Return UNQLITE_OK on success and an error code if we cannot obtain -** the lock. If the lock is obtained successfully, set the Pager.state +** the lock. If the lock is obtained successfully, set the Pager.state ** variable to locktype before returning. */ static int pager_wait_on_lock(Pager *pPager, int locktype){ @@ -13310,7 +12525,7 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){ /* ** This function is called after transitioning from PAGER_OPEN to ** PAGER_SHARED state. It tests if there is a hot journal present in -** the file-system for the given pager. A hot journal is one that +** the file-system for the given pager. A hot journal is one that ** needs to be played back. According to this function, a hot-journal ** file exists if the following criteria are met: ** @@ -13325,7 +12540,7 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){ ** just deleted using OsDelete, *pExists is set to 0 and UNQLITE_OK ** is returned. ** -** If a hot-journal file is found to exist, *pExists is set to 1 and +** If a hot-journal file is found to exist, *pExists is set to 1 and ** UNQLITE_OK returned. If no hot-journal file is present, *pExists is ** set to 0 and UNQLITE_OK returned. If an IO error occurs while trying ** to determine whether or not a hot-journal file exists, the IO error @@ -13343,7 +12558,7 @@ static int pager_has_hot_journal(Pager *pPager, int *pExists) int locked = 0; /* True if some process holds a RESERVED lock */ /* Race condition here: Another process might have been holding the - ** the RESERVED lock and have a journal open at the unqliteOsAccess() + ** the RESERVED lock and have a journal open at the unqliteOsAccess() ** call above, but then delete the journal and drop the lock before ** we get to the following unqliteOsCheckReservedLock() call. If that ** is the case, this routine might think there is a hot journal when @@ -13353,9 +12568,9 @@ static int pager_has_hot_journal(Pager *pPager, int *pExists) rc = unqliteOsCheckReservedLock(pPager->pfd, &locked); if( rc==UNQLITE_OK && !locked ){ sxi64 n = 0; /* Size of db file in bytes */ - + /* Check the size of the database file. If it consists of 0 pages, - ** then delete the journal file. See the header comment above for + ** then delete the journal file. See the header comment above for ** the reasoning here. Delete the obsolete journal file under ** a RESERVED lock to avoid race conditions. */ @@ -13404,12 +12619,12 @@ static int pager_journal_rollback(Pager *pPager,int check_hot) ** important that a RESERVED lock is not obtained on the way to the ** EXCLUSIVE lock. If it were, another process might open the ** database file, detect the RESERVED lock, and conclude that the - ** database is safe to read while this process is still rolling the + ** database is safe to read while this process is still rolling the ** hot-journal back. - ** + ** ** Because the intermediate RESERVED lock is not requested, any - ** other process attempting to access the database file will get to - ** this point in the code and fail to obtain its own EXCLUSIVE lock + ** other process attempting to access the database file will get to + ** this point in the code and fail to obtain its own EXCLUSIVE lock ** on the database file. ** ** Unless the pager is in locking_mode=exclusive mode, the lock is @@ -13515,12 +12730,12 @@ static int pager_extract_header(Pager *pPager,const unsigned char *zRaw,sxu32 nB zRaw += 4; /* 4 byte page size */ /* Check that the values read from the page-size and sector-size fields ** are within range. To be 'in range', both values need to be a power - ** of two greater than or equal to 512 or 32, and not greater than their + ** of two greater than or equal to 512 or 32, and not greater than their ** respective compile time maximum limits. */ if( pPager->iPageSizeiSectorSize<32 || pPager->iPageSize>UNQLITE_MAX_PAGE_SIZE || pPager->iSectorSize>MAX_SECTOR_SIZE - || ((pPager->iPageSize<-1)&pPager->iPageSize)!=0 || ((pPager->iSectorSize-1)&pPager->iSectorSize)!=0 + || ((pPager->iPageSize<-1)&pPager->iPageSize)!=0 || ((pPager->iSectorSize-1)&pPager->iSectorSize)!=0 ){ return UNQLITE_CORRUPT; } @@ -13639,10 +12854,10 @@ static int pager_create_header(Pager *pPager) ** on the database file), then an attempt is made to obtain a ** SHARED lock on the database file. Immediately after obtaining ** the SHARED lock, the file-system is checked for a hot-journal, -** which is played back if present. +** which is played back if present. ** -** If everything is successful, UNQLITE_OK is returned. If an IO error -** occurs while locking the database, checking for a hot-journal file or +** If everything is successful, UNQLITE_OK is returned. If an IO error +** occurs while locking the database, checking for a hot-journal file or ** rolling back a journal file, the IO error code is returned. */ static int pager_shared_lock(Pager *pPager) @@ -13710,12 +12925,12 @@ static int pager_shared_lock(Pager *pPager) } }else if( rc == UNQLITE_BUSY ){ unqliteGenError(pPager->pDb,"Another process or thread have a reserved or exclusive lock on this database"); - } + } } return rc; } /* -** Begin a write-transaction on the specified pager object. If a +** Begin a write-transaction on the specified pager object. If a ** write-transaction has already been opened, this function is a no-op. */ UNQLITE_PRIVATE int unqlitePagerBegin(Pager *pPager) @@ -13768,7 +12983,7 @@ UNQLITE_PRIVATE int unqlitePagerBegin(Pager *pPager) } /* ** This function is called at the start of every write transaction. -** There must already be a RESERVED or EXCLUSIVE lock on the database +** There must already be a RESERVED or EXCLUSIVE lock on the database ** file when this routine is called. ** */ @@ -13870,7 +13085,7 @@ static int unqliteFinalizeJournal(Pager *pPager,int *pRetry,int close_jrnl) return UNQLITE_OK; } /* - * Mark a single data page as writeable. The page is written into the + * Mark a single data page as writeable. The page is written into the * main journal as required. */ static int page_write(Pager *pPager,Page *pPage) @@ -13912,7 +13127,7 @@ static int page_write(Pager *pPager,Page *pPage) unqliteGenError(pPager->pDb,"Database maximum page limit (64-bit) reached"); return UNQLITE_LIMIT; } - } + } return UNQLITE_OK; } /* @@ -14047,7 +13262,7 @@ static int pager_commit_phase1(Pager *pPager) } } if( pPager->iFlags & PAGER_CTRL_DIRTY_COMMIT ){ - /* Synce the database first if a dirty commit have been applied */ + /* Sync the database first if a dirty commit have been applied */ unqliteOsSync(pPager->pfd,UNQLITE_SYNC_NORMAL); } /* Write the dirty pages */ @@ -14059,6 +13274,19 @@ static int pager_commit_phase1(Pager *pPager) unqliteGenError(pPager->pDb,"IO error while writing dirty pages, rollback your database"); return rc; } + /* release all pages */ + { + Page *p; + + while (1) { + p = pPager->pAll; + if (p == 0) { + break; + } + pager_unlink_page(pPager, p); + pager_release_page(pPager, p); + } + } /* If the file on disk is not the same size as the database image, * then use unqliteOsTruncate to grow or shrink the file here. */ @@ -14086,7 +13314,7 @@ static int pager_commit_phase2(Pager *pPager) /* Finally, unlink the journal file */ unqliteOsDelete(pPager->pVfs,pPager->zJournal,1); } - /* Downgrade to shraed lock */ + /* Downgrade to shared lock */ pager_unlock_db(pPager,SHARED_LOCK); pPager->iState = PAGER_READER; if( pPager->pVec ){ @@ -14148,7 +13376,7 @@ static int pager_dirty_commit(Pager *pPager) ** This routine ensures that: ** ** * the journal is synced, -** * all dirty pages are written to the database file, +** * all dirty pages are written to the database file, ** * the database file is truncated (if required), and ** * the database file synced. ** * the journal file is deleted. @@ -14250,20 +13478,20 @@ static int pager_reset_state(Pager *pPager,int bResetKvEngine) return UNQLITE_OK; } /* -** If a write transaction is open, then all changes made within the +** If a write transaction is open, then all changes made within the ** transaction are reverted and the current write-transaction is closed. ** The pager falls back to PAGER_READER state if successful. ** ** Otherwise, in rollback mode, this function performs two functions: ** -** 1) It rolls back the journal file, restoring all database file and +** 1) It rolls back the journal file, restoring all database file and ** in-memory cache pages to the state they were in when the transaction ** was opened, and ** ** 2) It finalizes the journal file, so that it is not used for hot ** rollback at any point in the future (i.e. deletion). ** -** Finalization of the journal file (task 2) is only performed if the +** Finalization of the journal file (task 2) is only performed if the ** rollback is successful. ** */ @@ -14331,9 +13559,9 @@ static int unqlitePagerDontWrite(unqlite_page *pMyPage) return UNQLITE_OK; } /* -** Mark a data page as writeable. This routine must be called before -** making changes to a page. The caller must check the return value -** of this function and be careful not to change any page data unless +** Mark a data page as writeable. This routine must be called before +** making changes to a page. The caller must check the return value +** of this function and be careful not to change any page data unless ** this routine returns UNQLITE_OK. */ static int unqlitePageWrite(unqlite_page *pMyPage) @@ -14371,10 +13599,10 @@ static int unqlitePageWrite(unqlite_page *pMyPage) } /* ** Acquire a reference to page number pgno in pager pPager (a page -** reference has type unqlite_page*). If the requested reference is +** reference has type unqlite_page*). If the requested reference is ** successfully obtained, it is copied to *ppPage and UNQLITE_OK returned. ** -** If the requested page is already in the cache, it is returned. +** If the requested page is already in the cache, it is returned. ** Otherwise, a new page object is allocated and populated with data ** read from the database file. */ @@ -14438,11 +13666,11 @@ static int unqliteInMemory(const char *zFilename) return TRUE; } n = SyStrlen(zFilename); - if( n == sizeof(":mem:") - 1 && + if( n == sizeof(":mem:") - 1 && SyStrnicmp(zFilename,":mem:",sizeof(":mem:") - 1) == 0 ){ return TRUE; } - if( n == sizeof(":memory:") - 1 && + if( n == sizeof(":memory:") - 1 && SyStrnicmp(zFilename,":memory:",sizeof(":memory:") - 1) == 0 ){ return TRUE; } @@ -14783,7 +14011,7 @@ UNQLITE_PRIVATE sxu32 unqlitePagerRandomNum(Pager *pPager) return iNum; } /* Exported KV IO Methods */ -/* +/* * Refer to [unqlitePagerAcquire()] */ static int unqliteKvIoPageGet(unqlite_kv_handle pHandle,pgno iNum,unqlite_page **ppPage) @@ -14792,7 +14020,7 @@ static int unqliteKvIoPageGet(unqlite_kv_handle pHandle,pgno iNum,unqlite_page * rc = unqlitePagerAcquire((Pager *)pHandle,iNum,ppPage,0,0); return rc; } -/* +/* * Refer to [unqlitePagerAcquire()] */ static int unqliteKvIoPageLookup(unqlite_kv_handle pHandle,pgno iNum,unqlite_page **ppPage) @@ -14801,14 +14029,14 @@ static int unqliteKvIoPageLookup(unqlite_kv_handle pHandle,pgno iNum,unqlite_pag rc = unqlitePagerAcquire((Pager *)pHandle,iNum,ppPage,1,0); return rc; } -/* +/* * Refer to [unqlitePagerAcquire()] */ static int unqliteKvIoNewPage(unqlite_kv_handle pHandle,unqlite_page **ppPage) { Pager *pPager = (Pager *)pHandle; int rc; - /* + /* * Acquire a reader-lock first so that pPager->dbSize get initialized. */ rc = pager_shared_lock(pPager); @@ -14817,7 +14045,7 @@ static int unqliteKvIoNewPage(unqlite_kv_handle pHandle,unqlite_page **ppPage) } return rc; } -/* +/* * Refer to [unqlitePageWrite()] */ static int unqliteKvIopageWrite(unqlite_page *pPage) @@ -14830,7 +14058,7 @@ static int unqliteKvIopageWrite(unqlite_page *pPage) rc = unqlitePageWrite(pPage); return rc; } -/* +/* * Refer to [unqlitePagerDontWrite()] */ static int unqliteKvIoPageDontWrite(unqlite_page *pPage) @@ -14843,7 +14071,7 @@ static int unqliteKvIoPageDontWrite(unqlite_page *pPage) rc = unqlitePagerDontWrite(pPage); return rc; } -/* +/* * Refer to [unqliteBitvecSet()] */ static int unqliteKvIoPageDontJournal(unqlite_page *pRaw) @@ -14862,13 +14090,13 @@ static int unqliteKvIoPageDontJournal(unqlite_page *pRaw) } return UNQLITE_OK; } -/* +/* * Do not add a page to the hot dirty list. */ static int unqliteKvIoPageDontMakeHot(unqlite_page *pRaw) { Page *pPage = (Page *)pRaw; - + if( pPage == 0 ){ /* TICKET 1433-0348 */ return UNQLITE_OK; @@ -14896,7 +14124,7 @@ static int unqliteKvIoPageDontMakeHot(unqlite_page *pRaw) return UNQLITE_OK; } -/* +/* * Refer to [page_ref()] */ static int unqliteKvIopage_ref(unqlite_page *pPage) @@ -14906,7 +14134,7 @@ static int unqliteKvIopage_ref(unqlite_page *pPage) } return UNQLITE_OK; } -/* +/* * Refer to [page_unref()] */ static int unqliteKvIoPageUnRef(unqlite_page *pPage) @@ -14916,28 +14144,28 @@ static int unqliteKvIoPageUnRef(unqlite_page *pPage) } return UNQLITE_OK; } -/* +/* * Refer to the declaration of the [Pager] structure */ static int unqliteKvIoReadOnly(unqlite_kv_handle pHandle) { return ((Pager *)pHandle)->is_rdonly; } -/* +/* * Refer to the declaration of the [Pager] structure */ static int unqliteKvIoPageSize(unqlite_kv_handle pHandle) { return ((Pager *)pHandle)->iPageSize; } -/* +/* * Refer to the declaration of the [Pager] structure */ static unsigned char * unqliteKvIoTempPage(unqlite_kv_handle pHandle) { return ((Pager *)pHandle)->zTmpPage; } -/* +/* * Set a page unpin callback. * Refer to the declaration of the [Pager] structure */ @@ -14946,7 +14174,7 @@ static void unqliteKvIoPageUnpin(unqlite_kv_handle pHandle,void (*xPageUnpin)(vo Pager *pPager = (Pager *)pHandle; pPager->xPageUnpin = xPageUnpin; } -/* +/* * Set a page reload callback. * Refer to the declaration of the [Pager] structure */ @@ -14955,7 +14183,7 @@ static void unqliteKvIoPageReload(unqlite_kv_handle pHandle,void (*xPageReload)( Pager *pPager = (Pager *)pHandle; pPager->xPageReload = xPageReload; } -/* +/* * Log an error. * Refer to the declaration of the [Pager] structure */ @@ -14971,12 +14199,12 @@ static int pager_kv_io_init(Pager *pPager,unqlite_kv_methods *pMethods,unqlite_k { pIo->pHandle = pPager; pIo->pMethods = pMethods; - + pIo->xGet = unqliteKvIoPageGet; pIo->xLookup = unqliteKvIoPageLookup; pIo->xNew = unqliteKvIoNewPage; - - pIo->xWrite = unqliteKvIopageWrite; + + pIo->xWrite = unqliteKvIopageWrite; pIo->xDontWrite = unqliteKvIoPageDontWrite; pIo->xDontJournal = unqliteKvIoPageDontJournal; pIo->xDontMkHot = unqliteKvIoPageDontMakeHot; @@ -15002,7 +14230,7 @@ static int pager_kv_io_init(Pager *pPager,unqlite_kv_methods *pMethods,unqlite_k * Symisc UnQLite-KV: A Transactional Key/Value Store Database Engine. * Copyright (C) 2016, Symisc Systems http://unqlite.org/ * Copyright (C) 2014, Yuras Shumovich - * Version 1.1 + * Version 1.1.2 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net @@ -15031,7 +14259,7 @@ static int pager_kv_io_init(Pager *pPager,unqlite_kv_methods *pMethods,unqlite_k * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/unqlite.h b/src/unqlite.h index 89dba14..1d6e44b 100644 --- a/src/unqlite.h +++ b/src/unqlite.h @@ -1,11 +1,15 @@ -/* This file was automatically generated. Do not edit (Except for compile time directives)! */ +/* This file was automatically generated. Do not edit (Except for compile time directives)! */ #ifndef _UNQLITE_H_ #define _UNQLITE_H_ +/* Make sure we can call this stuff from C++ */ +#ifdef __cplusplus +extern "C" { +#endif /* * Symisc UnQLite-KV: A Transactional Key/Value Database Engine. * Copyright (C) 2016, Symisc Systems http://unqlite.org/ * Copyright (C) 2014, Yuras Shumovich - * Version 1.1 + * Version 1.1.2 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net @@ -15,7 +19,7 @@ * http://unqlite.org/licensing.html */ /* - * Copyright (C) 2016 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine ]. + * Copyright (C) 2012, 2018 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine ]. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,12 +38,12 @@ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - /* $SymiscID: unqlite.h v1.1 Win10 2016-12-02 00:04:12 stable $ */ + /* $SymiscID: unqlite.h v1.2 Win10 2108-01-21 23:59:12 stable $ */ #include /* needed for the definition of va_list */ /* * Compile time engine version, signature, identification in the symisc source tree @@ -55,13 +59,13 @@ * version number and Y is the minor version number and Z is the release * number. */ -#define UNQLITE_VERSION "1.1.1" +#define UNQLITE_VERSION "1.1.2" /* * The UNQLITE_VERSION_NUMBER C preprocessor macro resolves to an integer * with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same * numbers used in [UNQLITE_VERSION]. */ -#define UNQLITE_VERSION_NUMBER 1001001 +#define UNQLITE_VERSION_NUMBER 1001002 /* * The UNQLITE_SIG C preprocessor macro evaluates to a string * literal which is the public signature of the unqlite engine. @@ -69,7 +73,7 @@ * generated Server MIME header as follows: * Server: YourWebServer/x.x unqlite/x.x.x \r\n */ -#define UNQLITE_SIG "unqlite/1.1.1" +#define UNQLITE_SIG "unqlite/1.1.2" /* * UnQLite identification in the Symisc source tree: * Each particular check-in of a particular software released @@ -86,7 +90,7 @@ * licensing@symisc.net * contact@symisc.net */ -#define UNQLITE_COPYRIGHT "Copyright (C) Symisc Systems, S.U.A.R.L [Mrad Chems Eddine ] 2016, http://unqlite.org/" +#define UNQLITE_COPYRIGHT "Copyright (C) Symisc Systems, S.U.A.R.L [Mrad Chems Eddine ] 2012-2018, http://unqlite.org/" /* Forward declaration to public objects */ typedef struct unqlite_io_methods unqlite_io_methods; @@ -118,7 +122,7 @@ typedef struct unqlite unqlite; * at run-time using the unqlite_lib_config() interface together with one of these verbs: * UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE * UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI - * Platforms others than Windows and UNIX systems must install their own mutex subsystem via + * Platforms others than Windows and UNIX systems must install their own mutex subsystem via * unqlite_lib_config() with a configuration verb set to UNQLITE_LIB_CONFIG_USER_MUTEX. * Otherwise the library is not threadsafe. * Note that you must link UnQLite with the POSIX threads library under UNIX systems (i.e: -lpthread). @@ -130,7 +134,7 @@ typedef struct unqlite unqlite; /* Windows Systems */ #if !defined(__WINNT__) #define __WINNT__ -#endif +#endif /* * Determine if we are dealing with WindowsCE - which has a much * reduced API. @@ -229,7 +233,7 @@ struct Sytm (pSYTM)->tm_zone = 0; /* Dynamic memory allocation methods. */ -struct SyMemMethods +struct SyMemMethods { void * (*xAlloc)(unsigned int); /* [Required:] Allocate a memory chunk */ void * (*xRealloc)(void *, unsigned int); /* [Required:] Re-allocate a memory chunk */ @@ -242,7 +246,7 @@ struct SyMemMethods /* Out of memory callback signature. */ typedef int (*ProcMemError)(void *); /* Mutex methods. */ -struct SyMutexMethods +struct SyMutexMethods { int (*xGlobalInit)(void); /* [Optional:] Global mutex initialization */ void (*xGlobalRelease)(void); /* [Optional:] Global Release callback () */ @@ -260,7 +264,7 @@ struct SyMutexMethods #define SX_APIEXPORT #endif /* Standard return values from Symisc public interfaces */ -#define SXRET_OK 0 /* Not an error */ +#define SXRET_OK 0 /* Not an error */ #define SXERR_MEM (-1) /* Out of memory */ #define SXERR_IO (-2) /* IO error */ #define SXERR_EMPTY (-3) /* Empty field */ @@ -280,7 +284,7 @@ struct SyMutexMethods #define SXERR_NOTIMPLEMENTED (-17) /* Operation not implemented */ #define SXERR_EOF (-18) /* End of input */ #define SXERR_PERM (-19) /* Permission error */ -#define SXERR_NOOP (-20) /* No-op */ +#define SXERR_NOOP (-20) /* No-op */ #define SXERR_FORMAT (-21) /* Invalid format */ #define SXERR_NEXT (-22) /* Not an error */ #define SXERR_OS (-23) /* System call return an error */ @@ -296,8 +300,8 @@ struct SyMutexMethods #define SXERR_RETRY (-33) /* Retry your call */ #define SXERR_IGNORE (-63) /* Ignore */ #endif /* SYMISC_PUBLIC_DEFS */ -/* - * Marker for exported interfaces. +/* + * Marker for exported interfaces. */ #define UNQLITE_APIEXPORT SX_APIEXPORT /* @@ -317,7 +321,7 @@ typedef sxi64 unqlite_int64; #define UNQLITE_ABORT SXERR_ABORT /* Another thread have released this instance */ #define UNQLITE_IOERR SXERR_IO /* IO error */ #define UNQLITE_CORRUPT SXERR_CORRUPT /* Corrupt pointer */ -#define UNQLITE_LOCKED SXERR_LOCKED /* Forbidden Operation */ +#define UNQLITE_LOCKED SXERR_LOCKED /* Forbidden Operation */ #define UNQLITE_BUSY SXERR_BUSY /* The database file is locked */ #define UNQLITE_DONE SXERR_DONE /* Operation done */ #define UNQLITE_PERM SXERR_PERM /* Permission error */ @@ -347,7 +351,7 @@ typedef sxi64 unqlite_int64; * Each options require a variable number of arguments. * The [unqlite_config()] interface will return UNQLITE_OK on success, any other * return value indicates failure. - * For a full discussion on the configuration verbs and their expected + * For a full discussion on the configuration verbs and their expected * parameters, please refer to this page: * http://unqlite.org/c_api/unqlite_config.html */ @@ -383,8 +387,8 @@ typedef sxi64 unqlite_int64; * value indicates failure. * Notes: * The default configuration is recommended for most applications and so the call to - * [unqlite_lib_config()] is usually not necessary. It is provided to support rare - * applications with unusual needs. + * [unqlite_lib_config()] is usually not necessary. It is provided to support rare + * applications with unusual needs. * The [unqlite_lib_config()] interface is not threadsafe. The application must insure that * no other [unqlite_*()] interfaces are invoked by other threads while [unqlite_lib_config()] * is running. Furthermore, [unqlite_lib_config()] may only be invoked prior to library @@ -395,11 +399,11 @@ typedef sxi64 unqlite_int64; * refer to this page: * http://unqlite.org/c_api/unqlite_lib.html */ -#define UNQLITE_LIB_CONFIG_USER_MALLOC 1 /* ONE ARGUMENT: const SyMemMethods *pMemMethods */ +#define UNQLITE_LIB_CONFIG_USER_MALLOC 1 /* ONE ARGUMENT: const SyMemMethods *pMemMethods */ #define UNQLITE_LIB_CONFIG_MEM_ERR_CALLBACK 2 /* TWO ARGUMENTS: int (*xMemError)(void *), void *pUserData */ -#define UNQLITE_LIB_CONFIG_USER_MUTEX 3 /* ONE ARGUMENT: const SyMutexMethods *pMutexMethods */ -#define UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE 4 /* NO ARGUMENTS */ -#define UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI 5 /* NO ARGUMENTS */ +#define UNQLITE_LIB_CONFIG_USER_MUTEX 3 /* ONE ARGUMENT: const SyMutexMethods *pMutexMethods */ +#define UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE 4 /* NO ARGUMENTS */ +#define UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI 5 /* NO ARGUMENTS */ #define UNQLITE_LIB_CONFIG_VFS 6 /* ONE ARGUMENT: const unqlite_vfs *pVfs */ #define UNQLITE_LIB_CONFIG_STORAGE_ENGINE 7 /* ONE ARGUMENT: unqlite_kv_methods *pStorage */ #define UNQLITE_LIB_CONFIG_PAGE_SIZE 8 /* ONE ARGUMENT: int iPageSize */ @@ -466,7 +470,7 @@ struct unqlite_file { * This object defines the methods used to perform various operations * against the open file represented by the [unqlite_file] object. * - * If the xOpen method sets the unqlite_file.pMethods element + * If the xOpen method sets the unqlite_file.pMethods element * to a non-NULL pointer, then the unqlite_io_methods.xClose method * may be invoked even if the xOpen reported that it failed. The * only way to prevent a call to xClose following a failed xOpen @@ -485,13 +489,13 @@ struct unqlite_file { * UNQLITE_LOCK_RESERVED * UNQLITE_LOCK_PENDING * UNQLITE_LOCK_EXCLUSIVE - * + * * xLock() increases the lock. xUnlock() decreases the lock. * The xCheckReservedLock() method checks whether any database connection, * either in this process or in some other process, is holding a RESERVED, * PENDING, or EXCLUSIVE lock on the file. It returns true if such a lock exists * and false otherwise. - * + * * The xSectorSize() method returns the sector size of the device that underlies * the file. The sector size is the minimum write that can be performed without * disturbing other bytes in the file. @@ -529,11 +533,11 @@ struct unqlite_io_methods { * must register their own vfs in order to be able to use the UnQLite library. * * The value of the iVersion field is initially 1 but may be larger in - * future versions of UnQLite. + * future versions of UnQLite. * * The szOsFile field is the size of the subclassed [unqlite_file] structure * used by this VFS. mxPathname is the maximum length of a pathname in this VFS. - * + * * At least szOsFile bytes of memory are allocated by UnQLite to hold the [unqlite_file] * structure passed as the third argument to xOpen. The xOpen method does not have to * allocate the structure; it should just fill it in. Note that the xOpen method must @@ -580,8 +584,8 @@ struct unqlite_vfs { * UnQLite. */ #define UNQLITE_ACCESS_EXISTS 0 -#define UNQLITE_ACCESS_READWRITE 1 -#define UNQLITE_ACCESS_READ 2 +#define UNQLITE_ACCESS_READWRITE 1 +#define UNQLITE_ACCESS_READ 2 /* * The type used to represent a page number. The first page in a file * is called page 1. 0 is used to represent "not a page". @@ -629,7 +633,7 @@ struct unqlite_kv_io int (*xPageSize)(unqlite_kv_handle); int (*xReadOnly)(unqlite_kv_handle); unsigned char * (*xTmpPage)(unqlite_kv_handle); - void (*xSetUnpin)(unqlite_kv_handle,void (*xPageUnpin)(void *)); + void (*xSetUnpin)(unqlite_kv_handle,void (*xPageUnpin)(void *)); void (*xSetReload)(unqlite_kv_handle,void (*xPageReload)(void *)); void (*xErr)(unqlite_kv_handle,const char *); }; @@ -662,7 +666,7 @@ struct unqlite_kv_cursor * UnQLite come with two built-in KV storage engine: A Virtual Linear Hash (VLH) storage * engine is used for persistent on-disk databases with O(1) lookup time and an in-memory * hash-table or Red-black tree storage engine is used for in-memory databases. - * Future versions of UnQLite might add other built-in storage engines (i.e. LSM). + * Future versions of UnQLite might add other built-in storage engines (i.e. LSM). * Registration of a Key/Value storage engine at run-time is done via [unqlite_lib_config()] * with a configuration verb set to UNQLITE_LIB_CONFIG_STORAGE_ENGINE. */ @@ -694,7 +698,7 @@ struct unqlite_kv_methods unqlite_kv_engine *, const void *pKey,int nKeyLen, const void *pData,unqlite_int64 nDataLen - ); + ); int (*xAppend)( unqlite_kv_engine *, const void *pKey,int nKeyLen, @@ -721,10 +725,10 @@ struct unqlite_kv_methods #ifndef UNQLITE_JOURNAL_FILE_SUFFIX #define UNQLITE_JOURNAL_FILE_SUFFIX "_unqlite_journal" #endif -/* +/* * C-API-REF: Please refer to the official documentation for interfaces - * purpose and expected parameters. - */ + * purpose and expected parameters. + */ /* Database Engine Handle */ UNQLITE_APIEXPORT int unqlite_open(unqlite **ppDB,const char *zFilename,unsigned int iMode); @@ -777,4 +781,7 @@ UNQLITE_APIEXPORT const char * unqlite_lib_signature(void); UNQLITE_APIEXPORT const char * unqlite_lib_ident(void); UNQLITE_APIEXPORT const char * unqlite_lib_copyright(void); +#ifdef __cplusplus +} +#endif /* __cplusplus */ #endif /* _UNQLITE_H_ */ diff --git a/src/vrmlsupport.cpp b/src/vrmlsupport.cpp index 60e10a8..4cec027 100644 --- a/src/vrmlsupport.cpp +++ b/src/vrmlsupport.cpp @@ -1,515 +1,515 @@ -/****************************************************************** -* -* This file is copied (with minor adaptations) from X3DBrowserFunc.cpp -* which is part of CyberX3D for C++. This library is distributed -* under the 3-clause BSD license. -* -* Copyright (C) Satoshi Konno 1996-2003 -* -******************************************************************/ - -#if !defined NO_3DMODEL - -#include -#include "vrmlsupport.h" - -using namespace CyberX3D; - -static int gnLights; -static PointLightNode headLight; -static float clearColor[] = {0.0f, 0.0f, 0.0f}; - -void SetSceneGraphBackground(unsigned long rgb) -{ - clearColor[0] = ((rgb >> 0) & 0xff) / 255.0f; - clearColor[1] = ((rgb >> 8) & 0xff) / 255.0f; - clearColor[2] = ((rgb >> 16) & 0xff) / 255.0f; -} - -static void PushLightNode(LightNode *lightNode) -{ - if (!lightNode->isOn()) - return; - - GLint nMaxLightMax; - glGetIntegerv(GL_MAX_LIGHTS, &nMaxLightMax); - - if (nMaxLightMax < gnLights) { - gnLights++; - return; - } - - float color[4]; - float pos[4]; - float attenuation[3]; - float direction[3]; - float intensity; - - if (lightNode->isPointLightNode()) { - - PointLightNode *pLight = (PointLightNode *)lightNode; - - glEnable(GL_LIGHT0+gnLights); - - pLight->getAmbientColor(color); - glLightfv(GL_LIGHT0+gnLights, GL_AMBIENT, color); - - pLight->getColor(color); - intensity = pLight->getIntensity(); - color[0] *= intensity; - color[1] *= intensity; - color[2] *= intensity; - glLightfv(GL_LIGHT0+gnLights, GL_DIFFUSE, color); - glLightfv(GL_LIGHT0+gnLights, GL_SPECULAR, color); - - pLight->getLocation(pos); pos[3] = 1.0f; - glLightfv(GL_LIGHT0+gnLights, GL_POSITION, pos); - - direction[0] = 0.0f; direction[0] = 0.0f; direction[0] = 0.0f; - glLightfv(GL_LIGHT0+gnLights, GL_SPOT_DIRECTION, direction); - glLightf(GL_LIGHT0+gnLights, GL_SPOT_EXPONENT, 0.0f); - glLightf(GL_LIGHT0+gnLights, GL_SPOT_CUTOFF, 180.0f); - - pLight->getAttenuation(attenuation); - glLightf(GL_LIGHT0+gnLights, GL_CONSTANT_ATTENUATION, attenuation[0]); - glLightf(GL_LIGHT0+gnLights, GL_LINEAR_ATTENUATION, attenuation[1]); - glLightf(GL_LIGHT0+gnLights, GL_QUADRATIC_ATTENUATION, attenuation[2]); - - gnLights++; - } - else if (lightNode->isDirectionalLightNode()) { - - DirectionalLightNode *dLight = (DirectionalLightNode *)lightNode; - - glEnable(GL_LIGHT0+gnLights); - - dLight->getAmbientColor(color); - glLightfv(GL_LIGHT0+gnLights, GL_AMBIENT, color); - - dLight->getColor(color); - intensity = dLight->getIntensity(); - color[0] *= intensity; - color[1] *= intensity; - color[2] *= intensity; - glLightfv(GL_LIGHT0+gnLights, GL_DIFFUSE, color); - glLightfv(GL_LIGHT0+gnLights, GL_SPECULAR, color); - - dLight->getDirection(pos); pos[3] = 0.0f; - glLightfv(GL_LIGHT0+gnLights, GL_POSITION, pos); - - direction[0] = 0.0f; direction[0] = 0.0f; direction[0] = 0.0f; - glLightfv(GL_LIGHT0+gnLights, GL_SPOT_DIRECTION, direction); - glLightf(GL_LIGHT0+gnLights, GL_SPOT_EXPONENT, 0.0f); - glLightf(GL_LIGHT0+gnLights, GL_SPOT_CUTOFF, 180.0f); - - glLightf(GL_LIGHT0+gnLights, GL_CONSTANT_ATTENUATION, 1.0); - glLightf(GL_LIGHT0+gnLights, GL_LINEAR_ATTENUATION, 0.0); - glLightf(GL_LIGHT0+gnLights, GL_QUADRATIC_ATTENUATION, 0.0); - - gnLights++; - } - else if (lightNode->isSpotLightNode()) { - - SpotLightNode *sLight = (SpotLightNode *)lightNode; - - glEnable(GL_LIGHT0+gnLights); - - sLight->getAmbientColor(color); - glLightfv(GL_LIGHT0+gnLights, GL_AMBIENT, color); - - sLight->getColor(color); - intensity = sLight->getIntensity(); - color[0] *= intensity; - color[1] *= intensity; - color[2] *= intensity; - glLightfv(GL_LIGHT0+gnLights, GL_DIFFUSE, color); - glLightfv(GL_LIGHT0+gnLights, GL_SPECULAR, color); - - sLight->getLocation(pos); pos[3] = 1.0f; - glLightfv(GL_LIGHT0+gnLights, GL_POSITION, pos); - - sLight->getDirection(direction); - glLightfv(GL_LIGHT0+gnLights, GL_SPOT_DIRECTION, direction); - - glLightf(GL_LIGHT0+gnLights, GL_SPOT_EXPONENT, 0.0f); - glLightf(GL_LIGHT0+gnLights, GL_SPOT_CUTOFF, sLight->getCutOffAngle()); - - sLight->getAttenuation(attenuation); - glLightf(GL_LIGHT0+gnLights, GL_CONSTANT_ATTENUATION, attenuation[0]); - glLightf(GL_LIGHT0+gnLights, GL_LINEAR_ATTENUATION, attenuation[1]); - glLightf(GL_LIGHT0+gnLights, GL_QUADRATIC_ATTENUATION, attenuation[2]); - - gnLights++; - } -} - -static void PopLightNode(LightNode *lightNode) -{ - if (!lightNode->isOn()) - return; - - GLint nMaxLightMax; - glGetIntegerv(GL_MAX_LIGHTS, &nMaxLightMax); - - gnLights--; - - if (gnLights < nMaxLightMax) - glDisable(GL_LIGHT0+gnLights); -} - -static void DrawShapeNode(SceneGraph *sg, ShapeNode *shape, int drawMode) -{ - glPushMatrix (); - - ///////////////////////////////// - // Appearance(Material) - ///////////////////////////////// - - float color[4]; - color[3] = 1.0f; - - AppearanceNode *appearance = shape->getAppearanceNodes(); - MaterialNode *material = NULL; - ImageTextureNode *imgTexture = NULL; - TextureTransformNode *texTransform = NULL; - - bool bEnableTexture = false; - - if (appearance) { - - // Texture Transform - TextureTransformNode *texTransform = appearance->getTextureTransformNodes(); - if (texTransform) { - float texCenter[2]; - float texScale[2]; - float texTranslation[2]; - float texRotation; - - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - - texTransform->getTranslation(texTranslation); - glTranslatef(texTranslation[0], texTranslation[1], 0.0f); - - texTransform->getCenter(texCenter); - glTranslatef(texCenter[0], texCenter[1], 0.0f); - - texRotation = texTransform->getRotation(); - glRotatef(0.0f, 0.0f, 1.0f, texRotation); - - texTransform->getScale(texScale); - glScalef(texScale[0], texScale[1], 1.0f); - - glTranslatef(-texCenter[0], -texCenter[1], 0.0f); - } - else { - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - glTranslatef(0.0f, 0.0f, 1.0f); - } - - glMatrixMode(GL_MODELVIEW); - - // Texture - imgTexture = appearance->getImageTextureNodes(); - if (imgTexture && drawMode == OGL_RENDERING_TEXTURE) { - - int width = imgTexture->getWidth(); - int height = imgTexture->getHeight(); - RGBAColor32 *color = imgTexture->getImage(); - - if (0 < width && 0 < height && color != NULL) - bEnableTexture = true; - - if (bEnableTexture == true) { - if (imgTexture->hasTransparencyColor() == true) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - else - glDisable(GL_BLEND); - - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glBindTexture(GL_TEXTURE_2D, imgTexture->getTextureName()); - - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - glEnable(GL_TEXTURE_2D); - - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); - glEnable(GL_COLOR_MATERIAL); - } - else { - glDisable(GL_TEXTURE_2D); - glDisable(GL_COLOR_MATERIAL); - } - } - else { - glDisable(GL_TEXTURE_2D); - glDisable(GL_COLOR_MATERIAL); - } - - // Material - material = appearance->getMaterialNodes(); - if (material) { - float ambientIntesity = material->getAmbientIntensity(); - - material->getDiffuseColor(color); - glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color); - - material->getSpecularColor(color); - glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, color); - - material->getEmissiveColor(color); - glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, color); - - material->getDiffuseColor(color); - color[0] *= ambientIntesity; - color[1] *= ambientIntesity; - color[2] *= ambientIntesity; - glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color); - - glMateriali(GL_FRONT, GL_SHININESS, (int)(material->getShininess()*128.0)); - } - - } - - if (!appearance || !material) { - color[0] = 0.8f; color[1] = 0.8f; color[2] = 0.8f; - glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color); - color[0] = 0.0f; color[1] = 0.0f; color[2] = 0.0f; - glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, color); - glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, color); - color[0] = 0.8f*0.2f; color[1] = 0.8f*0.2f; color[2] = 0.8f*0.2f; - glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color); - glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, (int)(0.2*128.0)); - } - - if (!appearance || !imgTexture || drawMode != OGL_RENDERING_TEXTURE) { - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); - } - - ///////////////////////////////// - // Transform - ///////////////////////////////// - - float m4[4][4]; - shape->getTransformMatrix(m4); - glMultMatrixf((float *)m4); - - glColor3f(1.0f, 1.0f, 1.0f); - - ///////////////////////////////// - // Geometry3D - ///////////////////////////////// - - Geometry3DNode *gnode = shape->getGeometry3D(); - if (gnode) { - if (0 < gnode->getDisplayList()) - gnode->draw(); - } - - ShapeNode *selectedShapeNode = sg->getSelectedShapeNode(); - if (gnode && selectedShapeNode == shape) { - float bboxCenter[3]; - float bboxSize[3]; - gnode->getBoundingBoxCenter(bboxCenter); - gnode->getBoundingBoxSize(bboxSize); - - glColor3f(1.0f, 1.0f, 1.0f); - glDisable(GL_LIGHTING); -// glDisable(GL_COLOR_MATERIAL); - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); - - glBegin(GL_LINES); - int x, y, z; - for (x=0; x<2; x++) { - for (y=0; y<2; y++) { - float point[3]; - point[0] = (x==0) ? bboxCenter[0] - bboxSize[0] : bboxCenter[0] + bboxSize[0]; - point[1] = (y==0) ? bboxCenter[1] - bboxSize[1] : bboxCenter[1] + bboxSize[1]; - point[2] = bboxCenter[2] - bboxSize[2]; - glVertex3fv(point); - point[2] = bboxCenter[2] + bboxSize[2]; - glVertex3fv(point); - } - } - for (x=0; x<2; x++) { - for (z=0; z<2; z++) { - float point[3]; - point[0] = (x==0) ? bboxCenter[0] - bboxSize[0] : bboxCenter[0] + bboxSize[0]; - point[1] = bboxCenter[1] - bboxSize[1]; - point[2] = (z==0) ? bboxCenter[2] - bboxSize[2] : bboxCenter[2] + bboxSize[2]; - glVertex3fv(point); - point[1] = bboxCenter[1] + bboxSize[1]; - glVertex3fv(point); - } - } - for (y=0; y<2; y++) { - for (z=0; z<2; z++) { - float point[3]; - point[0] = bboxCenter[0] - bboxSize[0]; - point[1] = (y==0) ? bboxCenter[1] - bboxSize[1] : bboxCenter[1] + bboxSize[1]; - point[2] = (z==0) ? bboxCenter[2] - bboxSize[2] : bboxCenter[2] + bboxSize[2]; - glVertex3fv(point); - point[0] = bboxCenter[0] + bboxSize[0]; - glVertex3fv(point); - } - } - glEnd(); - - glEnable(GL_LIGHTING); -// glEnable(GL_COLOR_MATERIAL); - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - } - - glPopMatrix(); -} - - -static void DrawNode(SceneGraph *sceneGraph, Node *firstNode, int drawMode) -{ - if (!firstNode) - return; - - Node *node; - - for (node = firstNode; node; node=node->next()) { - if (node->isLightNode()) - PushLightNode((LightNode *)node); - } - - for (node = firstNode; node; node=node->next()) { - if (node->isShapeNode()) - DrawShapeNode(sceneGraph, (ShapeNode *)node, drawMode); - else - DrawNode(sceneGraph, node->getChildNodes(), drawMode); - } - - for (node = firstNode; node; node=node->next()) { - if (node->isLightNode()) - PopLightNode((LightNode *)node); - } -} - -void DrawSceneGraph(SceneGraph *sg, int drawMode, float AngleX, float AngleZ) -{ - ///////////////////////////////// - // Headlight - ///////////////////////////////// - - NavigationInfoNode *navInfo = sg->getNavigationInfoNode(); - if (navInfo == NULL) - navInfo = sg->getDefaultNavigationInfoNode(); - - if (navInfo->getHeadlight()) { - float location[3]; - ViewpointNode *view = sg->getViewpointNode(); - if (view == NULL) - view = sg->getDefaultViewpointNode(); - view->getPosition(location); - headLight.setLocation(location); - headLight.setAmbientIntensity(0.3f); - headLight.setIntensity(0.7f); - sg->addNode(&headLight); - } - - ///////////////////////////////// - // Viewpoint - ///////////////////////////////// - - ViewpointNode *view = sg->getViewpointNode(); - if (view == NULL) - view = sg->getDefaultViewpointNode(); - -#if 0 //??? default viewport - if (view) { - GLint viewport[4]; - glGetIntegerv(GL_VIEWPORT, viewport); - UpdateViewport(sg, viewport[2], viewport[3]); - } -#endif - - ///////////////////////////////// - // Rendering - ///////////////////////////////// - - glEnable(GL_DEPTH_TEST); - switch (drawMode) { - case OGL_RENDERING_WIRE: - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - break; - default: - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - } - glCullFace(GL_BACK); - glEnable(GL_CULL_FACE); - - glEnable(GL_LIGHTING); - glShadeModel (GL_SMOOTH); - - ///////////////////////////////// - // Background - ///////////////////////////////// - - BackgroundNode *bg = sg->getBackgroundNode(); - if (bg != NULL) { - if (0 < bg->getNSkyColors()) - bg->getSkyColor(0, clearColor); - } - - glClearColor(clearColor[0], clearColor[1], clearColor[2], 1.0f); - glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - - if (!view) - return; - - ///////////////////////////////// - // Viewpoint Matrix - ///////////////////////////////// - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - float m4[4][4]; - view->getMatrix(m4); - glMultMatrixf((float *)m4); - - glRotatef(AngleX, 1.0, 0.0, 0.0); - glRotatef(AngleZ, 0.0, 1.0, 0.0); - glRotatef(30, 0.0, 0.0, 1.0); - - ///////////////////////////////// - // Light - ///////////////////////////////// - - GLint nMaxLightMax; - glGetIntegerv(GL_MAX_LIGHTS, &nMaxLightMax); - for (int n = 0; n < nMaxLightMax; n++) - glDisable(GL_LIGHT0+n); - gnLights = 0; - - ///////////////////////////////// - // General Node - ///////////////////////////////// - - DrawNode(sg, sg->getNodes(), drawMode); - - ///////////////////////////////// - // Headlight - ///////////////////////////////// - - headLight.remove(); - - ///////////////////////////////// - // glFlush - ///////////////////////////////// - - glFlush(); -} - -#endif /* !defined NO_3DMODEL */ +/****************************************************************** +* +* This file is copied (with minor adaptations) from X3DBrowserFunc.cpp +* which is part of CyberX3D for C++. This library is distributed +* under the 3-clause BSD license. +* +* Copyright (C) Satoshi Konno 1996-2003 +* +******************************************************************/ + +#if !defined NO_3DMODEL + +#include +#include "vrmlsupport.h" + +using namespace CyberX3D; + +static int gnLights; +static PointLightNode headLight; +static float clearColor[] = {0.0f, 0.0f, 0.0f}; + +void SetSceneGraphBackground(unsigned long rgb) +{ + clearColor[0] = ((rgb >> 0) & 0xff) / 255.0f; + clearColor[1] = ((rgb >> 8) & 0xff) / 255.0f; + clearColor[2] = ((rgb >> 16) & 0xff) / 255.0f; +} + +static void PushLightNode(LightNode *lightNode) +{ + if (!lightNode->isOn()) + return; + + GLint nMaxLightMax; + glGetIntegerv(GL_MAX_LIGHTS, &nMaxLightMax); + + if (nMaxLightMax < gnLights) { + gnLights++; + return; + } + + float color[4]; + float pos[4]; + float attenuation[3]; + float direction[3]; + float intensity; + + if (lightNode->isPointLightNode()) { + + PointLightNode *pLight = (PointLightNode *)lightNode; + + glEnable(GL_LIGHT0+gnLights); + + pLight->getAmbientColor(color); + glLightfv(GL_LIGHT0+gnLights, GL_AMBIENT, color); + + pLight->getColor(color); + intensity = pLight->getIntensity(); + color[0] *= intensity; + color[1] *= intensity; + color[2] *= intensity; + glLightfv(GL_LIGHT0+gnLights, GL_DIFFUSE, color); + glLightfv(GL_LIGHT0+gnLights, GL_SPECULAR, color); + + pLight->getLocation(pos); pos[3] = 1.0f; + glLightfv(GL_LIGHT0+gnLights, GL_POSITION, pos); + + direction[0] = 0.0f; direction[0] = 0.0f; direction[0] = 0.0f; + glLightfv(GL_LIGHT0+gnLights, GL_SPOT_DIRECTION, direction); + glLightf(GL_LIGHT0+gnLights, GL_SPOT_EXPONENT, 0.0f); + glLightf(GL_LIGHT0+gnLights, GL_SPOT_CUTOFF, 180.0f); + + pLight->getAttenuation(attenuation); + glLightf(GL_LIGHT0+gnLights, GL_CONSTANT_ATTENUATION, attenuation[0]); + glLightf(GL_LIGHT0+gnLights, GL_LINEAR_ATTENUATION, attenuation[1]); + glLightf(GL_LIGHT0+gnLights, GL_QUADRATIC_ATTENUATION, attenuation[2]); + + gnLights++; + } + else if (lightNode->isDirectionalLightNode()) { + + DirectionalLightNode *dLight = (DirectionalLightNode *)lightNode; + + glEnable(GL_LIGHT0+gnLights); + + dLight->getAmbientColor(color); + glLightfv(GL_LIGHT0+gnLights, GL_AMBIENT, color); + + dLight->getColor(color); + intensity = dLight->getIntensity(); + color[0] *= intensity; + color[1] *= intensity; + color[2] *= intensity; + glLightfv(GL_LIGHT0+gnLights, GL_DIFFUSE, color); + glLightfv(GL_LIGHT0+gnLights, GL_SPECULAR, color); + + dLight->getDirection(pos); pos[3] = 0.0f; + glLightfv(GL_LIGHT0+gnLights, GL_POSITION, pos); + + direction[0] = 0.0f; direction[0] = 0.0f; direction[0] = 0.0f; + glLightfv(GL_LIGHT0+gnLights, GL_SPOT_DIRECTION, direction); + glLightf(GL_LIGHT0+gnLights, GL_SPOT_EXPONENT, 0.0f); + glLightf(GL_LIGHT0+gnLights, GL_SPOT_CUTOFF, 180.0f); + + glLightf(GL_LIGHT0+gnLights, GL_CONSTANT_ATTENUATION, 1.0); + glLightf(GL_LIGHT0+gnLights, GL_LINEAR_ATTENUATION, 0.0); + glLightf(GL_LIGHT0+gnLights, GL_QUADRATIC_ATTENUATION, 0.0); + + gnLights++; + } + else if (lightNode->isSpotLightNode()) { + + SpotLightNode *sLight = (SpotLightNode *)lightNode; + + glEnable(GL_LIGHT0+gnLights); + + sLight->getAmbientColor(color); + glLightfv(GL_LIGHT0+gnLights, GL_AMBIENT, color); + + sLight->getColor(color); + intensity = sLight->getIntensity(); + color[0] *= intensity; + color[1] *= intensity; + color[2] *= intensity; + glLightfv(GL_LIGHT0+gnLights, GL_DIFFUSE, color); + glLightfv(GL_LIGHT0+gnLights, GL_SPECULAR, color); + + sLight->getLocation(pos); pos[3] = 1.0f; + glLightfv(GL_LIGHT0+gnLights, GL_POSITION, pos); + + sLight->getDirection(direction); + glLightfv(GL_LIGHT0+gnLights, GL_SPOT_DIRECTION, direction); + + glLightf(GL_LIGHT0+gnLights, GL_SPOT_EXPONENT, 0.0f); + glLightf(GL_LIGHT0+gnLights, GL_SPOT_CUTOFF, sLight->getCutOffAngle()); + + sLight->getAttenuation(attenuation); + glLightf(GL_LIGHT0+gnLights, GL_CONSTANT_ATTENUATION, attenuation[0]); + glLightf(GL_LIGHT0+gnLights, GL_LINEAR_ATTENUATION, attenuation[1]); + glLightf(GL_LIGHT0+gnLights, GL_QUADRATIC_ATTENUATION, attenuation[2]); + + gnLights++; + } +} + +static void PopLightNode(LightNode *lightNode) +{ + if (!lightNode->isOn()) + return; + + GLint nMaxLightMax; + glGetIntegerv(GL_MAX_LIGHTS, &nMaxLightMax); + + gnLights--; + + if (gnLights < nMaxLightMax) + glDisable(GL_LIGHT0+gnLights); +} + +static void DrawShapeNode(SceneGraph *sg, ShapeNode *shape, int drawMode) +{ + glPushMatrix (); + + ///////////////////////////////// + // Appearance(Material) + ///////////////////////////////// + + float color[4]; + color[3] = 1.0f; + + AppearanceNode *appearance = shape->getAppearanceNodes(); + MaterialNode *material = NULL; + ImageTextureNode *imgTexture = NULL; + TextureTransformNode *texTransform = NULL; + + bool bEnableTexture = false; + + if (appearance) { + + // Texture Transform + TextureTransformNode *texTransform = appearance->getTextureTransformNodes(); + if (texTransform) { + float texCenter[2]; + float texScale[2]; + float texTranslation[2]; + float texRotation; + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + + texTransform->getTranslation(texTranslation); + glTranslatef(texTranslation[0], texTranslation[1], 0.0f); + + texTransform->getCenter(texCenter); + glTranslatef(texCenter[0], texCenter[1], 0.0f); + + texRotation = texTransform->getRotation(); + glRotatef(0.0f, 0.0f, 1.0f, texRotation); + + texTransform->getScale(texScale); + glScalef(texScale[0], texScale[1], 1.0f); + + glTranslatef(-texCenter[0], -texCenter[1], 0.0f); + } + else { + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glTranslatef(0.0f, 0.0f, 1.0f); + } + + glMatrixMode(GL_MODELVIEW); + + // Texture + imgTexture = appearance->getImageTextureNodes(); + if (imgTexture && drawMode == OGL_RENDERING_TEXTURE) { + + int width = imgTexture->getWidth(); + int height = imgTexture->getHeight(); + RGBAColor32 *color = imgTexture->getImage(); + + if (0 < width && 0 < height && color != NULL) + bEnableTexture = true; + + if (bEnableTexture == true) { + if (imgTexture->hasTransparencyColor() == true) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + else + glDisable(GL_BLEND); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glBindTexture(GL_TEXTURE_2D, imgTexture->getTextureName()); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glEnable(GL_TEXTURE_2D); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); + glEnable(GL_COLOR_MATERIAL); + } + else { + glDisable(GL_TEXTURE_2D); + glDisable(GL_COLOR_MATERIAL); + } + } + else { + glDisable(GL_TEXTURE_2D); + glDisable(GL_COLOR_MATERIAL); + } + + // Material + material = appearance->getMaterialNodes(); + if (material) { + float ambientIntesity = material->getAmbientIntensity(); + + material->getDiffuseColor(color); + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color); + + material->getSpecularColor(color); + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, color); + + material->getEmissiveColor(color); + glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, color); + + material->getDiffuseColor(color); + color[0] *= ambientIntesity; + color[1] *= ambientIntesity; + color[2] *= ambientIntesity; + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color); + + glMateriali(GL_FRONT, GL_SHININESS, (int)(material->getShininess()*128.0)); + } + + } + + if (!appearance || !material) { + color[0] = 0.8f; color[1] = 0.8f; color[2] = 0.8f; + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color); + color[0] = 0.0f; color[1] = 0.0f; color[2] = 0.0f; + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, color); + glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, color); + color[0] = 0.8f*0.2f; color[1] = 0.8f*0.2f; color[2] = 0.8f*0.2f; + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color); + glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, (int)(0.2*128.0)); + } + + if (!appearance || !imgTexture || drawMode != OGL_RENDERING_TEXTURE) { + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + } + + ///////////////////////////////// + // Transform + ///////////////////////////////// + + float m4[4][4]; + shape->getTransformMatrix(m4); + glMultMatrixf((float *)m4); + + glColor3f(1.0f, 1.0f, 1.0f); + + ///////////////////////////////// + // Geometry3D + ///////////////////////////////// + + Geometry3DNode *gnode = shape->getGeometry3D(); + if (gnode) { + if (0 < gnode->getDisplayList()) + gnode->draw(); + } + + ShapeNode *selectedShapeNode = sg->getSelectedShapeNode(); + if (gnode && selectedShapeNode == shape) { + float bboxCenter[3]; + float bboxSize[3]; + gnode->getBoundingBoxCenter(bboxCenter); + gnode->getBoundingBoxSize(bboxSize); + + glColor3f(1.0f, 1.0f, 1.0f); + glDisable(GL_LIGHTING); +// glDisable(GL_COLOR_MATERIAL); + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + + glBegin(GL_LINES); + int x, y, z; + for (x=0; x<2; x++) { + for (y=0; y<2; y++) { + float point[3]; + point[0] = (x==0) ? bboxCenter[0] - bboxSize[0] : bboxCenter[0] + bboxSize[0]; + point[1] = (y==0) ? bboxCenter[1] - bboxSize[1] : bboxCenter[1] + bboxSize[1]; + point[2] = bboxCenter[2] - bboxSize[2]; + glVertex3fv(point); + point[2] = bboxCenter[2] + bboxSize[2]; + glVertex3fv(point); + } + } + for (x=0; x<2; x++) { + for (z=0; z<2; z++) { + float point[3]; + point[0] = (x==0) ? bboxCenter[0] - bboxSize[0] : bboxCenter[0] + bboxSize[0]; + point[1] = bboxCenter[1] - bboxSize[1]; + point[2] = (z==0) ? bboxCenter[2] - bboxSize[2] : bboxCenter[2] + bboxSize[2]; + glVertex3fv(point); + point[1] = bboxCenter[1] + bboxSize[1]; + glVertex3fv(point); + } + } + for (y=0; y<2; y++) { + for (z=0; z<2; z++) { + float point[3]; + point[0] = bboxCenter[0] - bboxSize[0]; + point[1] = (y==0) ? bboxCenter[1] - bboxSize[1] : bboxCenter[1] + bboxSize[1]; + point[2] = (z==0) ? bboxCenter[2] - bboxSize[2] : bboxCenter[2] + bboxSize[2]; + glVertex3fv(point); + point[0] = bboxCenter[0] + bboxSize[0]; + glVertex3fv(point); + } + } + glEnd(); + + glEnable(GL_LIGHTING); +// glEnable(GL_COLOR_MATERIAL); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + } + + glPopMatrix(); +} + + +static void DrawNode(SceneGraph *sceneGraph, Node *firstNode, int drawMode) +{ + if (!firstNode) + return; + + Node *node; + + for (node = firstNode; node; node=node->next()) { + if (node->isLightNode()) + PushLightNode((LightNode *)node); + } + + for (node = firstNode; node; node=node->next()) { + if (node->isShapeNode()) + DrawShapeNode(sceneGraph, (ShapeNode *)node, drawMode); + else + DrawNode(sceneGraph, node->getChildNodes(), drawMode); + } + + for (node = firstNode; node; node=node->next()) { + if (node->isLightNode()) + PopLightNode((LightNode *)node); + } +} + +void DrawSceneGraph(SceneGraph *sg, int drawMode, float AngleX, float AngleZ) +{ + ///////////////////////////////// + // Headlight + ///////////////////////////////// + + NavigationInfoNode *navInfo = sg->getNavigationInfoNode(); + if (navInfo == NULL) + navInfo = sg->getDefaultNavigationInfoNode(); + + if (navInfo->getHeadlight()) { + float location[3]; + ViewpointNode *view = sg->getViewpointNode(); + if (view == NULL) + view = sg->getDefaultViewpointNode(); + view->getPosition(location); + headLight.setLocation(location); + headLight.setAmbientIntensity(0.3f); + headLight.setIntensity(0.7f); + sg->addNode(&headLight); + } + + ///////////////////////////////// + // Viewpoint + ///////////////////////////////// + + ViewpointNode *view = sg->getViewpointNode(); + if (view == NULL) + view = sg->getDefaultViewpointNode(); + +#if 0 //??? default viewport + if (view) { + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + UpdateViewport(sg, viewport[2], viewport[3]); + } +#endif + + ///////////////////////////////// + // Rendering + ///////////////////////////////// + + glEnable(GL_DEPTH_TEST); + switch (drawMode) { + case OGL_RENDERING_WIRE: + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + break; + default: + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } + glCullFace(GL_BACK); + glEnable(GL_CULL_FACE); + + glEnable(GL_LIGHTING); + glShadeModel (GL_SMOOTH); + + ///////////////////////////////// + // Background + ///////////////////////////////// + + BackgroundNode *bg = sg->getBackgroundNode(); + if (bg != NULL) { + if (0 < bg->getNSkyColors()) + bg->getSkyColor(0, clearColor); + } + + glClearColor(clearColor[0], clearColor[1], clearColor[2], 1.0f); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + if (!view) + return; + + ///////////////////////////////// + // Viewpoint Matrix + ///////////////////////////////// + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + float m4[4][4]; + view->getMatrix(m4); + glMultMatrixf((float *)m4); + + glRotatef(AngleX, 1.0, 0.0, 0.0); + glRotatef(AngleZ, 0.0, 1.0, 0.0); + glRotatef(30, 0.0, 0.0, 1.0); + + ///////////////////////////////// + // Light + ///////////////////////////////// + + GLint nMaxLightMax; + glGetIntegerv(GL_MAX_LIGHTS, &nMaxLightMax); + for (int n = 0; n < nMaxLightMax; n++) + glDisable(GL_LIGHT0+n); + gnLights = 0; + + ///////////////////////////////// + // General Node + ///////////////////////////////// + + DrawNode(sg, sg->getNodes(), drawMode); + + ///////////////////////////////// + // Headlight + ///////////////////////////////// + + headLight.remove(); + + ///////////////////////////////// + // glFlush + ///////////////////////////////// + + glFlush(); +} + +#endif /* !defined NO_3DMODEL */ diff --git a/src/vrmlsupport.h b/src/vrmlsupport.h index 9b3ddb8..294cbd0 100644 --- a/src/vrmlsupport.h +++ b/src/vrmlsupport.h @@ -1,40 +1,40 @@ -/* - * Librarian for KiCad, a free EDA CAD application. - * Loading and drawing of VRML models, based on the CyberX3D library. - * - * Copyright (C) 2015 CompuPhase - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * $Id: vrmlsupport.h 5405 2015-11-20 09:40:55Z $ - */ -#ifndef VRMLSUPPORT_H -#define VRMLSUPPORT_H - -#if !defined NO_3DMODEL - -#define CX3D_SUPPORT_OPENGL -#include - -enum { - OGL_RENDERING_WIRE, - OGL_RENDERING_SHADE, - OGL_RENDERING_TEXTURE, -}; -void DrawSceneGraph(CyberX3D::SceneGraph* sceneGraph, int drawMode, float AngleX, float AngleZ); - -void SetSceneGraphBackground(unsigned long rgb); - -#endif /* !defined NO_3DMODEL */ - -#endif /* VRMLSUPPORT_H */ +/* + * Librarian for KiCad, a free EDA CAD application. + * Loading and drawing of VRML models, based on the CyberX3D library. + * + * Copyright (C) 2015 CompuPhase + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * $Id: vrmlsupport.h 5686 2017-05-24 13:56:46Z thiadmer $ + */ +#ifndef VRMLSUPPORT_H +#define VRMLSUPPORT_H + +#if !defined NO_3DMODEL + +#define CX3D_SUPPORT_OPENGL +#include + +enum { + OGL_RENDERING_WIRE, + OGL_RENDERING_SHADE, + OGL_RENDERING_TEXTURE, +}; +void DrawSceneGraph(CyberX3D::SceneGraph* sceneGraph, int drawMode, float AngleX, float AngleZ); + +void SetSceneGraphBackground(unsigned long rgb); + +#endif /* !defined NO_3DMODEL */ + +#endif /* VRMLSUPPORT_H */ diff --git a/template/BGA.mt b/template/BGA.mt index d14a3d3..6d6f5b5 100644 --- a/template/BGA.mt +++ b/template/BGA.mt @@ -2,7 +2,9 @@ #brief Ball Grid Array #note Square grid, either full grid or with centre void #pins 9 16 24 25 32 36 40 48 49 56 60 64 72 80 81 84 88 96 100 104 108 112 120 121 132 140 144 156 160 169 176 180 192 196 200 208 220 225 256 240 -#param 48 @PT 0.5 @PP 0.23 @PW 0.23 @PL 4.75 @BW 4.75 @BL 0.2 @BP 0.65 @TS 15 @TW +#param 48 @PT "circle" @PSH \ +# 0.5 @PP 0.23 @PW 0.23 @PL 0.2 @BP 0.65 @TS 15 @TW \ +# 4.75 @BW 4.75 @BL #model BGA $MODULE {NAME} AR BGA @@ -50,7 +52,8 @@ $PAD {?PN TOP <=}{PN 1 - MTX / floor 1 + @ROW PN ROW 1 - MTX * - @COL} {?PN BOT >}{PN BOT - 1 - MTX / floor 1 + @ROW PN BOT - ROW 1 - MTX * - @COL ROW RING + INNER + @ROW} {?PN TOP > PN BOT <= &}{PN TOP - 1 - MTX INNER - / floor 1 + @ROW PN TOP - ROW 1 - MTX INNER - * - @COL ROW RING + @ROW COL COL INNER + COL RING <= ? @COL} -Sh "{ROW gacol}{COL}" C {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{ROW gacol}{COL}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{ROW gacol}{COL}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/CAE.mt b/template/CAE.mt index d16cea0..6660890 100644 --- a/template/CAE.mt +++ b/template/CAE.mt @@ -1,7 +1,9 @@ #version 1 #brief Aluminium Electrolytic Capacitor #pins 2 -#param 2 @PT 4 @PP 2.5 @PW 1.0 @PL 5.3 @BW 5.3 @BL 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW +#param 2 @PT "rect" @PSH \ +# 4 @PP 2.5 @PW 1.0 @PL 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW \ +# 5.3 @BW 5.3 @BL #model CAE $MODULE {NAME} AR CAE @@ -28,7 +30,8 @@ DS {BW 2 / CUT - ~} {BL 2 /} {BW 2 / ~} {BL 2 / CUT -} {BP} 21 DS {BW 2 /} {BL 2 / ~} {BW 2 /} {OFF ~} {BP} 21 DS {BW 2 /} {BL 2 /} {BW 2 /} {OFF} {BP} 21 $PAD -Sh "{PN}" R {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/Chip.mt b/template/Chip.mt index 196d3b0..9b892f7 100644 --- a/template/Chip.mt +++ b/template/Chip.mt @@ -3,7 +3,8 @@ #note Non-polarized shape. #prefix C #pins 2 -#param 2 @PT 1.9 @PP 0.7 @PW 1.3 @PL 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW +#param 2 @PT "rect" @PSH \ +# 1.9 @PP 0.7 @PW 1.3 @PL 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW #model Chip $MODULE {NAME} AR Chip @@ -25,7 +26,8 @@ DS {X2} {Y1} {X2} {Y2} {BP} 21 DS {X2} {Y2} {X1} {Y2} {BP} 21 DS {X1} {Y2} {X1} {Y1} {BP} 21 $PAD -Sh "{PN}" R {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/ChipPol.mt b/template/ChipPol.mt index 573482a..c8fa9ca 100644 --- a/template/ChipPol.mt +++ b/template/ChipPol.mt @@ -4,7 +4,8 @@ #prefix X #pins 2 #flags rebuild -#param 2 @PT 1.9 @PP 0.7 @PW 1.3 @PL 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW +#param 2 @PT "rect" @PSH \ +# 1.9 @PP 0.7 @PW 1.3 @PL 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW #model ChipPol Chip $MODULE {NAME} AR ChipPol @@ -21,14 +22,15 @@ At SMD T0 0 {BL 2 / TSR + ~} {TSR} {TSR} 0 {TRpen} N {TRvis} 21 N "REF**" T1 0 {BL 2 / TSV +} {TSV} {TSV} 0 {TVpen} N {TVvis} 21 N "VAL**" {BL 0.3 * 1 min @SYMSIZE} -T2 {PP PW + 2 / ~ SYMSIZE - BP +} {PL 2 / ~ BP -} {BP 2 *} {BP 2 *} 0 {BP 2 /} N V 21 N "○" +T2 {PP PW + 2 / ~ SYMSIZE -} {PL 2 / ~ BP -} {BP 2 *} {BP 2 *} 0 {BP 2 /} N V 21 N "○" {BW 2 / @X2 X2 ~ @X1 BL 2 / @Y2 Y2 ~ @Y1} DS {X1} {Y1} {X2} {Y1} {BP} 21 DS {X2} {Y1} {X2} {Y2} {BP} 21 DS {X2} {Y2} {X1} {Y2} {BP} 21 DS {X1} {Y2} {X1} {Y1} {BP} 21 $PAD -Sh "{PN}" R {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/DIP.mt b/template/DIP.mt index f99c4b9..421d514 100644 --- a/template/DIP.mt +++ b/template/DIP.mt @@ -2,8 +2,9 @@ #brief Through-hole DIP #note Normal and wide versions. #pins 8 10 ... -#param 8 @?PT 2.54 @PP 7.62 @SH 1.6 @PW 1.6 @PL 0.2 @BP 0.65 @TS 15 @TW 0.2 @STP \ -# 5.5 @BW PT 2 / floor 1 - PP * 2.2 + @BL +#param 8 @?PT "sqcircle" @PSH \ +# 2.54 @PP 7.62 @SH 1.6 @PW 1.6 @PL 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW \ +# 5.5 @BW PT 2 / floor 1 - PP * 2.2 + @BL #model DIP $MODULE {NAME} AR DIP @@ -29,8 +30,8 @@ DS {BW 2 / ~} {BL 2 /} {BW 2 / ~} {BL 2 / ~} {BP} 21 {BW 4 / 0.1 - 0.5 min @RAD 0.5 @OFFS RAD BW 2 / - OFFS + @XC RAD BL 2 / - OFFS + @YC} DC {XC} {YC} {XC RAD +} {YC} {BP} 21 $PAD -{? PN 1 =}Sh "{PN}" R {PW} {PL} 0 0 0 -{? PN 1 >}Sh "{PN}" C {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr 0.8 0 0 At STD N 00E0FFFF Ne 0 "" diff --git a/template/HDR1x_.mt b/template/HDR1x_.mt index ee35f1a..19ee01f 100644 --- a/template/HDR1x_.mt +++ b/template/HDR1x_.mt @@ -2,7 +2,8 @@ #brief Through-hole pin array, single row #note Suitable for pin headers or resistor/capacitor arrays #pins 2 3 ... -#param 5 @?PT 2.54 @PP 1.6 @PW 1.6 @PL 1.0 @DS 0.2 @BP 0.65 @TS 15 @TW +#param 5 @?PT "sqcircle" @PSH \ +# 2.54 @PP 1.6 @PW 1.6 @PL 1.0 @DS 0.2 @BP 0.65 @TS 15 @TW #model HDR1x_ HDR1x_FrictionLock $MODULE {NAME} AR HDR1x_ @@ -24,8 +25,8 @@ DS {X1} {Y1} {X1} {Y2} {BP} 21 DS {X2} {Y1} {X2} {Y2} {BP} 21 {?PT 2 >}DS {X1} {Y1 PP +} {X2} {Y1 PP +} {BP} 21 $PAD -{? PN 1 =}Sh "{PN}" R {PW} {PL} 0 0 0 -{? PN 1 >}Sh "{PN}" C {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr {DS} 0 0 At STD N 00E0FFFF Ne 0 "" diff --git a/template/HDR1x_SMD.mt b/template/HDR1x_SMD.mt index 2a8898e..afb409d 100644 --- a/template/HDR1x_SMD.mt +++ b/template/HDR1x_SMD.mt @@ -2,7 +2,9 @@ #brief Zig-zag In-line Package #note Surface-mount single-line headers and sockets. #pins 3 4 ... -#param 6 @PT 2.54 @PP 2.5 @SH 3.0 @PW 1.2 @PL 2.5 @BW 15.14 @BL 0.3 @STP 0.2 @BP 0.65 @TS 15 @TW +#param 6 @PT "rect" @PSH \ +# 2.54 @PP 2.5 @SH 3.0 @PW 1.2 @PL 0.3 @STP 0.2 @BP 0.65 @TS 15 @TW \ +# 2.5 @BW 15.14 @BL #model HDR1x_SMD $MODULE {NAME} AR HDR1x_SMD @@ -26,7 +28,8 @@ DS {X1} {Y2} {X1} {Y2 SEGM -} {BP} 21 DS {X2} {Y1} {X2} {Y1 SEGM +} {BP} 21 DS {X2} {Y2} {X2} {Y2 SEGM -} {BP} 21 $PAD -Sh "{PN}" R {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/HDR2x_.mt b/template/HDR2x_.mt index 27bc98b..4927117 100644 --- a/template/HDR2x_.mt +++ b/template/HDR2x_.mt @@ -2,7 +2,7 @@ #brief Through-hole pin array, dual row #note Standard dual row pin headers (use HDRSIL for 2-pin header) #pins 4 6 ... -#param 4 @?PT PT 2 * @PT \ +#param 4 @?PT PT 2 * @PT "sqcircle" @PSH \ # 2.54 @PP 2.54 @SH 1.6 @PW 1.6 @PL 1.0 @DS 0.2 @BP 0.65 @TS 15 @TW \ # 5.08 @BW PT 2 / PP * @BL #model HDR2x_ @@ -27,8 +27,8 @@ DS {X2} {Y1} {X2} {Y2} {BP} 21 DS {X1} {YP} 0 {YP} {BP} 21 DS 0 {Y1} 0 {YP} {BP} 21 $PAD -{? PN 1 =}Sh "{PN}" R {PW} {PL} 0 0 0 -{? PN 1 >}Sh "{PN}" C {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr {DS} 0 0 At STD N 00E0FFFF Ne 0 "" diff --git a/template/HDR2x_SMD.mt b/template/HDR2x_SMD.mt index 60b9c1d..d6143c3 100644 --- a/template/HDR2x_SMD.mt +++ b/template/HDR2x_SMD.mt @@ -2,9 +2,9 @@ #brief SMD pin array, dual row #note Standard dual row pin headers, SMD version #pins 4 6 ... -#param 4 @?PT PT 2 * @PT \ -# 2.54 @PP 5.08 @SH 3.0 @PW 1.4 @PL 0.2 @BP 0.65 @TS 15 @TW 0.2 @STP \ -# 5.08 @BW PT 2 / PP * @BL +#param 4 @?PT PT 2 * @PT "rect" @PSH \ +# 2.54 @PP 5.08 @SH 3.0 @PW 1.4 @PL 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW \ +# 5.08 @BW PT 2 / PP * @BL #model HDR2x_SMD $MODULE {NAME} AR HDR2x_SMD @@ -28,7 +28,8 @@ DS {X1} {Y2} {X2} {Y2} {BP} 21 DS {X1} {Y2} {X1} {Y2 OFFS -} {BP} 21 DS {X2} {Y2} {X2} {Y2 OFFS -} {BP} 21 $PAD -Sh "{PN}" R {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/HDR2x_Shrouded.mt b/template/HDR2x_Shrouded.mt index fd37f0c..dab46db 100644 --- a/template/HDR2x_Shrouded.mt +++ b/template/HDR2x_Shrouded.mt @@ -2,7 +2,7 @@ #brief Vertical DIL shrouded header #note Vertical DIL shrouded header with polarity slot (through-hole) #pins 8 10 ... -#param 4 @?PT PT 2 * @PT \ +#param 4 @?PT PT 2 * @PT "sqcircle" @PSH \ # 2.54 @PP 2.54 @SH 1.6 @PW 1.6 @PL 1.0 @DS 0.2 @BP 0.65 @TS 15 @TW \ # 8.89 @BW PT 2 / 1 - PP * 10.16 + @BL #model HDR2x_Shrouded @@ -32,8 +32,8 @@ DS {X1 THK +} {Y2 THK -} {X1 THK +} {YS2} {BP} 21 DS {X1} {YS1} {X1 THK +} {YS1} {BP} 21 DS {X1} {YS2} {X1 THK +} {YS2} {BP} 21 $PAD -{? PN 1 =}Sh "{PN}" R {PW} {PL} 0 0 0 -{? PN 1 >}Sh "{PN}" C {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr {DS} 0 0 At STD N 00E0FFFF Ne 0 "" diff --git a/template/LED_Chip.mt b/template/LED_Chip.mt index 4bff9b6..46d41c4 100644 --- a/template/LED_Chip.mt +++ b/template/LED_Chip.mt @@ -2,7 +2,7 @@ #brief Chip LED #note Chip LED (multi-colour) in zig-zag pin numbering, SMD version #pins 2 4 6 -#param 4 @?PT \ +#param 4 @?PT "rect" @PSH \ # 2.2 @PP 4.2 @SH 1.6 @PW 1.4 @PL 0.2 @BP 0.65 @TS 15 @TW 0.1 @STP \ # 5.2 @BW 3.4 @BL 1 @PPDIR #flags rebuild @@ -45,7 +45,8 @@ DS {X1} {Y1} {X1} {Y2} {BP} 21 DS {X2} {Y1} {X2} {Y2} {BP} 21 :VERLINES $PAD -Sh "{PN}" R {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/MELF.mt b/template/MELF.mt index b473982..7a971bb 100644 --- a/template/MELF.mt +++ b/template/MELF.mt @@ -2,7 +2,8 @@ #brief diode or LED in MELF/chip package #note Polarized shape. Suitable for MELF, 2-pin DFN and some LED packages. #pins 2 -#param 2 @PT 1.9 @PP 0.7 @PW 1.3 @PL 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW +#param 2 @PT "rect" @PSH \ +# 1.9 @PP 0.7 @PW 1.3 @PL 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW #model MELF SOD $MODULE {NAME} AR MELF @@ -30,7 +31,8 @@ DS {X2} {Y1} {X2} {Y2} {BP} 21 DS {X2} {Y2} {X1} 0 {BP} 21 DS {X1} {Y1} {X1} {Y2} {BP} 21 $PAD -Sh "{PN}" R {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/MLD.mt b/template/MLD.mt index 41fec79..7813f38 100644 --- a/template/MLD.mt +++ b/template/MLD.mt @@ -3,7 +3,9 @@ #note Non-polarized shape, suitable for inductors and power resistors. #prefix M #pins 2 -#param 2 @PT 5.0 @PP 1.7 @PW 2.2 @PL 5.8 @BW 5.8 @BL 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW +#param 2 @PT "rect" @PSH \ +# 5.0 @PP 1.7 @PW 2.2 @PL 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW \ +# 5.8 @BW 5.8 @BL #model MLD $MODULE {NAME} AR MLD @@ -26,7 +28,8 @@ DS {X1} {Y2} {X1} {Y2 SEGM -} {BP} 21 DS {X2} {Y1} {X2} {Y1 SEGM +} {BP} 21 DS {X2} {Y2} {X2} {Y2 SEGM -} {BP} 21 $PAD -Sh "{PN}" R {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/MLDPOL.mt b/template/MLDPOL.mt index fc7e2b4..56bd582 100644 --- a/template/MLDPOL.mt +++ b/template/MLDPOL.mt @@ -3,7 +3,9 @@ #note Polarized shape, for tantalum capacitors. #prefix MP #pins 2 -#param 2 @PT 5.0 @PP 1.7 @PW 2.2 @PL 5.8 @BW 3.2 @BL 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW +#param 2 @PT "rect" @PSH \ +# 5.0 @PP 1.7 @PW 2.2 @PL 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW \ +# 5.8 @BW 3.2 @BL #model MLDPOL $MODULE {NAME} AR MLDPOL @@ -31,7 +33,8 @@ T2 {PP PW + 2 / ~ SYMSIZE 2 / -} {PL 2 / ~ SYMSIZE 0.7 * -} {SYMSIZE} {SYMSIZE} DS {X1} {Y1} {X1} {Y1 SEGM +} {BP} 21 DS {X1} {Y2} {X1} {Y2 SEGM -} {BP} 21 $PAD -Sh "{PN}" R {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/QFN.mt b/template/QFN.mt index fb2aae2..7599c9f 100644 --- a/template/QFN.mt +++ b/template/QFN.mt @@ -1,10 +1,12 @@ #version 1 #brief Quad Flat, No pins, No/Unconnected exposed pad #note Exposed pad is not connected to a signal (non-numbered pad). -#pins 16 20 ... +#pins 8 10 12 14 16 20 ... #flags aux-pad(flag-nc,*) rebuild -#param 32 @PT 0.5 @PP 4.8 @SH 4.8 @SV 0.7 @PW 0.25 @PL 3.2 @PWA 3.2 @PLA \ -# 5.0 @BW 5.0 @BL 0.2 @BP 0.65 @TS 15 @TW 0.2 @STP 25 @PSRA 1.5 @EPDOT +#param 32 @?PT "oval" @PSH \ +# 0.5 @PP 4.8 @SH 4.8 @SV 0.7 @PW 0.25 @PL 3.2 @PWA 3.2 @PLA \ +# 0.2 @STP 25 @PSRA 1.5 @EPDOT 0.2 @BP 0.65 @TS 15 @TW \ +# 5.0 @BW 5.0 @BL #model QFN $MODULE {NAME} AR QFN @@ -18,10 +20,13 @@ At SMD {TSR abs @TSR TSV abs @TSV TW 100 / TSR * @TRpen TW 100 / TSV * @TVpen} T0 0 {SV PW + 2 / TSR + ~} {TSR} {TSR} 0 {TRpen} N {TRvis} 21 N "REF**" T1 0 {SV PW + 2 / TSV +} {TSV} {TSV} 0 {TVpen} N {TVvis} 21 N "VAL**" -T2 {SH 2 / BP + ~} {SV 2 / BP + ~} {BP 2 *} {BP 2 *} 0 {BP 2 /} N V 21 N "○" -{BW 2 / @X2 X2 ~ @X1 BL 2 / @Y2 Y2 ~ @Y1 PT 4 / round @PINS} -{BW PL - 2 / PINS 2 / 0.5 - PP * - STP - 0 max @XSEG} #XSEG must not be less than zero -{BL PL - 2 / PINS 2 / 0.5 - PP * - STP - 0 max @YSEG} #same for YSEG +T2 {SH 2 / BW 2 / max BP + ~} {SV 2 / BL 2 / max BP + ~} {BP 2 *} {BP 2 *} 0 {BP 2 /} N V 21 N "○" +{?PT 14 >}{PT 4 / round @PINSV PINSV @PINSH} +{?PT 14 =}{PT 2 / round 2 - @PINSV 2 @PINSH} +{?PT 14 <}{PT 2 / round 1 - @PINSV 1 @PINSH} +{BW 2 / @X2 X2 ~ @X1 BL 2 / @Y2 Y2 ~ @Y1} +{BW PL - 2 / PINSV 2 / 0.5 - PP * - STP - 0 max @XSEG} #XSEG must not be less than zero +{BL PL - 2 / PINSV 2 / 0.5 - PP * - STP - 0 max @YSEG} #same for YSEG DS {X1} {Y1 YSEG +} {X1 XSEG +} {Y1} {BP} 21 DS {X2} {Y1} {X2 XSEG -} {Y1} {BP} 21 DS {X2} {Y1} {X2} {Y1 YSEG +} {BP} 21 @@ -30,17 +35,22 @@ DS {X1} {Y2} {X1} {Y2 YSEG -} {BP} 21 DS {X2} {Y2} {X2 XSEG -} {Y2} {BP} 21 DS {X2} {Y2} {X2} {Y2 YSEG -} {BP} 21 $PAD -{PN 1 - PINS / floor @ROW} #row=0..3, or 4+ for the exposed pad +{4 @ROW} #row 4 = exposed pad +{?PN PINSV 2 * PINSH 2 * + <=}{3 @ROW} +{?PN PINSV 2 * PINSH + <=}{2 @ROW} +{?PN PINSV PINSH + <=}{1 @ROW} +{?PN PINSV <=}{0 @ROW} {?:STDPAD ROW 4 <} #standard pad -{? ROW 0 = ROW 2 = |}Sh "{PN}" O {PW} {PL} 0 0 0 -{? ROW 1 = ROW 3 = |}Sh "{PN}" O {PW} {PL} 0 0 900 +{900 0 ROW 1 = ROW 3 = | ? @ANGLE} +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 {ANGLE} +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 {ANGLE} {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" -{? ROW 0 =}Po {SH -0.5 *} {PINS -0.5 * 0.5 - PN + PP *} -{? ROW 1 =}Po {PINS -1.5 * 0.5 - PN + PP *} {SV 0.5 *} -{? ROW 2 =}Po {SH 0.5 *} {PINS 2.5 * 0.5 + PN - PP *} -{? ROW 3 =}Po {PINS 3.5 * 0.5 + PN - PP *} {SV -0.5 *} +{? ROW 0 =}Po {SH -0.5 *} {PN PINSV 1 + 2 / - PP *} +{? ROW 1 =}Po {PN PINSV - PINSH 1 + 2 / - PP *} {SV 0.5 *} +{? ROW 2 =}Po {SH 0.5 *} {PN PINSV - PINSH - PINSV 1 + 2 / - ~ PP *} +{? ROW 3 =}Po {PN PINSV 2 * - PINSH - PINSH 1 + 2 / - ~ PP *} {SV -0.5 *} :STDPAD {?:THERMAL ROW 4 >=} #exposed pad {PWA EPDOT / floor 1 max @EPCOLS PLA EPDOT / floor 1 max @EPROWS PWA EPCOLS / @EPW PLA EPROWS / @EPL}# slice size & col/row counts diff --git a/template/QFNX.mt b/template/QFNX.mt index 30f7890..63b67a0 100644 --- a/template/QFNX.mt +++ b/template/QFNX.mt @@ -1,10 +1,12 @@ #version 1 #brief Quad Flat, No pins, Exposed pad -#note With exposed pad (numbered exposed pad). -#pins 17 21 ... +#note With exposed pad ("numbered" exposed pad; so number of pins must be odd). +#pins 9 11 13 15 17 21 ... #flags aux-pad(flag,*) rebuild -#param 33 @PT 0.5 @PP 4.8 @SH 4.8 @SV 0.7 @PW 0.25 @PL 3.2 @PWA 3.2 @PLA \ -# 5.0 @BW 5.0 @BL 0.2 @BP 0.65 @TS 15 @TW 0.2 @STP 25 @PSRA 1.5 @EPDOT +#param 33 @?PT "oval" @PSH \ +# 0.5 @PP 4.8 @SH 4.8 @SV 0.7 @PW 0.25 @PL 3.2 @PWA 3.2 @PLA \ +# 0.2 @STP 25 @PSRA 1.5 @EPDOT 0.2 @BP 0.65 @TS 15 @TW \ +# 5.0 @BW 5.0 @BL #model QFN $MODULE {NAME} AR QFNX @@ -18,10 +20,13 @@ At SMD {TSR abs @TSR TSV abs @TSV TW 100 / TSR * @TRpen TW 100 / TSV * @TVpen} T0 0 {SV PW + 2 / TSR + ~} {TSR} {TSR} 0 {TRpen} N {TRvis} 21 N "REF**" T1 0 {SV PW + 2 / TSV +} {TSV} {TSV} 0 {TVpen} N {TVvis} 21 N "VAL**" -T2 {SH 2 / BP + ~} {SV 2 / BP + ~} {BP 2 *} {BP 2 *} 0 {BP 2 /} N V 21 N "○" -{BW 2 / @X2 X2 ~ @X1 BL 2 / @Y2 Y2 ~ @Y1 PT 1 - 4 / round @PINS} -{BW PL - 2 / PINS 2 / 0.5 - PP * - STP - 0 max @XSEG} #XSEG must not be less than zero -{BL PL - 2 / PINS 2 / 0.5 - PP * - STP - 0 max @YSEG} #same for YSEG +T2 {SH 2 / BW 2 / max BP + ~} {SV 2 / BL 2 / max BP + ~} {BP 2 *} {BP 2 *} 0 {BP 2 /} N V 21 N "○" +{?PT 14 >}{PT 1 - 4 / round @PINSV PINSV @PINSH} +{?PT 14 =}{PT 1 - 2 / round 2 - @PINSV 2 @PINSH} +{?PT 14 <}{PT 1 - 2 / round 1 - @PINSV 1 @PINSH} +{BW 2 / @X2 X2 ~ @X1 BL 2 / @Y2 Y2 ~ @Y1} +{BW PL - 2 / PINSV 2 / 0.5 - PP * - STP - 0 max @XSEG} #XSEG must not be less than zero +{BL PL - 2 / PINSV 2 / 0.5 - PP * - STP - 0 max @YSEG} #same for YSEG DS {X1} {Y1 YSEG +} {X1 XSEG +} {Y1} {BP} 21 DS {X2} {Y1} {X2 XSEG -} {Y1} {BP} 21 DS {X2} {Y1} {X2} {Y1 YSEG +} {BP} 21 @@ -30,17 +35,22 @@ DS {X1} {Y2} {X1} {Y2 YSEG -} {BP} 21 DS {X2} {Y2} {X2 XSEG -} {Y2} {BP} 21 DS {X2} {Y2} {X2} {Y2 YSEG -} {BP} 21 $PAD -{PN 1 - PINS / floor @ROW} #row=0..3, or 4+ for the exposed pad +{4 @ROW} #row 4 = exposed pad +{?PN PINSV 2 * PINSH 2 * + <=}{3 @ROW} +{?PN PINSV 2 * PINSH + <=}{2 @ROW} +{?PN PINSV PINSH + <=}{1 @ROW} +{?PN PINSV <=}{0 @ROW} {?:STDPAD ROW 4 <} #standard pad -{? ROW 0 = ROW 2 = |}Sh "{PN}" O {PW} {PL} 0 0 0 -{? ROW 1 = ROW 3 = |}Sh "{PN}" O {PW} {PL} 0 0 900 +{900 0 ROW 1 = ROW 3 = | ? @ANGLE} +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 {ANGLE} +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 {ANGLE} {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" -{? ROW 0 =}Po {SH -0.5 *} {PINS -0.5 * 0.5 - PN + PP *} -{? ROW 1 =}Po {PINS -1.5 * 0.5 - PN + PP *} {SV 0.5 *} -{? ROW 2 =}Po {SH 0.5 *} {PINS 2.5 * 0.5 + PN - PP *} -{? ROW 3 =}Po {PINS 3.5 * 0.5 + PN - PP *} {SV -0.5 *} +{? ROW 0 =}Po {SH -0.5 *} {PN PINSV 1 + 2 / - PP *} +{? ROW 1 =}Po {PN PINSV - PINSH 1 + 2 / - PP *} {SV 0.5 *} +{? ROW 2 =}Po {SH 0.5 *} {PN PINSV - PINSH - PINSV 1 + 2 / - ~ PP *} +{? ROW 3 =}Po {PN PINSV 2 * - PINSH - PINSH 1 + 2 / - ~ PP *} {SV -0.5 *} :STDPAD {?:THERMAL ROW 4 >=} #exposed pad {PWA EPDOT / floor 1 max @EPCOLS PLA EPDOT / floor 1 max @EPROWS PWA EPCOLS / @EPW PLA EPROWS / @EPL}# slice size & col/row counts diff --git a/template/QFP.mt b/template/QFP.mt index 52ee328..be6b406 100644 --- a/template/QFP.mt +++ b/template/QFP.mt @@ -2,7 +2,9 @@ #brief Quad Flat Package w/ Pins #note Without exposed pad. #pins 28 32 ... -#param 32 @?PT 0.8 @PP 8.5 @SH 8.5 @SV 1.3 @PW 0.45 @PL 0.2 @BP 0.65 @TS 15 @TW 0.2 @STP \ +#param 32 @?PT "oval" @PSH \ +# 0.8 @PP 8.5 @SH 8.5 @SV 1.3 @PW 0.45 @PL 0.2 @STP \ +# 0.2 @BP 0.65 @TS 15 @TW \ # PT 4 / floor 1 - PP * 2.9 + @SH SH @SV \ # SH 1.7 - @BW SV 1.7 - @BL #model QFP @@ -36,8 +38,9 @@ DC {XC} {YC} {XC RAD +} {YC} {BP} 21 {PT 4 / round @PINS} $PAD {PN 1 - PINS / floor @ROW} -{? ROW 0 = ROW 2 = |}Sh "{PN}" O {PW} {PL} 0 0 0 -{? ROW 1 = ROW 3 = |}Sh "{PN}" O {PW} {PL} 0 0 900 +{900 0 ROW 1 = ROW 3 = | ? @ANGLE} +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 {ANGLE} +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 {ANGLE} {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/QFPX.mt b/template/QFPX.mt index 31c5ce3..4f47fc5 100644 --- a/template/QFPX.mt +++ b/template/QFPX.mt @@ -1,10 +1,13 @@ #version 1 #brief Quad Flat Package w/ Pins, Exposed pad -#note With exposed pad. +#note With exposed pad. The exposed pad is a "numbered pin", so the number of pins must be odd. #pins 25 29 ... #flags aux-pad(flag,*) rebuild -#param 33 @PT 0.8 @PP 8.5 @SH 8.5 @SV 1.3 @PW 0.45 @PL 5 @PLA 5 @PWA \ -5# 6.8 @BW 6.8 @BL 0.2 @BP 0.65 @TS 15 @TW 0.2 @STP 25 @PSRA 1.5 @EPDOT +#param 33 @?PT "oval" @PSH \ +# 0.8 @PP 1.3 @PW 0.45 @PL 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW \ +# PT 1 - 4 / floor 1 - PP * 2.9 + @SH SH @SV \ +# SH 1.7 - @BW SV 1.7 - @BL SH 3.5 - @PWA SV 3.5 - @PLA \ +# 25 @PSRA 1.5 @EPDOT #model QFP $MODULE {NAME} AR QFPX @@ -35,8 +38,9 @@ DS {X1} {Y1 OFFS +} {X1 OFFS +} {Y1} {BP} 21 $PAD {PN 1 - PINS / floor @ROW} #row=0..3, or 4+ for the exposed pad {?:STDPAD ROW 4 <} #standard pad -{? ROW 0 = ROW 2 = |}Sh "{PN}" O {PW} {PL} 0 0 0 -{? ROW 1 = ROW 3 = |}Sh "{PN}" O {PW} {PL} 0 0 900 +{900 0 ROW 1 = ROW 3 = | ? @ANGLE} +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 {ANGLE} +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 {ANGLE} {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/SOD.mt b/template/SOD.mt index c34a6c1..b095e9b 100644 --- a/template/SOD.mt +++ b/template/SOD.mt @@ -2,7 +2,8 @@ #brief 2-pin diodes in SOD package #note SOD123, SOD323 and similar. Also suitable for SMA, SMB, SMC packages. #pins 2 -#param 2 @PT 3.25 @PP 0.9 @PW 1.1 @PL 2.8 @BW 1.8 @BL 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW +#param 2 @PT "rect" @PSH 3.25 @PP 0.9 @PW 1.1 @PL 2.8 @BW 1.8 @BL 0.2 @STP \ +# 0.2 @BP 0.65 @TS 15 @TW #model SOD LED_Chip $MODULE {NAME} AR SOD @@ -28,13 +29,14 @@ DS {X2} {Y2} {X2} {Y2 SEGM -} {BP} 21 {X1 BP 1.5 * + @X1} DS {X1} {Y1} {X1} {Y1 SEGM +} {BP} 21 DS {X1} {Y2} {X1} {Y2 SEGM -} {BP} 21 -{PL 0.4 * @Y2 Y2 ~ @Y1 60 sin Y2 * 0.75 * @X2 X2 ~ 0.75 / @X1} -DS {X1} 0 {X2} {Y1} {BP} 21 -DS {X2} {Y1} {X2} {Y2} {BP} 21 -DS {X2} {Y2} {X1} 0 {BP} 21 -DS {X1} {Y1} {X1} {Y2} {BP} 21 +{PL 0.4 * @Y2 Y2 ~ @Y1 60 sin Y2 * 0.75 * @X2 X2 ~ 0.75 / @X1 BW 10 / BP min @PEN} +DS {X1} 0 {X2} {Y1} {PEN} 21 +DS {X2} {Y1} {X2} {Y2} {PEN} 21 +DS {X2} {Y2} {X1} 0 {PEN} 21 +DS {X1} {Y1} {X1} {Y2} {PEN} 21 $PAD -Sh "{PN}" R {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/SON.mt b/template/SON.mt index 9ea9a57..d1c791f 100644 --- a/template/SON.mt +++ b/template/SON.mt @@ -2,7 +2,8 @@ #brief Small Outline, No pins #note Suitable for DFN, SON and WSON parts. Without exposed pad. #pins 6 8 ... -#param 8 @?PT 0.65 @PP 2.75 @SH 0.7 @PW 0.35 @PL 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW \ +#param 8 @?PT "oval" @PSH \ +# 0.65 @PP 0.7 @PW 0.35 @PL 2.75 @SH 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW \ # 2.85 @BW PT 2 / floor 1 - PP * 1.15 + @BL #model SON $MODULE {NAME} @@ -29,7 +30,8 @@ DS {X2} {Y2} {X2} {Y2 SEGM -} {BP} 21 {PL 0.4 * @RAD RAD SH PW - 2 / - STP + @XC RAD BL 2 / - STP + @YC} {? XC -0.1 < }DC {XC} {YC} {XC RAD +} {YC} {BP} 21 $PAD -Sh "{PN}" O {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/SONX.mt b/template/SONX.mt index eb82554..2a35ae9 100644 --- a/template/SONX.mt +++ b/template/SONX.mt @@ -1,10 +1,13 @@ #version 1 #brief Small Outline, No pins, Exposed pad -#note Suitable for SON, WSON and some DFN parts. With exposed pad. +#note Suitable for SON, WSON and some DFN parts. With exposed pad; the pad is numbered, so the number of pins must be odd. #pins 7 9 ... #flags aux-pad(flag,*) rebuild -#param 9 @?PT 0.65 @PP 2.75 @SH 0.7 @PW 0.35 @PL 1.4 @PWA 2.0 @PLA 0.2 @BP 0.65 @TS 15 @TW \ -# 2.85 @BW PT 1 - 2 / floor 1 - PP * 1.15 + @BL 0.2 @STP 25 @PSRA 1.5 @EPDOT +#param 9 @?PT "oval" @PSH \ +# 0.65 @PP 0.7 @PW 0.35 @PL 2.75 @SH 0.2 @STP \ +# 1.4 @PWA 2.0 @PLA 25 @PSRA 1.5 @EPDOT \ +# 0.2 @BP 0.65 @TS 15 @TW \ +# 2.85 @BW PT 1 - 2 / floor 1 - PP * 1.15 + @BL #model SON $MODULE {NAME} AR SONX @@ -31,7 +34,8 @@ DS {X2} {Y2} {X2} {Y2 SEGM -} {BP} 21 {PT 1 - @PINS} $PAD {?:STDPAD PN PINS <=} #standard pad -Sh "{PN}" O {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/SOP.mt b/template/SOP.mt index 90db43a..b754908 100644 --- a/template/SOP.mt +++ b/template/SOP.mt @@ -1,9 +1,10 @@ #version 1 #brief Small Outline w/ Pins #note Suitable for SOIC and SSOP parts. Without exposed pad. -#pins 6 8 ... -#param 8 @?PT 1.27 @PP 5.6 @SH 1.75 @PW 0.65 @PL 0.2 @BP 0.65 @TS 15 @TW 0.2 @STP \ -# 3.6 @BW PT 2 / floor 1 - PP * 1.2 + @BL +#pins 4 6 8 ... +#param 8 @?PT "oval" @PSH \ +# 1.27 @PP 5.6 @SH 1.75 @PW 0.65 @PL 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW \ +# 3.6 @BW PT 2 / floor 1 - PP * 1.2 + @BL #model SOP $MODULE {NAME} AR SOP @@ -30,7 +31,8 @@ DS {BW 2 /} {BL 2 /} {BW 2 / ~} {BL 2 /} {BP} 21 DS {BW 2 / ~} {BL 2 /} {BW 2 / ~} {BL 2 / ~} {BP} 21 DC {XC} {YC} {XC RAD +} {YC} {BP} 21 $PAD -Sh "{PN}" O {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/SOPX.mt b/template/SOPX.mt index 2455ff7..d7d6f0e 100644 --- a/template/SOPX.mt +++ b/template/SOPX.mt @@ -3,8 +3,10 @@ #note Suitable for SOIC and SSOP parts. With exposed pad. #pins 9 11 ... #flags aux-pad(flag,*) rebuild -#param 9 @?PT 1.27 @PP 5.6 @SH 1.75 @PW 0.65 @PL 2.2 @PWA 3.0 @PLA 0.65 @TS 15 @TW \ -# 3.6 @BW PT 1 - 2 / floor 1 - PP * 1.2 + @BL 0.2 @BP 0.2 @STP 25 @PSRA 1.5 @EPDOT +#param 9 @?PT "oval" @PSH \ +# 1.27 @PP 5.6 @SH 1.75 @PW 0.65 @PL 0.2 @STP 2.2 @PWA 3.0 @PLA \ +# 0.2 @BP 0.65 @TS 15 @TW \ +# 3.6 @BW PT 1 - 2 / floor 1 - PP * 1.2 + @BL 25 @PSRA 1.5 @EPDOT #model SOP $MODULE {NAME} AR SOPX @@ -19,12 +21,12 @@ At SMD {TSR abs @TSR TSV abs @TSV TW 100 / TSR * @TRpen TW 100 / TSV * @TVpen} #body size is bound to a maximum, because the drawing must not run over the pads {SH PW - STP 2 * - BW min @BW} -#calculate the position of the (tiny) polarity marker -{BW 6 / 0.1 - 0.3 min @RAD 0.25 @OFFS RAD BW 2 / - OFFS + @XC RAD BL 2 / - OFFS + @YC} +#calculate the position of the polarity marker +{BW 4 / 0.1 - 0.5 min @RAD 0.25 @OFFS RAD BW 2 / - OFFS + @XC RAD BL 2 / - OFFS + @YC} #draw labels and body -T0 0 {BL 2 / TSR + ~} {TSR} {TSR} 0 {TRpen} N {TRvis} 21 N "REF**" +T0 0 {BL 2 / TSR + 0.2 + ~} {TSR} {TSR} 0 {TRpen} N {TRvis} 21 N "REF**" T1 0 {BL 2 / TSV +} {TSV} {TSV} 0 {TVpen} N {TVvis} 21 N "VAL**" -T2 {BW 2 / ~ BP 2 * -} {PT 1 - -0.25 * 0.5 + PP * PL - BP -} {BP 2 *} {BP 2 *} 0 {BP 2 /} N V 21 N "○" +T2 {BW 2 / ~ BP 2 * -} {PT 1 - -0.25 * 0.5 + PP * PL - BP - 0.1 -} {BP 2 *} {BP 2 *} 0 {BP 2 /} N V 21 N "○" DS {BW 2 / ~} {BL 2 / ~} {BW 2 /} {BL 2 / ~} {BP} 21 DS {BW 2 /} {BL 2 / ~} {BW 2 /} {BL 2 /} {BP} 21 DS {BW 2 /} {BL 2 /} {BW 2 / ~} {BL 2 /} {BP} 21 @@ -33,7 +35,8 @@ DC {XC} {YC} {XC RAD +} {YC} {BP} 21 {PT 1 - @PINS} $PAD {?:STDPAD PN PINS <=} #standard pad -Sh "{PN}" O {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/SOT23.mt b/template/SOT23.mt index 715b96e..221bb9a 100644 --- a/template/SOT23.mt +++ b/template/SOT23.mt @@ -2,7 +2,8 @@ #brief SOT23 and similar #note Supports 3, 5, 6 and 8-pin parts, but not 4-pin. Also suitable for SOT353 series and others. #pins 3 5 6 8 -#param 3 @?PT 0.95 @PP 2.3 @SH 1.1 @PW 1.0 @PL 0.8 @BW 2.9 @BL 0.2 @BP 0.65 @TS 15 @TW +#param 3 @?PT "rect" @PSH \ +# 0.95 @PP 1.1 @PW 1.0 @PL 2.3 @SH 0.8 @BW 2.9 @BL 0.2 @BP 0.65 @TS 15 @TW #model SOT23 $MODULE {NAME} AR SOT23 @@ -23,7 +24,8 @@ DS {BW 2 /} {BL 2 /} {BW 2 / ~} {BL 2 /} {BP} 21 DS {BW 2 / ~} {BL 2 /} {BW 2 / ~} {BL 2 / ~} {BP} 21 DC {BW 4 / 0.15 - @RAD RAD BW 2 / - 0.15 +} {RAD BL 2 / - 0.2 +} {0.15 BW 2 / -} {0.2 BL 2 / - 0.1 +} {BP} 21 $PAD -Sh "{PN}" R {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/XTAL_Chip.mt b/template/XTAL_Chip.mt index d006f9c..02ce5a3 100644 --- a/template/XTAL_Chip.mt +++ b/template/XTAL_Chip.mt @@ -2,7 +2,7 @@ #brief Crystal or oscillator, clockwise pin numbering #note 2-pin and 4-pin variants in clockwise numbering, SMD version #pins 2 4 -#param 4 @?PT \ +#param 4 @?PT "rect" @PSH \ # 2.2 @PP 4.2 @SH 1.6 @PW 1.4 @PL 0.2 @BP 0.65 @TS 15 @TW 0.2 @STP \ # 5.2 @BW 3.4 @BL #flags rebuild @@ -46,7 +46,8 @@ DS {X2} {Y1} {X2} {Y2} {BP} 21 :VERLINES {PP 2 / 0 PT 2 > ? @VOFFS} $PAD -Sh "{PN}" R {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/XTAL_Chip_Rev.mt b/template/XTAL_Chip_Rev.mt index 817412c..10cec25 100644 --- a/template/XTAL_Chip_Rev.mt +++ b/template/XTAL_Chip_Rev.mt @@ -2,7 +2,7 @@ #brief Crystal or oscillator, counter-clockwise pin numbering #note 2-pin and 4-pin variants in counter-clockwise numbering, SMD version #pins 2 4 -#param 4 @?PT \ +#param 4 @?PT "rect" @PSH \ # 2.2 @PP 4.2 @SH 1.6 @PW 1.4 @PL 0.2 @BP 0.65 @TS 15 @TW 0.1 @STP \ # 5.2 @BW 3.4 @BL #flags rebuild @@ -45,7 +45,8 @@ DS {X1} {Y1} {X1} {Y2} {BP} 21 DS {X2} {Y1} {X2} {Y2} {BP} 21 :VERLINES $PAD -Sh "{PN}" R {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/XTAL_Metal_can.mt b/template/XTAL_Metal_can.mt index e29d632..b11d670 100644 --- a/template/XTAL_Metal_can.mt +++ b/template/XTAL_Metal_can.mt @@ -3,7 +3,9 @@ #note 2-pin, metal can, SMD. #prefix XTAL #pins 2 -#param 2 @PT 9.5 @PP 5.5 @PW 2.0 @PL 11.6 @BW 4.4 @BL 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW +#param 2 @PT "rect" @PSH \ +# 9.5 @PP 5.5 @PW 2.0 @PL 0.2 @STP 0.2 @BP 0.65 @TS 15 @TW \ +# 11.6 @BW 4.4 @BL #model XTAL_Metal_can $MODULE {NAME} AR XTAL_Metal_Can @@ -26,7 +28,8 @@ DS {X1} {Y2} {X1} {Y2 SEGM -} {BP} 21 DS {X2} {Y1} {X2} {Y1 SEGM +} {BP} 21 DS {X2} {Y2} {X2} {Y2 SEGM -} {BP} 21 $PAD -Sh "{PN}" R {PW} {PL} 0 0 0 +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} Dr 0 0 0 At SMD N 00888000 Ne 0 "" diff --git a/template/ZIF-FPC.mt b/template/ZIF-FPC.mt index c17e433..2fe41fa 100644 --- a/template/ZIF-FPC.mt +++ b/template/ZIF-FPC.mt @@ -3,8 +3,9 @@ #note ZIF socket with horizontal entry #pins 4 6 ... #flags aux-pad(mechanic,2) rebuild -#param 8 @PT 1.0 @PP 2.75 @SH 11.6 @SV 1.3 @PW 0.6 @PL 1.8 @PLA 2 @PWA \ -# 5.8 @BW 12.6 @BL 0.2 @BP 0.65 @TS 15 @TW 0.2 @STP +#param 8 @?PT "oval" @PSH \ +# 1.0 @PP 1.3 @PW 0.6 @PL 2.75 @SH 11.6 @SV 1.8 @PLA 2 @PWA \ +# 5.8 @BW 12.6 @BL 0.2 @BP 0.65 @TS 15 @TW 0.2 @STP #model ZIF-FPC $MODULE {NAME} AR ZIF-FPC @@ -32,7 +33,10 @@ DS {X1} {DY1} {X1} {Y1} {BP} 21 DS {X1} {Y1} {DX1} {Y1} {BP} 21 # pads $PAD -{? PN PT <=}Sh "{PN}" O {PW} {PL} 0 0 0 +{?:STDPAD PN PT <=} #standard pad +{?PRR 0 <}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 +{?PRR 0 >=}Sh "{PN}" {PSH} {PW} {PL} 0 0 0 {PRR} +:STDPAD {? PN PT >}Sh "" R {PWA} {PLA} 0 0 0 Dr 0 0 0 At SMD N 00888000