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
18 * along with this program. If not, see <https://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 // Snapshot the selection, PopupMenu() below can lose it before doPopupSelection() runs
435 m_selectedRows = m_grid->GetSelectedRows();
436 wxArrayInt& selectedRows = m_selectedRows;
437
438 menu.Append( JOB_DESCRIPTION, _( "Edit Job Description" ) );
439 menu.Append( JOB_PROPERTIES, _( "Edit Job Settings..." ) );
440 menu.AppendSeparator();
441 menu.Append( GRIDTRICKS_ID_COPY, _( "Copy" ) + "\tCtrl+C", _( "Copy selected cells to clipboard" ) );
442 menu.Append( GRIDTRICKS_ID_DELETE, _( "Delete" ) + "\tDel", _( "Delete selected jobs" ) );
443 menu.Append( GRIDTRICKS_ID_SELECT, _( "Select All" ) + "\tCtrl+A", _( "Select all jobs" ) );
444
445 menu.Enable( JOB_DESCRIPTION, selectedRows.size() == 1 );
446 menu.Enable( JOB_PROPERTIES, selectedRows.size() == 1 );
447 menu.Enable( GRIDTRICKS_ID_DELETE, selectedRows.size() > 0 );
448
449 m_grid->PopupMenu( &menu );
450}
451
452
453void JOBS_GRID_TRICKS::doPopupSelection( wxCommandEvent& event )
454{
455 wxArrayInt& selectedRows = m_selectedRows;
456
457 if( event.GetId() == JOB_DESCRIPTION )
458 {
459 if( selectedRows.size() > 0 )
460 {
461 m_grid->SetGridCursor( selectedRows[0], 2 );
462 m_grid->EnableCellEditControl();
463 }
464 }
465 else if( event.GetId() == JOB_PROPERTIES )
466 {
467 if( selectedRows.size() > 0 )
468 m_parent->OpenJobOptionsForListItem( selectedRows[0] );
469 }
470 else if( event.GetId() == GRIDTRICKS_ID_DELETE )
471 {
472 wxCommandEvent dummy;
473 m_parent->OnJobButtonDelete( dummy );
474 }
475 else
476 {
478 }
479}
480
481
482bool JOBS_GRID_TRICKS::handleDoubleClick( wxGridEvent& aEvent )
483{
484 m_grid->CancelShowEditorOnMouseUp();
485
486 int curr_col = aEvent.GetCol();
487 int curr_row = aEvent.GetRow();
488
489 if( ( curr_col == COL_NUMBER || curr_col == COL_SOURCE || curr_col == COL_DESCR )
490 && curr_row >= 0 && curr_row < (int) m_parent->GetJobsFile()->GetJobs().size() )
491 {
492 m_doubleClickRow = curr_row;
493 m_grid->CancelPendingChanges();
494
495 CallAfter(
496 [this]()
497 {
498 // Yes, again. CancelShowEditorOnMouseUp() doesn't appear to be 100%
499 // reliable.
500 m_grid->CancelPendingChanges();
501 int row = m_doubleClickRow;
502 m_doubleClickRow = -1;
503
504 if( row >= 0 && row < (int) m_parent->GetJobsFile()->GetJobs().size() )
505 m_parent->OpenJobOptionsForListItem( row );
506 } );
507
508 return true;
509 }
510
511 return false;
512}
513
514
515PANEL_JOBSET::PANEL_JOBSET( wxAuiNotebook* aParent, KICAD_MANAGER_FRAME* aFrame,
516 std::unique_ptr<JOBSET> aJobsFile ) :
517 PANEL_JOBSET_BASE( aParent ),
518 m_parentBook( aParent ),
519 m_frame( aFrame ),
520 m_jobsFile( std::move( aJobsFile ) )
521{
522 m_jobsGrid->PushEventHandler( new JOBS_GRID_TRICKS( this, m_jobsGrid ) );
523
524 m_jobsGrid->OverrideMinSize( 0.6, 0.3 );
525 m_jobsGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
526
527 // 'm' for margins
528 m_jobsGrid->SetColSize( COL_NUMBER, GetTextExtent( wxT( "99m" ) ).x );
529 m_jobsGrid->SetColSize( COL_SOURCE, GetTextExtent( wxT( "PCBm" ) ).x );
530
536
539}
540
541
543{
544 // Delete the GRID_TRICKS.
545 m_jobsGrid->PopEventHandler( true );
546}
547
548
550{
552
553 m_destinationListSizer->Detach( aPanel );
554 aPanel->Destroy();
555
556 // ensure the window contents get shifted as needed
557 m_destinationList->Layout();
558 Layout();
559
560 m_jobsFile->RemoveDestination( output );
561}
562
563
565{
566 m_jobsGrid->ClearRows();
567 m_jobsGrid->AppendRows( (int) m_jobsFile->GetJobs().size() );
568
569 int num = 1;
570
571 for( JOBSET_JOB& job : m_jobsFile->GetJobs() )
572 {
573 m_jobsGrid->SetCellValue( num - 1, COL_NUMBER, wxString::Format( "%d", num ) );
574 m_jobsGrid->SetReadOnly( num - 1, COL_NUMBER );
575
576 m_jobsGrid->SetCellValue( num - 1, COL_DESCR, job.GetDescription() );
577
578 m_jobsGrid->SetReadOnly( num - 1, COL_SOURCE );
579
580 KIWAY::FACE_T iface = JOB_REGISTRY::GetKifaceType( job.m_type );
581 wxString source = wxEmptyString;
582
583 if( iface < KIWAY::KIWAY_FACE_COUNT )
584 {
585 if( iface == KIWAY::FACE_PCB )
586 source = wxT( "PCB" );
587 else if( iface == KIWAY::FACE_SCH )
588 source = wxT( "SCH" );
589 }
590
591 m_jobsGrid->SetCellValue( num - 1, COL_SOURCE, source );
592
593 num++;
594 }
595
596 UpdateTitle();
597
598 // Ensure the outputs get their Run-ability status updated
600 panel->UpdateStatus();
601}
602
603
605{
606 wxString tabName = m_jobsFile->GetFullName();
607
608 if( m_jobsFile->GetDirty() )
609 tabName = wxS( "*" ) + tabName;
610
611 int pageIdx = m_parentBook->FindPage( this );
612
613 if( pageIdx >= 0 )
614 m_parentBook->SetPageText( pageIdx, tabName );
615}
616
617
619{
620 PANEL_DESTINATION* destinationPanel = new PANEL_DESTINATION( m_destinationList, this, m_frame,
621 m_jobsFile.get(), aOutput );
622
623#if __OSX__
624 m_outputListSizer->Add( destinationPanel, 0, wxEXPAND, 5 );
625#else
626 m_destinationListSizer->Add( destinationPanel, 0, wxEXPAND|wxLEFT|wxRIGHT|wxBOTTOM, 5 );
627#endif
628
629 m_destinationList->Layout();
630}
631
632
633std::vector<PANEL_DESTINATION*> PANEL_JOBSET::GetDestinationPanels()
634{
635 std::vector<PANEL_DESTINATION*> panels;
636
637 for( const wxSizerItem* item : m_destinationListSizer->GetChildren() )
638 {
639 if( PANEL_DESTINATION* panel = dynamic_cast<PANEL_DESTINATION*>( item->GetWindow() ) )
640 panels.push_back( panel );
641 }
642
643 return panels;
644}
645
646
648{
649 Freeze();
650
651 for( JOBSET_DESTINATION& job : m_jobsFile->GetDestinations() )
652 addDestinationPanel( &job );
653
654 // ensure the window contents get shifted as needed
655 Layout();
656 Thaw();
657}
658
659
661{
662 bool success = false;
663 JOBSET_JOB& job = m_jobsFile->GetJobs()[aItemIndex];
665
666 if( iface < KIWAY::KIWAY_FACE_COUNT )
667 {
669 success = m_frame->Kiway().ProcessJobConfigDialog( iface, job.m_job.get(), m_frame );
670 }
671 else
672 {
673 // special jobs
674 if( job.m_job->GetType() == "special_execute" )
675 {
676 JOB_SPECIAL_EXECUTE* specialJob = static_cast<JOB_SPECIAL_EXECUTE*>( job.m_job.get() );
677
678 DIALOG_EXECUTECOMMAND_JOB_SETTINGS dialog( m_frame, specialJob );
679
680 // QuasiModal for Scintilla autocomplete
681 if( dialog.ShowQuasiModal() == wxID_OK )
682 success = true;
683 }
684 else if( job.m_job->GetType() == "special_copyfiles" )
685 {
686 JOB_SPECIAL_COPYFILES* specialJob = static_cast<JOB_SPECIAL_COPYFILES*>( job.m_job.get() );
687 DIALOG_COPYFILES_JOB_SETTINGS dialog( m_frame, specialJob );
688
689 if( dialog.ShowModal() == wxID_OK )
690 success = true;
691 }
692 }
693
694 if( success )
695 {
696 m_jobsFile->SetDirty();
697 UpdateTitle();
698 }
699
700 // Bring the Kicad manager frame back to the front
701 m_frame->Raise();
702
703 return success;
704}
705
706
707void PANEL_JOBSET::OnGridCellChange( wxGridEvent& aEvent )
708{
709 int row = aEvent.GetRow();
710 int col = aEvent.GetCol();
711
712 if( col == COL_DESCR )
713 m_jobsFile->GetJobs()[row].SetDescription( m_jobsGrid->GetCellValue( row, col ) );
714}
715
716
717void PANEL_JOBSET::OnSaveButtonClick( wxCommandEvent& aEvent )
718{
719 if( !m_jobsGrid->CommitPendingChanges() )
720 return;
721
722 m_jobsFile->SaveToFile( wxEmptyString, true );
723 UpdateTitle();
724}
725
726
727void PANEL_JOBSET::OnAddJobClick( wxCommandEvent& aEvent )
728{
729 if( !m_jobsGrid->CommitPendingChanges() )
730 return;
731
732 wxArrayString headers;
733 std::vector<wxArrayString> items;
734
735 headers.Add( _( "Job Types" ) );
736
738
739 for( const std::pair<const wxString, JOB_REGISTRY_ENTRY>& entry : jobMap )
740 {
741 if( entry.second.deprecated )
742 continue;
743
744 wxArrayString item;
745 item.Add( wxGetTranslation( entry.second.title ) );
746 items.emplace_back( item );
747 }
748
749 EDA_LIST_DIALOG dlg( this, _( "Add New Job" ), headers, items );
750 dlg.SetListLabel( _( "Select job type:" ) );
751
752 if( dlg.ShowModal() == wxID_OK )
753 {
754 wxString selectedString = dlg.GetTextSelection();
755
756 wxString jobKey;
757
758 if( !selectedString.IsEmpty() )
759 {
760 for( const std::pair<const wxString, JOB_REGISTRY_ENTRY>& entry : jobMap )
761 {
762 if( entry.second.deprecated )
763 continue;
764
765 if( wxGetTranslation( entry.second.title ) == selectedString )
766 {
767 jobKey = entry.first;
768 break;
769 }
770 }
771 }
772
773 if( !jobKey.IsEmpty() )
774 {
775 int row = m_jobsFile->GetJobs().size();
776 bool wasDirty = m_jobsFile->GetDirty();
777 JOB* job = JOB_REGISTRY::CreateInstance<JOB>( jobKey );
778
779 m_jobsFile->AddNewJob( jobKey, job );
780
781 if( OpenJobOptionsForListItem( row ) )
782 {
784
785 m_jobsGrid->SetGridCursor( row, 2 );
786 m_jobsGrid->EnableCellEditControl();
787 }
788 else
789 {
790 m_jobsFile->RemoveJob( row );
791 m_jobsFile->SetDirty( wasDirty );
792 }
793 }
794 }
795}
796
797
798void PANEL_JOBSET::OnJobButtonDelete( wxCommandEvent& aEvent )
799{
800 m_jobsGrid->OnDeleteRows(
801 [&]( int row )
802 {
803 m_jobsFile->RemoveJob( row );
804 m_jobsGrid->DeleteRows( row, 1 );
805 } );
806}
807
808
809void PANEL_JOBSET::OnAddDestinationClick( wxCommandEvent& aEvent )
810{
811 wxArrayString headers;
812 std::vector<wxArrayString> items;
813
814 headers.Add( _( "Destination Types" ) );
815
816 for( const auto& [destinationType, destinationTypeInfo] : JobsetDestinationTypeInfos )
817 {
818 wxArrayString item;
819 item.Add( wxGetTranslation( destinationTypeInfo.name ) );
820 items.emplace_back( item );
821 }
822
823 EDA_LIST_DIALOG dlg( this, _( "Add New Destination" ), headers, items );
824 dlg.SetListLabel( _( "Select destination type:" ) );
825 dlg.HideFilter();
826
827 if( dlg.ShowModal() == wxID_OK )
828 {
829 wxString selectedString = dlg.GetTextSelection();
830
831 for( const auto& [destinationType, destinationTypeInfo] : JobsetDestinationTypeInfos )
832 {
833 if( wxGetTranslation( destinationTypeInfo.name ) == selectedString )
834 {
835 JOBSET_DESTINATION* destination = m_jobsFile->AddNewDestination( destinationType );
836
837 DIALOG_DESTINATION dialog( m_frame, m_jobsFile.get(), destination );
838
839 if (dialog.ShowModal() == wxID_OK)
840 {
841 Freeze();
842 addDestinationPanel( destination );
843 Thaw();
844 }
845 else
846 {
847 // canceled
848 m_jobsFile->RemoveDestination( destination );
849 }
850 }
851 }
852 }
853}
854
855
857{
858 if( m_jobsFile->GetDirty() )
859 {
860 wxFileName fileName = m_jobsFile->GetFullFilename();
861 wxString msg = _( "Save changes to '%s' before closing?" );
862
863 if( !HandleUnsavedChanges( this, wxString::Format( msg, fileName.GetFullName() ),
864 [&]() -> bool
865 {
866 return m_jobsFile->SaveToFile( wxEmptyString, true );
867 } ) )
868 {
869 return false;
870 }
871 }
872
873 return true;
874}
875
876
878{
879 PROJECT& project = m_frame->Kiway().Prj();
880 KIWAY_PLAYER* frame = m_frame->Kiway().Player( FRAME_PCB_EDITOR, false );
881
882 if( !frame )
883 {
884 frame = m_frame->Kiway().Player( FRAME_PCB_EDITOR, true );
885
886 // frame can be null if Cvpcb cannot be run. No need to show a warning
887 // Kiway() generates the error messages
888 if( !frame )
889 return;
890
891 wxFileName boardfn = project.GetProjectFullName();
892 boardfn.SetExt( FILEEXT::PcbFileExtension );
893
894 // Prevent our window from being closed during the open process
895 wxEventBlocker blocker( this );
896
897 frame->OpenProjectFiles( std::vector<wxString>( 1, boardfn.GetFullPath() ) );
898
899 if( !frame->IsVisible() )
900 frame->Show( true );
901 }
902
903 frame = m_frame->Kiway().Player( FRAME_SCH, false );
904
905 if( !frame )
906 {
907 frame = m_frame->Kiway().Player( FRAME_SCH, true );
908
909 // frame can be null if Cvpcb cannot be run. No need to show a warning
910 // Kiway() generates the error messages
911 if( !frame )
912 return;
913
914 wxFileName schFn = project.GetProjectFullName();
916
917 wxEventBlocker blocker( this );
918
919 frame->OpenProjectFiles( std::vector<wxString>( 1, schFn.GetFullPath() ) );
920
921 if( !frame->IsVisible() )
922 frame->Show( true );
923 }
924
925 SetFocus();
926}
927
928
930{
931 return m_jobsFile->GetFullFilename();
932}
933
934
935void PANEL_JOBSET::OnJobButtonUp( wxCommandEvent& aEvent )
936{
937 if( !m_jobsGrid->CommitPendingChanges() )
938 return;
939
940 int item = m_jobsGrid->GetGridCursorRow();
941
942 if( item > 0 )
943 {
944 m_jobsFile->MoveJobUp( item );
945
947
948 m_jobsGrid->SelectRow( item - 1 );
949 m_jobsGrid->SetGridCursor( item - 1, m_jobsGrid->GetGridCursorCol() );
950 }
951 else
952 {
953 wxBell();
954 }
955}
956
957
958void PANEL_JOBSET::OnJobButtonDown( wxCommandEvent& aEvent )
959{
960 if( !m_jobsGrid->CommitPendingChanges() )
961 return;
962
963 int item = m_jobsGrid->GetGridCursorRow();
964
965 if( item < m_jobsGrid->GetNumberRows() - 1 )
966 {
967 m_jobsFile->MoveJobDown( item );
968
970
971 m_jobsGrid->SelectRow( item + 1 );
972 m_jobsGrid->SetGridCursor( item + 1, m_jobsGrid->GetGridCursorCol() );
973 }
974 else
975 {
976 wxBell();
977 }
978}
979
980
982{
983 if( !m_jobsGrid->CommitPendingChanges() )
984 return;
985
986 // sanity
987 if( m_jobsFile->GetDestinations().empty() )
988 {
989 DisplayError( this, _( "No destinations defined" ) );
990 return;
991 }
992
994 panel->ClearStatus();
995
996 Refresh();
997
998 CallAfter(
999 [this]()
1000 {
1001 PROJECT& project = m_frame->Kiway().Prj();
1003
1004 wxFileName fn = project.GetProjectFullName();
1005 wxSetWorkingDirectory( fn.GetPath() );
1006
1007 {
1008 JOBS_PROGRESS_REPORTER progressReporter( m_frame, _( "Running Jobs" ) );
1009 JOBS_RUNNER jobRunner( &m_frame->Kiway(), m_jobsFile.get(), &project,
1010 NULL_REPORTER::GetInstance(), &progressReporter );
1011
1012 jobRunner.RunJobsAllDestinations();
1013
1014 for( PANEL_DESTINATION* panel : GetDestinationPanels() )
1015 panel->UpdateStatus();
1016 }
1017
1018 // Bring the Kicad manager frame back to the front
1019 m_frame->Raise();
1020 } );
1021}
1022
1023
1024void PANEL_JOBSET::OnSizeGrid( wxSizeEvent& aEvent )
1025{
1026 m_jobsGrid->SetColSize( COL_DESCR, m_jobsGrid->GetSize().x - m_jobsGrid->GetColSize( COL_SOURCE )
1027 - m_jobsGrid->GetColSize( COL_NUMBER ) );
1028
1029 // Always propagate for a grid repaint (needed if the height changes, as well as width)
1030 aEvent.Skip();
1031}
1032
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
@ 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
wxArrayInt m_selectedRows
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:184
const std::string & GetType() const
Definition job.h:195
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:317
@ KIWAY_FACE_COUNT
Definition kiway.h:326
@ FACE_SCH
eeschema DSO
Definition kiway.h:318
@ FACE_PCB
pcbnew DSO
Definition kiway.h:319
static REPORTER & GetInstance()
Definition reporter.cpp:120
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:62
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition common.cpp:721
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition common.cpp:59
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:146
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.
#define _(s)
@ FRAME_PCB_EDITOR
Definition frame_type.h:38
@ FRAME_SCH
Definition frame_type.h:30
@ GRIDTRICKS_ID_SELECT
Definition grid_tricks.h:42
@ GRIDTRICKS_ID_COPY
Definition grid_tricks.h:39
@ GRIDTRICKS_ID_DELETE
Definition grid_tricks.h:40
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:27
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
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
nlohmann::json output
Definition of file extensions used in Kicad.