KiCad PCB EDA Suite
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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 <mark.roszko@gmail.com>
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
39#include <widgets/wx_grid.h>
41#include <kiplatform/ui.h>
42#include <confirm.h>
43
47
48
49extern KICOMMON_API
50std::map<JOBSET_DESTINATION_T, JOBSET_DESTINATION_T_INFO> JobsetDestinationTypeInfos;
51
52
54{
55public:
56 DIALOG_JOBSET_RUN_LOG( wxWindow* aParent, JOBSET* aJobsFile,
57 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 for( auto& job : aJobsFile->GetJobsForDestination( aDestination ) )
82 {
83 int imageIdx = -1;
84
85 if( aDestination->m_lastRunSuccessMap.contains( job.m_id ) )
86 {
87 if( aDestination->m_lastRunSuccessMap[job.m_id].value() )
88 imageIdx = 1;
89 else
90 imageIdx = 0;
91 }
92
93 long itemIndex = m_jobList->InsertItem( m_jobList->GetItemCount(), imageIdx );
94
95 m_jobList->SetItem( itemIndex, jobNoColId, wxString::Format( "%d", num++ ) );
96 m_jobList->SetItem( itemIndex, jobDescColId, job.GetDescription() );
97
98 KIWAY::FACE_T iface = JOB_REGISTRY::GetKifaceType( job.m_type );
99 wxString source = wxEmptyString;
100
101 if( iface < KIWAY::KIWAY_FACE_COUNT )
102 {
103 if( iface == KIWAY::FACE_PCB )
104 source = wxT( "PCB" );
105 else if( iface == KIWAY::FACE_SCH )
106 source = wxT( "SCH" );
107 }
108
109 m_jobList->SetItem( itemIndex, jobSourceColId, source );
110 }
111
112 SetupStandardButtons( { { wxID_OK, _( "Close" ) } } );
114 }
115
116 virtual void OnUpdateUI( wxUpdateUIEvent& event ) override
117 {
118 if( GetSize().GetWidth() != m_lastWidth )
119 {
120 m_lastWidth = GetSize().GetWidth();
121
122 if( m_marginsWidth < 0 )
123 m_marginsWidth = m_lastWidth - ( m_jobList->GetSize().GetWidth() * 2 );
124
125 int width = ( m_lastWidth / 2 );
126 width -= m_marginsWidth;
127 width -= m_jobList->GetColumnWidth( 0 );
128 width -= m_jobList->GetColumnWidth( 1 );
129 width -= m_jobList->GetColumnWidth( 3 );
130
131 m_jobList->SetColumnWidth( 2, width );
132 }
133 }
134
135 void OnJobListItemSelected( wxListEvent& event ) override
136 {
137 int itemIndex = event.GetIndex();
138
139 // The index could be negative (it is default -1)
140 if( itemIndex < 0 )
141 return;
142
143 std::vector<JOBSET_JOB> jobs = m_jobsFile->GetJobsForDestination( m_destination );
144
145 if( static_cast<size_t>( itemIndex ) < jobs.size() )
146 {
147 wxString jobId = jobs[itemIndex].m_id;
148
149 if( m_destination->m_lastRunReporters.contains( jobId ) )
150 {
151 WX_STRING_REPORTER* reporter =
152 static_cast<WX_STRING_REPORTER*>( m_destination->m_lastRunReporters[jobId] );
153
154 if( reporter )
155 m_textCtrlOutput->SetValue( reporter->GetMessages() );
156 }
157 else
158 {
159 m_textCtrlOutput->SetValue( _( "No output messages" ) );
160 }
161 }
162 }
163
164private:
167
170};
171
172
174{
175public:
176 PANEL_DESTINATION( wxWindow* aParent, PANEL_JOBSET* aPanelParent, KICAD_MANAGER_FRAME* aFrame,
177 JOBSET* aFile, JOBSET_DESTINATION* aDestination ) :
178 PANEL_DESTINATION_BASE( aParent ),
179 m_jobsFile( aFile ),
180 m_destinationId( aDestination->m_id ),
181 m_frame( aFrame ),
182 m_panelParent( aPanelParent )
183 {
184 m_buttonProperties->SetBitmap( KiBitmapBundle( BITMAPS::config ) );
185 m_buttonDelete->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
186
187#if _WIN32
188 // BORDER_RAISED/SUNKEN look pretty on every platform but Windows
189 long style = GetWindowStyleFlag();
190 style &= ~wxBORDER_MASK;
191 style |= wxBORDER_SIMPLE;
192 SetWindowStyleFlag( style );
193#endif // _WIN32
194
195 Connect( wxEVT_MENU, wxCommandEventHandler( PANEL_DESTINATION::onMenu ), nullptr, this );
196
197 if( JobsetDestinationTypeInfos.contains( aDestination->m_type ) )
198 {
199 JOBSET_DESTINATION_T_INFO& jobTypeInfo = JobsetDestinationTypeInfos[aDestination->m_type];
200 m_textOutputType->SetLabel( aDestination->GetDescription() );
201 m_bitmapOutputType->SetBitmap( KiBitmapBundle( jobTypeInfo.bitmap ) );
202 }
203
204 UpdateStatus();
205 }
206
208 {
209 Disconnect( wxEVT_MENU, wxCommandEventHandler( PANEL_DESTINATION::onMenu ), nullptr, this );
210 }
211
213 {
214 JOBSET_DESTINATION* destination = GetDestination();
215 wxCHECK( destination, /*void*/ );
216
217 destination->m_lastRunSuccess = 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 {
230 m_statusBitmap->SetBitmap( KiBitmapBundle( BITMAPS::checked_ok ) );
231 m_statusBitmap->Show();
232 m_statusBitmap->SetToolTip( _( "Last run successful" ) );
233 }
234 else
235 {
236 m_statusBitmap->SetBitmap( KiBitmapBundle( BITMAPS::ercerr ) );
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 virtual void OnGenerate( wxCommandEvent& event ) override
250 {
251 ClearStatus();
252 Refresh();
253
254 CallAfter(
255 [this]()
256 {
259
260 wxFileName fn = project.GetProjectFullName();
261 wxSetWorkingDirectory( fn.GetPath() );
262
263 JOBS_RUNNER jobRunner( &( m_frame->Kiway() ), m_jobsFile, &project );
264
265 auto* progressReporter = new WX_PROGRESS_REPORTER( m_frame, _( "Running jobs" ),
266 1 );
267
268 if( JOBSET_DESTINATION* destination = GetDestination() )
269 jobRunner.RunJobsForDestination( destination );
270
271 UpdateStatus();
272
273 delete progressReporter;
274
275 // Bring the Kicad manager frame back to the front
276 m_frame->Raise();
277 } );
278 }
279
280 virtual void OnLastStatusClick( wxMouseEvent& aEvent ) override
281 {
282 JOBSET_DESTINATION* destination = GetDestination();
283 wxCHECK( destination, /*void*/ );
284
285 DIALOG_JOBSET_RUN_LOG dialog( m_frame, m_jobsFile, destination );
286 dialog.ShowModal();
287 }
288
289 void OnRightDown( wxMouseEvent& aEvent ) override
290 {
291 JOBSET_DESTINATION* destination = GetDestination();
292 wxCHECK( destination, /*void*/ );
293
294 wxMenu menu;
295 menu.Append( wxID_EDIT, _( "Edit Destination Options..." ) );
296 menu.Append( wxID_DELETE, _( "Delete Destination" ) );
297
298 menu.AppendSeparator();
299 menu.Append( wxID_VIEW_DETAILS, _( "View Last Run Log..." ) );
300
301 menu.Enable( wxID_VIEW_DETAILS, destination->m_lastRunSuccess.has_value() );
302
303 PopupMenu( &menu );
304 }
305
306 void OnProperties( wxCommandEvent& aEvent ) override
307 {
308 JOBSET_DESTINATION* destination = GetDestination();
309 wxCHECK( destination, /*void*/ );
310
311 DIALOG_DESTINATION dialog( m_frame, m_jobsFile, destination );
312
313 if( dialog.ShowModal() == wxID_OK )
314 {
315 m_textOutputType->SetLabel( destination->GetDescription() );
318 }
319 }
320
321 virtual void OnDelete( wxCommandEvent& aEvent ) override
322 {
324 }
325
327 {
328 for( JOBSET_DESTINATION& destination : m_jobsFile->GetDestinations() )
329 {
330 if( destination.m_id == m_destinationId )
331 return &destination;
332 }
333
334 return nullptr;
335 }
336
337private:
338 void onMenu( wxCommandEvent& aEvent )
339 {
340 switch( aEvent.GetId() )
341 {
342 case wxID_EDIT:
343 {
344 wxCommandEvent dummy;
346 }
347 break;
348
349 case wxID_DELETE:
350 {
351 wxCommandEvent dummy;
352 OnDelete( dummy );
353 }
354 break;
355
356 case wxID_VIEW_DETAILS:
357 {
358 wxMouseEvent dummy;
360 }
361 break;
362
363 default:
364 wxFAIL_MSG( wxT( "Unknown ID in context menu event" ) );
365 }
366 }
367
368private:
373};
374
375
377 GRID_TRICKS( aGrid ),
378 m_parent( aParent )
379{
382}
383
384
385void JOBS_GRID_TRICKS::showPopupMenu( wxMenu& menu, wxGridEvent& aEvent )
386{
387 wxArrayInt selectedRows = m_grid->GetSelectedRows();
388
389 menu.Append( JOB_DESCRIPTION, _( "Edit Job Description" ) );
390 menu.Append( JOB_PROPERTIES, _( "Edit Job Settings..." ) );
391 menu.AppendSeparator();
392 menu.Append( GRIDTRICKS_ID_COPY, _( "Copy" ) + "\tCtrl+C", _( "Copy selected cells to clipboard" ) );
393 menu.Append( GRIDTRICKS_ID_DELETE, _( "Delete" ) + "\tDel", _( "Delete selected jobs" ) );
394 menu.Append( GRIDTRICKS_ID_SELECT, _( "Select All" ) + "\tCtrl+A", _( "Select all jobs" ) );
395
396 menu.Enable( JOB_DESCRIPTION, selectedRows.size() == 1 );
397 menu.Enable( JOB_PROPERTIES, selectedRows.size() == 1 );
398 menu.Enable( GRIDTRICKS_ID_DELETE, selectedRows.size() > 0 );
399
400 m_grid->PopupMenu( &menu );
401}
402
403
404void JOBS_GRID_TRICKS::doPopupSelection( wxCommandEvent& event )
405{
406 wxArrayInt selectedRows = m_grid->GetSelectedRows();
407
408 if( event.GetId() == JOB_DESCRIPTION )
409 {
410 if( selectedRows.size() > 0 )
411 {
412 m_grid->SetGridCursor( selectedRows[0], 2 );
413 m_grid->EnableCellEditControl();
414 }
415 }
416 else if( event.GetId() == JOB_PROPERTIES )
417 {
418 if( selectedRows.size() > 0 )
419 m_parent->OpenJobOptionsForListItem( selectedRows[0] );
420 }
421 else if( event.GetId() == GRIDTRICKS_ID_DELETE )
422 {
423 wxCommandEvent dummy;
425 }
426 else
427 {
429 }
430}
431
432
433bool JOBS_GRID_TRICKS::handleDoubleClick( wxGridEvent& aEvent )
434{
436
437 int curr_col = aEvent.GetCol();
438 int curr_row = aEvent.GetRow();
439
440 if( ( curr_col == COL_NUMBER || curr_col == COL_SOURCE || curr_col == COL_DESCR )
441 && curr_row >= 0 && curr_row < (int) m_parent->GetJobsFile()->GetJobs().size() )
442 {
443 m_doubleClickRow = curr_row;
445
446 CallAfter(
447 [this]()
448 {
449 // Yes, again. CancelShowEditorOnMouseUp() doesn't appear to be 100%
450 // reliable.
452 int row = m_doubleClickRow;
453 m_doubleClickRow = -1;
454
455 if( row >= 0 && row < (int) m_parent->GetJobsFile()->GetJobs().size() )
457 } );
458
459 return true;
460 }
461
462 return false;
463}
464
465
466PANEL_JOBSET::PANEL_JOBSET( wxAuiNotebook* aParent, KICAD_MANAGER_FRAME* aFrame,
467 std::unique_ptr<JOBSET> aJobsFile ) :
468 PANEL_JOBSET_BASE( aParent ),
469 m_parentBook( aParent ),
470 m_frame( aFrame ),
471 m_jobsFile( std::move( aJobsFile ) )
472{
473 m_jobsGrid->PushEventHandler( new JOBS_GRID_TRICKS( this, m_jobsGrid ) );
474
475 m_jobsGrid->SetDefaultRowSize( m_jobsGrid->GetDefaultRowSize() + 4 );
476 m_jobsGrid->OverrideMinSize( 0.6, 0.3 );
477 m_jobsGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
478
479 // 'm' for margins
480 m_jobsGrid->SetColSize( COL_NUMBER, GetTextExtent( wxT( "99m" ) ).x );
481 m_jobsGrid->SetColSize( COL_SOURCE, GetTextExtent( wxT( "PCBm" ) ).x );
482
483 m_buttonAddJob->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
484 m_buttonUp->SetBitmap( KiBitmapBundle( BITMAPS::small_up ) );
485 m_buttonDown->SetBitmap( KiBitmapBundle( BITMAPS::small_down ) );
486 m_buttonDelete->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
487 m_buttonAddDestination->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
488
491}
492
493
495{
496 // Delete the GRID_TRICKS.
497 m_jobsGrid->PopEventHandler( true );
498}
499
500
502{
503 JOBSET_DESTINATION* output = aPanel->GetDestination();
504
505 m_destinationListSizer->Detach( aPanel );
506 aPanel->Destroy();
507
508 // ensure the window contents get shifted as needed
509 m_destinationList->Layout();
510 Layout();
511
512 m_jobsFile->RemoveDestination( output );
513}
514
515
517{
518 if( m_jobsGrid->GetNumberRows() )
519 m_jobsGrid->DeleteRows( 0, m_jobsGrid->GetNumberRows() );
520
521 m_jobsGrid->AppendRows( (int) m_jobsFile->GetJobs().size() );
522
523 int num = 1;
524
525 for( JOBSET_JOB& job : m_jobsFile->GetJobs() )
526 {
527 m_jobsGrid->SetCellValue( num - 1, COL_NUMBER, wxString::Format( "%d", num ) );
528 m_jobsGrid->SetReadOnly( num - 1, COL_NUMBER );
529
530 m_jobsGrid->SetCellValue( num - 1, COL_DESCR, job.GetDescription() );
531
532 m_jobsGrid->SetReadOnly( num - 1, COL_SOURCE );
533
534 KIWAY::FACE_T iface = JOB_REGISTRY::GetKifaceType( job.m_type );
535 wxString source = wxEmptyString;
536
537 if( iface < KIWAY::KIWAY_FACE_COUNT )
538 {
539 if( iface == KIWAY::FACE_PCB )
540 source = wxT( "PCB" );
541 else if( iface == KIWAY::FACE_SCH )
542 source = wxT( "SCH" );
543 }
544
545 m_jobsGrid->SetCellValue( num - 1, COL_SOURCE, source );
546
547 num++;
548 }
549
550 UpdateTitle();
551
552 // Ensure the outputs get their Run-ability status updated
554 panel->UpdateStatus();
555}
556
557
559{
560 wxString tabName = m_jobsFile->GetFullName();
561
562 if( m_jobsFile->GetDirty() )
563 tabName = wxS( "*" ) + tabName;
564
565 int pageIdx = m_parentBook->FindPage( this );
566 m_parentBook->SetPageText( pageIdx, tabName );
567}
568
569
571{
572 PANEL_DESTINATION* destinationPanel = new PANEL_DESTINATION( m_destinationList, this, m_frame,
573 m_jobsFile.get(), aOutput );
574
575#if __OSX__
576 m_outputListSizer->Add( destinationPanel, 0, wxEXPAND, 5 );
577#else
578 m_destinationListSizer->Add( destinationPanel, 0, wxEXPAND|wxLEFT|wxRIGHT|wxBOTTOM, 5 );
579#endif
580
581 m_destinationList->Layout();
582}
583
584
585std::vector<PANEL_DESTINATION*> PANEL_JOBSET::GetDestinationPanels()
586{
587 std::vector<PANEL_DESTINATION*> panels;
588
589 for( const wxSizerItem* item : m_destinationListSizer->GetChildren() )
590 {
591 if( PANEL_DESTINATION* panel = dynamic_cast<PANEL_DESTINATION*>( item->GetWindow() ) )
592 panels.push_back( panel );
593 }
594
595 return panels;
596}
597
598
600{
601 Freeze();
602
603 for( JOBSET_DESTINATION& job : m_jobsFile->GetDestinations() )
604 addDestinationPanel( &job );
605
606 // ensure the window contents get shifted as needed
607 Layout();
608 Thaw();
609}
610
611
613{
614 bool success = false;
615 JOBSET_JOB& job = m_jobsFile->GetJobs()[aItemIndex];
617
618 if( iface < KIWAY::KIWAY_FACE_COUNT )
619 {
621 success = m_frame->Kiway().ProcessJobConfigDialog( iface, job.m_job.get(), m_frame );
622 }
623 else
624 {
625 // special jobs
626 if( job.m_job->GetType() == "special_execute" )
627 {
628 JOB_SPECIAL_EXECUTE* specialJob = static_cast<JOB_SPECIAL_EXECUTE*>( job.m_job.get() );
629
630 DIALOG_EXECUTECOMMAND_JOB_SETTINGS dialog( m_frame, specialJob );
631
632 // QuasiModal for Scintilla autocomplete
633 if( dialog.ShowQuasiModal() == wxID_OK )
634 success = true;
635 }
636 else if( job.m_job->GetType() == "special_copyfiles" )
637 {
638 JOB_SPECIAL_COPYFILES* specialJob = static_cast<JOB_SPECIAL_COPYFILES*>( job.m_job.get() );
639 DIALOG_COPYFILES_JOB_SETTINGS dialog( m_frame, specialJob );
640
641 if( dialog.ShowModal() == wxID_OK )
642 success = true;
643 }
644 }
645
646 if( success )
647 {
648 m_jobsFile->SetDirty();
649 UpdateTitle();
650 }
651
652 // Bring the Kicad manager frame back to the front
653 m_frame->Raise();
654
655 return success;
656}
657
658
659void PANEL_JOBSET::OnGridCellChange( wxGridEvent& aEvent )
660{
661 int row = aEvent.GetRow();
662 int col = aEvent.GetCol();
663
664 if( col == COL_DESCR )
665 m_jobsFile->GetJobs()[row].SetDescription( m_jobsGrid->GetCellValue( row, col ) );
666}
667
668
669void PANEL_JOBSET::OnSaveButtonClick( wxCommandEvent& aEvent )
670{
672 return;
673
674 m_jobsFile->SaveToFile( wxEmptyString, true );
675 UpdateTitle();
676}
677
678
679void PANEL_JOBSET::OnAddJobClick( wxCommandEvent& aEvent )
680{
682 return;
683
684 wxArrayString headers;
685 std::vector<wxArrayString> items;
686
687 headers.Add( _( "Job Types" ) );
688
690
691 for( const std::pair<const wxString, JOB_REGISTRY_ENTRY>& entry : jobMap )
692 {
693 wxArrayString item;
694 item.Add( wxGetTranslation( entry.second.title ) );
695 items.emplace_back( item );
696 }
697
698 EDA_LIST_DIALOG dlg( this, _( "Add New Job" ), headers, items );
699 dlg.SetListLabel( _( "Select job type:" ) );
700
701 if( dlg.ShowModal() == wxID_OK )
702 {
703 wxString selectedString = dlg.GetTextSelection();
704
705 wxString jobKey;
706
707 if( !selectedString.IsEmpty() )
708 {
709 for( const std::pair<const wxString, JOB_REGISTRY_ENTRY>& entry : jobMap )
710 {
711 if( wxGetTranslation( entry.second.title ) == selectedString )
712 {
713 jobKey = entry.first;
714 break;
715 }
716 }
717 }
718
719 if( !jobKey.IsEmpty() )
720 {
721 int row = m_jobsFile->GetJobs().size();
722 bool wasDirty = m_jobsFile->GetDirty();
723 JOB* job = JOB_REGISTRY::CreateInstance<JOB>( jobKey );
724
725 m_jobsFile->AddNewJob( jobKey, job );
726
727 if( OpenJobOptionsForListItem( row ) )
728 {
730
731 m_jobsGrid->SetGridCursor( row, 2 );
732 m_jobsGrid->EnableCellEditControl();
733 }
734 else
735 {
736 m_jobsFile->RemoveJob( row );
737 m_jobsFile->SetDirty( wasDirty );
738 }
739 }
740 }
741}
742
743
744void PANEL_JOBSET::OnJobButtonDelete( wxCommandEvent& aEvent )
745{
747 return;
748
749 wxArrayInt selectedRows = m_jobsGrid->GetSelectedRows();
750
751 if( selectedRows.empty() )
752 return;
753
754 m_jobsGrid->CommitPendingChanges( true /* quiet mode */ );
755 m_jobsGrid->ClearSelection();
756
757 // Reverse sort so deleting a row doesn't change the indexes of the other rows.
758 selectedRows.Sort( []( int* first, int* second ) { return *second - *first; } );
759
760 int select = selectedRows[0];
761
762 for( int row : selectedRows )
763 m_jobsFile->RemoveJob( row );
764
766
767 if( m_jobsGrid->GetNumberRows() )
768 {
769 m_jobsGrid->MakeCellVisible( std::max( 0, select-1 ), m_jobsGrid->GetGridCursorCol() );
770 m_jobsGrid->SetGridCursor( std::max( 0, select-1 ), m_jobsGrid->GetGridCursorCol() );
771 }
772}
773
774
775void PANEL_JOBSET::OnAddDestinationClick( wxCommandEvent& aEvent )
776{
777 wxArrayString headers;
778 std::vector<wxArrayString> items;
779
780 headers.Add( _( "Destination Types" ) );
781
782 for( const auto& [destinationType, destinationTypeInfo] : JobsetDestinationTypeInfos )
783 {
784 wxArrayString item;
785 item.Add( wxGetTranslation( destinationTypeInfo.name ) );
786 items.emplace_back( item );
787 }
788
789 EDA_LIST_DIALOG dlg( this, _( "Add New Destination" ), headers, items );
790 dlg.SetListLabel( _( "Select destination type:" ) );
791 dlg.HideFilter();
792
793 if( dlg.ShowModal() == wxID_OK )
794 {
795 wxString selectedString = dlg.GetTextSelection();
796
797 for( const auto& [destinationType, destinationTypeInfo] : JobsetDestinationTypeInfos )
798 {
799 if( wxGetTranslation( destinationTypeInfo.name ) == selectedString )
800 {
801 JOBSET_DESTINATION* destination = m_jobsFile->AddNewDestination( destinationType );
802
803 DIALOG_DESTINATION dialog( m_frame, m_jobsFile.get(), destination );
804
805 if (dialog.ShowModal() == wxID_OK)
806 {
807 Freeze();
808 addDestinationPanel( destination );
809 Thaw();
810 }
811 else
812 {
813 // canceled
814 m_jobsFile->RemoveDestination( destination );
815 }
816 }
817 }
818 }
819}
820
821
823{
824 if( m_jobsFile->GetDirty() )
825 {
826 wxFileName fileName = m_jobsFile->GetFullFilename();
827 wxString msg = _( "Save changes to '%s' before closing?" );
828
829 if( !HandleUnsavedChanges( this, wxString::Format( msg, fileName.GetFullName() ),
830 [&]() -> bool
831 {
832 return m_jobsFile->SaveToFile(wxEmptyString, true);
833 } ) )
834 {
835 return false;
836 }
837 }
838
839 return true;
840}
841
842
844{
846 KIWAY_PLAYER* frame = m_frame->Kiway().Player( FRAME_PCB_EDITOR, false );
847
848 if( !frame )
849 {
850 frame = m_frame->Kiway().Player( FRAME_PCB_EDITOR, true );
851
852 // frame can be null if Cvpcb cannot be run. No need to show a warning
853 // Kiway() generates the error messages
854 if( !frame )
855 return;
856
857 wxFileName boardfn = project.GetProjectFullName();
858 boardfn.SetExt( FILEEXT::PcbFileExtension );
859
860 // Prevent our window from being closed during the open process
861 wxEventBlocker blocker( this );
862
863 frame->OpenProjectFiles( std::vector<wxString>( 1, boardfn.GetFullPath() ) );
864
865 if( !frame->IsVisible() )
866 frame->Show( true );
867 }
868
869 frame = m_frame->Kiway().Player( FRAME_SCH, false );
870
871 if( !frame )
872 {
873 frame = m_frame->Kiway().Player( FRAME_SCH, true );
874
875 // frame can be null if Cvpcb cannot be run. No need to show a warning
876 // Kiway() generates the error messages
877 if( !frame )
878 return;
879
880 wxFileName schFn = project.GetProjectFullName();
882
883 wxEventBlocker blocker( this );
884
885 frame->OpenProjectFiles( std::vector<wxString>( 1, schFn.GetFullPath() ) );
886
887 if( !frame->IsVisible() )
888 frame->Show( true );
889 }
890
891 SetFocus();
892}
893
894
896{
897 return m_jobsFile->GetFullFilename();
898}
899
900
901void PANEL_JOBSET::OnJobButtonUp( wxCommandEvent& aEvent )
902{
904 return;
905
906 int item = m_jobsGrid->GetGridCursorRow();
907
908 if( item > 0 )
909 {
910 m_jobsFile->MoveJobUp( item );
911
913
914 m_jobsGrid->SelectRow( item - 1 );
915 m_jobsGrid->SetGridCursor( item - 1, m_jobsGrid->GetGridCursorCol() );
916 }
917 else
918 {
919 wxBell();
920 }
921}
922
923
924void PANEL_JOBSET::OnJobButtonDown( wxCommandEvent& aEvent )
925{
927 return;
928
929 int item = m_jobsGrid->GetGridCursorRow();
930
931 if( item < m_jobsGrid->GetNumberRows() - 1 )
932 {
933 m_jobsFile->MoveJobDown( item );
934
936
937 m_jobsGrid->SelectRow( item + 1 );
938 m_jobsGrid->SetGridCursor( item + 1, m_jobsGrid->GetGridCursorCol() );
939 }
940 else
941 {
942 wxBell();
943 }
944}
945
946
948{
950 return;
951
952 // sanity
953 if( m_jobsFile->GetDestinations().empty() )
954 {
955 DisplayError( this, _( "No destinations defined" ) );
956 return;
957 }
958
960 panel->ClearStatus();
961
962 Refresh();
963
964 CallAfter(
965 [this]()
966 {
969
970 wxFileName fn = project.GetProjectFullName();
971 wxSetWorkingDirectory( fn.GetPath() );
972
973 JOBS_RUNNER jobRunner( &( m_frame->Kiway() ), m_jobsFile.get(), &project );
974
975 WX_PROGRESS_REPORTER* progressReporter =
976 new WX_PROGRESS_REPORTER( m_frame, _( "Running jobs" ), 1 );
977
978 jobRunner.RunJobsAllDestinations();
979
981 panel->UpdateStatus();
982
983 delete progressReporter;
984
985 // Bring the Kicad manager frame back to the front
986 m_frame->Raise();
987 } );
988}
989
990
991void PANEL_JOBSET::OnSizeGrid( wxSizeEvent& aEvent )
992{
993 m_jobsGrid->SetColSize( COL_DESCR, m_jobsGrid->GetSize().x
994 - m_jobsGrid->GetColSize( COL_SOURCE )
995 - m_jobsGrid->GetColSize( COL_NUMBER ) );
996
997 // Always propagate for a grid repaint (needed if the height changes, as well as width)
998 aEvent.Skip();
999}
1000
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:106
std::vector< JOBSET_DESTINATION > & GetDestinations()
Definition: jobset.h:119
void SetDirty(bool aFlag=true)
Definition: jobset.h:125
std::vector< JOBSET_JOB > GetJobsForDestination(JOBSET_DESTINATION *aDestination)
Definition: jobset.cpp:301
std::vector< JOBSET_JOB > & GetJobs()
Definition: jobset.h:112
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:51
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:37
static KIWAY::FACE_T GetKifaceType(const wxString &aName)
An simple container class that lets us dispatch output jobs to kifaces.
Definition: job.h:182
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()
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:215
bool CancelPendingChanges()
Definition: wx_grid.cpp:616
void CancelShowEditorOnMouseUp()
Definition: wx_grid.h:186
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:644
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:172
const wxString & GetMessages() const
Definition: reporter.cpp:87
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
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:98
JOBSET_DESTINATION_T m_type
Definition: jobset.h:87
wxString GetDescription() const
Definition: jobset.cpp:159
std::optional< bool > m_lastRunSuccess
Definition: jobset.h:96
std::unordered_map< wxString, std::optional< bool > > m_lastRunSuccessMap
Definition: jobset.h:97
std::shared_ptr< JOB > m_job
Definition: jobset.h:49
wxString m_type
Definition: jobset.h:47
Definition of file extensions used in Kicad.