View Javadoc

1   /*
2      Copyright 2002-2006 Martin van den Bemt
3   
4      Licensed under the Apache License, Version 2.0 (the "License");
5      you may not use this file except in compliance with the License.
6      You may obtain a copy of the License at
7   
8          http://www.apache.org/licenses/LICENSE-2.0
9   
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15  */
16  package org.xulux.guilayer.swing.widgets;
17  
18  import java.awt.Container;
19  import java.awt.event.ActionListener;
20  import java.util.ArrayList;
21  import java.util.EventListener;
22  import java.util.Iterator;
23  import java.util.List;
24  
25  import javax.swing.JMenuItem;
26  import javax.swing.JScrollPane;
27  import javax.swing.JTable;
28  import javax.swing.table.TableModel;
29  
30  import org.xulux.api.dataprovider.IField;
31  import org.xulux.api.dataprovider.IMapping;
32  import org.xulux.api.gui.IContentWidget;
33  import org.xulux.api.gui.IWidget;
34  import org.xulux.api.gui.IXuluxListener;
35  import org.xulux.core.XuluxContext;
36  import org.xulux.gui.ContainerWidget;
37  import org.xulux.gui.WidgetFactory;
38  import org.xulux.guilayer.swing.extensions.NyxJTable;
39  import org.xulux.guilayer.swing.listeners.NewSelectionListener;
40  import org.xulux.guilayer.swing.listeners.PopupListener;
41  import org.xulux.guilayer.swing.listeners.UpdateButtonsListener;
42  import org.xulux.guilayer.swing.listeners.ValueChangedListener;
43  import org.xulux.guilayer.swing.models.NyxTableCellEditor;
44  import org.xulux.guilayer.swing.models.NyxTableColumnModel;
45  import org.xulux.guilayer.swing.models.NyxTableModel;
46  import org.xulux.guilayer.swing.rules.DefaultTableRule;
47  import org.xulux.utils.ClassLoaderUtils;
48  import org.xulux.utils.XuluxCollectionUtils;
49  
50  /**
51   * A nyx table..
52   * A nyx table can do without a popupmenu field and can add
53   * a menuitem directly to its table.
54   *
55   * @todo Redo this completely! It sucks big time!!
56   *
57   * @author <a href="mailto:martin@mvdb.net">Martin van den Bemt</a>
58   * @version $Id: Table.java,v 1.1 2005/12/18 12:58:18 mvdb Exp $
59   */
60  public class Table extends ContainerWidget implements IContentWidget {
61  
62      /**
63       * The native swing table
64       */
65      protected NyxJTable table;
66  
67      /**
68       * The native locked table
69       */
70      protected NyxJTable lockedTable;
71  
72      /**
73       * This is the native widget
74       */
75      protected JScrollPane scrollPane;
76      /**
77       * The table context menu
78       */
79      protected IWidget menu;
80      /**
81       * does the table have childpopups
82       */
83      protected boolean hasChildPopups;
84      /**
85       * are the child popups checked
86       */
87      private boolean childPopupsChecked;
88  
89      /**
90       * The content of the table
91       */
92      protected Object content;
93      /**
94       * has the contentchanged ?
95       */
96      protected boolean contentChanged;
97  
98      /**
99       * The columnModel
100      */
101     protected NyxTableColumnModel columnModel;
102 
103     /**
104      * The tablemodel
105      */
106     protected NyxTableModel model;
107     /**
108      * the celleditor
109      */
110     protected NyxTableCellEditor editor;
111     /**
112      * the new selection listener
113      */
114     protected NewSelectionListener newSelectionListener;
115     /**
116      * the oldsize list
117      */
118     private int oldListSize = 0;
119     /**
120      * the current list size
121      */
122     private int listSize = 0;
123 
124     /**
125      * @param name the name of the table
126      */
127     public Table(String name) {
128         super(name);
129     }
130 
131     /**
132      * Destroys the current table
133      */
134     public void destroyTable() {
135         if (!initialized) {
136             return;
137         }
138         if (newSelectionListener != null) {
139             table.getSelectionModel().removeListSelectionListener(newSelectionListener);
140         }
141         if (this.columnModel != null) {
142             this.columnModel.destroy();
143             this.columnModel = null;
144         }
145         if (this.model != null) {
146             this.model.destroy();
147             this.model = null;
148         }
149         if (this.editor != null) {
150             this.editor.destroy();
151             this.editor = null;
152         }
153         if (this.scrollPane != null) {
154             if (this.table != null) {
155                 this.scrollPane.remove(this.table);
156             }
157             if (this.lockedTable != null) {
158                 this.scrollPane.remove(lockedTable);
159             }
160         }
161         this.table = null;
162         this.lockedTable = null;
163     }
164     /**
165      * @see org.xulux.gui.XuluxWidget#destroy()
166      */
167     public void destroy() {
168         if (!initialized) {
169             return;
170         }
171         super.destroy();
172         destroyTable();
173         if (this.scrollPane == null) {
174             return;
175         }
176         Container container = this.scrollPane.getParent();
177         this.scrollPane.setVisible(false);
178         this.scrollPane.removeAll();
179         if (container != null) {
180             container.remove(this.scrollPane);
181         }
182         this.scrollPane = null;
183         this.table = null;
184     }
185 
186     /**
187      * @see org.xulux.nyx.gui.XuluxWidget#getNativeWidget()
188      */
189     public Object getNativeWidget() {
190         if (!initialized) {
191             initialize();
192         }
193         return this.scrollPane;
194     }
195 
196     /**
197      * @see org.xulux.nyx.gui.XuluxWidget#initialize()
198      */
199     public void initialize() {
200         if (initialized) {
201             return;
202         }
203         initialized = true;
204         initializing = true;
205         contentChanged = true;
206         this.scrollPane = new JScrollPane();
207         // always add the default table rule.
208         registerRule(new DefaultTableRule());
209         processIgnoreUse();
210         refresh();
211         processInit();
212         initializing = false;
213     }
214 
215     /**
216      * Sets all children of the table to ignore their use
217      * variable when setting values..
218      * @todo Add documentation about this. The widget should
219      * possibly do something with this if it wants to live
220      * in a table
221      */
222     private void processIgnoreUse() {
223         List list = getChildWidgets();
224         if (list == null) {
225             return;
226         }
227         Iterator it = list.iterator();
228         ValueChangedListener vcl = new ValueChangedListener(this);
229         while (it.hasNext()) {
230             IWidget widget = (IWidget) it.next();
231             widget.ignoreUse(true);
232             widget.addXuluxListener(vcl);
233         }
234     }
235 
236     /**
237      * @see org.xulux.nyx.gui.XuluxWidget#refresh()
238      */
239     public void refresh() {
240         if (isRefreshing) {
241             return;
242         }
243         initialize();
244         isRefreshing = true;
245         initializeContent();
246         if (contentChanged) {
247             //this.destroyTable();
248             if (this.columnModel == null) {
249                 this.columnModel = new NyxTableColumnModel(this);
250                 //                System.out.println("hasLockedColumns : "+this.columnModel.hasLockedColumns());
251             }
252             boolean refreshModel = false;
253             if (this.model == null) {
254                 if (content instanceof TableModel) {
255                     this.model = new NyxTableModel((TableModel) content, this);
256                 } else {
257                     this.model = new NyxTableModel(this);
258                 }
259             } else {
260                 refreshModel = true;
261             }
262                 
263             
264             if (this.editor == null) {
265                 this.editor = new NyxTableCellEditor(this);
266             }
267             boolean hasLockedColumns = columnModel.hasLockedColumns();
268             if (hasLockedColumns && lockedTable == null) {
269                 lockedTable = new NyxJTable(model, columnModel.getLockedColumnModel());
270                 lockedTable.setCellEditor(editor);
271                 lockedTable.getSelectionModel().addListSelectionListener(new UpdateButtonsListener(this));
272             }
273             if (table == null) {
274                 table = new NyxJTable(this.model, this.columnModel);
275                 table.setCellEditor(this.editor);
276                 table.getSelectionModel().addListSelectionListener(new UpdateButtonsListener(this));
277                 table.getSelectionModel().addListSelectionListener(new NewSelectionListener(this));
278                 table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
279                 table.getTableHeader().setReorderingAllowed(false);
280                 scrollPane.setViewportView(table);
281                 scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
282                 scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
283             }
284             if (hasLockedColumns && table.getSiblingTable() == null) {
285                 table.setSiblingTable(lockedTable);
286                 lockedTable.setSiblingTable(table);
287                 table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
288                 lockedTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
289                 lockedTable.getTableHeader().setReorderingAllowed(false);
290                 ((NyxTableColumnModel) table.getColumnModel()).removeLockedColumns();
291                 ((NyxTableColumnModel) table.getColumnModel()).removeUnlockedColumns();
292                 lockedTable.setPreferredScrollableViewportSize(columnModel.getLockedColumnWidth());
293                 //                System.out.println("ColumnModel : "+columnModel.getLockedColumnWidth());
294                 scrollPane.setRowHeaderView(lockedTable);
295                 scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, lockedTable.getTableHeader());
296             }
297             scrollPane.setVisible(true);
298             table.setVisible(true);
299             table.setVisible(false);
300             table.setVisible(true);
301             // make sure that the horizontal scrollbar is completely left..
302             scrollPane.getHorizontalScrollBar().setValue(0);
303             if (refreshModel) {
304                 // null means everything changed..
305                 table.tableChanged(null);
306                 if (lockedTable != null) {
307                     lockedTable.tableChanged(null);
308                 }
309             }
310             getPart().refreshWidgets(this);
311             contentChanged = false;
312         }
313         initializeUpdateButtons();
314         refreshUpdateButtons();
315         String height = getProperty("rowHeight");
316         if (height != null) {
317             int rowHeight = Integer.valueOf(height).intValue();
318             if (rowHeight > 0) {
319                 table.setRowHeight(Integer.valueOf(height).intValue());
320                 if (lockedTable != null) {
321                   lockedTable.setRowHeight(Integer.valueOf(height).intValue());
322                 }
323             }
324         }
325         // we want to set some things on initialization of the widget..
326         // @todo Move this to propertyhandlers..
327         if (content != null) {
328             String initialSelection = getProperty("initialselection");
329             if (initialSelection != null) {
330                 System.out.println("initialSelection : " + initialSelection);
331                 if ("last".equalsIgnoreCase(initialSelection)) {
332                     table.changeSelection(table.getRowCount() - 1, 1, false, false);
333                 } else if ("first".equalsIgnoreCase(initialSelection)) {
334                     table.changeSelection(0, 1, false, false);
335                 } else if ("all".equalsIgnoreCase(initialSelection)) {
336                     table.selectAll();
337                 }
338                 // and remove this property once processed!
339                 setProperty("initialselection", null);
340                 // with none, we don't have to do anything.
341                 // else if ("none".equalsIgnoreCase(initialSelection)) {
342                 //}
343             }
344         }
345         scrollPane.setVisible(isVisible());
346         isRefreshing = false;
347     }
348 
349     /**
350      * Checkes through other means if the content is changed.
351      * It could be that someone adjusted the content..
352      *
353      */
354     private void checkContentChanged() {
355         if (!contentChanged && listSize != oldListSize) {
356             contentChanged = true;
357         }
358     }
359 
360     /**
361      * @see org.xulux.nyx.gui.XuluxWidget#getGuiValue()
362      */
363     public Object getGuiValue() {
364         if (table == null) {
365             return null;
366         }
367         int selectedRow = table.getSelectedRow();
368         if (selectedRow == -1) {
369             return null;
370         }
371         if (table.getRowSelectionAllowed() && !table.getCellSelectionEnabled()) {
372             return table.getModel().getValueAt(selectedRow, -1);
373         } else if (!table.getRowSelectionAllowed() && table.getCellSelectionEnabled()) {
374             return table.getModel().getValueAt(selectedRow, table.getSelectedColumn());
375         }
376         return null;
377     }
378     
379     public Object getMouseOverValue() {
380        if (table == null) {
381            return null;
382        }
383        int selectedRow = table.getSelectedRow();
384        if (selectedRow == -1) {
385            return null;
386        	}
387    	   if (table.getRowSelectionAllowed() && !table.getCellSelectionEnabled()) {
388 		   return table.getModel().getValueAt(selectedRow, -1);
389 	   } else if (!table.getRowSelectionAllowed() && table.getCellSelectionEnabled()) {
390 	       return table.getModel().getValueAt(selectedRow, table.getSelectedColumn());
391 	   }
392 	   return null;
393     }
394 
395     /**
396      * @see org.xulux.nyx.gui.XuluxWidget#focus()
397      */
398     public void focus() {
399         this.scrollPane.requestFocus();
400         // if widget is not showing we have
401         // to make it showing..
402         if (!this.scrollPane.isShowing() && getParent() != null) {
403             // set the session variable, so controls
404             // can look who requested focus..
405             getPart().getSession().setValue("nyx.focusrequest", this);
406             getParent().focus();
407         }
408         // remove session variable again.
409         getPart().getSession().remove("nyx.focusrequest");
410         this.scrollPane.requestFocus();
411     }
412 
413     /**
414      * @see org.xulux.nyx.gui.ContainerWidget#addToParent(org.xulux.nyx.gui.XuluxWidget)
415      */
416     public void addToParent(IWidget widget) {
417         if (widget instanceof PopupMenu || widget instanceof MenuItem) {
418             hasChildPopups = true;
419         }
420     }
421 
422     /**
423      * @see org.xulux.nyx.gui.XuluxWidget#canContainValue()
424      */
425     public boolean canContainValue() {
426         return true;
427     }
428 
429     /**
430      * @see org.xulux.nyx.gui.XuluxWidget#isValueEmpty()
431      */
432     public boolean isValueEmpty() {
433         return getGuiValue() == null;
434     }
435 
436     /**
437      * Initializes the content when the property
438      * content and content.type is present
439      */
440     protected void initializeContent() {
441         String contentProp = getProperty("content");
442         String contentType = getProperty("content.type");
443         if (contentType == null) {
444             return;
445         }
446         if (contentProp == null && !contentType.equalsIgnoreCase("bean")) {
447             return;
448         }
449         if (contentType.equalsIgnoreCase("string")) {
450             this.content = XuluxCollectionUtils.getListFromCSV(contentProp);
451             contentChanged = true;
452         } else if (contentType.equalsIgnoreCase("field")) {
453             int index = contentProp.lastIndexOf(".");
454             IMapping mapping = null;
455             IField field = null;
456             if (index == -1) {
457                 mapping = XuluxContext.getDictionary().getDefaultProvider().getMapping(getPart().getBean());
458                 field = mapping.getField(contentProp);
459             } else {
460                 mapping = XuluxContext.getDictionary().getDefaultProvider().getMapping(ClassLoaderUtils.getClass(contentProp.substring(0, index)));
461                 field = mapping.getField(contentProp.substring(index + 1));
462             }
463             if (field != null) {
464                 //System.out.println("Field : "+field);
465                 this.content = XuluxCollectionUtils.getList(field.getValue(getPart().getBean()));
466                 //                System.err.println("CONTENT : "+this.content);
467                 //                System.err.println("field : "+((List)field.getValue(getPart().getBean())).size());
468                 contentChanged = true;
469             } else {
470                 System.out.println("Field " + content + " for table " + getName() + " could not be found");
471             }
472         } else if (contentType.equalsIgnoreCase("bean")) {
473             if ("raw".equalsIgnoreCase(contentProp)) {
474               this.content = getPart().getBean();
475             } else {
476               this.content = XuluxCollectionUtils.getList(getPart().getBean());
477             }
478             contentChanged = true;
479         }
480     }
481 
482     /**
483      * Refreshes the update buttons.
484      * The buttons update and delete are disabled when no
485      * row is selected.
486      *
487      */
488     public void refreshUpdateButtons() {
489         Object value = getGuiValue();
490         boolean buttonState = value != null;
491         // we need to disable update and delete
492         String buttons = getProperty("updatebuttons");
493         if (buttons != null) {
494             List strList = XuluxCollectionUtils.getListFromCSV(buttons);
495             Iterator it = strList.iterator();
496             while (it.hasNext()) {
497                 IWidget widget = getPart().getWidget((String) it.next());
498                 if (widget != null) {
499                     String actionType = widget.getProperty("action.type");
500                     if (actionType != null && (actionType.equals("update") || actionType.equals("delete"))) {
501                         widget.setEnabled(buttonState);
502                     }
503                 }
504             }
505         }
506         if (menu != null) {
507             if (menu.getChildWidgets() != null) {
508                 for (Iterator it = menu.getChildWidgets().iterator(); it.hasNext();) {
509                     IWidget cw = (IWidget) it.next();
510                     if (cw instanceof MenuItem) {
511                         String actionType = cw.getProperty("action.type");
512                         if (actionType != null && (actionType.equals("update") || actionType.equals("delete"))) {
513                             cw.setEnabled(buttonState);
514                         }
515                     }
516                 }
517             }
518         }
519     }
520     /**
521      * Initializes the popupmenus of the table.
522      *
523      * @return if initialize update did some work
524      */
525     protected boolean initializeUpdateButtons() {
526         if (this.menu != null) {
527             return false;
528         }
529         String buttons = getProperty("updatebuttons");
530         if (buttons != null) {
531             String updateAction = getProperty("updateaction");
532             List list = new ArrayList();
533             List strList = XuluxCollectionUtils.getListFromCSV(buttons);
534             Iterator it = strList.iterator();
535             while (it.hasNext()) {
536                 String wName = (String) it.next();
537                 IWidget widget = getPart().getWidget(wName);
538                 if (widget == null) {
539                     System.out.println("Button " + wName + " does not exist, check your part xml");
540                     continue;
541                 }
542                 if (updateAction != null) {
543                     widget.setProperty("action", updateAction);
544                     widget.addXuluxListener(new UpdateButtonsListener(this, widget));
545                 }
546                 list.add(widget);
547             }
548             menu = WidgetFactory.getPopupFromButtons(list, "Popup:" + getName());
549             if (menu == null) {
550               return false;
551             }
552             menu.setParent(this);
553             menu.setPart(getPart());
554             List children = menu.getChildWidgets();
555             if (children != null) {
556                 for (Iterator cit = children.iterator(); cit.hasNext();) {
557                     IWidget cw = (IWidget) cit.next();
558                     // we must clear the prepostfieldlistener...
559                     JMenuItem jcomp = (JMenuItem) cw.getNativeWidget();
560                     EventListener[] evs = jcomp.getListeners(ActionListener.class);
561                     if (evs != null && evs.length > 0) {
562                       jcomp.removeActionListener((ActionListener) evs[0]);
563                     }
564                     cw.addXuluxListener(new UpdateButtonsListener(this, cw));
565                 }
566             }
567             addChildWidget(menu);
568             table.addMouseListener(new PopupListener(this.menu, this));
569             if (this.lockedTable != null) {
570               this.lockedTable.addMouseListener(new PopupListener(this.menu, this));
571             }
572         }
573         // we also add UpdateButtonsListener here..
574         if (!childPopupsChecked) {
575             childPopupsChecked = true;
576             List list = getChildWidgets();
577             if (list != null && list.size() > 0) {
578                 for (Iterator it = getChildWidgets().iterator(); it.hasNext();) {
579                     IWidget cw = (IWidget) it.next();
580                     if (cw instanceof MenuItem) {
581                         hasChildPopups = true;
582                         break;
583                     }
584                 }
585             }
586         }
587         if (hasChildPopups && buttons != null) {
588             // just add a seperator by default
589             IWidget item = WidgetFactory.getWidget("menuitem", "Separator:" + getName());
590             item.setProperty("type", "separator");
591             item.setParent(menu);
592             menu.addChildWidget(item);
593         }
594         if (hasChildPopups) {
595             if (menu == null) {
596                 menu = WidgetFactory.getWidget("popupmenu", "PopupMenu:" + getName());
597             }
598             if (getChildWidgets() != null) {
599                 for (Iterator it = getChildWidgets().iterator(); it.hasNext();) {
600                     IWidget cw = (IWidget) it.next();
601                     if (cw instanceof MenuItem) {
602                         cw.addXuluxListener(new UpdateButtonsListener(this, cw));
603                         cw.setParent(menu);
604                         menu.addChildWidget(cw);
605                     }
606                 }
607             }
608         }
609         if (menu != null) {
610             if (menu.getParent() == null) {
611                 menu.setParent(this);
612             }
613             menu.initialize();
614         }
615         return true;
616     }
617 
618     /**
619      *
620      * @return the content of the table or null when no content is present
621      */
622     public Object getContent() {
623         return this.content;
624     }
625 
626     /**
627      * Set the content of the table
628      * @param object the content
629      */
630     public void setContent(Object object) {
631         contentChanged = true;
632         this.content = object;
633         if (object instanceof List) {
634             this.oldListSize = this.listSize;
635             listSize = ((List) content).size();
636         }
637         if (initialized) {
638             refresh();
639         }
640     }
641 
642     /**
643      * Remove this one..
644      * @return the native JTable
645      */
646     public JTable getJTable() {
647         return table;
648     }
649 
650     /**
651      * @return the native locked JTable.
652      */
653     public JTable getLockedJTable() {
654         return lockedTable;
655     }
656 
657     /**
658      * @see org.xulux.nyx.gui.XuluxWidget#addXuluxListener(org.xulux.nyx.gui.XuluxListener)
659      */
660     public void addXuluxListener(IXuluxListener listener) {
661     }
662 
663     /**
664      * Sets the current selected value in the table.
665      * @see org.xulux.nyx.gui.XuluxWidget#setValue(java.lang.Object)
666      */
667     public void setValue(Object value) {
668         if (getField() == null) {
669             this.previousValue = getGuiValue();
670             this.value = value;
671         } else {
672             IMapping map = XuluxContext.getDictionary().getDefaultProvider().getMapping(getPart().getBean());
673             IField field = map.getField(getField());
674             if (field != null) {
675                 Object currentValue = field.getValue(getPart().getBean());
676                 if (currentValue != null) {
677                     this.previousValue = currentValue;
678                 }
679                 field.setValue(getPart().getBean(), value);
680                 this.value = value;
681             }
682             if (this.value == null) {
683                 this.value = value;
684             }
685         }
686         initialize();
687         // @todo Add getting the index of the current value in the table,
688         // so we can select that one (mostly used on initial activation of
689         // table or after adding an entry to the table.
690         if (content instanceof List) {
691             int index = ((List) content).indexOf(value);
692             if (index != -1) {
693                 // select the row found
694                 table.setRowSelectionInterval(index, index);
695             }
696         }
697         getPart().refreshFields(this);
698         getPart().updateDependandWidgets(this);
699     }
700 
701     /**
702      * @see org.xulux.nyx.gui.XuluxWidget#getValue()
703      */
704     public Object getValue() {
705         return getGuiValue();
706     }
707 
708     /**
709      * Stop editing the table if currently editing.
710      */
711     public void stopEditing() {
712         editor.stopCellEditing(table);
713     }
714 
715     /**
716      * @see org.xulux.nyx.gui.IContentWidget#contentChanged()
717      */
718     public void contentChanged() {
719         contentChanged = true;
720     }
721 
722 }