1 /* 2 * Jalview - A Sequence Alignment Editor and Viewer (2.11.1.4) 3 * Copyright (C) 2021 The Jalview Authors 4 * 5 * This file is part of Jalview. 6 * 7 * Jalview is free software: you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation, either version 3 10 * of the License, or (at your option) any later version. 11 * 12 * Jalview is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty 14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 15 * PURPOSE. See the GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>. 19 * The Jalview Authors are detailed in the 'AUTHORS' file. 20 */ 21 package jalview.gui; 22 23 import jalview.util.MessageManager; 24 import jalview.ws.seqfetcher.DbSourceProxy; 25 26 import java.awt.BorderLayout; 27 import java.awt.Component; 28 import java.awt.Dimension; 29 import java.awt.FlowLayout; 30 import java.awt.GridLayout; 31 import java.awt.event.ActionEvent; 32 import java.awt.event.ActionListener; 33 import java.awt.event.KeyEvent; 34 import java.awt.event.KeyListener; 35 import java.awt.event.MouseAdapter; 36 import java.awt.event.MouseEvent; 37 import java.util.ArrayList; 38 import java.util.HashSet; 39 import java.util.Hashtable; 40 import java.util.List; 41 import java.util.Vector; 42 43 import javax.swing.JButton; 44 import javax.swing.JLabel; 45 import javax.swing.JPanel; 46 import javax.swing.JScrollPane; 47 import javax.swing.JTree; 48 import javax.swing.ToolTipManager; 49 import javax.swing.event.TreeSelectionEvent; 50 import javax.swing.event.TreeSelectionListener; 51 import javax.swing.tree.DefaultMutableTreeNode; 52 import javax.swing.tree.DefaultTreeCellRenderer; 53 import javax.swing.tree.DefaultTreeModel; 54 import javax.swing.tree.TreeCellRenderer; 55 import javax.swing.tree.TreeNode; 56 import javax.swing.tree.TreePath; 57 import javax.swing.tree.TreeSelectionModel; 58 59 public class JDatabaseTree extends JalviewDialog implements KeyListener 60 { 61 boolean allowMultiSelections = false; 62 63 public int action; 64 getDatabaseSelectorButton()65 JButton getDatabaseSelectorButton() 66 { 67 final JButton viewdbs = new JButton( 68 MessageManager.getString("action.select_ddbb")); 69 viewdbs.addActionListener(new ActionListener() 70 { 71 72 @Override 73 public void actionPerformed(ActionEvent arg0) 74 { 75 showDialog(); 76 } 77 }); 78 return viewdbs; 79 } 80 81 JScrollPane svp; 82 83 JTree dbviews; 84 85 private jalview.ws.SequenceFetcher sfetcher; 86 87 private JLabel dbstatus, dbstatex; 88 89 private JPanel mainPanel = new JPanel(new BorderLayout()); 90 JDatabaseTree(jalview.ws.SequenceFetcher sfetch)91 public JDatabaseTree(jalview.ws.SequenceFetcher sfetch) 92 { 93 mainPanel.add(this); 94 initDialogFrame(mainPanel, true, false, MessageManager 95 .getString("label.select_database_retrieval_source"), 650, 490); 96 /* 97 * Dynamically generated database list will need a translation function from 98 * internal source to externally distinct names. UNIPROT and UP_NAME are 99 * identical DB sources, and should be collapsed. 100 */ 101 DefaultMutableTreeNode tn = null, root = new DefaultMutableTreeNode(); 102 Hashtable<String, DefaultMutableTreeNode> source = new Hashtable<>(); 103 sfetcher = sfetch; 104 String dbs[] = sfetch.getSupportedDb(); 105 Hashtable<String, String> ht = new Hashtable<>(); 106 for (int i = 0; i < dbs.length; i++) 107 { 108 tn = source.get(dbs[i]); 109 List<DbSourceProxy> srcs = sfetch.getSourceProxy(dbs[i]); 110 if (tn == null) 111 { 112 source.put(dbs[i], tn = new DefaultMutableTreeNode(dbs[i], true)); 113 } 114 for (DbSourceProxy dbp : srcs) 115 { 116 if (ht.get(dbp.getDbName()) == null) 117 { 118 tn.add(new DefaultMutableTreeNode(dbp, false)); 119 ht.put(dbp.getDbName(), dbp.getDbName()); 120 } 121 else 122 { 123 System.err.println("dupe ig for : " + dbs[i] + " \t" 124 + dbp.getDbName() + " (" + dbp.getDbSource() + ")"); 125 source.remove(tn); 126 } 127 } 128 } 129 for (int i = 0; i < dbs.length; i++) 130 { 131 tn = source.get(dbs[i]); 132 if (tn == null) 133 { 134 continue; 135 } 136 if (tn.getChildCount() == 1) 137 { 138 DefaultMutableTreeNode ttn = (DefaultMutableTreeNode) tn 139 .getChildAt(0); 140 // remove nodes with only one child 141 tn.setUserObject(ttn.getUserObject()); 142 tn.removeAllChildren(); 143 source.put(dbs[i], tn); 144 tn.setAllowsChildren(false); 145 } 146 root.add(tn); 147 } 148 // and sort the tree 149 sortTreeNodes(root); 150 dbviews = new JTree(new DefaultTreeModel(root, false)); 151 dbviews.setCellRenderer(new DbTreeRenderer(this)); 152 153 dbviews.getSelectionModel() 154 .setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); 155 svp = new JScrollPane(dbviews); 156 svp.setMinimumSize(new Dimension(100, 200)); 157 svp.setPreferredSize(new Dimension(200, 400)); 158 svp.setMaximumSize(new Dimension(300, 600)); 159 160 JPanel panel = new JPanel(new BorderLayout()); 161 panel.setSize(new Dimension(350, 220)); 162 panel.add(svp); 163 dbviews.addTreeSelectionListener(new TreeSelectionListener() 164 { 165 166 @Override 167 public void valueChanged(TreeSelectionEvent arg0) 168 { 169 _setSelectionState(); 170 } 171 }); 172 dbviews.addMouseListener(new MouseAdapter() 173 { 174 175 @Override 176 public void mousePressed(MouseEvent e) 177 { 178 if (e.getClickCount() == 2) 179 { 180 okPressed(); 181 closeDialog(); 182 } 183 } 184 }); 185 JPanel jc = new JPanel(new BorderLayout()), 186 j = new JPanel(new FlowLayout()); 187 jc.add(svp, BorderLayout.CENTER); 188 189 java.awt.Font f; 190 // TODO: make the panel stay a fixed size for longest dbname+example set. 191 JPanel dbstat = new JPanel(new GridLayout(2, 1)); 192 dbstatus = new JLabel(" "); // set the height correctly for layout 193 dbstatus.setFont(f = JvSwingUtils.getLabelFont(false, true)); 194 dbstatus.setSize(new Dimension(290, 50)); 195 dbstatex = new JLabel(" "); 196 dbstatex.setFont(f); 197 dbstatex.setSize(new Dimension(290, 50)); 198 dbstat.add(dbstatus); 199 dbstat.add(dbstatex); 200 jc.add(dbstat, BorderLayout.SOUTH); 201 jc.validate(); 202 add(jc, BorderLayout.CENTER); 203 ok.setEnabled(false); 204 j.add(ok); 205 j.add(cancel); 206 add(j, BorderLayout.SOUTH); 207 dbviews.addKeyListener(this); 208 validate(); 209 } 210 sortTreeNodes(DefaultMutableTreeNode root)211 private void sortTreeNodes(DefaultMutableTreeNode root) 212 { 213 if (root.getChildCount() == 0) 214 { 215 return; 216 } 217 int count = root.getChildCount(); 218 String[] names = new String[count]; 219 DefaultMutableTreeNode[] nodes = new DefaultMutableTreeNode[count]; 220 for (int i = 0; i < count; i++) 221 { 222 TreeNode node = root.getChildAt(i); 223 if (node instanceof DefaultMutableTreeNode) 224 { 225 DefaultMutableTreeNode child = (DefaultMutableTreeNode) node; 226 nodes[i] = child; 227 if (child.getUserObject() instanceof DbSourceProxy) 228 { 229 names[i] = ((DbSourceProxy) child.getUserObject()).getDbName() 230 .toLowerCase(); 231 } 232 else 233 { 234 names[i] = ((String) child.getUserObject()).toLowerCase(); 235 sortTreeNodes(child); 236 } 237 } 238 else 239 { 240 throw new Error(MessageManager 241 .getString("error.implementation_error_cant_reorder_tree")); 242 } 243 } 244 jalview.util.QuickSort.sort(names, nodes); 245 root.removeAllChildren(); 246 for (int i = count - 1; i >= 0; i--) 247 { 248 root.add(nodes[i]); 249 } 250 } 251 252 private class DbTreeRenderer extends DefaultTreeCellRenderer 253 implements TreeCellRenderer 254 { 255 JDatabaseTree us; 256 DbTreeRenderer(JDatabaseTree me)257 public DbTreeRenderer(JDatabaseTree me) 258 { 259 us = me; 260 ToolTipManager.sharedInstance().registerComponent(dbviews); 261 } 262 returnLabel(String txt)263 private Component returnLabel(String txt) 264 { 265 JLabel jl = new JLabel(txt); 266 jl.setFont(JvSwingUtils.getLabelFont()); 267 return jl; 268 } 269 270 @Override getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus)271 public Component getTreeCellRendererComponent(JTree tree, Object value, 272 boolean selected, boolean expanded, boolean leaf, int row, 273 boolean hasFocus) 274 { 275 String val = ""; 276 if (value != null && value instanceof DefaultMutableTreeNode) 277 { 278 DefaultMutableTreeNode vl = (DefaultMutableTreeNode) value; 279 value = vl.getUserObject(); 280 if (value instanceof DbSourceProxy) 281 { 282 val = ((DbSourceProxy) value).getDbName(); 283 if (((DbSourceProxy) value).getDescription() != null) 284 { // getName() 285 this.setToolTipText(((DbSourceProxy) value).getDescription()); 286 } 287 } 288 else 289 { 290 if (value instanceof String) 291 { 292 val = (String) value; 293 } 294 } 295 } 296 if (value == null) 297 { 298 val = ""; 299 } 300 return super.getTreeCellRendererComponent(tree, val, selected, 301 expanded, leaf, row, hasFocus); 302 303 } 304 } 305 306 List<DbSourceProxy> oldselection, selection = null; 307 308 TreePath[] tsel = null, oldtsel = null; 309 310 @Override raiseClosed()311 protected void raiseClosed() 312 { 313 for (ActionListener al : lstners) 314 { 315 al.actionPerformed(null); 316 } 317 } 318 319 @Override okPressed()320 protected void okPressed() 321 { 322 _setSelectionState(); 323 } 324 325 @Override cancelPressed()326 protected void cancelPressed() 327 { 328 selection = oldselection; 329 tsel = oldtsel; 330 _revertSelectionState(); 331 closeDialog(); 332 } 333 showDialog()334 void showDialog() 335 { 336 oldselection = selection; 337 oldtsel = tsel; 338 validate(); 339 waitForInput(); 340 } 341 hasSelection()342 public boolean hasSelection() 343 { 344 return selection == null ? false : selection.size() == 0 ? false : true; 345 } 346 getSelectedSources()347 public List<DbSourceProxy> getSelectedSources() 348 { 349 return selection; 350 } 351 352 /** 353 * disable or enable selection handler 354 */ 355 boolean handleSelections = true; 356 _setSelectionState()357 private void _setSelectionState() 358 { 359 if (!handleSelections) 360 { 361 return; 362 } 363 ok.setEnabled(false); 364 if (dbviews.getSelectionCount() == 0) 365 { 366 selection = null; 367 } 368 369 tsel = dbviews.getSelectionPaths(); 370 boolean forcedFirstChild = false; 371 List<DbSourceProxy> srcs = new ArrayList<>(); 372 if (tsel != null) 373 { 374 for (TreePath tp : tsel) 375 { 376 DefaultMutableTreeNode admt, 377 dmt = (DefaultMutableTreeNode) tp.getLastPathComponent(); 378 if (dmt.getUserObject() != null) 379 { 380 /* 381 * enable OK button once a selection has been made 382 */ 383 ok.setEnabled(true); 384 if (dmt.getUserObject() instanceof DbSourceProxy) 385 { 386 srcs.add((DbSourceProxy) dmt.getUserObject()); 387 } 388 else 389 { 390 if (allowMultiSelections) 391 { 392 srcs.addAll(sfetcher 393 .getSourceProxy((String) dmt.getUserObject())); 394 } 395 else 396 { 397 srcs.add(sfetcher.getSourceProxy((String) dmt.getUserObject()) 398 .get(0)); 399 forcedFirstChild = true; 400 } 401 } 402 } 403 } 404 } 405 updateDbStatus(srcs, forcedFirstChild); 406 selection = srcs; 407 } 408 _revertSelectionState()409 private void _revertSelectionState() 410 { 411 handleSelections = false; 412 if (selection == null || selection.size() == 0) 413 { 414 dbviews.clearSelection(); 415 } 416 else 417 { 418 dbviews.setSelectionPaths(tsel); 419 } 420 handleSelections = true; 421 } 422 updateDbStatus(List<DbSourceProxy> srcs, boolean forcedFirstChild)423 private void updateDbStatus(List<DbSourceProxy> srcs, 424 boolean forcedFirstChild) 425 { 426 int x = 0; 427 String nm = "", qr = ""; 428 for (DbSourceProxy dbs : srcs) 429 { 430 String tq = dbs.getTestQuery(); 431 nm = dbs.getDbName(); 432 if (tq != null && tq.trim().length() > 0 && dbs.isValidReference(tq)) 433 { 434 qr = tq; 435 x++; 436 } 437 } 438 439 dbstatex.setText(" "); 440 if (allowMultiSelections) 441 { 442 dbstatus.setText(MessageManager.formatMessage( 443 "label.selected_database_to_fetch_from", new String[] 444 { Integer.valueOf(srcs.size()).toString(), 445 (srcs.size() == 1 ? "" : "s"), 446 (srcs.size() > 0 447 ? " with " + x + " test quer" 448 + (x == 1 ? "y" : "ies") 449 : ".") })); 450 } 451 else 452 { 453 if (nm.length() > 0) 454 { 455 dbstatus.setText(MessageManager 456 .formatMessage("label.database_param", new String[] 457 { nm })); 458 if (qr.length() > 0) 459 { 460 dbstatex.setText(MessageManager 461 .formatMessage("label.example_param", new String[] 462 { qr })); 463 } 464 } 465 else 466 { 467 dbstatus.setText(" "); 468 } 469 } 470 dbstatus.invalidate(); 471 dbstatex.invalidate(); 472 } 473 getSelectedItem()474 public String getSelectedItem() 475 { 476 if (hasSelection()) 477 { 478 return getSelectedSources().get(0).getDbName(); 479 } 480 return null; 481 } 482 getExampleQueries()483 public String getExampleQueries() 484 { 485 if (!hasSelection()) 486 { 487 return null; 488 } 489 StringBuffer sb = new StringBuffer(); 490 HashSet<String> hs = new HashSet<>(); 491 for (DbSourceProxy dbs : getSelectedSources()) 492 { 493 String tq = dbs.getTestQuery(); 494 ; 495 if (hs.add(tq)) 496 { 497 if (sb.length() > 0) 498 { 499 sb.append(";"); 500 } 501 sb.append(tq); 502 } 503 } 504 return sb.toString(); 505 } 506 507 List<ActionListener> lstners = new Vector<>(); 508 addActionListener(ActionListener actionListener)509 public void addActionListener(ActionListener actionListener) 510 { 511 lstners.add(actionListener); 512 } 513 removeActionListener(ActionListener actionListener)514 public void removeActionListener(ActionListener actionListener) 515 { 516 lstners.remove(actionListener); 517 } 518 519 520 @Override keyPressed(KeyEvent arg0)521 public void keyPressed(KeyEvent arg0) 522 { 523 if (!arg0.isConsumed() && arg0.getKeyCode() == KeyEvent.VK_ENTER) 524 { 525 action = arg0.getKeyCode(); 526 okPressed(); 527 closeDialog(); 528 } 529 if (!arg0.isConsumed() && arg0.getKeyChar() == KeyEvent.VK_ESCAPE) 530 { 531 action = arg0.getKeyCode(); 532 cancelPressed(); 533 } 534 } 535 536 @Override keyReleased(KeyEvent arg0)537 public void keyReleased(KeyEvent arg0) 538 { 539 // TODO Auto-generated method stub 540 541 } 542 543 @Override keyTyped(KeyEvent arg0)544 public void keyTyped(KeyEvent arg0) 545 { 546 // TODO Auto-generated method stub 547 548 } 549 550 @Override setVisible(boolean arg0)551 public void setVisible(boolean arg0) 552 { 553 System.out.println("setVisible: " + arg0); 554 super.setVisible(arg0); 555 } 556 } 557