KiCad PCB EDA Suite
Loading...
Searching...
No Matches
edit_table_tool_base.h
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) 2023-2024 KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#ifndef EDIT_TABLE_TOOL_BASE_H
25#define EDIT_TABLE_TOOL_BASE_H
26
27#include <tool/tool_base.h>
28#include <tool/selection.h>
29#include <tool/actions.h>
30#include <wx/translation.h>
31
32class BASE_SCREEN;
33
34
41template<typename T_TABLE, typename T_TABLECELL, typename T_COMMIT>
43{
44protected:
45 void addMenus( CONDITIONAL_MENU& selToolMenu )
46 {
47 auto cellSelection = SELECTION_CONDITIONS::MoreThan( 0 )
50
51 auto cellBlockSelection =
52 [&]( const SELECTION& sel )
53 {
54 if( sel.Size() < 2 )
55 return false;
56
57 int colMin = std::numeric_limits<int>::max();
58 int colMax = 0;
59 int rowMin = std::numeric_limits<int>::max();
60 int rowMax = 0;
61 int selectedArea = 0;
62
63 for( EDA_ITEM* item : sel )
64 {
65 if( T_TABLECELL* cell = dynamic_cast<T_TABLECELL*>( item ) )
66 {
67 colMin = std::min( colMin, cell->GetColumn() );
68 colMax = std::max( colMax, cell->GetColumn() + cell->GetColSpan() );
69 rowMin = std::min( rowMin, cell->GetRow() );
70 rowMax = std::max( rowMax, cell->GetRow() + cell->GetRowSpan() );
71
72 selectedArea += cell->GetColSpan() * cell->GetRowSpan();
73 }
74 }
75
76 return selectedArea == ( colMax - colMin ) * ( rowMax - rowMin );
77 };
78
79 auto mergedCellsSelection =
80 [&]( const SELECTION& sel )
81 {
82 for( EDA_ITEM* item : sel )
83 {
84 if( T_TABLECELL* cell = dynamic_cast<T_TABLECELL*>( item ) )
85 {
86 if( cell->GetColSpan() > 1 || cell->GetRowSpan() > 1 )
87 return true;
88 }
89 }
90
91 return false;
92 };
93
94 //
95 // Add editing actions to the selection tool menu
96 //
97 selToolMenu.AddSeparator( 100 );
98 selToolMenu.AddItem( ACTIONS::addRowAbove, cellSelection && SELECTION_CONDITIONS::Idle, 100 );
99 selToolMenu.AddItem( ACTIONS::addRowBelow, cellSelection && SELECTION_CONDITIONS::Idle, 100 );
100 selToolMenu.AddItem( ACTIONS::addColBefore, cellSelection && SELECTION_CONDITIONS::Idle, 100 );
101 selToolMenu.AddItem( ACTIONS::addColAfter, cellSelection && SELECTION_CONDITIONS::Idle, 100 );
102
103 selToolMenu.AddSeparator( 100 );
104 selToolMenu.AddItem( ACTIONS::deleteRows, cellSelection && SELECTION_CONDITIONS::Idle, 100 );
105 selToolMenu.AddItem( ACTIONS::deleteColumns, cellSelection && SELECTION_CONDITIONS::Idle, 100 );
106
107 selToolMenu.AddSeparator( 100 );
108 selToolMenu.AddItem( ACTIONS::mergeCells, cellSelection && cellBlockSelection, 100 );
109 selToolMenu.AddItem( ACTIONS::unmergeCells, cellSelection && mergedCellsSelection, 100 );
110
111 selToolMenu.AddSeparator( 100 );
112 selToolMenu.AddItem( ACTIONS::editTable, cellSelection && SELECTION_CONDITIONS::Idle, 100 );
113
114 selToolMenu.AddSeparator( 100 );
115 }
116
117 int doAddRowAbove( const TOOL_EVENT& aEvent )
118 {
119 const SELECTION& selection = getTableCellSelection();
120 T_TABLECELL* topmost = nullptr;
121
122 for( EDA_ITEM* item : selection )
123 {
124 T_TABLECELL* cell = static_cast<T_TABLECELL*>( item );
125
126 if( !topmost || cell->GetRow() < topmost->GetRow() )
127 topmost = cell;
128 }
129
130 if( !topmost )
131 return 0;
132
133 int row = topmost->GetRow();
134 T_TABLE* table = static_cast<T_TABLE*>( topmost->GetParent() );
135 T_COMMIT commit( getToolMgr() );
136 VECTOR2I pos = table->GetPosition();
137
138 // Make a copy of the source row before things start moving around
139 std::vector<T_TABLECELL*> sources;
140 sources.reserve( table->GetColCount() );
141
142 for( int col = 0; col < table->GetColCount(); ++col )
143 sources.push_back( table->GetCell( row, col ) );
144
145 commit.Modify( table, getScreen() );
146
147 for( int col = 0; col < table->GetColCount(); ++col )
148 {
149 T_TABLECELL* cell = copyCell( sources[col] );
150 table->InsertCell( row * table->GetColCount(), cell );
151 }
152
153 for( int afterRow = table->GetRowCount() - 1; afterRow > row; afterRow-- )
154 table->SetRowHeight( afterRow, table->GetRowHeight( afterRow - 1 ) );
155
156 table->SetPosition( pos );
157 table->Normalize();
158
160
161 commit.Push( _( "Add Row Above" ) );
162
163 return 0;
164 }
165
166 int doAddRowBelow( const TOOL_EVENT& aEvent )
167 {
168 const SELECTION& selection = getTableCellSelection();
169 T_TABLECELL* bottommost = nullptr;
170
171 if( selection.Empty() )
172 return 0;
173
174 for( EDA_ITEM* item : selection )
175 {
176 T_TABLECELL* cell = static_cast<T_TABLECELL*>( item );
177
178 if( !bottommost || cell->GetRow() > bottommost->GetRow() )
179 bottommost = cell;
180 }
181
182 if( !bottommost )
183 return 0;
184
185 int row = bottommost->GetRow();
186 T_TABLE* table = static_cast<T_TABLE*>( bottommost->GetParent() );
187 T_COMMIT commit( getToolMgr() );
188 VECTOR2I pos = table->GetPosition();
189
190 // Make a copy of the source row before things start moving around
191 std::vector<T_TABLECELL*> sources;
192 sources.reserve( table->GetColCount() );
193
194 for( int col = 0; col < table->GetColCount(); ++col )
195 sources.push_back( table->GetCell( row, col ) );
196
197 commit.Modify( table, getScreen() );
198
199 for( int col = 0; col < table->GetColCount(); ++col )
200 {
201 T_TABLECELL* cell = copyCell( sources[col] );
202 table->InsertCell( ( row + 1 ) * table->GetColCount(), cell );
203 }
204
205 for( int afterRow = table->GetRowCount() - 1; afterRow > row; afterRow-- )
206 table->SetRowHeight( afterRow, table->GetRowHeight( afterRow - 1 ) );
207
208 table->SetPosition( pos );
209 table->Normalize();
210
212
213 commit.Push( _( "Add Row Below" ) );
214
215 return 0;
216 }
217
218 int doAddColumnBefore( const TOOL_EVENT& aEvent )
219 {
220 const SELECTION& selection = getTableCellSelection();
221 T_TABLECELL* leftmost = nullptr;
222
223 for( EDA_ITEM* item : selection )
224 {
225 T_TABLECELL* cell = static_cast<T_TABLECELL*>( item );
226
227 if( !leftmost || cell->GetColumn() < leftmost->GetColumn() )
228 leftmost = cell;
229 }
230
231 if( !leftmost )
232 return 0;
233
234 int col = leftmost->GetColumn();
235 T_TABLE* table = static_cast<T_TABLE*>( leftmost->GetParent() );
236 int rowCount = table->GetRowCount();
237 T_COMMIT commit( getToolMgr() );
238 VECTOR2I pos = table->GetPosition();
239
240 // Make a copy of the source column before things start moving around
241 std::vector<T_TABLECELL*> sources;
242 sources.reserve( rowCount );
243
244 for( int row = 0; row < rowCount; ++row )
245 sources.push_back( table->GetCell( row, col ) );
246
247 commit.Modify( table, getScreen() );
248 table->SetColCount( table->GetColCount() + 1 );
249
250 for( int row = 0; row < rowCount; ++row )
251 {
252 T_TABLECELL* cell = copyCell( sources[row] );
253 table->InsertCell( row * table->GetColCount() + col, cell );
254 }
255
256 for( int afterCol = table->GetColCount() - 1; afterCol > col; afterCol-- )
257 table->SetColWidth( afterCol, table->GetColWidth( afterCol - 1 ) );
258
259 table->SetPosition( pos );
260 table->Normalize();
261
263
264 commit.Push( _( "Add Column Before" ) );
265
266 return 0;
267 }
268
269 int doAddColumnAfter( const TOOL_EVENT& aEvent )
270 {
271 const SELECTION& selection = getTableCellSelection();
272 T_TABLECELL* rightmost = nullptr;
273
274 for( EDA_ITEM* item : selection )
275 {
276 T_TABLECELL* cell = static_cast<T_TABLECELL*>( item );
277
278 if( !rightmost || cell->GetColumn() > rightmost->GetColumn() )
279 rightmost = cell;
280 }
281
282 if( !rightmost )
283 return 0;
284
285 int col = rightmost->GetColumn();
286 T_TABLE* table = static_cast<T_TABLE*>( rightmost->GetParent() );
287 int rowCount = table->GetRowCount();
288 T_COMMIT commit( getToolMgr() );
289 VECTOR2I pos = table->GetPosition();
290
291 // Make a copy of the source column before things start moving around
292 std::vector<T_TABLECELL*> sources;
293 sources.reserve( rowCount );
294
295 for( int row = 0; row < rowCount; ++row )
296 sources.push_back( table->GetCell( row, col ) );
297
298 commit.Modify( table, getScreen() );
299 table->SetColCount( table->GetColCount() + 1 );
300
301 for( int row = 0; row < rowCount; ++row )
302 {
303 T_TABLECELL* cell = copyCell( sources[row] );
304 table->InsertCell( row * table->GetColCount() + col + 1, cell );
305 }
306
307 for( int afterCol = table->GetColCount() - 1; afterCol > col; afterCol-- )
308 table->SetColWidth( afterCol, table->GetColWidth( afterCol - 1 ) );
309
310 table->SetPosition( pos );
311 table->Normalize();
312
314
315 commit.Push( _( "Add Column After" ) );
316
317 return 0;
318 }
319
320 int doDeleteRows( const TOOL_EVENT& aEvent )
321 {
322 const SELECTION& selection = getTableCellSelection();
323
324 if( selection.Empty() )
325 return 0;
326
327 T_TABLE* table = static_cast<T_TABLE*>( selection[0]->GetParent() );
328 std::vector<int> deleted;
329
330 for( T_TABLECELL* cell : table->GetCells() )
331 cell->ClearFlags( STRUCT_DELETED );
332
333 for( int row = 0; row < table->GetRowCount(); ++row )
334 {
335 bool deleteRow = false;
336
337 for( int col = 0; col < table->GetColCount(); ++col )
338 {
339 if( table->GetCell( row, col )->IsSelected() )
340 {
341 deleteRow = true;
342 break;
343 }
344 }
345
346 if( deleteRow )
347 {
348 for( int col = 0; col < table->GetColCount(); ++col )
349 table->GetCell( row, col )->SetFlags( STRUCT_DELETED );
350
351 deleted.push_back( row );
352 }
353 }
354
355 T_COMMIT commit( getToolMgr() );
356
357 if( deleted.size() == (unsigned) table->GetRowCount() )
358 {
359 commit.Remove( table );
360 }
361 else
362 {
363 commit.Modify( table, getScreen() );
364
365 VECTOR2I pos = table->GetPosition();
366
368 table->DeleteMarkedCells();
369
370 for( int row = 0; row < table->GetRowCount(); ++row )
371 {
372 int offset = 0;
373
374 for( int deletedRow : deleted )
375 {
376 if( deletedRow >= row )
377 offset++;
378 }
379
380 table->SetRowHeight( row, table->GetRowHeight( row + offset ) );
381 }
382
383 table->SetPosition( pos );
384 table->Normalize();
385
387 }
388
389 if( deleted.size() > 1 )
390 commit.Push( _( "Delete Rows" ) );
391 else
392 commit.Push( _( "Delete Row" ) );
393
394 return 0;
395 }
396
397 int doDeleteColumns( const TOOL_EVENT& aEvent )
398 {
399 const SELECTION& selection = getTableCellSelection();
400
401 if( selection.Empty() )
402 return 0;
403
404 T_TABLE* table = static_cast<T_TABLE*>( selection[0]->GetParent() );
405 std::vector<int> deleted;
406
407 for( T_TABLECELL* cell : table->GetCells() )
408 cell->ClearFlags( STRUCT_DELETED );
409
410 for( int col = 0; col < table->GetColCount(); ++col )
411 {
412 bool deleteColumn = false;
413
414 for( int row = 0; row < table->GetRowCount(); ++row )
415 {
416 if( table->GetCell( row, col )->IsSelected() )
417 {
418 deleteColumn = true;
419 break;
420 }
421 }
422
423 if( deleteColumn )
424 {
425 for( int row = 0; row < table->GetRowCount(); ++row )
426 table->GetCell( row, col )->SetFlags( STRUCT_DELETED );
427
428 deleted.push_back( col );
429 }
430 }
431
432 T_COMMIT commit( getToolMgr() );
433
434 if( deleted.size() == (unsigned) table->GetColCount() )
435 {
436 commit.Remove( table );
437 }
438 else
439 {
440 commit.Modify( table, getScreen() );
441
442 VECTOR2I pos = table->GetPosition();
443
445 table->DeleteMarkedCells();
446 table->SetColCount( table->GetColCount() - deleted.size() );
447
448 for( int col = 0; col < table->GetColCount(); ++col )
449 {
450 int offset = 0;
451
452 for( int deletedCol : deleted )
453 {
454 if( deletedCol >= col )
455 offset++;
456 }
457
458 table->SetColWidth( col, table->GetColWidth( col + offset ) );
459 }
460
461 table->SetPosition( pos );
462 table->Normalize();
463
465 }
466
467 if( deleted.size() > 1 )
468 commit.Push( _( "Delete Columns" ) );
469 else
470 commit.Push( _( "Delete Column" ) );
471
472 return 0;
473 }
474
475 int doMergeCells( const TOOL_EVENT& aEvent )
476 {
477 const SELECTION& sel = getTableCellSelection();
478
479 if( sel.Empty() )
480 return 0;
481
482 int colMin = std::numeric_limits<int>::max();
483 int colMax = 0;
484 int rowMin = std::numeric_limits<int>::max();
485 int rowMax = 0;
486
487 T_COMMIT commit( getToolMgr() );
488 T_TABLE* table = static_cast<T_TABLE*>( sel[0]->GetParent() );
489
490 for( EDA_ITEM* item : sel )
491 {
492 if( T_TABLECELL* cell = dynamic_cast<T_TABLECELL*>( item ) )
493 {
494 colMin = std::min( colMin, cell->GetColumn() );
495 colMax = std::max( colMax, cell->GetColumn() + cell->GetColSpan() );
496 rowMin = std::min( rowMin, cell->GetRow() );
497 rowMax = std::max( rowMax, cell->GetRow() + cell->GetRowSpan() );
498 }
499 }
500
501 wxString content;
502 VECTOR2I extents;
503
504 for( int row = rowMin; row < rowMax; ++row )
505 {
506 extents.y += table->GetRowHeight( row );
507 extents.x = 0;
508
509 for( int col = colMin; col < colMax; ++col )
510 {
511 extents.x += table->GetColWidth( col );
512
513 T_TABLECELL* cell = table->GetCell( row, col );
514
515 if( !cell->GetText().IsEmpty() )
516 {
517 if( !content.IsEmpty() )
518 content += "\n";
519
520 content += cell->GetText();
521 }
522
523 commit.Modify( cell, getScreen() );
524 cell->SetColSpan( 0 );
525 cell->SetRowSpan( 0 );
526 cell->SetText( wxEmptyString );
527 }
528 }
529
530 T_TABLECELL* topLeft = table->GetCell( rowMin, colMin );
531 topLeft->SetColSpan( colMax - colMin );
532 topLeft->SetRowSpan( rowMax - rowMin );
533 topLeft->SetText( content );
534 topLeft->SetEnd( topLeft->GetStart() + extents );
535
536 table->Normalize();
537 commit.Push( _( "Merge Cells" ) );
538
540
541 return 0;
542 }
543
544 int doUnmergeCells( const TOOL_EVENT& aEvent )
545 {
546 const SELECTION& sel = getTableCellSelection();
547
548 if( sel.Empty() )
549 return 0;
550
551 T_COMMIT commit( getToolMgr() );
552 T_TABLE* table = static_cast<T_TABLE*>( sel[0]->GetParent() );
553
554 for( EDA_ITEM* item : sel )
555 {
556 if( T_TABLECELL* cell = dynamic_cast<T_TABLECELL*>( item ) )
557 {
558 int rowSpan = cell->GetRowSpan();
559 int colSpan = cell->GetColSpan();
560
561 for( int row = cell->GetRow(); row < cell->GetRow() + rowSpan; ++row )
562 {
563 for( int col = cell->GetColumn(); col < cell->GetColumn() + colSpan; ++col )
564 {
565 T_TABLECELL* target = table->GetCell( row, col );
566 commit.Modify( target, getScreen() );
567 target->SetColSpan( 1 );
568 target->SetRowSpan( 1 );
569
570 VECTOR2I extents( table->GetColWidth( col ), table->GetRowHeight( row ) );
571 target->SetEnd( target->GetStart() + extents );
572 }
573 }
574 }
575 }
576
577 table->Normalize();
578 commit.Push( _( "Unmerge Cells" ) );
579
581
582 return 0;
583 }
584
585 virtual TOOL_MANAGER* getToolMgr() = 0;
586 virtual BASE_SCREEN* getScreen() = 0;
587
588 virtual const SELECTION& getTableCellSelection() = 0;
589 virtual void clearSelection() = 0;
590
591 virtual T_TABLECELL* copyCell( T_TABLECELL* aSource ) = 0;
592};
593
594#endif //EDIT_TABLE_TOOL_BASE_H
static TOOL_ACTION addRowAbove
Definition: actions.h:87
static TOOL_ACTION addColBefore
Definition: actions.h:89
static TOOL_ACTION deleteRows
Definition: actions.h:91
static TOOL_ACTION addRowBelow
Definition: actions.h:88
static TOOL_ACTION deleteColumns
Definition: actions.h:92
static TOOL_ACTION unmergeCells
Definition: actions.h:94
static TOOL_ACTION mergeCells
Definition: actions.h:93
static TOOL_ACTION addColAfter
Definition: actions.h:90
static TOOL_ACTION editTable
Definition: actions.h:95
Handles how to draw a screen (a board, a schematic ...)
Definition: base_screen.h:41
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
void AddSeparator(int aOrder=ANY_ORDER)
Add a separator to the menu.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:88
SCH_TABLE_EDIT_TOOL and PCB_TABLE_EDIT_TOOL share most of their algorithms, which are implemented her...
virtual TOOL_MANAGER * getToolMgr()=0
int doDeleteColumns(const TOOL_EVENT &aEvent)
int doAddColumnAfter(const TOOL_EVENT &aEvent)
int doAddRowBelow(const TOOL_EVENT &aEvent)
int doAddColumnBefore(const TOOL_EVENT &aEvent)
int doAddRowAbove(const TOOL_EVENT &aEvent)
int doMergeCells(const TOOL_EVENT &aEvent)
virtual const SELECTION & getTableCellSelection()=0
virtual BASE_SCREEN * getScreen()=0
int doDeleteRows(const TOOL_EVENT &aEvent)
virtual T_TABLECELL * copyCell(T_TABLECELL *aSource)=0
void addMenus(CONDITIONAL_MENU &selToolMenu)
virtual void clearSelection()=0
int doUnmergeCells(const TOOL_EVENT &aEvent)
static const TOOL_EVENT SelectedEvent
Definition: actions.h:260
static SELECTION_CONDITION MoreThan(int aNumber)
Create a functor that tests if the number of selected items is greater than the value given as parame...
static bool Idle(const SELECTION &aSelection)
Test if there no items selected or being edited.
static SELECTION_CONDITION OnlyTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if the selected items are only of given types.
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:109
Generic, UI-independent tool event.
Definition: tool_event.h:167
Master controller class:
Definition: tool_manager.h:57
void PostEvent(const TOOL_EVENT &aEvent)
Put an event to the event queue to be processed at the end of event processing cycle.
#define _(s)
#define STRUCT_DELETED
flag indication structures to be erased
@ SCH_TABLECELL_T
Definition: typeinfo.h:166
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition: typeinfo.h:95