1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/controls/gridtest.cpp
3 // Purpose: wxGrid unit test
4 // Author: Steven Lamerton
5 // Created: 2010-06-25
6 // Copyright: (c) 2010 Steven Lamerton
7 ///////////////////////////////////////////////////////////////////////////////
8
9 #include "testprec.h"
10
11 #if wxUSE_GRID
12
13
14 #ifndef WX_PRECOMP
15 #include "wx/app.h"
16 #include "wx/dcclient.h"
17 #endif // WX_PRECOMP
18
19 #include "wx/grid.h"
20 #include "wx/headerctrl.h"
21 #include "testableframe.h"
22 #include "asserthelper.h"
23 #include "wx/uiaction.h"
24
25 #ifdef __WXGTK__
26 #include "wx/stopwatch.h"
27 #endif // __WXGTK__
28
29 #include "waitforpaint.h"
30
31 namespace
32 {
33
CellCoordsToString(int row,int col)34 wxString CellCoordsToString(int row, int col)
35 {
36 return wxString::Format("R%dC%d", col + 1, row + 1);
37 }
38
CellSizeToString(int rows,int cols)39 wxString CellSizeToString(int rows, int cols)
40 {
41 return wxString::Format("%dx%d", rows, cols);
42 }
43
44 struct Multicell
45 {
46 int row, col, rows, cols;
47
Multicell__anonf7e7c6b40111::Multicell48 Multicell(int row, int col, int rows, int cols)
49 : row(row), col(col), rows(rows), cols(cols) { }
50
Coords__anonf7e7c6b40111::Multicell51 wxString Coords() const { return CellCoordsToString(row, col); }
52
Size__anonf7e7c6b40111::Multicell53 wxString Size() const { return CellSizeToString(rows, cols); }
54
ToString__anonf7e7c6b40111::Multicell55 wxString ToString() const
56 {
57 wxString s;
58
59 if ( rows == 1 && cols == 1 )
60 {
61 s = "cell";
62 }
63 else
64 {
65 s = Size() + " ";
66 if ( rows > 1 || cols > 1 )
67 s += "multicell";
68 else
69 s += "inside cell";
70 }
71
72 return wxString::Format("%s at %s", s, Coords());
73 }
74 };
75
76 // Stores insertion/deletion info of rows or columns.
77 struct EditInfo
78 {
79 int pos, count;
80 wxGridDirection direction;
81
EditInfo__anonf7e7c6b40111::EditInfo82 EditInfo(int pos = 0,
83 int count = 0,
84 wxGridDirection direction = wxGRID_COLUMN)
85 : pos(pos), count(count), direction(direction) { }
86 };
87
88 // Derive a new class inheriting from wxGrid, also to get access to its
89 // protected GetCellAttr(). This is not pretty, but we don't have any other way
90 // of testing this function.
91 class TestableGrid : public wxGrid
92 {
93 public:
TestableGrid(wxWindow * parent)94 explicit TestableGrid(wxWindow* parent)
95 : wxGrid(parent, wxID_ANY)
96 {
97 }
98
CallGetCellAttr(int row,int col) const99 wxGridCellAttr* CallGetCellAttr(int row, int col) const
100 {
101 return GetCellAttr(row, col);
102 }
103
HasAttr(int row,int col,wxGridCellAttr::wxAttrKind kind=wxGridCellAttr::Cell) const104 bool HasAttr(int row, int col,
105 wxGridCellAttr::wxAttrKind kind = wxGridCellAttr::Cell) const
106 {
107 // Can't use GetCellAttr() here as it always returns an attr.
108 wxGridCellAttr* attr = GetTable()->GetAttr(row, col, kind);
109 if ( attr )
110 attr->DecRef();
111
112 return attr != NULL;
113 }
114
GetCellAttrCount() const115 size_t GetCellAttrCount() const
116 {
117 // Note that only attributes in grid range can easily be checked
118 // and this function only counts those, not any outside of
119 // the grid (e.g. with invalid negative coords).
120 size_t count = 0;
121 for ( int row = 0; row < GetNumberRows(); ++row )
122 {
123 for ( int col = 0; col < GetNumberCols(); ++col )
124 count += HasAttr(row, col);
125 }
126
127 return count;
128 }
129
SetMulticell(const Multicell & multi)130 void SetMulticell(const Multicell& multi)
131 {
132 SetCellSize(multi.row, multi.col, multi.rows, multi.cols);
133 }
134
135 // Performs given insertions/deletions on either rows or columns.
136 void DoEdit(const EditInfo& edit);
137
138 // Returns annotated grid represented as a string.
139 wxString ToString() const;
140
141 // Used when drawing annotated grid to know what happens to it.
142 EditInfo m_edit;
143
144 // Grid as string before editing, with edit info annotated.
145 wxString m_beforeGridAnnotated;
146 };
147
148 // Compares two grids, checking for differences with attribute presence and
149 // cell sizes.
150 class GridAttrMatcher : public Catch::MatcherBase<TestableGrid>
151 {
152 public:
153 GridAttrMatcher(const TestableGrid& grid);
154
155 bool match(const TestableGrid& other) const wxOVERRIDE;
156
157 std::string describe() const wxOVERRIDE;
158
159 private:
160 const TestableGrid* m_grid;
161
162 mutable wxString m_diffDesc;
163 mutable wxString m_expectedGridDesc;
164 };
165
166 // Helper function for recreating a grid to fit (only) a multicell.
FitGridToMulticell(TestableGrid * grid,const Multicell & multi)167 void FitGridToMulticell(TestableGrid* grid, const Multicell& multi)
168 {
169 const int oldRowCount = grid->GetNumberRows();
170 const int oldColCount = grid->GetNumberCols();
171
172 const int margin = 1;
173 const int newRowCount = multi.row + multi.rows + margin;
174 const int newColCount = multi.col + multi.cols + margin;
175
176 if ( !oldRowCount && !oldColCount )
177 {
178 grid->CreateGrid(newRowCount, newColCount);
179 }
180 else
181 {
182 grid->DeleteRows(0, oldRowCount);
183 grid->DeleteCols(0, oldColCount);
184 grid->AppendRows(newRowCount);
185 grid->AppendCols(newColCount);
186 }
187 }
188
189 } // anonymous namespace
190
191 namespace Catch
192 {
193
194 template <> struct StringMaker<TestableGrid>
195 {
convertCatch::StringMaker196 static std::string convert(const TestableGrid& grid)
197 {
198 return ("Content before edit:\n" + grid.m_beforeGridAnnotated
199 + "\nContent after edit:\n" + grid.ToString()).ToStdString();
200 }
201 };
202
203 template <> struct StringMaker<Multicell>
204 {
convertCatch::StringMaker205 static std::string convert(const Multicell& multi)
206 {
207 return multi.ToString().ToStdString();
208 }
209 };
210
211 } // namespace Catch
212
213 class GridTestCase
214 {
215 public:
216 GridTestCase();
217 ~GridTestCase();
218
219 protected:
InsertRows(int pos=0,int count=1)220 void InsertRows(int pos = 0, int count = 1)
221 {
222 m_grid->DoEdit(EditInfo(pos, count, wxGRID_ROW));
223 }
224
InsertCols(int pos=0,int count=1)225 void InsertCols(int pos = 0, int count = 1)
226 {
227 m_grid->DoEdit(EditInfo(pos, count, wxGRID_COLUMN));
228 }
229
DeleteRows(int pos=0,int count=1)230 void DeleteRows(int pos = 0, int count = 1)
231 {
232 m_grid->DoEdit(EditInfo(pos, -count, wxGRID_ROW));
233 }
234
DeleteCols(int pos=0,int count=1)235 void DeleteCols(int pos = 0, int count = 1)
236 {
237 m_grid->DoEdit(EditInfo(pos, -count, wxGRID_COLUMN));
238 }
239
240 // The helper function to determine the width of the column label depending
241 // on whether the native column header is used.
GetColumnLabelWidth(wxClientDC & dc,int col,int margin) const242 int GetColumnLabelWidth(wxClientDC& dc, int col, int margin) const
243 {
244 if ( m_grid->IsUsingNativeHeader() )
245 return m_grid->GetGridColHeader()->GetColumnTitleWidth(col);
246
247 int w, h;
248 dc.GetMultiLineTextExtent(m_grid->GetColLabelValue(col), &w, &h);
249 return w + margin;
250 }
251
252 void CheckFirstColAutoSize(int expected);
253
254 // Helper to check that the selection is equal to the specified block.
CheckSelection(const wxGridBlockCoords & block)255 void CheckSelection(const wxGridBlockCoords& block)
256 {
257 const wxGridBlocks selected = m_grid->GetSelectedBlocks();
258 wxGridBlocks::iterator it = selected.begin();
259
260 REQUIRE( it != selected.end() );
261 CHECK( *it == block );
262 CHECK( ++it == selected.end() );
263 }
264
265 // Or specified ranges.
266 struct RowRange
267 {
RowRangeGridTestCase::RowRange268 RowRange(int top, int bottom) : top(top), bottom(bottom) { }
269
270 int top, bottom;
271 };
272
273 typedef wxVector<RowRange> RowRanges;
274
CheckRowSelection(const RowRanges & ranges)275 void CheckRowSelection(const RowRanges& ranges)
276 {
277 const wxGridBlockCoordsVector sel = m_grid->GetSelectedRowBlocks();
278 REQUIRE( sel.size() == ranges.size() );
279
280 for ( size_t n = 0; n < sel.size(); ++n )
281 {
282 INFO("n = " << n);
283
284 const RowRange& r = ranges[n];
285 CHECK( sel[n] == wxGridBlockCoords(r.top, 0, r.bottom, 1) );
286 }
287 }
288
HasCellAttr(int row,int col) const289 bool HasCellAttr(int row, int col) const
290 {
291 return m_grid->HasAttr(row, col, wxGridCellAttr::Cell);
292 }
293
SetCellAttr(int row,int col)294 void SetCellAttr(int row, int col)
295 {
296 m_grid->SetAttr(row, col, new wxGridCellAttr);
297 }
298
299 // Fills temp. grid with a multicell and returns a matcher with it.
HasMulticellOnly(const Multicell & multi)300 GridAttrMatcher HasMulticellOnly(const Multicell& multi)
301 {
302 return CheckMulticell(multi);
303 }
304
305 // Returns a matcher with empty (temp.) grid.
HasEmptyGrid()306 GridAttrMatcher HasEmptyGrid()
307 {
308 return CheckMulticell(Multicell(0, 0, 1, 1));
309 }
310
311 // Helper function used by the previous two functions.
CheckMulticell(const Multicell & multi)312 GridAttrMatcher CheckMulticell(const Multicell& multi)
313 {
314 TestableGrid* grid = GetTempGrid();
315
316 FitGridToMulticell(grid, multi);
317
318 if ( multi.rows > 1 || multi.cols > 1 )
319 grid->SetMulticell(multi);
320
321 return GridAttrMatcher(*grid);
322 }
323
GetTempGrid()324 TestableGrid* GetTempGrid()
325 {
326 if ( !m_tempGrid )
327 {
328 m_tempGrid = new TestableGrid(wxTheApp->GetTopWindow());
329 m_tempGrid->Hide();
330 }
331
332 return m_tempGrid;
333 }
334
335 TestableGrid *m_grid;
336
337 // Temporary/scratch grid filled with expected content, used when
338 // comparing against m_grid.
339 TestableGrid *m_tempGrid;
340
341 wxDECLARE_NO_COPY_CLASS(GridTestCase);
342 };
343
GridTestCase()344 GridTestCase::GridTestCase() : m_tempGrid(NULL)
345 {
346 m_grid = new TestableGrid(wxTheApp->GetTopWindow());
347 m_grid->CreateGrid(10, 2);
348 m_grid->SetSize(400, 200);
349
350 WaitForPaint waitForPaint(m_grid->GetGridWindow());
351
352 m_grid->Refresh();
353 m_grid->Update();
354
355 waitForPaint.YieldUntilPainted();
356 }
357
~GridTestCase()358 GridTestCase::~GridTestCase()
359 {
360 // This is just a hack to continue the rest of the tests to run: if we
361 // destroy the header control while it has capture, this results in an
362 // assert failure and while handling an exception from it more bad things
363 // happen (as it's thrown from a dtor), resulting in simply aborting
364 // everything. So ensure that it doesn't have capture in any case.
365 //
366 // Of course, the right thing to do would be to understand why does it
367 // still have capture when the grid is destroyed sometimes.
368 wxWindow* const win = wxWindow::GetCapture();
369 if ( win )
370 win->ReleaseMouse();
371
372 wxDELETE(m_grid);
373 delete m_tempGrid;
374 }
375
376 TEST_CASE_METHOD(GridTestCase, "Grid::CellEdit", "[grid]")
377 {
378 // TODO on OSX when running the grid test suite solo this works
379 // but not when running it together with other tests
380 #if wxUSE_UIACTIONSIMULATOR && !defined(__WXOSX__)
381 if ( !EnableUITests() )
382 return;
383
384 EventCounter changing(m_grid, wxEVT_GRID_CELL_CHANGING);
385 EventCounter changed(m_grid, wxEVT_GRID_CELL_CHANGED);
386 EventCounter created(m_grid, wxEVT_GRID_EDITOR_CREATED);
387
388 wxUIActionSimulator sim;
389
390 m_grid->SetFocus();
391 m_grid->SetGridCursor(1, 1);
392
393 wxYield();
394
395 sim.Text("abab");
396
397 wxYield();
398
399 sim.Char(WXK_RETURN);
400
401 wxYield();
402
403 CHECK(m_grid->GetCellValue(1, 1) == "abab");
404
405 CHECK(created.GetCount() == 1);
406 CHECK(changing.GetCount() == 1);
407 CHECK(changed.GetCount() == 1);
408 #endif
409 }
410
411 TEST_CASE_METHOD(GridTestCase, "Grid::CellClick", "[grid]")
412 {
413 #if wxUSE_UIACTIONSIMULATOR
414 EventCounter lclick(m_grid, wxEVT_GRID_CELL_LEFT_CLICK);
415 EventCounter ldclick(m_grid, wxEVT_GRID_CELL_LEFT_DCLICK);
416 EventCounter rclick(m_grid, wxEVT_GRID_CELL_RIGHT_CLICK);
417 EventCounter rdclick(m_grid, wxEVT_GRID_CELL_RIGHT_DCLICK);
418
419
420 wxUIActionSimulator sim;
421
422 wxRect rect = m_grid->CellToRect(0, 0);
423 wxPoint point = m_grid->CalcScrolledPosition(rect.GetPosition());
424 point = m_grid->ClientToScreen(point + wxPoint(m_grid->GetRowLabelSize(),
425 m_grid->GetColLabelSize())
426 + wxPoint(2, 2));
427
428 sim.MouseMove(point);
429 wxYield();
430
431 sim.MouseClick();
432 wxYield();
433
434 CHECK(lclick.GetCount() == 1);
435 lclick.Clear();
436
437 sim.MouseDblClick();
438 wxYield();
439
440 //A double click event sends a single click event first
441 //test to ensure this still happens in the future
442 CHECK(lclick.GetCount() == 1);
443 CHECK(ldclick.GetCount() == 1);
444
445 sim.MouseClick(wxMOUSE_BTN_RIGHT);
446 wxYield();
447
448 CHECK(rclick.GetCount() == 1);
449 rclick.Clear();
450
451 sim.MouseDblClick(wxMOUSE_BTN_RIGHT);
452 wxYield();
453
454 CHECK(rclick.GetCount() == 1);
455 CHECK(rdclick.GetCount() == 1);
456 #endif
457 }
458
459 TEST_CASE_METHOD(GridTestCase, "Grid::ReorderedColumnsCellClick", "[grid]")
460 {
461 #if wxUSE_UIACTIONSIMULATOR
462 EventCounter click(m_grid, wxEVT_GRID_CELL_LEFT_CLICK);
463
464 wxUIActionSimulator sim;
465
466 wxArrayInt neworder;
467 neworder.push_back(1);
468 neworder.push_back(0);
469
470 m_grid->SetColumnsOrder(neworder);
471
472 wxRect rect = m_grid->CellToRect(0, 1);
473 wxPoint point = m_grid->CalcScrolledPosition(rect.GetPosition());
474 point = m_grid->ClientToScreen(point + wxPoint(m_grid->GetRowLabelSize(),
475 m_grid->GetColLabelSize())
476 + wxPoint(2, 2));
477
478 sim.MouseMove(point);
479 wxYield();
480
481 sim.MouseClick();
482 wxYield();
483
484 CHECK(click.GetCount() == 1);
485 #endif
486 }
487
488 TEST_CASE_METHOD(GridTestCase, "Grid::CellSelect", "[grid]")
489 {
490 #if wxUSE_UIACTIONSIMULATOR
491 EventCounter cell(m_grid, wxEVT_GRID_SELECT_CELL);
492
493 wxUIActionSimulator sim;
494
495 wxRect rect = m_grid->CellToRect(0, 0);
496 wxPoint point = m_grid->CalcScrolledPosition(rect.GetPosition());
497 point = m_grid->ClientToScreen(point + wxPoint(m_grid->GetRowLabelSize(),
498 m_grid->GetColLabelSize())
499 + wxPoint(4, 4));
500
501 sim.MouseMove(point);
502 wxYield();
503
504 sim.MouseClick();
505 wxYield();
506
507 CHECK(cell.GetCount() == 1);
508
509 cell.Clear();
510
511 m_grid->SetGridCursor(1, 1);
512 m_grid->GoToCell(1, 0);
513
514 sim.MouseMove(point);
515 wxYield();
516
517 sim.MouseDblClick();
518 wxYield();
519
520 CHECK(cell.GetCount() == 3);
521 #endif
522 }
523
524 TEST_CASE_METHOD(GridTestCase, "Grid::LabelClick", "[grid]")
525 {
526 #if wxUSE_UIACTIONSIMULATOR
527 if ( !EnableUITests() )
528 return;
529
530 SECTION("Default") {}
531 SECTION("Native header") { m_grid->UseNativeColHeader(); }
532 SECTION("Native labels") { m_grid->SetUseNativeColLabels(); }
533
534 EventCounter lclick(m_grid, wxEVT_GRID_LABEL_LEFT_CLICK);
535 EventCounter ldclick(m_grid, wxEVT_GRID_LABEL_LEFT_DCLICK);
536 EventCounter rclick(m_grid, wxEVT_GRID_LABEL_RIGHT_CLICK);
537 EventCounter rdclick(m_grid, wxEVT_GRID_LABEL_RIGHT_DCLICK);
538
539 wxUIActionSimulator sim;
540
541 wxPoint pos(m_grid->GetRowLabelSize() + 2, 2);
542 pos = m_grid->ClientToScreen(pos);
543
544 sim.MouseMove(pos);
545 wxYield();
546
547 sim.MouseClick();
548 wxYield();
549
550 CHECK(lclick.GetCount() == 1);
551
552 sim.MouseDblClick();
553 wxYield();
554
555 CHECK(ldclick.GetCount() == 1);
556
557 sim.MouseClick(wxMOUSE_BTN_RIGHT);
558 wxYield();
559
560 CHECK(rclick.GetCount() == 1);
561 rclick.Clear();
562
563 sim.MouseDblClick(wxMOUSE_BTN_RIGHT);
564 wxYield();
565
566 if ( m_grid->IsUsingNativeHeader() )
567 {
568 //Right double click not supported with native headers so we get two
569 //right click events
570 CHECK(rclick.GetCount() == 2);
571 }
572 else
573 {
574 CHECK(rclick.GetCount() == 1);
575 CHECK(rdclick.GetCount() == 1);
576 }
577 #endif
578 }
579
580 TEST_CASE_METHOD(GridTestCase, "Grid::SortClick", "[grid]")
581 {
582 #if wxUSE_UIACTIONSIMULATOR
583 if ( !EnableUITests() )
584 return;
585
586 SECTION("Default") {}
587 SECTION("Native header") { m_grid->UseNativeColHeader(); }
588 SECTION("Native labels") { m_grid->SetUseNativeColLabels(); }
589
590 m_grid->SetSortingColumn(0);
591
592 EventCounter sort(m_grid, wxEVT_GRID_COL_SORT);
593
594 wxUIActionSimulator sim;
595
596 wxPoint pos(m_grid->GetRowLabelSize() + 4, 4);
597 pos = m_grid->ClientToScreen(pos);
598
599 sim.MouseMove(pos);
600 wxYield();
601
602 sim.MouseClick();
603 wxYield();
604
605 CHECK(sort.GetCount() == 1);
606 #endif
607 }
608
609 TEST_CASE_METHOD(GridTestCase, "Grid::Size", "[grid]")
610 {
611 // TODO on OSX resizing interactively works, but not automated
612 // Grid could not pass the test under GTK, OSX, and Universal.
613 // So there may has bug in Grid implementation
614 #if wxUSE_UIACTIONSIMULATOR && !defined(__WXOSX__) && !defined(__WXUNIVERSAL__)
615 if ( !EnableUITests() )
616 return;
617
618 #ifdef __WXGTK20__
619 // Works locally, but not when run on Travis CI.
620 if ( IsAutomaticTest() )
621 return;
622 #endif
623
624 EventCounter colsize(m_grid, wxEVT_GRID_COL_SIZE);
625 EventCounter rowsize(m_grid, wxEVT_GRID_ROW_SIZE);
626
627 wxUIActionSimulator sim;
628
629 wxPoint pt = m_grid->ClientToScreen(wxPoint(m_grid->GetRowLabelSize() +
630 m_grid->GetColSize(0), 5));
631 sim.MouseMove(pt);
632 wxYield();
633
634 sim.MouseDown();
635 wxYield();
636
637 sim.MouseMove(pt.x + 50, pt.y);
638 wxYield();
639
640 sim.MouseUp();
641 wxYield();
642
643 CHECK(colsize.GetCount() == 1);
644
645 pt = m_grid->ClientToScreen(wxPoint(5, m_grid->GetColLabelSize() +
646 m_grid->GetRowSize(0)));
647
648 sim.MouseDragDrop(pt.x, pt.y, pt.x, pt.y + 50);
649
650 wxYield();
651
652 CHECK(rowsize.GetCount() == 1);
653 #endif
654 }
655
656 TEST_CASE_METHOD(GridTestCase, "Grid::RangeSelect", "[grid]")
657 {
658 #if wxUSE_UIACTIONSIMULATOR
659 if ( !EnableUITests() )
660 return;
661
662 EventCounter select(m_grid, wxEVT_GRID_RANGE_SELECTED);
663
664 wxUIActionSimulator sim;
665
666 //We add the extra 10 to ensure that we are inside the cell
667 wxPoint pt = m_grid->ClientToScreen(wxPoint(m_grid->GetRowLabelSize() + 10,
668 m_grid->GetColLabelSize() + 10)
669 );
670
671 sim.MouseMove(pt);
672 wxYield();
673
674 sim.MouseDown();
675 wxYield();
676
677 sim.MouseMove(pt.x + 50, pt.y + 50);
678 wxYield();
679
680 sim.MouseUp();
681 wxYield();
682
683 CHECK(select.GetCount() == 1);
684 #endif
685 }
686
687 TEST_CASE_METHOD(GridTestCase, "Grid::Cursor", "[grid]")
688 {
689 m_grid->SetGridCursor(1, 1);
690
691 CHECK(m_grid->GetGridCursorCol() == 1);
692 CHECK(m_grid->GetGridCursorRow() == 1);
693
694 m_grid->MoveCursorDown(false);
695 m_grid->MoveCursorLeft(false);
696 m_grid->MoveCursorUp(false);
697 m_grid->MoveCursorUp(false);
698 m_grid->MoveCursorRight(false);
699
700 CHECK(m_grid->GetGridCursorCol() == 1);
701 CHECK(m_grid->GetGridCursorRow() == 0);
702
703 m_grid->SetCellValue(0, 0, "some text");
704 m_grid->SetCellValue(3, 0, "other text");
705 m_grid->SetCellValue(0, 1, "more text");
706 m_grid->SetCellValue(3, 1, "extra text");
707
708 m_grid->Update();
709 m_grid->Refresh();
710
711 m_grid->MoveCursorLeftBlock(false);
712
713 CHECK(m_grid->GetGridCursorCol() == 0);
714 CHECK(m_grid->GetGridCursorRow() == 0);
715
716 m_grid->MoveCursorDownBlock(false);
717
718 CHECK(m_grid->GetGridCursorCol() == 0);
719 CHECK(m_grid->GetGridCursorRow() == 3);
720
721 m_grid->MoveCursorRightBlock(false);
722
723 CHECK(m_grid->GetGridCursorCol() == 1);
724 CHECK(m_grid->GetGridCursorRow() == 3);
725
726 m_grid->MoveCursorUpBlock(false);
727
728 CHECK(m_grid->GetGridCursorCol() == 1);
729 CHECK(m_grid->GetGridCursorRow() == 0);
730 }
731
732 TEST_CASE_METHOD(GridTestCase, "Grid::KeyboardSelection", "[grid][selection]")
733 {
734 m_grid->SetCellValue(1, 1, "R2C2");
735 m_grid->SetCellValue(2, 0, "R3C1");
736 m_grid->SetCellValue(3, 0, "R4C1");
737 m_grid->SetCellValue(4, 0, "R5C1");
738 m_grid->SetCellValue(7, 0, "R8C1");
739
740 CHECK(m_grid->GetGridCursorCoords() == wxGridCellCoords(0, 0));
741
742 m_grid->MoveCursorRight(true);
743 CheckSelection(wxGridBlockCoords(0, 0, 0, 1));
744
745 m_grid->MoveCursorDownBlock(true);
746 CheckSelection(wxGridBlockCoords(0, 0, 2, 1));
747
748 m_grid->MoveCursorDownBlock(true);
749 CheckSelection(wxGridBlockCoords(0, 0, 4, 1));
750
751 m_grid->MoveCursorDownBlock(true);
752 CheckSelection(wxGridBlockCoords(0, 0, 7, 1));
753
754 m_grid->MoveCursorUpBlock(true);
755 CheckSelection(wxGridBlockCoords(0, 0, 4, 1));
756
757 m_grid->MoveCursorLeft(true);
758 CheckSelection(wxGridBlockCoords(0, 0, 4, 0));
759 }
760
761 TEST_CASE_METHOD(GridTestCase, "Grid::Selection", "[grid]")
762 {
763 m_grid->SelectAll();
764
765 CHECK(m_grid->IsSelection());
766 CHECK(m_grid->IsInSelection(0, 0));
767 CHECK(m_grid->IsInSelection(9, 1));
768
769 m_grid->SelectBlock(1, 0, 3, 1);
770
771 wxGridCellCoordsArray topleft = m_grid->GetSelectionBlockTopLeft();
772 wxGridCellCoordsArray bottomright = m_grid->GetSelectionBlockBottomRight();
773
774 CHECK(topleft.Count() == 1);
775 CHECK(bottomright.Count() == 1);
776
777 CHECK(topleft.Item(0).GetCol() == 0);
778 CHECK(topleft.Item(0).GetRow() == 1);
779 CHECK(bottomright.Item(0).GetCol() == 1);
780 CHECK(bottomright.Item(0).GetRow() == 3);
781
782 m_grid->SelectCol(1);
783
784 CHECK(m_grid->IsInSelection(0, 1));
785 CHECK(m_grid->IsInSelection(9, 1));
786 CHECK(!m_grid->IsInSelection(3, 0));
787
788 m_grid->SelectRow(4, true /* add to selection */);
789
790 CHECK(m_grid->IsInSelection(4, 0));
791 CHECK(m_grid->IsInSelection(4, 1));
792 CHECK(!m_grid->IsInSelection(3, 0));
793
794 // Check that deselecting a row does deselect the cells in it, but leaves
795 // the other ones selected.
796 m_grid->DeselectRow(4);
797 CHECK(!m_grid->IsInSelection(4, 0));
798 CHECK(!m_grid->IsInSelection(4, 1));
799 CHECK(m_grid->IsInSelection(0, 1));
800
801 m_grid->DeselectCol(1);
802 CHECK(!m_grid->IsInSelection(0, 1));
803 }
804
805 TEST_CASE_METHOD(GridTestCase, "Grid::SelectionRange", "[grid]")
806 {
807 const wxGridBlocks empty = m_grid->GetSelectedBlocks();
808 CHECK( empty.begin() == empty.end() );
809
810 m_grid->SelectBlock(1, 0, 3, 1);
811
812 wxGridBlocks sel = m_grid->GetSelectedBlocks();
813 REQUIRE( sel.begin() != sel.end() );
814 CHECK( *sel.begin() == wxGridBlockCoords(1, 0, 3, 1) );
815
816 #if __cplusplus >= 201103L || wxCHECK_VISUALC_VERSION(11)
817 m_grid->SelectBlock(4, 0, 7, 1, true);
818 int index = 0;
819 for ( const wxGridBlockCoords& block : m_grid->GetSelectedBlocks() )
820 {
821 switch ( index )
822 {
823 case 0:
824 CHECK(block == wxGridBlockCoords(1, 0, 3, 1));
825 break;
826 case 1:
827 CHECK(block == wxGridBlockCoords(4, 0, 7, 1));
828 break;
829 default:
830 FAIL("Unexpected iterations count");
831 break;
832 }
833 ++index;
834 }
835 #endif
836 }
837
838 TEST_CASE_METHOD(GridTestCase, "Grid::SelectEmptyGrid", "[grid]")
839 {
840 for ( int i = 0; i < 2; ++i )
841 {
842 SECTION(i == 0 ? "No rows" : "No columns")
843 {
844
845 if ( i == 0 )
846 {
847 m_grid->DeleteRows(0, 10);
848 REQUIRE( m_grid->GetNumberRows() == 0 );
849 }
850 else
851 {
852 m_grid->DeleteCols(0, 2);
853 REQUIRE( m_grid->GetNumberCols() == 0 );
854 }
855
856 SECTION("Move right")
857 {
858 m_grid->MoveCursorRight(true);
859 }
860 SECTION("Move down")
861 {
862 m_grid->MoveCursorDown(true);
863 }
864 SECTION("Select row")
865 {
866 m_grid->SelectRow(1);
867 }
868 SECTION("Select column")
869 {
870 m_grid->SelectCol(1);
871 }
872 }
873 }
874
875 CHECK( m_grid->GetSelectedCells().Count() == 0 );
876 CHECK( m_grid->GetSelectionBlockTopLeft().Count() == 0 );
877 CHECK( m_grid->GetSelectionBlockBottomRight().Count() == 0 );
878 CHECK( m_grid->GetSelectedRows().Count() == 0 );
879 CHECK( m_grid->GetSelectedCols().Count() == 0 );
880 }
881
882 TEST_CASE_METHOD(GridTestCase, "Grid::ScrollWhenSelect", "[grid]")
883 {
884 m_grid->AppendCols(10);
885
886 REQUIRE( m_grid->GetGridCursorCol() == 0 );
887 REQUIRE( m_grid->GetGridCursorRow() == 0 );
888 REQUIRE( m_grid->IsVisible(0, 0) );
889 REQUIRE( !m_grid->IsVisible(0, 4) );
890
891 for ( int i = 0; i < 4; ++i )
892 {
893 m_grid->MoveCursorRight(true);
894 }
895 CHECK( m_grid->IsVisible(0, 4) );
896
897 m_grid->ClearSelection();
898 m_grid->GoToCell(1, 1);
899 for ( int i = 0; i < 8; ++i )
900 {
901 m_grid->MoveCursorDown(true);
902 }
903 CHECK( m_grid->IsVisible(9, 1) );
904 }
905
906 TEST_CASE_METHOD(GridTestCase, "Grid::MoveGridCursorUsingEndKey", "[grid]")
907 {
908 #if wxUSE_UIACTIONSIMULATOR
909 if ( !EnableUITests() )
910 return;
911
912 wxUIActionSimulator sim;
913
914 m_grid->AppendCols(10);
915
916 REQUIRE( m_grid->GetGridCursorCol() == 0 );
917 REQUIRE( m_grid->GetGridCursorRow() == 0 );
918 REQUIRE( m_grid->IsVisible(0, 0) );
919
920 // Hide the last row.
921 m_grid->HideRow(9);
922 // Hide the last column.
923 m_grid->HideCol(11);
924 // Move the penult column.
925 m_grid->SetColPos(10, 5);
926
927 m_grid->SetFocus();
928
929 sim.KeyDown(WXK_END, wxMOD_CONTROL);
930 sim.KeyUp(WXK_END, wxMOD_CONTROL);
931 wxYield();
932
933 CHECK( m_grid->GetGridCursorRow() == 8 );
934 CHECK( m_grid->GetGridCursorCol() == 9 );
935 CHECK( m_grid->IsVisible(8, 9) );
936 #endif
937 }
938
939 TEST_CASE_METHOD(GridTestCase, "Grid::SelectUsingEndKey", "[grid]")
940 {
941 #if wxUSE_UIACTIONSIMULATOR
942 if ( !EnableUITests() )
943 return;
944
945 wxUIActionSimulator sim;
946
947 m_grid->AppendCols(10);
948
949 REQUIRE( m_grid->GetGridCursorCol() == 0 );
950 REQUIRE( m_grid->GetGridCursorRow() == 0 );
951 REQUIRE( m_grid->IsVisible(0, 0) );
952
953 m_grid->SetFocus();
954
955 sim.KeyDown(WXK_END, wxMOD_CONTROL | wxMOD_SHIFT);
956 sim.KeyUp(WXK_END, wxMOD_CONTROL | wxMOD_SHIFT);
957 wxYield();
958
959 wxGridCellCoordsArray topleft = m_grid->GetSelectionBlockTopLeft();
960 wxGridCellCoordsArray bottomright = m_grid->GetSelectionBlockBottomRight();
961
962 CHECK( topleft.Count() == 1 );
963 CHECK( bottomright.Count() == 1 );
964
965 CHECK( topleft.Item(0).GetCol() == 0 );
966 CHECK( topleft.Item(0).GetRow() == 0 );
967 CHECK( bottomright.Item(0).GetCol() == 11 );
968 CHECK( bottomright.Item(0).GetRow() == 9 );
969
970 CHECK( m_grid->IsVisible(8, 9) );
971 #endif
972 }
973
974 TEST_CASE_METHOD(GridTestCase, "Grid::AddRowCol", "[grid]")
975 {
976 CHECK(m_grid->GetNumberRows() == 10);
977 CHECK(m_grid->GetNumberCols() == 2);
978
979 m_grid->AppendCols();
980 m_grid->AppendRows();
981
982 CHECK(m_grid->GetNumberRows() == 11);
983 CHECK(m_grid->GetNumberCols() == 3);
984
985 m_grid->AppendCols(2);
986 m_grid->AppendRows(2);
987
988 CHECK(m_grid->GetNumberRows() == 13);
989 CHECK(m_grid->GetNumberCols() == 5);
990
991 m_grid->InsertCols(1, 2);
992 m_grid->InsertRows(2, 3);
993
994 CHECK(m_grid->GetNumberRows() == 16);
995 CHECK(m_grid->GetNumberCols() == 7);
996 }
997
998 TEST_CASE_METHOD(GridTestCase, "Grid::DeleteAndAddRowCol", "[grid]")
999 {
1000 SECTION("Default") {}
1001 SECTION("Native header") { m_grid->UseNativeColHeader(); }
1002
1003 CHECK(m_grid->GetNumberRows() == 10);
1004 CHECK(m_grid->GetNumberCols() == 2);
1005
1006 m_grid->DeleteRows(0, 10);
1007 m_grid->DeleteCols(0, 2);
1008
1009 CHECK(m_grid->GetNumberRows() == 0);
1010 CHECK(m_grid->GetNumberCols() == 0);
1011
1012 m_grid->AppendRows(5);
1013 m_grid->AppendCols(3);
1014
1015 CHECK(m_grid->GetNumberRows() == 5);
1016 CHECK(m_grid->GetNumberCols() == 3);
1017
1018 // The order of functions calls can be important
1019 m_grid->DeleteCols(0, 3);
1020 m_grid->DeleteRows(0, 5);
1021
1022 CHECK(m_grid->GetNumberRows() == 0);
1023 CHECK(m_grid->GetNumberCols() == 0);
1024
1025 // Different functions calls order
1026 m_grid->AppendCols(3);
1027 m_grid->AppendRows(5);
1028 }
1029
1030 TEST_CASE_METHOD(GridTestCase, "Grid::ColumnOrder", "[grid]")
1031 {
1032 SECTION("Default") {}
1033 SECTION("Native header") { m_grid->UseNativeColHeader(); }
1034 SECTION("Native labels") { m_grid->SetUseNativeColLabels(); }
1035
1036 m_grid->AppendCols(2);
1037
1038 CHECK(m_grid->GetNumberCols() == 4);
1039
1040 wxArrayInt neworder;
1041 neworder.push_back(1);
1042 neworder.push_back(3);
1043 neworder.push_back(2);
1044 neworder.push_back(0);
1045
1046 m_grid->SetColumnsOrder(neworder);
1047
1048 CHECK(m_grid->GetColPos(1) == 0);
1049 CHECK(m_grid->GetColPos(3) == 1);
1050 CHECK(m_grid->GetColPos(2) == 2);
1051 CHECK(m_grid->GetColPos(0) == 3);
1052
1053 CHECK(m_grid->GetColAt(0) == 1);
1054 CHECK(m_grid->GetColAt(1) == 3);
1055 CHECK(m_grid->GetColAt(2) == 2);
1056 CHECK(m_grid->GetColAt(3) == 0);
1057
1058 m_grid->ResetColPos();
1059
1060 CHECK(m_grid->GetColPos(0) == 0);
1061 CHECK(m_grid->GetColPos(1) == 1);
1062 CHECK(m_grid->GetColPos(2) == 2);
1063 CHECK(m_grid->GetColPos(3) == 3);
1064 }
1065
1066 TEST_CASE_METHOD(GridTestCase, "Grid::ColumnVisibility", "[grid]")
1067 {
1068 m_grid->AppendCols(3);
1069 CHECK( m_grid->IsColShown(1) );
1070
1071 m_grid->HideCol(1);
1072 CHECK( !m_grid->IsColShown(1) );
1073 CHECK( m_grid->IsColShown(2) );
1074
1075 m_grid->ShowCol(1);
1076 CHECK( m_grid->IsColShown(1) );
1077 }
1078
1079 TEST_CASE_METHOD(GridTestCase, "Grid::LineFormatting", "[grid]")
1080 {
1081 CHECK(m_grid->GridLinesEnabled());
1082
1083 m_grid->EnableGridLines(false);
1084
1085 CHECK(!m_grid->GridLinesEnabled());
1086
1087 m_grid->EnableGridLines();
1088
1089 m_grid->SetGridLineColour(*wxRED);
1090
1091 CHECK(*wxRED == m_grid->GetGridLineColour());
1092 }
1093
1094 TEST_CASE_METHOD(GridTestCase, "Grid::SortSupport", "[grid]")
1095 {
1096 CHECK(m_grid->GetSortingColumn() == wxNOT_FOUND);
1097
1098 m_grid->SetSortingColumn(1);
1099
1100 CHECK(!m_grid->IsSortingBy(0));
1101 CHECK(m_grid->IsSortingBy(1));
1102 CHECK(m_grid->IsSortOrderAscending());
1103
1104 m_grid->SetSortingColumn(0, false);
1105
1106 CHECK(m_grid->IsSortingBy(0));
1107 CHECK(!m_grid->IsSortingBy(1));
1108 CHECK(!m_grid->IsSortOrderAscending());
1109
1110 m_grid->UnsetSortingColumn();
1111
1112 CHECK(!m_grid->IsSortingBy(0));
1113 CHECK(!m_grid->IsSortingBy(1));
1114 }
1115
1116 TEST_CASE_METHOD(GridTestCase, "Grid::Labels", "[grid]")
1117 {
1118 CHECK(m_grid->GetColLabelValue(0) == "A");
1119 CHECK(m_grid->GetRowLabelValue(0) == "1");
1120
1121 m_grid->SetColLabelValue(0, "Column 1");
1122 m_grid->SetRowLabelValue(0, "Row 1");
1123
1124 CHECK(m_grid->GetColLabelValue(0) == "Column 1");
1125 CHECK(m_grid->GetRowLabelValue(0) == "Row 1");
1126
1127 m_grid->SetLabelTextColour(*wxGREEN);
1128 m_grid->SetLabelBackgroundColour(*wxRED);
1129
1130 CHECK(m_grid->GetLabelTextColour() == *wxGREEN);
1131 CHECK(m_grid->GetLabelBackgroundColour() == *wxRED);
1132
1133 m_grid->SetColLabelTextOrientation(wxVERTICAL);
1134
1135 CHECK(m_grid->GetColLabelTextOrientation() == wxVERTICAL);
1136 }
1137
1138 TEST_CASE_METHOD(GridTestCase, "Grid::SelectionMode", "[grid]")
1139 {
1140 //We already test this mode in Select
1141 CHECK(m_grid->GetSelectionMode() == wxGrid::wxGridSelectCells);
1142
1143 // Select an individual cell and an entire row.
1144 m_grid->SelectBlock(3, 1, 3, 1);
1145 m_grid->SelectRow(5, true /* add to selection */);
1146
1147 // Test that after switching to row selection mode only the row remains
1148 // selected.
1149 m_grid->SetSelectionMode(wxGrid::wxGridSelectRows);
1150 CHECK( m_grid->IsInSelection(5, 0) );
1151 CHECK( m_grid->IsInSelection(5, 1) );
1152 CHECK( !m_grid->IsInSelection(3, 1) );
1153
1154 //Test row selection be selecting a single cell and checking the whole
1155 //row is selected
1156 m_grid->ClearSelection();
1157 m_grid->SelectBlock(3, 1, 3, 1);
1158
1159 wxArrayInt selectedRows = m_grid->GetSelectedRows();
1160 CHECK(selectedRows.Count() == 1);
1161 CHECK(selectedRows[0] == 3);
1162
1163 // Check that overlapping selection blocks are handled correctly.
1164 m_grid->ClearSelection();
1165 m_grid->SelectBlock(0, 0, 4, 1);
1166 m_grid->SelectBlock(2, 0, 6, 1, true /* add to selection */);
1167 CHECK( m_grid->GetSelectedRows().size() == 7 );
1168
1169 CHECK( m_grid->GetSelectedColBlocks().empty() );
1170
1171 RowRanges rowRanges;
1172 rowRanges.push_back(RowRange(0, 6));
1173 CheckRowSelection(rowRanges);
1174
1175 m_grid->SelectBlock(6, 0, 8, 1);
1176 m_grid->SelectBlock(1, 0, 4, 1, true /* add to selection */);
1177 m_grid->SelectBlock(0, 0, 2, 1, true /* add to selection */);
1178 CHECK( m_grid->GetSelectedRows().size() == 8 );
1179
1180 rowRanges.clear();
1181 rowRanges.push_back(RowRange(0, 4));
1182 rowRanges.push_back(RowRange(6, 8));
1183 CheckRowSelection(rowRanges);
1184
1185 // Select all odd rows.
1186 m_grid->ClearSelection();
1187 rowRanges.clear();
1188 for ( int i = 1; i < m_grid->GetNumberRows(); i += 2 )
1189 {
1190 m_grid->SelectBlock(i, 0, i, 1, true);
1191 rowRanges.push_back(RowRange(i, i));
1192 }
1193
1194 CheckRowSelection(rowRanges);
1195
1196 // Now select another block overlapping 2 of them and bordering 2 others.
1197 m_grid->SelectBlock(2, 0, 6, 1, true);
1198
1199 rowRanges.clear();
1200 rowRanges.push_back(RowRange(1, 7));
1201 rowRanges.push_back(RowRange(9, 9));
1202 CheckRowSelection(rowRanges);
1203
1204 CHECK(m_grid->GetSelectionMode() == wxGrid::wxGridSelectRows);
1205
1206
1207 //Test column selection be selecting a single cell and checking the whole
1208 //column is selected
1209 m_grid->SetSelectionMode(wxGrid::wxGridSelectColumns);
1210 m_grid->SelectBlock(3, 1, 3, 1);
1211
1212 CHECK( m_grid->GetSelectedRowBlocks().empty() );
1213
1214 wxArrayInt selectedCols = m_grid->GetSelectedCols();
1215 CHECK(selectedCols.Count() == 1);
1216 CHECK(selectedCols[0] == 1);
1217
1218 wxGridBlockCoordsVector colBlocks = m_grid->GetSelectedColBlocks();
1219 CHECK( colBlocks.size() == 1 );
1220 CHECK( colBlocks.at(0) == wxGridBlockCoords(0, 1, 9, 1) );
1221
1222 CHECK(m_grid->GetSelectionMode() == wxGrid::wxGridSelectColumns);
1223 }
1224
1225 TEST_CASE_METHOD(GridTestCase, "Grid::CellFormatting", "[grid]")
1226 {
1227 //Check that initial alignment is default
1228 int horiz, cellhoriz, vert, cellvert;
1229
1230 m_grid->GetDefaultCellAlignment(&horiz, &vert);
1231 m_grid->GetCellAlignment(0, 0, &cellhoriz, &cellvert);
1232
1233 CHECK(horiz == cellhoriz);
1234 CHECK(vert == cellvert);
1235
1236 //Check initial text colour and background colour are default
1237 wxColour text, back;
1238
1239 back = m_grid->GetDefaultCellBackgroundColour();
1240
1241 CHECK(m_grid->GetCellBackgroundColour(0, 0) == back);
1242
1243 back = m_grid->GetDefaultCellTextColour();
1244
1245 CHECK(m_grid->GetCellTextColour(0, 0) == back);
1246
1247 #if WXWIN_COMPATIBILITY_2_8
1248 m_grid->SetCellAlignment(wxALIGN_CENTRE, 0, 0);
1249 m_grid->GetCellAlignment(0, 0, &cellhoriz, &cellvert);
1250
1251 CHECK(cellhoriz == wxALIGN_CENTRE);
1252 CHECK(cellvert == wxALIGN_CENTRE);
1253 #endif // WXWIN_COMPATIBILITY_2_8
1254
1255 m_grid->SetCellAlignment(0, 0, wxALIGN_LEFT, wxALIGN_BOTTOM);
1256 m_grid->GetCellAlignment(0, 0, &cellhoriz, &cellvert);
1257
1258 CHECK(cellhoriz == wxALIGN_LEFT);
1259 CHECK(cellvert == wxALIGN_BOTTOM);
1260
1261 #if WXWIN_COMPATIBILITY_2_8
1262 m_grid->SetCellTextColour(*wxRED, 0, 0);
1263 CHECK(m_grid->GetCellTextColour(0, 0) == *wxRED);
1264 #endif // WXWIN_COMPATIBILITY_2_8
1265
1266 m_grid->SetCellTextColour(0, 0, *wxGREEN);
1267 CHECK(m_grid->GetCellTextColour(0,0) == *wxGREEN);
1268 }
1269
1270 TEST_CASE_METHOD(GridTestCase, "Grid::GetNonDefaultAlignment", "[grid]")
1271 {
1272 // GetNonDefaultAlignment() is used by several renderers having their own
1273 // preferred alignment, so check that if we don't reset the alignment
1274 // explicitly, it doesn't override the alignment used by default.
1275 wxGridCellAttrPtr attr;
1276 int hAlign = wxALIGN_RIGHT,
1277 vAlign = wxALIGN_INVALID;
1278
1279 attr = m_grid->CallGetCellAttr(0, 0);
1280 REQUIRE( attr );
1281
1282 // Check that the specified alignment is preserved, while the unspecified
1283 // component is filled with the default value (which is "top" by default).
1284 attr->GetNonDefaultAlignment(&hAlign, &vAlign);
1285 CHECK( hAlign == wxALIGN_RIGHT );
1286 CHECK( vAlign == wxALIGN_TOP );
1287
1288 // Now change the defaults and check that the unspecified alignment
1289 // component is filled with the new default.
1290 m_grid->SetDefaultCellAlignment(wxALIGN_CENTRE_HORIZONTAL,
1291 wxALIGN_CENTRE_VERTICAL);
1292
1293 vAlign = wxALIGN_INVALID;
1294
1295 attr = m_grid->CallGetCellAttr(0, 0);
1296 REQUIRE( attr );
1297
1298 attr->GetNonDefaultAlignment(&hAlign, &vAlign);
1299 CHECK( hAlign == wxALIGN_RIGHT );
1300 CHECK( vAlign == wxALIGN_CENTRE_VERTICAL );
1301
1302 // This is only indirectly related, but test here for CanOverflow() working
1303 // correctly for the cells with non-default alignment, as this used to be
1304 // broken.
1305 m_grid->SetCellAlignment(0, 0, wxALIGN_INVALID, wxALIGN_CENTRE);
1306 attr = m_grid->CallGetCellAttr(0, 0);
1307 REQUIRE( attr );
1308 CHECK( attr->CanOverflow() );
1309 }
1310
1311 TEST_CASE_METHOD(GridTestCase, "Grid::Editable", "[grid]")
1312 {
1313 #if wxUSE_UIACTIONSIMULATOR
1314 if ( !EnableUITests() )
1315 return;
1316
1317 //As the grid is not editable we shouldn't create an editor
1318 EventCounter created(m_grid, wxEVT_GRID_EDITOR_CREATED);
1319
1320 wxUIActionSimulator sim;
1321
1322 CHECK(m_grid->IsEditable());
1323
1324 m_grid->EnableEditing(false);
1325
1326 CHECK(!m_grid->IsEditable());
1327
1328 m_grid->SetFocus();
1329 m_grid->SetGridCursor(1, 1);
1330
1331 sim.Text("abab");
1332 wxYield();
1333
1334 sim.Char(WXK_RETURN);
1335 wxYield();
1336
1337 CHECK(created.GetCount() == 0);
1338 #endif
1339 }
1340
1341 TEST_CASE_METHOD(GridTestCase, "Grid::ReadOnly", "[grid]")
1342 {
1343 #if wxUSE_UIACTIONSIMULATOR
1344 if ( !EnableUITests() )
1345 return;
1346
1347 //As the cell is readonly we shouldn't create an editor
1348 EventCounter created(m_grid, wxEVT_GRID_EDITOR_CREATED);
1349
1350 wxUIActionSimulator sim;
1351
1352 CHECK(!m_grid->IsReadOnly(1, 1));
1353
1354 m_grid->SetReadOnly(1, 1);
1355
1356 CHECK(m_grid->IsReadOnly(1, 1));
1357
1358 m_grid->SetFocus();
1359
1360 m_grid->SetGridCursor(1, 1);
1361
1362 CHECK(m_grid->IsCurrentCellReadOnly());
1363
1364 sim.Text("abab");
1365 wxYield();
1366
1367 sim.Char(WXK_RETURN);
1368 wxYield();
1369
1370 CHECK(created.GetCount() == 0);
1371 #endif
1372 }
1373
1374 TEST_CASE_METHOD(GridTestCase, "Grid::WindowAsEditorControl", "[grid]")
1375 {
1376 #if wxUSE_UIACTIONSIMULATOR
1377 if ( !EnableUITests() )
1378 return;
1379
1380 // A very simple editor using a window not derived from wxControl as the
1381 // editor.
1382 class TestEditor : public wxGridCellEditor
1383 {
1384 public:
TestEditor()1385 TestEditor() {}
1386
Create(wxWindow * parent,wxWindowID id,wxEvtHandler * evtHandler)1387 void Create(wxWindow* parent,
1388 wxWindowID id,
1389 wxEvtHandler* evtHandler) wxOVERRIDE
1390 {
1391 SetWindow(new wxWindow(parent, id));
1392 wxGridCellEditor::Create(parent, id, evtHandler);
1393 }
1394
BeginEdit(int,int,wxGrid *)1395 void BeginEdit(int, int, wxGrid*) wxOVERRIDE {}
1396
EndEdit(int,int,wxGrid const *,wxString const &,wxString * newval)1397 bool EndEdit(int, int, wxGrid const*, wxString const&,
1398 wxString* newval) wxOVERRIDE
1399 {
1400 *newval = GetValue();
1401 return true;
1402 }
1403
ApplyEdit(int row,int col,wxGrid * grid)1404 void ApplyEdit(int row, int col, wxGrid* grid) wxOVERRIDE
1405 {
1406 grid->GetTable()->SetValue(row, col, GetValue());
1407 }
1408
Reset()1409 void Reset() wxOVERRIDE {}
1410
Clone() const1411 wxGridCellEditor* Clone() const wxOVERRIDE { return new TestEditor(); }
1412
GetValue() const1413 wxString GetValue() const wxOVERRIDE { return "value"; }
1414 };
1415
1416 wxGridCellAttr* attr = new wxGridCellAttr();
1417 attr->SetRenderer(new wxGridCellStringRenderer());
1418 attr->SetEditor(new TestEditor());
1419 m_grid->SetAttr(1, 1, attr);
1420
1421 EventCounter created(m_grid, wxEVT_GRID_EDITOR_CREATED);
1422
1423 wxUIActionSimulator sim;
1424
1425 m_grid->SetFocus();
1426 m_grid->SetGridCursor(1, 1);
1427 m_grid->EnableCellEditControl();
1428
1429 sim.Char(WXK_RETURN);
1430
1431 wxYield();
1432
1433 CHECK(created.GetCount() == 1);
1434 #endif
1435 }
1436
1437 TEST_CASE_METHOD(GridTestCase, "Grid::ResizeScrolledHeader", "[grid]")
1438 {
1439 // TODO this test currently works only under Windows unfortunately
1440 #if wxUSE_UIACTIONSIMULATOR && (defined(__WXMSW__) || defined(__WXGTK__))
1441 if ( !EnableUITests() )
1442 return;
1443
1444 #ifdef __WXGTK20__
1445 // Works locally, but not when run on Travis CI.
1446 if ( IsAutomaticTest() )
1447 return;
1448 #endif
1449
1450 SECTION("Default") {}
1451 SECTION("Native header") { m_grid->UseNativeColHeader(); }
1452
1453 int const startwidth = m_grid->GetColSize(0);
1454 int const draglength = 100;
1455
1456 m_grid->AppendCols(8);
1457 m_grid->Scroll(5, 0);
1458 m_grid->Refresh();
1459 m_grid->Update();
1460
1461 wxRect rect = m_grid->CellToRect(0, 1);
1462 wxPoint point = m_grid->CalcScrolledPosition(rect.GetPosition());
1463 point = m_grid->ClientToScreen(point
1464 + wxPoint(m_grid->GetRowLabelSize(),
1465 m_grid->GetColLabelSize())
1466 - wxPoint(0, 5));
1467
1468 wxUIActionSimulator sim;
1469
1470 wxYield();
1471 sim.MouseMove(point);
1472
1473 wxYield();
1474 sim.MouseDown();
1475
1476 wxYield();
1477 sim.MouseMove(point + wxPoint(draglength, 0));
1478
1479 wxYield();
1480 sim.MouseUp();
1481
1482 wxYield();
1483
1484 CHECK(m_grid->GetColSize(0) == startwidth + draglength);
1485 #endif
1486 }
1487
1488 TEST_CASE_METHOD(GridTestCase, "Grid::ColumnMinWidth", "[grid]")
1489 {
1490 // TODO this test currently works only under Windows unfortunately
1491 #if wxUSE_UIACTIONSIMULATOR && (defined(__WXMSW__) || defined(__WXGTK__))
1492 if ( !EnableUITests() )
1493 return;
1494
1495 #ifdef __WXGTK20__
1496 // Works locally, but not when run on Travis CI.
1497 if ( IsAutomaticTest() )
1498 return;
1499 #endif
1500
1501 SECTION("Default") {}
1502 SECTION("Native header")
1503 {
1504 // For some unknown reason, this test fails under AppVeyor even though
1505 // it passes locally, so disable it there. If anybody can reproduce the
1506 // problem locally, where it can be debugged, please let us know.
1507 if ( IsAutomaticTest() )
1508 return;
1509
1510 m_grid->UseNativeColHeader();
1511 }
1512
1513 int const startminwidth = m_grid->GetColMinimalAcceptableWidth();
1514 m_grid->SetColMinimalAcceptableWidth(startminwidth*2);
1515 int const newminwidth = m_grid->GetColMinimalAcceptableWidth();
1516 int const startwidth = m_grid->GetColSize(0);
1517
1518 CHECK(m_grid->GetColMinimalAcceptableWidth() < startwidth);
1519
1520 wxRect rect = m_grid->CellToRect(0, 1);
1521 wxPoint point = m_grid->CalcScrolledPosition(rect.GetPosition());
1522 point = m_grid->ClientToScreen(point
1523 + wxPoint(m_grid->GetRowLabelSize(),
1524 m_grid->GetColLabelSize())
1525 - wxPoint(0, 5));
1526
1527 wxUIActionSimulator sim;
1528
1529 // Drag to reach the minimal width.
1530 wxYield();
1531 sim.MouseMove(point);
1532 wxYield();
1533 sim.MouseDown();
1534 wxYield();
1535 sim.MouseMove(point - wxPoint(startwidth - startminwidth, 0));
1536 wxYield();
1537 sim.MouseUp();
1538 wxYield();
1539
1540 CHECK(m_grid->GetColSize(0) == newminwidth);
1541 #endif
1542 }
1543
CheckFirstColAutoSize(int expected)1544 void GridTestCase::CheckFirstColAutoSize(int expected)
1545 {
1546 m_grid->AutoSizeColumn(0);
1547
1548 wxYield();
1549 CHECK(m_grid->GetColSize(0) == expected);
1550 }
1551
1552 TEST_CASE_METHOD(GridTestCase, "Grid::AutoSizeColumn", "[grid]")
1553 {
1554 SECTION("Default") {}
1555 SECTION("Native header") { m_grid->UseNativeColHeader(); }
1556 SECTION("Native labels") { m_grid->SetUseNativeColLabels(); }
1557
1558 // Hardcoded extra margin for the columns used in grid.cpp.
1559 const int margin = m_grid->FromDIP(10);
1560
1561 wxGridCellAttrPtr attr(m_grid->GetOrCreateCellAttr(0, 0));
1562 wxGridCellRendererPtr renderer(attr->GetRenderer(m_grid, 0, 0));
1563 REQUIRE(renderer);
1564
1565 wxClientDC dcCell(m_grid->GetGridWindow());
1566
1567 wxClientDC dcLabel(m_grid->GetGridWindow());
1568 dcLabel.SetFont(m_grid->GetLabelFont());
1569
1570 const wxString shortStr = "W";
1571 const wxString mediumStr = "WWWW";
1572 const wxString longStr = "WWWWWWWW";
1573 const wxString multilineStr = mediumStr + "\n" + longStr;
1574
1575 SECTION("Empty column and label")
1576 {
1577 m_grid->SetColLabelValue(0, wxString());
1578 CheckFirstColAutoSize( m_grid->GetDefaultColSize() );
1579 }
1580
1581 SECTION("Empty column with label")
1582 {
1583 m_grid->SetColLabelValue(0, mediumStr);
1584 CheckFirstColAutoSize( GetColumnLabelWidth(dcLabel, 0, margin) );
1585 }
1586
1587 SECTION("Column with empty label")
1588 {
1589 m_grid->SetColLabelValue(0, wxString());
1590 m_grid->SetCellValue(0, 0, mediumStr);
1591 m_grid->SetCellValue(1, 0, shortStr);
1592 m_grid->SetCellValue(3, 0, longStr);
1593
1594 CheckFirstColAutoSize(
1595 renderer->GetBestWidth(*m_grid, *attr, dcCell, 3, 0,
1596 m_grid->GetRowHeight(3)) + margin );
1597 }
1598
1599 SECTION("Column with label longer than contents")
1600 {
1601 m_grid->SetColLabelValue(0, multilineStr);
1602 m_grid->SetCellValue(0, 0, mediumStr);
1603 m_grid->SetCellValue(1, 0, shortStr);
1604 CheckFirstColAutoSize( GetColumnLabelWidth(dcLabel, 0, margin) );
1605 }
1606
1607 SECTION("Column with contents longer than label")
1608 {
1609 m_grid->SetColLabelValue(0, mediumStr);
1610 m_grid->SetCellValue(0, 0, mediumStr);
1611 m_grid->SetCellValue(1, 0, shortStr);
1612 m_grid->SetCellValue(3, 0, multilineStr);
1613 CheckFirstColAutoSize(
1614 renderer->GetBestWidth(*m_grid, *attr, dcCell, 3, 0,
1615 m_grid->GetRowHeight(3)) + margin );
1616 }
1617
1618 SECTION("Column with equally sized contents and label")
1619 {
1620 m_grid->SetColLabelValue(0, mediumStr);
1621 m_grid->SetCellValue(0, 0, mediumStr);
1622 m_grid->SetCellValue(1, 0, mediumStr);
1623 m_grid->SetCellValue(3, 0, mediumStr);
1624
1625 const int labelWidth = GetColumnLabelWidth(dcLabel, 0, margin);
1626
1627 const int cellWidth =
1628 renderer->GetBestWidth(*m_grid, *attr, dcCell, 3, 0,
1629 m_grid->GetRowHeight(3))
1630 + margin;
1631
1632 // We can't be sure which size will be greater because of different fonts
1633 // so just calculate the maximum width.
1634 CheckFirstColAutoSize( wxMax(labelWidth, cellWidth) );
1635 }
1636
1637 SECTION("Column with auto wrapping contents taller than row")
1638 {
1639 // Verify row height remains unchanged with an auto-wrapping multi-line
1640 // cell.
1641 // Also shouldn't continuously try to fit the multi-line content into
1642 // a single line, which is not possible. See
1643 // https://trac.wxwidgets.org/ticket/15943 .
1644
1645 m_grid->SetCellValue(0, 0, multilineStr);
1646 m_grid->SetCellRenderer(0 , 0, new wxGridCellAutoWrapStringRenderer);
1647 m_grid->AutoSizeColumn(0);
1648
1649 wxYield();
1650 CHECK( m_grid->GetRowSize(0) == m_grid->GetDefaultRowSize() );
1651 }
1652 }
1653
1654 TEST_CASE_METHOD(GridTestCase, "Grid::DrawInvalidCell", "[grid][multicell]")
1655 {
1656 // Set up a multicell with inside an overflowing cell.
1657 // This is artificial and normally inside cells are probably not expected
1658 // to have a value but this is merely done to check if inside cells are
1659 // drawn, which they shouldn't be.
1660 m_grid->SetCellSize(0, 0, 2, 1);
1661 m_grid->SetCellValue( 1, 0, wxString('W', 42) );
1662
1663 // Update()s, yields and sleep are needed to try to make the test fail with
1664 // macOS, GTK and MSW.
1665 // MSW needs just the yields (or updates), macOS in addition needs to sleep
1666 // (doesn't work with updates) and for GTK it's usually enough to just do
1667 // two updates (not yields). This test does all unconditionally.
1668 m_grid->Update();
1669 wxYield();
1670 wxMilliSleep(20);
1671
1672 // Try to force redrawing of the inside cell: if it still draws there will
1673 // be an infinite recursion.
1674 m_grid->SetColSize(1, m_grid->GetColSize(1) + 1);
1675
1676 m_grid->Update();
1677 wxYield();
1678 }
1679
1680 #define CHECK_ATTR_COUNT(n) CHECK( m_grid->GetCellAttrCount() == n )
1681
1682 TEST_CASE_METHOD(GridTestCase, "Grid::CellAttribute", "[attr][cell][grid]")
1683 {
1684 SECTION("Overwrite")
1685 {
1686 CHECK_ATTR_COUNT( 0 );
1687
1688 m_grid->SetAttr(0, 0, NULL);
1689 CHECK_ATTR_COUNT( 0 );
1690
1691 SetCellAttr(0, 0);
1692 CHECK_ATTR_COUNT( 1 );
1693
1694 // Overwrite existing attribute with another.
1695 SetCellAttr(0, 0);
1696 CHECK_ATTR_COUNT( 1 );
1697
1698 m_grid->SetAttr(0, 0, NULL);
1699 CHECK_ATTR_COUNT( 0 );
1700
1701 SetCellAttr(0, 0);
1702 m_grid->SetCellBackgroundColour(0, 1, *wxGREEN);
1703 CHECK_ATTR_COUNT( 2 );
1704
1705 m_grid->SetAttr(0, 1, NULL);
1706 m_grid->SetAttr(0, 0, NULL);
1707 CHECK_ATTR_COUNT( 0 );
1708 }
1709
1710
1711 // Fill the grid with attributes for next sections.
1712
1713 const int numRows = m_grid->GetNumberRows();
1714 const int numCols = m_grid->GetNumberCols();
1715
1716 for ( int row = 0; row < numRows; ++row )
1717 {
1718 for ( int col = 0; col < numCols; ++col )
1719 SetCellAttr(row, col);
1720 }
1721
1722 size_t numAttrs = static_cast<size_t>(numRows * numCols);
1723
1724 CHECK_ATTR_COUNT( numAttrs );
1725
1726 SECTION("Expanding")
1727 {
1728 CHECK( !HasCellAttr(numRows, numCols) );
1729
1730 m_grid->InsertCols();
1731 CHECK_ATTR_COUNT( numAttrs );
1732 CHECK( !HasCellAttr(0, 0) );
1733 CHECK( HasCellAttr(0, numCols) );
1734
1735 m_grid->InsertRows();
1736 CHECK_ATTR_COUNT( numAttrs );
1737 CHECK( HasCellAttr(numRows, numCols) );
1738 }
1739
1740 SECTION("Shrinking")
1741 {
1742 CHECK( HasCellAttr(numRows - 1 , numCols - 1) );
1743 CHECK( HasCellAttr(0, numCols - 1) );
1744
1745 m_grid->DeleteCols();
1746 numAttrs -= m_grid->GetNumberRows();
1747 CHECK_ATTR_COUNT( numAttrs );
1748 CHECK( HasCellAttr(0, 0) );
1749 CHECK( !HasCellAttr(0, numCols - 1) );
1750
1751 m_grid->DeleteRows();
1752 numAttrs -= m_grid->GetNumberCols();
1753 CHECK_ATTR_COUNT( numAttrs );
1754 CHECK( !HasCellAttr(numRows - 1 , numCols - 1) );
1755 }
1756 }
1757
1758 #define CHECK_MULTICELL() CHECK_THAT( *m_grid, HasMulticellOnly(multi) )
1759
1760 #define CHECK_NO_MULTICELL() CHECK_THAT( *m_grid, HasEmptyGrid() )
1761
1762 #define WHEN_N(s, n) WHEN(wxString::Format(s, n).ToStdString())
1763
1764 TEST_CASE_METHOD(GridTestCase,
1765 "Grid::InsertionsWithMulticell",
1766 "[attr][cell][grid][insert][multicell]")
1767 {
1768 int insertions = 0, offset = 0;
1769
1770 Multicell multi(1, 1, 3, 5);
1771
1772 SECTION("Sanity checks")
1773 {
1774 FitGridToMulticell(m_grid, multi);
1775 m_grid->SetMulticell(multi);
1776
1777 REQUIRE( static_cast<int>(m_grid->GetCellAttrCount())
1778 == multi.rows * multi.cols );
1779
1780 int row, col, rows, cols;
1781
1782 // Check main cell.
1783 row = multi.row,
1784 col = multi.col;
1785 wxGrid::CellSpan span = m_grid->GetCellSize(row, col, &rows, &cols);
1786
1787 REQUIRE( span == wxGrid::CellSpan_Main );
1788 REQUIRE( rows == multi.rows );
1789 REQUIRE( cols == multi.cols );
1790
1791 // Check inside cell at opposite of main.
1792 row = multi.row + multi.rows - 1;
1793 col = multi.col + multi.cols - 1;
1794 span = m_grid->GetCellSize(row, col, &rows, &cols);
1795
1796 REQUIRE( span == wxGrid::CellSpan_Inside );
1797 REQUIRE( rows == multi.row - row );
1798 REQUIRE( cols == multi.col - col );
1799 }
1800
1801 // Do some basic testing with column insertions first and do more tests
1802 // with edge cases later on just with rows. It's not really needed to
1803 // repeat the same tests for both rows and columns as the code for
1804 // updating them works symmetrically.
1805
GIVEN(Catch::toString (multi))1806 GIVEN(Catch::toString(multi))
1807 {
1808 FitGridToMulticell(m_grid, multi);
1809 m_grid->SetMulticell(multi);
1810
1811 insertions = 2;
1812
1813 WHEN("inserting any columns in multicell, at main")
1814 {
1815 InsertCols(multi.col + 0, insertions);
1816
1817 THEN("the position changes but not the size")
1818 {
1819 multi.col += insertions;
1820 CHECK_MULTICELL();
1821 }
1822 }
1823
1824 WHEN("inserting any columns in multicell, just after main")
1825 {
1826 InsertCols(multi.col + 1, insertions);
1827
1828 THEN("the size changes but not the position")
1829 {
1830 multi.cols += insertions;
1831 CHECK_MULTICELL();
1832 }
1833 }
1834
1835 }
1836
1837 // Do more extensive testing with rows.
1838
1839 wxSwap(multi.rows, multi.cols);
1840
GIVEN(Catch::toString (multi))1841 GIVEN(Catch::toString(multi))
1842 {
1843 FitGridToMulticell(m_grid, multi);
1844 m_grid->SetMulticell(multi);
1845
1846 const int insertionCounts[] = {1, 2, multi.rows};
1847
1848 for ( size_t i = 0; i < WXSIZEOF(insertionCounts); ++i )
1849 {
1850 insertions = insertionCounts[i];
1851
1852 WHEN_N("inserting %d row(s), just before main", insertions)
1853 {
1854 InsertRows(multi.row - 1, insertions);
1855
1856 THEN("the position changes but not the size")
1857 {
1858 multi.row += insertions;
1859 CHECK_MULTICELL();
1860 }
1861 }
1862
1863 WHEN_N("inserting %d row(s) in multicell, at main", insertions)
1864 {
1865 InsertRows(multi.row + 0, insertions);
1866
1867 THEN("the position changes but not the size")
1868 {
1869 multi.row += insertions;
1870 CHECK_MULTICELL();
1871 }
1872 }
1873 }
1874
1875 insertions = multi.rows / 2;
1876
1877 // Check insertions within multicell, at and near edges.
1878 const int insertionOffsets[] = {1, 2, multi.rows - 2, multi.rows - 1};
1879
1880 for ( size_t i = 0; i < WXSIZEOF(insertionOffsets); ++i )
1881 {
1882 offset = insertionOffsets[i];
1883
1884 WHEN_N("inserting rows in multicell, %d row(s) after main", offset)
1885 {
1886 InsertRows(multi.row + offset, insertions);
1887
1888 THEN("the size changes but not the position")
1889 {
1890 multi.rows += insertions;
1891 CHECK_MULTICELL();
1892 }
1893 }
1894 }
1895
1896 // Check at least one case of inserting after multicell.
1897 WHEN("inserting rows, just after multicell")
1898 {
1899 insertions = 2;
1900 InsertRows(multi.row + multi.rows, insertions);
1901
1902 THEN("neither size nor position change")
1903 {
1904 CHECK_MULTICELL();
1905 }
1906 }
1907 }
1908
1909 }
1910
1911 TEST_CASE_METHOD(GridTestCase,
1912 "GridMulticell::DeletionsWithMulticell",
1913 "[cellattr][delete][grid][multicell]")
1914 {
1915 int deletions = 0, offset = 0;
1916
1917 // Same as with the previous (insertions) test case but instead of some
1918 // basic testing with columns first, this time use rows for that and do more
1919 // extensive testing with columns.
1920
1921 Multicell multi(1, 1, 5, 3);
1922
GIVEN(Catch::toString (multi))1923 GIVEN(Catch::toString(multi))
1924 {
1925 FitGridToMulticell(m_grid, multi);
1926 m_grid->SetMulticell(multi);
1927
1928 WHEN("deleting any rows, at main")
1929 {
1930 deletions = 1;
1931 DeleteRows(multi.row + 0, deletions);
1932
1933 THEN("the multicell is deleted")
1934 {
1935 CHECK_NO_MULTICELL();
1936 }
1937 }
1938
1939 WHEN("deleting more rows than length of multicell,"
1940 " at end of multicell")
1941 {
1942 deletions = multi.rows + 2;
1943 offset = multi.rows - 1;
1944 DeleteRows(multi.row + offset, deletions);
1945
1946 THEN("the size changes but not the position")
1947 {
1948 multi.rows = offset;
1949 CHECK_MULTICELL();
1950 }
1951 }
1952 }
1953
1954 // Do more extensive testing with columns.
1955
1956 wxSwap(multi.rows, multi.cols);
1957
GIVEN(Catch::toString (multi))1958 GIVEN(Catch::toString(multi))
1959 {
1960 FitGridToMulticell(m_grid, multi);
1961 m_grid->SetMulticell(multi);
1962
1963 WHEN("deleting one column, just before main")
1964 {
1965 DeleteCols(multi.col - 1);
1966
1967 THEN("the position changes but not the size")
1968 {
1969 multi.col--;
1970 CHECK_MULTICELL();
1971 }
1972 }
1973
1974 WHEN("deleting multiple columns, just before main")
1975 {
1976 deletions = 2; // Must be at least 2 to affect main.
1977 DeleteCols(multi.col - 1, wxMax(2, deletions));
1978
1979 THEN("the multicell is deleted")
1980 {
1981 CHECK_NO_MULTICELL();
1982 }
1983 }
1984
1985 WHEN("deleting any columns, at main")
1986 {
1987 deletions = 1;
1988 DeleteCols(multi.col + 0, deletions);
1989
1990 THEN("the multicell is deleted")
1991 {
1992 CHECK_NO_MULTICELL();
1993 }
1994 }
1995
1996 WHEN("deleting one column within multicell, after main")
1997 {
1998 offset = 1;
1999 offset = wxClip(offset, 1, multi.cols - 1);
2000 DeleteCols(multi.col + offset, 1);
2001
2002 THEN("the size changes but not the position")
2003 {
2004 multi.cols--;
2005 CHECK_MULTICELL();
2006 }
2007 }
2008
2009 deletions = 2;
2010
2011 // Check deletions within multicell, at and near edges.
2012 const int offsets[] = {1, 2, multi.cols - deletions};
2013
2014 for ( size_t i = 0; i < WXSIZEOF(offsets); ++i )
2015 {
2016 offset = offsets[i];
2017
2018 WHEN_N("deleting columns only within multicell,"
2019 " %d column(s) after main", offset)
2020 {
2021 DeleteCols(multi.col + offset, deletions);
2022
2023 THEN("the size changes but not the position")
2024 {
2025 multi.cols -= deletions;
2026 CHECK_MULTICELL();
2027 }
2028 }
2029 }
2030
2031 // Instead of stuffing the multicell's length logic in the above test,
2032 // separately check at least two cases of starting many deletions
2033 // within multicell.
2034
2035 WHEN("deleting more columns than length of multicell, just after main")
2036 {
2037 deletions = multi.cols + 2;
2038 offset = 1;
2039 DeleteCols(multi.col + offset, deletions);
2040
2041 THEN("the size changes but not the position")
2042 {
2043 multi.cols = offset;
2044 CHECK_MULTICELL();
2045 }
2046 }
2047
2048 WHEN("deleting more columns than length of multicell,"
2049 " at end of multicell")
2050 {
2051 deletions = multi.cols + 2;
2052 offset = multi.cols - 1;
2053 DeleteCols(multi.col + offset, deletions);
2054
2055 THEN("the size changes but not the position")
2056 {
2057 multi.cols = offset;
2058 CHECK_MULTICELL();
2059 }
2060 }
2061 }
2062
2063 }
2064
2065 // Test wxGridBlockCoords here because it'a a part of grid sources.
2066
operator <<(std::ostream & os,const wxGridBlockCoords & block)2067 std::ostream& operator<<(std::ostream& os, const wxGridBlockCoords& block) {
2068 os << "wxGridBlockCoords(" <<
2069 block.GetTopRow() << ", " << block.GetLeftCol() << ", " <<
2070 block.GetBottomRow() << ", " << block.GetRightCol() << ")";
2071 return os;
2072 }
2073
2074 TEST_CASE("GridBlockCoords::Canonicalize", "[grid]")
2075 {
2076 const wxGridBlockCoords block =
2077 wxGridBlockCoords(4, 3, 2, 1).Canonicalize();
2078
2079 CHECK(block.GetTopRow() == 2);
2080 CHECK(block.GetLeftCol() == 1);
2081 CHECK(block.GetBottomRow() == 4);
2082 CHECK(block.GetRightCol() == 3);
2083 }
2084
2085 TEST_CASE("GridBlockCoords::Intersects", "[grid]")
2086 {
2087 // Inside.
2088 CHECK(wxGridBlockCoords(1, 1, 3, 3).Intersects(wxGridBlockCoords(1, 2, 2, 3)));
2089
2090 // Intersects.
2091 CHECK(wxGridBlockCoords(1, 1, 3, 3).Intersects(wxGridBlockCoords(2, 2, 4, 4)));
2092
2093 // Doesn't intersects.
2094 CHECK(!wxGridBlockCoords(1, 1, 3, 3).Intersects(wxGridBlockCoords(4, 4, 6, 6)));
2095 }
2096
2097 TEST_CASE("GridBlockCoords::Contains", "[grid]")
2098 {
2099 // Inside.
2100 CHECK(wxGridBlockCoords(1, 1, 3, 3).Contains(wxGridCellCoords(2, 2)));
2101
2102 // Outside.
2103 CHECK(!wxGridBlockCoords(1, 1, 3, 3).Contains(wxGridCellCoords(5, 5)));
2104
2105 wxGridBlockCoords block1(1, 1, 5, 5);
2106 wxGridBlockCoords block2(1, 1, 3, 3);
2107 wxGridBlockCoords block3(2, 2, 7, 7);
2108 wxGridBlockCoords block4(10, 10, 12, 12);
2109
2110 CHECK( block1.Contains(block2));
2111 CHECK(!block2.Contains(block1));
2112 CHECK(!block1.Contains(block3));
2113 CHECK(!block1.Contains(block4));
2114 CHECK(!block3.Contains(block1));
2115 CHECK(!block4.Contains(block1));
2116 }
2117
2118 TEST_CASE("GridBlockCoords::Difference", "[grid]")
2119 {
2120 SECTION("Subtract contained block (splitted horizontally)")
2121 {
2122 const wxGridBlockCoords block1(1, 1, 7, 7);
2123 const wxGridBlockCoords block2(3, 3, 5, 5);
2124 const wxGridBlockDiffResult result =
2125 block1.Difference(block2, wxHORIZONTAL);
2126
2127 CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 2, 7));
2128 CHECK(result.m_parts[1] == wxGridBlockCoords(6, 1, 7, 7));
2129 CHECK(result.m_parts[2] == wxGridBlockCoords(3, 1, 5, 2));
2130 CHECK(result.m_parts[3] == wxGridBlockCoords(3, 6, 5, 7));
2131 }
2132
2133 SECTION("Subtract contained block (splitted vertically)")
2134 {
2135 const wxGridBlockCoords block1(1, 1, 7, 7);
2136 const wxGridBlockCoords block2(3, 3, 5, 5);
2137 const wxGridBlockDiffResult result =
2138 block1.Difference(block2, wxVERTICAL);
2139
2140 CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 7, 2));
2141 CHECK(result.m_parts[1] == wxGridBlockCoords(1, 6, 7, 7));
2142 CHECK(result.m_parts[2] == wxGridBlockCoords(1, 3, 2, 5));
2143 CHECK(result.m_parts[3] == wxGridBlockCoords(6, 3, 7, 5));
2144 }
2145
2146 SECTION("Blocks intersect by the corner (splitted horizontally)")
2147 {
2148 const wxGridBlockCoords block1(1, 1, 5, 5);
2149 const wxGridBlockCoords block2(3, 3, 7, 7);
2150 const wxGridBlockDiffResult result =
2151 block1.Difference(block2, wxHORIZONTAL);
2152
2153 CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 2, 5));
2154 CHECK(result.m_parts[1] == wxGridNoBlockCoords);
2155 CHECK(result.m_parts[2] == wxGridBlockCoords(3, 1, 5, 2));
2156 CHECK(result.m_parts[3] == wxGridNoBlockCoords);
2157 }
2158
2159 SECTION("Blocks intersect by the corner (splitted vertically)")
2160 {
2161 const wxGridBlockCoords block1(1, 1, 5, 5);
2162 const wxGridBlockCoords block2(3, 3, 7, 7);
2163 const wxGridBlockDiffResult result =
2164 block1.Difference(block2, wxVERTICAL);
2165
2166 CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 5, 2));
2167 CHECK(result.m_parts[1] == wxGridNoBlockCoords);
2168 CHECK(result.m_parts[2] == wxGridBlockCoords(1, 3, 2, 5));
2169 CHECK(result.m_parts[3] == wxGridNoBlockCoords);
2170 }
2171
2172 SECTION("Blocks are the same")
2173 {
2174 const wxGridBlockCoords block1(1, 1, 3, 3);
2175 const wxGridBlockCoords block2(1, 1, 3, 3);
2176 const wxGridBlockDiffResult result =
2177 block1.Difference(block2, wxHORIZONTAL);
2178
2179 CHECK(result.m_parts[0] == wxGridNoBlockCoords);
2180 CHECK(result.m_parts[1] == wxGridNoBlockCoords);
2181 CHECK(result.m_parts[2] == wxGridNoBlockCoords);
2182 CHECK(result.m_parts[3] == wxGridNoBlockCoords);
2183 }
2184
2185 SECTION("Blocks doesn't intersects")
2186 {
2187 const wxGridBlockCoords block1(1, 1, 3, 3);
2188 const wxGridBlockCoords block2(5, 5, 7, 7);
2189 const wxGridBlockDiffResult result =
2190 block1.Difference(block2, wxHORIZONTAL);
2191
2192 CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 3, 3));
2193 CHECK(result.m_parts[1] == wxGridNoBlockCoords);
2194 CHECK(result.m_parts[2] == wxGridNoBlockCoords);
2195 CHECK(result.m_parts[3] == wxGridNoBlockCoords);
2196 }
2197 }
2198
2199
2200 TEST_CASE("GridBlockCoords::SymDifference", "[grid]")
2201 {
2202 SECTION("With contained block")
2203 {
2204 const wxGridBlockCoords block1(1, 1, 7, 7);
2205 const wxGridBlockCoords block2(3, 3, 5, 5);
2206 const wxGridBlockDiffResult result = block1.SymDifference(block2);
2207
2208 CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 2, 7));
2209 CHECK(result.m_parts[1] == wxGridBlockCoords(6, 1, 7, 7));
2210 CHECK(result.m_parts[2] == wxGridBlockCoords(3, 1, 5, 2));
2211 CHECK(result.m_parts[3] == wxGridBlockCoords(3, 6, 5, 7));
2212 }
2213
2214 SECTION("Blocks intersect by the corner")
2215 {
2216 const wxGridBlockCoords block1(1, 1, 5, 5);
2217 const wxGridBlockCoords block2(3, 3, 7, 7);
2218 const wxGridBlockDiffResult result = block1.SymDifference(block2);
2219
2220 CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 2, 5));
2221 CHECK(result.m_parts[1] == wxGridBlockCoords(6, 3, 7, 7));
2222 CHECK(result.m_parts[2] == wxGridBlockCoords(3, 1, 5, 2));
2223 CHECK(result.m_parts[3] == wxGridBlockCoords(3, 6, 5, 7));
2224 }
2225
2226 SECTION("Blocks are the same")
2227 {
2228 const wxGridBlockCoords block1(1, 1, 3, 3);
2229 const wxGridBlockCoords block2(1, 1, 3, 3);
2230 const wxGridBlockDiffResult result = block1.SymDifference(block2);
2231
2232 CHECK(result.m_parts[0] == wxGridNoBlockCoords);
2233 CHECK(result.m_parts[1] == wxGridNoBlockCoords);
2234 CHECK(result.m_parts[2] == wxGridNoBlockCoords);
2235 CHECK(result.m_parts[3] == wxGridNoBlockCoords);
2236 }
2237
2238 SECTION("Blocks doesn't intersects")
2239 {
2240 const wxGridBlockCoords block1(1, 1, 3, 3);
2241 const wxGridBlockCoords block2(5, 5, 7, 7);
2242 const wxGridBlockDiffResult result = block1.SymDifference(block2);
2243
2244 CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 3, 3));
2245 CHECK(result.m_parts[1] == wxGridBlockCoords(5, 5, 7, 7));
2246 CHECK(result.m_parts[2] == wxGridNoBlockCoords);
2247 CHECK(result.m_parts[3] == wxGridNoBlockCoords);
2248 }
2249 }
2250
2251 //
2252 // TestableGrid
2253 //
2254
DoEdit(const EditInfo & edit)2255 void TestableGrid::DoEdit(const EditInfo& edit)
2256 {
2257 m_edit = edit;
2258 m_beforeGridAnnotated = ToString();
2259
2260 switch ( edit.direction )
2261 {
2262 case wxGRID_COLUMN:
2263 if ( edit.count < 0 )
2264 DeleteCols(edit.pos, -edit.count);
2265 else
2266 InsertCols(edit.pos, edit.count);
2267 break;
2268
2269 case wxGRID_ROW:
2270 if ( edit.count < 0 )
2271 DeleteRows(edit.pos, -edit.count);
2272 else
2273 InsertRows(edit.pos, edit.count);
2274 break;
2275 }
2276 }
2277
ToString() const2278 wxString TestableGrid::ToString() const
2279 {
2280 const int numRows = GetNumberRows();
2281 const int numCols = GetNumberCols();
2282
2283 const int colMargin = GetRowLabelValue(numRows - 1).length();
2284 const wxString leftIndent = wxString(' ', colMargin + 1);
2285
2286 // String s contains the rendering of the grid, start with drawing
2287 // the header columns.
2288 wxString s = leftIndent;
2289
2290 const int base = 10;
2291 // Draw the multiples of 10.
2292 for ( int col = base; col <= numCols; col += base)
2293 {
2294 s += wxString(' ', base - 1);
2295 s += ('0' + (col / base));
2296 }
2297
2298 if ( numCols >= base )
2299 s += "\n" + leftIndent;
2300
2301 // Draw the single digits.
2302 for ( int col = 1; col <= numCols; ++col )
2303 s += '0' + (col % base);
2304 s += "\n";
2305
2306 // Draw horizontal divider.
2307 s += wxString(' ', colMargin) + '+' + wxString('-', numCols) + "\n";
2308
2309 const int absEditCount = abs(m_edit.count);
2310 wxString action;
2311 action.Printf(" %s: %d",
2312 m_edit.count < 0 ? "deletions" : "insertions",
2313 absEditCount);
2314
2315 // Will contain summary of grid (only multicells mentioned).
2316 wxString content;
2317
2318 // Draw grid content.
2319 for ( int row = 0; row < numRows; ++row )
2320 {
2321 const wxString label = GetRowLabelValue(row);
2322 s += wxString(' ', colMargin - label.length());
2323 s += label + '|';
2324
2325 for ( int col = 0; col < numCols; ++col )
2326 {
2327 char c = 'x';
2328 int rows, cols;
2329 switch ( GetCellSize(row, col, &rows, &cols) )
2330 {
2331 case wxGrid::CellSpan_None:
2332 c = HasAttr(row, col) ? '*' : '.';
2333 break;
2334
2335 case wxGrid::CellSpan_Main:
2336 c = 'M';
2337 content += Multicell(row, col, rows, cols).ToString() + "\n";
2338 break;
2339
2340 case wxGrid::CellSpan_Inside:
2341 // Check if the offset to main cell is correct.
2342 c = (GetCellSize(row + rows, col + cols, &rows, &cols)
2343 == wxGrid::CellSpan_Main) ? 'm' : '?';
2344 break;
2345 }
2346
2347 s += c;
2348 }
2349
2350 // If applicable draw annotated row edits.
2351 if ( m_edit.count && m_edit.direction == wxGRID_ROW
2352 && row >= m_edit.pos && row < m_edit.pos + absEditCount)
2353 {
2354 s += (m_edit.count < 0 ? " ^" : " v");
2355
2356 if ( row == m_edit.pos )
2357 s += action;
2358 }
2359
2360 s += "\n";
2361 }
2362
2363 // Draw annotated footer if columns edited.
2364 if ( m_edit.count && m_edit.direction == wxGRID_COLUMN )
2365 {
2366 s += leftIndent;
2367
2368 for ( int col = 0; col < m_edit.pos + absEditCount; ++col )
2369 {
2370 if ( col < m_edit.pos )
2371 s += " ";
2372 else
2373 s += (m_edit.count < 0 ? "<" : ">");
2374 }
2375
2376 s += action + "\n";
2377 }
2378
2379 s += "\n";
2380
2381 if ( content.empty() )
2382 content = "Empty grid\n";
2383
2384 return content + s;
2385 }
2386
2387 //
2388 // GridAttrMatcher
2389 //
2390
GridAttrMatcher(const TestableGrid & grid)2391 GridAttrMatcher::GridAttrMatcher(const TestableGrid& grid) : m_grid(&grid)
2392 {
2393 m_expectedGridDesc = m_grid->ToString();
2394 }
2395
match(const TestableGrid & other) const2396 bool GridAttrMatcher::match(const TestableGrid& other) const
2397 {
2398 const int rows = wxMax(m_grid->GetNumberRows(), other.GetNumberRows());
2399 const int cols = wxMax(m_grid->GetNumberCols(), other.GetNumberCols());
2400
2401 for ( int row = 0; row < rows; ++row )
2402 {
2403 for ( int col = 0; col < cols; ++col )
2404 {
2405 const bool hasAttr = m_grid->HasAttr(row, col);
2406 if ( hasAttr != other.HasAttr(row, col) )
2407 {
2408 m_diffDesc.Printf("%s: attribute presence (%d, expected %d)",
2409 CellCoordsToString(row, col),
2410 !hasAttr, hasAttr);
2411
2412 return false;
2413 }
2414
2415 int thisRows, thisCols;
2416 const wxGrid::CellSpan thisSpan
2417 = m_grid->GetCellSize(row, col, &thisRows, &thisCols);
2418
2419 int otherRows, otherCols;
2420 (void) other.GetCellSize(row, col, &otherRows, &otherCols);
2421
2422 if ( thisRows != otherRows || thisCols != otherCols )
2423 {
2424 wxString mismatchKind;
2425 switch ( thisSpan )
2426 {
2427 case wxGrid::CellSpan_None:
2428 mismatchKind = "different cell size";
2429 break;
2430
2431 case wxGrid::CellSpan_Main:
2432 mismatchKind = "different multicell size";
2433 break;
2434
2435 case wxGrid::CellSpan_Inside:
2436 mismatchKind = "main offset mismatch";
2437 break;
2438 }
2439
2440 m_diffDesc.Printf( "%s: %s (%s, expected %s)",
2441 CellCoordsToString(row, col),
2442 mismatchKind,
2443 CellSizeToString(otherRows, otherCols),
2444 CellSizeToString(thisRows, thisCols)
2445 );
2446
2447 return false;
2448 }
2449 }
2450 }
2451
2452 return true;
2453 }
2454
describe() const2455 std::string GridAttrMatcher::describe() const
2456 {
2457 std::string desc = (m_diffDesc.empty() ? "Matches" : "Doesn't match");
2458 desc += " expected content:\n" + m_expectedGridDesc.ToStdString();
2459
2460 if ( !m_diffDesc.empty() )
2461 desc += + "first difference at " + m_diffDesc.ToStdString();
2462
2463 return desc;
2464 }
2465
2466 #endif //wxUSE_GRID
2467