Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.library / org.gvsig.expressionevaluator / org.gvsig.expressionevaluator.swing / org.gvsig.expressionevaluator.swing.impl / src / main / java / org / gvsig / expressionevaluator / swing / impl / DefaultJExpressionBuilder.java @ 44148

History | View | Annotate | Download (32 KB)

1
package org.gvsig.expressionevaluator.swing.impl;
2

    
3
import java.awt.BorderLayout;
4
import java.awt.Dimension;
5
import java.awt.Toolkit;
6
import java.awt.datatransfer.Clipboard;
7
import java.awt.datatransfer.StringSelection;
8
import java.awt.event.ActionEvent;
9
import java.awt.event.ActionListener;
10
import java.awt.event.KeyAdapter;
11
import java.awt.event.KeyEvent;
12
import java.awt.event.MouseAdapter;
13
import java.awt.event.MouseEvent;
14
import java.net.URL;
15
import java.util.ArrayList;
16
import java.util.Collection;
17
import java.util.HashSet;
18
import java.util.List;
19
import java.util.Objects;
20
import javax.swing.DefaultListCellRenderer;
21
import javax.swing.ImageIcon;
22
import javax.swing.JComponent;
23
import javax.swing.JEditorPane;
24
import javax.swing.JLabel;
25
import javax.swing.JList;
26
import javax.swing.JMenuItem;
27
import javax.swing.JOptionPane;
28
import javax.swing.JPanel;
29
import javax.swing.JPopupMenu;
30
import javax.swing.JScrollPane;
31
import javax.swing.JTabbedPane;
32
import javax.swing.JTree;
33
import javax.swing.SwingUtilities;
34
import javax.swing.event.CaretEvent;
35
import javax.swing.event.CaretListener;
36
import javax.swing.event.ListSelectionEvent;
37
import javax.swing.event.ListSelectionListener;
38
import javax.swing.event.TreeModelEvent;
39
import javax.swing.event.TreeModelListener;
40
import javax.swing.event.TreeSelectionEvent;
41
import javax.swing.event.TreeSelectionListener;
42
import javax.swing.tree.DefaultTreeCellRenderer;
43
import javax.swing.tree.TreeModel;
44
import javax.swing.tree.TreePath;
45
import org.apache.commons.io.FilenameUtils;
46
import org.apache.commons.lang3.StringUtils;
47
import org.gvsig.expressionevaluator.Expression;
48
import org.gvsig.expressionevaluator.ExpressionEvaluatorLocator;
49
import org.gvsig.expressionevaluator.Function;
50
import org.gvsig.expressionevaluator.SymbolTable;
51
import org.gvsig.expressionevaluator.swing.Element;
52
import org.gvsig.expressionevaluator.swing.Element.CalculatedElement;
53
import org.gvsig.expressionevaluator.swing.Element.ConstantElement;
54
import org.gvsig.expressionevaluator.swing.Element.FunctionElement;
55
import org.gvsig.expressionevaluator.swing.Element.GroupElement;
56
import org.gvsig.expressionevaluator.swing.Element.SimpleElement;
57
import org.gvsig.expressionevaluator.swing.ExpressionBuilderConfig;
58
import org.gvsig.expressionevaluator.swing.ExpressionEvaluatorSwingManager;
59
import org.gvsig.expressionevaluator.swing.JExpressionBuilder;
60
import org.gvsig.tools.ToolsLocator;
61
import org.gvsig.tools.i18n.I18nManager;
62
import org.gvsig.tools.script.Script;
63
import org.gvsig.tools.swing.api.Component;
64
import org.gvsig.tools.swing.api.ListElement;
65
import org.gvsig.tools.swing.api.ToolsSwingLocator;
66
import org.gvsig.tools.swing.api.ToolsSwingManager;
67
import org.gvsig.tools.swing.api.pickercontroller.PickerController;
68
import org.gvsig.tools.swing.api.script.ScriptSwingManager;
69
import org.gvsig.tools.swing.api.threadsafedialogs.ThreadSafeDialogsManager;
70
import org.gvsig.tools.swing.icontheme.IconTheme;
71

    
72
/**
73
 *
74
 * @author jjdelcerro
75
 */
76
@SuppressWarnings("UseSpecificCatch")
77
public class DefaultJExpressionBuilder
78
        extends DefaultJExpressionBuilderView
79
        implements JExpressionBuilder {
80

    
81
    private final ExpressionEvaluatorSwingManager manager;
82
    private GroupElement currentGroup;
83
    private PickerController<List<Script>> scriptPicker;
84
    private ScriptSwingManager.ScriptEditor scriptEditor;
85
    
86
    private final ExpressionBuilderConfig config;
87
    private ElementsTreeModel treeModel;
88
    private ImageIcon defaultIconGroup;
89
    private ImageIcon defaultIconElement;
90
    private InfoPanel infoPanel;
91
    private AbstractAutomaticExpressionChecker automaticExpressionChecker;
92
    
93
    private static class InfoPanel implements Component {
94

    
95
        private final JEditorPane txtDescription;
96
        private final JScrollPane scrDescription;
97
        private final JTabbedPane tab;
98

    
99
        private final String labelDescription;
100
        private final String labelAdditionalPanel;
101
        private final JPanel pnlContainer;
102

    
103
        private String description; 
104
        private Component additionalPanel;
105
        
106
        public InfoPanel(JPanel pnlContainer) {
107
            ToolsSwingManager toolsSwingManager = ToolsSwingLocator.getToolsSwingManager();
108
            I18nManager i18n = ToolsLocator.getI18nManager();
109
            
110
            this.pnlContainer = pnlContainer;
111
            this.labelDescription = i18n.getTranslation("_Description");
112
            this.labelAdditionalPanel = i18n.getTranslation("_Assistant");
113
            
114
            this.txtDescription = new JEditorPane();
115
            this.scrDescription = new JScrollPane(this.txtDescription);
116
            this.tab = new JTabbedPane();
117
            
118
            toolsSwingManager.setDefaultPopupMenu(this.txtDescription);
119
            this.txtDescription.setPreferredSize(new Dimension(200,200));
120
        }
121
        
122
        public void clean() {
123
            this.description = null;
124
            this.additionalPanel = null;
125
        }
126
        
127
        public void setDescription(String description) {
128
            this.description = description;
129
        }
130
        
131
        public void setAdditionalPanel(Component panel) {
132
            this.additionalPanel = panel;
133
        }
134

    
135
        @Override
136
        public JComponent asJComponent() {
137
            this.txtDescription.setContentType("text/html");
138
            this.txtDescription.setText(this.description);
139
            this.txtDescription.setCaretPosition(0);
140

    
141
            if( StringUtils.isBlank(this.description) ) {
142
                if( this.additionalPanel==null ) {
143
                    // Sin descripcion ni panel adicional, mostramos
144
                    // la descripcion vacia.
145
                    return this.scrDescription;
146
                }
147
                // Sin descripcion pero con panel adicional, mostramos el 
148
                // panel adicional.
149
                return this.additionalPanel.asJComponent();
150
            }
151
            if( this.additionalPanel==null ) {
152
                // Con descripcion y sin panel adicional, mostramos
153
                // la descripcion.
154
                return this.scrDescription;                
155
            } 
156
            // Con descripcion y panel adicional, mostramos un tab con los dos.
157
            this.tab.removeAll();
158
            this.tab.add(
159
                    this.labelDescription, 
160
                    this.scrDescription
161
            );
162
            this.tab.add(
163
                    this.labelAdditionalPanel, 
164
                    this.additionalPanel.asJComponent()
165
            );
166
            this.tab.setSelectedIndex(1);
167
            return this.tab;
168
        }
169

    
170
        public void repaint() {
171
            this.pnlContainer.removeAll();
172
            this.pnlContainer.add(this.asJComponent(), BorderLayout.CENTER);
173
            this.pnlContainer.revalidate();
174
            this.pnlContainer.repaint();
175
        }
176
    }
177

    
178
    private class ElementsTreeModel implements TreeModel {
179

    
180
        private final GroupElement root;
181
        private final HashSet<TreeModelListener> listeners;
182

    
183
        public ElementsTreeModel(GroupElement root) {
184
            this.root = root;
185
            this.listeners = new HashSet<>();
186
        }
187

    
188
        @Override
189
        public Object getRoot() {
190
            return this.root;
191
        }
192

    
193
        public void reload() {
194
            this.reload(this.root);
195
            this.fireTreeChanged();
196
        }
197
        
198
        private void reload(Element element) {
199
            if (element instanceof Element) {
200
                if (element instanceof GroupElement) {
201
                    reload(element);
202
                }
203
                element.reload();
204
            }
205
        }
206
        
207
        private List<Element> getChilds(final GroupElement node) {
208
            List<Element> x = new ArrayList<>();
209
            for (Element element : node.getElements()) {
210
                if (element instanceof GroupElement) {
211
                    x.add(element);
212
                }
213
            }
214
            return x; //node.getElements();
215
        }
216

    
217
        @Override
218
        public Object getChild(Object parent, int index) {
219
            List<Element> childs = getChilds((GroupElement) parent);
220
            Element child = childs.get(index);
221
            return child;
222
        }
223

    
224
        @Override
225
        public int getChildCount(Object parent) {
226
            List<Element> childs = getChilds((GroupElement) parent);
227
            return childs.size();
228
        }
229

    
230
        @Override
231
        public boolean isLeaf(Object node) {
232
            if( node instanceof GroupElement ) {
233
                return !((GroupElement) node).hasSubgroups();
234
            }
235
            return false;
236
        }
237

    
238
        @Override
239
        public void valueForPathChanged(TreePath path, Object newValue) {
240
        }
241

    
242
        @Override
243
        public int getIndexOfChild(Object parent, Object child) {
244
            List<Element> childs = getChilds((GroupElement) parent);
245
            int n = childs.indexOf(child);
246
            return n;
247
        }
248

    
249
        @Override
250
        public void addTreeModelListener(TreeModelListener l) {
251
            this.listeners.add(l);
252
        }
253

    
254
        @Override
255
        public void removeTreeModelListener(TreeModelListener l) {
256
            this.listeners.remove(l);
257
        }
258

    
259
        protected void fireTreeChanged() {
260
            for (TreeModelListener listener : listeners) {
261
                TreeModelEvent e = new TreeModelEvent(this, new Object[] {this.root});
262
                listener.treeNodesChanged(e);
263
            }
264
        }
265
    }
266
  
267
    @SuppressWarnings("OverridableMethodCallInConstructor")
268
    public DefaultJExpressionBuilder(ExpressionEvaluatorSwingManager manager, ExpressionBuilderConfig config) {
269
        this.manager = manager;
270
        if( config == null ) {
271
            this.config = new DefaultExpressionBuilderConfig(manager);
272
        } else {
273
            this.config = config;
274
        }
275
        this.initComponents();
276
    }
277
    
278
    @Override
279
    public ImageIcon loadImage(String imageName) {
280
        String name = FilenameUtils.getBaseName(imageName);
281
        IconTheme theme = ToolsSwingLocator.getIconThemeManager().getDefault();
282
        if (theme.exists(name)) {
283
            return theme.get(name);
284
        }
285
        URL url = this.getClass().getResource(name + ".png");
286
        if (url == null) {
287
            return null;
288
        }
289
        return new ImageIcon(url);
290
    }
291

    
292
    private void initComponents() {
293
        final ToolsSwingManager toolsSwingManager = ToolsSwingLocator.getToolsSwingManager();
294
        final IconTheme theme = ToolsSwingLocator.getIconThemeManager().getCurrent();
295
        final I18nManager i18n = ToolsLocator.getI18nManager();
296
        
297
        this.automaticExpressionChecker = new AbstractAutomaticExpressionChecker(
298
                config, txtExpression
299
            ) {
300
            @Override
301
            protected Expression getExpression() {
302
                return DefaultJExpressionBuilder.this.getExpression();
303
            }
304

    
305
            @Override
306
            protected void setMessage(int mode, String text) {
307
                super.setMessage(mode, text); 
308
                lblMsg.setText("<html>"+StringUtils.abbreviate(text,100)+"</html>");
309
            }
310

    
311
            @Override
312
            protected void setPreview(Object value) {
313
                super.setPreview(value);
314
                lblMsg.setText("<html>"+StringUtils.abbreviate(this.getPreview(),100)+"</html>");
315
            }
316

    
317
            @Override
318
            protected void setTip(String theTip) {
319
                super.setTip(theTip); 
320
                btnTip.setToolTipText(this.getTip());
321
                btnTip.setVisible(this.isTipEnabled());
322
            }
323
            
324
        };
325
        this.infoPanel = new InfoPanel(this.pnlDescription);
326
        
327
        this.btnTip.setVisible(false);
328
        this.btnTip.addActionListener(new ActionListener() {
329
            @Override
330
            public void actionPerformed(ActionEvent e) {
331
                ThreadSafeDialogsManager dialogs = ToolsSwingLocator.getThreadSafeDialogsManager();
332
                dialogs.messageDialog(
333
                        btnTip.getToolTipText(), 
334
                        "Tip", 
335
                        JOptionPane.INFORMATION_MESSAGE
336
                );
337
            }
338
        });
339

    
340
        this.defaultIconGroup = theme.get("expressionbuilder-element-group");
341
        this.defaultIconElement = theme.get("expressionbuilder-element");
342
        
343
        toolsSwingManager.translate(this.tabExpressionBuilder);
344
        toolsSwingManager.setDefaultPopupMenu(this.txtGroupElement);
345
        toolsSwingManager.setDefaultPopupMenu(this.txtSimpleElementFilter);
346
        toolsSwingManager.addClearButton(this.txtSimpleElementFilter, new ActionListener() {
347
            @Override
348
            public void actionPerformed(ActionEvent e) {
349
                txtSimpleElementFilter.setText("");
350
                doFilter();
351
            }
352
        });
353
        JMenuItem msgMenuTextEditor = new JMenuItem(i18n.getTranslation("text_editor"));
354
        msgMenuTextEditor.addActionListener(new ActionListener() {
355
            @Override
356
            public void actionPerformed(ActionEvent e) {
357
                automaticExpressionChecker.showMessageDialog();
358
            }
359
        });
360
        JMenuItem msgMenuCopy = new JMenuItem(i18n.getTranslation("copy"));
361
        msgMenuTextEditor.addActionListener(new ActionListener() {
362
            @Override
363
            public void actionPerformed(ActionEvent e) {
364
                StringSelection selection = new StringSelection(automaticExpressionChecker.getMessage());
365
                Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
366
                clipboard.setContents(selection, selection);                
367
            }
368
        });
369
        
370
        JPopupMenu msgMenu = new JPopupMenu();
371
        msgMenu.add(msgMenuTextEditor);
372
        msgMenu.addSeparator();
373
        msgMenu.add(msgMenuCopy);
374
        this.lblMsg.setComponentPopupMenu(msgMenu);
375

    
376
        this.pnlDescription.setLayout(new BorderLayout());
377
//        this.pnlDescription.setBackground(this.txtDescription.getBackground());
378
        this.txtExpression.addCaretListener(new CaretListener() {
379
            @Override
380
            public void caretUpdate(CaretEvent e) {
381
                lblColumn.setText(Integer.toString(e.getDot()));
382
            }
383
        });
384
        this.treeElements.setCellRenderer(new DefaultTreeCellRenderer() {
385
            @Override
386
            public java.awt.Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
387
                JLabel label = (JLabel) super.getTreeCellRendererComponent(tree,value, selected, expanded, leaf, row, hasFocus);
388
                if( value instanceof Element ) {
389
                    Element element = (Element) value;
390
                    if( element.getIconName()==null ) {
391
                        if( element instanceof GroupElement ) {
392
                            label.setIcon(defaultIconGroup);
393
                        } else {
394
                            label.setIcon(defaultIconElement);
395
                        }
396
                    } else {
397
                        label.setIcon(element.getIcon());
398
                    }
399
                    label.setText(element.getName());
400
                } else {
401
                    label.setIcon(null);
402
                    label.setText(Objects.toString(value, ""));
403
                }
404
                return label;
405
            }
406
        });
407
        
408
        this.treeElements.addTreeSelectionListener(new TreeSelectionListener() {
409
            @Override
410
            public void valueChanged(TreeSelectionEvent e) {
411
                TreePath path = e.getPath();
412
                Element element = (Element) path.getLastPathComponent();
413
                if (element instanceof GroupElement) {
414
                    updateSimpleElementsList((GroupElement) element);
415
                }
416
            }
417
        });
418
        this.treeElements.addMouseListener(new MouseAdapter() {
419
            @Override
420
            public void mouseClicked(MouseEvent e) {
421
                if( e.getButton()==MouseEvent.BUTTON1 && e.getClickCount()==2 ) {
422
                    TreePath path = treeElements.getSelectionPath();
423
                    Element element = (Element) path.getLastPathComponent();
424
                    if( element instanceof SimpleElement ) {
425
                        doInsert((SimpleElement) element);
426
                    }
427
                }
428
            }
429
        });
430
        this.btnGroupElementInsert.addActionListener(new ActionListener() {
431
            @Override
432
            public void actionPerformed(ActionEvent e) {
433
                doInsert(txtGroupElement.getText());
434
            }
435
        });
436
        this.btnSimpleElementInsert.addActionListener(new ActionListener() {
437
            @Override
438
            public void actionPerformed(ActionEvent e) {
439
                ListElement<SimpleElement> element = (ListElement<SimpleElement>) lstSimpleElement.getSelectedValue();
440
                if (element != null) {
441
                    doInsert(element.getValue());
442
                }
443
            }
444
        });
445
        this.btnSimpleElementFilter.addActionListener(new ActionListener() {
446
            @Override
447
            public void actionPerformed(ActionEvent e) {
448
                doFilter();
449
            }
450
        });
451
        
452
        this.lstSimpleElement.setModel(new FilteredListModel());
453
        this.lstSimpleElement.setCellRenderer(new DefaultListCellRenderer() {
454
            @Override
455
            public java.awt.Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
456
                JLabel label = (JLabel)super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
457
                Element element = null;
458
                if( value instanceof ListElement ) {
459
                    if( ((ListElement) value).getValue() instanceof Element ) {
460
                        element = (Element) ((ListElement) value).getValue();
461
                    }
462
                }
463
                if( element !=null ) {
464
                    if( element.getIconName()==null ) {
465
                        if( element instanceof GroupElement ) {
466
                            label.setIcon(defaultIconGroup);
467
                        } else {
468
                            label.setIcon(defaultIconElement);
469
                        }
470
                    } else {
471
                        label.setIcon(element.getIcon());
472
                    }
473
                    label.setText(element.getName());
474
                    label.setEnabled(element.isEnabled());
475
                } else {
476
                    label.setIcon(null);
477
                    label.setText(Objects.toString(value, ""));
478
                }
479
                return label;
480
            }
481
        });
482
        this.lstSimpleElement.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
483
            @Override
484
            public void valueChanged(ListSelectionEvent e) {
485
                ListElement<SimpleElement> element = (ListElement<SimpleElement>) lstSimpleElement.getSelectedValue();
486
                if (element != null) {
487
                    doSelectElement(element.getValue());
488
                }
489
            }
490
        });
491
        this.lstSimpleElement.addMouseListener(new MouseAdapter() {
492
            @Override
493
            public void mouseClicked(MouseEvent e) {
494
                if( e.getButton()==MouseEvent.BUTTON1 && e.getClickCount()==2 ) {
495
                    ListElement<SimpleElement> element = (ListElement<SimpleElement>) lstSimpleElement.getSelectedValue();
496
                    if (element != null) {
497
                        doSelectElement(element.getValue());
498
                        doInsert(element.getValue());
499
                    }
500
                }
501
            }
502
        });
503
        this.lstSimpleElement.addKeyListener(new KeyAdapter() {
504
            @Override
505
            public void keyReleased(KeyEvent e) {
506
                if( e.getKeyCode()==KeyEvent.VK_ENTER ) {
507
                    ListElement<SimpleElement> element = (ListElement<SimpleElement>) lstSimpleElement.getSelectedValue();
508
                    if (element != null) {
509
                        doSelectElement(element.getValue());
510
                        doInsert(element.getValue());
511
                    }
512
                }
513
            }
514
        });
515

    
516
        this.btnSimpleElementTimeLimit.addActionListener(new ActionListener() {
517
            @Override
518
            public void actionPerformed(ActionEvent e) {
519
                doSetTimeLimit();
520
            }
521
        });
522
        this.btnSimpleElementSortDown.addActionListener(new ActionListener() {
523
            @Override
524
            public void actionPerformed(ActionEvent e) {
525
                FilteredListModel model = (FilteredListModel) lstSimpleElement.getModel();
526
                model.sort(false);
527
            }
528
        });
529
        this.btnSimpleElementSortUp.addActionListener(new ActionListener() {
530
            @Override
531
            public void actionPerformed(ActionEvent e) {
532
                FilteredListModel model = (FilteredListModel) lstSimpleElement.getModel();
533
                model.sort(true);
534
            }
535
        });
536
        this.txtSimpleElementFilter.addKeyListener(new KeyAdapter() {
537
            @Override
538
            public void keyTyped(KeyEvent e) {
539
                if (e.getKeyChar() == '\n') {
540
                    doFilter();
541
                }
542
            }
543
        });
544
        this.btnAdd.addActionListener(new ActionListener() {
545
            @Override
546
            public void actionPerformed(ActionEvent e) {
547
                doInsert(" + ");
548
            }
549
        });
550
        this.btnDiv.addActionListener(new ActionListener() {
551
            @Override
552
            public void actionPerformed(ActionEvent e) {
553
                doInsert(" / ");
554
            }
555
        });
556
        this.btnEq.addActionListener(new ActionListener() {
557
            @Override
558
            public void actionPerformed(ActionEvent e) {
559
                doInsert(" = ");
560
            }
561
        });
562
        this.btnMult.addActionListener(new ActionListener() {
563
            @Override
564
            public void actionPerformed(ActionEvent e) {
565
                doInsert(" * ");
566
            }
567
        });
568
        this.btnNeq.addActionListener(new ActionListener() {
569
            @Override
570
            public void actionPerformed(ActionEvent e) {
571
                doInsert(" <> ");
572
            }
573
        });
574
        this.btnParentClose.addActionListener(new ActionListener() {
575
            @Override
576
            public void actionPerformed(ActionEvent e) {
577
                doInsert(" ) ");
578
            }
579
        });
580
        this.btnParentOpen.addActionListener(new ActionListener() {
581
            @Override
582
            public void actionPerformed(ActionEvent e) {
583
                doInsert(" ( ");
584
            }
585
        });
586
        this.btnSubst.addActionListener(new ActionListener() {
587
            @Override
588
            public void actionPerformed(ActionEvent e) {
589
                doInsert(" - ");
590
            }
591
        });
592
        ScriptSwingManager scriptSwingManager = ToolsSwingLocator.getScriptSwingManager();
593
        this.scriptPicker = scriptSwingManager.createListScriptPickerController(
594
                cboPickerScripts,
595
                btnPickerRemove,
596
                btnPickerSelectScript
597
        );
598
        this.scriptEditor = scriptSwingManager.createScriptEditor();
599
        this.pnlScriptEditorContainer.setLayout(new BorderLayout());
600
        this.pnlScriptEditorContainer.add(this.scriptEditor.asJComponent(), BorderLayout.CENTER);
601
    
602
        this.tabExpressionBuilder.setEnabledAt(0, this.config.getScriptsEnabled());
603
        this.setPreferredSize(new Dimension(650, 350));
604
    }
605

    
606
    private String removeCursorMark(String s) {
607
        s = s.replace("{{", "");
608
        s = s.replace("}}", "");
609
        return s;
610
    }
611

    
612
    public void insertText(String text) {
613
        doInsert(text);
614
    }
615

    
616
    public String getText() {
617
        return this.txtExpression.getText();
618
    }
619
    
620
    public void setText(String text) {
621
        this.txtExpression.setText(text);
622
    }
623
    
624
    private void doInsert(String s) {
625
        this.txtExpression.replaceSelection(s);
626
        String ss = this.txtExpression.getText();
627
        int start = ss.indexOf("{{");
628
        int end = ss.indexOf("}}");
629
        if (start > 0 && end > 0) {
630
            this.txtExpression.replaceRange("", end, end + 2);
631
            this.txtExpression.replaceRange("", start, start + 2);
632
            this.txtExpression.setSelectionStart(start);
633
            this.txtExpression.setSelectionEnd(end - 2);
634
        }
635
        this.txtExpression.requestFocusInWindow();
636
    }
637

    
638
    private void doInsert(SimpleElement element) {
639
        if( element instanceof FunctionElement ) {
640
            Function function = ((FunctionElement)element).getFunction();
641
            if (function == null) {
642
                doInsert(element.getName());
643
            } else {
644
                doInsert(function.template() == null ? function.name() + "({{}})" : function.template());
645
            }
646
        } else if( element instanceof CalculatedElement) {
647
            Object value = ((CalculatedElement) element).getValue();
648
            if( value != null ) {
649
                doInsert(" "+value.toString()+" ");
650
            }
651
        } else if( element instanceof ConstantElement) {
652
            Object value = ((ConstantElement) element).getValue();
653
            doInsert(" "+Utils.repr(value)+" ");
654
        } else {
655
            doInsert(" "+element.getName()+" ");
656
        }
657
    }
658

    
659
    private void doFilter() {
660
        FilteredListModel model = (FilteredListModel) this.lstSimpleElement.getModel();
661
        model.setFilter(this.txtSimpleElementFilter.getText());
662
    }
663

    
664
    private void doSelectElement(SimpleElement element) {
665
        if( !element.isEnabled() ) {
666
            this.infoPanel.clean();
667
        } else {
668
            this.infoPanel.setDescription(removeCursorMark(element.getDescription()));
669
            this.infoPanel.setAdditionalPanel(element.getAditionalPanel(this));
670
            this.infoPanel.repaint();
671
        }        
672
    }
673

    
674
    private void updateSimpleElementsList(GroupElement group) {
675
        FilteredListModel model = new FilteredListModel();
676
        model.addElement("Loading...");
677
        this.currentGroup = group;
678
        this.txtGroupElement.setText(group.getName());
679
        this.txtSimpleElementFilter.setText("");
680
        this.infoPanel.clean();
681
        this.infoPanel.repaint();
682
        if (group instanceof SimpleElement) {
683
            this.btnGroupElementInsert.setEnabled(true);
684
        } else {
685
            this.btnGroupElementInsert.setEnabled(false);
686
        }
687
        this.lstSimpleElement.setEnabled(false);
688
        this.lstSimpleElement.setModel(model);
689

    
690
        Thread th = new Thread(new Runnable() {
691
            @Override
692
            public void run() {
693
                simpleElementsUpdater();
694
            }
695
        }, "ExpressionBuilderSimpleElementsUpdater");
696
        th.start();
697
    }
698

    
699
    private void simpleElementsUpdater() {
700
        final GroupElement group = this.currentGroup;
701
        final FilteredListModel model = new FilteredListModel();
702
        for (Element child : group) {
703
            if (group != this.currentGroup) {
704
                return;
705
            }
706
            if (child instanceof SimpleElement) {
707
                String label = child.getName();
708
                Function function = child instanceof FunctionElement?  ((FunctionElement)child).getFunction(): null;
709
                if (function != null && !StringUtils.isEmpty(function.returnType())) {
710
                    label = "<html><b>" + label + "</b> return " + function.returnType() + "</html>";
711
//                    label = label + " return " + function.returnType();
712
                }
713
                model.addElement(new ListElement<>(label, child));
714
            }
715
        }
716
        model.sort(true);
717
        SwingUtilities.invokeLater(new Runnable() {
718
            @Override
719
            public void run() {
720
                if (group != currentGroup) {
721
                    return;
722
                }
723
                if( group.hasMoreElements() ) {
724
                    lblSimpleElementsMsg.setText("More elements...");
725
                } else {
726
                    lblSimpleElementsMsg.setText("");
727
                }
728
                lstSimpleElement.setEnabled(true);
729
                lstSimpleElement.setModel(model);
730
            }
731
        });
732

    
733
    }
734

    
735
    @Override
736
    public Expression getExpression() {
737
        String phrase = this.txtExpression.getText();
738
        if (StringUtils.isEmpty(phrase)) {
739
            return null;
740
        }
741
        Expression expression = ExpressionEvaluatorLocator.getManager().createExpression();
742
        expression.setPhrase(phrase);
743
        Script script = ToolsLocator.getScriptManager().createScript("UserScript", "", "python");
744
        this.scriptEditor.fetch(script);
745
        if( !StringUtils.isEmpty(script.getCode()) ) {
746
            expression.setUserScript(script);
747
        }
748
        return expression;
749
    }
750

    
751
    @Override
752
    public void setExpression(Expression expression) {
753
        if( expression==null ) {
754
            this.txtExpression.setText("");
755
            this.scriptEditor.set(null);
756
            this.scriptPicker.set(null);
757
        } else {
758
            this.txtExpression.setText(expression.getPhrase());
759
            if( expression.getUserScript()!=null ) {
760
                this.scriptEditor.set(expression.getUserScript());
761
            }
762
            if( expression.getScripts()!=null ) {
763
                this.scriptPicker.set(expression.getScripts().toList());
764
            }
765
        }
766
    }
767
    
768
    @Override
769
    public int getSimpleElementsLimit() {
770
        return this.config.getSimpleElementsLimit();
771
    }
772

    
773
    @Override
774
    public void setSimpleElementsLimit(int limit) {
775
        this.config.setSimpleElementsLimit(limit);
776
    }
777
    
778
    @Override
779
    public List<Element> getElements() {
780
        return this.config.getElements();
781
    }
782
    
783
    @Override
784
    public void addElement(Element element) {
785
        this.config.addElement(element);
786
    }
787
    
788
    @Override
789
    public Collection<SymbolTable> getSymbolTables() {
790
        return this.config.getSymbolTables();
791
    }
792
    
793
    @Override
794
    public void addSymbolTable(String name) {
795
        this.config.addSymbolTable(name);
796
    }
797
    
798
    @Override
799
    public void addSymbolTable(SymbolTable symbolTable) {
800
        this.config.addSymbolTable(symbolTable);
801
    }
802

    
803
    @Override
804
    public void removeAllSymbolTables() {
805
        this.config.removeAllSymbolTables();
806
    }
807

    
808
    @Override
809
    public void removeSymbolTable(String name) {
810
        this.config.removeSymbolTable(name);
811
    }
812

    
813
    @Override
814
    public SymbolTable getPreviewSymbolTable() {
815
        return this.config.getPreviewSymbolTable();
816
    }
817

    
818
    @Override
819
    public void setPreviewSymbolTable(SymbolTable symbolTable) {
820
        this.config.setPreviewSymbolTable(symbolTable);
821
    }
822

    
823
    @Override
824
    public void setScriptsEnabled(boolean enabled) {
825
        this.config.setScriptsEnabled(enabled);
826
        this.tabExpressionBuilder.setEnabledAt(0, this.config.getScriptsEnabled());
827
    }
828

    
829
    @Override
830
    public boolean getScriptsEnabled() {
831
        return this.config.getScriptsEnabled();
832
    }
833
    
834
    @Override
835
    public JComponent asJComponent() {
836
        if( this.treeModel == null ) {
837
            I18nManager i18n = ToolsLocator.getI18nManager();
838
            this.treeModel = new ElementsTreeModel(
839
                    this.manager.createGroupElement(
840
                            i18n.getTranslation("_Elements"),
841
                            this.getElements()
842
                    )
843
            );
844
            this.treeElements.setModel(this.treeModel);
845
        }
846
        return this;
847
    }
848
    
849
    private void doSetTimeLimit() {
850
        ThreadSafeDialogsManager dialogs = ToolsSwingLocator.getThreadSafeDialogsManager();
851
        String s = dialogs.inputDialog(
852
                "Indicate the time limit in seconds for calculating the elements", 
853
                "Time limit", 
854
                JOptionPane.QUESTION_MESSAGE, 
855
                String.valueOf(this.config.getSimpleElementsLimit())
856
        );
857
        if( StringUtils.isBlank(s) ) {
858
            return;
859
        }
860
        try {
861
            int n = Integer.parseInt(s);
862
            this.config.setSimpleElementsLimit(n);
863
            this.treeModel.reload();
864
        } catch(Exception ex) {
865
            dialogs.messageDialog(
866
                "Invalid time limit '"+s+"'.", 
867
                "Time limit", 
868
                JOptionPane.WARNING_MESSAGE
869
            );                    
870
        }
871
    }
872
}