NumeRe v1.1.4
NumeRe: Framework für Numerische Rechnungen
tableviewer.cpp
Go to the documentation of this file.
1/*****************************************************************************
2 NumeRe: Framework fuer Numerische Rechnungen
3 Copyright (C) 2017 Erik Haenel et al.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17******************************************************************************/
18
19#include "tableviewer.hpp"
20#include "gridtable.hpp"
21#include "tableeditpanel.hpp"
22#include "../../kernel/core/ui/language.hpp"
23#include "../../kernel/core/utils/tools.hpp"
24#include "../../kernel/core/datamanagement/tablecolumn.hpp"
25#include "../../kernel/core/io/file.hpp"
26#include <wx/clipbrd.h>
27#include <wx/dataobj.h>
28#include <wx/tokenzr.h>
29
30#include <memory>
31
32#include "cellattributes.hpp"
33
34#define STATUSBAR_PRECISION 5
35#define MAXIMAL_RENDERING_SIZE 5000
36
37
38extern Language _guilang;
39
40BEGIN_EVENT_TABLE(TableViewer, wxGrid)
41 EVT_KEY_DOWN (TableViewer::OnKeyDown)
42 EVT_CHAR (TableViewer::OnChar)
43 EVT_GRID_CELL_CHANGING (TableViewer::OnCellChange)
44 EVT_GRID_CELL_RIGHT_CLICK (TableViewer::OnCellRightClick)
45 EVT_GRID_LABEL_RIGHT_CLICK (TableViewer::OnLabelRightClick)
46 EVT_GRID_LABEL_LEFT_DCLICK (TableViewer::OnLabelDoubleClick)
47 EVT_MENU_RANGE (ID_MENU_SAVE, ID_MENU_CVS, TableViewer::OnMenu)
48 EVT_GRID_SELECT_CELL (TableViewer::OnCellSelect)
49 EVT_GRID_RANGE_SELECT (TableViewer::OnCellRangeSelect)
51
52
53
66TableViewer::TableViewer(wxWindow* parent, wxWindowID id, wxStatusBar* statusbar, TablePanel* parentPanel, const wxPoint& pos, const wxSize& size, long style, const wxString& name)
67 : wxGrid(parent, id, pos, size, style, name), nHeight(600), nWidth(800), nFirstNumRow(1), readOnly(true)
68{
69 // Cells are always aligned right and centered vertically
70 SetDefaultCellAlignment(wxALIGN_RIGHT, wxALIGN_CENTER);
71 SetDefaultRenderer(new AdvStringCellRenderer);
72
73 // Prepare the context menu
74 m_popUpMenu.Append(ID_MENU_CVS, _guilang.get("GUI_TABLE_CVS"));
75 m_popUpMenu.AppendSeparator();
76 m_popUpMenu.Append(ID_MENU_COPY, _guilang.get("GUI_COPY_TABLE_CONTENTS"));
77
78 // prepare the status bar
79 m_statusBar = statusbar;
80 m_parentPanel = parentPanel;
81
82 isGridNumeReTable = false;
83
84 if (m_statusBar)
85 {
86 int widths[3] = {120, 120, -1};
87 m_statusBar->SetStatusWidths(3, widths);
88 createMenuBar();
89 }
90}
91
92
102{
103 BeginBatch();
104
105 // Enable editing, if the table was not
106 // set to read-only mode
107 EnableEditing(!readOnly);
108
110 {
111 GridNumeReTable* gridtab = static_cast<GridNumeReTable*>(GetTable());
113
114 for (int j = 0; j < GetNumberCols(); j++)
115 {
116 if ((int)m_currentColTypes.size() > j)
117 {
118 wxGridCellAttr* attr = nullptr;
119
121 {
122 attr = new wxGridCellAttr(*wxBLACK, *wxWHITE, this->GetDefaultCellFont(),
123 wxALIGN_LEFT, wxALIGN_CENTER);
124 attr->SetRenderer(new AdvStringCellRenderer);
125 }
127 {
128 attr = new wxGridCellAttr(*wxBLACK, *wxWHITE, this->GetDefaultCellFont(),
129 wxALIGN_CENTER, wxALIGN_CENTER);
130 attr->SetRenderer(new AdvStringCellRenderer);
131 }
133 {
134 attr = new wxGridCellAttr(*wxBLACK, *wxWHITE, this->GetDefaultCellFont(),
135 wxALIGN_CENTER, wxALIGN_CENTER);
136 attr->SetRenderer(new AdvBooleanCellRenderer);
137 }
138 else
139 {
140 attr = new wxGridCellAttr(*wxBLACK, *wxWHITE, this->GetDefaultCellFont(),
141 wxALIGN_RIGHT, wxALIGN_CENTER);
142 attr->SetRenderer(new AdvStringCellRenderer);
143 }
144
145 SetColAttr(j, attr);
146 }
147 }
148
149 // Set the default editor for this grid
150 SetDefaultEditor(new CombinedCellEditor(this));
151 }
152 else
153 {
154 // Search the boundaries and color the frame correspondingly
155 for (int i = 0; i < GetNumberRows(); i++)
156 {
157 for (int j = 0; j < GetNumberCols(); j++)
158 {
159 if (i >= (int)nFirstNumRow && GetCellValue(i, j)[0] == '"')
160 SetCellAlignment(wxALIGN_LEFT, i, j);
161 }
162 }
163
164 }
165
166
167 // Define the minimal size of the window depending
168 // on the number of columns. The maximal size is defined
169 // by the surrounding ViewerFrame class
170 int nColSize = GetColSize(0);
171
172 nHeight = GetRowHeight(0) * (GetNumberRows()+3.5);
173 nWidth = nColSize*(GetNumberCols()+1.5);
174
175 if (!readOnly)
176 {
177 if (nHeight < 400)
178 nHeight = 400;
179 }
180
181 if (nWidth < 600)
182 nWidth = 600;
183
184 EndBatch();
185}
186
187
197void TableViewer::OnKeyDown(wxKeyEvent& event)
198{
199 // connecting the ESC Key with closing the table
200 if (event.GetKeyCode() == WXK_ESCAPE)
201 m_parent->Close();
202 else if (event.GetKeyCode() == WXK_UP)
203 this->MoveCursorUp(false);
204 else if (event.GetKeyCode() == WXK_DOWN)
205 this->MoveCursorDown(false);
206 else if (event.GetKeyCode() == WXK_RETURN)
207 this->MoveCursorDown(false);
208 else if (event.GetKeyCode() == WXK_LEFT)
209 this->MoveCursorLeft(false);
210 else if (event.GetKeyCode() == WXK_RIGHT)
211 this->MoveCursorRight(false);
212 else if (event.GetKeyCode() == WXK_TAB)
213 this->MoveCursorRight(false);
214 else if (event.GetKeyCode() == WXK_DELETE)
215 {
216 if (!readOnly)
218 }
219 else if (event.ControlDown() && event.ShiftDown())
220 {
221 if (event.GetKeyCode() == 'C')
222 copyContents();
223 else if (event.GetKeyCode() == 'V')
225 }
226 else
227 event.Skip();
228}
229
230
240void TableViewer::OnChar(wxKeyEvent& event)
241{
242 if (readOnly)
243 return;
244
245 // Is this the last column?
246 if (this->GetCursorColumn()+1 == this->GetCols())
247 {
248 this->AppendCols();
249 updateFrame();
250 }
251
252 // Is this the last row?
253 if (this->GetCursorRow()+1 == this->GetRows())
254 {
255 this->AppendRows();
256 updateFrame();
257 }
258
259 event.Skip();
260}
261
262
272void TableViewer::OnEnter(wxMouseEvent& event)
273{
274 this->SetFocus();
275 event.Skip();
276}
277
278
289void TableViewer::OnCellChange(wxGridEvent& event)
290{
291 SetCellValue(event.GetRow(), event.GetCol(), event.GetString());
292
293 //updateFrame();
294 UpdateColumnAlignment(GetGridCursorCol());
295 event.Veto();
296}
297
298
308void TableViewer::OnCellSelect(wxGridEvent& event)
309{
310 wxGridCellCoords coords(event.GetRow(), event.GetCol());
311 updateStatusBar(wxGridCellCoordsContainer(coords, coords), &coords);
312 Refresh();
313 event.Skip();
314}
315
316
326void TableViewer::OnCellRangeSelect(wxGridRangeSelectEvent& event)
327{
328 if (event.Selecting())
329 {
330 if (event.GetTopLeftCoords() == wxGridCellCoords(0, 0)
331 && event.GetBottomRightCoords() == wxGridCellCoords(GetRows()-1, GetCols()-1))
332 {
333 updateStatusBar(wxGridCellCoordsContainer(event.GetTopLeftCoords(), event.GetBottomRightCoords()));
334 selectedCells.Clear();
335 }
336 else if (!selectedCells.size())
337 {
338 for (int i = event.GetTopLeftCoords().GetRow(); i <= event.GetBottomRightCoords().GetRow(); i++)
339 {
340 for (int j = event.GetTopLeftCoords().GetCol(); j <= event.GetBottomRightCoords().GetCol(); j++)
341 {
342 selectedCells.Add(wxGridCellCoords(i, j));
343 }
344 }
345
346 updateStatusBar(wxGridCellCoordsContainer(event.GetTopLeftCoords(), event.GetBottomRightCoords()));
347 }
348 else
349 {
350 for (int i = event.GetTopLeftCoords().GetRow(); i <= event.GetBottomRightCoords().GetRow(); i++)
351 {
352 for (int j = event.GetTopLeftCoords().GetCol(); j <= event.GetBottomRightCoords().GetCol(); j++)
353 {
354 selectedCells.Add(wxGridCellCoords(i, j));
355 }
356 }
357
359 }
360 }
361 else
362 selectedCells.Clear();
363}
364
365
375void TableViewer::OnLabelDoubleClick(wxGridEvent& event)
376{
377 if (event.GetCol() >= 0)
378 AutoSizeColumn(event.GetCol());
379}
380
381
391{
392 for (int i = this->GetRows()-1; i >= 0; i--)
393 {
394 if (GetCellValue(i, nCol).length() && GetCellValue(i, nCol) != "---")
395 return i;
396 }
397
398 return 0;
399}
400
401
411{
412 wxFont font = this->GetCellFont(0,0);
413 font.SetWeight(wxFONTWEIGHT_NORMAL);
414
415 if (GetRows() > MAXIMAL_RENDERING_SIZE || GetCols() > MAXIMAL_RENDERING_SIZE)
416 return;
417
418
419 for (int i = 0; i < this->GetRows(); i++)
420 {
421 for (int j = 0; j < this->GetCols(); j++)
422 {
423 if (i+1 == this->GetRows() || j+1 == this->GetCols())
424 {
425 // Surrounding frame
426 SetCellBackgroundColour(i, j, FrameColor);
427 }
428 else if (i < (int)nFirstNumRow)
429 {
430 // Headline
431 SetCellFont(i, j, this->GetCellFont(i, j).MakeBold());
432 SetCellBackgroundColour(i, j, HeadlineColor);
433 SetCellAlignment(wxALIGN_LEFT, i, j);
434 }
435 else if ((i+2 == this->GetRows() || j+2 == this->GetCols())
436 && GetCellBackgroundColour(i, j) != HighlightColor)
437 {
438 // Cells near the boundary, which might have been modified
439 SetCellBackgroundColour(i, j, *wxWHITE);
440 }
441 }
442 }
443}
444
445
455{
456 wxGridCellCoordsContainer coordsContainer;
457
458 // Handle all possible selection types
459 if (GetSelectedCells().size()) // not a block layout
460 coordsContainer = wxGridCellCoordsContainer(GetSelectedCells());
461 else if (GetSelectionBlockTopLeft().size() && GetSelectionBlockBottomRight().size()) // block layout
462 coordsContainer = wxGridCellCoordsContainer(GetSelectionBlockTopLeft()[0], GetSelectionBlockBottomRight()[0]);
463 else if (GetSelectedCols().size()) // multiple selected columns
464 coordsContainer = wxGridCellCoordsContainer(GetSelectedCols(), GetRows()-1, false);
465 else if (GetSelectedRows().size()) // multiple selected rows
466 coordsContainer = wxGridCellCoordsContainer(GetSelectedRows(), GetCols()-1, true);
467 else
468 {
469 // single cell: overwrite and return
470 SetCellValue(GetCursorRow(), GetCursorColumn(), "");
471 return;
472 }
473
474 // Get the extent of the container
475 const wxGridCellsExtent& cellsExtent = coordsContainer.getExtent();
476
477 // Go through the extent and handle all selected columns
478 for (int i = cellsExtent.m_topleft.GetRow(); i <= cellsExtent.m_bottomright.GetRow(); i++)
479 {
480 for (int j = cellsExtent.m_topleft.GetCol(); j <= cellsExtent.m_bottomright.GetCol(); j++)
481 {
482 if (coordsContainer.contains(i, j))
483 SetCellValue(i, j, "");
484 }
485 }
486}
487
488
498bool TableViewer::isNumerical(const std::string& sCell)
499{
500 static std::string sNums = "0123456789,.eE+-* INFinf";
501 return sCell.find_first_not_of(sNums) == std::string::npos;
502}
503
504
515{
516 if (col >= this->GetCols() || col < 0)
517 return false;
518
519 for (int i = nFirstNumRow; i < this->GetRows()-1; i++)
520 {
521 if (GetCellValue(i, col).length() && GetCellValue(i, col) != "---")
522 return false;
523 }
524
525 return true;
526}
527
528
538wxString TableViewer::replaceCtrlChars(const wxString& sStr)
539{
540 wxString sReturn = sStr;
541
542 while (sReturn.find('_') != std::string::npos)
543 sReturn[sReturn.find('_')] = ' ';
544
545 return sReturn;
546}
547
548
560{
561 wxString sSelection;
562
563 // Simple case: only one cell selected
564 if (!(GetSelectedCells().size() || GetSelectedCols().size() || GetSelectedRows().size() || GetSelectionBlockTopLeft().size() || GetSelectionBlockBottomRight().size()))
565 sSelection = copyCell(this->GetCursorRow(), this->GetCursorColumn());
566
567 // More diffcult: more than one cell selected
568 if (GetSelectedCells().size())
569 {
570 // Non-block selection
571 wxGridCellCoordsArray cellarray = GetSelectedCells();
572
573 for (size_t i = 0; i < cellarray.size(); i++)
574 {
575 sSelection += copyCell(cellarray[i].GetRow(), cellarray[i].GetCol());
576
577 if (i < cellarray.size()-1)
578 sSelection += "\t";
579 }
580 }
581 else if (GetSelectionBlockTopLeft().size() && GetSelectionBlockBottomRight().size())
582 {
583 // Block selection
584 wxGridCellCoordsArray topleftarray = GetSelectionBlockTopLeft();
585 wxGridCellCoordsArray bottomrightarray = GetSelectionBlockBottomRight();
586
587 for (int i = topleftarray[0].GetRow(); i <= bottomrightarray[0].GetRow(); i++)
588 {
589 for (int j = topleftarray[0].GetCol(); j <= bottomrightarray[0].GetCol(); j++)
590 {
591 sSelection += copyCell(i,j);
592
593 if (j < bottomrightarray[0].GetCol())
594 sSelection += "\t";
595 }
596
597 if (i < bottomrightarray[0].GetRow())
598 sSelection += "\n";
599 }
600 }
601 else if (GetSelectedCols().size())
602 {
603 // Multiple selected columns
604 wxArrayInt colarray = GetSelectedCols();
605
606 for (int i = 0; i < GetRows(); i++)
607 {
608 for (size_t j = 0; j < colarray.size(); j++)
609 {
610 sSelection += copyCell(i, colarray[j]);
611
612 if (j < colarray.size()-1)
613 sSelection += "\t";
614 }
615
616 if (i < GetRows()-1)
617 sSelection += "\n";
618 }
619 }
620 else if (GetSelectedRows().size())
621 {
622 // Multiple selected rows
623 wxArrayInt rowarray = GetSelectedRows();
624
625 for (size_t i = 0; i < rowarray.size(); i++)
626 {
627 for (int j = 0; j < GetCols(); j++)
628 {
629 sSelection += copyCell(rowarray[i], j);
630
631 if (j < GetCols()-1)
632 sSelection += "\t";
633 }
634
635 if (i < rowarray.size()-1)
636 sSelection += "\n";
637 }
638 }
639
640 if (!sSelection.length())
641 return;
642
643 // Open the clipboard and store the selection
644 if (wxTheClipboard->Open())
645 {
646 wxTheClipboard->SetData(new wxTextDataObject(sSelection));
647 wxTheClipboard->Close();
648 }
649
650 return;
651}
652
653
663void TableViewer::pasteContents(bool useCursor)
664{
665 std::vector<wxString> vTableData;
666
667 // Get the data from the clipboard as a
668 // vector table
669 if (wxTheClipboard->Open())
670 {
671 // Ensure that the data in the clipboard
672 // can be converted into simple text
673 if (wxTheClipboard->IsSupported(wxDF_TEXT))
674 {
675 wxTextDataObject data;
676 wxTheClipboard->GetData(data);
677 vTableData = getLinesFromPaste(data.GetText());
678 }
679
680 wxTheClipboard->Close();
681 }
682 else
683 return;
684
685 // Do nothing if the data is empty
686 if (!vTableData.size())
687 return;
688
689 int nLines = vTableData.size();
690 int nCols = 0;
691 int nSkip = 0;
692
693 if (nLines && !useCursor && !isNumerical(vTableData.front().ToStdString()))
694 {
695 nSkip++;
696 nLines--;
697 }
698
699 // Return if the number of lines is zero
700 if (nLines <= 0)
701 return;
702
703 // Get the number of columns in the data table
704 for (unsigned int i = 0; i < vTableData.size(); i++)
705 {
706 wxStringTokenizer tok(vTableData[i], " ");
707
708 if (nCols < (int)tok.CountTokens())
709 nCols = (int)tok.CountTokens();
710 }
711
712 // Create a place in the grid to paste the data
713 wxGridCellCoords topleft = CreateEmptyGridSpace(nLines, nSkip, nCols, useCursor);
714
715 // Go to the whole data table
716 for (unsigned int i = 0; i < vTableData.size(); i++)
717 {
718 // Tokenize the current line
719 wxStringTokenizer tok(vTableData[i], " ");
720 wxString sLine;
721
722 long long int j = 0;
723
724 // As long as there are more elements in the
725 // tokenizer cache
726 while (tok.HasMoreTokens())
727 {
728 sLine = tok.GetNextToken();
729 sLine.Replace("\1", " ");
730
731 // Remove trailing percentage signs
732 if (sLine[sLine.length()-1] == '%')
733 sLine.erase(sLine.length()-1);
734
735 // Set the value to the correct cell
736 this->SetCellValue(topleft.GetRow()+i-nSkip, topleft.GetCol()+j, sLine);
737 j++;
738
739 // Stop, if the the counter reached the
740 // number of prepared columns
741 if (j == nCols)
742 break;
743 }
744 }
745
746 // Go to the topleft cell and select the entered block of data
747 this->GoToCell(topleft);
748 this->SelectBlock(topleft, wxGridCellCoords(topleft.GetRow()+nLines-1, topleft.GetCol()+nCols-1));
749}
750
751
760{
761 wxGridCellCoordsContainer coordsContainer;
762
763 // Handle all possible selection types
764 if (GetSelectedCells().size()) // not a block layout
765 coordsContainer = wxGridCellCoordsContainer(GetSelectedCells());
766 else if (GetSelectionBlockTopLeft().size() && GetSelectionBlockBottomRight().size()) // block layout
767 coordsContainer = wxGridCellCoordsContainer(GetSelectionBlockTopLeft()[0], GetSelectionBlockBottomRight()[0]);
768 else if (GetSelectedCols().size()) // multiple selected columns
769 coordsContainer = wxGridCellCoordsContainer(GetSelectedCols(), GetRows()-1, false);
770 else if (GetSelectedRows().size()) // multiple selected rows
771 coordsContainer = wxGridCellCoordsContainer(GetSelectedRows(), GetCols()-1, true);
772 else
773 coordsContainer = wxGridCellCoordsContainer(wxGridCellCoords(0, 0), wxGridCellCoords(GetRows()-1, GetCols()-1));
774
775 double minVal = calculateMin(coordsContainer);
776 double maxVal = calculateMax(coordsContainer);
777
778 CellValueShaderDialog dialog(this, minVal, maxVal);
779
780 if (dialog.ShowModal() == wxID_OK)
781 {
782 // Get the extent of the container
783 const wxGridCellsExtent& cellsExtent = coordsContainer.getExtent();
784
785 // Whole columns are selected
786 if ((coordsContainer.isBlock() || coordsContainer.columnsSelected())
787 && cellsExtent.m_topleft.GetRow() <= (int)nFirstNumRow
788 && cellsExtent.m_bottomright.GetRow()+2 >= GetRows())
789 {
790 for (int j = cellsExtent.m_topleft.GetCol(); j <= cellsExtent.m_bottomright.GetCol(); j++)
791 {
792 if (!coordsContainer.contains(cellsExtent.m_topleft.GetRow(), j))
793 continue;
794
795 int h_align, v_align;
796 GetCellAlignment(nFirstNumRow, j, &h_align, &v_align);
797
798 wxGridCellAttr* attr = new wxGridCellAttr(*wxBLACK, *wxWHITE, this->GetDefaultCellFont(),
799 h_align, wxALIGN_CENTER);
800
801 if (m_currentColTypes.size() > (size_t)j && m_currentColTypes[j] == TableColumn::TYPE_LOGICAL)
802 attr->SetRenderer(new AdvBooleanCellRenderer(dialog.getShader()));
803 else
804 attr->SetRenderer(new AdvStringCellRenderer(dialog.getShader()));
805
806 SetColAttr(j, attr, true);
807 }
808 }
809 else
810 {
811 for (int i = cellsExtent.m_topleft.GetRow(); i <= cellsExtent.m_bottomright.GetRow(); i++)
812 {
813 for (int j = cellsExtent.m_topleft.GetCol(); j <= cellsExtent.m_bottomright.GetCol(); j++)
814 {
815 if (!coordsContainer.contains(i, j))
816 continue;
817
818 if (m_currentColTypes.size() > (size_t)j && m_currentColTypes[j] == TableColumn::TYPE_LOGICAL)
819 SetCellRenderer(i, j, new AdvBooleanCellRenderer(dialog.getShader()));
820 else
821 SetCellRenderer(i, j, new AdvStringCellRenderer(dialog.getShader()));
822 }
823 }
824 }
825
826 // Refresh the window to redraw all cells
827 Refresh();
828 }
829}
830
831
841{
842 std::vector<int> vTypes;
843
844 if (col+1 == GetNumberCols())
845 return;
846
848 {
849 GridNumeReTable* gridtab = static_cast<GridNumeReTable*>(GetTable());
850 vTypes = gridtab->getColumnTypes();
851
852 if (col < (int)vTypes.size())
853 {
854 if (col >= (int)m_currentColTypes.size() || m_currentColTypes[col] != vTypes[col])
855 {
856 if (vTypes[col] == TableColumn::TYPE_STRING)
857 {
858 wxGridCellAttr* attr = new wxGridCellAttr(*wxBLACK, *wxWHITE, this->GetDefaultCellFont(),
859 wxALIGN_LEFT, wxALIGN_CENTER);
860 attr->SetRenderer(new AdvStringCellRenderer);
861 SetColAttr(col, attr);
862 }
863 else if (vTypes[col] == TableColumn::TYPE_CATEGORICAL)
864 {
865 wxGridCellAttr* attr = new wxGridCellAttr(*wxBLACK, *wxWHITE, this->GetDefaultCellFont(),
866 wxALIGN_CENTER, wxALIGN_CENTER);
867 attr->SetRenderer(new AdvStringCellRenderer);
868 SetColAttr(col, attr);
869 }
870 else if (vTypes[col] == TableColumn::TYPE_LOGICAL)
871 {
872 wxGridCellAttr* attr = new wxGridCellAttr(*wxBLACK, *wxWHITE, this->GetDefaultCellFont(),
873 wxALIGN_CENTER, wxALIGN_CENTER);
874 attr->SetRenderer(new AdvBooleanCellRenderer);
875 SetColAttr(col, attr);
876 }
877 else // All other cases
878 {
879 wxGridCellAttr* attr = new wxGridCellAttr(*wxBLACK, *wxWHITE, this->GetDefaultCellFont(),
880 wxALIGN_RIGHT, wxALIGN_CENTER);
881 attr->SetRenderer(new AdvStringCellRenderer);
882 SetColAttr(col, attr);
883 }
884 }
885 }
886
887 m_currentColTypes = vTypes;
888 }
889 else
890 {
891 // Search the boundaries and color the frame correspondingly
892 for (int i = (int)nFirstNumRow; i < GetNumberRows(); i++)
893 {
894 if (GetCellValue(i, col)[0] == '"')
895 SetCellAlignment(wxALIGN_LEFT, i, col);
896 else
897 SetCellAlignment(wxALIGN_RIGHT, i, col);
898 }
899 }
900}
901
902
915wxGridCellCoords TableViewer::CreateEmptyGridSpace(int rows, int headrows, int cols, bool useCursor)
916{
917 wxGridCellCoords topLeft(headrows,0);
918
919 if (useCursor)
920 {
921 // We use the cursor in this case (this
922 // implies overriding existent data)
923 topLeft = m_lastRightClick;
924
925 while (this->GetRowLabelValue(topLeft.GetRow()) == "#")
926 topLeft.SetRow(topLeft.GetRow()+1);
927
928 if (this->GetCols()-1 < topLeft.GetCol() + cols)
929 this->AppendCols(topLeft.GetCol() + cols - this->GetCols()+1, true);
930
931 if (this->GetRows()-1 < topLeft.GetRow() + rows)
932 this->AppendRows(topLeft.GetRow() + rows - this->GetRows()+1, true);
933 }
934 else
935 {
936 // In this case, we search for empty columns
937 // to insert the data
938 for (int i = this->GetCols()-1; i >= 0; i--)
939 {
940 if (!isEmptyCol(i))
941 {
942 if (this->GetCols()-i-1 < cols+1)
943 this->AppendCols(cols-(this->GetCols()-(i+1))+1);
944
945 topLeft.SetCol(i+1);
946 break;
947 }
948
949 if (!i)
950 {
951 if (this->GetCols()-1 < cols)
952 this->AppendCols(cols-(this->GetCols()-1));
953
954 topLeft.SetCol(0);
955 }
956 }
957
958 // Now create the needed space
959 for (int i = 0; i < this->GetRows(); i++)
960 {
961 if (this->GetRowLabelValue(i) != "#")
962 {
963 if (i >= headrows)
964 {
965 topLeft.SetRow(i);
966
967 if (this->GetRows()-i < rows+1)
968 {
969 this->AppendRows(rows-(this->GetRows()-i)+1);
970 }
971 }
972 else
973 {
974 nFirstNumRow = headrows;
975 this->InsertRows(i, headrows-(i-1));
976
977 for (int j = i-1; j < GetRows(); j++)
978 this->SetRowLabelValue(j, this->GetRowLabelValue(j));
979
980 if (this->GetRows()-headrows < rows+1)
981 {
982 this->AppendRows(rows-(this->GetRows()-headrows)+1);
983 }
984 }
985
986 break;
987 }
988 }
989 }
990
991 // Redraw the grey frame
992 updateFrame();
993
994 return topLeft;
995}
996
997
1007{
1009 return *static_cast<mu::value_type*>(GetTable()->GetValueAsCustom(row, col, "complex"));
1010
1011 if (GetCellValue(row, col)[0] != '"' && isNumerical(GetCellValue(row, col).ToStdString()))
1012 return StrToCmplx(GetCellValue(row, col).ToStdString());
1013
1014 return NAN;
1015}
1016
1017
1027{
1029 return static_cast<GridNumeReTable*>(GetTable())->min(coords);
1030
1031 const wxGridCellsExtent& cellExtent = coords.getExtent();
1032 double dMin = NAN;
1033
1034 for (int i = cellExtent.m_topleft.GetRow(); i <= cellExtent.m_bottomright.GetRow(); i++)
1035 {
1036 for (int j = cellExtent.m_topleft.GetCol(); j <= cellExtent.m_bottomright.GetCol(); j++)
1037 {
1038 if (!coords.contains(i, j))
1039 continue;
1040
1041 if (isnan(dMin) || CellToCmplx(i, j).real() < dMin)
1042 dMin = CellToCmplx(i, j).real();
1043 }
1044 }
1045
1046 return dMin;
1047}
1048
1049
1059{
1061 return static_cast<GridNumeReTable*>(GetTable())->max(coords);
1062
1063 const wxGridCellsExtent& cellExtent = coords.getExtent();
1064 double dMax = NAN;
1065
1066 for (int i = cellExtent.m_topleft.GetRow(); i <= cellExtent.m_bottomright.GetRow(); i++)
1067 {
1068 for (int j = cellExtent.m_topleft.GetCol(); j <= cellExtent.m_bottomright.GetCol(); j++)
1069 {
1070 if (!coords.contains(i, j))
1071 continue;
1072
1073 if (isnan(dMax) || CellToCmplx(i, j).real() > dMax)
1074 dMax = CellToCmplx(i,j).real();
1075 }
1076 }
1077
1078 return dMax;
1079}
1080
1081
1091{
1093 return static_cast<GridNumeReTable*>(GetTable())->sum(coords);
1094
1095 const wxGridCellsExtent& cellExtent = coords.getExtent();
1096 mu::value_type dSum = 0;
1097
1098 for (int i = cellExtent.m_topleft.GetRow(); i <= cellExtent.m_bottomright.GetRow(); i++)
1099 {
1100 for (int j = cellExtent.m_topleft.GetCol(); j <= cellExtent.m_bottomright.GetCol(); j++)
1101 {
1102 if (!coords.contains(i, j))
1103 continue;
1104
1105 if (!mu::isnan(CellToCmplx(i, j)))
1106 dSum += CellToCmplx(i,j);
1107 }
1108 }
1109
1110 return dSum;
1111}
1112
1113
1123{
1125 return static_cast<GridNumeReTable*>(GetTable())->avg(coords);
1126
1127 const wxGridCellsExtent& cellExtent = coords.getExtent();
1128 mu::value_type dSum = 0;
1129 int nCount = 0;
1130
1131 for (int i = cellExtent.m_topleft.GetRow(); i <= cellExtent.m_bottomright.GetRow(); i++)
1132 {
1133 for (int j = cellExtent.m_topleft.GetCol(); j <= cellExtent.m_bottomright.GetCol(); j++)
1134 {
1135 if (!coords.contains(i, j))
1136 continue;
1137
1138 if (!mu::isnan(CellToCmplx(i, j)))
1139 {
1140 nCount++;
1141 dSum += CellToCmplx(i, j);
1142 }
1143 }
1144 }
1145
1146 if (nCount)
1147 return dSum / (double)nCount;
1148
1149 return 0.0;
1150}
1151
1152
1163void TableViewer::updateStatusBar(const wxGridCellCoordsContainer& coords, wxGridCellCoords* cursor /*= nullptr*/)
1164{
1165 if (!m_statusBar)
1166 return;
1167
1168 // Get the dimensions
1169 wxString dim = "Dim: ";
1170 dim << this->GetRowLabelValue(GetRows()-2) << "x" << GetCols()-1;
1171
1172 // Get the current cursor position
1173 wxString sel = "Cur: ";
1174 if (cursor)
1175 sel << this->GetRowLabelValue(cursor->GetRow()) << "," << cursor->GetCol()+1;
1176 else
1177 sel << "--,--";
1178
1179 // Calculate the simple statistics
1180 wxString statustext = "Min: " + toString(calculateMin(coords), STATUSBAR_PRECISION);
1181 statustext << " | Max: " << toString(calculateMax(coords), STATUSBAR_PRECISION);
1182 statustext << " | Sum: " << toString(calculateSum(coords), 2*STATUSBAR_PRECISION);
1183 statustext << " | Avg: " << toString(calculateAvg(coords), 2*STATUSBAR_PRECISION);
1184
1185 // Set the status bar valuey
1186 m_statusBar->SetStatusText(dim);
1187 m_statusBar->SetStatusText(sel, 1);
1188 m_statusBar->SetStatusText(statustext, 2);
1189}
1190
1191
1199{
1200 if (!m_parentPanel)
1201 return;
1202
1203 wxMenuBar* menuBar = m_parentPanel->getMenuBar();
1204
1205 // Create the file menu
1206 wxMenu* menuFile = new wxMenu();
1207
1208 menuFile->Append(ID_MENU_SAVE, _guilang.get("GUI_MENU_SAVEFILE"));
1209 menuFile->Append(ID_MENU_SAVE_AS, _guilang.get("GUI_MENU_SAVEFILEAS"));
1210
1211 menuBar->Append(menuFile, _guilang.get("GUI_MENU_FILE"));
1212
1213 // Create the edit menu
1214 wxMenu* menuEdit = new wxMenu();
1215
1216 menuEdit->Append(ID_MENU_COPY, _guilang.get("GUI_COPY_TABLE_CONTENTS") + "\tCtrl-C");
1217 menuEdit->Append(ID_MENU_PASTE, _guilang.get("GUI_PASTE_TABLE_CONTENTS") + "\tCtrl-V");
1218 menuEdit->Append(ID_MENU_PASTE_HERE, _guilang.get("GUI_PASTE_TABLE_CONTENTS_HERE") + "\tCtrl-Shift-V");
1219 menuEdit->AppendSeparator();
1220 menuEdit->Append(ID_MENU_INSERT_ROW, _guilang.get("GUI_INSERT_TABLE_ROW"));
1221 menuEdit->Append(ID_MENU_INSERT_COL, _guilang.get("GUI_INSERT_TABLE_COL"));
1222 menuEdit->Append(ID_MENU_INSERT_CELL, _guilang.get("GUI_INSERT_TABLE_CELL"));
1223 menuEdit->AppendSeparator();
1224 menuEdit->Append(ID_MENU_REMOVE_ROW, _guilang.get("GUI_REMOVE_TABLE_ROW"));
1225 menuEdit->Append(ID_MENU_REMOVE_COL, _guilang.get("GUI_REMOVE_TABLE_COL"));
1226 menuEdit->Append(ID_MENU_REMOVE_CELL, _guilang.get("GUI_REMOVE_TABLE_CELL"));
1227
1228 menuBar->Append(menuEdit, _guilang.get("GUI_MENU_EDIT"));
1229
1230 // Create the tools menu
1231 wxMenu* menuTools = new wxMenu();
1232
1233 menuTools->Append(ID_MENU_RELOAD, _guilang.get("GUI_TABLE_RELOAD") + "\tCtrl-R");
1234 menuTools->Append(ID_MENU_CHANGE_COL_TYPE, _guilang.get("GUI_TABLE_CHANGE_COL_TYPE") + "\tCtrl-T");
1235 menuTools->Append(ID_MENU_CVS, _guilang.get("GUI_TABLE_CVS") + "\tCtrl-Shift-F");
1236
1237 menuBar->Append(menuTools, _guilang.get("GUI_MENU_TOOLS"));
1238
1239 wxFrame* parFrame = m_parentPanel->getFrame();
1240 parFrame->Bind(wxEVT_MENU, &TableViewer::OnMenu, this);
1241}
1242
1243
1254wxString TableViewer::copyCell(int row, int col)
1255{
1256 return this->GetCellValue(row, col);
1257}
1258
1259
1270{
1271 wxArrayString toks = wxStringTokenize(text);
1272 text.clear();
1273
1274 for (size_t i = 0; i < toks.size(); i++)
1275 {
1276 if (isNumerical(toks[i].ToStdString()))
1277 toks[i].Replace(",", ".");
1278
1279 if (text.length())
1280 text += " ";
1281
1282 text += toks[i];
1283 }
1284}
1285
1286
1296{
1297 if (!text.length())
1298 return;
1299
1300 bool bKeepColumns = false;
1301
1302 // Determine, if columns shall be kept
1303 if (text.find(' ') == std::string::npos)
1304 {
1305 bKeepColumns = true;
1306
1307 if (text[0] == '\t')
1308 text.insert(0, "---");
1309 }
1310
1311 size_t pos = 0;
1312
1313 // Replace tabulators with whitespace and
1314 // an empty cell, if necessary
1315 while ((pos = text.find('\t')) != std::string::npos)
1316 {
1317 text[pos] = ' ';
1318
1319 if (bKeepColumns && pos+1 < text.length() && text[pos+1] == '\t')
1320 text.insert(pos + 1, "---");
1321 }
1322}
1323
1324
1334{
1335 SetTable(new GridNumeReTable(NumeRe::Table(1, 1)), true);
1336
1337 isGridNumeReTable = true;
1338 nFirstNumRow = 1;
1339
1340 layoutGrid();
1341
1342 updateStatusBar(wxGridCellCoordsContainer(wxGridCellCoords(0,0), wxGridCellCoords(this->GetRows()-1, this->GetCols()-1)));
1343}
1344
1345
1355std::vector<wxString> TableViewer::getLinesFromPaste(const wxString& data)
1356{
1357 std::vector<wxString> vPaste;
1358 wxString sClipboard = data;
1359 wxString sLine;
1360
1361 // Endless loop
1362 while (true)
1363 {
1364 bool tabSeparated = false;
1365 // Abort, if the clipboards contents indicate an
1366 // empty table
1367 if (!sClipboard.length() || sClipboard == "\n")
1368 break;
1369
1370 // Get the next line
1371 sLine = sClipboard.substr(0, sClipboard.find('\n'));
1372
1373 // Remove the carriage return character
1374 if (sLine.length() && sLine[sLine.length()-1] == (char)13)
1375 sLine.erase(sLine.length()-1);
1376
1377 // determine the separation mode
1378 if (sLine.find('\t') != std::string::npos)
1379 tabSeparated = true;
1380
1381 // Remove the obtained line from the clipboard
1382 if (sClipboard.find('\n') != std::string::npos)
1383 sClipboard.erase(0, sClipboard.find('\n')+1);
1384 else
1385 sClipboard.clear();
1386
1387 // Replace whitespaces with underscores, if the current
1388 // line also contains tabulator characters and it is a
1389 // non-numerical line
1390 if (!isNumerical(sLine.ToStdString())
1391 && (tabSeparated || sLine.find(" ") != std::string::npos))
1392 {
1393 for (unsigned int i = 1; i < sLine.length()-1; i++)
1394 {
1395 if (sLine[i] == ' ' && sLine[i-1] != ' ' && sLine[i+1] != ' ')
1396 sLine[i] = '\1';
1397 }
1398 }
1399
1400 // Now replace the tabulator characters with
1401 // whitespaces
1402 replaceTabSign(sLine);
1403
1404 // Ignore empty lines
1405 if (sLine.find_first_not_of(' ') == std::string::npos)
1406 continue;
1407
1408 // Replace the decimal sign, if the line is numerical,
1409 // otherwise try to detect, whether the comma is used
1410 // to separate the columns
1411 if (sLine.find(',') != std::string::npos && (sLine.find('.') == std::string::npos || tabSeparated))
1412 replaceDecimalSign(sLine);
1413 else if (!tabSeparated && sLine.find(',') != std::string::npos && sLine.find(';') != std::string::npos)
1414 {
1415 // The semicolon is used to separate the columns
1416 // in this case
1417 for (unsigned int i = 0; i < sLine.length(); i++)
1418 {
1419 if (sLine[i] == ',')
1420 sLine[i] = '.';
1421
1422 if (sLine[i] == ';')
1423 {
1424 sLine[i] = ' ';
1425 }
1426 }
1427 }
1428 else if (!tabSeparated)
1429 {
1430 // The comma is used to separate the columns
1431 // in this case
1432 for (unsigned int i = 0; i < sLine.length(); i++)
1433 {
1434 if (sLine[i] == ',')
1435 {
1436 sLine[i] = ' ';
1437 }
1438 }
1439 }
1440
1441 // Add the decoded line to the vector table
1442 vPaste.push_back(sLine);
1443 }
1444
1445 return vPaste;
1446}
1447
1448
1459void TableViewer::SetData(NumeRe::Container<std::string>& _stringTable, const std::string& sName, const std::string& sIntName)
1460{
1461 m_displayName = sName;
1462 m_intName = sIntName;
1463
1464 if (!_stringTable.getCols() || !_stringTable.getRows())
1465 {
1467 return;
1468 }
1469
1470 this->CreateGrid(_stringTable.getRows()+1, _stringTable.getCols()+1);
1471
1472 // String tables and clusters do not have a headline
1473 nFirstNumRow = 0;
1474 isGridNumeReTable = false;
1475
1476 for (size_t i = 0; i < _stringTable.getRows()+1; i++)
1477 {
1478 this->SetRowLabelValue(i, GetRowLabelValue(i));
1479
1480 for (size_t j = 0; j < _stringTable.getCols()+1; j++)
1481 {
1482 if (i == _stringTable.getRows() || j == _stringTable.getCols())
1483 {
1484 this->SetColLabelValue(j, toString(j+1));
1485 continue;
1486 }
1487
1488 if (_stringTable.get(i, j).length())
1489 this->SetCellValue(i, j, replaceControlCharacters(_stringTable.get(i, j)));
1490 }
1491 }
1492
1493 if (m_parentPanel)
1494 {
1495 wxMenuBar* menuBar = m_parentPanel->getMenuBar();
1496
1497 wxMenu* toolsMenu = menuBar->GetMenu(menuBar->FindMenu(_guilang.get("GUI_MENU_TOOLS")));
1498
1499 if (toolsMenu)
1500 toolsMenu->Enable(ID_MENU_CHANGE_COL_TYPE, false);
1501 }
1502
1503 layoutGrid();
1504
1505 updateStatusBar(wxGridCellCoordsContainer(wxGridCellCoords(0,0), wxGridCellCoords(this->GetRows()-1, this->GetCols()-1)));
1506}
1507
1508
1520void TableViewer::SetData(NumeRe::Table& _table, const std::string& sName, const std::string& sIntName)
1521{
1522 if (m_parentPanel)
1523 m_parentPanel->update(_table.getMetaData());
1524
1525 m_displayName = sName;
1526 m_intName = sIntName;
1527
1528 // Create an empty table, if necessary
1529 if (_table.isEmpty() || !_table.getLines())
1530 {
1532 return;
1533 }
1534
1535 // Store the number headlines and create the data
1536 // providing object
1537 nFirstNumRow = _table.getHeadCount();
1538 SetTable(new GridNumeReTable(std::move(_table)), true);
1539 isGridNumeReTable = true;
1540
1541 layoutGrid();
1542
1543 updateStatusBar(wxGridCellCoordsContainer(wxGridCellCoords(0,0), wxGridCellCoords(this->GetRows()-1, this->GetCols()-1)));
1544}
1545
1546
1558{
1559 readOnly = isReadOnly;
1560
1561 if (!readOnly)
1562 {
1563 m_popUpMenu.Append(ID_MENU_PASTE, _guilang.get("GUI_PASTE_TABLE_CONTENTS"));
1564 m_popUpMenu.Append(ID_MENU_PASTE_HERE, _guilang.get("GUI_PASTE_TABLE_CONTENTS_HERE"));
1565 m_popUpMenu.AppendSeparator();
1566 m_popUpMenu.Append(ID_MENU_INSERT_ROW, _guilang.get("GUI_INSERT_TABLE_ROW"));
1567 m_popUpMenu.Append(ID_MENU_INSERT_COL, _guilang.get("GUI_INSERT_TABLE_COL"));
1568 m_popUpMenu.Append(ID_MENU_INSERT_CELL, _guilang.get("GUI_INSERT_TABLE_CELL"));
1569 m_popUpMenu.AppendSeparator();
1570 m_popUpMenu.Append(ID_MENU_REMOVE_ROW, _guilang.get("GUI_REMOVE_TABLE_ROW"));
1571 m_popUpMenu.Append(ID_MENU_REMOVE_COL, _guilang.get("GUI_REMOVE_TABLE_COL"));
1572 m_popUpMenu.Append(ID_MENU_REMOVE_CELL, _guilang.get("GUI_REMOVE_TABLE_CELL"));
1573
1574 if (m_parentPanel)
1575 {
1576 wxMenuBar* menuBar = m_parentPanel->getMenuBar();
1577
1578 wxMenu* toolsMenu = menuBar->GetMenu(menuBar->FindMenu(_guilang.get("GUI_MENU_TOOLS")));
1579
1580 if (toolsMenu)
1581 toolsMenu->Enable(ID_MENU_RELOAD, false);
1582 }
1583 }
1584 else if (m_parentPanel)
1585 {
1586 wxMenuBar* menuBar = m_parentPanel->getMenuBar();
1587
1588 wxMenu* editMenu = menuBar->GetMenu(menuBar->FindMenu(_guilang.get("GUI_MENU_EDIT")));
1589
1590 if (editMenu)
1591 {
1592 editMenu->Enable(ID_MENU_PASTE, false);
1593 editMenu->Enable(ID_MENU_PASTE_HERE, false);
1594 editMenu->Enable(ID_MENU_INSERT_ROW, false);
1595 editMenu->Enable(ID_MENU_INSERT_COL, false);
1596 editMenu->Enable(ID_MENU_INSERT_CELL, false);
1597 editMenu->Enable(ID_MENU_REMOVE_ROW, false);
1598 editMenu->Enable(ID_MENU_REMOVE_COL, false);
1599 editMenu->Enable(ID_MENU_REMOVE_CELL, false);
1600 }
1601 }
1602}
1603
1604
1615void TableViewer::SetDefaultSize(size_t rows, size_t cols)
1616{
1617 CreateGrid(rows+1,cols+1);
1618
1619 for (size_t i = 0; i < rows+1; i++)
1620 {
1621 SetRowLabelValue(i, GetRowLabelValue(i));
1622
1623 for (size_t j = 0; j < cols+1; j++)
1624 {
1625 if (i < 1)
1626 {
1627 SetCellAlignment(i, j, wxALIGN_RIGHT, wxALIGN_CENTRE);
1628 SetCellFont(i, j, this->GetCellFont(i, j).MakeBold());
1629 SetCellBackgroundColour(i, j, *wxLIGHT_GREY);
1630 }
1631 else if (i == rows || j == cols)
1632 {
1633 SetColLabelValue(j, GetColLabelValue(j));
1634 SetCellBackgroundColour(i, j, wxColor(230,230,230));
1635 SetReadOnly(i, j, readOnly);
1636 continue;
1637 }
1638 else
1639 SetCellAlignment(i, j, wxALIGN_RIGHT, wxALIGN_CENTER);
1640
1641 SetReadOnly(i, j, readOnly);
1642 }
1643 }
1644}
1645
1646
1655void TableViewer::OnCellRightClick(wxGridEvent& event)
1656{
1657 m_lastRightClick.Set(event.GetRow(), event.GetCol());
1658
1659 if (!readOnly)
1660 {
1661 for (int i = ID_MENU_INSERT_ROW; i <= ID_MENU_REMOVE_CELL; i++)
1662 m_popUpMenu.Enable(i,true);
1663
1664 m_popUpMenu.Enable(ID_MENU_PASTE_HERE, true);
1665 }
1666
1667 int id = GetPopupMenuSelectionFromUser(m_popUpMenu, event.GetPosition());
1668
1669 if (id == wxID_NONE)
1670 m_lastRightClick.Set(-1, -1);
1671 else
1672 {
1673 wxCommandEvent evt(wxEVT_MENU, id);
1674 GetEventHandler()->ProcessEvent(evt);
1675 }
1676}
1677
1678
1687void TableViewer::OnLabelRightClick(wxGridEvent& event)
1688{
1689 m_lastRightClick.Set(event.GetRow(), event.GetCol());
1690
1691 if (!readOnly)
1692 {
1693 // Enable the row entries for row labels and
1694 // the column entries for columns
1695 if (event.GetRow() == -1)
1696 {
1697 m_popUpMenu.Enable(ID_MENU_INSERT_ROW, false);
1698 m_popUpMenu.Enable(ID_MENU_REMOVE_ROW, false);
1699 m_popUpMenu.Enable(ID_MENU_INSERT_COL, true);
1700 m_popUpMenu.Enable(ID_MENU_REMOVE_COL, true);
1701 }
1702 else if (event.GetCol() == -1)
1703 {
1704 m_popUpMenu.Enable(ID_MENU_INSERT_ROW, true);
1705 m_popUpMenu.Enable(ID_MENU_REMOVE_ROW, true);
1706 m_popUpMenu.Enable(ID_MENU_INSERT_COL, false);
1707 m_popUpMenu.Enable(ID_MENU_REMOVE_COL, false);
1708 }
1709
1710 m_popUpMenu.Enable(ID_MENU_INSERT_CELL, false);
1711 m_popUpMenu.Enable(ID_MENU_REMOVE_CELL, false);
1712 m_popUpMenu.Enable(ID_MENU_PASTE_HERE, false);
1713 }
1714
1715 int id = GetPopupMenuSelectionFromUser(m_popUpMenu, event.GetPosition());
1716
1717 if (id == wxID_NONE)
1718 m_lastRightClick.Set(-1, -1);
1719 else
1720 {
1721 wxCommandEvent evt(wxEVT_MENU, id);
1722 GetEventHandler()->ProcessEvent(evt);
1723 }
1724}
1725
1726
1737void TableViewer::OnMenu(wxCommandEvent& event)
1738{
1739 if (m_lastRightClick.GetRow() == -1 && m_lastRightClick.GetCol() == -1)
1740 m_lastRightClick.Set(GetCursorRow(), GetCursorColumn());
1741
1742 switch (event.GetId())
1743 {
1744 case ID_MENU_SAVE:
1745 saveTable();
1746 break;
1747 case ID_MENU_SAVE_AS:
1748 saveTable(true);
1749 break;
1750 // fallthrough intended
1751 case ID_MENU_INSERT_ROW:
1752 case ID_MENU_INSERT_COL:
1754 insertElement(event.GetId());
1755 break;
1756 case ID_MENU_REMOVE_ROW:
1757 case ID_MENU_REMOVE_COL:
1759 removeElement(event.GetId());
1760 break;
1761 case ID_MENU_COPY:
1762 copyContents();
1763 break;
1764 case ID_MENU_PASTE:
1765 pasteContents();
1766 break;
1767 case ID_MENU_PASTE_HERE:
1768 pasteContents(true);
1769 break;
1770 case ID_MENU_RELOAD:
1771 reloadTable();
1772 break;
1774 changeColType();
1775 break;
1776 case ID_MENU_CVS:
1778 break;
1779 }
1780
1781 m_lastRightClick.Set(-1, -1);
1782}
1783
1784
1794{
1795 if (id == ID_MENU_INSERT_ROW)
1796 {
1797 // New row
1798 if (m_lastRightClick.GetRow() < (int)nFirstNumRow)
1799 nFirstNumRow++;
1800
1801 InsertRows(m_lastRightClick.GetRow());
1802 }
1803 else if (id == ID_MENU_INSERT_COL)
1804 {
1805 // New column
1806 InsertCols(m_lastRightClick.GetCol());
1807 }
1808 else
1809 {
1810 // New cell -> will append an empty line
1811 // and move the trailing cells one cell
1812 // downwards
1813 int nLastLine = findLastElement(m_lastRightClick.GetCol());
1814
1815 if (nLastLine+2 == GetRows())
1816 AppendRows();
1817
1818 for (int i = nLastLine; i >= m_lastRightClick.GetRow(); i--)
1819 {
1820 SetCellValue(i+1, m_lastRightClick.GetCol(), GetCellValue(i, m_lastRightClick.GetCol()));
1821 }
1822
1823 SetCellValue(m_lastRightClick.GetRow(), m_lastRightClick.GetCol(), "");
1824 }
1825
1826 updateFrame();
1827}
1828
1829
1839{
1840 if (id == ID_MENU_REMOVE_ROW)
1841 {
1842 // Remove row
1843 if (m_lastRightClick.GetRow() < (int)nFirstNumRow)
1844 nFirstNumRow--;
1845
1846 DeleteRows(m_lastRightClick.GetRow());
1847 }
1848 else if (id == ID_MENU_REMOVE_COL)
1849 {
1850 // Remove column
1851 DeleteCols(m_lastRightClick.GetCol());
1852 }
1853 else
1854 {
1855 // Remove cell -> implies that the trailing cells
1856 // will move one cell upwards
1857 int nLastLine = findLastElement(m_lastRightClick.GetCol());
1858
1859 for (int i = m_lastRightClick.GetRow(); i < nLastLine; i++)
1860 {
1861 this->SetCellValue(i, m_lastRightClick.GetCol(), this->GetCellValue(i+1, m_lastRightClick.GetCol()));
1862 }
1863
1864 this->SetCellValue(nLastLine, m_lastRightClick.GetCol(), "");
1865 }
1866
1867 updateFrame();
1868}
1869
1870
1879void TableViewer::saveTable(bool saveAs)
1880{
1882 return;
1883
1885 std::vector<std::string> vPaths = term->getPathSettings();
1886 std::string filename = m_displayName.substr(0, m_displayName.find_first_of("({")) + ".ndat";
1887
1888 // Get the filename from the user
1889 if (saveAs || !filename.length())
1890 {
1891 wxFileDialog fd(this, _guilang.get("GUI_VARVIEWER_SAVENAME"), vPaths[SAVEPATH], filename,
1892 _guilang.get("COMMON_FILETYPE_NDAT") + " (*.ndat)|*.ndat|"
1893 + _guilang.get("COMMON_FILETYPE_DAT") + " (*.dat)|*.dat|"
1894 + _guilang.get("COMMON_FILETYPE_TXT") + " (*.txt)|*.txt|"
1895 + _guilang.get("COMMON_FILETYPE_CSV") + " (*.csv)|*.csv|"
1896 + _guilang.get("COMMON_FILETYPE_XLS") + " (*.xls)|*.xls|"
1897 + _guilang.get("COMMON_FILETYPE_TEX") + " (*.tex)|*.tex",
1898 wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
1899
1900 if (fd.ShowModal() == wxID_CANCEL)
1901 return;
1902
1903 filename = fd.GetPath().ToStdString();
1904 }
1905 else
1906 filename = vPaths[SAVEPATH ] + "/" + filename;
1907
1908 // Try to open the file type
1909 std::unique_ptr<NumeRe::GenericFile> file(NumeRe::getFileByType(filename));
1910
1911 if (!file)
1912 {
1913 wxMessageBox("Cannot save to this file: " + filename, "NumeRe: Error", wxID_OK | wxICON_ERROR, this);
1914 return;
1915 }
1916
1917 // Get a reference to the table and copy the
1918 // necessary fields to the file
1919 NumeRe::Table& table = static_cast<GridNumeReTable*>(GetTable())->getTableRef();
1920 file->setDimensions(table.getLines(), table.getCols());
1921 file->setData(&table.getTableData(), table.getLines(), table.getCols());
1922 file->setTableName(m_displayName.substr(0, m_displayName.find_first_of("({")));
1923 file->setTextfilePrecision(7);
1924
1925 if (file->getExtension() == "ndat")
1926 static_cast<NumeRe::NumeReDataFile*>(file.get())->setComment(table.getMetaData().comment);
1927
1928 // Try to write to the file
1929 try
1930 {
1931 file->write();
1932 }
1933 catch (...)
1934 {
1935 wxMessageBox("Cannot save to this file: " + filename, "NumeRe: Error", wxID_OK | wxICON_ERROR, this);
1936 return;
1937 }
1938}
1939
1940
1949{
1950 if (!m_parentPanel)
1951 return;
1952
1954
1956 {
1957 NumeRe::Table _tab = term->getTable(m_intName);
1959 }
1960 else
1961 {
1963 SetData(_strTab, m_displayName, m_intName);
1964 }
1965}
1966
1967
1976{
1977 if (!isGridNumeReTable)
1978 return;
1979
1980 wxGridCellCoordsContainer coordsContainer;
1981
1982 // Handle all possible selection types
1983 if (GetSelectionBlockTopLeft().size() && GetSelectionBlockBottomRight().size()) // block layout
1984 coordsContainer = wxGridCellCoordsContainer(GetSelectionBlockTopLeft()[0], GetSelectionBlockBottomRight()[0]);
1985 else if (GetSelectedCols().size()) // multiple selected columns
1986 coordsContainer = wxGridCellCoordsContainer(GetSelectedCols(), GetRows()-1, false);
1987 else if (GetSelectedCells().size())
1988 coordsContainer = wxGridCellCoordsContainer(GetSelectedCells());
1989 else
1990 coordsContainer = wxGridCellCoordsContainer(wxArrayInt(1, GetCursorColumn()), GetRows()-1, false);
1991
1992 // Get the target column type
1993 std::vector<std::string> vTypes = TableColumn::getTypesAsString();
1994 wxArrayString types;
1995
1996 for (const auto& t : vTypes)
1997 {
1998 types.Add(t);
1999 }
2000
2001 wxString strType = wxGetSingleChoice(_guilang.get("GUI_TABLE_CHANGE_TYPE"), _guilang.get("GUI_TABLE_CHANGE_TYPE_HEAD"), types, 0, this);
2002
2003 if (!strType.length())
2004 return;
2005
2006 // Get the type ID
2007 TableColumn::ColumnType type = TableColumn::stringToType(strType.ToStdString());
2008
2009 // Change the column types
2010 wxGridCellsExtent _extent = coordsContainer.getExtent();
2011 NumeRe::Table& _table = static_cast<GridNumeReTable*>(GetTable())->getTableRef();
2012
2013 for (int j = _extent.m_topleft.GetCol(); j <= _extent.m_bottomright.GetCol(); j++)
2014 {
2015 if (!coordsContainer.contains(0, j))
2016 continue;
2017
2018 // Change the typ of this column
2019 if (_table.setColumnType(j, type))
2020 {
2021 // We have to refresh the alignment and to update the
2022 // renderers as well
2024 }
2025 }
2026
2027 // Refresh now, bc. we probably changed the renderers
2028 Refresh();
2029}
2030
2031
2040{
2041 SaveEditControlValue();
2042}
2043
2044
2053{
2054 // Simple case: only one cell
2055 if (!(GetSelectedCells().size()
2056 || GetSelectedCols().size()
2057 || GetSelectedRows().size()
2058 || GetSelectionBlockTopLeft().size()
2059 || GetSelectionBlockBottomRight().size()))
2060 return this->GetCellValue(this->GetCursorRow(), this->GetCursorColumn());
2061
2062 wxString values;
2063
2064 // More difficult: multiple selections
2065 if (GetSelectedCells().size())
2066 {
2067 // not a block layout
2068 wxGridCellCoordsArray cellarray = GetSelectedCells();
2069
2070 for (size_t i = 0; i < cellarray.size(); i++)
2071 {
2072 values += this->GetCellValue(cellarray[i]) + ",";
2073 }
2074 }
2075 else if (GetSelectionBlockTopLeft().size() && GetSelectionBlockBottomRight().size())
2076 {
2077 // block layout
2078 wxGridCellCoordsArray topleftarray = GetSelectionBlockTopLeft();
2079 wxGridCellCoordsArray bottomrightarray = GetSelectionBlockBottomRight();
2080
2081 for (int i = topleftarray[0].GetRow(); i <= bottomrightarray[0].GetRow(); i++)
2082 {
2083 for (int j = topleftarray[0].GetCol(); j <= bottomrightarray[0].GetCol(); j++)
2084 {
2085 values += GetCellValue(i, j) + ",";
2086 }
2087 }
2088 }
2089 else if (GetSelectedCols().size())
2090 {
2091 // multiple selected columns
2092 wxArrayInt colarray = GetSelectedCols();
2093
2094 for (int i = 0; i < GetRows(); i++)
2095 {
2096 for (size_t j = 0; j < colarray.size(); j++)
2097 {
2098 values += this->GetCellValue(i, colarray[j]) + ",";
2099 }
2100 }
2101 }
2102 else
2103 {
2104 // multiple selected rows
2105 wxArrayInt rowarray = GetSelectedRows();
2106
2107 for (size_t i = 0; i < rowarray.size(); i++)
2108 {
2109 for (int j = 0; j < GetCols(); j++)
2110 {
2111 values += this->GetCellValue(rowarray[i], j) + ",";
2112 }
2113 }
2114 }
2115
2116 if (values.length())
2117 return values.substr(0, values.length()-1);
2118
2119 return "";
2120}
2121
2122
2134{
2135 NumeRe::Table table = static_cast<GridNumeReTable*>(GetTable())->getTable();
2136
2137 if (!readOnly && m_parentPanel)
2139
2140 return table;
2141}
2142
2143
2152{
2153 NumeRe::Table tableCopy = static_cast<GridNumeReTable*>(GetTable())->getTableCopy();
2154
2155 if (!readOnly && m_parentPanel)
2156 tableCopy.setComment(m_parentPanel->getComment());
2157
2158 return tableCopy;
2159}
2160
static wxColour HighlightColor
static wxColour FrameColor
static wxColour HeadlineColor
This class represents a special renderer for three-state booleans, i.e. booleans, which may have a un...
This class represents an extension to the usual cell string renderer to provide functionalities to hi...
This class implements the dialog for choosing the shader properties of the selected cells.
const CellValueShader & getShader() const
Get a reference to the internally available shader instance.
This class represents the grid cell editor which automatically selects the necessary edit control for...
This class is a specialisation for the standard wxGridTableBase supporting complex numbers as well as...
Definition: gridtable.hpp:34
std::vector< int > getColumnTypes() const
Returns the types of the handled table.
Definition: gridtable.cpp:640
This class handles the internal language system and returns the language strings of the selected lang...
Definition: language.hpp:38
std::string get(const std::string &sMessage, const std::vector< std::string > &vTokens) const
This member function returns the language string for the passed language identifier and replaces all ...
Definition: language.cpp:292
size_t getCols() const
Definition: container.hpp:209
T & get(size_t row, size_t col)
Definition: container.hpp:186
size_t getRows() const
Definition: container.hpp:205
This class resembles the binary NumeRe data file format. The data is red and written in binary mode u...
Definition: file.hpp:1471
virtual bool write() override
Pure virtual declaration of the write access method. Has to be implemented in all derived classes and...
Definition: file.hpp:1508
This data container is a copy- efficient table to interchange data between Kernel and GUI.
Definition: table.hpp:87
bool isEmpty() const
Return, whether the table is empty.
Definition: table.cpp:668
void setComment(const std::string &_comment)
Setter function for the table comment.
Definition: table.cpp:213
int getHeadCount() const
Getter function for the needed number of headlines (depending on the number of linebreaks found in th...
Definition: table.cpp:429
TableColumnArray & getTableData()
Definition: table.hpp:130
size_t getCols() const
Get the number of columns.
Definition: table.cpp:656
TableMetaData getMetaData() const
Getter function for the table meta data.
Definition: table.cpp:403
size_t getLines() const
Get the number of lines.
Definition: table.cpp:636
bool setColumnType(size_t j, TableColumn::ColumnType _type)
Tries to change the column type of the selected column.
Definition: table.cpp:375
The terminal class for the GUI. It's a specialisation of the GenericTerminal.
Definition: terminal.hpp:46
NumeRe::Table getTable(const std::string &sTableName)
This function will return the named table from the kernel to be shown in a GUI window.
Definition: terminal.cpp:964
NumeRe::Container< std::string > getStringTable(const std::string &sStringTableName)
This member function will return the named table containing strings.
Definition: terminal.cpp:979
std::vector< std::string > getPathSettings()
Returns the standard paths as a STL vector.
Definition: terminal.cpp:185
Generic table panel, which also contains all meta UI elements.
void update(const NumeRe::TableMetaData &meta)
Update the panel with the passed table meta data.
std::string getComment() const
Returns the comment listed in the documentation field.
wxFrame * getFrame()
Returns a pointer to the parent frame.
NumeReTerminal * GetTerminal()
wxMenuBar * getMenuBar()
Returns a pointer to the menu bar of the parent frame. If no menu bar exists, a new one is created.
This class is an adaption of the wxGrid class to present the tabular data in NumeRe's memory and enab...
Definition: tableviewer.hpp:42
void OnCellSelect(wxGridEvent &event)
This member function will highlight the cursor position in the grid and update the status bar corresp...
void deleteSelection()
This member function will delete the contents of the selected cells by setting them to NaN.
wxGridCellCoords CreateEmptyGridSpace(int rows, int headrows, int cols, bool useCursor=false)
This member function creates the needed space in the grid, which is needed to paste data.
double calculateMax(const wxGridCellCoordsContainer &coords)
This member function calculates the maximal value of the selected cells.
std::string m_intName
Definition: tableviewer.hpp:53
void applyConditionalCellColourScheme()
Apply a conditional cell colour scheme to the selected cells.
void replaceDecimalSign(wxString &text)
This member function replaces the german comma decimal sign with the dot used in anglo-american notat...
std::vector< int > m_currentColTypes
Definition: tableviewer.hpp:51
bool isGridNumeReTable
Definition: tableviewer.hpp:48
void SetData(NumeRe::Container< std::string > &_stringTable, const std::string &sName, const std::string &sIntName)
This member function is the data setter for string and cluster tables.
void copyContents()
This member function will copy the contents of the selected cells to the clipboard.
void UpdateColumnAlignment(int col)
Changes the alignment of a whole column to reflect its internal type.
void changeColType()
Enables the switching of the column types of the selected columns.
bool isEmptyCol(int col)
This member function will determine, whether the selected column is completely empty or not.
void OnLabelRightClick(wxGridEvent &event)
This member function displays the context menu for labels.
void createZeroElementTable()
This member function creates a zero- element table to visualize empty tables (or non-existent ones).
size_t nHeight
Definition: tableviewer.hpp:44
wxGridCellCoords m_lastRightClick
Definition: tableviewer.hpp:97
void saveTable(bool saveAs=false)
Saves the currently displayed table directly to the selected file.
int findLastElement(int nCol)
This member function will search the last non-empty cell in the selected column.
NumeRe::Table GetData()
This member function returns the internal NumeRe::Table from the data provider object.
mu::value_type calculateSum(const wxGridCellCoordsContainer &coords)
This member function calculates the sum of the selected cells.
void OnChar(wxKeyEvent &event)
This member function appends necessary columns or rows, if the user entered a character in the last c...
std::string m_displayName
Definition: tableviewer.hpp:52
void finalize()
Ensure that the editors are all closed.
wxMenu m_popUpMenu
Definition: tableviewer.hpp:96
double calculateMin(const wxGridCellCoordsContainer &coords)
This member function calculates the minimal value of the selected cells.
void SetDefaultSize(size_t rows=1, size_t cols=1) __attribute__((deprecated))
This member function creates an empty table of some size.
void OnMenu(wxCommandEvent &event)
This member function is the menu command event handler function. It will redirect the control to the ...
void layoutGrid()
This private member function will layout the initial grid after the data table was set.
bool isNumerical(const std::string &sCell)
This is a simple helper function to determine, whether the entered cell value is a numerical value.
void removeElement(int id)
This member function processes the removing of cells, columns or rows.
@ ID_MENU_CHANGE_COL_TYPE
void OnCellChange(wxGridEvent &event)
This member function processes the values entered in the table and updates the frame after entering (...
wxStatusBar * m_statusBar
Definition: tableviewer.hpp:57
void SetTableReadOnly(bool isReadOnly=true)
This member function declares the table to be read-only and enables the context menu entries,...
void OnCellRangeSelect(wxGridRangeSelectEvent &event)
This member function will calculate the simple statistics if the user selected a range of cells in th...
void replaceTabSign(wxString &text)
This member function replaces the tabulator character with whitespaces.
mu::value_type calculateAvg(const wxGridCellCoordsContainer &coords)
This member function calculates the average of the selected cells.
void reloadTable()
Reloads the currently displayed table data from the kernel.
wxString copyCell(int row, int col)
This member function returns the contents of the selected cells and replaces whitespaces with undersc...
size_t nFirstNumRow
Definition: tableviewer.hpp:46
void updateFrame()
This member function is called every time the grid itself changes to draw the frame colours.
void OnCellRightClick(wxGridEvent &event)
This member function displays the context menu for cells.
void OnLabelDoubleClick(wxGridEvent &event)
This member function will autosize the columns, if the user double clicked on their labels.
wxString getSelectedValues()
Returns the values of all selected cells as a string list.
mu::value_type CellToCmplx(int row, int col)
Return the cell value as value_type.
void OnEnter(wxMouseEvent &event)
This member function is the event handler for entering this window. It will activate the focus and br...
std::vector< wxString > getLinesFromPaste(const wxString &data)
This member function transformes the data obtained by the clipboard into a table- like layout.
void createMenuBar()
Creates a menu bar in the top frame.
NumeRe::Table GetDataCopy()
This member function returns a safe copy of the internal NumeRe::Table.
TablePanel * m_parentPanel
Definition: tableviewer.hpp:56
wxString replaceCtrlChars(const wxString &sStr)
This member function is a simple helper to replace underscores with whitespaces.
void OnKeyDown(wxKeyEvent &event)
This member function tracks the entered keys and processes a keybord navigation.
size_t nWidth
Definition: tableviewer.hpp:45
wxGridCellCoordsArray selectedCells
Definition: tableviewer.hpp:50
void updateStatusBar(const wxGridCellCoordsContainer &coords, wxGridCellCoords *cursor=nullptr)
This member function updates the status bar of the enclosing ViewerFrame.
void insertElement(int id)
This member function processes the insertion of new empty cells, columns or rows.
void pasteContents(bool useCursor=false)
This member function handles the case that the user tries to paste text contents, which may be be con...
A class to simplify the access to different types of grid cell coords. Especially useful in the conte...
const wxGridCellsExtent & getExtent() const
Get the maximal needed enclosing box in terms of wxGridCellCoordinates.
bool contains(const wxGridCellCoords &cell) const
Does this wxGridCellCoordsContainer contain the passed coordinates?
bool isBlock() const
Returns true, if the contained coordinates actually form a contigious block.
bool columnsSelected() const
Returns true, if the contained coordinates originated from complete columns.
@ SAVEPATH
@ ID_MENU_COPY
@ ID_MENU_SAVE
GenericFile * getFileByType(const string &filename)
This function determines the correct class to be used for the filename passed to this function....
Definition: file.cpp:45
MUP_BASETYPE value_type
The numeric datatype used by the parser.
Definition: muParserDef.h:251
bool isnan(const value_type &v)
Definition: muParserDef.h:379
char name[32]
Definition: resampler.cpp:371
#define min(a, b)
Definition: resampler.cpp:34
#define max(a, b)
Definition: resampler.cpp:30
std::string replaceControlCharacters(std::string sToModify)
This function is a simple wrapper for replaceAll() and specialized to remove control characters from ...
std::complex< double > StrToCmplx(const std::string &sString)
Converts a string into a complex number.
std::string comment
Definition: table.hpp:33
static ColumnType stringToType(const std::string &sType)
Converts the passed string representation to a ColumnType value.
static std::vector< std::string > getTypesAsString()
Returns a list of all available column types as strings.
std::string toString(int)
Converts an integer to a string without the Settings bloat.
A simple structure to define the needed grid space to enclose all cells contained in the wxGridCellCo...
wxGridCellCoords m_bottomright
#define MAXIMAL_RENDERING_SIZE
Definition: tableviewer.cpp:35
Language _guilang
#define STATUSBAR_PRECISION
Definition: tableviewer.cpp:34
END_EVENT_TABLE()