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