KiCad PCB EDA Suite
Loading...
Searching...
No Matches
panel_jobset.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2024 Mark Roszko <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#include "panel_jobset.h"
22#include "dialog_destination.h"
25#include <wx/aui/auibook.h>
26#include <jobs/jobset.h>
27#include <jobs/job_registry.h>
28#include <eda_list_dialog.h>
29#include <wx/checkbox.h>
30#include <wx/menu.h>
31#include <bitmaps.h>
32#include <i18n_utility.h>
33#include <jobs_runner.h>
35#include <kicad_manager_frame.h>
36#include <vector>
37#include <wx/dcclient.h>
38
41#include <widgets/wx_grid.h>
43#include <kiplatform/ui.h>
44#include <confirm.h>
45#include <launch_ext.h>
46#include <wx/filename.h>
47
52#include <common.h>
53
54
55extern KICOMMON_API
56std::map<JOBSET_DESTINATION_T, JOBSET_DESTINATION_T_INFO> JobsetDestinationTypeInfos;
57
58
60{
61public:
62 DIALOG_JOBSET_RUN_LOG( wxWindow* aParent, JOBSET* aJobsFile, JOBSET_DESTINATION* aDestination ) :
64 m_jobsFile( aJobsFile ),
65 m_destination( aDestination ),
66 m_lastWidth( -1 ),
67 m_marginsWidth( -1 )
68 {
69 // Don't show stale output from last run
71
72 m_staticTextOutputName->SetLabel( wxString::Format( _( "Destination: %s" ),
73 aDestination->GetDescription() ) );
74
75 int jobBmpColId = m_jobList->AppendColumn( wxT( "" ) );
76 int jobNoColId = m_jobList->AppendColumn( _( "No." ) );
77 int jobDescColId = m_jobList->AppendColumn( _( "Job Description" ) );
78 int jobSourceColId = m_jobList->AppendColumn( _( "Source" ) );
79 m_jobList->SetColumnWidth( jobBmpColId, 26 );
80 m_jobList->SetColumnWidth( jobNoColId, GetTextExtent( wxT( "XXXX" ) ).GetWidth() );
81 m_jobList->SetColumnWidth( jobSourceColId, GetTextExtent( wxT( "XXXXXX" ) ).GetWidth() );
82
83 wxImageList* imageList = new wxImageList( 16, 16, true, 3 );
84 imageList->Add( KiBitmapBundle( BITMAPS::ercerr ).GetBitmap( wxSize( 16, 16 ) ) );
85 imageList->Add( KiBitmapBundle( BITMAPS::checked_ok ).GetBitmap( wxSize( 16, 16 ) ) );
86 m_jobList->SetImageList( imageList, wxIMAGE_LIST_SMALL );
87
88 int num = 1;
89
90 for( JOBSET_JOB& job : aJobsFile->GetJobsForDestination( aDestination ) )
91 {
92 int imageIdx = -1;
93
94 if( aDestination->m_lastRunSuccessMap.contains( job.m_id ) )
95 {
96 if( aDestination->m_lastRunSuccessMap[job.m_id].value() )
97 imageIdx = 1;
98 else
99 imageIdx = 0;
100 }
101
102 long itemIndex = m_jobList->InsertItem( m_jobList->GetItemCount(), imageIdx );
103
104 m_jobList->SetItem( itemIndex, jobNoColId, wxString::Format( "%d", num++ ) );
105 m_jobList->SetItem( itemIndex, jobDescColId, job.GetDescription() );
106
107 KIWAY::FACE_T iface = JOB_REGISTRY::GetKifaceType( job.m_type );
108 wxString source = wxEmptyString;
109
110 if( iface < KIWAY::KIWAY_FACE_COUNT )
111 {
112 if( iface == KIWAY::FACE_PCB )
113 source = wxT( "PCB" );
114 else if( iface == KIWAY::FACE_SCH )
115 source = wxT( "SCH" );
116 }
117
118 m_jobList->SetItem( itemIndex, jobSourceColId, source );
119 }
120
121 SetupStandardButtons( { { wxID_OK, _( "Close" ) } } );
123 }
124
125 virtual void OnUpdateUI( wxUpdateUIEvent& event ) override
126 {
127 if( GetSize().GetWidth() != m_lastWidth )
128 {
129 m_lastWidth = GetSize().GetWidth();
130
131 if( m_marginsWidth < 0 )
132 m_marginsWidth = m_lastWidth - ( m_jobList->GetSize().GetWidth() * 2 );
133
134 int width = ( m_lastWidth / 2 );
135 width -= m_marginsWidth;
136 width -= m_jobList->GetColumnWidth( 0 );
137 width -= m_jobList->GetColumnWidth( 1 );
138 width -= m_jobList->GetColumnWidth( 3 );
139
140 m_jobList->SetColumnWidth( 2, width );
141 }
142 }
143
144 void OnJobListItemSelected( wxListEvent& event ) override
145 {
146 int itemIndex = event.GetIndex();
147
148 // The index could be negative (it is default -1)
149 if( itemIndex < 0 )
150 return;
151
152 std::vector<JOBSET_JOB> jobs = m_jobsFile->GetJobsForDestination( m_destination );
153
154 if( static_cast<size_t>( itemIndex ) < jobs.size() )
155 {
156 wxString jobId = jobs[itemIndex].m_id;
157
158 if( m_destination->m_lastRunReporters.contains( jobId ) )
159 m_textCtrlOutput->SetValue( m_destination->m_lastRunReporters.at( jobId )->GetMessages() );
160 else
161 m_textCtrlOutput->SetValue( _( "No output messages" ) );
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 {
188
189#if _WIN32
190 // BORDER_RAISED/SUNKEN look pretty on every platform but Windows
191 long style = GetWindowStyleFlag();
192 style &= ~wxBORDER_MASK;
193 style |= wxBORDER_SIMPLE;
194 SetWindowStyleFlag( style );
195#endif // _WIN32
196
197 Connect( wxEVT_MENU, wxCommandEventHandler( PANEL_DESTINATION::onMenu ), nullptr, this );
198
199 if( JobsetDestinationTypeInfos.contains( aDestination->m_type ) )
200 {
201 JOBSET_DESTINATION_T_INFO& jobTypeInfo = JobsetDestinationTypeInfos[aDestination->m_type];
202 m_textOutputType->SetLabel( aDestination->GetDescription() );
203 m_bitmapOutputType->SetBitmap( KiBitmapBundle( jobTypeInfo.bitmap ) );
204 }
205
206#ifndef _WIN32
207 m_pathInfo->SetFont( KIUI::GetSmallInfoFont( this ) );
208#endif
209 UpdatePathInfo( aDestination->GetPathInfo() );
210 UpdateStatus();
211 }
212
214 {
215 Disconnect( wxEVT_MENU, wxCommandEventHandler( PANEL_DESTINATION::onMenu ), nullptr, this );
216 }
217
219 {
220 JOBSET_DESTINATION* destination = GetDestination();
221 wxCHECK( destination, /*void*/ );
222
223 destination->m_lastRunSuccess = std::nullopt;
224 destination->m_lastResolvedOutputPath = std::nullopt;
225 m_statusBitmap->SetBitmap( wxNullBitmap );
226 }
227
229 {
230 JOBSET_DESTINATION* destination = GetDestination();
231 wxCHECK( destination, /*void*/ );
232
233 if( destination->m_lastRunSuccess.has_value() )
234 {
235 if( destination->m_lastRunSuccess.value() )
236 {
238 m_statusBitmap->Show();
239 m_statusBitmap->SetToolTip( _( "Last run successful" ) );
240 }
241 else
242 {
244 m_statusBitmap->Show();
245 m_statusBitmap->SetToolTip( _( "Last run failed" ) );
246 }
247 }
248 else
249 {
250 m_statusBitmap->SetBitmap( wxNullBitmap );
251 }
252
253 m_buttonGenerate->Enable( !m_jobsFile->GetJobsForDestination( destination ).empty() );
254 }
255
256 void UpdatePathInfo( const wxString& aMsg )
257 {
258 wxClientDC dc( this );
259 int width = GetSize().GetWidth();
260
261 m_pathInfo->SetLabel( wxControl::Ellipsize( aMsg, dc, wxELLIPSIZE_MIDDLE, width ) );
262 }
263
264 virtual void OnGenerate( wxCommandEvent& event ) override
265 {
266 ClearStatus();
267 Refresh();
268
269 CallAfter(
270 [this]()
271 {
272 PROJECT& project = m_frame->Kiway().Prj();
273 m_panelParent->EnsurePcbSchFramesOpen();
274
275 wxFileName fn = project.GetProjectFullName();
276 wxSetWorkingDirectory( fn.GetPath() );
277
278 {
279 JOBS_PROGRESS_REPORTER progressReporter( m_frame, _( "Running Jobs" ) );
280 JOBS_RUNNER jobRunner( &m_frame->Kiway(), m_jobsFile, &project,
281 NULL_REPORTER::GetInstance(), &progressReporter );
282
283 if( JOBSET_DESTINATION* destination = GetDestination() )
284 jobRunner.RunJobsForDestination( destination );
285
286 UpdateStatus();
287 }
288
289 // Bring the Kicad manager frame back to the front
290 m_frame->Raise();
291 } );
292 }
293
294 void OnOpenOutput( wxCommandEvent& aEvent ) override
295 {
296 JOBSET_DESTINATION* destination = GetDestination();
297 wxCHECK( destination, /*void*/ );
298
299 wxString resolvedPath;
300
301 if( destination->m_lastResolvedOutputPath.has_value() )
302 {
303 resolvedPath = destination->m_lastResolvedOutputPath.value();
304 }
305 else
306 {
307 resolvedPath = JOBS_OUTPUT_HANDLER::ResolveOutputPath( destination->GetPathInfo(),
308 &m_frame->Prj() );
309 }
310
311 if( resolvedPath.IsEmpty() )
312 return;
313
314 wxString fullPath = wxFileName( resolvedPath ).GetFullPath();
315
316 if( !LaunchExternal( fullPath ) )
317 DisplayError( this, wxString::Format( _( "Failed to open '%s'." ), fullPath ) );
318 }
319
320 void OnLastStatusClick( wxMouseEvent& aEvent ) override
321 {
322 JOBSET_DESTINATION* destination = GetDestination();
323 wxCHECK( destination, /*void*/ );
324
325 DIALOG_JOBSET_RUN_LOG dialog( m_frame, m_jobsFile, destination );
326 dialog.ShowModal();
327 }
328
329 void OnRightDown( wxMouseEvent& aEvent ) override
330 {
331 JOBSET_DESTINATION* destination = GetDestination();
332 wxCHECK( destination, /*void*/ );
333
334 wxMenu menu;
335 menu.Append( wxID_EDIT, _( "Edit Destination Options..." ) );
336 menu.Append( wxID_DELETE, _( "Delete Destination" ) );
337
338 menu.AppendSeparator();
339 menu.Append( wxID_VIEW_DETAILS, _( "View Last Run Log..." ) );
340 menu.Append( wxID_OPEN, _( "Open Output" ) );
341
342 menu.Enable( wxID_VIEW_DETAILS, destination->m_lastRunSuccess.has_value() );
343 menu.Enable( wxID_OPEN, m_buttonOpenOutput->IsEnabled() );
344
345 PopupMenu( &menu );
346 }
347
348 void OnProperties( wxCommandEvent& aEvent ) override
349 {
350 JOBSET_DESTINATION* destination = GetDestination();
351 wxCHECK( destination, /*void*/ );
352
353 DIALOG_DESTINATION dialog( m_frame, m_jobsFile, destination );
354
355 if( dialog.ShowModal() == wxID_OK )
356 {
357 m_textOutputType->SetLabel( destination->GetDescription() );
358 UpdatePathInfo( destination->GetPathInfo() );
359 m_jobsFile->SetDirty();
360 m_panelParent->UpdateTitle();
361 ClearStatus();
362 }
363 }
364
365 virtual void OnDelete( wxCommandEvent& aEvent ) override
366 {
367 m_panelParent->RemoveDestination( this );
368 }
369
371 {
372 for( JOBSET_DESTINATION& destination : m_jobsFile->GetDestinations() )
373 {
374 if( destination.m_id == m_destinationId )
375 return &destination;
376 }
377
378 return nullptr;
379 }
380
381private:
382 void onMenu( wxCommandEvent& aEvent )
383 {
384 switch( aEvent.GetId() )
385 {
386 case wxID_EDIT:
387 {
388 wxCommandEvent dummy;
390 }
391 break;
392
393 case wxID_DELETE:
394 {
395 wxCommandEvent dummy;
396 OnDelete( dummy );
397 }
398 break;
399
400 case wxID_VIEW_DETAILS:
401 {
402 wxMouseEvent dummy;
404 }
405 break;
406
407 case wxID_OPEN:
408 {
409 wxCommandEvent dummy;
411 }
412 break;
413
414 default: wxFAIL_MSG( wxT( "Unknown ID in context menu event" ) );
415 }
416 }
417
418private:
423};
424
425
427 GRID_TRICKS( aGrid ),
428 m_parent( aParent ),
429 m_doubleClickRow( -1 )
430{
433}
434
435
436void JOBS_GRID_TRICKS::showPopupMenu( wxMenu& menu, wxGridEvent& aEvent )
437{
438 // Snapshot the selection, PopupMenu() below can lose it before doPopupSelection() runs
439 m_selectedRows = m_grid->GetSelectedRows();
440 wxArrayInt& selectedRows = m_selectedRows;
441
442 menu.Append( JOB_DESCRIPTION, _( "Edit Job Description" ) );
443 menu.Append( JOB_PROPERTIES, _( "Edit Job Settings..." ) );
444 menu.AppendSeparator();
445 menu.Append( GRIDTRICKS_ID_COPY, _( "Copy" ) + "\tCtrl+C", _( "Copy selected cells to clipboard" ) );
446 menu.Append( GRIDTRICKS_ID_DELETE, _( "Delete" ) + "\tDel", _( "Delete selected jobs" ) );
447 menu.Append( GRIDTRICKS_ID_SELECT, _( "Select All" ) + "\tCtrl+A", _( "Select all jobs" ) );
448
449 menu.Enable( JOB_DESCRIPTION, selectedRows.size() == 1 );
450 menu.Enable( JOB_PROPERTIES, selectedRows.size() == 1 );
451 menu.Enable( GRIDTRICKS_ID_DELETE, selectedRows.size() > 0 );
452
453 m_grid->PopupMenu( &menu );
454}
455
456
457void JOBS_GRID_TRICKS::doPopupSelection( wxCommandEvent& event )
458{
459 wxArrayInt& selectedRows = m_selectedRows;
460
461 if( event.GetId() == JOB_DESCRIPTION )
462 {
463 if( selectedRows.size() > 0 )
464 {
465 m_grid->SetGridCursor( selectedRows[0], 2 );
466 m_grid->EnableCellEditControl();
467 }
468 }
469 else if( event.GetId() == JOB_PROPERTIES )
470 {
471 if( selectedRows.size() > 0 )
472 m_parent->OpenJobOptionsForListItem( selectedRows[0] );
473 }
474 else if( event.GetId() == GRIDTRICKS_ID_DELETE )
475 {
476 wxCommandEvent dummy;
477 m_parent->OnJobButtonDelete( dummy );
478 }
479 else
480 {
482 }
483}
484
485
486bool JOBS_GRID_TRICKS::handleDoubleClick( wxGridEvent& aEvent )
487{
488 m_grid->CancelShowEditorOnMouseUp();
489
490 int curr_col = aEvent.GetCol();
491 int curr_row = aEvent.GetRow();
492
493 if( ( curr_col == COL_NUMBER || curr_col == COL_SOURCE || curr_col == COL_DESCR )
494 && curr_row >= 0 && curr_row < (int) m_parent->GetJobsFile()->GetJobs().size() )
495 {
496 m_doubleClickRow = curr_row;
497 m_grid->CancelPendingChanges();
498
499 CallAfter(
500 [this]()
501 {
502 // Yes, again. CancelShowEditorOnMouseUp() doesn't appear to be 100%
503 // reliable.
504 m_grid->CancelPendingChanges();
505 int row = m_doubleClickRow;
506 m_doubleClickRow = -1;
507
508 if( row >= 0 && row < (int) m_parent->GetJobsFile()->GetJobs().size() )
509 m_parent->OpenJobOptionsForListItem( row );
510 } );
511
512 return true;
513 }
514
515 return false;
516}
517
518
519PANEL_JOBSET::PANEL_JOBSET( wxAuiNotebook* aParent, KICAD_MANAGER_FRAME* aFrame,
520 std::unique_ptr<JOBSET> aJobsFile ) :
521 PANEL_JOBSET_BASE( aParent ),
522 m_parentBook( aParent ),
523 m_frame( aFrame ),
524 m_jobsFile( std::move( aJobsFile ) )
525{
526 m_jobsGrid->PushEventHandler( new JOBS_GRID_TRICKS( this, m_jobsGrid ) );
527
528 m_jobsGrid->OverrideMinSize( 0.6, 0.3 );
529 m_jobsGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
530
531 // 'm' for margins
532 m_jobsGrid->SetColSize( COL_NUMBER, GetTextExtent( wxT( "99m" ) ).x );
533 m_jobsGrid->SetColSize( COL_SOURCE, GetTextExtent( wxT( "PCBm" ) ).x );
534
540
543}
544
545
547{
548 // Delete the GRID_TRICKS.
549 m_jobsGrid->PopEventHandler( true );
550}
551
552
554{
555 JOBSET_DESTINATION* output = aPanel->GetDestination();
556
557 m_destinationListSizer->Detach( aPanel );
558 aPanel->Destroy();
559
560 // ensure the window contents get shifted as needed
561 m_destinationList->Layout();
562 Layout();
563
564 m_jobsFile->RemoveDestination( output );
565}
566
567
569{
570 m_jobsGrid->ClearRows();
571 m_jobsGrid->AppendRows( (int) m_jobsFile->GetJobs().size() );
572
573 int num = 1;
574
575 for( JOBSET_JOB& job : m_jobsFile->GetJobs() )
576 {
577 m_jobsGrid->SetCellValue( num - 1, COL_NUMBER, wxString::Format( "%d", num ) );
578 m_jobsGrid->SetReadOnly( num - 1, COL_NUMBER );
579
580 m_jobsGrid->SetCellValue( num - 1, COL_DESCR, job.GetDescription() );
581
582 m_jobsGrid->SetReadOnly( num - 1, COL_SOURCE );
583
584 KIWAY::FACE_T iface = JOB_REGISTRY::GetKifaceType( job.m_type );
585 wxString source = wxEmptyString;
586
587 if( iface < KIWAY::KIWAY_FACE_COUNT )
588 {
589 if( iface == KIWAY::FACE_PCB )
590 source = wxT( "PCB" );
591 else if( iface == KIWAY::FACE_SCH )
592 source = wxT( "SCH" );
593 }
594
595 m_jobsGrid->SetCellValue( num - 1, COL_SOURCE, source );
596
597 num++;
598 }
599
600 UpdateTitle();
601
602 // Ensure the outputs get their Run-ability status updated
604 panel->UpdateStatus();
605}
606
607
609{
610 wxString tabName = m_jobsFile->GetFullName();
611
612 if( m_jobsFile->GetDirty() )
613 tabName = wxS( "*" ) + tabName;
614
615 int pageIdx = m_parentBook->FindPage( this );
616
617 if( pageIdx >= 0 )
618 m_parentBook->SetPageText( pageIdx, tabName );
619}
620
621
623{
624 PANEL_DESTINATION* destinationPanel = new PANEL_DESTINATION( m_destinationList, this, m_frame,
625 m_jobsFile.get(), aOutput );
626
627#if __OSX__
628 m_outputListSizer->Add( destinationPanel, 0, wxEXPAND, 5 );
629#else
630 m_destinationListSizer->Add( destinationPanel, 0, wxEXPAND|wxLEFT|wxRIGHT|wxBOTTOM, 5 );
631#endif
632
633 m_destinationList->Layout();
634}
635
636
637std::vector<PANEL_DESTINATION*> PANEL_JOBSET::GetDestinationPanels()
638{
639 std::vector<PANEL_DESTINATION*> panels;
640
641 for( const wxSizerItem* item : m_destinationListSizer->GetChildren() )
642 {
643 if( PANEL_DESTINATION* panel = dynamic_cast<PANEL_DESTINATION*>( item->GetWindow() ) )
644 panels.push_back( panel );
645 }
646
647 return panels;
648}
649
650
652{
653 Freeze();
654
655 for( JOBSET_DESTINATION& job : m_jobsFile->GetDestinations() )
656 addDestinationPanel( &job );
657
658 // ensure the window contents get shifted as needed
659 Layout();
660 Thaw();
661}
662
663
665{
666 bool success = false;
667 JOBSET_JOB& job = m_jobsFile->GetJobs()[aItemIndex];
669
670 if( iface < KIWAY::KIWAY_FACE_COUNT )
671 {
673 success = m_frame->Kiway().ProcessJobConfigDialog( iface, job.m_job.get(), m_frame );
674 }
675 else
676 {
677 // special jobs
678 if( job.m_job->GetType() == "special_execute" )
679 {
680 JOB_SPECIAL_EXECUTE* specialJob = static_cast<JOB_SPECIAL_EXECUTE*>( job.m_job.get() );
681
682 DIALOG_EXECUTECOMMAND_JOB_SETTINGS dialog( m_frame, specialJob, &m_frame->Prj() );
683
684 // QuasiModal for Scintilla autocomplete
685 if( dialog.ShowQuasiModal() == wxID_OK )
686 success = true;
687 }
688 else if( job.m_job->GetType() == "special_copyfiles" )
689 {
690 JOB_SPECIAL_COPYFILES* specialJob = static_cast<JOB_SPECIAL_COPYFILES*>( job.m_job.get() );
691 DIALOG_COPYFILES_JOB_SETTINGS dialog( m_frame, specialJob );
692
693 if( dialog.ShowModal() == wxID_OK )
694 success = true;
695 }
696 else if( job.m_job->GetType() == "special_archive" )
697 {
698 JOB_SPECIAL_ARCHIVE* specialJob = static_cast<JOB_SPECIAL_ARCHIVE*>( job.m_job.get() );
699 DIALOG_ARCHIVE_JOB_SETTINGS dialog( m_frame, specialJob );
700
701 if( dialog.ShowModal() == wxID_OK )
702 success = true;
703 }
704 else
705 {
706 DisplayErrorMessage( m_frame, wxString::Format( _( "This version of KiCad does not "
707 "support the '%s' job type." ),
708 job.m_type ) );
709 }
710 }
711
712 if( success )
713 {
714 m_jobsFile->SetDirty();
715 UpdateTitle();
716 }
717
718 // Bring the Kicad manager frame back to the front
719 m_frame->Raise();
720
721 return success;
722}
723
724
725void PANEL_JOBSET::OnGridCellChange( wxGridEvent& aEvent )
726{
727 int row = aEvent.GetRow();
728 int col = aEvent.GetCol();
729
730 if( col == COL_DESCR )
731 m_jobsFile->GetJobs()[row].SetDescription( m_jobsGrid->GetCellValue( row, col ) );
732}
733
734
735void PANEL_JOBSET::OnSaveButtonClick( wxCommandEvent& aEvent )
736{
737 if( !m_jobsGrid->CommitPendingChanges() )
738 return;
739
740 m_jobsFile->SaveToFile( wxEmptyString, true );
741 UpdateTitle();
742}
743
744
745void PANEL_JOBSET::OnAddJobClick( wxCommandEvent& aEvent )
746{
747 if( !m_jobsGrid->CommitPendingChanges() )
748 return;
749
750 wxArrayString headers;
751 std::vector<wxArrayString> items;
752
753 headers.Add( _( "Job Types" ) );
754
756
757 for( const std::pair<const wxString, JOB_REGISTRY_ENTRY>& entry : jobMap )
758 {
759 if( entry.second.deprecated )
760 continue;
761
762 wxArrayString item;
763 item.Add( wxGetTranslation( entry.second.title ) );
764 items.emplace_back( item );
765 }
766
767 EDA_LIST_DIALOG dlg( this, _( "Add New Job" ), headers, items );
768 dlg.SetListLabel( _( "Select job type:" ) );
769
770 if( dlg.ShowModal() == wxID_OK )
771 {
772 wxString selectedString = dlg.GetTextSelection();
773
774 wxString jobKey;
775
776 if( !selectedString.IsEmpty() )
777 {
778 for( const std::pair<const wxString, JOB_REGISTRY_ENTRY>& entry : jobMap )
779 {
780 if( entry.second.deprecated )
781 continue;
782
783 if( wxGetTranslation( entry.second.title ) == selectedString )
784 {
785 jobKey = entry.first;
786 break;
787 }
788 }
789 }
790
791 if( !jobKey.IsEmpty() )
792 {
793 int row = m_jobsFile->GetJobs().size();
794 bool wasDirty = m_jobsFile->GetDirty();
795 JOB* job = JOB_REGISTRY::CreateInstance<JOB>( jobKey );
796
797 m_jobsFile->AddNewJob( jobKey, job );
798
799 if( OpenJobOptionsForListItem( row ) )
800 {
802
803 m_jobsGrid->SetGridCursor( row, 2 );
804 m_jobsGrid->EnableCellEditControl();
805 }
806 else
807 {
808 m_jobsFile->RemoveJob( row );
809 m_jobsFile->SetDirty( wasDirty );
810 }
811 }
812 }
813}
814
815
816void PANEL_JOBSET::OnJobButtonDelete( wxCommandEvent& aEvent )
817{
818 m_jobsGrid->OnDeleteRows(
819 [&]( int row )
820 {
821 m_jobsFile->RemoveJob( row );
822 m_jobsGrid->DeleteRows( row, 1 );
823 } );
824}
825
826
827void PANEL_JOBSET::OnAddDestinationClick( wxCommandEvent& aEvent )
828{
829 wxArrayString headers;
830 std::vector<wxArrayString> items;
831
832 headers.Add( _( "Destination Types" ) );
833
834 for( const auto& [destinationType, destinationTypeInfo] : JobsetDestinationTypeInfos )
835 {
836 wxArrayString item;
837 item.Add( wxGetTranslation( destinationTypeInfo.name ) );
838 items.emplace_back( item );
839 }
840
841 EDA_LIST_DIALOG dlg( this, _( "Add New Destination" ), headers, items );
842 dlg.SetListLabel( _( "Select destination type:" ) );
843 dlg.HideFilter();
844
845 if( dlg.ShowModal() == wxID_OK )
846 {
847 wxString selectedString = dlg.GetTextSelection();
848
849 for( const auto& [destinationType, destinationTypeInfo] : JobsetDestinationTypeInfos )
850 {
851 if( wxGetTranslation( destinationTypeInfo.name ) == selectedString )
852 {
853 JOBSET_DESTINATION* destination = m_jobsFile->AddNewDestination( destinationType );
854
855 DIALOG_DESTINATION dialog( m_frame, m_jobsFile.get(), destination );
856
857 if (dialog.ShowModal() == wxID_OK)
858 {
859 Freeze();
860 addDestinationPanel( destination );
861 Thaw();
862 }
863 else
864 {
865 // canceled
866 m_jobsFile->RemoveDestination( destination );
867 }
868 }
869 }
870 }
871}
872
873
875{
876 if( m_jobsFile->GetDirty() )
877 {
878 wxFileName fileName = m_jobsFile->GetFullFilename();
879 wxString msg = _( "Save changes to '%s' before closing?" );
880
881 if( !HandleUnsavedChanges( this, wxString::Format( msg, fileName.GetFullName() ),
882 [&]() -> bool
883 {
884 return m_jobsFile->SaveToFile( wxEmptyString, true );
885 } ) )
886 {
887 return false;
888 }
889 }
890
891 return true;
892}
893
894
896{
897 PROJECT& project = m_frame->Kiway().Prj();
898 KIWAY_PLAYER* frame = m_frame->Kiway().Player( FRAME_PCB_EDITOR, false );
899
900 if( !frame )
901 {
902 frame = m_frame->Kiway().Player( FRAME_PCB_EDITOR, true );
903
904 // frame can be null if Cvpcb cannot be run. No need to show a warning
905 // Kiway() generates the error messages
906 if( !frame )
907 return;
908
909 wxFileName boardfn = project.GetProjectFullName();
910 boardfn.SetExt( FILEEXT::PcbFileExtension );
911
912 // Prevent our window from being closed during the open process
913 wxEventBlocker blocker( this );
914
915 frame->OpenProjectFiles( std::vector<wxString>( 1, boardfn.GetFullPath() ) );
916
917 if( !frame->IsVisible() )
918 frame->Show( true );
919 }
920
921 frame = m_frame->Kiway().Player( FRAME_SCH, false );
922
923 if( !frame )
924 {
925 frame = m_frame->Kiway().Player( FRAME_SCH, true );
926
927 // frame can be null if Cvpcb cannot be run. No need to show a warning
928 // Kiway() generates the error messages
929 if( !frame )
930 return;
931
932 wxFileName schFn = project.GetProjectFullName();
934
935 wxEventBlocker blocker( this );
936
937 frame->OpenProjectFiles( std::vector<wxString>( 1, schFn.GetFullPath() ) );
938
939 if( !frame->IsVisible() )
940 frame->Show( true );
941 }
942
943 SetFocus();
944}
945
946
948{
949 return m_jobsFile->GetFullFilename();
950}
951
952
953void PANEL_JOBSET::OnJobButtonUp( wxCommandEvent& aEvent )
954{
955 if( !m_jobsGrid->CommitPendingChanges() )
956 return;
957
958 int item = m_jobsGrid->GetGridCursorRow();
959
960 if( item > 0 )
961 {
962 m_jobsFile->MoveJobUp( item );
963
965
966 m_jobsGrid->SelectRow( item - 1 );
967 m_jobsGrid->SetGridCursor( item - 1, m_jobsGrid->GetGridCursorCol() );
968 }
969 else
970 {
971 wxBell();
972 }
973}
974
975
976void PANEL_JOBSET::OnJobButtonDown( wxCommandEvent& aEvent )
977{
978 if( !m_jobsGrid->CommitPendingChanges() )
979 return;
980
981 int item = m_jobsGrid->GetGridCursorRow();
982
983 if( item < m_jobsGrid->GetNumberRows() - 1 )
984 {
985 m_jobsFile->MoveJobDown( item );
986
988
989 m_jobsGrid->SelectRow( item + 1 );
990 m_jobsGrid->SetGridCursor( item + 1, m_jobsGrid->GetGridCursorCol() );
991 }
992 else
993 {
994 wxBell();
995 }
996}
997
998
1000{
1001 if( !m_jobsGrid->CommitPendingChanges() )
1002 return;
1003
1004 // sanity
1005 if( m_jobsFile->GetDestinations().empty() )
1006 {
1007 DisplayError( this, _( "No destinations defined" ) );
1008 return;
1009 }
1010
1011 for( PANEL_DESTINATION* panel : GetDestinationPanels() )
1012 panel->ClearStatus();
1013
1014 Refresh();
1015
1016 CallAfter(
1017 [this]()
1018 {
1019 PROJECT& project = m_frame->Kiway().Prj();
1021
1022 wxFileName fn = project.GetProjectFullName();
1023 wxSetWorkingDirectory( fn.GetPath() );
1024
1025 {
1026 JOBS_PROGRESS_REPORTER progressReporter( m_frame, _( "Running Jobs" ) );
1027 JOBS_RUNNER jobRunner( &m_frame->Kiway(), m_jobsFile.get(), &project,
1028 NULL_REPORTER::GetInstance(), &progressReporter );
1029
1030 jobRunner.RunJobsAllDestinations();
1031
1032 for( PANEL_DESTINATION* panel : GetDestinationPanels() )
1033 panel->UpdateStatus();
1034 }
1035
1036 // Bring the Kicad manager frame back to the front
1037 m_frame->Raise();
1038 } );
1039}
1040
1041
1042void PANEL_JOBSET::OnSizeGrid( wxSizeEvent& aEvent )
1043{
1044 m_jobsGrid->SetColSize( COL_DESCR, m_jobsGrid->GetSize().x - m_jobsGrid->GetColSize( COL_SOURCE )
1045 - m_jobsGrid->GetColSize( COL_NUMBER ) );
1046
1047 // Always propagate for a grid repaint (needed if the height changes, as well as width)
1048 aEvent.Skip();
1049}
1050
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
@ small_new_window
DIALOG_JOBSET_RUN_LOG_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Jobset Run Log"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
void OnJobListItemSelected(wxListEvent &event) override
DIALOG_JOBSET_RUN_LOG(wxWindow *aParent, JOBSET *aJobsFile, JOBSET_DESTINATION *aDestination)
JOBSET_DESTINATION * m_destination
virtual void OnUpdateUI(wxUpdateUIEvent &event) override
void OptOut(wxWindow *aWindow)
Opt out of control state saving.
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:309
PANEL_JOBSET * m_parent
void showPopupMenu(wxMenu &menu, wxGridEvent &aEvent) override
void doPopupSelection(wxCommandEvent &event) override
bool handleDoubleClick(wxGridEvent &aEvent) override
wxArrayInt m_selectedRows
JOBS_GRID_TRICKS(PANEL_JOBSET *aParent, WX_GRID *aGrid)
static wxString ResolveOutputPath(const wxString &aPath, const PROJECT *aProject)
Expand variables and a leading '~' in a destination path, then anchor a relative result at the projec...
bool RunJobsAllDestinations(bool aBail=false)
bool RunJobsForDestination(JOBSET_DESTINATION *aDestination, bool aBail=false)
static const REGISTRY_MAP_T & GetRegistry()
std::unordered_map< wxString, JOB_REGISTRY_ENTRY > REGISTRY_MAP_T
static T * CreateInstance(const wxString &aName)
static KIWAY::FACE_T GetKifaceType(const wxString &aName)
An simple container class that lets us dispatch output jobs to kifaces.
Definition job.h:184
const std::string & GetType() const
Definition job.h:195
The main KiCad project manager frame.
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
virtual bool OpenProjectFiles(const std::vector< wxString > &aFileList, int aCtl=0)
Open a project or set of files given by aFileList.
FACE_T
Known KIFACE implementations.
Definition kiway.h:354
@ KIWAY_FACE_COUNT
Definition kiway.h:361
@ FACE_SCH
eeschema DSO
Definition kiway.h:355
@ FACE_PCB
pcbnew DSO
Definition kiway.h:356
static REPORTER & GetInstance()
Definition reporter.cpp:207
wxStaticBitmap * m_statusBitmap
PANEL_DESTINATION_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxBORDER_SUNKEN|wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
STD_BITMAP_BUTTON * m_buttonProperties
STD_BITMAP_BUTTON * m_buttonDelete
wxStaticBitmap * m_bitmapOutputType
STD_BITMAP_BUTTON * m_buttonOpenOutput
wxStaticText * m_textOutputType
void onMenu(wxCommandEvent &aEvent)
JOBSET_DESTINATION * GetDestination()
void OnLastStatusClick(wxMouseEvent &aEvent) override
void UpdatePathInfo(const wxString &aMsg)
PANEL_DESTINATION(wxWindow *aParent, PANEL_JOBSET *aPanelParent, KICAD_MANAGER_FRAME *aFrame, JOBSET *aFile, JOBSET_DESTINATION *aDestination)
void OnOpenOutput(wxCommandEvent &aEvent) override
virtual void OnDelete(wxCommandEvent &aEvent) override
virtual void OnGenerate(wxCommandEvent &event) override
void OnRightDown(wxMouseEvent &aEvent) override
KICAD_MANAGER_FRAME * m_frame
void OnProperties(wxCommandEvent &aEvent) override
PANEL_JOBSET * m_panelParent
STD_BITMAP_BUTTON * m_buttonAddDestination
STD_BITMAP_BUTTON * m_buttonDown
PANEL_JOBSET_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
wxScrolledWindow * m_destinationList
wxBoxSizer * m_destinationListSizer
STD_BITMAP_BUTTON * m_buttonAddJob
STD_BITMAP_BUTTON * m_buttonDelete
STD_BITMAP_BUTTON * m_buttonUp
wxString GetFilePath() const
virtual void OnSaveButtonClick(wxCommandEvent &aEvent) override
void OnJobButtonDelete(wxCommandEvent &aEvent) override
virtual void OnJobButtonDown(wxCommandEvent &aEvent) override
void EnsurePcbSchFramesOpen()
bool GetCanClose() override
void rebuildJobList()
wxAuiNotebook * m_parentBook
void addDestinationPanel(JOBSET_DESTINATION *aDestination)
virtual void OnGenerateAllDestinationsClick(wxCommandEvent &event) override
bool OpenJobOptionsForListItem(size_t aItemIndex)
std::vector< PANEL_DESTINATION * > GetDestinationPanels()
void buildDestinationList()
KICAD_MANAGER_FRAME * m_frame
PANEL_JOBSET(wxAuiNotebook *aParent, KICAD_MANAGER_FRAME *aFrame, std::unique_ptr< JOBSET > aJobsFile)
virtual void OnAddDestinationClick(wxCommandEvent &aEvent) override
virtual void OnAddJobClick(wxCommandEvent &aEvent) override
std::unique_ptr< JOBSET > m_jobsFile
virtual void OnGridCellChange(wxGridEvent &aEvent) override
void RemoveDestination(PANEL_DESTINATION *aPanel)
virtual void OnJobButtonUp(wxCommandEvent &aEvent) override
virtual void OnSizeGrid(wxSizeEvent &aEvent) override
Container for project specific data.
Definition project.h:63
bool HandleUnsavedChanges(wxWindow *aParent, const wxString &aMessage, const std::function< bool()> &aSaveFunction)
Display a dialog with Save, Cancel and Discard Changes buttons.
Definition confirm.cpp:146
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:192
This file is part of the common library.
#define _(s)
@ FRAME_PCB_EDITOR
Definition frame_type.h:38
@ FRAME_SCH
Definition frame_type.h:30
@ GRIDTRICKS_ID_SELECT
Definition grid_tricks.h:42
@ GRIDTRICKS_ID_COPY
Definition grid_tricks.h:39
@ GRIDTRICKS_ID_DELETE
Definition grid_tricks.h:40
static const std::string KiCadSchematicFileExtension
Some functions to handle hotkeys in KiCad.
std::string source
KICOMMON_API std::map< JOBSET_DESTINATION_T, JOBSET_DESTINATION_T_INFO > JobsetDestinationTypeInfos
Definition jobset.cpp:43
#define KICOMMON_API
Definition kicommon.h:27
bool LaunchExternal(const wxString &aPath)
Launches the given file or folder in the host OS.
KICOMMON_API wxFont GetSmallInfoFont(wxWindow *aWindow)
STL namespace.
KICOMMON_API std::map< JOBSET_DESTINATION_T, JOBSET_DESTINATION_T_INFO > JobsetDestinationTypeInfos
Definition jobset.cpp:43
@ COL_NUMBER
@ COL_DESCR
@ COL_SOURCE
std::vector< FAB_LAYER_COLOR > dummy
JOBSET_DESTINATION_T m_type
Definition jobset.h:135
wxString GetPathInfo() const
Definition jobset.cpp:160
wxString GetDescription() const
Definition jobset.cpp:154
std::optional< wxString > m_lastResolvedOutputPath
Definition jobset.h:144
std::optional< bool > m_lastRunSuccess
Definition jobset.h:141
std::unordered_map< wxString, std::optional< bool > > m_lastRunSuccessMap
Definition jobset.h:142
std::shared_ptr< JOB > m_job
Definition jobset.h:91
wxString m_type
Definition jobset.h:89
Definition of file extensions used in Kicad.