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,
58 JOBSET_DESTINATION* aDestination ) :
60 m_jobsFile( aJobsFile ),
61 m_destination( aDestination ),
62 m_lastWidth( -1 ),
63 m_marginsWidth( -1 )
64 {
65 m_staticTextOutputName->SetLabel( wxString::Format( _( "Destination: %s" ),
66 aDestination->GetDescription() ) );
67
68 int jobBmpColId = m_jobList->AppendColumn( wxT( "" ) );
69 int jobNoColId = m_jobList->AppendColumn( _( "No." ) );
70 int jobDescColId = m_jobList->AppendColumn( _( "Job Description" ) );
71 int jobSourceColId = m_jobList->AppendColumn( _( "Source" ) );
72 m_jobList->SetColumnWidth( jobBmpColId, 26 );
73 m_jobList->SetColumnWidth( jobNoColId, GetTextExtent( wxT( "XXXX" ) ).GetWidth() );
74 m_jobList->SetColumnWidth( jobSourceColId, GetTextExtent( wxT( "XXXXXX" ) ).GetWidth() );
75
76 wxImageList* imageList = new wxImageList( 16, 16, true, 3 );
77 imageList->Add( KiBitmapBundle( BITMAPS::ercerr ).GetBitmap( wxSize( 16, 16 ) ) );
78 imageList->Add( KiBitmapBundle( BITMAPS::checked_ok ).GetBitmap( wxSize( 16, 16 ) ) );
79 m_jobList->SetImageList( imageList, wxIMAGE_LIST_SMALL );
80
81 int num = 1;
82 for( auto& 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 {
152 WX_STRING_REPORTER* reporter =
153 static_cast<WX_STRING_REPORTER*>( m_destination->m_lastRunReporters[jobId] );
154
155 if( reporter )
156 m_textCtrlOutput->SetValue( reporter->GetMessages() );
157 }
158 else
159 {
160 m_textCtrlOutput->SetValue( _( "No output messages" ) );
161 }
162 }
163 }
164
165private:
168
171};
172
173
175{
176public:
177 PANEL_DESTINATION( wxWindow* aParent, PANEL_JOBSET* aPanelParent, KICAD_MANAGER_FRAME* aFrame,
178 JOBSET* aFile, JOBSET_DESTINATION* aDestination ) :
179 PANEL_DESTINATION_BASE( aParent ),
180 m_jobsFile( aFile ),
181 m_destinationId( aDestination->m_id ),
182 m_frame( aFrame ),
183 m_panelParent( aPanelParent )
184 {
185 m_buttonProperties->SetBitmap( KiBitmapBundle( BITMAPS::config ) );
186 m_buttonDelete->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
187
188#if _WIN32
189 // BORDER_RAISED/SUNKEN look pretty on every platform but Windows
190 long style = GetWindowStyleFlag();
191 style &= ~wxBORDER_MASK;
192 style |= wxBORDER_SIMPLE;
193 SetWindowStyleFlag( style );
194#endif // _WIN32
195
196 Connect( wxEVT_MENU, wxCommandEventHandler( PANEL_DESTINATION::onMenu ), nullptr, this );
197
198 if( JobsetDestinationTypeInfos.contains( aDestination->m_type ) )
199 {
200 JOBSET_DESTINATION_T_INFO& jobTypeInfo = JobsetDestinationTypeInfos[aDestination->m_type];
201 m_textOutputType->SetLabel( aDestination->GetDescription() );
202 m_bitmapOutputType->SetBitmap( KiBitmapBundle( jobTypeInfo.bitmap ) );
203 }
204
205 m_pathInfo->SetFont( KIUI::GetSmallInfoFont( this ) );
206 UpdatePathInfo( aDestination->GetPathInfo() );
207 UpdateStatus();
208 }
209
211 {
212 Disconnect( wxEVT_MENU, wxCommandEventHandler( PANEL_DESTINATION::onMenu ), nullptr, this );
213 }
214
216 {
217 JOBSET_DESTINATION* destination = GetDestination();
218 wxCHECK( destination, /*void*/ );
219
220 destination->m_lastRunSuccess = std::nullopt;
221 m_statusBitmap->SetBitmap( wxNullBitmap );
222 }
223
225 {
226 JOBSET_DESTINATION* destination = GetDestination();
227 wxCHECK( destination, /*void*/ );
228
229 if( destination->m_lastRunSuccess.has_value() )
230 {
231 if( destination->m_lastRunSuccess.value() )
232 {
233 m_statusBitmap->SetBitmap( KiBitmapBundle( BITMAPS::checked_ok ) );
234 m_statusBitmap->Show();
235 m_statusBitmap->SetToolTip( _( "Last run successful" ) );
236 }
237 else
238 {
239 m_statusBitmap->SetBitmap( KiBitmapBundle( BITMAPS::ercerr ) );
240 m_statusBitmap->Show();
241 m_statusBitmap->SetToolTip( _( "Last run failed" ) );
242 }
243 }
244 else
245 {
246 m_statusBitmap->SetBitmap( wxNullBitmap );
247 }
248
249 m_buttonGenerate->Enable( !m_jobsFile->GetJobsForDestination( destination ).empty() );
250 }
251
252 void UpdatePathInfo( const wxString& aMsg )
253 {
254 wxClientDC dc( this );
255 int width = GetSize().GetWidth();
256 wxString msg = aMsg;
257
258 m_pathInfo->SetLabel( wxControl::Ellipsize( msg, dc, wxELLIPSIZE_MIDDLE, width ) );
259 }
260
261 virtual void OnGenerate( wxCommandEvent& event ) override
262 {
263 ClearStatus();
264 Refresh();
265
266 CallAfter(
267 [this]()
268 {
271
272 wxFileName fn = project.GetProjectFullName();
273 wxSetWorkingDirectory( fn.GetPath() );
274
275 JOBS_RUNNER jobRunner( &( m_frame->Kiway() ), m_jobsFile, &project );
276
277 auto* progressReporter = new WX_PROGRESS_REPORTER( m_frame, _( "Running jobs" ),
278 1 );
279
280 if( JOBSET_DESTINATION* destination = GetDestination() )
281 jobRunner.RunJobsForDestination( destination );
282
283 UpdateStatus();
284
285 delete progressReporter;
286
287 // Bring the Kicad manager frame back to the front
288 m_frame->Raise();
289 } );
290 }
291
292 virtual void OnLastStatusClick( wxMouseEvent& aEvent ) override
293 {
294 JOBSET_DESTINATION* destination = GetDestination();
295 wxCHECK( destination, /*void*/ );
296
297 DIALOG_JOBSET_RUN_LOG dialog( m_frame, m_jobsFile, destination );
298 dialog.ShowModal();
299 }
300
301 void OnRightDown( wxMouseEvent& aEvent ) override
302 {
303 JOBSET_DESTINATION* destination = GetDestination();
304 wxCHECK( destination, /*void*/ );
305
306 wxMenu menu;
307 menu.Append( wxID_EDIT, _( "Edit Destination Options..." ) );
308 menu.Append( wxID_DELETE, _( "Delete Destination" ) );
309
310 menu.AppendSeparator();
311 menu.Append( wxID_VIEW_DETAILS, _( "View Last Run Log..." ) );
312
313 menu.Enable( wxID_VIEW_DETAILS, destination->m_lastRunSuccess.has_value() );
314
315 PopupMenu( &menu );
316 }
317
318 void OnProperties( wxCommandEvent& aEvent ) override
319 {
320 JOBSET_DESTINATION* destination = GetDestination();
321 wxCHECK( destination, /*void*/ );
322
323 DIALOG_DESTINATION dialog( m_frame, m_jobsFile, destination );
324
325 if( dialog.ShowModal() == wxID_OK )
326 {
327 m_textOutputType->SetLabel( destination->GetDescription() );
328 UpdatePathInfo( destination->GetPathInfo() );
331 }
332 }
333
334 virtual void OnDelete( wxCommandEvent& aEvent ) override
335 {
337 }
338
340 {
341 for( JOBSET_DESTINATION& destination : m_jobsFile->GetDestinations() )
342 {
343 if( destination.m_id == m_destinationId )
344 return &destination;
345 }
346
347 return nullptr;
348 }
349
350private:
351 void onMenu( wxCommandEvent& aEvent )
352 {
353 switch( aEvent.GetId() )
354 {
355 case wxID_EDIT:
356 {
357 wxCommandEvent dummy;
359 }
360 break;
361
362 case wxID_DELETE:
363 {
364 wxCommandEvent dummy;
365 OnDelete( dummy );
366 }
367 break;
368
369 case wxID_VIEW_DETAILS:
370 {
371 wxMouseEvent dummy;
373 }
374 break;
375
376 default:
377 wxFAIL_MSG( wxT( "Unknown ID in context menu event" ) );
378 }
379 }
380
381private:
386};
387
388
390 GRID_TRICKS( aGrid ),
391 m_parent( aParent )
392{
395}
396
397
398void JOBS_GRID_TRICKS::showPopupMenu( wxMenu& menu, wxGridEvent& aEvent )
399{
400 wxArrayInt selectedRows = m_grid->GetSelectedRows();
401
402 menu.Append( JOB_DESCRIPTION, _( "Edit Job Description" ) );
403 menu.Append( JOB_PROPERTIES, _( "Edit Job Settings..." ) );
404 menu.AppendSeparator();
405 menu.Append( GRIDTRICKS_ID_COPY, _( "Copy" ) + "\tCtrl+C", _( "Copy selected cells to clipboard" ) );
406 menu.Append( GRIDTRICKS_ID_DELETE, _( "Delete" ) + "\tDel", _( "Delete selected jobs" ) );
407 menu.Append( GRIDTRICKS_ID_SELECT, _( "Select All" ) + "\tCtrl+A", _( "Select all jobs" ) );
408
409 menu.Enable( JOB_DESCRIPTION, selectedRows.size() == 1 );
410 menu.Enable( JOB_PROPERTIES, selectedRows.size() == 1 );
411 menu.Enable( GRIDTRICKS_ID_DELETE, selectedRows.size() > 0 );
412
413 m_grid->PopupMenu( &menu );
414}
415
416
417void JOBS_GRID_TRICKS::doPopupSelection( wxCommandEvent& event )
418{
419 wxArrayInt selectedRows = m_grid->GetSelectedRows();
420
421 if( event.GetId() == JOB_DESCRIPTION )
422 {
423 if( selectedRows.size() > 0 )
424 {
425 m_grid->SetGridCursor( selectedRows[0], 2 );
426 m_grid->EnableCellEditControl();
427 }
428 }
429 else if( event.GetId() == JOB_PROPERTIES )
430 {
431 if( selectedRows.size() > 0 )
432 m_parent->OpenJobOptionsForListItem( selectedRows[0] );
433 }
434 else if( event.GetId() == GRIDTRICKS_ID_DELETE )
435 {
436 wxCommandEvent dummy;
438 }
439 else
440 {
442 }
443}
444
445
446bool JOBS_GRID_TRICKS::handleDoubleClick( wxGridEvent& aEvent )
447{
449
450 int curr_col = aEvent.GetCol();
451 int curr_row = aEvent.GetRow();
452
453 if( ( curr_col == COL_NUMBER || curr_col == COL_SOURCE || curr_col == COL_DESCR )
454 && curr_row >= 0 && curr_row < (int) m_parent->GetJobsFile()->GetJobs().size() )
455 {
456 m_doubleClickRow = curr_row;
458
459 CallAfter(
460 [this]()
461 {
462 // Yes, again. CancelShowEditorOnMouseUp() doesn't appear to be 100%
463 // reliable.
465 int row = m_doubleClickRow;
466 m_doubleClickRow = -1;
467
468 if( row >= 0 && row < (int) m_parent->GetJobsFile()->GetJobs().size() )
470 } );
471
472 return true;
473 }
474
475 return false;
476}
477
478
479PANEL_JOBSET::PANEL_JOBSET( wxAuiNotebook* aParent, KICAD_MANAGER_FRAME* aFrame,
480 std::unique_ptr<JOBSET> aJobsFile ) :
481 PANEL_JOBSET_BASE( aParent ),
482 m_parentBook( aParent ),
483 m_frame( aFrame ),
484 m_jobsFile( std::move( aJobsFile ) )
485{
486 m_jobsGrid->PushEventHandler( new JOBS_GRID_TRICKS( this, m_jobsGrid ) );
487
488 m_jobsGrid->SetDefaultRowSize( m_jobsGrid->GetDefaultRowSize() + 4 );
489 m_jobsGrid->OverrideMinSize( 0.6, 0.3 );
490 m_jobsGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
491
492 // 'm' for margins
493 m_jobsGrid->SetColSize( COL_NUMBER, GetTextExtent( wxT( "99m" ) ).x );
494 m_jobsGrid->SetColSize( COL_SOURCE, GetTextExtent( wxT( "PCBm" ) ).x );
495
496 m_buttonAddJob->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
497 m_buttonUp->SetBitmap( KiBitmapBundle( BITMAPS::small_up ) );
498 m_buttonDown->SetBitmap( KiBitmapBundle( BITMAPS::small_down ) );
499 m_buttonDelete->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
500 m_buttonAddDestination->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
501
504}
505
506
508{
509 // Delete the GRID_TRICKS.
510 m_jobsGrid->PopEventHandler( true );
511}
512
513
515{
516 JOBSET_DESTINATION* output = aPanel->GetDestination();
517
518 m_destinationListSizer->Detach( aPanel );
519 aPanel->Destroy();
520
521 // ensure the window contents get shifted as needed
522 m_destinationList->Layout();
523 Layout();
524
525 m_jobsFile->RemoveDestination( output );
526}
527
528
530{
531 if( m_jobsGrid->GetNumberRows() )
532 m_jobsGrid->DeleteRows( 0, m_jobsGrid->GetNumberRows() );
533
534 m_jobsGrid->AppendRows( (int) m_jobsFile->GetJobs().size() );
535
536 int num = 1;
537
538 for( JOBSET_JOB& job : m_jobsFile->GetJobs() )
539 {
540 m_jobsGrid->SetCellValue( num - 1, COL_NUMBER, wxString::Format( "%d", num ) );
541 m_jobsGrid->SetReadOnly( num - 1, COL_NUMBER );
542
543 m_jobsGrid->SetCellValue( num - 1, COL_DESCR, job.GetDescription() );
544
545 m_jobsGrid->SetReadOnly( num - 1, COL_SOURCE );
546
547 KIWAY::FACE_T iface = JOB_REGISTRY::GetKifaceType( job.m_type );
548 wxString source = wxEmptyString;
549
550 if( iface < KIWAY::KIWAY_FACE_COUNT )
551 {
552 if( iface == KIWAY::FACE_PCB )
553 source = wxT( "PCB" );
554 else if( iface == KIWAY::FACE_SCH )
555 source = wxT( "SCH" );
556 }
557
558 m_jobsGrid->SetCellValue( num - 1, COL_SOURCE, source );
559
560 num++;
561 }
562
563 UpdateTitle();
564
565 // Ensure the outputs get their Run-ability status updated
567 panel->UpdateStatus();
568}
569
570
572{
573 wxString tabName = m_jobsFile->GetFullName();
574
575 if( m_jobsFile->GetDirty() )
576 tabName = wxS( "*" ) + tabName;
577
578 int pageIdx = m_parentBook->FindPage( this );
579 m_parentBook->SetPageText( pageIdx, tabName );
580}
581
582
584{
585 PANEL_DESTINATION* destinationPanel = new PANEL_DESTINATION( m_destinationList, this, m_frame,
586 m_jobsFile.get(), aOutput );
587
588#if __OSX__
589 m_outputListSizer->Add( destinationPanel, 0, wxEXPAND, 5 );
590#else
591 m_destinationListSizer->Add( destinationPanel, 0, wxEXPAND|wxLEFT|wxRIGHT|wxBOTTOM, 5 );
592#endif
593
594 m_destinationList->Layout();
595}
596
597
598std::vector<PANEL_DESTINATION*> PANEL_JOBSET::GetDestinationPanels()
599{
600 std::vector<PANEL_DESTINATION*> panels;
601
602 for( const wxSizerItem* item : m_destinationListSizer->GetChildren() )
603 {
604 if( PANEL_DESTINATION* panel = dynamic_cast<PANEL_DESTINATION*>( item->GetWindow() ) )
605 panels.push_back( panel );
606 }
607
608 return panels;
609}
610
611
613{
614 Freeze();
615
616 for( JOBSET_DESTINATION& job : m_jobsFile->GetDestinations() )
617 addDestinationPanel( &job );
618
619 // ensure the window contents get shifted as needed
620 Layout();
621 Thaw();
622}
623
624
626{
627 bool success = false;
628 JOBSET_JOB& job = m_jobsFile->GetJobs()[aItemIndex];
630
631 if( iface < KIWAY::KIWAY_FACE_COUNT )
632 {
634 success = m_frame->Kiway().ProcessJobConfigDialog( iface, job.m_job.get(), m_frame );
635 }
636 else
637 {
638 // special jobs
639 if( job.m_job->GetType() == "special_execute" )
640 {
641 JOB_SPECIAL_EXECUTE* specialJob = static_cast<JOB_SPECIAL_EXECUTE*>( job.m_job.get() );
642
643 DIALOG_EXECUTECOMMAND_JOB_SETTINGS dialog( m_frame, specialJob );
644
645 // QuasiModal for Scintilla autocomplete
646 if( dialog.ShowQuasiModal() == wxID_OK )
647 success = true;
648 }
649 else if( job.m_job->GetType() == "special_copyfiles" )
650 {
651 JOB_SPECIAL_COPYFILES* specialJob = static_cast<JOB_SPECIAL_COPYFILES*>( job.m_job.get() );
652 DIALOG_COPYFILES_JOB_SETTINGS dialog( m_frame, specialJob );
653
654 if( dialog.ShowModal() == wxID_OK )
655 success = true;
656 }
657 }
658
659 if( success )
660 {
661 m_jobsFile->SetDirty();
662 UpdateTitle();
663 }
664
665 // Bring the Kicad manager frame back to the front
666 m_frame->Raise();
667
668 return success;
669}
670
671
672void PANEL_JOBSET::OnGridCellChange( wxGridEvent& aEvent )
673{
674 int row = aEvent.GetRow();
675 int col = aEvent.GetCol();
676
677 if( col == COL_DESCR )
678 m_jobsFile->GetJobs()[row].SetDescription( m_jobsGrid->GetCellValue( row, col ) );
679}
680
681
682void PANEL_JOBSET::OnSaveButtonClick( wxCommandEvent& aEvent )
683{
685 return;
686
687 m_jobsFile->SaveToFile( wxEmptyString, true );
688 UpdateTitle();
689}
690
691
692void PANEL_JOBSET::OnAddJobClick( wxCommandEvent& aEvent )
693{
695 return;
696
697 wxArrayString headers;
698 std::vector<wxArrayString> items;
699
700 headers.Add( _( "Job Types" ) );
701
703
704 for( const std::pair<const wxString, JOB_REGISTRY_ENTRY>& entry : jobMap )
705 {
706 if( entry.second.deprecated )
707 continue;
708
709 wxArrayString item;
710 item.Add( wxGetTranslation( entry.second.title ) );
711 items.emplace_back( item );
712 }
713
714 EDA_LIST_DIALOG dlg( this, _( "Add New Job" ), headers, items );
715 dlg.SetListLabel( _( "Select job type:" ) );
716
717 if( dlg.ShowModal() == wxID_OK )
718 {
719 wxString selectedString = dlg.GetTextSelection();
720
721 wxString jobKey;
722
723 if( !selectedString.IsEmpty() )
724 {
725 for( const std::pair<const wxString, JOB_REGISTRY_ENTRY>& entry : jobMap )
726 {
727 if( entry.second.deprecated )
728 continue;
729
730 if( wxGetTranslation( entry.second.title ) == selectedString )
731 {
732 jobKey = entry.first;
733 break;
734 }
735 }
736 }
737
738 if( !jobKey.IsEmpty() )
739 {
740 int row = m_jobsFile->GetJobs().size();
741 bool wasDirty = m_jobsFile->GetDirty();
742 JOB* job = JOB_REGISTRY::CreateInstance<JOB>( jobKey );
743
744 m_jobsFile->AddNewJob( jobKey, job );
745
746 if( OpenJobOptionsForListItem( row ) )
747 {
749
750 m_jobsGrid->SetGridCursor( row, 2 );
751 m_jobsGrid->EnableCellEditControl();
752 }
753 else
754 {
755 m_jobsFile->RemoveJob( row );
756 m_jobsFile->SetDirty( wasDirty );
757 }
758 }
759 }
760}
761
762
763void PANEL_JOBSET::OnJobButtonDelete( wxCommandEvent& aEvent )
764{
766 return;
767
768 wxArrayInt selectedRows = m_jobsGrid->GetSelectedRows();
769
770 if( selectedRows.empty() )
771 return;
772
773 m_jobsGrid->CommitPendingChanges( true /* quiet mode */ );
774 m_jobsGrid->ClearSelection();
775
776 // Reverse sort so deleting a row doesn't change the indexes of the other rows.
777 selectedRows.Sort( []( int* first, int* second ) { return *second - *first; } );
778
779 int select = selectedRows[0];
780
781 for( int row : selectedRows )
782 m_jobsFile->RemoveJob( row );
783
785
786 if( m_jobsGrid->GetNumberRows() )
787 {
788 m_jobsGrid->MakeCellVisible( std::max( 0, select-1 ), m_jobsGrid->GetGridCursorCol() );
789 m_jobsGrid->SetGridCursor( std::max( 0, select-1 ), m_jobsGrid->GetGridCursorCol() );
790 }
791}
792
793
794void PANEL_JOBSET::OnAddDestinationClick( wxCommandEvent& aEvent )
795{
796 wxArrayString headers;
797 std::vector<wxArrayString> items;
798
799 headers.Add( _( "Destination Types" ) );
800
801 for( const auto& [destinationType, destinationTypeInfo] : JobsetDestinationTypeInfos )
802 {
803 wxArrayString item;
804 item.Add( wxGetTranslation( destinationTypeInfo.name ) );
805 items.emplace_back( item );
806 }
807
808 EDA_LIST_DIALOG dlg( this, _( "Add New Destination" ), headers, items );
809 dlg.SetListLabel( _( "Select destination type:" ) );
810 dlg.HideFilter();
811
812 if( dlg.ShowModal() == wxID_OK )
813 {
814 wxString selectedString = dlg.GetTextSelection();
815
816 for( const auto& [destinationType, destinationTypeInfo] : JobsetDestinationTypeInfos )
817 {
818 if( wxGetTranslation( destinationTypeInfo.name ) == selectedString )
819 {
820 JOBSET_DESTINATION* destination = m_jobsFile->AddNewDestination( destinationType );
821
822 DIALOG_DESTINATION dialog( m_frame, m_jobsFile.get(), destination );
823
824 if (dialog.ShowModal() == wxID_OK)
825 {
826 Freeze();
827 addDestinationPanel( destination );
828 Thaw();
829 }
830 else
831 {
832 // canceled
833 m_jobsFile->RemoveDestination( destination );
834 }
835 }
836 }
837 }
838}
839
840
842{
843 if( m_jobsFile->GetDirty() )
844 {
845 wxFileName fileName = m_jobsFile->GetFullFilename();
846 wxString msg = _( "Save changes to '%s' before closing?" );
847
848 if( !HandleUnsavedChanges( this, wxString::Format( msg, fileName.GetFullName() ),
849 [&]() -> bool
850 {
851 return m_jobsFile->SaveToFile(wxEmptyString, true);
852 } ) )
853 {
854 return false;
855 }
856 }
857
858 return true;
859}
860
861
863{
865 KIWAY_PLAYER* frame = m_frame->Kiway().Player( FRAME_PCB_EDITOR, false );
866
867 if( !frame )
868 {
869 frame = m_frame->Kiway().Player( FRAME_PCB_EDITOR, true );
870
871 // frame can be null if Cvpcb cannot be run. No need to show a warning
872 // Kiway() generates the error messages
873 if( !frame )
874 return;
875
876 wxFileName boardfn = project.GetProjectFullName();
877 boardfn.SetExt( FILEEXT::PcbFileExtension );
878
879 // Prevent our window from being closed during the open process
880 wxEventBlocker blocker( this );
881
882 frame->OpenProjectFiles( std::vector<wxString>( 1, boardfn.GetFullPath() ) );
883
884 if( !frame->IsVisible() )
885 frame->Show( true );
886 }
887
888 frame = m_frame->Kiway().Player( FRAME_SCH, false );
889
890 if( !frame )
891 {
892 frame = m_frame->Kiway().Player( FRAME_SCH, true );
893
894 // frame can be null if Cvpcb cannot be run. No need to show a warning
895 // Kiway() generates the error messages
896 if( !frame )
897 return;
898
899 wxFileName schFn = project.GetProjectFullName();
901
902 wxEventBlocker blocker( this );
903
904 frame->OpenProjectFiles( std::vector<wxString>( 1, schFn.GetFullPath() ) );
905
906 if( !frame->IsVisible() )
907 frame->Show( true );
908 }
909
910 SetFocus();
911}
912
913
915{
916 return m_jobsFile->GetFullFilename();
917}
918
919
920void PANEL_JOBSET::OnJobButtonUp( wxCommandEvent& aEvent )
921{
923 return;
924
925 int item = m_jobsGrid->GetGridCursorRow();
926
927 if( item > 0 )
928 {
929 m_jobsFile->MoveJobUp( item );
930
932
933 m_jobsGrid->SelectRow( item - 1 );
934 m_jobsGrid->SetGridCursor( item - 1, m_jobsGrid->GetGridCursorCol() );
935 }
936 else
937 {
938 wxBell();
939 }
940}
941
942
943void PANEL_JOBSET::OnJobButtonDown( wxCommandEvent& aEvent )
944{
946 return;
947
948 int item = m_jobsGrid->GetGridCursorRow();
949
950 if( item < m_jobsGrid->GetNumberRows() - 1 )
951 {
952 m_jobsFile->MoveJobDown( item );
953
955
956 m_jobsGrid->SelectRow( item + 1 );
957 m_jobsGrid->SetGridCursor( item + 1, m_jobsGrid->GetGridCursorCol() );
958 }
959 else
960 {
961 wxBell();
962 }
963}
964
965
967{
969 return;
970
971 // sanity
972 if( m_jobsFile->GetDestinations().empty() )
973 {
974 DisplayError( this, _( "No destinations defined" ) );
975 return;
976 }
977
979 panel->ClearStatus();
980
981 Refresh();
982
983 CallAfter(
984 [this]()
985 {
988
989 wxFileName fn = project.GetProjectFullName();
990 wxSetWorkingDirectory( fn.GetPath() );
991
992 JOBS_RUNNER jobRunner( &( m_frame->Kiway() ), m_jobsFile.get(), &project );
993
994 WX_PROGRESS_REPORTER* progressReporter =
995 new WX_PROGRESS_REPORTER( m_frame, _( "Running jobs" ), 1 );
996
997 jobRunner.RunJobsAllDestinations();
998
1000 panel->UpdateStatus();
1001
1002 delete progressReporter;
1003
1004 // Bring the Kicad manager frame back to the front
1005 m_frame->Raise();
1006 } );
1007}
1008
1009
1010void PANEL_JOBSET::OnSizeGrid( wxSizeEvent& aEvent )
1011{
1012 m_jobsGrid->SetColSize( COL_DESCR, m_jobsGrid->GetSize().x
1013 - m_jobsGrid->GetColSize( COL_SOURCE )
1014 - m_jobsGrid->GetColSize( COL_NUMBER ) );
1015
1016 // Always propagate for a grid repaint (needed if the height changes, as well as width)
1017 aEvent.Skip();
1018}
1019
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition: bitmap.cpp:110
Class DIALOG_JOBSET_RUN_LOG_BASE.
wxStaticText * m_staticTextOutputName
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={})
int ShowQuasiModal()
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)
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition: grid_tricks.h:61
bool m_multiCellEditEnabled
Definition: grid_tricks.h:139
bool m_enableSingleClickEdit
Definition: grid_tricks.h:138
virtual void doPopupSelection(wxCommandEvent &event)
WX_GRID * m_grid
I don't own the grid, but he owns me.
Definition: grid_tricks.h:125
Definition: jobset.h:108
std::vector< JOBSET_DESTINATION > & GetDestinations()
Definition: jobset.h:121
void SetDirty(bool aFlag=true)
Definition: jobset.h:127
std::vector< JOBSET_JOB > GetJobsForDestination(JOBSET_DESTINATION *aDestination)
Definition: jobset.cpp:307
std::vector< JOBSET_JOB > & GetJobs()
Definition: jobset.h:114
PANEL_JOBSET * m_parent
Definition: panel_jobset.h:63
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)
Definition: jobs_runner.cpp:49
bool RunJobsForDestination(JOBSET_DESTINATION *aDestination, bool aBail=false)
static const REGISTRY_MAP_T & GetRegistry()
Definition: job_registry.h:55
std::unordered_map< wxString, JOB_REGISTRY_ENTRY > REGISTRY_MAP_T
Definition: job_registry.h:38
static KIWAY::FACE_T GetKifaceType(const wxString &aName)
An simple container class that lets us dispatch output jobs to kifaces.
Definition: job.h:183
The main KiCad project manager frame.
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
Definition: kiway_holder.h:55
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
Definition: kiway_player.h:65
virtual bool OpenProjectFiles(const std::vector< wxString > &aFileList, int aCtl=0)
Open a project or set of files given by aFileList.
Definition: kiway_player.h:113
bool ProcessJobConfigDialog(KIWAY::FACE_T aFace, JOB *aJob, wxWindow *aWindow)
Definition: kiway.cpp:719
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:406
FACE_T
Known KIFACE implementations.
Definition: kiway.h:291
@ KIWAY_FACE_COUNT
Definition: kiway.h:301
@ FACE_SCH
eeschema DSO
Definition: kiway.h:292
@ FACE_PCB
pcbnew DSO
Definition: kiway.h:293
virtual PROJECT & Prj() const
Return the PROJECT associated with this KIWAY.
Definition: kiway.cpp:195
Class PANEL_DESTINATION_BASE.
wxStaticBitmap * m_statusBitmap
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)
wxString m_destinationId
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
Class PANEL_JOBSET_BASE.
STD_BITMAP_BUTTON * m_buttonAddDestination
STD_BITMAP_BUTTON * m_buttonDown
wxScrolledWindow * m_destinationList
wxBoxSizer * m_destinationListSizer
STD_BITMAP_BUTTON * m_buttonAddJob
STD_BITMAP_BUTTON * m_buttonDelete
STD_BITMAP_BUTTON * m_buttonUp
wxString GetFilePath() const
JOBSET * GetJobsFile()
Definition: panel_jobset.h:83
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
Definition: panel_jobset.h:108
void addDestinationPanel(JOBSET_DESTINATION *aDestination)
void UpdateTitle()
virtual void OnGenerateAllDestinationsClick(wxCommandEvent &event) override
bool OpenJobOptionsForListItem(size_t aItemIndex)
std::vector< PANEL_DESTINATION * > GetDestinationPanels()
void buildDestinationList()
KICAD_MANAGER_FRAME * m_frame
Definition: panel_jobset.h:109
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
Definition: panel_jobset.h:110
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:64
void SetBitmap(const wxBitmapBundle &aBmp)
void OverrideMinSize(double aXPct, double aYPct)
Grids that have column sizes automatically set to fill the available width don't want to shrink after...
Definition: wx_grid.h:225
bool CancelPendingChanges()
Definition: wx_grid.cpp:621
void CancelShowEditorOnMouseUp()
Definition: wx_grid.h:196
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:649
Multi-thread safe progress reporter dialog, intended for use of tasks that parallel reporting back of...
A wrapper for reporting to a wxString object.
Definition: reporter.h:190
const wxString & GetMessages() const
Definition: reporter.cpp:83
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:130
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:170
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:39
PROJECT & Prj()
Definition: kicad.cpp:597
#define KICOMMON_API
Definition: kicommon.h:28
KICOMMON_API wxFont GetSmallInfoFont(wxWindow *aWindow)
Definition: ui_common.cpp:162
STL namespace.
KICOMMON_API std::map< JOBSET_DESTINATION_T, JOBSET_DESTINATION_T_INFO > JobsetDestinationTypeInfos
Definition: jobset.cpp:39
@ COL_NUMBER
Definition: panel_jobset.h:36
@ COL_DESCR
Definition: panel_jobset.h:38
@ COL_SOURCE
Definition: panel_jobset.h:37
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
std::unordered_map< wxString, REPORTER * > m_lastRunReporters
Definition: jobset.h:100
JOBSET_DESTINATION_T m_type
Definition: jobset.h:87
wxString GetPathInfo() const
Transient property, not stored for now.
Definition: jobset.cpp:165
wxString GetDescription() const
Definition: jobset.cpp:159
std::optional< bool > m_lastRunSuccess
Definition: jobset.h:98
std::unordered_map< wxString, std::optional< bool > > m_lastRunSuccessMap
Definition: jobset.h:99
std::shared_ptr< JOB > m_job
Definition: jobset.h:49
wxString m_type
Definition: jobset.h:47
Definition of file extensions used in Kicad.