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#include <common.h>
51
52
53extern KICOMMON_API
54std::map<JOBSET_DESTINATION_T, JOBSET_DESTINATION_T_INFO> JobsetDestinationTypeInfos;
55
56
58{
59public:
60 DIALOG_JOBSET_RUN_LOG( wxWindow* aParent, JOBSET* aJobsFile, JOBSET_DESTINATION* aDestination ) :
62 m_jobsFile( aJobsFile ),
63 m_destination( aDestination ),
64 m_lastWidth( -1 ),
65 m_marginsWidth( -1 )
66 {
67 m_staticTextOutputName->SetLabel( wxString::Format( _( "Destination: %s" ),
68 aDestination->GetDescription() ) );
69
70 int jobBmpColId = m_jobList->AppendColumn( wxT( "" ) );
71 int jobNoColId = m_jobList->AppendColumn( _( "No." ) );
72 int jobDescColId = m_jobList->AppendColumn( _( "Job Description" ) );
73 int jobSourceColId = m_jobList->AppendColumn( _( "Source" ) );
74 m_jobList->SetColumnWidth( jobBmpColId, 26 );
75 m_jobList->SetColumnWidth( jobNoColId, GetTextExtent( wxT( "XXXX" ) ).GetWidth() );
76 m_jobList->SetColumnWidth( jobSourceColId, GetTextExtent( wxT( "XXXXXX" ) ).GetWidth() );
77
78 wxImageList* imageList = new wxImageList( 16, 16, true, 3 );
79 imageList->Add( KiBitmapBundle( BITMAPS::ercerr ).GetBitmap( wxSize( 16, 16 ) ) );
80 imageList->Add( KiBitmapBundle( BITMAPS::checked_ok ).GetBitmap( wxSize( 16, 16 ) ) );
81 m_jobList->SetImageList( imageList, wxIMAGE_LIST_SMALL );
82
83 int num = 1;
84
85 for( JOBSET_JOB& job : aJobsFile->GetJobsForDestination( aDestination ) )
86 {
87 int imageIdx = -1;
88
89 if( aDestination->m_lastRunSuccessMap.contains( job.m_id ) )
90 {
91 if( aDestination->m_lastRunSuccessMap[job.m_id].value() )
92 imageIdx = 1;
93 else
94 imageIdx = 0;
95 }
96
97 long itemIndex = m_jobList->InsertItem( m_jobList->GetItemCount(), imageIdx );
98
99 m_jobList->SetItem( itemIndex, jobNoColId, wxString::Format( "%d", num++ ) );
100 m_jobList->SetItem( itemIndex, jobDescColId, job.GetDescription() );
101
102 KIWAY::FACE_T iface = JOB_REGISTRY::GetKifaceType( job.m_type );
103 wxString source = wxEmptyString;
104
105 if( iface < KIWAY::KIWAY_FACE_COUNT )
106 {
107 if( iface == KIWAY::FACE_PCB )
108 source = wxT( "PCB" );
109 else if( iface == KIWAY::FACE_SCH )
110 source = wxT( "SCH" );
111 }
112
113 m_jobList->SetItem( itemIndex, jobSourceColId, source );
114 }
115
116 SetupStandardButtons( { { wxID_OK, _( "Close" ) } } );
118 }
119
120 virtual void OnUpdateUI( wxUpdateUIEvent& event ) override
121 {
122 if( GetSize().GetWidth() != m_lastWidth )
123 {
124 m_lastWidth = GetSize().GetWidth();
125
126 if( m_marginsWidth < 0 )
127 m_marginsWidth = m_lastWidth - ( m_jobList->GetSize().GetWidth() * 2 );
128
129 int width = ( m_lastWidth / 2 );
130 width -= m_marginsWidth;
131 width -= m_jobList->GetColumnWidth( 0 );
132 width -= m_jobList->GetColumnWidth( 1 );
133 width -= m_jobList->GetColumnWidth( 3 );
134
135 m_jobList->SetColumnWidth( 2, width );
136 }
137 }
138
139 void OnJobListItemSelected( wxListEvent& event ) override
140 {
141 int itemIndex = event.GetIndex();
142
143 // The index could be negative (it is default -1)
144 if( itemIndex < 0 )
145 return;
146
147 std::vector<JOBSET_JOB> jobs = m_jobsFile->GetJobsForDestination( m_destination );
148
149 if( static_cast<size_t>( itemIndex ) < jobs.size() )
150 {
151 wxString jobId = jobs[itemIndex].m_id;
152
153 if( m_destination->m_lastRunReporters.contains( jobId ) )
154 m_textCtrlOutput->SetValue( m_destination->m_lastRunReporters.at( jobId )->GetMessages() );
155 else
156 m_textCtrlOutput->SetValue( _( "No output messages" ) );
157 }
158 }
159
160private:
163
166};
167
168
170{
171public:
172 PANEL_DESTINATION( wxWindow* aParent, PANEL_JOBSET* aPanelParent, KICAD_MANAGER_FRAME* aFrame,
173 JOBSET* aFile, JOBSET_DESTINATION* aDestination ) :
174 PANEL_DESTINATION_BASE( aParent ),
175 m_jobsFile( aFile ),
176 m_destinationId( aDestination->m_id ),
177 m_frame( aFrame ),
178 m_panelParent( aPanelParent )
179 {
183
184#if _WIN32
185 // BORDER_RAISED/SUNKEN look pretty on every platform but Windows
186 long style = GetWindowStyleFlag();
187 style &= ~wxBORDER_MASK;
188 style |= wxBORDER_SIMPLE;
189 SetWindowStyleFlag( style );
190#endif // _WIN32
191
192 Connect( wxEVT_MENU, wxCommandEventHandler( PANEL_DESTINATION::onMenu ), nullptr, this );
193
194 if( JobsetDestinationTypeInfos.contains( aDestination->m_type ) )
195 {
196 JOBSET_DESTINATION_T_INFO& jobTypeInfo = JobsetDestinationTypeInfos[aDestination->m_type];
197 m_textOutputType->SetLabel( aDestination->GetDescription() );
198 m_bitmapOutputType->SetBitmap( KiBitmapBundle( jobTypeInfo.bitmap ) );
199 }
200
201 m_pathInfo->SetFont( KIUI::GetSmallInfoFont( this ) );
202 UpdatePathInfo( aDestination->GetPathInfo() );
203 UpdateStatus();
204 }
205
207 {
208 Disconnect( wxEVT_MENU, wxCommandEventHandler( PANEL_DESTINATION::onMenu ), nullptr, this );
209 }
210
212 {
213 JOBSET_DESTINATION* destination = GetDestination();
214 wxCHECK( destination, /*void*/ );
215
216 destination->m_lastRunSuccess = std::nullopt;
217 destination->m_lastResolvedOutputPath = std::nullopt;
218 m_statusBitmap->SetBitmap( wxNullBitmap );
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 }
248
249 void UpdatePathInfo( const wxString& aMsg )
250 {
251 wxClientDC dc( this );
252 int width = GetSize().GetWidth();
253
254 m_pathInfo->SetLabel( wxControl::Ellipsize( aMsg, dc, wxELLIPSIZE_MIDDLE, width ) );
255 }
256
257 virtual void OnGenerate( wxCommandEvent& event ) override
258 {
259 ClearStatus();
260 Refresh();
261
262 CallAfter(
263 [this]()
264 {
265 PROJECT& project = m_frame->Kiway().Prj();
266 m_panelParent->EnsurePcbSchFramesOpen();
267
268 wxFileName fn = project.GetProjectFullName();
269 wxSetWorkingDirectory( fn.GetPath() );
270
271 {
272 JOBS_PROGRESS_REPORTER progressReporter( m_frame, _( "Running Jobs" ) );
273 JOBS_RUNNER jobRunner( &m_frame->Kiway(), m_jobsFile, &project,
274 NULL_REPORTER::GetInstance(), &progressReporter );
275
276 if( JOBSET_DESTINATION* destination = GetDestination() )
277 jobRunner.RunJobsForDestination( destination );
278
279 UpdateStatus();
280 }
281
282 // Bring the Kicad manager frame back to the front
283 m_frame->Raise();
284 } );
285 }
286
287 void OnOpenOutput( wxCommandEvent& aEvent ) override
288 {
289 JOBSET_DESTINATION* destination = GetDestination();
290 wxCHECK( destination, /*void*/ );
291
292 wxString resolvedPath;
293
294 if( destination->m_lastResolvedOutputPath.has_value() )
295 {
296 resolvedPath = destination->m_lastResolvedOutputPath.value();
297 }
298 else
299 {
300 resolvedPath = ExpandTextVars( destination->GetPathInfo(), &m_frame->Prj() );
301 resolvedPath = ExpandEnvVarSubstitutions( resolvedPath, &m_frame->Prj() );
302
303 if( resolvedPath.StartsWith( "~" ) )
304 resolvedPath.Replace( "~", wxGetHomeDir(), false );
305 }
306
307 if( resolvedPath.IsEmpty() )
308 return;
309
310 wxString fullPath = wxFileName( resolvedPath ).GetFullPath();
311
312 if( !LaunchExternal( fullPath ) )
313 DisplayError( this, wxString::Format( _( "Failed to open '%s'." ), fullPath ) );
314 }
315
316 void OnLastStatusClick( wxMouseEvent& aEvent ) override
317 {
318 JOBSET_DESTINATION* destination = GetDestination();
319 wxCHECK( destination, /*void*/ );
320
321 DIALOG_JOBSET_RUN_LOG dialog( m_frame, m_jobsFile, destination );
322 dialog.ShowModal();
323 }
324
325 void OnRightDown( wxMouseEvent& aEvent ) override
326 {
327 JOBSET_DESTINATION* destination = GetDestination();
328 wxCHECK( destination, /*void*/ );
329
330 wxMenu menu;
331 menu.Append( wxID_EDIT, _( "Edit Destination Options..." ) );
332 menu.Append( wxID_DELETE, _( "Delete Destination" ) );
333
334 menu.AppendSeparator();
335 menu.Append( wxID_VIEW_DETAILS, _( "View Last Run Log..." ) );
336 menu.Append( wxID_OPEN, _( "Open Output" ) );
337
338 menu.Enable( wxID_VIEW_DETAILS, destination->m_lastRunSuccess.has_value() );
339 menu.Enable( wxID_OPEN, m_buttonOpenOutput->IsEnabled() );
340
341 PopupMenu( &menu );
342 }
343
344 void OnProperties( wxCommandEvent& aEvent ) override
345 {
346 JOBSET_DESTINATION* destination = GetDestination();
347 wxCHECK( destination, /*void*/ );
348
349 DIALOG_DESTINATION dialog( m_frame, m_jobsFile, destination );
350
351 if( dialog.ShowModal() == wxID_OK )
352 {
353 m_textOutputType->SetLabel( destination->GetDescription() );
354 UpdatePathInfo( destination->GetPathInfo() );
355 m_jobsFile->SetDirty();
356 m_panelParent->UpdateTitle();
357 ClearStatus();
358 }
359 }
360
361 virtual void OnDelete( wxCommandEvent& aEvent ) override
362 {
363 m_panelParent->RemoveDestination( this );
364 }
365
367 {
368 for( JOBSET_DESTINATION& destination : m_jobsFile->GetDestinations() )
369 {
370 if( destination.m_id == m_destinationId )
371 return &destination;
372 }
373
374 return nullptr;
375 }
376
377private:
378 void onMenu( wxCommandEvent& aEvent )
379 {
380 switch( aEvent.GetId() )
381 {
382 case wxID_EDIT:
383 {
384 wxCommandEvent dummy;
386 }
387 break;
388
389 case wxID_DELETE:
390 {
391 wxCommandEvent dummy;
392 OnDelete( dummy );
393 }
394 break;
395
396 case wxID_VIEW_DETAILS:
397 {
398 wxMouseEvent dummy;
400 }
401 break;
402
403 case wxID_OPEN:
404 {
405 wxCommandEvent dummy;
407 }
408 break;
409
410 default: wxFAIL_MSG( wxT( "Unknown ID in context menu event" ) );
411 }
412 }
413
414private:
419};
420
421
423 GRID_TRICKS( aGrid ),
424 m_parent( aParent ),
425 m_doubleClickRow( -1 )
426{
429}
430
431
432void JOBS_GRID_TRICKS::showPopupMenu( wxMenu& menu, wxGridEvent& aEvent )
433{
434 wxArrayInt selectedRows = m_grid->GetSelectedRows();
435
436 menu.Append( JOB_DESCRIPTION, _( "Edit Job Description" ) );
437 menu.Append( JOB_PROPERTIES, _( "Edit Job Settings..." ) );
438 menu.AppendSeparator();
439 menu.Append( GRIDTRICKS_ID_COPY, _( "Copy" ) + "\tCtrl+C", _( "Copy selected cells to clipboard" ) );
440 menu.Append( GRIDTRICKS_ID_DELETE, _( "Delete" ) + "\tDel", _( "Delete selected jobs" ) );
441 menu.Append( GRIDTRICKS_ID_SELECT, _( "Select All" ) + "\tCtrl+A", _( "Select all jobs" ) );
442
443 menu.Enable( JOB_DESCRIPTION, selectedRows.size() == 1 );
444 menu.Enable( JOB_PROPERTIES, selectedRows.size() == 1 );
445 menu.Enable( GRIDTRICKS_ID_DELETE, selectedRows.size() > 0 );
446
447 m_grid->PopupMenu( &menu );
448}
449
450
451void JOBS_GRID_TRICKS::doPopupSelection( wxCommandEvent& event )
452{
453 wxArrayInt selectedRows = m_grid->GetSelectedRows();
454
455 if( event.GetId() == JOB_DESCRIPTION )
456 {
457 if( selectedRows.size() > 0 )
458 {
459 m_grid->SetGridCursor( selectedRows[0], 2 );
460 m_grid->EnableCellEditControl();
461 }
462 }
463 else if( event.GetId() == JOB_PROPERTIES )
464 {
465 if( selectedRows.size() > 0 )
466 m_parent->OpenJobOptionsForListItem( selectedRows[0] );
467 }
468 else if( event.GetId() == GRIDTRICKS_ID_DELETE )
469 {
470 wxCommandEvent dummy;
471 m_parent->OnJobButtonDelete( dummy );
472 }
473 else
474 {
476 }
477}
478
479
480bool JOBS_GRID_TRICKS::handleDoubleClick( wxGridEvent& aEvent )
481{
482 m_grid->CancelShowEditorOnMouseUp();
483
484 int curr_col = aEvent.GetCol();
485 int curr_row = aEvent.GetRow();
486
487 if( ( curr_col == COL_NUMBER || curr_col == COL_SOURCE || curr_col == COL_DESCR )
488 && curr_row >= 0 && curr_row < (int) m_parent->GetJobsFile()->GetJobs().size() )
489 {
490 m_doubleClickRow = curr_row;
491 m_grid->CancelPendingChanges();
492
493 CallAfter(
494 [this]()
495 {
496 // Yes, again. CancelShowEditorOnMouseUp() doesn't appear to be 100%
497 // reliable.
498 m_grid->CancelPendingChanges();
499 int row = m_doubleClickRow;
500 m_doubleClickRow = -1;
501
502 if( row >= 0 && row < (int) m_parent->GetJobsFile()->GetJobs().size() )
503 m_parent->OpenJobOptionsForListItem( row );
504 } );
505
506 return true;
507 }
508
509 return false;
510}
511
512
513PANEL_JOBSET::PANEL_JOBSET( wxAuiNotebook* aParent, KICAD_MANAGER_FRAME* aFrame,
514 std::unique_ptr<JOBSET> aJobsFile ) :
515 PANEL_JOBSET_BASE( aParent ),
516 m_parentBook( aParent ),
517 m_frame( aFrame ),
518 m_jobsFile( std::move( aJobsFile ) )
519{
520 m_jobsGrid->PushEventHandler( new JOBS_GRID_TRICKS( this, m_jobsGrid ) );
521
522 m_jobsGrid->OverrideMinSize( 0.6, 0.3 );
523 m_jobsGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
524
525 // 'm' for margins
526 m_jobsGrid->SetColSize( COL_NUMBER, GetTextExtent( wxT( "99m" ) ).x );
527 m_jobsGrid->SetColSize( COL_SOURCE, GetTextExtent( wxT( "PCBm" ) ).x );
528
534
537}
538
539
541{
542 // Delete the GRID_TRICKS.
543 m_jobsGrid->PopEventHandler( true );
544}
545
546
548{
549 JOBSET_DESTINATION* output = aPanel->GetDestination();
550
551 m_destinationListSizer->Detach( aPanel );
552 aPanel->Destroy();
553
554 // ensure the window contents get shifted as needed
555 m_destinationList->Layout();
556 Layout();
557
558 m_jobsFile->RemoveDestination( output );
559}
560
561
563{
564 m_jobsGrid->ClearRows();
565 m_jobsGrid->AppendRows( (int) m_jobsFile->GetJobs().size() );
566
567 int num = 1;
568
569 for( JOBSET_JOB& job : m_jobsFile->GetJobs() )
570 {
571 m_jobsGrid->SetCellValue( num - 1, COL_NUMBER, wxString::Format( "%d", num ) );
572 m_jobsGrid->SetReadOnly( num - 1, COL_NUMBER );
573
574 m_jobsGrid->SetCellValue( num - 1, COL_DESCR, job.GetDescription() );
575
576 m_jobsGrid->SetReadOnly( num - 1, COL_SOURCE );
577
578 KIWAY::FACE_T iface = JOB_REGISTRY::GetKifaceType( job.m_type );
579 wxString source = wxEmptyString;
580
581 if( iface < KIWAY::KIWAY_FACE_COUNT )
582 {
583 if( iface == KIWAY::FACE_PCB )
584 source = wxT( "PCB" );
585 else if( iface == KIWAY::FACE_SCH )
586 source = wxT( "SCH" );
587 }
588
589 m_jobsGrid->SetCellValue( num - 1, COL_SOURCE, source );
590
591 num++;
592 }
593
594 UpdateTitle();
595
596 // Ensure the outputs get their Run-ability status updated
598 panel->UpdateStatus();
599}
600
601
603{
604 wxString tabName = m_jobsFile->GetFullName();
605
606 if( m_jobsFile->GetDirty() )
607 tabName = wxS( "*" ) + tabName;
608
609 int pageIdx = m_parentBook->FindPage( this );
610
611 if( pageIdx >= 0 )
612 m_parentBook->SetPageText( pageIdx, tabName );
613}
614
615
617{
618 PANEL_DESTINATION* destinationPanel = new PANEL_DESTINATION( m_destinationList, this, m_frame,
619 m_jobsFile.get(), aOutput );
620
621#if __OSX__
622 m_outputListSizer->Add( destinationPanel, 0, wxEXPAND, 5 );
623#else
624 m_destinationListSizer->Add( destinationPanel, 0, wxEXPAND|wxLEFT|wxRIGHT|wxBOTTOM, 5 );
625#endif
626
627 m_destinationList->Layout();
628}
629
630
631std::vector<PANEL_DESTINATION*> PANEL_JOBSET::GetDestinationPanels()
632{
633 std::vector<PANEL_DESTINATION*> panels;
634
635 for( const wxSizerItem* item : m_destinationListSizer->GetChildren() )
636 {
637 if( PANEL_DESTINATION* panel = dynamic_cast<PANEL_DESTINATION*>( item->GetWindow() ) )
638 panels.push_back( panel );
639 }
640
641 return panels;
642}
643
644
646{
647 Freeze();
648
649 for( JOBSET_DESTINATION& job : m_jobsFile->GetDestinations() )
650 addDestinationPanel( &job );
651
652 // ensure the window contents get shifted as needed
653 Layout();
654 Thaw();
655}
656
657
659{
660 bool success = false;
661 JOBSET_JOB& job = m_jobsFile->GetJobs()[aItemIndex];
663
664 if( iface < KIWAY::KIWAY_FACE_COUNT )
665 {
667 success = m_frame->Kiway().ProcessJobConfigDialog( iface, job.m_job.get(), m_frame );
668 }
669 else
670 {
671 // special jobs
672 if( job.m_job->GetType() == "special_execute" )
673 {
674 JOB_SPECIAL_EXECUTE* specialJob = static_cast<JOB_SPECIAL_EXECUTE*>( job.m_job.get() );
675
676 DIALOG_EXECUTECOMMAND_JOB_SETTINGS dialog( m_frame, specialJob );
677
678 // QuasiModal for Scintilla autocomplete
679 if( dialog.ShowQuasiModal() == wxID_OK )
680 success = true;
681 }
682 else if( job.m_job->GetType() == "special_copyfiles" )
683 {
684 JOB_SPECIAL_COPYFILES* specialJob = static_cast<JOB_SPECIAL_COPYFILES*>( job.m_job.get() );
685 DIALOG_COPYFILES_JOB_SETTINGS dialog( m_frame, specialJob );
686
687 if( dialog.ShowModal() == wxID_OK )
688 success = true;
689 }
690 }
691
692 if( success )
693 {
694 m_jobsFile->SetDirty();
695 UpdateTitle();
696 }
697
698 // Bring the Kicad manager frame back to the front
699 m_frame->Raise();
700
701 return success;
702}
703
704
705void PANEL_JOBSET::OnGridCellChange( wxGridEvent& aEvent )
706{
707 int row = aEvent.GetRow();
708 int col = aEvent.GetCol();
709
710 if( col == COL_DESCR )
711 m_jobsFile->GetJobs()[row].SetDescription( m_jobsGrid->GetCellValue( row, col ) );
712}
713
714
715void PANEL_JOBSET::OnSaveButtonClick( wxCommandEvent& aEvent )
716{
717 if( !m_jobsGrid->CommitPendingChanges() )
718 return;
719
720 m_jobsFile->SaveToFile( wxEmptyString, true );
721 UpdateTitle();
722}
723
724
725void PANEL_JOBSET::OnAddJobClick( wxCommandEvent& aEvent )
726{
727 if( !m_jobsGrid->CommitPendingChanges() )
728 return;
729
730 wxArrayString headers;
731 std::vector<wxArrayString> items;
732
733 headers.Add( _( "Job Types" ) );
734
736
737 for( const std::pair<const wxString, JOB_REGISTRY_ENTRY>& entry : jobMap )
738 {
739 if( entry.second.deprecated )
740 continue;
741
742 wxArrayString item;
743 item.Add( wxGetTranslation( entry.second.title ) );
744 items.emplace_back( item );
745 }
746
747 EDA_LIST_DIALOG dlg( this, _( "Add New Job" ), headers, items );
748 dlg.SetListLabel( _( "Select job type:" ) );
749
750 if( dlg.ShowModal() == wxID_OK )
751 {
752 wxString selectedString = dlg.GetTextSelection();
753
754 wxString jobKey;
755
756 if( !selectedString.IsEmpty() )
757 {
758 for( const std::pair<const wxString, JOB_REGISTRY_ENTRY>& entry : jobMap )
759 {
760 if( entry.second.deprecated )
761 continue;
762
763 if( wxGetTranslation( entry.second.title ) == selectedString )
764 {
765 jobKey = entry.first;
766 break;
767 }
768 }
769 }
770
771 if( !jobKey.IsEmpty() )
772 {
773 int row = m_jobsFile->GetJobs().size();
774 bool wasDirty = m_jobsFile->GetDirty();
775 JOB* job = JOB_REGISTRY::CreateInstance<JOB>( jobKey );
776
777 m_jobsFile->AddNewJob( jobKey, job );
778
779 if( OpenJobOptionsForListItem( row ) )
780 {
782
783 m_jobsGrid->SetGridCursor( row, 2 );
784 m_jobsGrid->EnableCellEditControl();
785 }
786 else
787 {
788 m_jobsFile->RemoveJob( row );
789 m_jobsFile->SetDirty( wasDirty );
790 }
791 }
792 }
793}
794
795
796void PANEL_JOBSET::OnJobButtonDelete( wxCommandEvent& aEvent )
797{
798 m_jobsGrid->OnDeleteRows(
799 [&]( int row )
800 {
801 m_jobsFile->RemoveJob( row );
802 m_jobsGrid->DeleteRows( row, 1 );
803 } );
804}
805
806
807void PANEL_JOBSET::OnAddDestinationClick( wxCommandEvent& aEvent )
808{
809 wxArrayString headers;
810 std::vector<wxArrayString> items;
811
812 headers.Add( _( "Destination Types" ) );
813
814 for( const auto& [destinationType, destinationTypeInfo] : JobsetDestinationTypeInfos )
815 {
816 wxArrayString item;
817 item.Add( wxGetTranslation( destinationTypeInfo.name ) );
818 items.emplace_back( item );
819 }
820
821 EDA_LIST_DIALOG dlg( this, _( "Add New Destination" ), headers, items );
822 dlg.SetListLabel( _( "Select destination type:" ) );
823 dlg.HideFilter();
824
825 if( dlg.ShowModal() == wxID_OK )
826 {
827 wxString selectedString = dlg.GetTextSelection();
828
829 for( const auto& [destinationType, destinationTypeInfo] : JobsetDestinationTypeInfos )
830 {
831 if( wxGetTranslation( destinationTypeInfo.name ) == selectedString )
832 {
833 JOBSET_DESTINATION* destination = m_jobsFile->AddNewDestination( destinationType );
834
835 DIALOG_DESTINATION dialog( m_frame, m_jobsFile.get(), destination );
836
837 if (dialog.ShowModal() == wxID_OK)
838 {
839 Freeze();
840 addDestinationPanel( destination );
841 Thaw();
842 }
843 else
844 {
845 // canceled
846 m_jobsFile->RemoveDestination( destination );
847 }
848 }
849 }
850 }
851}
852
853
855{
856 if( m_jobsFile->GetDirty() )
857 {
858 wxFileName fileName = m_jobsFile->GetFullFilename();
859 wxString msg = _( "Save changes to '%s' before closing?" );
860
861 if( !HandleUnsavedChanges( this, wxString::Format( msg, fileName.GetFullName() ),
862 [&]() -> bool
863 {
864 return m_jobsFile->SaveToFile( wxEmptyString, true );
865 } ) )
866 {
867 return false;
868 }
869 }
870
871 return true;
872}
873
874
876{
877 PROJECT& project = m_frame->Kiway().Prj();
878 KIWAY_PLAYER* frame = m_frame->Kiway().Player( FRAME_PCB_EDITOR, false );
879
880 if( !frame )
881 {
882 frame = m_frame->Kiway().Player( FRAME_PCB_EDITOR, true );
883
884 // frame can be null if Cvpcb cannot be run. No need to show a warning
885 // Kiway() generates the error messages
886 if( !frame )
887 return;
888
889 wxFileName boardfn = project.GetProjectFullName();
890 boardfn.SetExt( FILEEXT::PcbFileExtension );
891
892 // Prevent our window from being closed during the open process
893 wxEventBlocker blocker( this );
894
895 frame->OpenProjectFiles( std::vector<wxString>( 1, boardfn.GetFullPath() ) );
896
897 if( !frame->IsVisible() )
898 frame->Show( true );
899 }
900
901 frame = m_frame->Kiway().Player( FRAME_SCH, false );
902
903 if( !frame )
904 {
905 frame = m_frame->Kiway().Player( FRAME_SCH, true );
906
907 // frame can be null if Cvpcb cannot be run. No need to show a warning
908 // Kiway() generates the error messages
909 if( !frame )
910 return;
911
912 wxFileName schFn = project.GetProjectFullName();
914
915 wxEventBlocker blocker( this );
916
917 frame->OpenProjectFiles( std::vector<wxString>( 1, schFn.GetFullPath() ) );
918
919 if( !frame->IsVisible() )
920 frame->Show( true );
921 }
922
923 SetFocus();
924}
925
926
928{
929 return m_jobsFile->GetFullFilename();
930}
931
932
933void PANEL_JOBSET::OnJobButtonUp( wxCommandEvent& aEvent )
934{
935 if( !m_jobsGrid->CommitPendingChanges() )
936 return;
937
938 int item = m_jobsGrid->GetGridCursorRow();
939
940 if( item > 0 )
941 {
942 m_jobsFile->MoveJobUp( item );
943
945
946 m_jobsGrid->SelectRow( item - 1 );
947 m_jobsGrid->SetGridCursor( item - 1, m_jobsGrid->GetGridCursorCol() );
948 }
949 else
950 {
951 wxBell();
952 }
953}
954
955
956void PANEL_JOBSET::OnJobButtonDown( wxCommandEvent& aEvent )
957{
958 if( !m_jobsGrid->CommitPendingChanges() )
959 return;
960
961 int item = m_jobsGrid->GetGridCursorRow();
962
963 if( item < m_jobsGrid->GetNumberRows() - 1 )
964 {
965 m_jobsFile->MoveJobDown( item );
966
968
969 m_jobsGrid->SelectRow( item + 1 );
970 m_jobsGrid->SetGridCursor( item + 1, m_jobsGrid->GetGridCursorCol() );
971 }
972 else
973 {
974 wxBell();
975 }
976}
977
978
980{
981 if( !m_jobsGrid->CommitPendingChanges() )
982 return;
983
984 // sanity
985 if( m_jobsFile->GetDestinations().empty() )
986 {
987 DisplayError( this, _( "No destinations defined" ) );
988 return;
989 }
990
992 panel->ClearStatus();
993
994 Refresh();
995
996 CallAfter(
997 [this]()
998 {
999 PROJECT& project = m_frame->Kiway().Prj();
1001
1002 wxFileName fn = project.GetProjectFullName();
1003 wxSetWorkingDirectory( fn.GetPath() );
1004
1005 {
1006 JOBS_PROGRESS_REPORTER progressReporter( m_frame, _( "Running Jobs" ) );
1007 JOBS_RUNNER jobRunner( &m_frame->Kiway(), m_jobsFile.get(), &project,
1008 NULL_REPORTER::GetInstance(), &progressReporter );
1009
1010 jobRunner.RunJobsAllDestinations();
1011
1012 for( PANEL_DESTINATION* panel : GetDestinationPanels() )
1013 panel->UpdateStatus();
1014 }
1015
1016 // Bring the Kicad manager frame back to the front
1017 m_frame->Raise();
1018 } );
1019}
1020
1021
1022void PANEL_JOBSET::OnSizeGrid( wxSizeEvent& aEvent )
1023{
1024 m_jobsGrid->SetColSize( COL_DESCR, m_jobsGrid->GetSize().x - m_jobsGrid->GetColSize( COL_SOURCE )
1025 - m_jobsGrid->GetColSize( COL_NUMBER ) );
1026
1027 // Always propagate for a grid repaint (needed if the height changes, as well as width)
1028 aEvent.Skip();
1029}
1030
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:110
@ small_new_window
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:300
@ KIWAY_FACE_COUNT
Definition kiway.h:310
@ FACE_SCH
eeschema DSO
Definition kiway.h:301
@ FACE_PCB
pcbnew DSO
Definition kiway.h:302
static REPORTER & GetInstance()
Definition reporter.cpp:97
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
STD_BITMAP_BUTTON * m_buttonOpenOutput
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
PANEL_JOBSET_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
wxScrolledWindow * m_destinationList
wxBoxSizer * m_destinationListSizer
STD_BITMAP_BUTTON * m_buttonAddJob
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:65
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition common.cpp:558
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition common.cpp:62
The common library.
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.