KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcbnew/dialogs/dialog_tablecell_properties.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <wx/hyperlink.h>
21
23#include <widgets/font_choice.h>
25#include <confirm.h>
26#include <board_commit.h>
28#include <board.h>
29#include <footprint.h>
30#include <pcb_textbox.h>
31#include <pcb_tablecell.h>
32#include <pcb_table.h>
33#include <project.h>
34#include <pcb_edit_frame.h>
36#include <tool/tool_manager.h>
37#include <tools/pcb_actions.h>
38#include <scintilla_tricks.h>
40
42 std::vector<PCB_TABLECELL*> aCells ) :
44 m_frame( aFrame ),
45 m_table( nullptr ),
46 m_cells( std::move( aCells ) ),
50 m_marginLeft( aFrame, nullptr, m_marginLeftCtrl, nullptr ),
51 m_marginTop( aFrame, nullptr, m_marginTopCtrl, m_marginTopUnits ),
52 m_marginRight( aFrame, nullptr, m_marginRightCtrl, nullptr ),
53 m_marginBottom( aFrame, nullptr, m_marginBottomCtrl, nullptr ),
55 m_cellTextIsGenerated( false ),
57{
58 wxASSERT( m_cells.size() > 0 && m_cells[0] );
59
60 m_cellText->SetEOLMode( wxSTC_EOL_LF );
61
62 // Wrapping is display-only and does not insert newlines into the stored text.
63 m_cellText->SetWrapMode( wxSTC_WRAP_WORD );
64 m_cellText->SetWrapVisualFlags( wxSTC_WRAPVISUALFLAG_END );
65 m_cellText->SetWrapIndentMode( wxSTC_WRAPINDENT_INDENT );
66
67#ifdef _WIN32
68 // Without this setting, on Windows, some esoteric unicode chars create display issue
69 // in a wxStyledTextCtrl.
70 // for SetTechnology() info, see https://www.scintilla.org/ScintillaDoc.html#SCI_SETTECHNOLOGY
71 m_cellText->SetTechnology( wxSTC_TECHNOLOGY_DIRECTWRITE );
72#endif
73
75 m_cellText, wxT( "{}" ), false,
76 // onAcceptFn
77 [this]( wxKeyEvent& aEvent )
78 {
79 wxPostEvent( this, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
80 },
81 // onCharFn
82 [this]( wxStyledTextEvent& aEvent )
83 {
84 m_scintillaTricks->DoTextVarAutocomplete(
85 // getTokensFn
86 [this]( const wxString& xRef, wxArrayString* tokens )
87 {
88 m_frame->GetContextualTextVars( m_table, xRef, tokens );
89 } );
90 } );
91
92 // A hack which causes Scintilla to auto-size the text editor canvas
93 // See: https://github.com/jacobslusser/ScintillaNET/issues/216
94 m_cellText->SetScrollWidth( 1 );
95 m_cellText->SetScrollWidthTracking( true );
96
98
99 m_table = static_cast<PCB_TABLE*>( m_cells[0]->GetParent() );
100
101 // A generated table reports the board. Its text is generated, so only the formatting on
102 // this page is the user's to change
103 if( m_table->IsGenerated() )
104 {
106 m_cellText->SetReadOnly( true );
108 }
109
110 m_hAlignLeft->SetIsRadioButton();
112 m_hAlignCenter->SetIsRadioButton();
114 m_hAlignRight->SetIsRadioButton();
116
117 m_separator0->SetIsSeparator();
118
119 m_vAlignTop->SetIsRadioButton();
121 m_vAlignCenter->SetIsRadioButton();
123 m_vAlignBottom->SetIsRadioButton();
125
126 m_separator1->SetIsSeparator();
127
128 m_bold->SetIsCheckButton();
130 m_italic->SetIsCheckButton();
132
133 m_autoTextThickness->SetIsCheckButton();
135
137 Layout();
138
139 m_helpWindow = nullptr;
140
141 Connect( wxEVT_CHAR_HOOK, wxKeyEventHandler( DIALOG_TABLECELL_PROPERTIES::OnCharHook ), nullptr, this );
142
143 m_hAlignLeft->Bind( wxEVT_BUTTON, &DIALOG_TABLECELL_PROPERTIES::onHAlignButton, this );
146 m_vAlignTop->Bind( wxEVT_BUTTON, &DIALOG_TABLECELL_PROPERTIES::onVAlignButton, this );
149
150 // Now all widgets have the size fixed, call FinishDialogSettings
152}
153
155{
156 Disconnect( wxEVT_CHAR_HOOK, wxKeyEventHandler( DIALOG_TABLECELL_PROPERTIES::OnCharHook ), nullptr, this );
157
158 delete m_scintillaTricks;
159 m_scintillaTricks = nullptr;
160
161 if( m_helpWindow )
162 m_helpWindow->Destroy();
163}
164
165
167{
168 if( !wxDialog::TransferDataToWindow() )
169 return false;
170
171 // Scintilla drops every write while it is read-only, so generated text would arrive here
172 // as an empty control and be written back over the cell on OK
173 m_cellText->SetReadOnly( false );
174
175 bool firstCell = true;
178 int textThickness = 0;
179 int basePenWidth = 0;
180
181 for( PCB_TABLECELL* cell : m_cells )
182 {
183 if( firstCell )
184 {
185 wxString text = cell->GetText();
186
187 // show text variable cross-references in a human-readable format
188 if( BOARD* board = cell->GetBoard() )
189 text = board->ConvertKIIDsToCrossReferences( text );
190
191 m_cellTextCtrl->SetValue( text );
192
193 m_fontCtrl->SetFontSelection( cell->GetFont() );
194 m_textWidth.SetValue( cell->GetTextWidth() );
195 m_textHeight.SetValue( cell->GetTextHeight() );
196 textThickness = cell->GetTextThickness();
197 basePenWidth = GetPenSizeForNormal( cell->GetTextWidth() );
198
199 hAlign = cell->GetHorizJustify();
200 vAlign = cell->GetVertJustify();
201
202 m_marginLeft.SetValue( cell->GetMarginLeft() );
203 m_marginTop.SetValue( cell->GetMarginTop() );
204 m_marginRight.SetValue( cell->GetMarginRight() );
205 m_marginBottom.SetValue( cell->GetMarginBottom() );
206
207 m_bold->Check( cell->IsBold() );
208 m_mixedBoldSetting = false;
209
210 m_italic->Check( cell->IsItalic() );
211 m_mixedItalicSetting = false;
212
213 m_cbKnockout->SetValue( cell->IsKnockout() );
214
215 firstCell = false;
216 }
217 else
218 {
219 wxString text = cell->GetText();
220
221 // show text variable cross-references in a human-readable format
222 if( BOARD* board = cell->GetBoard() )
223 text = board->ConvertKIIDsToCrossReferences( text );
224
225 if( text != m_cellTextCtrl->GetValue() )
227
228 if( cell->GetFont() != m_fontCtrl->GetFontSelection( cell->IsBold(), cell->IsItalic() ) )
229 m_fontCtrl->SetSelection( -1 );
230
231 if( cell->GetTextWidth() != m_textWidth.GetValue() )
233
234 if( cell->GetTextHeight() != m_textHeight.GetValue() )
236
237 if( cell->GetTextThickness() != textThickness )
238 textThickness = -1;
239
240 if( GetPenSizeForNormal( cell->GetTextWidth() ) != basePenWidth )
241 basePenWidth = -1;
242
243 if( cell->IsBold() != m_bold->IsChecked() )
244 {
245 m_mixedBoldSetting = true;
246 m_bold->Check( false );
247 }
248
249 if( cell->IsItalic() != m_italic->IsChecked() )
250 {
252 m_italic->Check( false );
253 }
254
255 if( cell->GetHorizJustify() != hAlign )
257
258 if( cell->GetVertJustify() != vAlign )
260
261 if( cell->GetMarginLeft() != m_marginLeft.GetIntValue() )
263
264 if( cell->GetMarginTop() != m_marginTop.GetIntValue() )
266
267 if( cell->GetMarginRight() != m_marginRight.GetIntValue() )
269
270 if( cell->GetMarginBottom() != m_marginBottom.GetIntValue() )
272
273 if( cell->IsKnockout() != m_cbKnockout->GetValue() )
274 m_cbKnockout->Set3StateValue( wxCHK_UNDETERMINED );
275 }
276
277 m_hAlignLeft->Check( false );
278 m_hAlignCenter->Check( false );
279 m_hAlignRight->Check( false );
280
281 switch( hAlign )
282 {
283 case GR_TEXT_H_ALIGN_LEFT: m_hAlignLeft->Check(); break;
284 case GR_TEXT_H_ALIGN_CENTER: m_hAlignCenter->Check(); break;
285 case GR_TEXT_H_ALIGN_RIGHT: m_hAlignRight->Check(); break;
287 }
288
289 m_vAlignTop->Check( false );
290 m_vAlignCenter->Check( false );
291 m_vAlignBottom->Check( false );
292
293 switch( vAlign )
294 {
295 case GR_TEXT_V_ALIGN_TOP: m_vAlignTop->Check(); break;
296 case GR_TEXT_V_ALIGN_CENTER: m_vAlignCenter->Check(); break;
297 case GR_TEXT_V_ALIGN_BOTTOM: m_vAlignBottom->Check(); break;
299 }
300 }
301
303 m_autoTextThickness->Check( false );
304
305 if( textThickness == 0 )
306 {
307 // Show the base width (frozen if auto is unchecked); Bold scales it at render time.
308 if( basePenWidth > 0 )
309 m_textThickness.SetValue( basePenWidth );
310
311 m_autoTextThickness->Check( true );
312 m_textThickness.Enable( false );
313 }
314 else if( textThickness > 0 )
315 {
316 m_textThickness.SetValue( textThickness );
317 }
318
319 m_cellText->SetReadOnly( m_cellTextIsGenerated );
320
321 return true;
322}
323
324void DIALOG_TABLECELL_PROPERTIES::onTextSize( wxCommandEvent& aEvent )
325{
326 if( m_autoTextThickness->IsChecked() )
327 m_textThickness.SetValue( GetPenSizeForNormal( m_textWidth.GetIntValue() ) );
328}
329
330
332{
333 if( aEvent.IsChecked() )
334 {
335 m_autoTextThickness->Check( true );
336
337 if( !m_textWidth.IsIndeterminate() && !m_textHeight.IsIndeterminate() )
338 {
339 wxCommandEvent dummy;
340 onTextSize( dummy );
341 }
342
343 m_textThickness.Enable( false );
344 }
345 else
346 {
347 m_textThickness.Enable( true );
348 }
349}
350
351
352void DIALOG_TABLECELL_PROPERTIES::onBold(wxCommandEvent& aEvent)
353{
354 m_mixedBoldSetting = false;
355 aEvent.Skip();
356}
357
358
359void DIALOG_TABLECELL_PROPERTIES::onItalic(wxCommandEvent& aEvent)
360{
361 m_mixedItalicSetting = false;
362 aEvent.Skip();
363}
364
365
366void DIALOG_TABLECELL_PROPERTIES::onHAlignButton( wxCommandEvent& aEvent )
367{
369 {
370 if( btn->IsChecked() && btn != aEvent.GetEventObject() )
371 btn->Check( false );
372 }
373}
374
375
376void DIALOG_TABLECELL_PROPERTIES::onVAlignButton( wxCommandEvent& aEvent )
377{
378 for( BITMAP_BUTTON* btn : { m_vAlignTop, m_vAlignCenter, m_vAlignBottom } )
379 {
380 if( btn->IsChecked() && btn != aEvent.GetEventObject() )
381 btn->Check( false );
382 }
383}
384
385
387{
388 if( !wxDialog::TransferDataFromWindow() )
389 return false;
390
391 BOARD_COMMIT commit( m_frame );
392 commit.Modify( m_table );
393
394 // If no other command in progress, prepare undo command
395 // (for a command in progress, will be made later, at the completion of command)
396 bool pushCommit = ( m_table->GetEditFlags() == 0 );
397
398 // Set IN_EDIT flag to force undo/redo/abort proper operation and avoid new calls to
399 // SaveCopyInUndoList for the same text if is moved, and then rotated, edited, etc....
400 if( !pushCommit )
401 m_table->SetFlags( IN_EDIT );
402
403 for( PCB_TABLECELL* cell : m_cells )
404 {
406 {
407 wxString txt = m_cellTextCtrl->GetValue();
408
409 // convert any text variable cross-references to their UUIDs
410 if( BOARD* board = cell->GetBoard() )
411 txt = board->ConvertCrossReferencesToKIIDs( txt );
412
413#ifdef __WXMAC__
414 // On macOS CTRL+Enter produces '\r' instead of '\n' regardless of EOL setting.
415 // Replace it now.
416 txt.Replace( "\r", "\n" );
417#elif defined( __WINDOWS__ )
418 // On Windows, a new line is coded as \r\n. We use only \n in kicad files and in
419 // drawing routines so strip the \r char.
420 txt.Replace( "\r", "" );
421#endif
422
423 cell->SetText( txt );
424 }
425
426 if( !m_mixedBoldSetting )
427 cell->SetBold( m_bold->IsChecked() );
428
430 cell->SetItalic( m_italic->IsChecked() );
431
432 if( m_cbKnockout->Get3StateValue() == wxCHK_CHECKED )
433 cell->SetIsKnockout( true );
434 else if( m_cbKnockout->Get3StateValue() == wxCHK_UNCHECKED )
435 cell->SetIsKnockout( false );
436
437 if( m_fontCtrl->HaveFontSelection() )
438 cell->SetFont( m_fontCtrl->GetFontSelection( cell->IsBold(), cell->IsItalic() ) );
439
440 if( !m_textWidth.IsIndeterminate() )
441 cell->SetTextWidth( m_textWidth.GetIntValue() );
442
443 if( !m_textHeight.IsIndeterminate() )
444 cell->SetTextHeight( m_textHeight.GetIntValue() );
445
446 if( m_autoTextThickness->IsChecked() )
447 cell->SetAutoThickness( true );
448 else if( !m_textThickness.IsIndeterminate() )
449 cell->SetTextThickness( m_textThickness.GetIntValue() );
450
451 if( m_hAlignLeft->IsChecked() )
452 cell->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
453 else if( m_hAlignRight->IsChecked() )
454 cell->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
455 else if( m_hAlignCenter->IsChecked() )
456 cell->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
457
458 if( m_vAlignTop->IsChecked() )
459 cell->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
460 else if( m_vAlignBottom->IsChecked() )
461 cell->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
462 else if( m_vAlignCenter->IsChecked() )
463 cell->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
464
465 if( !m_marginLeft.IsIndeterminate() )
466 cell->SetMarginLeft( m_marginLeft.GetIntValue() );
467
468 if( !m_marginTop.IsIndeterminate() )
469 cell->SetMarginTop( m_marginTop.GetIntValue() );
470
471 if( !m_marginRight.IsIndeterminate() )
472 cell->SetMarginRight( m_marginRight.GetIntValue() );
473
474 if( !m_marginBottom.IsIndeterminate() )
475 cell->SetMarginBottom( m_marginBottom.GetIntValue() );
476 }
477
478 if( !commit.Empty() )
479 commit.Push( _( "Edit Table Cell Properties" ), SKIP_CONNECTIVITY );
480
482 return true;
483}
484
485
486void DIALOG_TABLECELL_PROPERTIES::onEditTable( wxCommandEvent& aEvent )
487{
489 {
491 Close();
492 }
493}
494
495
496void DIALOG_TABLECELL_PROPERTIES::onSyntaxHelp( wxHyperlinkEvent& aEvent )
497{
498 if( m_helpWindow )
499 {
500 m_helpWindow->Raise();
501 m_helpWindow->Show( true );
502 return;
503 }
504
506}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
@ text_align_right
@ text_valign_top
@ text_align_left
@ text_valign_center
@ text_align_center
@ text_valign_bottom
@ edit_cmp_symb_links
A bitmap button widget that behaves like an AUI toolbar item's button when it is drawn.
void SetInitialFocus(wxWindow *aWindow)
Sets the window (usually a wxTextCtrl) that should be focused when the dialog is shown.
Definition dialog_shim.h:94
void SetupStandardButtons(std::map< int, wxString > aLabels={})
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
virtual void OnCharHook(wxKeyEvent &aEvt)
DIALOG_TABLECELL_PROPERTIES_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Table Cell Properties"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
bool m_cellTextIsGenerated
Generated cell text, shown so the user can read it but not theirs to change.
void onAutoTextThickness(wxCommandEvent &aEvent) override
DIALOG_TABLECELL_PROPERTIES(SCH_EDIT_FRAME *aParentFrame, std::vector< SCH_TABLECELL * > aCells)
void onSyntaxHelp(wxHyperlinkEvent &aEvent) override
Common, abstract interface for edit frames.
static HTML_MESSAGE_BOX * ShowSyntaxHelp(wxWindow *aParentWindow)
Display a syntax help window for text variables and expressions.
Definition pcb_text.cpp:899
Add cut/copy/paste, dark theme, autocomplete and brace highlighting to a wxStyleTextCtrl instance.
This file is part of the common library.
#define _(s)
#define IN_EDIT
Item currently edited.
int GetPenSizeForNormal(int aTextSize)
Definition gr_text.cpp:57
STL namespace.
#define SKIP_CONNECTIVITY
Definition sch_commit.h:41
std::vector< FAB_LAYER_COLOR > dummy
GR_TEXT_H_ALIGN_T
This is API surface mapped to common.types.HorizontalAlignment.
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_H_ALIGN_INDETERMINATE
GR_TEXT_V_ALIGN_T
This is API surface mapped to common.types.VertialAlignment.
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_INDETERMINATE
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
#define INDETERMINATE_STATE
Used for holding indeterminate values, such as with multiple selections holding different values or c...
Definition ui_common.h:46