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 The 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,
99 cellSelection && SELECTION_CONDITIONS::Idle, 100 );
100 selToolMenu.AddItem( ACTIONS::addRowBelow,
101 cellSelection && SELECTION_CONDITIONS::Idle, 100 );
102 selToolMenu.AddItem( ACTIONS::addColBefore,
103 cellSelection && SELECTION_CONDITIONS::Idle, 100 );
104 selToolMenu.AddItem( ACTIONS::addColAfter,
105 cellSelection && SELECTION_CONDITIONS::Idle, 100 );
106
107 selToolMenu.AddSeparator( 100 );
108 selToolMenu.AddItem( ACTIONS::deleteRows,
109 cellSelection && SELECTION_CONDITIONS::Idle, 100 );
110 selToolMenu.AddItem( ACTIONS::deleteColumns, cellSelection && SELECTION_CONDITIONS::Idle,
111 100 );
112
113 selToolMenu.AddSeparator( 100 );
114 selToolMenu.AddItem( ACTIONS::mergeCells, cellSelection && cellBlockSelection, 100 );
115 selToolMenu.AddItem( ACTIONS::unmergeCells, cellSelection && mergedCellsSelection, 100 );
116
117 selToolMenu.AddSeparator( 100 );
118 selToolMenu.AddItem( ACTIONS::editTable,
119 cellSelection && SELECTION_CONDITIONS::Idle, 100 );
120
121 selToolMenu.AddSeparator( 100 );
122 }
123
124 int doAddRowAbove( const TOOL_EVENT& aEvent )
125 {
126 const SELECTION& selection = getTableCellSelection();
127 T_TABLECELL* topmost = nullptr;
128
129 for( EDA_ITEM* item : selection )
130 {
131 T_TABLECELL* cell = static_cast<T_TABLECELL*>( item );
132
133 if( !topmost || cell->GetRow() < topmost->GetRow() )
134 topmost = cell;
135 }
136
137 if( !topmost )
138 return 0;
139
140 int row = topmost->GetRow();
141 T_TABLE* table = static_cast<T_TABLE*>( topmost->GetParent() );
142 T_COMMIT commit( getToolMgr() );
143 VECTOR2I pos = table->GetPosition();
144
145 // Make a copy of the source row before things start moving around
146 std::vector<T_TABLECELL*> sources;
147 sources.reserve( table->GetColCount() );
148
149 for( int col = 0; col < table->GetColCount(); ++col )
150 sources.push_back( table->GetCell( row, col ) );
151
152 commit.Modify( table, getScreen() );
153
154 for( int col = 0; col < table->GetColCount(); ++col )
155 {
156 T_TABLECELL* cell = copyCell( sources[col] );
157 table->InsertCell( row * table->GetColCount(), cell );
158 }
159
160 for( int afterRow = table->GetRowCount() - 1; afterRow > row; afterRow-- )
161 table->SetRowHeight( afterRow, table->GetRowHeight( afterRow - 1 ) );
162
163 table->SetPosition( pos );
164 table->Normalize();
165
167
168 commit.Push( _( "Add Row Above" ) );
169
170 return 0;
171 }
172
173 int doAddRowBelow( const TOOL_EVENT& aEvent )
174 {
175 const SELECTION& selection = getTableCellSelection();
176 T_TABLECELL* bottommost = nullptr;
177
178 if( selection.Empty() )
179 return 0;
180
181 for( EDA_ITEM* item : selection )
182 {
183 T_TABLECELL* cell = static_cast<T_TABLECELL*>( item );
184
185 if( !bottommost || cell->GetRow() > bottommost->GetRow() )
186 bottommost = cell;
187 }
188
189 if( !bottommost )
190 return 0;
191
192 int row = bottommost->GetRow();
193 T_TABLE* table = static_cast<T_TABLE*>( bottommost->GetParent() );
194 T_COMMIT commit( getToolMgr() );
195 VECTOR2I pos = table->GetPosition();
196
197 // Make a copy of the source row before things start moving around
198 std::vector<T_TABLECELL*> sources;
199 sources.reserve( table->GetColCount() );
200
201 for( int col = 0; col < table->GetColCount(); ++col )
202 sources.push_back( table->GetCell( row, col ) );
203
204 commit.Modify( table, getScreen() );
205
206 for( int col = 0; col < table->GetColCount(); ++col )
207 {
208 T_TABLECELL* cell = copyCell( sources[col] );
209 table->InsertCell( ( row + 1 ) * table->GetColCount(), cell );
210 }
211
212 for( int afterRow = table->GetRowCount() - 1; afterRow > row; afterRow-- )
213 table->SetRowHeight( afterRow, table->GetRowHeight( afterRow - 1 ) );
214
215 table->SetPosition( pos );
216 table->Normalize();
217
219
220 commit.Push( _( "Add Row Below" ) );
221
222 return 0;
223 }
224
225 int doAddColumnBefore( const TOOL_EVENT& aEvent )
226 {
227 const SELECTION& selection = getTableCellSelection();
228 T_TABLECELL* leftmost = nullptr;
229
230 for( EDA_ITEM* item : selection )
231 {
232 T_TABLECELL* cell = static_cast<T_TABLECELL*>( item );
233
234 if( !leftmost || cell->GetColumn() < leftmost->GetColumn() )
235 leftmost = cell;
236 }
237
238 if( !leftmost )
239 return 0;
240
241 int col = leftmost->GetColumn();
242 T_TABLE* table = static_cast<T_TABLE*>( leftmost->GetParent() );
243 int rowCount = table->GetRowCount();
244 T_COMMIT commit( getToolMgr() );
245 VECTOR2I pos = table->GetPosition();
246
247 // Make a copy of the source column before things start moving around
248 std::vector<T_TABLECELL*> sources;
249 sources.reserve( rowCount );
250
251 for( int row = 0; row < rowCount; ++row )
252 sources.push_back( table->GetCell( row, col ) );
253
254 commit.Modify( table, getScreen() );
255 table->SetColCount( table->GetColCount() + 1 );
256
257 for( int row = 0; row < rowCount; ++row )
258 {
259 T_TABLECELL* cell = copyCell( sources[row] );
260 table->InsertCell( row * table->GetColCount() + col, cell );
261 }
262
263 for( int afterCol = table->GetColCount() - 1; afterCol > col; afterCol-- )
264 table->SetColWidth( afterCol, table->GetColWidth( afterCol - 1 ) );
265
266 table->SetPosition( pos );
267 table->Normalize();
268
270
271 commit.Push( _( "Add Column Before" ) );
272
273 return 0;
274 }
275
276 int doAddColumnAfter( const TOOL_EVENT& aEvent )
277 {
278 const SELECTION& selection = getTableCellSelection();
279 T_TABLECELL* rightmost = nullptr;
280
281 for( EDA_ITEM* item : selection )
282 {
283 T_TABLECELL* cell = static_cast<T_TABLECELL*>( item );
284
285 if( !rightmost || cell->GetColumn() > rightmost->GetColumn() )
286 rightmost = cell;
287 }
288
289 if( !rightmost )
290 return 0;
291
292 int col = rightmost->GetColumn();
293 T_TABLE* table = static_cast<T_TABLE*>( rightmost->GetParent() );
294 int rowCount = table->GetRowCount();
295 T_COMMIT commit( getToolMgr() );
296 VECTOR2I pos = table->GetPosition();
297
298 // Make a copy of the source column before things start moving around
299 std::vector<T_TABLECELL*> sources;
300 sources.reserve( rowCount );
301
302 for( int row = 0; row < rowCount; ++row )
303 sources.push_back( table->GetCell( row, col ) );
304
305 commit.Modify( table, getScreen() );
306 table->SetColCount( table->GetColCount() + 1 );
307
308 for( int row = 0; row < rowCount; ++row )
309 {
310 T_TABLECELL* cell = copyCell( sources[row] );
311 table->InsertCell( row * table->GetColCount() + col + 1, cell );
312 }
313
314 for( int afterCol = table->GetColCount() - 1; afterCol > col; afterCol-- )
315 table->SetColWidth( afterCol, table->GetColWidth( afterCol - 1 ) );
316
317 table->SetPosition( pos );
318 table->Normalize();
319
321
322 commit.Push( _( "Add Column After" ) );
323
324 return 0;
325 }
326
327 int doDeleteRows( const TOOL_EVENT& aEvent )
328 {
329 const SELECTION& selection = getTableCellSelection();
330
331 if( selection.Empty() )
332 return 0;
333
334 T_TABLE* table = static_cast<T_TABLE*>( selection[0]->GetParent() );
335 std::vector<int> deleted;
336
337 for( T_TABLECELL* cell : table->GetCells() )
338 cell->ClearFlags( STRUCT_DELETED );
339
340 for( int row = 0; row < table->GetRowCount(); ++row )
341 {
342 bool deleteRow = false;
343
344 for( int col = 0; col < table->GetColCount(); ++col )
345 {
346 if( table->GetCell( row, col )->IsSelected() )
347 {
348 deleteRow = true;
349 break;
350 }
351 }
352
353 if( deleteRow )
354 {
355 for( int col = 0; col < table->GetColCount(); ++col )
356 table->GetCell( row, col )->SetFlags( STRUCT_DELETED );
357
358 deleted.push_back( row );
359 }
360 }
361
362 T_COMMIT commit( getToolMgr() );
363
364 if( deleted.size() == (unsigned) table->GetRowCount() )
365 {
366 commit.Remove( table );
367 }
368 else
369 {
370 commit.Modify( table, getScreen() );
371
372 VECTOR2I pos = table->GetPosition();
373
375 table->DeleteMarkedCells();
376
377 for( int row = 0; row < table->GetRowCount(); ++row )
378 {
379 int offset = 0;
380
381 for( int deletedRow : deleted )
382 {
383 if( deletedRow >= row )
384 offset++;
385 }
386
387 table->SetRowHeight( row, table->GetRowHeight( row + offset ) );
388 }
389
390 table->SetPosition( pos );
391 table->Normalize();
392
394 }
395
396 if( deleted.size() > 1 )
397 commit.Push( _( "Delete Rows" ) );
398 else
399 commit.Push( _( "Delete Row" ) );
400
401 return 0;
402 }
403
404 int doDeleteColumns( const TOOL_EVENT& aEvent )
405 {
406 const SELECTION& selection = getTableCellSelection();
407
408 if( selection.Empty() )
409 return 0;
410
411 T_TABLE* table = static_cast<T_TABLE*>( selection[0]->GetParent() );
412 std::vector<int> deleted;
413
414 for( T_TABLECELL* cell : table->GetCells() )
415 cell->ClearFlags( STRUCT_DELETED );
416
417 for( int col = 0; col < table->GetColCount(); ++col )
418 {
419 bool deleteColumn = false;
420
421 for( int row = 0; row < table->GetRowCount(); ++row )
422 {
423 if( table->GetCell( row, col )->IsSelected() )
424 {
425 deleteColumn = true;
426 break;
427 }
428 }
429
430 if( deleteColumn )
431 {
432 for( int row = 0; row < table->GetRowCount(); ++row )
433 table->GetCell( row, col )->SetFlags( STRUCT_DELETED );
434
435 deleted.push_back( col );
436 }
437 }
438
439 T_COMMIT commit( getToolMgr() );
440
441 if( deleted.size() == (unsigned) table->GetColCount() )
442 {
443 commit.Remove( table );
444 }
445 else
446 {
447 commit.Modify( table, getScreen() );
448
449 VECTOR2I pos = table->GetPosition();
450
452 table->DeleteMarkedCells();
453 table->SetColCount( table->GetColCount() - deleted.size() );
454
455 for( int col = 0; col < table->GetColCount(); ++col )
456 {
457 int offset = 0;
458
459 for( int deletedCol : deleted )
460 {
461 if( deletedCol >= col )
462 offset++;
463 }
464
465 table->SetColWidth( col, table->GetColWidth( col + offset ) );
466 }
467
468 table->SetPosition( pos );
469 table->Normalize();
470
472 }
473
474 if( deleted.size() > 1 )
475 commit.Push( _( "Delete Columns" ) );
476 else
477 commit.Push( _( "Delete Column" ) );
478
479 return 0;
480 }
481
482 int doMergeCells( const TOOL_EVENT& aEvent )
483 {
484 const SELECTION& sel = getTableCellSelection();
485
486 if( sel.Empty() )
487 return 0;
488
489 int colMin = std::numeric_limits<int>::max();
490 int colMax = 0;
491 int rowMin = std::numeric_limits<int>::max();
492 int rowMax = 0;
493
494 T_COMMIT commit( getToolMgr() );
495 T_TABLE* table = static_cast<T_TABLE*>( sel[0]->GetParent() );
496
497 for( EDA_ITEM* item : sel )
498 {
499 if( T_TABLECELL* cell = dynamic_cast<T_TABLECELL*>( item ) )
500 {
501 colMin = std::min( colMin, cell->GetColumn() );
502 colMax = std::max( colMax, cell->GetColumn() + cell->GetColSpan() );
503 rowMin = std::min( rowMin, cell->GetRow() );
504 rowMax = std::max( rowMax, cell->GetRow() + cell->GetRowSpan() );
505 }
506 }
507
508 wxString content;
509 VECTOR2I extents;
510
511 for( int row = rowMin; row < rowMax; ++row )
512 {
513 extents.y += table->GetRowHeight( row );
514 extents.x = 0;
515
516 for( int col = colMin; col < colMax; ++col )
517 {
518 extents.x += table->GetColWidth( col );
519
520 T_TABLECELL* cell = table->GetCell( row, col );
521
522 if( !cell->GetText().IsEmpty() )
523 {
524 if( !content.IsEmpty() )
525 content += "\n";
526
527 content += cell->GetText();
528 }
529
530 commit.Modify( cell, getScreen() );
531 cell->SetColSpan( 0 );
532 cell->SetRowSpan( 0 );
533 cell->SetText( wxEmptyString );
534 }
535 }
536
537 T_TABLECELL* topLeft = table->GetCell( rowMin, colMin );
538 topLeft->SetColSpan( colMax - colMin );
539 topLeft->SetRowSpan( rowMax - rowMin );
540 topLeft->SetText( content );
541 topLeft->SetEnd( topLeft->GetStart() + extents );
542
543 table->Normalize();
544 commit.Push( _( "Merge Cells" ) );
545
547
548 return 0;
549 }
550
551 int doUnmergeCells( const TOOL_EVENT& aEvent )
552 {
553 const SELECTION& sel = getTableCellSelection();
554
555 if( sel.Empty() )
556 return 0;
557
558 T_COMMIT commit( getToolMgr() );
559 T_TABLE* table = static_cast<T_TABLE*>( sel[0]->GetParent() );
560
561 for( EDA_ITEM* item : sel )
562 {
563 if( T_TABLECELL* cell = dynamic_cast<T_TABLECELL*>( item ) )
564 {
565 int rowSpan = cell->GetRowSpan();
566 int colSpan = cell->GetColSpan();
567
568 for( int row = cell->GetRow(); row < cell->GetRow() + rowSpan; ++row )
569 {
570 for( int col = cell->GetColumn(); col < cell->GetColumn() + colSpan; ++col )
571 {
572 T_TABLECELL* target = table->GetCell( row, col );
573 commit.Modify( target, getScreen() );
574 target->SetColSpan( 1 );
575 target->SetRowSpan( 1 );
576
577 VECTOR2I extents( table->GetColWidth( col ), table->GetRowHeight( row ) );
578 target->SetEnd( target->GetStart() + extents );
579 }
580 }
581 }
582 }
583
584 table->Normalize();
585 commit.Push( _( "Unmerge Cells" ) );
586
588
589 return 0;
590 }
591
592 virtual TOOL_MANAGER* getToolMgr() = 0;
593 virtual BASE_SCREEN* getScreen() = 0;
594
595 virtual const SELECTION& getTableCellSelection() = 0;
596 virtual void clearSelection() = 0;
597
598 virtual T_TABLECELL* copyCell( T_TABLECELL* aSource ) = 0;
599};
600
601#endif //EDIT_TABLE_TOOL_BASE_H
static TOOL_ACTION addRowAbove
Definition: actions.h:97
static TOOL_ACTION addColBefore
Definition: actions.h:99
static TOOL_ACTION deleteRows
Definition: actions.h:101
static TOOL_ACTION addRowBelow
Definition: actions.h:98
static TOOL_ACTION deleteColumns
Definition: actions.h:102
static TOOL_ACTION unmergeCells
Definition: actions.h:104
static TOOL_ACTION mergeCells
Definition: actions.h:103
static TOOL_ACTION addColAfter
Definition: actions.h:100
static TOOL_ACTION editTable
Definition: actions.h:105
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:89
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:292
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:110
Generic, UI-independent tool event.
Definition: tool_event.h:168
Master controller class:
Definition: tool_manager.h:62
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