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 if( !LaunchExternal( resolvedPath ) )
311 DisplayError( this, wxString::Format( _( "Failed to open '%s'." ), resolvedPath ) );
312 }
313
314 void OnLastStatusClick( wxMouseEvent& aEvent ) override
315 {
316 JOBSET_DESTINATION* destination = GetDestination();
317 wxCHECK( destination, /*void*/ );
318
319 DIALOG_JOBSET_RUN_LOG dialog( m_frame, m_jobsFile, destination );
320 dialog.ShowModal();
321 }
322
323 void OnRightDown( wxMouseEvent& aEvent ) override
324 {
325 JOBSET_DESTINATION* destination = GetDestination();
326 wxCHECK( destination, /*void*/ );
327
328 wxMenu menu;
329 menu.Append( wxID_EDIT, _( "Edit Destination Options..." ) );
330 menu.Append( wxID_DELETE, _( "Delete Destination" ) );
331
332 menu.AppendSeparator();
333 menu.Append( wxID_VIEW_DETAILS, _( "View Last Run Log..." ) );
334 menu.Append( wxID_OPEN, _( "Open Output" ) );
335
336 menu.Enable( wxID_VIEW_DETAILS, destination->m_lastRunSuccess.has_value() );
337 menu.Enable( wxID_OPEN, m_buttonOpenOutput->IsEnabled() );
338
339 PopupMenu( &menu );
340 }
341
342 void OnProperties( wxCommandEvent& aEvent ) override
343 {
344 JOBSET_DESTINATION* destination = GetDestination();
345 wxCHECK( destination, /*void*/ );
346
347 DIALOG_DESTINATION dialog( m_frame, m_jobsFile, destination );
348
349 if( dialog.ShowModal() == wxID_OK )
350 {
351 m_textOutputType->SetLabel( destination->GetDescription() );
352 UpdatePathInfo( destination->GetPathInfo() );
353 m_jobsFile->SetDirty();
354 m_panelParent->UpdateTitle();
355 ClearStatus();
356 }
357 }
358
359 virtual void OnDelete( wxCommandEvent& aEvent ) override
360 {
361 m_panelParent->RemoveDestination( this );
362 }
363
365 {
366 for( JOBSET_DESTINATION& destination : m_jobsFile->GetDestinations() )
367 {
368 if( destination.m_id == m_destinationId )
369 return &destination;
370 }
371
372 return nullptr;
373 }
374
375private:
376 void onMenu( wxCommandEvent& aEvent )
377 {
378 switch( aEvent.GetId() )
379 {
380 case wxID_EDIT:
381 {
382 wxCommandEvent dummy;
384 }
385 break;
386
387 case wxID_DELETE:
388 {
389 wxCommandEvent dummy;
390 OnDelete( dummy );
391 }
392 break;
393
394 case wxID_VIEW_DETAILS:
395 {
396 wxMouseEvent dummy;
398 }
399 break;
400
401 case wxID_OPEN:
402 {
403 wxCommandEvent dummy;
405 }
406 break;
407
408 default: wxFAIL_MSG( wxT( "Unknown ID in context menu event" ) );
409 }
410 }
411
412private:
417};
418
419
421 GRID_TRICKS( aGrid ),
422 m_parent( aParent ),
423 m_doubleClickRow( -1 )
424{
427}
428
429
430void JOBS_GRID_TRICKS::showPopupMenu( wxMenu& menu, wxGridEvent& aEvent )
431{
432 wxArrayInt selectedRows = m_grid->GetSelectedRows();
433
434 menu.Append( JOB_DESCRIPTION, _( "Edit Job Description" ) );
435 menu.Append( JOB_PROPERTIES, _( "Edit Job Settings..." ) );
436 menu.AppendSeparator();
437 menu.Append( GRIDTRICKS_ID_COPY, _( "Copy" ) + "\tCtrl+C", _( "Copy selected cells to clipboard" ) );
438 menu.Append( GRIDTRICKS_ID_DELETE, _( "Delete" ) + "\tDel", _( "Delete selected jobs" ) );
439 menu.Append( GRIDTRICKS_ID_SELECT, _( "Select All" ) + "\tCtrl+A", _( "Select all jobs" ) );
440
441 menu.Enable( JOB_DESCRIPTION, selectedRows.size() == 1 );
442 menu.Enable( JOB_PROPERTIES, selectedRows.size() == 1 );
443 menu.Enable( GRIDTRICKS_ID_DELETE, selectedRows.size() > 0 );
444
445 m_grid->PopupMenu( &menu );
446}
447
448
449void JOBS_GRID_TRICKS::doPopupSelection( wxCommandEvent& event )
450{
451 wxArrayInt selectedRows = m_grid->GetSelectedRows();
452
453 if( event.GetId() == JOB_DESCRIPTION )
454 {
455 if( selectedRows.size() > 0 )
456 {
457 m_grid->SetGridCursor( selectedRows[0], 2 );
458 m_grid->EnableCellEditControl();
459 }
460 }
461 else if( event.GetId() == JOB_PROPERTIES )
462 {
463 if( selectedRows.size() > 0 )
464 m_parent->OpenJobOptionsForListItem( selectedRows[0] );
465 }
466 else if( event.GetId() == GRIDTRICKS_ID_DELETE )
467 {
468 wxCommandEvent dummy;
469 m_parent->OnJobButtonDelete( dummy );
470 }
471 else
472 {
474 }
475}
476
477
478bool JOBS_GRID_TRICKS::handleDoubleClick( wxGridEvent& aEvent )
479{
480 m_grid->CancelShowEditorOnMouseUp();
481
482 int curr_col = aEvent.GetCol();
483 int curr_row = aEvent.GetRow();
484
485 if( ( curr_col == COL_NUMBER || curr_col == COL_SOURCE || curr_col == COL_DESCR )
486 && curr_row >= 0 && curr_row < (int) m_parent->GetJobsFile()->GetJobs().size() )
487 {
488 m_doubleClickRow = curr_row;
489 m_grid->CancelPendingChanges();
490
491 CallAfter(
492 [this]()
493 {
494 // Yes, again. CancelShowEditorOnMouseUp() doesn't appear to be 100%
495 // reliable.
496 m_grid->CancelPendingChanges();
497 int row = m_doubleClickRow;
498 m_doubleClickRow = -1;
499
500 if( row >= 0 && row < (int) m_parent->GetJobsFile()->GetJobs().size() )
501 m_parent->OpenJobOptionsForListItem( row );
502 } );
503
504 return true;
505 }
506
507 return false;
508}
509
510
511PANEL_JOBSET::PANEL_JOBSET( wxAuiNotebook* aParent, KICAD_MANAGER_FRAME* aFrame,
512 std::unique_ptr<JOBSET> aJobsFile ) :
513 PANEL_JOBSET_BASE( aParent ),
514 m_parentBook( aParent ),
515 m_frame( aFrame ),
516 m_jobsFile( std::move( aJobsFile ) )
517{
518 m_jobsGrid->PushEventHandler( new JOBS_GRID_TRICKS( this, m_jobsGrid ) );
519
520 m_jobsGrid->OverrideMinSize( 0.6, 0.3 );
521 m_jobsGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
522
523 // 'm' for margins
524 m_jobsGrid->SetColSize( COL_NUMBER, GetTextExtent( wxT( "99m" ) ).x );
525 m_jobsGrid->SetColSize( COL_SOURCE, GetTextExtent( wxT( "PCBm" ) ).x );
526
532
535}
536
537
539{
540 // Delete the GRID_TRICKS.
541 m_jobsGrid->PopEventHandler( true );
542}
543
544
546{
547 JOBSET_DESTINATION* output = aPanel->GetDestination();
548
549 m_destinationListSizer->Detach( aPanel );
550 aPanel->Destroy();
551
552 // ensure the window contents get shifted as needed
553 m_destinationList->Layout();
554 Layout();
555
556 m_jobsFile->RemoveDestination( output );
557}
558
559
561{
562 m_jobsGrid->ClearRows();
563 m_jobsGrid->AppendRows( (int) m_jobsFile->GetJobs().size() );
564
565 int num = 1;
566
567 for( JOBSET_JOB& job : m_jobsFile->GetJobs() )
568 {
569 m_jobsGrid->SetCellValue( num - 1, COL_NUMBER, wxString::Format( "%d", num ) );
570 m_jobsGrid->SetReadOnly( num - 1, COL_NUMBER );
571
572 m_jobsGrid->SetCellValue( num - 1, COL_DESCR, job.GetDescription() );
573
574 m_jobsGrid->SetReadOnly( num - 1, COL_SOURCE );
575
576 KIWAY::FACE_T iface = JOB_REGISTRY::GetKifaceType( job.m_type );
577 wxString source = wxEmptyString;
578
579 if( iface < KIWAY::KIWAY_FACE_COUNT )
580 {
581 if( iface == KIWAY::FACE_PCB )
582 source = wxT( "PCB" );
583 else if( iface == KIWAY::FACE_SCH )
584 source = wxT( "SCH" );
585 }
586
587 m_jobsGrid->SetCellValue( num - 1, COL_SOURCE, source );
588
589 num++;
590 }
591
592 UpdateTitle();
593
594 // Ensure the outputs get their Run-ability status updated
596 panel->UpdateStatus();
597}
598
599
601{
602 wxString tabName = m_jobsFile->GetFullName();
603
604 if( m_jobsFile->GetDirty() )
605 tabName = wxS( "*" ) + tabName;
606
607 int pageIdx = m_parentBook->FindPage( this );
608
609 if( pageIdx >= 0 )
610 m_parentBook->SetPageText( pageIdx, tabName );
611}
612
613
615{
616 PANEL_DESTINATION* destinationPanel = new PANEL_DESTINATION( m_destinationList, this, m_frame,
617 m_jobsFile.get(), aOutput );
618
619#if __OSX__
620 m_outputListSizer->Add( destinationPanel, 0, wxEXPAND, 5 );
621#else
622 m_destinationListSizer->Add( destinationPanel, 0, wxEXPAND|wxLEFT|wxRIGHT|wxBOTTOM, 5 );
623#endif
624
625 m_destinationList->Layout();
626}
627
628
629std::vector<PANEL_DESTINATION*> PANEL_JOBSET::GetDestinationPanels()
630{
631 std::vector<PANEL_DESTINATION*> panels;
632
633 for( const wxSizerItem* item : m_destinationListSizer->GetChildren() )
634 {
635 if( PANEL_DESTINATION* panel = dynamic_cast<PANEL_DESTINATION*>( item->GetWindow() ) )
636 panels.push_back( panel );
637 }
638
639 return panels;
640}
641
642
644{
645 Freeze();
646
647 for( JOBSET_DESTINATION& job : m_jobsFile->GetDestinations() )
648 addDestinationPanel( &job );
649
650 // ensure the window contents get shifted as needed
651 Layout();
652 Thaw();
653}
654
655
657{
658 bool success = false;
659 JOBSET_JOB& job = m_jobsFile->GetJobs()[aItemIndex];
661
662 if( iface < KIWAY::KIWAY_FACE_COUNT )
663 {
665 success = m_frame->Kiway().ProcessJobConfigDialog( iface, job.m_job.get(), m_frame );
666 }
667 else
668 {
669 // special jobs
670 if( job.m_job->GetType() == "special_execute" )
671 {
672 JOB_SPECIAL_EXECUTE* specialJob = static_cast<JOB_SPECIAL_EXECUTE*>( job.m_job.get() );
673
674 DIALOG_EXECUTECOMMAND_JOB_SETTINGS dialog( m_frame, specialJob );
675
676 // QuasiModal for Scintilla autocomplete
677 if( dialog.ShowQuasiModal() == wxID_OK )
678 success = true;
679 }
680 else if( job.m_job->GetType() == "special_copyfiles" )
681 {
682 JOB_SPECIAL_COPYFILES* specialJob = static_cast<JOB_SPECIAL_COPYFILES*>( job.m_job.get() );
683 DIALOG_COPYFILES_JOB_SETTINGS dialog( m_frame, specialJob );
684
685 if( dialog.ShowModal() == wxID_OK )
686 success = true;
687 }
688 }
689
690 if( success )
691 {
692 m_jobsFile->SetDirty();
693 UpdateTitle();
694 }
695
696 // Bring the Kicad manager frame back to the front
697 m_frame->Raise();
698
699 return success;
700}
701
702
703void PANEL_JOBSET::OnGridCellChange( wxGridEvent& aEvent )
704{
705 int row = aEvent.GetRow();
706 int col = aEvent.GetCol();
707
708 if( col == COL_DESCR )
709 m_jobsFile->GetJobs()[row].SetDescription( m_jobsGrid->GetCellValue( row, col ) );
710}
711
712
713void PANEL_JOBSET::OnSaveButtonClick( wxCommandEvent& aEvent )
714{
715 if( !m_jobsGrid->CommitPendingChanges() )
716 return;
717
718 m_jobsFile->SaveToFile( wxEmptyString, true );
719 UpdateTitle();
720}
721
722
723void PANEL_JOBSET::OnAddJobClick( wxCommandEvent& aEvent )
724{
725 if( !m_jobsGrid->CommitPendingChanges() )
726 return;
727
728 wxArrayString headers;
729 std::vector<wxArrayString> items;
730
731 headers.Add( _( "Job Types" ) );
732
734
735 for( const std::pair<const wxString, JOB_REGISTRY_ENTRY>& entry : jobMap )
736 {
737 if( entry.second.deprecated )
738 continue;
739
740 wxArrayString item;
741 item.Add( wxGetTranslation( entry.second.title ) );
742 items.emplace_back( item );
743 }
744
745 EDA_LIST_DIALOG dlg( this, _( "Add New Job" ), headers, items );
746 dlg.SetListLabel( _( "Select job type:" ) );
747
748 if( dlg.ShowModal() == wxID_OK )
749 {
750 wxString selectedString = dlg.GetTextSelection();
751
752 wxString jobKey;
753
754 if( !selectedString.IsEmpty() )
755 {
756 for( const std::pair<const wxString, JOB_REGISTRY_ENTRY>& entry : jobMap )
757 {
758 if( entry.second.deprecated )
759 continue;
760
761 if( wxGetTranslation( entry.second.title ) == selectedString )
762 {
763 jobKey = entry.first;
764 break;
765 }
766 }
767 }
768
769 if( !jobKey.IsEmpty() )
770 {
771 int row = m_jobsFile->GetJobs().size();
772 bool wasDirty = m_jobsFile->GetDirty();
773 JOB* job = JOB_REGISTRY::CreateInstance<JOB>( jobKey );
774
775 m_jobsFile->AddNewJob( jobKey, job );
776
777 if( OpenJobOptionsForListItem( row ) )
778 {
780
781 m_jobsGrid->SetGridCursor( row, 2 );
782 m_jobsGrid->EnableCellEditControl();
783 }
784 else
785 {
786 m_jobsFile->RemoveJob( row );
787 m_jobsFile->SetDirty( wasDirty );
788 }
789 }
790 }
791}
792
793
794void PANEL_JOBSET::OnJobButtonDelete( wxCommandEvent& aEvent )
795{
796 m_jobsGrid->OnDeleteRows(
797 [&]( int row )
798 {
799 m_jobsFile->RemoveJob( row );
800 m_jobsGrid->DeleteRows( row, 1 );
801 } );
802}
803
804
805void PANEL_JOBSET::OnAddDestinationClick( wxCommandEvent& aEvent )
806{
807 wxArrayString headers;
808 std::vector<wxArrayString> items;
809
810 headers.Add( _( "Destination Types" ) );
811
812 for( const auto& [destinationType, destinationTypeInfo] : JobsetDestinationTypeInfos )
813 {
814 wxArrayString item;
815 item.Add( wxGetTranslation( destinationTypeInfo.name ) );
816 items.emplace_back( item );
817 }
818
819 EDA_LIST_DIALOG dlg( this, _( "Add New Destination" ), headers, items );
820 dlg.SetListLabel( _( "Select destination type:" ) );
821 dlg.HideFilter();
822
823 if( dlg.ShowModal() == wxID_OK )
824 {
825 wxString selectedString = dlg.GetTextSelection();
826
827 for( const auto& [destinationType, destinationTypeInfo] : JobsetDestinationTypeInfos )
828 {
829 if( wxGetTranslation( destinationTypeInfo.name ) == selectedString )
830 {
831 JOBSET_DESTINATION* destination = m_jobsFile->AddNewDestination( destinationType );
832
833 DIALOG_DESTINATION dialog( m_frame, m_jobsFile.get(), destination );
834
835 if (dialog.ShowModal() == wxID_OK)
836 {
837 Freeze();
838 addDestinationPanel( destination );
839 Thaw();
840 }
841 else
842 {
843 // canceled
844 m_jobsFile->RemoveDestination( destination );
845 }
846 }
847 }
848 }
849}
850
851
853{
854 if( m_jobsFile->GetDirty() )
855 {
856 wxFileName fileName = m_jobsFile->GetFullFilename();
857 wxString msg = _( "Save changes to '%s' before closing?" );
858
859 if( !HandleUnsavedChanges( this, wxString::Format( msg, fileName.GetFullName() ),
860 [&]() -> bool
861 {
862 return m_jobsFile->SaveToFile( wxEmptyString, true );
863 } ) )
864 {
865 return false;
866 }
867 }
868
869 return true;
870}
871
872
874{
875 PROJECT& project = m_frame->Kiway().Prj();
876 KIWAY_PLAYER* frame = m_frame->Kiway().Player( FRAME_PCB_EDITOR, false );
877
878 if( !frame )
879 {
880 frame = m_frame->Kiway().Player( FRAME_PCB_EDITOR, true );
881
882 // frame can be null if Cvpcb cannot be run. No need to show a warning
883 // Kiway() generates the error messages
884 if( !frame )
885 return;
886
887 wxFileName boardfn = project.GetProjectFullName();
888 boardfn.SetExt( FILEEXT::PcbFileExtension );
889
890 // Prevent our window from being closed during the open process
891 wxEventBlocker blocker( this );
892
893 frame->OpenProjectFiles( std::vector<wxString>( 1, boardfn.GetFullPath() ) );
894
895 if( !frame->IsVisible() )
896 frame->Show( true );
897 }
898
899 frame = m_frame->Kiway().Player( FRAME_SCH, false );
900
901 if( !frame )
902 {
903 frame = m_frame->Kiway().Player( FRAME_SCH, true );
904
905 // frame can be null if Cvpcb cannot be run. No need to show a warning
906 // Kiway() generates the error messages
907 if( !frame )
908 return;
909
910 wxFileName schFn = project.GetProjectFullName();
912
913 wxEventBlocker blocker( this );
914
915 frame->OpenProjectFiles( std::vector<wxString>( 1, schFn.GetFullPath() ) );
916
917 if( !frame->IsVisible() )
918 frame->Show( true );
919 }
920
921 SetFocus();
922}
923
924
926{
927 return m_jobsFile->GetFullFilename();
928}
929
930
931void PANEL_JOBSET::OnJobButtonUp( wxCommandEvent& aEvent )
932{
933 if( !m_jobsGrid->CommitPendingChanges() )
934 return;
935
936 int item = m_jobsGrid->GetGridCursorRow();
937
938 if( item > 0 )
939 {
940 m_jobsFile->MoveJobUp( item );
941
943
944 m_jobsGrid->SelectRow( item - 1 );
945 m_jobsGrid->SetGridCursor( item - 1, m_jobsGrid->GetGridCursorCol() );
946 }
947 else
948 {
949 wxBell();
950 }
951}
952
953
954void PANEL_JOBSET::OnJobButtonDown( wxCommandEvent& aEvent )
955{
956 if( !m_jobsGrid->CommitPendingChanges() )
957 return;
958
959 int item = m_jobsGrid->GetGridCursorRow();
960
961 if( item < m_jobsGrid->GetNumberRows() - 1 )
962 {
963 m_jobsFile->MoveJobDown( item );
964
966
967 m_jobsGrid->SelectRow( item + 1 );
968 m_jobsGrid->SetGridCursor( item + 1, m_jobsGrid->GetGridCursorCol() );
969 }
970 else
971 {
972 wxBell();
973 }
974}
975
976
978{
979 if( !m_jobsGrid->CommitPendingChanges() )
980 return;
981
982 // sanity
983 if( m_jobsFile->GetDestinations().empty() )
984 {
985 DisplayError( this, _( "No destinations defined" ) );
986 return;
987 }
988
990 panel->ClearStatus();
991
992 Refresh();
993
994 CallAfter(
995 [this]()
996 {
997 PROJECT& project = m_frame->Kiway().Prj();
999
1000 wxFileName fn = project.GetProjectFullName();
1001 wxSetWorkingDirectory( fn.GetPath() );
1002
1003 {
1004 JOBS_PROGRESS_REPORTER progressReporter( m_frame, _( "Running Jobs" ) );
1005 JOBS_RUNNER jobRunner( &m_frame->Kiway(), m_jobsFile.get(), &project,
1006 NULL_REPORTER::GetInstance(), &progressReporter );
1007
1008 jobRunner.RunJobsAllDestinations();
1009
1010 for( PANEL_DESTINATION* panel : GetDestinationPanels() )
1011 panel->UpdateStatus();
1012 }
1013
1014 // Bring the Kicad manager frame back to the front
1015 m_frame->Raise();
1016 } );
1017}
1018
1019
1020void PANEL_JOBSET::OnSizeGrid( wxSizeEvent& aEvent )
1021{
1022 m_jobsGrid->SetColSize( COL_DESCR, m_jobsGrid->GetSize().x - m_jobsGrid->GetColSize( COL_SOURCE )
1023 - m_jobsGrid->GetColSize( COL_NUMBER ) );
1024
1025 // Always propagate for a grid repaint (needed if the height changes, as well as width)
1026 aEvent.Skip();
1027}
1028
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:557
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition common.cpp:61
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.