3 TrakEM2 plugin for ImageJ(C).
4 Copyright (C) 2005-2009 Albert Cardona and Rodney Douglas.
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation (http://www.gnu.org/licenses/gpl.txt )
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 You may contact Albert Cardona at acardona at ini.phys.ethz.ch
20 Institute of Neuroinformatics, University of Zurich / ETH, Switzerland.
23 package ini
.trakem2
.utils
;
25 import ini
.trakem2
.Project
;
26 import ini
.trakem2
.persistence
.DBObject
;
27 import ini
.trakem2
.display
.*;
28 import ini
.trakem2
.ControlWindow
;
30 import javax
.swing
.table
.*;
31 import java
.awt
.event
.*;
32 import java
.awt
.Dimension
;
33 import java
.util
.ArrayList
;
34 import java
.util
.Hashtable
;
35 import java
.util
.Iterator
;
36 import java
.util
.Vector
;
37 import java
.util
.regex
.*;
40 private JFrame search_frame
= null;
41 private JTabbedPane search_tabs
= null;
42 private JTextField search_field
= null;
43 private JComboBox pulldown
= null;
44 private KeyListener kl
= null;
46 static private Search instance
= null;
48 private Class
[] types
= null;
51 /** Creates the GUI for searching text in any TrakEM2 element. */
53 if (null != instance
) {
57 types
= new Class
[]{DBObject
.class, Displayable
.class, DLabel
.class, Patch
.class, AreaList
.class, Profile
.class, Pipe
.class, Ball
.class, Layer
.class, Dissector
.class};
62 private void tryCloseTab(KeyEvent ke
) {
63 switch (ke
.getKeyCode()) {
65 if (!ke
.isControlDown()) return;
66 int ntabs
= search_tabs
.getTabCount();
71 search_tabs
.remove(search_tabs
.getSelectedIndex());
78 private void makeGUI() {
79 // create GUI if not there
80 if (null == search_frame
) {
81 search_frame
= ControlWindow
.createJFrame("Search");
82 search_frame
.addWindowListener(new WindowAdapter() {
83 public void windowClosing(WindowEvent we
) {
87 search_tabs
= new JTabbedPane(JTabbedPane
.TOP
);
88 kl
= new KeyAdapter() {
89 public void keyPressed(KeyEvent ke
) {
93 search_tabs
.addKeyListener(kl
);
94 search_field
= new JTextField(14);
95 search_field
.addKeyListener(new VKEnterListener());
96 JButton b
= new JButton("Search");
97 b
.addActionListener(new ButtonListener());
98 pulldown
= new JComboBox(new String
[]{"All", "All displayables", "Labels", "Images", "Area Lists", "Profiles", "Pipes", "Balls", "Layers", "Dissectors"});
99 JPanel top
= new JPanel();
100 top
.add(search_field
);
103 top
.setMinimumSize(new Dimension(400, 30));
104 top
.setMaximumSize(new Dimension(10000, 30));
105 top
.setPreferredSize(new Dimension(400, 30));
106 JPanel all
= new JPanel();
107 all
.setPreferredSize(new Dimension(400, 400));
108 BoxLayout bl
= new BoxLayout(all
, BoxLayout
.Y_AXIS
);
111 all
.add(search_tabs
);
112 search_frame
.getContentPane().add(all
);
114 search_frame
.setVisible(true);
116 search_frame
.toFront();
120 synchronized private void destroy() {
121 if (null != instance
) {
122 if (null != search_frame
) search_frame
.dispose();
134 private class DisplayableTableModel
extends AbstractTableModel
{
135 private Vector v_obs
;
136 private Vector v_txt
;
137 DisplayableTableModel(Vector v_obs
, Vector v_txt
) {
142 public String
getColumnName(int col
) {
143 if (0 == col
) return "Type";
144 else if (1 == col
) return "Info";
145 else if (2 == col
) return "Matched";
148 public int getRowCount() { return v_obs
.size(); }
149 public int getColumnCount() { return 3; }
150 public Object
getValueAt(int row
, int col
) {
151 if (0 == col
) return Project
.getName(v_obs
.get(row
).getClass());
152 else if (1 == col
) return ((DBObject
)v_obs
.get(row
)).getShortTitle();
153 else if (2 == col
) return v_txt
.get(row
).toString();
156 public DBObject
getDBObjectAt(int row
) {
157 return (DBObject
)v_obs
.get(row
);
159 public Displayable
getDisplayableAt(int row
) {
160 return (Displayable
)v_obs
.get(row
);
162 public boolean isCellEditable(int row
, int col
) {
165 public void setValueAt(Object value
, int row
, int col
) {
167 //fireTableCellUpdated(row, col);
169 public boolean remove(Displayable displ
) {
170 int i
= v_obs
.indexOf(displ
);
178 public boolean contains(Object ob
) {
179 return v_obs
.contains(ob
);
183 private void executeSearch() {
184 String pattern
= search_field
.getText();
185 if (null == pattern
|| 0 == pattern
.length()) {
189 final String typed_pattern
= pattern
;
190 final StringBuffer sb
= new StringBuffer(); // I hate java
191 if (!pattern
.startsWith("^")) sb
.append("^.*");
192 for (int i
=0; i
<pattern
.length(); i
++) {
193 sb
.append(pattern
.charAt(i
));
195 if (!pattern
.endsWith("$")) sb
.append(".*$");
196 pattern
= sb
.toString();
197 final Pattern pat
= Pattern
.compile(pattern
, Pattern
.CASE_INSENSITIVE
| Pattern
.MULTILINE
| Pattern
.DOTALL
);
198 //Utils.log2("pattern after: " + pattern);
199 final ArrayList al
= new ArrayList();
200 //Utils.log("types[pulldown] = " + types[pulldown.getSelectedIndex()]);
201 find(ControlWindow
.getActive().getRootLayerSet(), al
, types
[pulldown
.getSelectedIndex()]);
202 //Utils.log2("found labels: " + al.size());
203 if (0 == al
.size()) return;
204 final Vector v_obs
= new Vector();
205 final Vector v_txt
= new Vector();
206 for (Iterator it
= al
.iterator(); it
.hasNext(); ) {
207 final DBObject dbo
= (DBObject
)it
.next();
208 boolean matched
= false;
209 // Search in its title
210 String txt
= dbo
instanceof Displayable ?
211 dbo
.getProject().getMeaningfulTitle((Displayable
)dbo
)
213 matched
= pat
.matcher(txt
).matches();
214 long id
= dbo
.getId();
215 if (87 == id
) Utils
.log2("Searching title: " + txt
+ (matched ?
"********** TRUE" : ""));
217 // Search also in its toString()
218 txt
= dbo
.toString();
219 matched
= pat
.matcher(txt
).matches();
220 if (87 == id
) Utils
.log2("Searching toString: " + txt
+ (matched ?
"********** TRUE" : ""));
223 // Search also in its id
224 txt
= Long
.toString(dbo
.getId());
225 matched
= pat
.matcher(txt
).matches();
226 if (matched
) txt
= "id: #" + txt
;
227 if (87 == id
) Utils
.log2("Searching ID: " + txt
+ (matched ?
"********** TRUE" : ""));
229 if (!matched
) continue;
231 //txt = txt.length() > 30 ? txt.substring(0, 27) + "..." : txt;
236 if (0 == v_obs
.size()) {
237 Utils
.showMessage("Nothing found.");
240 final JScrollPane jsp
= makeTable(new DisplayableTableModel(v_obs
, v_txt
));
241 search_tabs
.addTab(typed_pattern
, jsp
);
242 search_tabs
.setSelectedComponent(jsp
);
246 private JScrollPane
makeTable(TableModel model
) {
247 JTable table
= new JTable(model
);
248 //java 1.6.0 only!! //table.setAutoCreateRowSorter(true);
249 table
.addMouseListener(new DisplayableListListener());
250 table
.addKeyListener(kl
);
251 JScrollPane jsp
= new JScrollPane(table
);
252 jsp
.setPreferredSize(new Dimension(500, 500));
256 /** Listen to the search button. */
257 private class ButtonListener
implements ActionListener
{
258 public void actionPerformed(ActionEvent ae
) {
262 /** Listen to the search field. */
263 private class VKEnterListener
extends KeyAdapter
{
264 public void keyPressed(KeyEvent ke
) {
265 if (ke
.getKeyCode() == KeyEvent
.VK_ENTER
) {
273 /** Listen to double clicks in the table rows. */
274 private class DisplayableListListener
extends MouseAdapter
{
275 public void mousePressed(MouseEvent me
) {
276 final JTable table
= (JTable
)me
.getSource();
277 final DBObject ob
= ((DisplayableTableModel
)table
.getModel()).getDBObjectAt(table
.rowAtPoint(me
.getPoint()));
278 if (2 == me
.getClickCount()) {
279 // is a table//Utils.log2("LLL source is " + me.getSource());
280 // JTable is AN ABSOLUTE PAIN to work with
281 // ???? THERE IS NO OBVIOUS WAY to retrieve the data. How lame.
282 // Ah, a "model" ... since when a model holds the DATA (!!), same with JTree, how lame.
283 if (ob
instanceof Displayable
) {
284 Displayable displ
= (Displayable
)ob
;
285 Display
.showCentered(displ
.getLayer(), displ
, true, me
.isShiftDown());
286 } else if (ob
instanceof Layer
) {
287 Display
.showFront((Layer
)ob
);
289 Utils
.log2("Search: Unhandable table selection: " + ob
);
291 } else if (Utils
.isPopupTrigger(me
)) {
292 JPopupMenu popup
= new JPopupMenu();
293 final String show2D
= "Show";
294 final String select
= "Select in display";
295 ActionListener listener
= new ActionListener() {
296 public void actionPerformed(ActionEvent ae
) {
297 final String command
= ae
.getActionCommand();
298 if (command
.equals(show2D
)) {
299 if (ob
instanceof Displayable
) {
300 Displayable displ
= (Displayable
)ob
;
301 Display
.showCentered(displ
.getLayer(), displ
, true, 0 != (ae
.getModifiers() & ActionEvent
.SHIFT_MASK
));
302 } else if (ob
instanceof Layer
) {
303 Display
.showFront((Layer
)ob
);
305 } else if (command
.equals(select
)) {
306 if (ob
instanceof Layer
) {
307 Display
.showFront((Layer
)ob
);
308 } else if (ob
instanceof Displayable
) {
309 Displayable displ
= (Displayable
)ob
;
310 if (!displ
.isVisible()) displ
.setVisible(true);
311 Display display
= Display
.getFront(displ
.getProject());
312 if (null == display
) return;
313 boolean shift_down
= 0 != (ae
.getModifiers() & ActionEvent
.SHIFT_MASK
);
314 display
.select(displ
, shift_down
);
319 JMenuItem item
= new JMenuItem(show2D
); popup
.add(item
); item
.addActionListener(listener
);
320 item
= new JMenuItem(select
); popup
.add(item
); item
.addActionListener(listener
);
321 popup
.show(table
, me
.getX(), me
.getY());
326 /** Recursive search into nested LayerSet instances, accumulating instances of type into the list al. */
327 private void find(final LayerSet set
, final ArrayList al
, final Class type
) {
328 if (type
== DBObject
.class) {
331 for (ZDisplayable zd
: set
.getZDisplayables()) {
332 if (DBObject
.class == type
|| Displayable
.class == type
) {
334 } else if (zd
.getClass() == type
) {
338 for (Layer layer
: set
.getLayers()) {
339 if (DBObject
.class == type
|| Layer
.class == type
) {
342 for (Displayable ob
: layer
.getDisplayables()) {
343 if (DBObject
.class == type
|| Displayable
.class == type
) {
344 if (ob
instanceof LayerSet
) find((LayerSet
)ob
, al
, type
);
346 } else if (ob
.getClass() == type
) {
353 /** Remove from the tables if there. */
354 static public void remove(final Displayable displ
) {
355 if (null == displ
|| null == instance
) return;
356 final int n_tabs
= instance
.search_tabs
.getTabCount();
357 boolean repaint
= false;
358 final int selected
= instance
.search_tabs
.getSelectedIndex();
359 for (int t
=0; t
<n_tabs
; t
++) {
360 java
.awt
.Component c
= instance
.search_tabs
.getComponentAt(t
);
361 JTable table
= (JTable
)((JScrollPane
)c
).getViewport().getComponent(0);
362 DisplayableTableModel data
= (DisplayableTableModel
)table
.getModel();
363 if (data
.remove(displ
)) {
364 // remake table (can't delete just a row, only columns??)
365 String name
= instance
.search_tabs
.getTitleAt(t
);
366 instance
.search_tabs
.removeTabAt(t
);
367 // need to think about it TODO // if (0 == data.getRowCount()) continue;
368 instance
.search_tabs
.insertTab(name
, null, instance
.makeTable(data
), "", t
);
369 if (t
== selected
) repaint
= true;
370 try { Thread
.sleep(100); } catch (Exception e
) {} // I love swing
374 Utils
.updateComponent(instance
.search_frame
);
375 instance
.search_tabs
.setSelectedIndex(selected
);
379 /** Repaint (refresh the text in the cells) only if any of the selected tabs in any of the search frames contains the given object in its rows. */
380 static public void repaint(final Object ob
) {
381 if (null == instance
) return;
382 final int selected
= instance
.search_tabs
.getSelectedIndex();
383 if (-1 == selected
) return;
384 java
.awt
.Component c
= instance
.search_tabs
.getComponentAt(selected
);
385 JTable table
= (JTable
)((JScrollPane
)c
).getViewport().getComponent(0);
386 DisplayableTableModel data
= (DisplayableTableModel
)table
.getModel();
387 if (data
.contains(ob
)) {
388 Utils
.updateComponent(instance
.search_frame
);