KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_label_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 (C) 2013 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2013 Wayne Stambaugh <[email protected]>
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <bitmaps.h>
23#include <template_fieldnames.h>
25#include <widgets/font_choice.h>
30#include <sch_edit_frame.h>
31#include <tool/tool_manager.h>
32#include <gr_text.h>
33#include <confirm.h>
34#include <schematic.h>
37#include <string_utils.h>
38#include <kiface_base.h>
39#include <sch_label.h>
40#include <sch_commit.h>
41
42
45 m_Parent( aParent ),
46 m_currentLabel( aLabel ),
47 m_activeTextEntry( nullptr ),
48 m_netNameValidator( true ),
49 m_fields( nullptr ),
51 m_helpWindow( nullptr ),
52 m_labelList( nullptr )
53{
54 COLOR_SETTINGS* colorSettings = m_Parent->GetColorSettings();
55 COLOR4D schematicBackground = colorSettings->GetColor( LAYER_SCHEMATIC_BACKGROUND );
56
57 std::vector<EMBEDDED_FILES*> embeddedFilesStack;
58 embeddedFilesStack.push_back( m_currentLabel->Schematic() );
59
60 m_fields = new FIELDS_GRID_TABLE( this, aParent, m_grid, m_currentLabel, embeddedFilesStack );
63
64 if( m_currentLabel->Type() == SCH_GLOBAL_LABEL_T || m_currentLabel->Type() == SCH_LABEL_T )
65 {
66 m_activeTextEntry = m_valueCombo;
67 SetInitialFocus( m_valueCombo->GetTextCtrl() );
68
69 m_labelSingleLine->Show( false );
70 m_valueSingleLine->Show( false );
71 }
72 else if( m_currentLabel->Type() == SCH_HIER_LABEL_T )
73 {
74 m_activeTextEntry = m_valueSingleLine;
75 SetInitialFocus( m_valueSingleLine );
76
77 m_labelCombo->Show( false );
78 m_valueCombo->Show( false );
79 }
80 else if( m_currentLabel->Type() == SCH_DIRECTIVE_LABEL_T )
81 {
82 SetInitialFocus( m_grid );
83 m_delayedFocusRow = 0;
84
85 m_labelSingleLine->Show( false );
86 m_valueSingleLine->Show( false );
87 m_labelCombo->Show( false );
88 m_valueCombo->Show( false );
89 m_syntaxHelp->Show( false );
90 m_textEntrySizer->Show( false );
91 m_labelCombo->Show( false );
92 m_valueCombo->Show( false );
93 m_cbMultiLine->Show( false );
94
95 m_textSizeLabel->SetLabel( _( "Pin length:" ) );
96 }
97
98 if( !aNew )
99 m_cbMultiLine->Show( false );
100
101 // multiline set of labels can be used only to create new labels
102 m_multilineAllowed = aNew && m_cbMultiLine->IsShown();
103
104 switch( m_currentLabel->Type() )
105 {
106 case SCH_GLOBAL_LABEL_T: SetTitle( _( "Global Label Properties" ) ); break;
107 case SCH_HIER_LABEL_T: SetTitle( _( "Hierarchical Label Properties" ) ); break;
108 case SCH_LABEL_T: SetTitle( _( "Label Properties" ) ); break;
109 case SCH_DIRECTIVE_LABEL_T: SetTitle( _( "Directive Label Properties" ) ); break;
110 case SCH_SHEET_PIN_T: SetTitle( _( "Hierarchical Sheet Pin Properties" ) ); break;
111 default: UNIMPLEMENTED_FOR( m_currentLabel->GetClass() ); break;
112 }
113
114 m_grid->SetTable( m_fields );
115 m_grid->PushEventHandler( new FIELDS_GRID_TRICKS( m_grid, this, embeddedFilesStack,
116 [&]( wxCommandEvent& aEvent )
117 {
118 OnAddField( aEvent );
119 } ) );
120 m_grid->SetSelectionMode( wxGrid::wxGridSelectRows );
121 m_grid->ShowHideColumns( "0 1 2 3 4 5 6 7" );
122 m_shownColumns = m_grid->GetShownColumns();
123
124 // Configure button logos
129
130 m_separator1->SetIsSeparator();
131
132 m_bold->SetIsCheckButton();
134 m_italic->SetIsCheckButton();
136
137 m_separator2->SetIsSeparator();
138
139 m_spin0->SetIsRadioButton();
140 m_spin1->SetIsRadioButton();
141 m_spin2->SetIsRadioButton();
142 m_spin3->SetIsRadioButton();
143
144 m_separator3->SetIsSeparator();
145
146 m_textColorSwatch->SetDefaultColor( COLOR4D::UNSPECIFIED );
147 m_textColorSwatch->SetSwatchBackground( schematicBackground );
148
149 // Show/hide relevant controls
151 {
152 m_dot->Hide();
153 m_circle->Hide();
154 m_diamond->Hide();
155 m_rectangle->Hide();
156
157 m_spin0->SetBitmap( KiBitmapBundle( BITMAPS::label_align_left ) );
158 m_spin1->SetBitmap( KiBitmapBundle( BITMAPS::label_align_right ) );
159 m_spin2->SetBitmap( KiBitmapBundle( BITMAPS::label_align_bottom ) );
160 m_spin3->SetBitmap( KiBitmapBundle( BITMAPS::label_align_top ) );
161 }
162 else if( m_currentLabel->Type() == SCH_DIRECTIVE_LABEL_T )
163 {
164 m_input->Hide();
165 m_output->Hide();
166 m_bidirectional->Hide();
167 m_triState->Hide();
168 m_passive->Hide();
169
170 m_fontLabel->SetLabel( _( "Orientation:" ) );
171 m_fontCtrl->Hide();
172 m_separator1->Hide();
173 m_bold->Hide();
174 m_italic->Hide();
175 m_separator2->Hide();
176 m_spin0->SetBitmap( KiBitmapBundle( BITMAPS::pinorient_down ) );
177 m_spin1->SetBitmap( KiBitmapBundle( BITMAPS::pinorient_up ) );
178 m_spin2->SetBitmap( KiBitmapBundle( BITMAPS::pinorient_right ) );
179 m_spin3->SetBitmap( KiBitmapBundle( BITMAPS::pinorient_left ) );
180 m_separator3->Hide();
181
182 m_formattingGB->Detach( m_fontCtrl );
183 m_formattingGB->Detach( m_iconBar );
184 m_formattingGB->Add( m_iconBar, wxGBPosition( 0, 1 ), wxGBSpan( 1, 1 ), wxEXPAND | wxRIGHT, 5 );
185 }
186 else
187 {
188 m_shapeSizer->Show( false );
189
190 m_spin0->SetBitmap( KiBitmapBundle( BITMAPS::text_align_left ) );
191 m_spin1->SetBitmap( KiBitmapBundle( BITMAPS::text_align_right ) );
192 m_spin2->SetBitmap( KiBitmapBundle( BITMAPS::text_align_bottom ) );
193 m_spin3->SetBitmap( KiBitmapBundle( BITMAPS::text_align_top ) );
194 }
195
196 if( !m_currentLabel->AutoRotateOnPlacementSupported() )
197 {
198 m_autoRotate->Hide();
199 wxSizer* parentSizer = m_autoRotate->GetContainingSizer();
200 parentSizer->Detach( m_autoRotate );
201 parentSizer->Layout();
202 }
203
205
206 // DIALOG_SHIM needs a unique hash_key because classname is not sufficient because the
207 // various versions have different controls so we want to store sizes for each version.
208 m_hash_key = TO_UTF8( GetTitle() );
209
210 m_spin0->Bind( wxEVT_BUTTON, &DIALOG_LABEL_PROPERTIES::onSpinButton, this );
211 m_spin1->Bind( wxEVT_BUTTON, &DIALOG_LABEL_PROPERTIES::onSpinButton, this );
212 m_spin2->Bind( wxEVT_BUTTON, &DIALOG_LABEL_PROPERTIES::onSpinButton, this );
213 m_spin3->Bind( wxEVT_BUTTON, &DIALOG_LABEL_PROPERTIES::onSpinButton, this );
214
215 // wxFormBuilder doesn't include this event...
216 m_grid->Connect( wxEVT_GRID_CELL_CHANGING,
217 wxGridEventHandler( DIALOG_LABEL_PROPERTIES::OnGridCellChanging ), nullptr,
218 this );
219
220 // Now all widgets have the size fixed, call FinishDialogSettings
222}
223
224
226{
227 // Prevents crash bug in wxGrid's d'tor
228 m_grid->DestroyTable( m_fields );
229
230 m_grid->Disconnect( wxEVT_GRID_CELL_CHANGING,
231 wxGridEventHandler( DIALOG_LABEL_PROPERTIES::OnGridCellChanging ), nullptr,
232 this );
233
234 // Delete the GRID_TRICKS.
235 m_grid->PopEventHandler( true );
236
237 if( m_helpWindow )
238 m_helpWindow->Destroy();
239}
240
241
243{
244 if( !wxDialog::TransferDataToWindow() )
245 return false;
246
247 // Respond to previously-saved state of multilable checkbox
248 wxCommandEvent dummy;
250
251 wxString text;
252
254 {
255 // show control characters in a human-readable format
256 text = UnescapeString( m_currentLabel->GetText() );
257
258 // show text variable cross-references in a human-readable format
259 text = m_currentLabel->Schematic()->ConvertKIIDsToRefs( text );
260 }
261
262 if( m_currentLabel->Type() == SCH_GLOBAL_LABEL_T || m_currentLabel->Type() == SCH_LABEL_T )
263 {
264 // Load the combobox with the existing labels of the same type
265 std::set<wxString> existingLabels;
266 SCH_SCREENS allScreens( m_Parent->Schematic().Root() );
267
268 for( SCH_SCREEN* screen = allScreens.GetFirst(); screen; screen = allScreens.GetNext() )
269 {
270 for( SCH_ITEM* item : screen->Items().OfType( m_currentLabel->Type() ) )
271 {
272 const SCH_LABEL_BASE* label = static_cast<const SCH_LABEL_BASE*>( item );
273 existingLabels.insert( UnescapeString( label->GetText() ) );
274 }
275
276 // Add global power labels from power symbols
277 if( m_currentLabel->Type() == SCH_GLOBAL_LABEL_T )
278 {
279 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_LOCATE_POWER_T ) )
280 {
281 const SCH_SYMBOL* power = static_cast<const SCH_SYMBOL*>( item );
282
283 // Ensure the symbol has the Power (i.e. equivalent to a global label
284 // before adding its value in list
285 if( power->IsSymbolLikePowerGlobalLabel() )
286 {
287 const SCH_FIELD* valueField = power->GetField( FIELD_T::VALUE );
288 existingLabels.insert( UnescapeString( valueField->GetText() ) );
289 }
290 }
291 }
292
293 // Add local power labels from power symbols
294 if( m_currentLabel->Type() == SCH_LABEL_T )
295 {
296 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_LOCATE_POWER_T ) )
297 {
298 const SCH_SYMBOL* power = static_cast<const SCH_SYMBOL*>( item );
299
300 // Ensure the symbol has the Power (i.e. equivalent to a local label
301 // before adding its value in list
302 if( power->IsSymbolLikePowerLocalLabel() )
303 {
304 const SCH_FIELD* valueField = power->GetField( FIELD_T::VALUE );
305 existingLabels.insert( UnescapeString( valueField->GetText() ) );
306 }
307 }
308 }
309 }
310
311 // Add bus aliases to label list
312 for( const std::shared_ptr<BUS_ALIAS>& busAlias : m_Parent->Schematic().GetAllBusAliases() )
313 existingLabels.insert( wxT( "{" ) + busAlias->GetName() + wxT( "}" ) );
314
315 for( const wxString& label : existingLabels )
316 m_existingLabelArray.push_back( label );
317
318 m_valueCombo->SetStringList( m_existingLabelArray );
319 m_valueCombo->SetSelectedString( text );
320 }
321 else if( m_activeTextEntry )
322 {
323 m_activeTextEntry->SetValue( text );
324 }
325
326 // Push a copy of each field into m_updateFields
327 for( SCH_FIELD& field : m_currentLabel->GetFields() )
328 {
329 SCH_FIELD field_copy( field );
330
331 // change offset to be symbol-relative
332 field_copy.Offset( -m_currentLabel->GetPosition() );
333
334 m_fields->push_back( field_copy );
335 }
336
337 // notify the grid
338 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, (int) m_fields->size() );
339 m_grid->ProcessTableMessage( msg );
340
341 if( m_shapeSizer->AreAnyItemsShown() )
342 {
343 switch( m_currentLabel->GetShape() )
344 {
345 case LABEL_FLAG_SHAPE::L_INPUT: m_input->SetValue( true ); break;
346 case LABEL_FLAG_SHAPE::L_OUTPUT: m_output->SetValue( true ); break;
347 case LABEL_FLAG_SHAPE::L_BIDI: m_bidirectional->SetValue( true ); break;
348 case LABEL_FLAG_SHAPE::L_TRISTATE: m_triState->SetValue( true ); break;
349 case LABEL_FLAG_SHAPE::L_UNSPECIFIED: m_passive->SetValue( true ); break;
350 case LABEL_FLAG_SHAPE::F_DOT: m_dot->SetValue( true ); break;
351 case LABEL_FLAG_SHAPE::F_ROUND: m_circle->SetValue( true ); break;
352 case LABEL_FLAG_SHAPE::F_DIAMOND: m_diamond->SetValue( true ); break;
353 case LABEL_FLAG_SHAPE::F_RECTANGLE: m_rectangle->SetValue( true ); break;
354 }
355 }
356
357 m_fontCtrl->SetFontSelection( m_currentLabel->GetFont() );
358
360 m_textSize.SetValue( static_cast<SCH_DIRECTIVE_LABEL*>( m_currentLabel )->GetPinLength() );
361 else
362 m_textSize.SetValue( m_currentLabel->GetTextWidth() );
363
364 m_bold->Check( m_currentLabel->IsBold() );
365 m_italic->Check( m_currentLabel->IsItalic() );
366 m_textColorSwatch->SetSwatchColor( m_currentLabel->GetTextColor(), false );
367
368 switch( m_currentLabel->GetSpinStyle() )
369 {
370 case SPIN_STYLE::RIGHT: m_spin0->Check( true ); break;
371 case SPIN_STYLE::LEFT: m_spin1->Check( true ); break;
372 case SPIN_STYLE::UP: m_spin2->Check( true ); break;
373 case SPIN_STYLE::BOTTOM: m_spin3->Check( true ); break;
374 }
375
376 if( m_currentLabel->AutoRotateOnPlacementSupported() )
377 m_autoRotate->SetValue( m_currentLabel->AutoRotateOnPlacement() );
378
379 // Recalculate layout after grid population
380 m_grid->Layout();
381 Layout();
382 GetSizer()->SetSizeHints( this );
383
384 wxSize minSize = GetMinSize();
385 wxSize curSize = GetSize();
386
387 if( curSize.x < minSize.x || curSize.y < minSize.y )
388 SetSize( wxSize( std::max( curSize.x, minSize.x ), std::max( curSize.y, minSize.y ) ) );
389
390 SendSizeEvent( wxSEND_EVENT_POST );
391
392 return true;
393}
394
395
396void DIALOG_LABEL_PROPERTIES::OnEnterKey( wxCommandEvent& aEvent )
397{
398 wxPostEvent( this, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
399}
400
401
403{
404 // If the key is WXK_RETURN/WXK_NUMPAD_ENTER because m_valueCombo is the source event, we do not skip
405 // the key event because the default action is to show the m_valueCombo dropdown list,
406 // and we only want to accept the entered string.
407 if(( aEvent.GetKeyCode() == WXK_RETURN ) || (aEvent.GetKeyCode() == WXK_NUMPAD_ENTER))
408 {
409 wxPostEvent( this, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
410 }
411 else if( aEvent.GetKeyCode() == WXK_SPACE )
412 {
413 // our FILTER_COMBOBOX uses the space as special command, not wanted here,
414 // to open the dropdown list of labels.
415 // So just add the space char to the current text, at current insertion point,
416 // and do not skip this event
417 m_activeTextEntry->WriteText( wxT(" " ) );
418 }
419 else
420 {
421 aEvent.Skip();
422 }
423}
424
425
427{
428 if( aEvent.GetKeyCode() == WXK_TAB )
429 {
430 if( aEvent.ShiftDown() )
431 {
432 m_textSizeCtrl->SetFocusFromKbd();
433 }
434 else if( !m_fields->empty() )
435 {
436 m_grid->SetFocusFromKbd();
437 m_grid->MakeCellVisible( 0, 0 );
438 m_grid->SetGridCursor( 0, 0 );
439 }
440 else
441 {
442 m_textSizeCtrl->SetFocusFromKbd();
443 }
444 }
445 else
446 {
447 aEvent.Skip();
448 }
449}
450
451
452static bool positioningChanged( const SCH_FIELD& a, const SCH_FIELD& b )
453{
454 return a.GetPosition() != b.GetPosition()
455 || a.GetHorizJustify() != b.GetHorizJustify()
456 || a.GetVertJustify() != b.GetVertJustify()
457 || a.GetTextAngle() != b.GetTextAngle();
458}
459
460
461static bool positioningChanged( FIELDS_GRID_TABLE* a, std::vector<SCH_FIELD>& b )
462{
463 for( size_t i = 0; i < a->size() && i < b.size(); ++i )
464 {
465 if( positioningChanged( a->at( i ), b.at( i ) ) )
466 return true;
467 }
468
469 return false;
470}
471
472
474{
475 if( !m_grid->CommitPendingChanges() )
476 return false;
477
478 if( !wxDialog::TransferDataFromWindow() )
479 return false;
480
481 // Don't allow text to disappear; it can be difficult to correct if you can't select it
482 if( !m_textSize.Validate( 0.01, 1000.0, EDA_UNITS::MM ) )
483 return false;
484
485 wxString text;
486
488 {
489 // labels need escaping
491
492 // convert any text variable cross-references to their UUIDs
493 text = m_currentLabel->Schematic()->ConvertRefsToKIIDs( text );
494
495#ifdef __WXMAC__
496 // On macOS CTRL+Enter produces '\r' instead of '\n' regardless of EOL setting
497 text.Replace( wxS( "\r" ), wxS( "\n" ) );
498#endif
499
500 wxString visibleText = text;
501 visibleText.Trim( false ).Trim( true );
502
503 if( visibleText.IsEmpty() )
504 {
505 DisplayError( this, _( "Label can not be empty." ) );
506 return false;
507 }
508 }
509
510 // Stage only after validation, because a staged commit that is never pushed leaves the
511 // connectivity revision ahead of the published rows
512 SCH_COMMIT commit( m_Parent );
513
514 /* save old text in undo list if not already in edit */
515 if( m_currentLabel->GetEditFlags() == 0 )
516 commit.Modify( m_currentLabel, m_Parent->GetScreen() );
517
518 m_Parent->GetCanvas()->Refresh();
519
521 m_currentLabel->SetText( text );
522
523 // change all field positions from relative to absolute
524 for( SCH_FIELD& field : *m_fields )
525 {
526 field.Offset( m_currentLabel->GetPosition() );
527
528 if( field.GetUntranslatedName() == wxT( "Netclass" ) )
529 {
530 field.SetLayer( LAYER_NETCLASS_REFS );
531 }
532 else if( field.GetId() == FIELD_T::INTERSHEET_REFS )
533 {
534 if( field.IsVisible() != m_Parent->Schematic().Settings().m_IntersheetRefsShow )
535 {
536 DisplayInfoMessage( this, _( "Intersheet reference visibility is controlled globally from "
537 "Schematic Setup > General > Formatting" ) );
538 }
539
540 field.SetLayer( LAYER_INTERSHEET_REFS );
541 }
542 else
543 {
544 field.SetLayer( LAYER_FIELDS );
545 }
546 }
547
548 if( positioningChanged( m_fields, m_currentLabel->GetFields() ) )
549 m_currentLabel->SetFieldsAutoplaced( AUTOPLACE_NONE );
550
551 for( int ii = m_fields->GetNumberRows() - 1; ii >= 0; ii-- )
552 {
553 SCH_FIELD& field = m_fields->at( ii );
554 const wxString& fieldName = field.GetUntranslatedName();
555 const wxString& fieldText = field.GetText();
556
557 if( fieldName.IsEmpty() && fieldText.IsEmpty() )
558 {
559 // delete empty, unnamed fields
560 m_fields->erase( m_fields->begin() + ii );
561 }
562 else if( fieldName == wxT( "Netclass" ) && fieldText.IsEmpty() )
563 {
564 // delete empty Netclass fields if there are other Netclass fields present
565 int netclassFieldCount = 0;
566
567 for( int jj = 0; jj < m_fields->GetNumberRows(); ++jj )
568 {
569 if( m_fields->at( jj ).GetUntranslatedName() == wxT( "Netclass" ) )
570 netclassFieldCount++;
571 }
572
573 if( netclassFieldCount > 1 )
574 m_fields->erase( m_fields->begin() + ii );
575 }
576 else if( fieldName.IsEmpty() )
577 {
578 // give non-empty, unnamed fields a name
579 field.SetName( _( "untitled" ) );
580 }
581 }
582
583 int ordinal = 42; // Arbitrarily larger than any mandatory FIELD_T ids.
584
585 for( SCH_FIELD& field : *m_fields )
586 {
587 if( !field.IsMandatory() )
588 field.SetOrdinal( ordinal++, FIELD_T::USER );
589 }
590
591 m_currentLabel->SetFields( *m_fields );
592
593 if( m_shapeSizer->AreAnyItemsShown() )
594 {
595 if( m_bidirectional->GetValue() ) m_currentLabel->SetShape( LABEL_FLAG_SHAPE::L_BIDI );
596 else if( m_input->GetValue() ) m_currentLabel->SetShape( LABEL_FLAG_SHAPE::L_INPUT );
597 else if( m_output->GetValue() ) m_currentLabel->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT );
598 else if( m_triState->GetValue() ) m_currentLabel->SetShape( LABEL_FLAG_SHAPE::L_TRISTATE );
599 else if( m_passive->GetValue() ) m_currentLabel->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED );
600 else if( m_dot->GetValue() ) m_currentLabel->SetShape( LABEL_FLAG_SHAPE::F_DOT );
601 else if( m_circle->GetValue() ) m_currentLabel->SetShape( LABEL_FLAG_SHAPE::F_ROUND );
602 else if( m_diamond->GetValue() ) m_currentLabel->SetShape( LABEL_FLAG_SHAPE::F_DIAMOND );
603 else if( m_rectangle->GetValue() ) m_currentLabel->SetShape( LABEL_FLAG_SHAPE::F_RECTANGLE );
604 }
605
606 if( m_fontCtrl->HaveFontSelection() )
607 m_currentLabel->SetFont( m_fontCtrl->GetFontSelection( m_bold->IsChecked(), m_italic->IsChecked() ) );
608
610 static_cast<SCH_DIRECTIVE_LABEL*>( m_currentLabel )->SetPinLength( m_textSize.GetIntValue() );
611 else if( m_currentLabel->GetTextWidth() != m_textSize.GetIntValue() )
612 m_currentLabel->SetTextSize( VECTOR2I( m_textSize.GetIntValue(), m_textSize.GetIntValue() ) );
613
614 // Must come after SetTextSize()
615 m_currentLabel->SetBold( m_bold->IsChecked() );
616 m_currentLabel->SetItalic( m_italic->IsChecked() );
617
618 m_currentLabel->SetTextColor( m_textColorSwatch->GetSwatchColor() );
619
620 SPIN_STYLE selectedSpinStyle= SPIN_STYLE::LEFT;
621
622 if( m_spin0->IsChecked() ) selectedSpinStyle = SPIN_STYLE::RIGHT;
623 else if( m_spin1->IsChecked() ) selectedSpinStyle = SPIN_STYLE::LEFT;
624 else if( m_spin2->IsChecked() ) selectedSpinStyle = SPIN_STYLE::UP;
625 else if( m_spin3->IsChecked() ) selectedSpinStyle = SPIN_STYLE::BOTTOM;
626
627 if( m_currentLabel->AutoRotateOnPlacementSupported() )
628 {
629 SCH_EDIT_FRAME* frame = static_cast<SCH_EDIT_FRAME*>( m_parentFrame );
630 m_currentLabel->SetAutoRotateOnPlacement( m_autoRotate->IsChecked() );
631 frame->AutoRotateItem( frame->GetScreen(), m_currentLabel );
632 }
633 else
634 {
635 m_currentLabel->SetAutoRotateOnPlacement( false );
636 }
637
638 if( !m_currentLabel->AutoRotateOnPlacement() && m_currentLabel->GetSpinStyle() != selectedSpinStyle )
639 m_currentLabel->SetSpinStyle( selectedSpinStyle );
640
641 AUTOPLACE_ALGO fieldsAutoplaced = m_currentLabel->GetFieldsAutoplaced();
642
643 if( fieldsAutoplaced == AUTOPLACE_AUTO || fieldsAutoplaced == AUTOPLACE_MANUAL )
644 m_currentLabel->AutoplaceFields( m_Parent->GetScreen(), fieldsAutoplaced );
645
646 if( !commit.Empty() )
647 {
648 commit.Push( _( "Edit Label Properties" ) );
649 }
650 else if( m_activeTextEntry && m_labelList )
651 {
652 text = m_activeTextEntry->GetValue();
653 // On macOS CTRL+Enter produces '\r' instead of '\n' regardless of EOL setting
654 text.Replace( wxS( "\r" ), wxS( "\n" ) );
655 wxArrayString lines = wxSplit( text, '\n' );
656
657 for( const wxString& line : lines )
658 {
659 text = EscapeString( line, CTX_NETNAME );
660 text.Trim( false ).Trim( true );
661
662 if( text.empty() )
663 continue;
664
665 // convert any text variable cross-references to their UUIDs
666 text = m_currentLabel->Schematic()->ConvertRefsToKIIDs( text );
667
668 switch ( m_currentLabel->Type() )
669 {
671 {
672 SCH_GLOBALLABEL* label = new SCH_GLOBALLABEL( *static_cast<SCH_GLOBALLABEL*>( m_currentLabel ) );
673 const_cast<KIID&>( label->m_Uuid ) = KIID(); // Gives a new UUID to the copy
674 label->SetText( text );
675 m_labelList->push_back( std::unique_ptr<SCH_LABEL_BASE>( label ) );
676 break;
677 }
678 case SCH_HIER_LABEL_T:
679 {
680 SCH_HIERLABEL* label = new SCH_HIERLABEL( *static_cast<SCH_HIERLABEL*>( m_currentLabel ) );
681 const_cast<KIID&>( label->m_Uuid ) = KIID();
682 label->SetText( text );
683 m_labelList->push_back( std::unique_ptr<SCH_LABEL_BASE>( label ) );
684 break;
685 }
686 case SCH_LABEL_T:
687 {
688 SCH_LABEL* label = new SCH_LABEL( *static_cast<SCH_LABEL*>( m_currentLabel ) );
689 const_cast<KIID&>( label->m_Uuid ) = KIID();
690 label->SetText( text );
691 m_labelList->push_back( std::unique_ptr<SCH_LABEL_BASE>( label ) );
692 break;
693 }
694 default:
695 break;
696 }
697 }
698 }
699 else if( m_labelList && m_currentLabel->Type() == SCH_DIRECTIVE_LABEL_T )
700 {
702 m_labelList->push_back( std::unique_ptr<SCH_LABEL_BASE>( label ) );
703 }
704
705 return true;
706}
707
708
709void DIALOG_LABEL_PROPERTIES::onSpinButton( wxCommandEvent& aEvent )
710{
711 for( BITMAP_BUTTON* btn : { m_spin0, m_spin1, m_spin2, m_spin3 } )
712 {
713 if( btn->IsChecked() && btn != aEvent.GetEventObject() )
714 btn->Check( false );
715 }
716}
717
718
719void DIALOG_LABEL_PROPERTIES::OnFormattingHelp( wxHyperlinkEvent& aEvent )
720{
722}
723
724
726{
727 wxGridCellEditor* editor = m_grid->GetCellEditor( event.GetRow(), event.GetCol() );
728 wxControl* control = editor->GetControl();
729
730 if( control && control->GetValidator() && !control->GetValidator()->Validate( control ) )
731 {
732 event.Veto();
733 m_delayedFocusRow = event.GetRow();
734 m_delayedFocusColumn = event.GetCol();
735 }
736 else if( event.GetCol() == FDC_NAME )
737 {
738 wxString newName = event.GetString();
739 bool isGlobalLabel = m_currentLabel->Type() == SCH_GLOBAL_LABEL_T;
740
741 for( int i = 0; i < m_grid->GetNumberRows(); ++i )
742 {
743 if( i == event.GetRow() )
744 continue;
745
746 wxString existing = m_grid->GetCellValue( i, FDC_NAME );
747
748 // Only global labels have a mandatory field (Intersheet References). Hierarchical,
749 // regular, and directive labels carry only user fields, which compare case-sensitively.
750 bool duplicate = isGlobalLabel
752 : FieldNamesAreDuplicates( newName, existing, {} );
753
754 if( duplicate )
755 {
756 DisplayError( this, wxString::Format( _( "Field name '%s' already in use." ),
757 newName ) );
758 event.Veto();
759 m_delayedFocusRow = event.GetRow();
760 m_delayedFocusColumn = event.GetCol();
761 break;
762 }
763 }
764 }
765
766 editor->DecRef();
767}
768
769
770void DIALOG_LABEL_PROPERTIES::OnAddField( wxCommandEvent& event )
771{
772 m_grid->OnAddRow(
773 [&]() -> std::pair<int, int>
774 {
775 wxString fieldName = wxT( "Netclass" );
776
777 for( SCH_FIELD& field : *m_fields )
778 {
779 if( field.GetId() != FIELD_T::INTERSHEET_REFS && field.GetName() != wxT( "Netclass" ) )
780 {
781 fieldName = wxEmptyString;
782 break;
783 }
784 }
785
786 fieldName = SCH_LABEL_BASE::GetDefaultFieldName( fieldName, true );
787
788 SCH_FIELD newField( m_currentLabel, FIELD_T::USER, fieldName );
789
790 if( m_fields->size() > 0 )
791 {
792 // SetAttributes() also covers text angle, size, italic and bold
793 newField.SetAttributes( m_fields->at( m_fields->size() - 1 ) );
794 newField.SetVisible( m_fields->at( m_fields->size() - 1 ).IsVisible() );
795 }
796 else
797 {
798 newField.SetVisible( true );
799 newField.SetItalic( true );
800 }
801
802 m_fields->push_back( newField );
803
804 // notify the grid
805 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
806 m_grid->ProcessTableMessage( msg );
807 return { m_fields->GetNumberRows() - 1, FDC_NAME };
808 } );
809}
810
811
812void DIALOG_LABEL_PROPERTIES::OnDeleteField( wxCommandEvent& event )
813{
814 m_grid->OnDeleteRows(
815 [&]( int row )
816 {
817 if( row < m_fields->GetMandatoryRowCount() )
818 {
819 DisplayError( this, _( "The first field is mandatory." ) );
820 return false;
821 }
822
823 return true;
824 },
825 [&]( int row )
826 {
827 if( !m_fields->EraseRow( row ) )
828 return;
829
830 // notify the grid
831 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_DELETED, row, 1 );
832 m_grid->ProcessTableMessage( msg );
833 } );
834}
835
836
837void DIALOG_LABEL_PROPERTIES::OnMoveUp( wxCommandEvent& event )
838{
839 m_grid->OnMoveRowUp(
840 [&]( int row )
841 {
842 return row > m_fields->GetMandatoryRowCount();
843 },
844 [&]( int row )
845 {
846 m_fields->SwapRows( row, row - 1 );
847 m_grid->ForceRefresh();
848 } );
849}
850
851
852void DIALOG_LABEL_PROPERTIES::OnMoveDown( wxCommandEvent& event )
853{
854 m_grid->OnMoveRowDown(
855 [&]( int row )
856 {
857 return row >= m_fields->GetMandatoryRowCount();
858 },
859 [&]( int row )
860 {
861 m_fields->SwapRows( row, row + 1 );
862 m_grid->ForceRefresh();
863 } );
864}
865
866
867void DIALOG_LABEL_PROPERTIES::OnUpdateUI( wxUpdateUIEvent& event )
868{
869 std::bitset<64> shownColumns = m_grid->GetShownColumns();
870
871 if( shownColumns != m_shownColumns )
872 {
873 m_shownColumns = shownColumns;
874
875 if( !m_grid->IsCellEditControlShown() )
876 m_grid->SetGridWidthsDirty();
877 }
878
879 // Handle a delayed focus
880 if( m_delayedFocusRow >= 0 && m_delayedFocusRow < m_grid->GetNumberRows() )
881 {
882 m_grid->SetFocus();
883 m_grid->MakeCellVisible( m_delayedFocusRow, m_delayedFocusColumn );
885 }
886
889}
890
891
893{
894 bool multiLine = m_multilineAllowed && m_cbMultiLine->IsChecked();
895
896 if( m_currentLabel->Type() == SCH_GLOBAL_LABEL_T || m_currentLabel->Type() == SCH_LABEL_T )
897 {
898 m_labelCombo->Show( !multiLine );
899 m_valueCombo->Show( !multiLine );
900 m_labelMultiLine->Show( multiLine );
901 m_valueMultiLine->Show( multiLine );
902
903 if( multiLine )
904 {
905 m_valueMultiLine->SetValue( m_valueCombo->GetValue() );
908 }
909 else
910 {
911 wxString multiText = m_valueMultiLine->GetValue();
912 m_valueCombo->SetValue( multiText.BeforeFirst( '\n' ) );
914 SetInitialFocus( m_valueCombo->GetTextCtrl() );
915 }
916 }
917 else if( m_currentLabel->Type() == SCH_HIER_LABEL_T )
918 {
919 m_labelSingleLine->Show( !multiLine );
920 m_valueSingleLine->Show( !multiLine );
921 m_labelMultiLine->Show( multiLine );
922 m_valueMultiLine->Show( multiLine );
923
924 if( multiLine )
925 {
926 m_valueMultiLine->SetValue( m_valueSingleLine->GetValue() );
929 }
930 else
931 {
932 wxString multiText = m_valueMultiLine->GetValue();
933 m_valueSingleLine->SetValue( multiText.BeforeFirst( '\n' ) );
936 }
937 }
938
939 Layout();
940}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
A bitmap button widget that behaves like an AUI toolbar item's button when it is drawn.
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:400
Color settings are a bit different than most of the settings objects in that there can be more than o...
COLOR4D GetColor(int aLayer) const
bool Empty() const
Definition commit.h:145
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:102
DIALOG_LABEL_PROPERTIES_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Label Properties"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
std::list< std::unique_ptr< SCH_LABEL_BASE > > * m_labelList
void OnMoveUp(wxCommandEvent &event) override
void OnMoveDown(wxCommandEvent &event) override
DIALOG_LABEL_PROPERTIES(SCH_EDIT_FRAME *parent, SCH_LABEL_BASE *aLabel, bool aNew)
void OnEnterKey(wxCommandEvent &aEvent) override
wxEVT_COMMAND_ENTER event handler for single-line control.
void OnAddField(wxCommandEvent &event) override
SCH_NETNAME_VALIDATOR m_netNameValidator
void OnFormattingHelp(wxHyperlinkEvent &aEvent) override
void OnCBValueCharHook(wxKeyEvent &aEvent) override
void OnUpdateUI(wxUpdateUIEvent &event) override
void onSpinButton(wxCommandEvent &aEvent)
void onMultiLabelCheck(wxCommandEvent &aEvent) override
void OnDeleteField(wxCommandEvent &event) override
void OnGridCellChanging(wxGridEvent &event)
void OnValueCharHook(wxKeyEvent &aEvent) override
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={})
std::string m_hash_key
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
EDA_BASE_FRAME * m_parentFrame
const KIID m_Uuid
Definition eda_item.h:599
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:118
void SetAttributes(const EDA_TEXT &aSrc, bool aSetPosition=true)
Set the text attributes from another instance.
Definition eda_text.cpp:394
virtual void Offset(const VECTOR2I &aOffset)
Definition eda_text.cpp:575
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:239
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:347
virtual EDA_ANGLE GetTextAngle() const
Definition eda_text.h:178
GR_TEXT_V_ALIGN_T GetVertJustify() const
Definition eda_text.h:242
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:236
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition eda_text.cpp:290
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
Definition kiid.h:46
Commit for the schematic and symbol editors.
Definition sch_commit.h:54
void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
Schematic editor (Eeschema) main window.
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
void AutoRotateItem(SCH_SCREEN *aScreen, SCH_ITEM *aItem)
Automatically set the rotation of an item (if the item supports it).
VECTOR2I GetPosition() const override
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:139
wxString GetUntranslatedName() const
Get the untranslated field name for storage, variable look-up, etc.
void SetName(const wxString &aName)
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
static const wxString GetDefaultFieldName(const wxString &aName, bool aUseDefaultName)
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition sch_screen.h:826
SCH_SCREEN * GetNext()
SCH_SCREEN * GetFirst()
Schematic symbol object.
Definition sch_symbol.h:75
bool IsSymbolLikePowerGlobalLabel() const
bool IsSymbolLikePowerLocalLabel() const
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
static HTML_MESSAGE_BOX * ShowSyntaxHelp(wxWindow *aParentWindow)
Spin style for labels of all kinds on schematics.
Definition sch_label.h:38
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Display an informational message box with aMessage.
Definition confirm.cpp:245
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:192
This file is part of the common library.
static bool positioningChanged(const SCH_FIELD &a, const SCH_FIELD &b)
const int minSize
Push and Shove router track width and via size dialog.
#define _(s)
@ FDC_NAME
@ FDC_VALUE
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition idf_helpers.h:37
@ LAYER_FIELDS
Definition layer_ids.h:490
@ LAYER_NETCLASS_REFS
Definition layer_ids.h:492
@ LAYER_SCHEMATIC_BACKGROUND
Definition layer_ids.h:516
@ LAYER_INTERSHEET_REFS
Definition layer_ids.h:491
AUTOPLACE_ALGO
Definition sch_item.h:73
@ AUTOPLACE_MANUAL
Definition sch_item.h:76
@ AUTOPLACE_NONE
Definition sch_item.h:74
@ AUTOPLACE_AUTO
Definition sch_item.h:75
@ L_BIDI
Definition sch_label.h:100
@ L_TRISTATE
Definition sch_label.h:101
@ L_UNSPECIFIED
Definition sch_label.h:102
@ F_DOT
Definition sch_label.h:105
@ F_ROUND
Definition sch_label.h:106
@ L_OUTPUT
Definition sch_label.h:99
@ F_DIAMOND
Definition sch_label.h:107
@ L_INPUT
Definition sch_label.h:98
@ F_RECTANGLE
Definition sch_label.h:108
std::vector< FAB_LAYER_COLOR > dummy
wxString UnescapeString(const wxString &aSource)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_NETNAME
bool FieldNamesAreDuplicates(const wxString &aLhs, const wxString &aRhs, std::initializer_list< FIELD_T > aMandatoryFields)
Test whether two field names should be treated as duplicates for the purposes of field name uniquenes...
@ USER
The field ID hasn't been set yet; field is invalid.
@ INTERSHEET_REFS
Global label cross-reference page numbers.
@ VALUE
Field Value of part, i.e. "3.3K".
#define GLOBALLABEL_MANDATORY_FIELDS
@ SCH_DIRECTIVE_LABEL_T
Definition typeinfo.h:167
@ SCH_LABEL_T
Definition typeinfo.h:163
@ SCH_HIER_LABEL_T
Definition typeinfo.h:165
@ SCH_SYMBOL_LOCATE_POWER_T
Definition typeinfo.h:192
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:164
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708