View Javadoc

1   /*
2    *  File: CsvListEditor.java 
3    *  Copyright (c) 2004-2007  Peter Kliem (Peter.Kliem@jaret.de)
4    *  A commercial license is available, see http://www.jaret.de.
5    *
6    * All rights reserved. This program and the accompanying materials
7    * are made available under the terms of the Common Public License v1.0
8    * which accompanies this distribution, and is available at
9    * http://www.eclipse.org/legal/cpl-v10.html
10   */
11  package de.jaret.util.ui;
12  
13  import java.util.ArrayList;
14  import java.util.HashMap;
15  import java.util.List;
16  import java.util.Map;
17  import java.util.StringTokenizer;
18  
19  import org.eclipse.jface.viewers.IContentProvider;
20  import org.eclipse.jface.viewers.ILabelProvider;
21  import org.eclipse.jface.viewers.IStructuredContentProvider;
22  import org.eclipse.swt.SWT;
23  import org.eclipse.swt.custom.JaretStyledText;
24  import org.eclipse.swt.custom.StyleRange;
25  import org.eclipse.swt.events.FocusEvent;
26  import org.eclipse.swt.events.FocusListener;
27  import org.eclipse.swt.events.KeyEvent;
28  import org.eclipse.swt.events.KeyListener;
29  import org.eclipse.swt.events.ModifyEvent;
30  import org.eclipse.swt.events.ModifyListener;
31  import org.eclipse.swt.events.VerifyEvent;
32  import org.eclipse.swt.events.VerifyListener;
33  import org.eclipse.swt.layout.GridData;
34  import org.eclipse.swt.layout.GridLayout;
35  import org.eclipse.swt.widgets.Composite;
36  import org.eclipse.swt.widgets.Display;
37  
38  import de.jaret.util.misc.MiscUtil;
39  import de.jaret.util.ui.model.IMutableContentProvider;
40  
41  /**
42   * @author Peter Kliem
43   * @version $Id: CsvListEditor.java 242 2007-02-11 21:05:07Z olk $
44   */
45  public class CsvListEditor extends Composite implements FocusListener, VerifyListener, KeyListener, ModifyListener {
46  
47      private ILabelProvider _srcLabelProvider;
48      private ILabelProvider _destLabelProvider;
49      private IMutableContentProvider _destContentProvider;
50      private IStructuredContentProvider _srcContentProvider;
51  
52      private String _separator = ",";
53      private Map _labelMap = new HashMap();
54      private List _labelList = new ArrayList();
55      private boolean _caseSensitive = false;
56      private boolean _contentParsable = true;
57  
58      JaretStyledText _text;
59  
60      public CsvListEditor(Composite parent, int style) {
61          super(parent, style);
62  
63          _srcLabelProvider = new DefaultLabelProvider();
64          _destLabelProvider = _srcLabelProvider;
65  
66          createControls();
67      }
68  
69      private void updateText() {
70          StringBuffer buf = new StringBuffer();
71          Object[] dest = _destContentProvider.getElements(null);
72          for (int i = 0; i < dest.length; i++) {
73              String label = _destLabelProvider.getText(dest[i]);
74              buf.append(label);
75              if (i < dest.length - 1) {
76                  buf.append(_separator);
77              }
78          }
79          _text.setText(buf.toString());
80          _contentParsable = true;
81      }
82  
83      /**
84       * parses the text and updates the destination List
85       * 
86       * @return
87       */
88      private boolean updateDest() {
89          String input = _text.getText().trim();
90          // if there is an ending separator -> remove
91          if (input.endsWith(_separator)) {
92              input = input.substring(0, input.length() - 1);
93          }
94          StringTokenizer tokenizer = new StringTokenizer(input, _separator, true);
95          boolean success = true;
96          List objects = new ArrayList();
97  
98          while (tokenizer.hasMoreTokens()) {
99              String token = tokenizer.nextToken();
100             if (!token.equals(_separator)) {
101                 token = token.trim();
102                 if (!_caseSensitive) {
103                     token = token.toLowerCase();
104                 }
105                 Object o = _labelMap.get(token);
106                 if (o == null) {
107                     success = false;
108                     break;
109                 } else {
110                     objects.add(o);
111                 }
112             }
113         }
114         if (success) {
115             // TODO this is rude way
116             _destContentProvider.clear();
117             for (int i = 0; i < objects.size(); i++) {
118                 _destContentProvider.addToDest(objects.get(i));
119             }
120         }
121         _contentParsable = success;
122         return success;
123     }
124 
125     private void createControls() {
126         GridLayout gridLayout = new GridLayout();
127         gridLayout.numColumns = 1;
128         this.setLayout(gridLayout);
129 
130         GridData gd = new GridData(GridData.FILL_HORIZONTAL);
131         _text = new JaretStyledText(this, SWT.SINGLE | SWT.BORDER);
132         _text.setLayoutData(gd);
133         _text.addFocusListener(this);
134         // _text.addKeyListener(this);
135         _text.addModifyListener(this);
136     }
137 
138     /**
139      * check wether the labels of the elements for a unique key
140      * 
141      * @param cp
142      * @param lp
143      * @return
144      */
145     private boolean checkLabelUniqueness(IStructuredContentProvider cp, ILabelProvider lp) {
146         boolean result = true;
147         Map labels = new HashMap();
148         Object[] elements = cp.getElements(null);
149         for (int i = 0; i < elements.length; i++) {
150             String label = lp.getText(elements[i]).trim();
151             if (!_caseSensitive) {
152                 label = label.toLowerCase();
153             }
154             if (labels.get(label) != null) {
155                 result = false;
156                 break;
157             } else {
158                 labels.put(label, "x");
159             }
160         }
161         return result;
162     }
163 
164     private void fillLabelMap() {
165         Object[] elements = _srcContentProvider.getElements(null);
166         for (int i = 0; i < elements.length; i++) {
167             String label = _srcLabelProvider.getText(elements[i]).trim();
168             if (!_caseSensitive) {
169                 label = label.toLowerCase();
170             }
171             _labelMap.put(label, elements[i]);
172             _labelList.add(label);
173         }
174     }
175 
176     public void setSrcLabelProvider(ILabelProvider labelProvider) {
177         _srcLabelProvider = labelProvider;
178     }
179 
180     public void setDestLabelProvider(ILabelProvider labelProvider) {
181         _destLabelProvider = labelProvider;
182     }
183 
184     /**
185      * @return Returns the destContentProvider.
186      */
187     public IMutableContentProvider getDestContentProvider() {
188         return _destContentProvider;
189     }
190 
191     /**
192      * @param destContentProvider The destContentProvider to set.
193      */
194     public void setDestContentProvider(IMutableContentProvider destContentProvider) {
195         _destContentProvider = destContentProvider;
196         updateText();
197     }
198 
199     public void updateX() {
200         updateText();
201         updateDest2(false);
202     }
203 
204     /**
205      * @return Returns the srcContentProvider.
206      */
207     public IContentProvider getSrcContentProvider() {
208         return _srcContentProvider;
209     }
210 
211     /**
212      * @param srcContentProvider The srcContentProvider to set.
213      */
214     public void setSrcContentProvider(IStructuredContentProvider srcContentProvider) {
215         _srcContentProvider = srcContentProvider;
216         if (!checkLabelUniqueness(_srcContentProvider, _srcLabelProvider)) {
217             throw new RuntimeException("SrcLabels must form a unique key");
218         }
219         fillLabelMap();
220     }
221 
222     /**
223      * @return Returns the destLabelProvider.
224      */
225     public ILabelProvider getDestLabelProvider() {
226         return _destLabelProvider;
227     }
228 
229     /**
230      * @return Returns the srcLabelProvider.
231      */
232     public ILabelProvider getSrcLabelProvider() {
233         return _srcLabelProvider;
234     }
235 
236     /*
237      * (non-Javadoc)
238      * 
239      * @see org.eclipse.swt.events.FocusListener#focusGained(org.eclipse.swt.events.FocusEvent)
240      */
241     public void focusGained(FocusEvent arg0) {
242     }
243 
244     /*
245      * (non-Javadoc)
246      * 
247      * @see org.eclipse.swt.events.FocusListener#focusLost(org.eclipse.swt.events.FocusEvent)
248      */
249     public void focusLost(FocusEvent arg0) {
250         boolean success = updateDest();
251         if (!success) {
252             _text.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_RED));
253         } else {
254             _text.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
255             _text.removeModifyListener(this);
256             updateText();
257             updateDest2(false);
258             _text.addModifyListener(this);
259         }
260     }
261 
262     private void resetStyle() {
263         _text.setStyleRanges(new StyleRange[] {});
264     }
265 
266     private boolean updateDest2(boolean updateDest) {
267         String input = MiscUtil.rightTrim(_text.getText());
268         // if there is an ending separator -> remove
269         if (input.endsWith(_separator)) {
270             input = input.substring(0, input.length() - 1);
271         }
272         boolean success = true;
273         char separator = _separator.charAt(0);
274         List objects = new ArrayList();
275         List styleRanges = new ArrayList();
276         int pos = 0;
277 
278         while (pos < input.length()) {
279             int beginToken = -1;
280             int endToken = -1;
281 
282             // skip leading whitespaces
283             while (pos < input.length() && Character.isWhitespace(input.charAt(pos))) {
284                 pos++;
285             }
286             beginToken = pos;
287             // search ending position
288             while (pos < input.length() && input.charAt(pos) != separator) {
289                 pos++;
290             }
291             if (beginToken == endToken) {
292                 // no input
293                 break;
294             }
295             endToken = pos;
296             String token = input.substring(beginToken, endToken);
297             // System.out.println("TOKEN:"+token+"|");
298             pos++; // skip separator
299 
300             // check token
301             token = token.trim();
302             if (!_caseSensitive) {
303                 token = token.toLowerCase();
304             }
305             Object o = _labelMap.get(token);
306             if (o == null) {
307                 success = false;
308             } else {
309                 objects.add(o);
310                 // System.out.println("TOKENSUCCESS:"+token+"|"+beginToken+","+endToken);
311                 styleRanges.add(new StyleRange(beginToken, endToken - beginToken, _text.getForeground(), _text
312                         .getBackground(), SWT.BOLD));
313             }
314         }
315         /*
316          * if (success) { // TODO this is rude way _destContentProvider.clear(); for(int i=0;i<objects.size();i++){
317          * _destContentProvider.addToDest(objects.get(i)); } }
318          */_contentParsable = success;
319         StyleRange srs[] = new StyleRange[styleRanges.size()];
320         for (int i = 0; i < styleRanges.size(); i++) {
321             StyleRange sr = (StyleRange) styleRanges.get(i);
322             srs[i] = sr;
323         }
324         _text.setStyleRanges(srs);
325         return success;
326     }
327 
328     /**
329      * @return Returns the caseSensitive.
330      */
331     public boolean isCaseSensitive() {
332         return _caseSensitive;
333     }
334 
335     /**
336      * @param caseSensitive The caseSensitive to set.
337      */
338     public void setCaseSensitive(boolean caseSensitive) {
339         _caseSensitive = caseSensitive;
340     }
341 
342     /**
343      * @return Returns the separator.
344      */
345     public String getSeparator() {
346         return _separator;
347     }
348 
349     /**
350      * @param separator The separator to set.
351      */
352     public void setSeparator(String separator) {
353         _separator = separator;
354     }
355 
356     /**
357      * @return Returns the contentParsable.
358      */
359     public boolean isContentParsable() {
360         return _contentParsable;
361     }
362 
363     private String possibleLabel(String prefix) {
364         if (!_caseSensitive) {
365             prefix = prefix.toLowerCase();
366         }
367         if (prefix.length() == 0) {
368             return "";
369         }
370         // exact match?
371         Object o = _labelMap.get(prefix);
372         if (o != null) {
373             return prefix;
374         }
375         for (int i = 0; i < _labelList.size(); i++) {
376             String label = (String) _labelList.get(i);
377             if (label.startsWith(prefix)) {
378                 return label;
379             }
380         }
381         return "";
382     }
383 
384     /*
385      * (non-Javadoc)
386      * 
387      * @see org.eclipse.swt.events.VerifyListener#verifyText(org.eclipse.swt.events.VerifyEvent)
388      */
389     public void verifyText(VerifyEvent ve) {
390         /*
391          * System.out.println("ve "+ve.text+" start:"+ve.start+" end:"+ve.end); // search for the prefix String text =
392          * _text.getText(); String prefix = getPrefix(text, ve.start);
393          */}
394 
395     /**
396      * @param text
397      * @return
398      */
399     private String getPrefix(String text, int pos) {
400         if (text.length() < 1) {
401             return "";
402         }
403         int end = pos;
404         while (pos > 0 && !text.substring(pos - 1, pos).equals(_separator)) {
405             pos--;
406         }
407         String prefix = text.substring(pos, end);
408         return MiscUtil.leftTrim(prefix);
409     }
410 
411     /*
412      * (non-Javadoc)
413      * 
414      * @see org.eclipse.swt.events.KeyListener#keyPressed(org.eclipse.swt.events.KeyEvent)
415      */
416     public void keyPressed(KeyEvent e) {
417         // System.out.println("keytpressed "+e.toString());
418         // if (e.character=='a') e.doit=false;
419     }
420 
421     /*
422      * (non-Javadoc)
423      * 
424      * @see org.eclipse.swt.events.KeyListener#keyReleased(org.eclipse.swt.events.KeyEvent)
425      */
426     public void keyReleased(KeyEvent arg0) {
427     }
428 
429     /*
430      * (non-Javadoc)
431      * 
432      * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
433      */
434     private boolean ignoreNext = false;
435 
436     public void modifyText(ModifyEvent arg0) {
437         updateDest2(false);
438         // System.out.println("Pos "+_text.getCaretOffset()+" lpos "+lastPos+" ignoreNext "+ignoreNext);
439         int pos = _text.getCaretOffset();
440         if (ignoreNext) {
441             ignoreNext = false;
442             return;
443         }
444         System.out.println("modify " + _text.getText());
445         String prefix = getPrefix(_text.getText(), pos);
446         // System.out.println("Prefix:"+prefix);
447         String label = possibleLabel(prefix);
448         // System.out.println("possible:"+label);
449         if (label.length() > 0) {
450             String rest = label.substring(prefix.length());
451             System.out.println("Rest:" + rest);
452             _text.removeModifyListener(this);
453             ignoreNext = true;
454             _text.insert(rest);
455             _text.setSelection(pos, pos + rest.length());
456             _text.setCaretOffsetJaret(pos); // hacked version of setCaretOffset without clearing of the selection
457             _text.addModifyListener(this);
458         }
459     }
460 
461     /*
462      * (non-Javadoc)
463      * 
464      * @see org.eclipse.swt.widgets.Control#setToolTipText(java.lang.String)
465      */
466     public void setToolTipText(String toolTip) {
467         super.setToolTipText(toolTip);
468         _text.setToolTipText(toolTip);
469     }
470 }