001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.tasklist.filter;
043:
044: import java.awt.BorderLayout;
045: import java.awt.Dialog;
046: import java.awt.event.ActionEvent;
047: import java.awt.event.ActionListener;
048: import java.beans.PropertyChangeEvent;
049: import java.beans.PropertyChangeListener;
050: import java.util.ArrayList;
051: import java.util.HashMap;
052: import java.util.Iterator;
053: import javax.swing.AbstractListModel;
054: import javax.swing.DefaultListSelectionModel;
055: import javax.swing.JButton;
056: import javax.swing.JPanel;
057: import javax.swing.ListSelectionModel;
058: import javax.swing.event.DocumentEvent;
059: import javax.swing.event.DocumentListener;
060: import javax.swing.event.ListSelectionEvent;
061: import javax.swing.event.ListSelectionListener;
062: import javax.swing.text.BadLocationException;
063: import org.openide.DialogDescriptor;
064: import org.openide.DialogDisplayer;
065: import org.openide.util.HelpCtx;
066: import org.openide.util.NbBundle;
067:
068: /**
069: *
070: * @author sa154850
071: */
072: public class FilterEditor extends JPanel implements
073: PropertyChangeListener {
074:
075: private HashMap<KeywordsFilter, KeywordsPanel> filter2keywords = new HashMap<KeywordsFilter, KeywordsPanel>(
076: 10);
077: private HashMap<TypesFilter, TypesPanel> filter2types = new HashMap<TypesFilter, TypesPanel>(
078: 10);
079:
080: /** Reference to orginal filterRepository this dialog act upon.
081: * It is not changed until ok or apply is pressed */
082: private FilterRepository filterRepository;
083: /**
084: * Contains temporary data (cloned filters) for the list and also selection
085: * model for the list.
086: */
087: private FilterModel filterModel;
088:
089: private JButton btnOk;
090: private JButton btnCancel;
091:
092: /** Creates new form FilterEditor */
093: public FilterEditor(FilterRepository filters) {
094: initComponents();
095: this .filterRepository = filters;
096: if (filterRepository.size() == 0)
097: filterRepository.add(filterRepository.createNewFilter());
098: this .filterModel = new FilterModel(filterRepository);
099:
100: init();
101: }
102:
103: public boolean showWindow() {
104: DialogDescriptor dd = new DialogDescriptor(
105: this ,
106: NbBundle.getMessage(FilterEditor.class,
107: "LBL_FilterEditor"),
108: true, //NOI18N
109: new Object[] { btnOk, btnCancel }, btnOk,
110: DialogDescriptor.DEFAULT_ALIGN, HelpCtx.DEFAULT_HELP,
111: null);
112:
113: Dialog dlg = DialogDisplayer.getDefault().createDialog(dd);
114:
115: dlg.setVisible(true);
116: if (btnOk.equals(dd.getValue())) {
117: updateFilters();
118: return true;
119: }
120: return false;
121: }
122:
123: private void init() {
124: initComponents();
125:
126: // init filters-listbox model
127: lstFilters.setModel(filterModel);
128: lstFilters.setSelectionModel(filterModel.selection);
129: lstFilters
130: .setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
131:
132: // try to select just applied filter
133:
134: TaskFilter selected = filterRepository.getActive();
135: if (null != selected) {
136: int selIndex = filterModel.getIndexOf(selected);
137: lstFilters.setSelectedIndex(selIndex);
138: }
139:
140: if (filterModel.getSelectedIndex() == -1) {
141: if (filterModel.getSize() > 0) {
142: lstFilters.setSelectedIndex(0);
143: }
144: }
145:
146: txtFilterName.getDocument().addDocumentListener(
147: new DocumentListener() {
148: public void changedUpdate(DocumentEvent e) {
149: update(e);
150: }
151:
152: public void insertUpdate(DocumentEvent e) {
153: update(e);
154: }
155:
156: public void removeUpdate(DocumentEvent e) {
157: update(e);
158: }
159:
160: private void update(DocumentEvent e) {
161: try {
162: filterModel
163: .setCurrentFilterName(e
164: .getDocument()
165: .getText(
166: 0,
167: e
168: .getDocument()
169: .getLength()));
170: propertyChange(null);
171: } catch (BadLocationException ex) {
172: //ignore
173: }
174: }
175: });
176:
177: // hook list selection
178: lstFilters
179: .addListSelectionListener(new ListSelectionListener() {
180: public void valueChanged(ListSelectionEvent e) {
181: if (!e.getValueIsAdjusting()) {
182: showFilter(filterModel.getSelectedFilter());
183: btnRemoveFilter.setEnabled(filterModel
184: .getSelectedIndex() != -1);
185: txtFilterName.setEnabled(filterModel
186: .getSelectedIndex() != -1);
187: }
188: }
189: });
190:
191: showFilter(filterModel.getSelectedFilter());
192: btnRemoveFilter
193: .setEnabled(filterModel.getSelectedIndex() != -1);
194:
195: btnOk = new JButton("Ok");
196: btnCancel = new JButton("Cancel");
197: }
198:
199: /**
200: * Initializes the editor to the state when <filter> is selected in the list
201: * and it is shown on the right side. It can be used to propagate values in
202: * both directions - from list to pane and opposite and also to both at once.
203: */
204: private void showFilter(final TaskFilter filter) {
205: if (null == filter) {
206: txtFilterName.setText(null);
207: panelKeywords.removeAll();
208: panelTypes.removeAll();
209: panelKeywords.add(new KeywordsPanel(null),
210: BorderLayout.CENTER);
211: panelTypes.add(new TypesPanel(null), BorderLayout.CENTER);
212: } else {
213: KeywordsPanel kPanel = filter2keywords.get(filter
214: .getKeywordsFilter());
215: if (kPanel == null) {
216: kPanel = new KeywordsPanel(
217: filter.getKeywordsFilter() == null ? new KeywordsFilter()
218: : filter.getKeywordsFilter());
219: filter2keywords.put(filter.getKeywordsFilter(), kPanel);
220: kPanel.addPropertyChangeListener(
221: FilterCondition.PROP_VALUE_VALID, this );
222: }
223:
224: panelKeywords.removeAll();
225: panelKeywords.add(kPanel, BorderLayout.CENTER);
226: kPanel.setVisible(true);
227:
228: TypesPanel tPanel = filter2types.get(filter
229: .getTypesFilter());
230: if (tPanel == null) {
231: tPanel = new TypesPanel(
232: filter.getTypesFilter() == null ? new TypesFilter()
233: : filter.getTypesFilter());
234: filter2types.put(filter.getTypesFilter(), tPanel);
235: tPanel.addPropertyChangeListener(
236: FilterCondition.PROP_VALUE_VALID, this );
237: }
238:
239: panelTypes.removeAll();
240: panelTypes.add(tPanel, BorderLayout.CENTER);
241: tPanel.setVisible(true);
242:
243: tabs.requestFocus();
244:
245: // select the active filter
246: if (filterModel.getSelectedFilter() != filter) { // check to prevent cycle in notifications
247: lstFilters.setSelectedIndex(filterModel
248: .getIndexOf(filter));
249: }
250:
251: txtFilterName.setText(filter.getName());
252: }
253:
254: panelKeywords.validate();
255: panelKeywords.repaint();
256: panelTypes.validate();
257: panelTypes.repaint();
258: }
259:
260: /**
261: * Lift of isValueValid to FiltersPanel
262: */
263: public boolean isValueValid() {
264: for (KeywordsPanel fp : filter2keywords.values()) {
265: if (!fp.isValueValid())
266: return false;
267: }
268: for (TypesPanel tp : filter2types.values()) {
269: if (!tp.isValueValid())
270: return false;
271: }
272: if (txtFilterName.getText().length() == 0)
273: return false;
274: return true;
275: }
276:
277: public void propertyChange(PropertyChangeEvent arg0) {
278: if (null != btnOk)
279: btnOk.setEnabled(isValueValid());
280: }
281:
282: /**
283: * Reads data from the form into the filter repository
284: * that was passed-in in the constructor (returned by {@link #getFilterRepository})
285: */
286: void updateFilters() {
287: filterRepository.clear(); // throw away all original filters
288:
289: Iterator filterIt = filterModel.iterator();
290: while (filterIt.hasNext()) {
291: TaskFilter f = (TaskFilter) filterIt.next();
292: if (filter2keywords.get(f.getKeywordsFilter()) != null)
293: f.setKeywordsFilter(filter2keywords.get(
294: f.getKeywordsFilter()).getFilter()); // has panel, was touched
295:
296: if (filter2types.get(f.getTypesFilter()) != null)
297: f.setTypesFilter(filter2types.get(f.getTypesFilter())
298: .getFilter()); // has panel, was touched
299:
300: filterRepository.add(f);
301: }
302: if (filterModel.getSelectedFilter() != null) {
303: filterRepository.setActive(filterModel.getSelectedFilter());
304: }
305: }
306:
307: /** This method is called from within the constructor to
308: * initialize the form.
309: * WARNING: Do NOT modify this code. The content of this method is
310: * always regenerated by the Form Editor.
311: */
312: // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
313: private void initComponents() {
314:
315: lblFilters = new javax.swing.JLabel();
316: scrollFilters = new javax.swing.JScrollPane();
317: lstFilters = new javax.swing.JList();
318: btnNewFilter = new javax.swing.JButton();
319: btnRemoveFilter = new javax.swing.JButton();
320: tabs = new javax.swing.JTabbedPane();
321: panelTypes = new javax.swing.JPanel();
322: panelKeywords = new javax.swing.JPanel();
323: lblFilterName = new javax.swing.JLabel();
324: txtFilterName = new javax.swing.JTextField();
325:
326: lblFilters.setLabelFor(lstFilters);
327: org.openide.awt.Mnemonics.setLocalizedText(lblFilters,
328: org.openide.util.NbBundle.getMessage(
329: FilterEditor.class,
330: "FilterEditor.lblFilters.text")); // NOI18N
331:
332: lstFilters.setModel(new javax.swing.AbstractListModel() {
333: String[] strings = { "Item 1", "Item 2", "Item 3",
334: "Item 4", "Item 5" };
335:
336: public int getSize() {
337: return strings.length;
338: }
339:
340: public Object getElementAt(int i) {
341: return strings[i];
342: }
343: });
344: lstFilters.setToolTipText(org.openide.util.NbBundle.getMessage(
345: FilterEditor.class,
346: "FilterEditor.lstFilters.toolTipText")); // NOI18N
347: scrollFilters.setViewportView(lstFilters);
348:
349: org.openide.awt.Mnemonics.setLocalizedText(btnNewFilter,
350: org.openide.util.NbBundle.getMessage(
351: FilterEditor.class,
352: "FilterEditor.btnNewFilter.text")); // NOI18N
353: btnNewFilter.setToolTipText(org.openide.util.NbBundle
354: .getMessage(FilterEditor.class,
355: "FilterEditor.btnNewFilter.toolTipText")); // NOI18N
356: btnNewFilter
357: .addActionListener(new java.awt.event.ActionListener() {
358: public void actionPerformed(
359: java.awt.event.ActionEvent evt) {
360: onNewFilter(evt);
361: }
362: });
363:
364: org.openide.awt.Mnemonics.setLocalizedText(btnRemoveFilter,
365: org.openide.util.NbBundle.getMessage(
366: FilterEditor.class,
367: "FilterEditor.btnRemoveFilter.text")); // NOI18N
368: btnRemoveFilter.setToolTipText(org.openide.util.NbBundle
369: .getMessage(FilterEditor.class,
370: "FilterEditor.btnRemoveFilter.toolTipText")); // NOI18N
371: btnRemoveFilter
372: .addActionListener(new java.awt.event.ActionListener() {
373: public void actionPerformed(
374: java.awt.event.ActionEvent evt) {
375: onRemoveFilter(evt);
376: }
377: });
378:
379: panelTypes.setOpaque(false);
380: panelTypes.setLayout(new java.awt.BorderLayout());
381: tabs.addTab(org.openide.util.NbBundle.getMessage(
382: FilterEditor.class,
383: "FilterEditor.panelTypes.TabConstraints.tabTitle"),
384: panelTypes); // NOI18N
385:
386: panelKeywords.setOpaque(false);
387: panelKeywords.setLayout(new java.awt.BorderLayout());
388: tabs.addTab(org.openide.util.NbBundle.getMessage(
389: FilterEditor.class,
390: "FilterEditor.panelKeywords.TabConstraints.tabTitle"),
391: panelKeywords); // NOI18N
392:
393: lblFilterName.setLabelFor(txtFilterName);
394: org.openide.awt.Mnemonics.setLocalizedText(lblFilterName,
395: org.openide.util.NbBundle.getMessage(
396: FilterEditor.class,
397: "FilterEditor.lblFilterName.text")); // NOI18N
398:
399: txtFilterName.setToolTipText(org.openide.util.NbBundle
400: .getMessage(FilterEditor.class,
401: "FilterEditor.txtFilterName.toolTipText")); // NOI18N
402:
403: org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(
404: this );
405: this .setLayout(layout);
406: layout
407: .setHorizontalGroup(layout
408: .createParallelGroup(
409: org.jdesktop.layout.GroupLayout.LEADING)
410: .add(
411: layout
412: .createSequentialGroup()
413: .addContainerGap()
414: .add(
415: layout
416: .createParallelGroup(
417: org.jdesktop.layout.GroupLayout.LEADING,
418: false)
419: .add(lblFilters)
420: .add(
421: layout
422: .createSequentialGroup()
423: .add(
424: btnNewFilter)
425: .addPreferredGap(
426: org.jdesktop.layout.LayoutStyle.RELATED)
427: .add(
428: btnRemoveFilter))
429: .add(
430: scrollFilters))
431: .addPreferredGap(
432: org.jdesktop.layout.LayoutStyle.RELATED)
433: .add(
434: layout
435: .createParallelGroup(
436: org.jdesktop.layout.GroupLayout.LEADING)
437: .add(
438: layout
439: .createSequentialGroup()
440: .add(
441: lblFilterName)
442: .addPreferredGap(
443: org.jdesktop.layout.LayoutStyle.RELATED)
444: .add(
445: txtFilterName,
446: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
447: 459,
448: Short.MAX_VALUE))
449: .add(
450: tabs,
451: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
452: 494,
453: Short.MAX_VALUE))
454: .addContainerGap()));
455: layout
456: .setVerticalGroup(layout
457: .createParallelGroup(
458: org.jdesktop.layout.GroupLayout.LEADING)
459: .add(
460: org.jdesktop.layout.GroupLayout.TRAILING,
461: layout
462: .createSequentialGroup()
463: .addContainerGap()
464: .add(lblFilters)
465: .addPreferredGap(
466: org.jdesktop.layout.LayoutStyle.RELATED)
467: .add(
468: layout
469: .createParallelGroup(
470: org.jdesktop.layout.GroupLayout.LEADING)
471: .add(
472: layout
473: .createSequentialGroup()
474: .add(
475: scrollFilters,
476: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
477: 229,
478: Short.MAX_VALUE)
479: .addPreferredGap(
480: org.jdesktop.layout.LayoutStyle.RELATED)
481: .add(
482: layout
483: .createParallelGroup(
484: org.jdesktop.layout.GroupLayout.BASELINE)
485: .add(
486: btnNewFilter)
487: .add(
488: btnRemoveFilter)))
489: .add(
490: layout
491: .createSequentialGroup()
492: .add(
493: layout
494: .createParallelGroup(
495: org.jdesktop.layout.GroupLayout.BASELINE)
496: .add(
497: lblFilterName)
498: .add(
499: txtFilterName,
500: org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
501: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
502: org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
503: .addPreferredGap(
504: org.jdesktop.layout.LayoutStyle.RELATED)
505: .add(
506: tabs,
507: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
508: 233,
509: Short.MAX_VALUE)))
510: .addContainerGap()));
511: }// </editor-fold>//GEN-END:initComponents
512:
513: private void onRemoveFilter(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onRemoveFilter
514: int i = filterModel.getSelectedIndex();
515: if (i != -1) {
516: TaskFilter f = filterModel.get(i);
517: filterModel.remove(i);
518: filter2keywords.remove(f.getKeywordsFilter());
519: filter2types.remove(f.getTypesFilter());
520: }
521:
522: }//GEN-LAST:event_onRemoveFilter
523:
524: private void onNewFilter(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onNewFilter
525: TaskFilter f = filterRepository.createNewFilter();
526: filterModel.add(f);
527: showFilter(f);
528:
529: }//GEN-LAST:event_onNewFilter
530:
531: // Variables declaration - do not modify//GEN-BEGIN:variables
532: private javax.swing.JButton btnNewFilter;
533: private javax.swing.JButton btnRemoveFilter;
534: private javax.swing.JLabel lblFilterName;
535: private javax.swing.JLabel lblFilters;
536: private javax.swing.JList lstFilters;
537: private javax.swing.JPanel panelKeywords;
538: private javax.swing.JPanel panelTypes;
539: private javax.swing.JScrollPane scrollFilters;
540: private javax.swing.JTabbedPane tabs;
541: private javax.swing.JTextField txtFilterName;
542:
543: // End of variables declaration//GEN-END:variables
544:
545: private static class FilterModel extends AbstractListModel {
546:
547: public DefaultListSelectionModel selection = new DefaultListSelectionModel();
548: public ArrayList<TaskFilter> filters;
549:
550: public FilterModel(FilterRepository rep) {
551: filters = new ArrayList<TaskFilter>(rep.size() * 2);
552: int selectedi = 0;
553: for (TaskFilter f : rep.getFilters()) {
554: if (f == rep.getActive())
555: selection
556: .setSelectionInterval(selectedi, selectedi);
557: filters.add((TaskFilter) f.clone());
558: selectedi++;
559: }
560: }
561:
562: public Iterator iterator() {
563: return filters.iterator();
564: }
565:
566: public Object getElementAt(int index) {
567: return filters.get(index).getName();
568: }
569:
570: public int getSize() {
571: return filters.size();
572: }
573:
574: public TaskFilter getSelectedFilter() {
575: if (getSelectedIndex() > -1) {
576: return filters.get(getSelectedIndex());
577: } else
578: return null;
579: }
580:
581: public int getSelectedIndex() {
582: int i1 = selection.getMinSelectionIndex(), i2 = selection
583: .getMaxSelectionIndex();
584: if (i1 == i2 && i1 >= 0 && i1 < filters.size()) {
585: return i1;
586: } else {
587: return -1;
588: }
589: }
590:
591: public void remove(int i) {
592: int s = getSelectedIndex();
593: if (s != -1) {
594: filters.remove(i);
595: fireIntervalRemoved(this , i, i);
596:
597: if (i < s) {
598: selection.setSelectionInterval(s - 1, s - 1);
599: }
600: if (i == s) {
601: selection.setSelectionInterval(100, 0);
602: }
603: }
604: }
605:
606: public TaskFilter get(int i) {
607: return (TaskFilter) filters.get(i);
608: }
609:
610: public boolean add(TaskFilter f) {
611: if (filters.add(f)) {
612: fireIntervalAdded(this , filters.size() - 1, filters
613: .size() - 1);
614: return true;
615: } else
616: return false;
617: }
618:
619: public int getIndexOf(TaskFilter f) {
620: return filters.indexOf(f);
621: }
622:
623: public void setCurrentFilterName(String name) {
624: int selIndex = getSelectedIndex();
625: if (selIndex >= 0) {
626: get(selIndex).setName(name);
627: fireContentsChanged(this, selIndex, selIndex);
628: }
629: }
630: }
631: }
|