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.Color;
046: import java.awt.Component;
047: import java.awt.FontMetrics;
048: import java.awt.GridBagConstraints;
049: import java.awt.Insets;
050: import java.awt.event.ActionEvent;
051: import java.awt.event.ActionListener;
052: import java.util.ArrayList;
053: import java.util.Iterator;
054: import java.util.List;
055: import java.beans.PropertyChangeEvent;
056: import java.beans.PropertyChangeListener;
057: import javax.swing.JPanel;
058: import javax.swing.JScrollBar;
059: import javax.swing.UIManager;
060: import org.openide.util.NbBundle;
061:
062: /**
063: * Panel which is used to create a filter for the tasklist
064: * The GUI is based on the one in Mozilla's mail tool.
065: * Please read comment at the beginning of initA11y before editing
066: * this file using the form builder.
067: *
068: * @author Tor Norbye
069: */
070: final class KeywordsPanel extends JPanel implements ActionListener,
071: PropertyChangeListener {
072:
073: private static final long serialVersionUID = 1;
074:
075: // private FilteredTopComponent view;
076: private KeywordsFilter filter;
077:
078: // head of subpanels chain
079: private FilterSubpanel subpanel;
080:
081: /**
082: * Creates new form FilterPanel.
083: *
084: * @param view view with the given filter
085: * @param filter filter to be edited. Can be null and in that case
086: * all fields are disabled.
087: */
088: public KeywordsPanel(KeywordsFilter filter) {
089: this .filter = filter;
090:
091: initComponents();
092: initA11y();
093:
094: if ("Metal".equals(UIManager.getLookAndFeel().getID())) //NOI18N
095: setOpaque(true);
096: else
097: setOpaque(false);
098:
099: // it's not generated by form editor
100: JPanel topAlign = new JPanel();
101: topAlign.setLayout(new BorderLayout());
102: topAlign.add(conditionsPanel, BorderLayout.NORTH);
103: conditionsScrollPane.setViewportView(topAlign);
104: // compute 80x10 chars space in scroll pane
105: FontMetrics fm = getFontMetrics(getFont());
106: int width = fm.charWidth('n') * 80; // NOI18N
107: int height = fm.getHeight() * 10;
108: conditionsScrollPane.setPreferredSize(new java.awt.Dimension(
109: width, height));
110:
111: Color background = (Color) UIManager.get("Table.background"); //NOI18N
112: conditionsPanel.setBackground(background);
113: topAlign.setBackground(background);
114:
115: moreButton.addActionListener(this );
116: fewerButton.addActionListener(this );
117: matchAllRadio.addActionListener(this );
118: matchAnyRadio.addActionListener(this );
119:
120: showFilter(filter);
121: updateSensitivity();
122: }
123:
124: /**
125: * Links to next filter customizer (or null).
126: */
127: public void initSubpanel(FilterSubpanel next) {
128: assert subpanel == null;
129: subpanel = next;
130: }
131:
132: /**
133: * Show the given filter in the GUI
134: */
135: private void showFilter(KeywordsFilter filter) {
136: if (filter != null) {
137: if (filter.matchAll()) {
138: matchAllRadio.setSelected(true);
139: } else {
140: matchAnyRadio.setSelected(true);
141: }
142: conditionsPanel.removeAll();
143: List conditions = filter.getConditions();
144: if (conditions.size() > 0) {
145: Iterator it = conditions.iterator();
146: while (it.hasNext()) {
147: AppliedFilterCondition cond = (AppliedFilterCondition) it
148: .next();
149: addCondition(false, cond);
150: }
151: } else {
152: addCondition(false, null);
153: }
154: } else {
155: this .setEnabled(false);
156: matchAnyRadio.setEnabled(false);
157: matchAllRadio.setEnabled(false);
158: conditionsPanel.setEnabled(false);
159: moreButton.setEnabled(false);
160: fewerButton.setEnabled(false);
161: }
162: }
163:
164: /**
165: * This method is called from within the constructor to
166: * initialize the form.
167: * WARNING: Do NOT modify this code. The content of this method is
168: * always regenerated by the Form Editor.
169: */
170: // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
171: private void initComponents() {
172:
173: matchGroup.add(matchAllRadio);
174: org.openide.awt.Mnemonics.setLocalizedText(matchAllRadio,
175: NbBundle.getMessage(KeywordsPanel.class, "MatchAll")); // NOI18N(); // NOI18N
176: matchAllRadio.setToolTipText(org.openide.util.NbBundle
177: .getMessage(KeywordsPanel.class, "HINT_MatchAll")); // NOI18N
178: matchAllRadio.setOpaque(false);
179:
180: matchGroup.add(matchAnyRadio);
181: matchAnyRadio.setSelected(true);
182: org.openide.awt.Mnemonics.setLocalizedText(matchAnyRadio,
183: NbBundle.getMessage(KeywordsPanel.class, "MatchAny")); // NOI18N(); // NOI18N
184: matchAnyRadio.setToolTipText(org.openide.util.NbBundle
185: .getMessage(KeywordsPanel.class, "HINT_MatchAny")); // NOI18N
186: matchAnyRadio.setOpaque(false);
187:
188: conditionsScrollPane
189: .setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
190: conditionsScrollPane.setAlignmentX(0.0F);
191: conditionsScrollPane.setAlignmentY(0.0F);
192: conditionsScrollPane.setPreferredSize(new java.awt.Dimension(
193: 32767, 32767));
194:
195: conditionsPanel.setLayout(new java.awt.GridBagLayout());
196: conditionsScrollPane.setViewportView(conditionsPanel);
197:
198: org.openide.awt.Mnemonics.setLocalizedText(fewerButton,
199: NbBundle.getMessage(KeywordsPanel.class, "Fewer")); // NOI18N(); // NOI18N
200: fewerButton.setToolTipText(org.openide.util.NbBundle
201: .getMessage(KeywordsPanel.class, "HINT_Fewer")); // NOI18N
202:
203: org.openide.awt.Mnemonics.setLocalizedText(moreButton, NbBundle
204: .getMessage(KeywordsPanel.class, "More")); // NOI18N(); // NOI18N
205: moreButton.setToolTipText(org.openide.util.NbBundle.getMessage(
206: KeywordsPanel.class, "HINT_More")); // NOI18N
207:
208: org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(
209: this );
210: this .setLayout(layout);
211: layout
212: .setHorizontalGroup(layout
213: .createParallelGroup(
214: org.jdesktop.layout.GroupLayout.LEADING)
215: .add(
216: layout
217: .createSequentialGroup()
218: .addContainerGap()
219: .add(
220: layout
221: .createParallelGroup(
222: org.jdesktop.layout.GroupLayout.LEADING)
223: .add(
224: layout
225: .createSequentialGroup()
226: .add(
227: moreButton)
228: .addPreferredGap(
229: org.jdesktop.layout.LayoutStyle.RELATED)
230: .add(
231: fewerButton))
232: .add(
233: layout
234: .createSequentialGroup()
235: .add(
236: matchAllRadio,
237: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
238: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
239: Short.MAX_VALUE)
240: .addPreferredGap(
241: org.jdesktop.layout.LayoutStyle.RELATED)
242: .add(
243: matchAnyRadio,
244: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
245: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
246: Short.MAX_VALUE)
247: .add(
248: 278,
249: 278,
250: 278))
251: .add(
252: org.jdesktop.layout.GroupLayout.TRAILING,
253: conditionsScrollPane,
254: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
255: 584,
256: Short.MAX_VALUE))
257: .addContainerGap()));
258: layout
259: .setVerticalGroup(layout
260: .createParallelGroup(
261: org.jdesktop.layout.GroupLayout.LEADING)
262: .add(
263: layout
264: .createSequentialGroup()
265: .addContainerGap()
266: .add(
267: layout
268: .createParallelGroup(
269: org.jdesktop.layout.GroupLayout.BASELINE)
270: .add(
271: matchAllRadio)
272: .add(
273: matchAnyRadio))
274: .addPreferredGap(
275: org.jdesktop.layout.LayoutStyle.RELATED)
276: .add(
277: conditionsScrollPane,
278: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
279: 321, Short.MAX_VALUE)
280: .addPreferredGap(
281: org.jdesktop.layout.LayoutStyle.RELATED)
282: .add(
283: layout
284: .createParallelGroup(
285: org.jdesktop.layout.GroupLayout.TRAILING)
286: .add(
287: fewerButton)
288: .add(moreButton))
289: .addContainerGap()));
290:
291: conditionsScrollPane.getAccessibleContext().setAccessibleName(
292: null);
293: conditionsScrollPane.getAccessibleContext()
294: .setAccessibleDescription(null);
295: }// </editor-fold>//GEN-END:initComponents
296:
297: private void initA11y() {
298: // accessible
299: this .getAccessibleContext()
300: .setAccessibleDescription(
301: NbBundle.getMessage(KeywordsPanel.class,
302: "ACSD_Filter")); // NOI18N
303: conditionsPanel.getAccessibleContext().setAccessibleName(
304: NbBundle.getMessage(KeywordsPanel.class,
305: "ACSN_Conditions")); // NOI18N
306: conditionsPanel.getAccessibleContext()
307: .setAccessibleDescription(
308: NbBundle.getMessage(KeywordsPanel.class,
309: "ACSD_Conditions")); // NOI18N
310: }
311:
312: /**
313: * Adds a condition to the filter.
314: *
315: * @param revalidate true = invalidate() will be called
316: * @param condition a condition or null
317: */
318: private ConditionPanel addCondition(boolean revalidate,
319: AppliedFilterCondition condition) {
320: GridBagConstraints gridBagConstraints = new GridBagConstraints();
321: gridBagConstraints.gridwidth = GridBagConstraints.REMAINDER;
322: gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
323: gridBagConstraints.anchor = GridBagConstraints.NORTH;
324: gridBagConstraints.insets = new Insets(6, 0, 0, 0);
325: gridBagConstraints.weightx = 1.0;
326: ConditionPanel panel = new ConditionPanel(filter, condition);
327: panel.addPropertyChangeListener(
328: FilterCondition.PROP_VALUE_VALID, this );
329: panel.setBackground((Color) UIManager.get("Table.background")); //NOI18N
330: conditionsPanel.add(panel, gridBagConstraints);
331: if (revalidate) {
332: invalidate();
333: getParent().validate();
334: repaint();
335: }
336: putClientProperty(FilterCondition.PROP_VALUE_VALID, Boolean
337: .valueOf(isValueValid()));
338: return panel;
339: }
340:
341: /** @return aggregated PROP_VALUE_VALID value */
342: public final boolean isValueValid() {
343: Component[] cps = conditionsPanel.getComponents();
344: for (int i = 0; i < cps.length; i++) {
345: ConditionPanel condition = (ConditionPanel) cps[i];
346: if (condition != null) {
347: if (condition.isValueValid() == false) {
348: return false;
349: }
350: }
351: }
352: return true;
353: }
354:
355: // forward aggregated PROP_VALUE_VALID value
356: public void propertyChange(PropertyChangeEvent evt) {
357: putClientProperty(FilterCondition.PROP_VALUE_VALID, Boolean
358: .valueOf(isValueValid()));
359: }
360:
361: /**
362: * Invoked when an action occurs.
363: */
364: public void actionPerformed(ActionEvent evt) {
365: Object source = evt.getSource();
366: if (source == moreButton) {
367: ConditionPanel comp = addCondition(true, null);
368: JScrollBar vsb = conditionsScrollPane
369: .getVerticalScrollBar();
370: vsb.setValue(vsb.getMaximum());
371: comp.focusPropertyCombo();
372: } else if (source == fewerButton) {
373: conditionsPanel
374: .remove(conditionsPanel.getComponentCount() - 1);
375: invalidate();
376: getParent().validate();
377: repaint();
378: }
379: updateSensitivity();
380: putClientProperty(FilterCondition.PROP_VALUE_VALID, Boolean
381: .valueOf(isValueValid()));
382: }
383:
384: /**
385: * Updates enabled/disabled state of the "fewer" button
386: */
387: private void updateSensitivity() {
388: int n = conditionsPanel.getComponentCount();
389: fewerButton.setEnabled(n > 0);
390: }
391:
392: /**
393: * Return a filter corresponding to what is in the GUI
394: *
395: * @return filter or null
396: */
397: public KeywordsFilter getFilter() {
398: if (filter != null) {
399: // Get conditions
400: Component[] cps = conditionsPanel.getComponents();
401: ArrayList<AppliedFilterCondition> conditions = new ArrayList<AppliedFilterCondition>(
402: cps.length);
403: for (int i = 0; i < cps.length; i++) {
404: AppliedFilterCondition condition = ((ConditionPanel) cps[i])
405: .getCondition();
406: if (condition != null) {
407: conditions.add(condition);
408: }
409: }
410: filter.setConditions(conditions);
411: filter.setMatchAll(matchAllRadio.isSelected());
412: if (subpanel != null) {
413: subpanel.updateFilter(filter);
414: }
415: }
416: return filter;
417:
418: }
419:
420: // Variables declaration - do not modify//GEN-BEGIN:variables
421: final javax.swing.JPanel conditionsPanel = new javax.swing.JPanel();
422: final javax.swing.JScrollPane conditionsScrollPane = new javax.swing.JScrollPane();
423: final javax.swing.JButton fewerButton = new javax.swing.JButton();
424: final javax.swing.JRadioButton matchAllRadio = new javax.swing.JRadioButton();
425: final javax.swing.JRadioButton matchAnyRadio = new javax.swing.JRadioButton();
426: final javax.swing.ButtonGroup matchGroup = new javax.swing.ButtonGroup();
427: final javax.swing.JButton moreButton = new javax.swing.JButton();
428: final javax.swing.ButtonGroup subtaskGroup = new javax.swing.ButtonGroup();
429: // End of variables declaration//GEN-END:variables
430:
431: }
|