1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2004-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file    MFXAddEditTypedTable.cpp
11 /// @author  Daniel Krajzewicz
12 /// @date    2004-07-02
13 /// @version $Id$
14 ///
15 // missing_desc
16 /****************************************************************************/
17 
18 
19 // ===========================================================================
20 // included modules
21 // ===========================================================================
22 #include <config.h>
23 
24 #include <fx.h>
25 #include <fxkeys.h>
26 #include <utils/common/StringUtils.h>
27 #include <utils/common/ToString.h>
28 #include "MFXAddEditTypedTable.h"
29 #include <iostream>
30 
31 
32 // Map
33 FXDEFMAP(MFXAddEditTypedTable) MFXAddEditTypedTableMap[] = {
34     FXMAPFUNC(SEL_CLICKED, 0, MFXAddEditTypedTable::onClicked),
35     FXMAPFUNC(SEL_DOUBLECLICKED, 0, MFXAddEditTypedTable::onDoubleClicked),
36     FXMAPFUNC(SEL_LEFTBUTTONRELEASE, 0, MFXAddEditTypedTable::onLeftBtnRelease),
37     FXMAPFUNC(SEL_LEFTBUTTONPRESS, 0, MFXAddEditTypedTable::onLeftBtnPress),
38 };
39 // Object implementation
FXIMPLEMENT(MFXAddEditTypedTable,FXTable,MFXAddEditTypedTableMap,ARRAYNUMBER (MFXAddEditTypedTableMap))40 FXIMPLEMENT(MFXAddEditTypedTable, FXTable, MFXAddEditTypedTableMap, ARRAYNUMBER(MFXAddEditTypedTableMap))
41 
42 
43 MFXAddEditTypedTable::MFXAddEditTypedTable(FXComposite* p, FXObject* tgt,
44         FXSelector sel, FXuint opts,
45         FXint x, FXint y, FXint w, FXint h,
46         FXint pl, FXint pr, FXint pt, FXint pb)
47     : FXTable(p, tgt, sel, opts, x, y, w, h, pl, pr, pt, pb) {}
48 
49 
~MFXAddEditTypedTable()50 MFXAddEditTypedTable::~MFXAddEditTypedTable() {}
51 
52 /*
53 void
54 MFXAddEditTypedTable::editItem(FXTableItem* item,FXint how)
55 {
56     if(item==0) {
57         editEnd();
58         return;
59     }
60     if(myWriteProtectedCols.find(myEditedCol)!=myWriteProtectedCols.end()) {
61         editEnd();
62         return;
63     }
64     FXTableItem* it= item;
65     myPreviousText = item->getText();
66     FXint x = getColumnX(myEditedCol) + getRowHeader()->getWidth() + xpos;
67     FXint y = getRowY(myEditedRow) + getColumnHeader()->getHeight() + ypos;
68     FXIcon* icon = item->getIcon();
69     if(icon) x += icon->getWidth() + 4;
70     FXint vw = getViewportWidth();
71     if(vertical->shown()) vw -= vertical->getWidth();
72     if(vw>getColumnWidth(myEditedCol)) {
73         vw = getColumnWidth(myEditedCol) + x;
74     }
75     switch(getCellType(myEditedCol)) {
76     case CT_UNDEFINED:
77     case CT_STRING:
78         myEditor->setText(it->getText());
79         myEditor->move(x, y);
80         myEditor->resize(vw - x + 1, getRowHeight(myEditedRow) + 1);
81         myEditor->show();
82         myEditor->raise();
83         myEditor->enable();
84         myEditor->setFocus();
85         myEditor->grab();
86         if(how == 'I') {
87             myEditor->killSelection();
88             myEditor->setCursorPos(0);
89         } else if(how == 'A') {
90             myEditor->killSelection();
91             myEditor->setCursorPos(myEditor->getText().length());
92         } else myEditor->selectAll();
93         break;
94     case CT_REAL:
95         {
96             try {
97                 myNumberEditor->setValue(
98                     TplConvert::_2double(it->getText().text()));
99             } catch (NumberFormatException &) {
100             } catch (EmptyData &) {
101             }
102             NumberCellParams p = getNumberCellParams(myEditedCol);
103             if(p.format!="undefined") {
104                 myNumberEditor->setFormatString((char*) p.format.c_str());
105                 myNumberEditor->setIncrements(p.steps1, p.steps2, p.steps3);
106                 myNumberEditor->setRange(p.min, p.max);
107             }
108             myNumberEditor->move(x, y);
109             myNumberEditor->resize(vw - x + 1, getRowHeight(myEditedRow) + 1);
110             myNumberEditor->show();
111             myNumberEditor->raise();
112             myNumberEditor->setFocus();
113             myNumberEditor->selectAll();
114         }
115         //myNumberEditor->setRange(0,1000);
116         break;
117     case CT_INT:
118         {
119             try {
120                 myNumberEditor->setValue(
121                     TplConvert::_2int(it->getText().text()));
122             } catch (NumberFormatException &) {
123             } catch (EmptyData &) {
124             }
125             NumberCellParams p = getNumberCellParams(myEditedCol);
126             if(p.format!="undefined") {
127                 myNumberEditor->setFormatString((char*) p.format.c_str());
128                 myNumberEditor->setIncrements(p.steps1, p.steps2, p.steps3);
129                 myNumberEditor->setRange(p.min, p.max);
130             }
131             myNumberEditor->move(x, y);
132             myNumberEditor->resize(vw - x + 1, getRowHeight(myEditedRow) + 1);
133             myNumberEditor->show();
134             myNumberEditor->raise();
135             myNumberEditor->setFocus();
136             myNumberEditor->selectAll();
137         }
138         break;
139     case CT_BOOL:
140         try {
141             myBoolEditor->setCheck(
142                 TplConvert::_2bool(it->getText().text())
143                 ? true : false);
144         } catch (NumberFormatException &) {
145         } catch (EmptyData &) {
146         }
147         myBoolEditor->move(x, y);
148         myBoolEditor->resize(vw - x + 1, getRowHeight(myEditedRow) + 1);
149         myBoolEditor->show();
150         myBoolEditor->raise();
151         myBoolEditor->setFocus();
152         break;
153     case CT_ENUM:
154         {
155             myEnumEditor->hide();
156             myEnumEditor->clearItems();
157             if(myEnums.size()>myEditedCol) {
158                 for(int i=0; i<myEnums[myEditedCol].size(); i++) {
159                     myEnumEditor->appendItem(myEnums[myEditedCol][i].c_str());
160                 }
161             }
162             if(myEnumEditor->findItem(it->getText())>=0) {
163                 myEnumEditor->setCurrentItem(
164                     myEnumEditor->findItem(it->getText()));
165             } else {
166                 myEnumEditor->setCurrentItem(0);
167             }
168             myEnumEditor->setNumVisible(
169                 myEnums[myEditedCol].size()<10
170                 ? myEnums[myEditedCol].size()
171                 : 10);
172             myEnumEditor->layout();
173             y = getRowY(myEditedRow) + getColumnHeader()->getHeight() + ypos
174                 - getRowHeight(myEditedRow);
175             myEnumEditor->move(x, y);
176             myEnumEditor->resize(vw - x + 1, getRowHeight(myEditedRow) + 1);
177             myEnumEditor->show();
178             myEnumEditor->raise();
179             myEnumEditor->setFocus();
180         }
181         break;
182     default:
183         throw 1;
184     }
185     myEditedItem = it;
186 }
187 */
188 
189 
190 FXWindow*
getControlForItem(FXint r,FXint c)191 MFXAddEditTypedTable::getControlForItem(FXint r, FXint c) {
192     FXTableItem* item = cells[r * ncols + c];
193     if (item == nullptr) {
194         return nullptr;
195 //         cells[r * ncols + c] = item = createItem("", NULL, NULL);
196 //         if (isItemSelected(r, c)) {
197 //             item->setSelected(FALSE);
198 //         }
199     }
200     delete editor;
201     editor = nullptr;
202     switch (getCellType(c)) {
203         case CT_UNDEFINED:
204         case CT_STRING: {
205             FXTextField* field;
206             FXuint justify = 0;
207             field = new FXTextField(this, 1, nullptr, 0, TEXTFIELD_ENTER_ONLY, 0, 0, 0, 0, getMarginLeft(), getMarginRight(), getMarginTop(), getMarginBottom());
208             // !!! if(state&LEFT) justify|=JUSTIFY_LEFT;
209             // !!! if(state&RIGHT) justify|=JUSTIFY_RIGHT;
210             // !!! if(state&TOP) justify|=JUSTIFY_TOP;
211             // !!! if(state&BOTTOM) justify|=JUSTIFY_BOTTOM;
212             field->create();
213             field->setJustify(justify);
214             field->setFont(getFont());
215             field->setBackColor(getBackColor());
216             field->setTextColor(getTextColor());
217             field->setSelBackColor(getSelBackColor());
218             field->setSelTextColor(getSelTextColor());
219             field->setText(item->getText());
220             field->selectAll();
221             return field;
222         }
223         case CT_REAL:
224 //        return myNumberEditor;
225         case CT_INT: {
226             FXRealSpinner* field;
227             //FXuint justify=0;
228             field = new FXRealSpinner(this, 1, nullptr, 0, TEXTFIELD_ENTER_ONLY, 0, 0, 0, 0, getMarginLeft(), getMarginRight(), getMarginTop(), getMarginBottom());
229             // !!! if(state&LEFT) justify|=JUSTIFY_LEFT;
230             // !!! if(state&RIGHT) justify|=JUSTIFY_RIGHT;
231             // !!! if(state&TOP) justify|=JUSTIFY_TOP;
232             // !!! if(state&BOTTOM) justify|=JUSTIFY_BOTTOM;
233             field->create();
234 //            field->setJustify(justify);
235             field->setFont(getFont());
236             field->setBackColor(getBackColor());
237             field->setTextColor(getTextColor());
238             field->setSelBackColor(getSelBackColor());
239             field->setSelTextColor(getSelTextColor());
240             NumberCellParams p = getNumberCellParams(c);
241             if (p.format != "undefined") {
242                 //field->setFormatString((char*) p.format.c_str());
243                 //field->setIncrements(p.steps1, p.steps2, p.steps3);
244                 field->setIncrement(p.steps2);
245                 field->setRange(p.min, p.max);
246             }
247             try {
248                 if (getCellType(c) == CT_REAL) {
249                     field->setValue(StringUtils::toDouble(item->getText().text()));
250                 } else {
251                     field->setValue(StringUtils::toInt(item->getText().text()));
252                 }
253             } catch (NumberFormatException&) {
254                 field->setValue(0);
255             }
256             //field->selectAll();
257             return field;
258         }
259         case CT_BOOL:
260 //        return myBoolEditor;
261         case CT_ENUM:
262 //        return myEnumEditor;
263         default:
264             throw 1;
265     }
266 }
267 
268 
269 // Cancel editing cell
270 void
cancelInput()271 MFXAddEditTypedTable::cancelInput() {
272     if (editor) {
273         delete editor;
274         input.fm.row = -1;
275         input.to.row = -1;
276         input.fm.col = -1;
277         input.to.col = -1;
278         editor = nullptr;
279     }
280 }
281 
282 // Done with editing cell
283 void
acceptInput(FXbool notify)284 MFXAddEditTypedTable::acceptInput(FXbool notify) {
285     bool set = false;
286     FXTableRange tablerange = input;
287     if (editor) {
288         FXRealSpinner* dial = dynamic_cast<FXRealSpinner*>(editor);
289         if (dial != nullptr) {
290             setItemFromControl_NoRelease(input.fm.row, input.fm.col, editor);
291         }
292         if (dynamic_cast<FXTextField*>(editor) != nullptr) {
293             set = true;
294         }
295     }
296     if (set) {
297         setItemFromControl(input.fm.row, input.fm.col, editor);
298         cancelInput();
299         if (notify && target) {
300             target->tryHandle(this, FXSEL(SEL_REPLACED, message), (void*)&tablerange);
301         }
302     }
303 }
304 
305 
306 
307 
308 void
setItemFromControl(FXint r,FXint c,FXWindow * control)309 MFXAddEditTypedTable::setItemFromControl(FXint r, FXint c, FXWindow* control) {
310     FXTableItem* item = cells[r * ncols + c];
311     if (item == nullptr) {
312         cells[r * ncols + c] = item = createItem("", nullptr, nullptr);
313         if (isItemSelected(r, c)) {
314             item->setSelected(FALSE);
315         }
316     }
317     switch (getCellType(c)) {
318         case CT_UNDEFINED:
319         case CT_STRING:
320             item->setFromControl(control);
321             break;
322         case CT_REAL:
323             item->setText(toString(static_cast<FXRealSpinner*>(control)->getValue()).c_str());
324             break;
325         case CT_INT:
326             item->setText(toString((int) static_cast<FXRealSpinner*>(control)->getValue()).c_str());
327             break;
328         case CT_BOOL:
329 //        return myBoolEditor;
330         case CT_ENUM:
331 //        return myEnumEditor;
332         default:
333             throw 1;
334     }
335 //    current.row = -1;
336 //    current.col = -1;
337     EditedTableItem edited;
338     edited.item = item;
339     edited.row = r;
340     edited.col = c;
341     edited.updateOnly = false;
342     killSelection(true);
343     bool accepted = true;
344     if (target) {
345         if (!target->handle(this, FXSEL(SEL_CHANGED, ID_TEXT_CHANGED), (void*) &edited)) {
346             accepted = false;
347             // !!! item->setText(myPreviousText);
348         }
349     }
350     if (accepted) {
351         if (edited.row == getNumRows() - 1) {
352             insertRows(getNumRows(), 1, true);
353             for (int i = 0; i < getNumColumns(); i++) {
354                 setItemText(getNumRows() - 1, i, "");
355                 setItemJustify(getNumRows() - 1, i, JUSTIFY_CENTER_X);
356             }
357         }
358     }
359     mode = MOUSE_NONE;
360 }
361 
362 
363 void
setItemFromControl_NoRelease(FXint r,FXint c,FXWindow * control)364 MFXAddEditTypedTable::setItemFromControl_NoRelease(FXint r, FXint c, FXWindow* control) {
365     FXTableItem* item = cells[r * ncols + c];
366     if (item == nullptr) {
367         return;
368     }
369     switch (getCellType(c)) {
370         case CT_UNDEFINED:
371         case CT_STRING:
372             item->setFromControl(control);
373             break;
374         case CT_REAL:
375             item->setText(toString(static_cast<FXRealSpinner*>(control)->getValue()).c_str());
376             break;
377         case CT_INT:
378             item->setText(toString((int) static_cast<FXRealSpinner*>(control)->getValue()).c_str());
379             break;
380         case CT_BOOL:
381 //        return myBoolEditor;
382         case CT_ENUM:
383 //        return myEnumEditor;
384         default:
385             throw 1;
386     }
387     EditedTableItem edited;
388     edited.item = item;
389     edited.row = r;
390     edited.col = c;
391     edited.updateOnly = true;
392     if (target) {
393         if (!target->handle(this, FXSEL(SEL_CHANGED, ID_TEXT_CHANGED), (void*) &edited)) {
394             // !!! item->setText(myPreviousText);
395         }
396     }
397 }
398 
399 
400 // Released button
onLeftBtnRelease(FXObject *,FXSelector,void * ptr)401 long MFXAddEditTypedTable::onLeftBtnRelease(FXObject*, FXSelector, void* ptr) {
402     FXEvent* event = (FXEvent*)ptr;
403     if (isEnabled()) {
404         ungrab();
405         flags &= ~FLAG_PRESSED;
406         flags |= FLAG_UPDATE;
407         mode = MOUSE_NONE;
408         stopAutoScroll();
409         setDragCursor(getApp()->getDefaultCursor(DEF_ARROW_CURSOR));
410         if (target && target->tryHandle(this, FXSEL(SEL_LEFTBUTTONRELEASE, message), ptr)) {
411             return 1;
412         }
413 
414         // Scroll to make item visibke
415         makePositionVisible(current.row, current.col);
416 
417         // Update anchor
418         //setAnchorItem(current.row,current.col); // FIXME look into the selection stuff
419 
420         // Generate clicked callbacks
421         if (event->click_count == 1) {
422             handle(this, FXSEL(SEL_CLICKED, 0), (void*)&current);
423         } else if (event->click_count == 2) {
424             handle(this, FXSEL(SEL_DOUBLECLICKED, 0), (void*)&current);
425         } else if (event->click_count == 3) {
426             handle(this, FXSEL(SEL_TRIPLECLICKED, 0), (void*)&current);
427         }
428 
429         // Command callback only when clicked on item
430         if (0 <= current.row && 0 <= current.col && isItemEnabled(current.row, current.col)) {
431             handle(this, FXSEL(SEL_COMMAND, 0), (void*)&current);
432         }
433         return 1;
434     }
435     return 0;
436 }
437 
438 
439 // Pressed button
440 long
onLeftBtnPress(FXObject *,FXSelector,void * ptr)441 MFXAddEditTypedTable::onLeftBtnPress(FXObject*, FXSelector, void* ptr) {
442     FXEvent* event = (FXEvent*)ptr;
443     FXTablePos tablepos;
444     flags &= ~FLAG_TIP;
445     handle(this, FXSEL(SEL_FOCUS_SELF, 0), ptr);
446     if (isEnabled()) {
447         grab();
448         if (target && target->tryHandle(this, FXSEL(SEL_LEFTBUTTONPRESS, message), ptr)) {
449             return 1;
450         }
451 
452         // Cell being clicked on
453         tablepos.row = rowAtY(event->win_y);
454         tablepos.col = colAtX(event->win_x);
455 
456         // Outside table
457         if (tablepos.row < 0 || tablepos.row >= nrows || tablepos.col < 0 || tablepos.col >= ncols) {
458             setCurrentItem(-1, -1, TRUE);
459             return 0;
460         }
461 
462         // Change current item
463         bool wasEdited = editor != nullptr;
464         setCurrentItem(tablepos.row, tablepos.col, TRUE);
465         if (!wasEdited) {
466 
467             // Select or deselect
468             if (event->state & SHIFTMASK) {
469                 if (0 <= anchor.row && 0 <= anchor.col) {
470                     if (isItemEnabled(anchor.row, anchor.col)) {
471                         extendSelection(current.row, current.col, TRUE);
472                     }
473                 } else {
474                     setAnchorItem(current.row, current.col);
475                     if (isItemEnabled(current.row, current.col)) {
476                         extendSelection(current.row, current.col, TRUE);
477                     }
478                 }
479                 mode = MOUSE_SELECT;
480             } else {
481                 if (isItemEnabled(current.row, current.col)) {
482                     killSelection(TRUE);
483                     setAnchorItem(current.row, current.col);
484                     extendSelection(current.row, current.col, TRUE);
485                 } else {
486                     setAnchorItem(current.row, current.col);
487                 }
488                 mode = MOUSE_SELECT;
489             }
490         }
491         flags &= ~FLAG_UPDATE;
492         flags |= FLAG_PRESSED;
493         return 1;
494     }
495     return 0;
496 }
497 
498 
499 
500 // Clicked in list
501 long
onClicked(FXObject *,FXSelector,void * ptr)502 MFXAddEditTypedTable::onClicked(FXObject*, FXSelector, void* ptr) {
503     if (editor) {
504         delete editor;
505         input.fm.row = -1;
506         input.to.row = -1;
507         input.fm.col = -1;
508         input.to.col = -1;
509         editor = nullptr;
510         current.row = -1;
511         current.col = -1;
512     }
513     if (target && target->tryHandle(this, FXSEL(SEL_CLICKED, message), ptr)) {
514         return 1;
515     }
516     handle(this, FXSEL(SEL_COMMAND, ID_START_INPUT), nullptr);
517     return 1;
518 }
519 
520 
521 // Double clicked in list; ptr may or may not point to an item
onDoubleClicked(FXObject *,FXSelector,void * ptr)522 long MFXAddEditTypedTable::onDoubleClicked(FXObject*, FXSelector, void* ptr) {
523     if (editor) {
524         delete editor;
525         input.fm.row = -1;
526         input.to.row = -1;
527         input.fm.col = -1;
528         input.to.col = -1;
529         editor = nullptr;
530     } else {
531         if (target && target->tryHandle(this, FXSEL(SEL_CLICKED, message), ptr)) {
532             return 1;
533         }
534         handle(this, FXSEL(SEL_COMMAND, ID_START_INPUT), nullptr);
535     }
536     return 1;
537 }
538 
539 
540 CellType
getCellType(int pos) const541 MFXAddEditTypedTable::getCellType(int pos) const {
542     if ((int)myCellTypes.size() <= pos) {
543         return CT_UNDEFINED;
544     }
545     return myCellTypes[pos];
546 }
547 
548 
549 void
setCellType(int pos,CellType t)550 MFXAddEditTypedTable::setCellType(int pos, CellType t) {
551     while ((int)myCellTypes.size() < pos + 1) {
552         myCellTypes.push_back(CT_UNDEFINED);
553     }
554     myCellTypes[pos] = t;
555 }
556 
557 void
setNumberCellParams(int pos,double min,double max,double steps1,double steps2,double steps3,const std::string & format)558 MFXAddEditTypedTable::setNumberCellParams(int pos, double min, double max,
559         double steps1,
560         double steps2,
561         double steps3,
562         const std::string& format) {
563     while ((int)myNumberCellParams.size() <= pos) {
564         NumberCellParams np;
565         np.format = "undefined";
566         myNumberCellParams.push_back(np);
567     }
568     NumberCellParams np;
569     np.pos = (int)(pos);
570     np.min = min;
571     np.max = max;
572     np.steps1 = steps1;
573     np.steps2 = steps2;
574     np.steps3 = steps3;
575     np.format = format;
576     myNumberCellParams[pos] = np;
577 }
578 
579 
580 MFXAddEditTypedTable::NumberCellParams
getNumberCellParams(int pos) const581 MFXAddEditTypedTable::getNumberCellParams(int pos) const {
582     if ((int)myNumberCellParams.size() <= pos) {
583         NumberCellParams np;
584         np.format = "undefined";
585         return np;
586     }
587     return myNumberCellParams[pos];
588 }
589 
590 
591 
592 void
setEnums(int pos,const std::vector<std::string> & params)593 MFXAddEditTypedTable::setEnums(int pos,
594                                const std::vector<std::string>& params) {
595     while ((int)myEnums.size() <= pos) {
596         myEnums.push_back(std::vector<std::string>());
597     }
598     myEnums[pos] = params;
599 }
600 
601 
602 void
addEnum(int pos,const std::string & e)603 MFXAddEditTypedTable::addEnum(int pos,
604                               const std::string& e) {
605     while ((int)myEnums.size() <= pos) {
606         myEnums.push_back(std::vector<std::string>());
607     }
608     myEnums[pos].push_back(e);
609 }
610 
611 
612 const std::vector<std::string>&
getEnums(int pos) const613 MFXAddEditTypedTable::getEnums(int pos) const {
614     return myEnums[pos];
615 }
616 
617 
618 
619 /****************************************************************************/
620 
621