KiCad PCB EDA Suite
Loading...
Searching...
No Matches
panel_jobset.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) 2024 Mark Roszko <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include "panel_jobset.h"
22#include "dialog_destination.h"
24#include <wx/aui/auibook.h>
25#include <jobs/jobset.h>
26#include <jobs/job_registry.h>
27#include <eda_list_dialog.h>
28#include <wx/checkbox.h>
29#include <wx/menu.h>
30#include <bitmaps.h>
31#include <i18n_utility.h>
32#include <jobs_runner.h>
34#include <kicad_manager_frame.h>
35#include <vector>
36#include <wx/dcclient.h>
37
40#include <widgets/wx_grid.h>
42#include <kiplatform/ui.h>
43#include <confirm.h>
44#include <launch_ext.h>
45#include <wx/filename.h>
46
50
51
52extern KICOMMON_API
53std::map<JOBSET_DESTINATION_T, JOBSET_DESTINATION_T_INFO> JobsetDestinationTypeInfos;
54
55
57{
58public:
59 DIALOG_JOBSET_RUN_LOG( wxWindow* aParent, JOBSET* aJobsFile, JOBSET_DESTINATION* aDestination ) :
61 m_jobsFile( aJobsFile ),
62 m_destination( aDestination ),
63 m_lastWidth( -1 ),
64 m_marginsWidth( -1 )
65 {
66 m_staticTextOutputName->SetLabel( wxString::Format( _( "Destination: %s" ),
67 aDestination->GetDescription() ) );
68
69 int jobBmpColId = m_jobList->AppendColumn( wxT( "" ) );
70 int jobNoColId = m_jobList->AppendColumn( _( "No." ) );
71 int jobDescColId = m_jobList->AppendColumn( _( "Job Description" ) );
72 int jobSourceColId = m_jobList->AppendColumn( _( "Source" ) );
73 m_jobList->SetColumnWidth( jobBmpColId, 26 );
74 m_jobList->SetColumnWidth( jobNoColId, GetTextExtent( wxT( "XXXX" ) ).GetWidth() );
75 m_jobList->SetColumnWidth( jobSourceColId, GetTextExtent( wxT( "XXXXXX" ) ).GetWidth() );
76
77 wxImageList* imageList = new wxImageList( 16, 16, true, 3 );
78 imageList->Add( KiBitmapBundle( BITMAPS::ercerr ).GetBitmap( wxSize( 16, 16 ) ) );
79 imageList->Add( KiBitmapBundle( BITMAPS::checked_ok ).GetBitmap( wxSize( 16, 16 ) ) );
80 m_jobList->SetImageList( imageList, wxIMAGE_LIST_SMALL );
81
82 int num = 1;
83
84 for( JOBSET_JOB& job : aJobsFile->GetJobsForDestination( aDestination ) )
85 {
86 int imageIdx = -1;
87
88 if( aDestination->m_lastRunSuccessMap.contains( job.m_id ) )
89 {
90 if( aDestination->m_lastRunSuccessMap[job.m_id].value() )
91 imageIdx = 1;
92 else
93 imageIdx = 0;
94 }
95
96 long itemIndex = m_jobList->InsertItem( m_jobList->GetItemCount(), imageIdx );
97
98 m_jobList->SetItem( itemIndex, jobNoColId, wxString::Format( "%d", num++ ) );
99 m_jobList->SetItem( itemIndex, jobDescColId, job.GetDescription() );
100
101 KIWAY::FACE_T iface = JOB_REGISTRY::GetKifaceType( job.m_type );
102 wxString source = wxEmptyString;
103
104 if( iface < KIWAY::KIWAY_FACE_COUNT )
105 {
106 if( iface == KIWAY::FACE_PCB )
107 source = wxT( "PCB" );
108 else if( iface == KIWAY::FACE_SCH )
109 source = wxT( "SCH" );
110 }
111
112 m_jobList->SetItem( itemIndex, jobSourceColId, source );
113 }
114
115 SetupStandardButtons( { { wxID_OK, _( "Close" ) } } );
117 }
118
119 virtual void OnUpdateUI( wxUpdateUIEvent& event ) override
120 {
121 if( GetSize().GetWidth() != m_lastWidth )
122 {
123 m_lastWidth = GetSize().GetWidth();
124
125 if( m_marginsWidth < 0 )
126 m_marginsWidth = m_lastWidth - ( m_jobList->GetSize().GetWidth() * 2 );
127
128 int width = ( m_lastWidth / 2 );
129 width -= m_marginsWidth;
130 width -= m_jobList->GetColumnWidth( 0 );
131 width -= m_jobList->GetColumnWidth( 1 );
132 width -= m_jobList->GetColumnWidth( 3 );
133
134 m_jobList->SetColumnWidth( 2, width );
135 }
136 }
137
138 void OnJobListItemSelected( wxListEvent& event ) override
139 {
140 int itemIndex = event.GetIndex();
141
142 // The index could be negative (it is default -1)
143 if( itemIndex < 0 )
144 return;
145
146 std::vector<JOBSET_JOB> jobs = m_jobsFile->GetJobsForDestination( m_destination );
147
148 if( static_cast<size_t>( itemIndex ) < jobs.size() )
149 {
150 wxString jobId = jobs[itemIndex].m_id;
151
152 if( m_destination->m_lastRunReporters.contains( jobId ) )
153 m_textCtrlOutput->SetValue( m_destination->m_lastRunReporters.at( jobId )->GetMessages() );
154 else
155 m_textCtrlOutput->SetValue( _( "No output messages" ) );
156 }
157 }
158
159private:
162
165};
166
167
169{
170public:
171 PANEL_DESTINATION( wxWindow* aParent, PANEL_JOBSET* aPanelParent, KICAD_MANAGER_FRAME* aFrame,
172 JOBSET* aFile, JOBSET_DESTINATION* aDestination ) :
173 PANEL_DESTINATION_BASE( aParent ),
174 m_jobsFile( aFile ),
175 m_destinationId( aDestination->m_id ),
176 m_frame( aFrame ),
177 m_panelParent( aPanelParent )
178 {
181
182#if _WIN32
183 // BORDER_RAISED/SUNKEN look pretty on every platform but Windows
184 long style = GetWindowStyleFlag();
185 style &= ~wxBORDER_MASK;
186 style |= wxBORDER_SIMPLE;
187 SetWindowStyleFlag( style );
188#endif // _WIN32
189
190 Connect( wxEVT_MENU, wxCommandEventHandler( PANEL_DESTINATION::onMenu ), nullptr, this );
191
192 if( JobsetDestinationTypeInfos.contains( aDestination->m_type ) )
193 {
194 JOBSET_DESTINATION_T_INFO& jobTypeInfo = JobsetDestinationTypeInfos[aDestination->m_type];
195 m_textOutputType->SetLabel( aDestination->GetDescription() );
196 m_bitmapOutputType->SetBitmap( KiBitmapBundle( jobTypeInfo.bitmap ) );
197 }
198
199 m_pathInfo->SetFont( KIUI::GetSmallInfoFont( this ) );
200 UpdatePathInfo( aDestination->GetPathInfo() );
201 m_buttonOpenOutput->Enable( false );
202 UpdateStatus();
203 }
204
206 {
207 Disconnect( wxEVT_MENU, wxCommandEventHandler( PANEL_DESTINATION::onMenu ), nullptr, this );
208 }
209
211 {
212 JOBSET_DESTINATION* destination = GetDestination();
213 wxCHECK( destination, /*void*/ );
214
215 destination->m_lastRunSuccess = std::nullopt;
216 destination->m_lastResolvedOutputPath = std::nullopt;
217 m_statusBitmap->SetBitmap( wxNullBitmap );
218 m_buttonOpenOutput->Enable( false );
219 }
220
222 {
223 JOBSET_DESTINATION* destination = GetDestination();
224 wxCHECK( destination, /*void*/ );
225
226 if( destination->m_lastRunSuccess.has_value() )
227 {
228 if( destination->m_lastRunSuccess.value() )
229 {
231 m_statusBitmap->Show();
232 m_statusBitmap->SetToolTip( _( "Last run successful" ) );
233 }
234 else
235 {
237 m_statusBitmap->Show();
238 m_statusBitmap->SetToolTip( _( "Last run failed" ) );
239 }
240 }
241 else
242 {
243 m_statusBitmap->SetBitmap( wxNullBitmap );
244 }
245
246 m_buttonGenerate->Enable( !m_jobsFile->GetJobsForDestination( destination ).empty() );
247 bool enableOpenOutput =
248 destination->m_lastRunSuccess.value_or( false ) && destination->m_lastResolvedOutputPath.has_value();
249
250 m_buttonOpenOutput->Enable( enableOpenOutput );
251 }
252
253 void UpdatePathInfo( const wxString& aMsg )
254 {
255 wxClientDC dc( this );
256 int width = GetSize().GetWidth();
257
258 m_pathInfo->SetLabel( wxControl::Ellipsize( aMsg, dc, wxELLIPSIZE_MIDDLE, width ) );
259 }
260
261 virtual void OnGenerate( wxCommandEvent& event ) override
262 {
263 ClearStatus();
264 Refresh();
265
266 CallAfter(
267 [this]()
268 {
269 PROJECT& project = m_frame->Kiway().Prj();
270 m_panelParent->EnsurePcbSchFramesOpen();
271
272 wxFileName fn = project.GetProjectFullName();
273 wxSetWorkingDirectory( fn.GetPath() );
274
275 {
276 JOBS_PROGRESS_REPORTER progressReporter( m_frame, _( "Running Jobs" ) );
277 JOBS_RUNNER jobRunner( &m_frame->Kiway(), m_jobsFile, &project,
278 NULL_REPORTER::GetInstance(), &progressReporter );
279
280 if( JOBSET_DESTINATION* destination = GetDestination() )
281 jobRunner.RunJobsForDestination( destination );
282
283 UpdateStatus();
284 }
285
286 // Bring the Kicad manager frame back to the front
287 m_frame->Raise();
288 } );
289 }
290
291 void OnOpenOutput( wxCommandEvent& aEvent ) override
292 {
293 JOBSET_DESTINATION* destination = GetDestination();
294 wxCHECK( destination, /*void*/ );
295
296 if( !destination->m_lastResolvedOutputPath.has_value() )
297 return;
298
299 const wxString& resolvedPath = destination->m_lastResolvedOutputPath.value();
300
301 if( resolvedPath.IsEmpty() )
302 return;
303
304 if( !LaunchExternal( resolvedPath ) )
305 DisplayError( this, wxString::Format( _( "Failed to open '%s'." ), resolvedPath ) );
306 }
307
308 void OnLastStatusClick( wxMouseEvent& aEvent ) override
309 {
310 JOBSET_DESTINATION* destination = GetDestination();
311 wxCHECK( destination, /*void*/ );
312
313 DIALOG_JOBSET_RUN_LOG dialog( m_frame, m_jobsFile, destination );
314 dialog.ShowModal();
315 }
316
317 void OnRightDown( wxMouseEvent& aEvent ) override
318 {
319 JOBSET_DESTINATION* destination = GetDestination();
320 wxCHECK( destination, /*void*/ );
321
322 wxMenu menu;
323 menu.Append( wxID_EDIT, _( "Edit Destination Options..." ) );
324 menu.Append( wxID_DELETE, _( "Delete Destination" ) );
325
326 menu.AppendSeparator();
327 menu.Append( wxID_VIEW_DETAILS, _( "View Last Run Log..." ) );
328 menu.Append( wxID_OPEN, _( "Open Output" ) );
329
330 menu.Enable( wxID_VIEW_DETAILS, destination->m_lastRunSuccess.has_value() );
331 menu.Enable( wxID_OPEN, m_buttonOpenOutput->IsEnabled() );
332
333 PopupMenu( &menu );
334 }
335
336 void OnProperties( wxCommandEvent& aEvent ) override
337 {
338 JOBSET_DESTINATION* destination = GetDestination();
339 wxCHECK( destination, /*void*/ );
340
341 DIALOG_DESTINATION dialog( m_frame, m_jobsFile, destination );
342
343 if( dialog.ShowModal() == wxID_OK )
344 {
345 m_textOutputType->SetLabel( destination->GetDescription() );
346 UpdatePathInfo( destination->GetPathInfo() );
347 m_jobsFile->SetDirty();
348 m_panelParent->UpdateTitle();
349 ClearStatus();
350 }
351 }
352
353 virtual void OnDelete( wxCommandEvent& aEvent ) override
354 {
355 m_panelParent->RemoveDestination( this );
356 }
357
359 {
360 for( JOBSET_DESTINATION& destination : m_jobsFile->GetDestinations() )
361 {
362 if( destination.m_id == m_destinationId )
363 return &destination;
364 }
365
366 return nullptr;
367 }
368
369private:
370 void onMenu( wxCommandEvent& aEvent )
371 {
372 switch( aEvent.GetId() )
373 {
374 case wxID_EDIT:
375 {
376 wxCommandEvent dummy;
378 }
379 break;
380
381 case wxID_DELETE:
382 {
383 wxCommandEvent dummy;
384 OnDelete( dummy );
385 }
386 break;
387
388 case wxID_VIEW_DETAILS:
389 {
390 wxMouseEvent dummy;
392 }
393 break;
394
395 case wxID_OPEN:
396 {
397 wxCommandEvent dummy;
399 }
400 break;
401
402 default: wxFAIL_MSG( wxT( "Unknown ID in context menu event" ) );
403 }
404 }
405
406private:
411};
412
413
415 GRID_TRICKS( aGrid ),
416 m_parent( aParent ),
417 m_doubleClickRow( -1 )
418{
421}
422
423
424void JOBS_GRID_TRICKS::showPopupMenu( wxMenu& menu, wxGridEvent& aEvent )
425{
426 wxArrayInt selectedRows = m_grid->GetSelectedRows();
427
428 menu.Append( JOB_DESCRIPTION, _( "Edit Job Description" ) );
429 menu.Append( JOB_PROPERTIES, _( "Edit Job Settings..." ) );
430 menu.AppendSeparator();
431 menu.Append( GRIDTRICKS_ID_COPY, _( "Copy" ) + "\tCtrl+C", _( "Copy selected cells to clipboard" ) );
432 menu.Append( GRIDTRICKS_ID_DELETE, _( "Delete" ) + "\tDel", _( "Delete selected jobs" ) );
433 menu.Append( GRIDTRICKS_ID_SELECT, _( "Select All" ) + "\tCtrl+A", _( "Select all jobs" ) );
434
435 menu.Enable( JOB_DESCRIPTION, selectedRows.size() == 1 );
436 menu.Enable( JOB_PROPERTIES, selectedRows.size() == 1 );
437 menu.Enable( GRIDTRICKS_ID_DELETE, selectedRows.size() > 0 );
438
439 m_grid->PopupMenu( &menu );
440}
441
442
443void JOBS_GRID_TRICKS::doPopupSelection( wxCommandEvent& event )
444{
445 wxArrayInt selectedRows = m_grid->GetSelectedRows();
446
447 if( event.GetId() == JOB_DESCRIPTION )
448 {
449 if( selectedRows.size() > 0 )
450 {
451 m_grid->SetGridCursor( selectedRows[0], 2 );
452 m_grid->EnableCellEditControl();
453 }
454 }
455 else if( event.GetId() == JOB_PROPERTIES )
456 {
457 if( selectedRows.size() > 0 )
458 m_parent->OpenJobOptionsForListItem( selectedRows[0] );
459 }
460 else if( event.GetId() == GRIDTRICKS_ID_DELETE )
461 {
462 wxCommandEvent dummy;
463 m_parent->OnJobButtonDelete( dummy );
464 }
465 else
466 {
468 }
469}
470
471
472bool JOBS_GRID_TRICKS::handleDoubleClick( wxGridEvent& aEvent )
473{
474 m_grid->CancelShowEditorOnMouseUp();
475
476 int curr_col = aEvent.GetCol();
477 int curr_row = aEvent.GetRow();
478
479 if( ( curr_col == COL_NUMBER || curr_col == COL_SOURCE || curr_col == COL_DESCR )
480 && curr_row >= 0 && curr_row < (int) m_parent->GetJobsFile()->GetJobs().size() )
481 {
482 m_doubleClickRow = curr_row;
483 m_grid->CancelPendingChanges();
484
485 CallAfter(
486 [this]()
487 {
488 // Yes, again. CancelShowEditorOnMouseUp() doesn't appear to be 100%
489 // reliable.
490 m_grid->CancelPendingChanges();
491 int row = m_doubleClickRow;
492 m_doubleClickRow = -1;
493
494 if( row >= 0 && row < (int) m_parent->GetJobsFile()->GetJobs().size() )
495 m_parent->OpenJobOptionsForListItem( row );
496 } );
497
498 return true;
499 }
500
501 return false;
502}
503
504
505PANEL_JOBSET::PANEL_JOBSET( wxAuiNotebook* aParent, KICAD_MANAGER_FRAME* aFrame,
506 std::unique_ptr<JOBSET> aJobsFile ) :
507 PANEL_JOBSET_BASE( aParent ),
508 m_parentBook( aParent ),
509 m_frame( aFrame ),
510 m_jobsFile( std::move( aJobsFile ) )
511{
512 m_jobsGrid->PushEventHandler( new JOBS_GRID_TRICKS( this, m_jobsGrid ) );
513
514 m_jobsGrid->OverrideMinSize( 0.6, 0.3 );
515 m_jobsGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
516
517 // 'm' for margins
518 m_jobsGrid->SetColSize( COL_NUMBER, GetTextExtent( wxT( "99m" ) ).x );
519 m_jobsGrid->SetColSize( COL_SOURCE, GetTextExtent( wxT( "PCBm" ) ).x );
520
526
529}
530
531
533{
534 // Delete the GRID_TRICKS.
535 m_jobsGrid->PopEventHandler( true );
536}
537
538
540{
541 JOBSET_DESTINATION* output = aPanel->GetDestination();
542
543 m_destinationListSizer->Detach( aPanel );
544 aPanel->Destroy();
545
546 // ensure the window contents get shifted as needed
547 m_destinationList->Layout();
548 Layout();
549
550 m_jobsFile->RemoveDestination( output );
551}
552
553
555{
556 m_jobsGrid->ClearRows();
557 m_jobsGrid->AppendRows( (int) m_jobsFile->GetJobs().size() );
558
559 int num = 1;
560
561 for( JOBSET_JOB& job : m_jobsFile->GetJobs() )
562 {
563 m_jobsGrid->SetCellValue( num - 1, COL_NUMBER, wxString::Format( "%d", num ) );
564 m_jobsGrid->SetReadOnly( num - 1, COL_NUMBER );
565
566 m_jobsGrid->SetCellValue( num - 1, COL_DESCR, job.GetDescription() );
567
568 m_jobsGrid->SetReadOnly( num - 1, COL_SOURCE );
569
570 KIWAY::FACE_T iface = JOB_REGISTRY::GetKifaceType( job.m_type );
571 wxString source = wxEmptyString;
572
573 if( iface < KIWAY::KIWAY_FACE_COUNT )
574 {
575 if( iface == KIWAY::FACE_PCB )
576 source = wxT( "PCB" );
577 else if( iface == KIWAY::FACE_SCH )
578 source = wxT( "SCH" );
579 }
580
581 m_jobsGrid->SetCellValue( num - 1, COL_SOURCE, source );
582
583 num++;
584 }
585
586 UpdateTitle();
587
588 // Ensure the outputs get their Run-ability status updated
590 panel->UpdateStatus();
591}
592
593
595{
596 wxString tabName = m_jobsFile->GetFullName();
597
598 if( m_jobsFile->GetDirty() )
599 tabName = wxS( "*" ) + tabName;
600
601 int pageIdx = m_parentBook->FindPage( this );
602
603 if( pageIdx >= 0 )
604 m_parentBook->SetPageText( pageIdx, tabName );
605}
606
607
609{
610 PANEL_DESTINATION* destinationPanel = new PANEL_DESTINATION( m_destinationList, this, m_frame,
611 m_jobsFile.get(), aOutput );
612
613#if __OSX__
614 m_outputListSizer->Add( destinationPanel, 0, wxEXPAND, 5 );
615#else
616 m_destinationListSizer->Add( destinationPanel, 0, wxEXPAND|wxLEFT|wxRIGHT|wxBOTTOM, 5 );
617#endif
618
619 m_destinationList->Layout();
620}
621
622
623std::vector<PANEL_DESTINATION*> PANEL_JOBSET::GetDestinationPanels()
624{
625 std::vector<PANEL_DESTINATION*> panels;
626
627 for( const wxSizerItem* item : m_destinationListSizer->GetChildren() )
628 {
629 if( PANEL_DESTINATION* panel = dynamic_cast<PANEL_DESTINATION*>( item->GetWindow() ) )
630 panels.push_back( panel );
631 }
632
633 return panels;
634}
635
636
638{
639 Freeze();
640
641 for( JOBSET_DESTINATION& job : m_jobsFile->GetDestinations() )
642 addDestinationPanel( &job );
643
644 // ensure the window contents get shifted as needed
645 Layout();
646 Thaw();
647}
648
649
651{
652 bool success = false;
653 JOBSET_JOB& job = m_jobsFile->GetJobs()[aItemIndex];
655
656 if( iface < KIWAY::KIWAY_FACE_COUNT )
657 {
659 success = m_frame->Kiway().ProcessJobConfigDialog( iface, job.m_job.get(), m_frame );
660 }
661 else
662 {
663 // special jobs
664 if( job.m_job->GetType() == "special_execute" )
665 {
666 JOB_SPECIAL_EXECUTE* specialJob = static_cast<JOB_SPECIAL_EXECUTE*>( job.m_job.get() );
667
668 DIALOG_EXECUTECOMMAND_JOB_SETTINGS dialog( m_frame, specialJob );
669
670 // QuasiModal for Scintilla autocomplete
671 if( dialog.ShowQuasiModal() == wxID_OK )
672 success = true;
673 }
674 else if( job.m_job->GetType() == "special_copyfiles" )
675 {
676 JOB_SPECIAL_COPYFILES* specialJob = static_cast<JOB_SPECIAL_COPYFILES*>( job.m_job.get() );
677 DIALOG_COPYFILES_JOB_SETTINGS dialog( m_frame, specialJob );
678
679 if( dialog.ShowModal() == wxID_OK )
680 success = true;
681 }
682 }
683
684 if( success )
685 {
686 m_jobsFile->SetDirty();
687 UpdateTitle();
688 }
689
690 // Bring the Kicad manager frame back to the front
691 m_frame->Raise();
692
693 return success;
694}
695
696
697void PANEL_JOBSET::OnGridCellChange( wxGridEvent& aEvent )
698{
699 int row = aEvent.GetRow();
700 int col = aEvent.GetCol();
701
702 if( col == COL_DESCR )
703 m_jobsFile->GetJobs()[row].SetDescription( m_jobsGrid->GetCellValue( row, col ) );
704}
705
706
707void PANEL_JOBSET::OnSaveButtonClick( wxCommandEvent& aEvent )
708{
709 if( !m_jobsGrid->CommitPendingChanges() )
710 return;
711
712 m_jobsFile->SaveToFile( wxEmptyString, true );
713 UpdateTitle();
714}
715
716
717void PANEL_JOBSET::OnAddJobClick( wxCommandEvent& aEvent )
718{
719 if( !m_jobsGrid->CommitPendingChanges() )
720 return;
721
722 wxArrayString headers;
723 std::vector<wxArrayString> items;
724
725 headers.Add( _( "Job Types" ) );
726
728
729 for( const std::pair<const wxString, JOB_REGISTRY_ENTRY>& entry : jobMap )
730 {
731 if( entry.second.deprecated )
732 continue;
733
734 wxArrayString item;
735 item.Add( wxGetTranslation( entry.second.title ) );
736 items.emplace_back( item );
737 }
738
739 EDA_LIST_DIALOG dlg( this, _( "Add New Job" ), headers, items );
740 dlg.SetListLabel( _( "Select job type:" ) );
741
742 if( dlg.ShowModal() == wxID_OK )
743 {
744 wxString selectedString = dlg.GetTextSelection();
745
746 wxString jobKey;
747
748 if( !selectedString.IsEmpty() )
749 {
750 for( const std::pair<const wxString, JOB_REGISTRY_ENTRY>& entry : jobMap )
751 {
752 if( entry.second.deprecated )
753 continue;
754
755 if( wxGetTranslation( entry.second.title ) == selectedString )
756 {
757 jobKey = entry.first;
758 break;
759 }
760 }
761 }
762
763 if( !jobKey.IsEmpty() )
764 {
765 int row = m_jobsFile->GetJobs().size();
766 bool wasDirty = m_jobsFile->GetDirty();
767 JOB* job = JOB_REGISTRY::CreateInstance<JOB>( jobKey );
768
769 m_jobsFile->AddNewJob( jobKey, job );
770
771 if( OpenJobOptionsForListItem( row ) )
772 {
774
775 m_jobsGrid->SetGridCursor( row, 2 );
776 m_jobsGrid->EnableCellEditControl();
777 }
778 else
779 {
780 m_jobsFile->RemoveJob( row );
781 m_jobsFile->SetDirty( wasDirty );
782 }
783 }
784 }
785}
786
787
788void PANEL_JOBSET::OnJobButtonDelete( wxCommandEvent& aEvent )
789{
790 m_jobsGrid->OnDeleteRows(
791 [&]( int row )
792 {
793 m_jobsFile->RemoveJob( row );
794 m_jobsGrid->DeleteRows( row, 1 );
795 } );
796}
797
798
799void PANEL_JOBSET::OnAddDestinationClick( wxCommandEvent& aEvent )
800{
801 wxArrayString headers;
802 std::vector<wxArrayString> items;
803
804 headers.Add( _( "Destination Types" ) );
805
806 for( const auto& [destinationType, destinationTypeInfo] : JobsetDestinationTypeInfos )
807 {
808 wxArrayString item;
809 item.Add( wxGetTranslation( destinationTypeInfo.name ) );
810 items.emplace_back( item );
811 }
812
813 EDA_LIST_DIALOG dlg( this, _( "Add New Destination" ), headers, items );
814 dlg.SetListLabel( _( "Select destination type:" ) );
815 dlg.HideFilter();
816
817 if( dlg.ShowModal() == wxID_OK )
818 {
819 wxString selectedString = dlg.GetTextSelection();
820
821 for( const auto& [destinationType, destinationTypeInfo] : JobsetDestinationTypeInfos )
822 {
823 if( wxGetTranslation( destinationTypeInfo.name ) == selectedString )
824 {
825 JOBSET_DESTINATION* destination = m_jobsFile->AddNewDestination( destinationType );
826
827 DIALOG_DESTINATION dialog( m_frame, m_jobsFile.get(), destination );
828
829 if (dialog.ShowModal() == wxID_OK)
830 {
831 Freeze();
832 addDestinationPanel( destination );
833 Thaw();
834 }
835 else
836 {
837 // canceled
838 m_jobsFile->RemoveDestination( destination );
839 }
840 }
841 }
842 }
843}
844
845
847{
848 if( m_jobsFile->GetDirty() )
849 {
850 wxFileName fileName = m_jobsFile->GetFullFilename();
851 wxString msg = _( "Save changes to '%s' before closing?" );
852
853 if( !HandleUnsavedChanges( this, wxString::Format( msg, fileName.GetFullName() ),
854 [&]() -> bool
855 {
856 return m_jobsFile->SaveToFile( wxEmptyString, true );
857 } ) )
858 {
859 return false;
860 }
861 }
862
863 return true;
864}
865
866
868{
869 PROJECT& project = m_frame->Kiway().Prj();
870 KIWAY_PLAYER* frame = m_frame->Kiway().Player( FRAME_PCB_EDITOR, false );
871
872 if( !frame )
873 {
874 frame = m_frame->Kiway().Player( FRAME_PCB_EDITOR, true );
875
876 // frame can be null if Cvpcb cannot be run. No need to show a warning
877 // Kiway() generates the error messages
878 if( !frame )
879 return;
880
881 wxFileName boardfn = project.GetProjectFullName();
882 boardfn.SetExt( FILEEXT::PcbFileExtension );
883
884 // Prevent our window from being closed during the open process
885 wxEventBlocker blocker( this );
886
887 frame->OpenProjectFiles( std::vector<wxString>( 1, boardfn.GetFullPath() ) );
888
889 if( !frame->IsVisible() )
890 frame->Show( true );
891 }
892
893 frame = m_frame->Kiway().Player( FRAME_SCH, false );
894
895 if( !frame )
896 {
897 frame = m_frame->Kiway().Player( FRAME_SCH, true );
898
899 // frame can be null if Cvpcb cannot be run. No need to show a warning
900 // Kiway() generates the error messages
901 if( !frame )
902 return;
903
904 wxFileName schFn = project.GetProjectFullName();
906
907 wxEventBlocker blocker( this );
908
909 frame->OpenProjectFiles( std::vector<wxString>( 1, schFn.GetFullPath() ) );
910
911 if( !frame->IsVisible() )
912 frame->Show( true );
913 }
914
915 SetFocus();
916}
917
918
920{
921 return m_jobsFile->GetFullFilename();
922}
923
924
925void PANEL_JOBSET::OnJobButtonUp( wxCommandEvent& aEvent )
926{
927 if( !m_jobsGrid->CommitPendingChanges() )
928 return;
929
930 int item = m_jobsGrid->GetGridCursorRow();
931
932 if( item > 0 )
933 {
934 m_jobsFile->MoveJobUp( item );
935
937
938 m_jobsGrid->SelectRow( item - 1 );
939 m_jobsGrid->SetGridCursor( item - 1, m_jobsGrid->GetGridCursorCol() );
940 }
941 else
942 {
943 wxBell();
944 }
945}
946
947
948void PANEL_JOBSET::OnJobButtonDown( wxCommandEvent& aEvent )
949{
950 if( !m_jobsGrid->CommitPendingChanges() )
951 return;
952
953 int item = m_jobsGrid->GetGridCursorRow();
954
955 if( item < m_jobsGrid->GetNumberRows() - 1 )
956 {
957 m_jobsFile->MoveJobDown( item );
958
960
961 m_jobsGrid->SelectRow( item + 1 );
962 m_jobsGrid->SetGridCursor( item + 1, m_jobsGrid->GetGridCursorCol() );
963 }
964 else
965 {
966 wxBell();
967 }
968}
969
970
972{
973 if( !m_jobsGrid->CommitPendingChanges() )
974 return;
975
976 // sanity
977 if( m_jobsFile->GetDestinations().empty() )
978 {
979 DisplayError( this, _( "No destinations defined" ) );
980 return;
981 }
982
984 panel->ClearStatus();
985
986 Refresh();
987
988 CallAfter(
989 [this]()
990 {
991 PROJECT& project = m_frame->Kiway().Prj();
993
994 wxFileName fn = project.GetProjectFullName();
995 wxSetWorkingDirectory( fn.GetPath() );
996
997 {
998 JOBS_PROGRESS_REPORTER progressReporter( m_frame, _( "Running Jobs" ) );
999 JOBS_RUNNER jobRunner( &m_frame->Kiway(), m_jobsFile.get(), &project,
1000 NULL_REPORTER::GetInstance(), &progressReporter );
1001
1002 jobRunner.RunJobsAllDestinations();
1003
1004 for( PANEL_DESTINATION* panel : GetDestinationPanels() )
1005 panel->UpdateStatus();
1006 }
1007
1008 // Bring the Kicad manager frame back to the front
1009 m_frame->Raise();
1010 } );
1011}
1012
1013
1014void PANEL_JOBSET::OnSizeGrid( wxSizeEvent& aEvent )
1015{
1016 m_jobsGrid->SetColSize( COL_DESCR, m_jobsGrid->GetSize().x - m_jobsGrid->GetColSize( COL_SOURCE )
1017 - m_jobsGrid->GetColSize( COL_NUMBER ) );
1018
1019 // Always propagate for a grid repaint (needed if the height changes, as well as width)
1020 aEvent.Skip();
1021}
1022
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:110
DIALOG_JOBSET_RUN_LOG_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Jobset Run Log"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
void OnJobListItemSelected(wxListEvent &event) override
DIALOG_JOBSET_RUN_LOG(wxWindow *aParent, JOBSET *aJobsFile, JOBSET_DESTINATION *aDestination)
JOBSET_DESTINATION * m_destination
virtual void OnUpdateUI(wxUpdateUIEvent &event) override
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...
int ShowModal() override
A dialog which shows:
wxString GetTextSelection(int aColumn=0)
Return the selected text from aColumn in the wxListCtrl in the dialog.
void SetListLabel(const wxString &aLabel)
bool m_multiCellEditEnabled
GRID_TRICKS(WX_GRID *aGrid)
bool m_enableSingleClickEdit
virtual void doPopupSelection(wxCommandEvent &event)
WX_GRID * m_grid
I don't own the grid, but he owns me.
std::vector< JOBSET_JOB > GetJobsForDestination(JOBSET_DESTINATION *aDestination)
Definition jobset.cpp:307
PANEL_JOBSET * m_parent
void showPopupMenu(wxMenu &menu, wxGridEvent &aEvent) override
void doPopupSelection(wxCommandEvent &event) override
bool handleDoubleClick(wxGridEvent &aEvent) override
JOBS_GRID_TRICKS(PANEL_JOBSET *aParent, WX_GRID *aGrid)
bool RunJobsAllDestinations(bool aBail=false)
bool RunJobsForDestination(JOBSET_DESTINATION *aDestination, bool aBail=false)
static const REGISTRY_MAP_T & GetRegistry()
std::unordered_map< wxString, JOB_REGISTRY_ENTRY > REGISTRY_MAP_T
static T * CreateInstance(const wxString &aName)
static KIWAY::FACE_T GetKifaceType(const wxString &aName)
An simple container class that lets us dispatch output jobs to kifaces.
Definition job.h:183
const std::string & GetType() const
Definition job.h:194
The main KiCad project manager frame.
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
virtual bool OpenProjectFiles(const std::vector< wxString > &aFileList, int aCtl=0)
Open a project or set of files given by aFileList.
FACE_T
Known KIFACE implementations.
Definition kiway.h:298
@ KIWAY_FACE_COUNT
Definition kiway.h:308
@ FACE_SCH
eeschema DSO
Definition kiway.h:299
@ FACE_PCB
pcbnew DSO
Definition kiway.h:300
static REPORTER & GetInstance()
Definition reporter.cpp:96
wxStaticBitmap * m_statusBitmap
PANEL_DESTINATION_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxBORDER_SUNKEN|wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
STD_BITMAP_BUTTON * m_buttonProperties
STD_BITMAP_BUTTON * m_buttonDelete
wxStaticBitmap * m_bitmapOutputType
wxStaticText * m_textOutputType
void onMenu(wxCommandEvent &aEvent)
JOBSET_DESTINATION * GetDestination()
void OnLastStatusClick(wxMouseEvent &aEvent) override
void UpdatePathInfo(const wxString &aMsg)
PANEL_DESTINATION(wxWindow *aParent, PANEL_JOBSET *aPanelParent, KICAD_MANAGER_FRAME *aFrame, JOBSET *aFile, JOBSET_DESTINATION *aDestination)
void OnOpenOutput(wxCommandEvent &aEvent) override
virtual void OnDelete(wxCommandEvent &aEvent) override
virtual void OnGenerate(wxCommandEvent &event) override
void OnRightDown(wxMouseEvent &aEvent) override
KICAD_MANAGER_FRAME * m_frame
void OnProperties(wxCommandEvent &aEvent) override
PANEL_JOBSET * m_panelParent
STD_BITMAP_BUTTON * m_buttonAddDestination
STD_BITMAP_BUTTON * m_buttonDown
wxScrolledWindow * m_destinationList
wxBoxSizer * m_destinationListSizer
STD_BITMAP_BUTTON * m_buttonAddJob
PANEL_JOBSET_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(600, 400), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
STD_BITMAP_BUTTON * m_buttonDelete
STD_BITMAP_BUTTON * m_buttonUp
wxString GetFilePath() const
virtual void OnSaveButtonClick(wxCommandEvent &aEvent) override
void OnJobButtonDelete(wxCommandEvent &aEvent) override
virtual void OnJobButtonDown(wxCommandEvent &aEvent) override
void EnsurePcbSchFramesOpen()
bool GetCanClose() override
void rebuildJobList()
wxAuiNotebook * m_parentBook
void addDestinationPanel(JOBSET_DESTINATION *aDestination)
virtual void OnGenerateAllDestinationsClick(wxCommandEvent &event) override
bool OpenJobOptionsForListItem(size_t aItemIndex)
std::vector< PANEL_DESTINATION * > GetDestinationPanels()
void buildDestinationList()
KICAD_MANAGER_FRAME * m_frame
PANEL_JOBSET(wxAuiNotebook *aParent, KICAD_MANAGER_FRAME *aFrame, std::unique_ptr< JOBSET > aJobsFile)
virtual void OnAddDestinationClick(wxCommandEvent &aEvent) override
virtual void OnAddJobClick(wxCommandEvent &aEvent) override
std::unique_ptr< JOBSET > m_jobsFile
virtual void OnGridCellChange(wxGridEvent &aEvent) override
void RemoveDestination(PANEL_DESTINATION *aPanel)
virtual void OnJobButtonUp(wxCommandEvent &aEvent) override
virtual void OnSizeGrid(wxSizeEvent &aEvent) override
Container for project specific data.
Definition project.h:66
bool HandleUnsavedChanges(wxWindow *aParent, const wxString &aMessage, const std::function< bool()> &aSaveFunction)
Display a dialog with Save, Cancel and Discard Changes buttons.
Definition confirm.cpp:131
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:177
This file is part of the common library.
#define _(s)
@ FRAME_PCB_EDITOR
Definition frame_type.h:42
@ FRAME_SCH
Definition frame_type.h:34
@ GRIDTRICKS_ID_SELECT
Definition grid_tricks.h:46
@ GRIDTRICKS_ID_COPY
Definition grid_tricks.h:43
@ GRIDTRICKS_ID_DELETE
Definition grid_tricks.h:44
static const std::string KiCadSchematicFileExtension
Some functions to handle hotkeys in KiCad.
KICOMMON_API std::map< JOBSET_DESTINATION_T, JOBSET_DESTINATION_T_INFO > JobsetDestinationTypeInfos
Definition jobset.cpp:41
#define KICOMMON_API
Definition kicommon.h:28
bool LaunchExternal(const wxString &aPath)
Launches the given file or folder in the host OS.
KICOMMON_API wxFont GetSmallInfoFont(wxWindow *aWindow)
STL namespace.
KICOMMON_API std::map< JOBSET_DESTINATION_T, JOBSET_DESTINATION_T_INFO > JobsetDestinationTypeInfos
Definition jobset.cpp:41
@ COL_NUMBER
@ COL_DESCR
@ COL_SOURCE
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
std::vector< FAB_LAYER_COLOR > dummy
JOBSET_DESTINATION_T m_type
Definition jobset.h:134
wxString GetPathInfo() const
Definition jobset.cpp:158
wxString GetDescription() const
Definition jobset.cpp:152
std::optional< wxString > m_lastResolvedOutputPath
Definition jobset.h:143
std::optional< bool > m_lastRunSuccess
Definition jobset.h:140
std::unordered_map< wxString, std::optional< bool > > m_lastRunSuccessMap
Definition jobset.h:141
std::shared_ptr< JOB > m_job
Definition jobset.h:90
wxString m_type
Definition jobset.h:88
Definition of file extensions used in Kicad.