001 /* 002 * Copyright 2007, 2012 Stephen Fisher and Junhyong Kim, University of 003 * Pennsylvania. 004 * 005 * This file is part of Glo-DB. 006 * 007 * Glo-DB is free software: you can redistribute it and/or modify it 008 * under the terms of the GNU General Public License as published by 009 * the Free Software Foundation, either version 3 of the License, or 010 * (at your option) any later version. 011 * 012 * Glo-DB is distributed in the hope that it will be useful, but 013 * WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * General Public License for more details. 016 * 017 * You should have received a copy of the GNU General Public License 018 * along with Glo-DB. If not, see <http://www.gnu.org/licenses/>. 019 * 020 * @(#)QueryBuilder.java 021 */ 022 023 package edu.upenn.gloDB.gui; 024 025 import edu.upenn.gloDB.*; 026 import java.awt.*; 027 import java.awt.event.*; 028 import javax.swing.*; 029 import javax.swing.event.*; 030 import javax.swing.border.Border; 031 import javax.swing.text.*; 032 import java.util.Iterator; 033 import java.util.ArrayList; 034 import javax.swing.table.AbstractTableModel; 035 import javax.swing.table.TableColumn; 036 037 /** 038 * Browse existing Tracks. 039 * 040 * @author Stephen Fisher 041 * @version $Id: QueryBuilder.java,v 1.1.2.35 2007/03/01 21:17:33 fisher Exp $ 042 */ 043 044 public class QueryBuilder extends JFrame { 045 // private final String[] OPERATORS = { "Intersection: AND", "Intersection (strict): sAND", "Intersection (bitwise): &&", "Union: OR", "Union (bitwise): ||", "Relative complement: MINUS", "Relative complement (strict): sMINUS", "Relative complement (bitwise): -", "Order: POS", "Order (bitwise): ." }; 046 private final String[] OPERATORS = { "Intersection: AND", "Intersection (strict): sAND", "Intersection (bitwise): &&", "Union: OR", "Union (bitwise): ||", "Relative complement: MINUS", "Relative complement (strict): sMINUS", "Relative complement (bitwise): -", "Order: POS" }; 047 048 private JTextField queryTF; 049 private JTable table; 050 private ListTableModel tableModel; 051 private String trackID = ""; 052 053 private JButton updateB; 054 private JButton groupB; 055 private JButton ungroupB; 056 private JButton deleteB; 057 private JButton deleteAllB; 058 private JButton copyB; 059 private JButton computeB; 060 private JButton newB; 061 062 private JList operatorL; 063 private JCheckBox negateChB; 064 private JList trackL; 065 private JCheckBox sequenceChB; 066 private JComboBox sequenceCB; 067 068 // these aren't forced to be integers here because then the user 069 // would need to know that '-1' is the default value and as 070 // strings we can just use the empty string. 071 private JTextField minWidthTF; 072 private JTextField maxWidthTF; 073 private JTextField minSeqPosTF; 074 private JTextField maxSeqPosTF; 075 private JTextField minRepeatTF; 076 private JTextField maxRepeatTF; 077 private JTextField minWithinTF; 078 private JTextField maxWithinTF; 079 private JTextField minPosTF; 080 private JTextField maxPosTF; 081 082 QueryBuilder thisFrame; 083 084 public QueryBuilder() { 085 this(""); 086 } 087 088 public QueryBuilder(String id) { 089 super("Query Builder"); 090 091 // keep pointer to self so can 'dispose' Frame below 092 thisFrame = this; 093 094 setDefaultCloseOperation(DISPOSE_ON_CLOSE); 095 096 // create various borders 097 Border emptyBorder = BorderFactory.createEmptyBorder(10,10,10,10); 098 Border emptyBorder5 = BorderFactory.createEmptyBorder(5,5,5,5); 099 Border lowBevelBorder = BorderFactory.createLoweredBevelBorder(); 100 Border etchedBorder = BorderFactory.createEtchedBorder(); 101 Border tmpBorder = BorderFactory.createCompoundBorder(emptyBorder, lowBevelBorder); 102 // create a lower bevel border with outside and inside padding 103 Border lowBevelPadBorder = BorderFactory.createCompoundBorder(tmpBorder, emptyBorder); 104 // create an etched border with inside padding 105 // Border etchedPadBorder = BorderFactory.createCompoundBorder(etchedBorder, emptyBorder); 106 // create an etched border with no top padding, for use with titles 107 Border emptyBorder0 = BorderFactory.createEmptyBorder(0,10,10,10); 108 Border etchedPadBorder0 = BorderFactory.createCompoundBorder(etchedBorder, emptyBorder0); 109 110 111 // ************************************************* 112 // create query panel 113 JPanel queryP = new JPanel(new BorderLayout()); 114 queryP.setBorder(lowBevelPadBorder); 115 116 // ************************************************* 117 // create text composition panel 118 JPanel textP = new JPanel(new BorderLayout(10,0)); 119 // textP.setBorder(BorderFactory.createLoweredBevelBorder()); 120 textP.setBorder(emptyBorder5); 121 queryTF = new JTextField(50); 122 queryTF.setEditable(false); 123 JButton queryB = new JButton("Change Output Track ID"); 124 queryB.addActionListener(new ActionListener() { 125 public void actionPerformed(ActionEvent e) { 126 setID(getID()); 127 } 128 }); 129 textP.add(new JLabel(" Query: "), BorderLayout.WEST); 130 textP.add(queryTF, BorderLayout.CENTER); 131 textP.add(queryB, BorderLayout.EAST); 132 133 // ************************************************* 134 // TABLE SETUP 135 tableModel = new ListTableModel(); 136 table = new JTable(tableModel); 137 table.setPreferredScrollableViewportSize(new Dimension(500, 70)); 138 table.setColumnSelectionAllowed(false); 139 // table.setShowGrid(false); 140 // table.setShowHorizontalLines(false); 141 table.setShowVerticalLines(false); 142 // XXX this will hopefully disallow moving cols 143 table.setDragEnabled(false); 144 //Create the scroll pane and add the table to it. 145 JScrollPane tableSP = new JScrollPane(table); 146 /* 147 //Set up tool tip for Track column 148 DefaultTableCellRenderer renderer = new DefaultTableCellRenderer(); 149 renderer.setToolTipText("Click for combo box"); 150 trackTC.setCellRenderer(renderer); 151 */ 152 TableColumn column = null; 153 for (int i = 0; i < tableModel.getColumnCount(); i++) { 154 column = table.getColumnModel().getColumn(i); 155 // first 2 columns should be smaller 156 if (i < 2) column.setPreferredWidth(10); 157 else column.setPreferredWidth(100); 158 } 159 // END TABLE SETUP 160 // ************************************************* 161 162 // ************************************************* 163 // create panel with list 164 DefaultListSelectionModel dlsm = new DefaultListSelectionModel(); 165 table.setSelectionModel(dlsm); 166 // don't allow discontinuous selection 167 dlsm.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); 168 dlsm.addListSelectionListener(new ListSelectionListener() { 169 public void valueChanged(ListSelectionEvent e) { 170 if (e.getValueIsAdjusting()) return; 171 172 if (table.getSelectedRowCount() == 0) { 173 // no selection so disable buttons 174 updateB.setEnabled(false); 175 groupB.setEnabled(false); 176 ungroupB.setEnabled(false); 177 deleteB.setEnabled(false); 178 179 updateEditPanel(null); 180 181 // don't allow operator if first item 182 if (tableModel.getRowCount() == 0) { 183 operatorL.setEnabled(false); 184 operatorL.clearSelection(); 185 } else { 186 operatorL.setEnabled(true); 187 } 188 } else { 189 // we know that at least 1 row is selected 190 QueryElement qElement = tableModel.getRow(table.getSelectedRow()); 191 updateEditPanel(qElement); 192 193 // enable buttons if selection 194 groupB.setEnabled(true); 195 deleteB.setEnabled(true); 196 197 // only enable the follwoing buttons if 1 item 198 // is selected. For ungroupB, if only 1 199 // selected item, then only enable if group. 200 // If multiple selected items then just leave 201 // enabled. 202 if (table.getSelectedRowCount() == 1) { 203 // only enable the appropriate button 204 if (qElement.isGrouped()) { 205 ungroupB.setEnabled(true); 206 } else { 207 ungroupB.setEnabled(false); 208 } 209 updateB.setEnabled(true); 210 } else { 211 ungroupB.setEnabled(true); 212 updateB.setEnabled(false); 213 trackL.clearSelection(); 214 } 215 } 216 } 217 }); 218 219 // ************************************************* 220 // create panel with control button 221 JPanel controlP = new JPanel(new GridLayout(1,0,200,5)); 222 controlP.setBorder(emptyBorder5); 223 // add delete button 224 deleteB = new JButton("Delete Selected Elements(s)"); 225 deleteB.setEnabled(false); 226 deleteB.addActionListener(new ActionListener() { 227 public void actionPerformed(ActionEvent e) { 228 tableModel.removeRows(table.getSelectedRows()); 229 updateQuery(); 230 } 231 }); 232 controlP.add(deleteB); 233 // add clear button 234 deleteAllB = new JButton("Delete All Elements"); 235 deleteAllB.setEnabled(false); 236 deleteAllB.addActionListener(new ActionListener() { 237 public void actionPerformed(ActionEvent e) { 238 String msg = "Are you sure you want to delete all items?"; 239 Object[] options = {"Delete", "Cancel"}; 240 int flag = JOptionPane.showOptionDialog(null, msg, 241 "Delete Confirmation", 242 JOptionPane.YES_NO_OPTION, 243 JOptionPane.QUESTION_MESSAGE, 244 null, 245 options, 246 options[1]); 247 if (flag == JOptionPane.YES_OPTION) { // "Delete" 248 tableModel.removeAllRows(); 249 updateQuery(); 250 } 251 } 252 }); 253 controlP.add(deleteAllB); 254 255 queryP.add(textP, BorderLayout.NORTH); 256 queryP.add(tableSP, BorderLayout.CENTER); 257 queryP.add(controlP, BorderLayout.SOUTH); 258 259 // ************************************************* 260 // ************ EDIT PANEL ************* 261 JPanel editP = new JPanel(new BorderLayout()); 262 editP.setBorder(lowBevelPadBorder); 263 264 // ********* EDIT BUTTON PANEL ************ 265 JPanel buttonP = new JPanel(new GridLayout(1,0,10,10)); 266 buttonP.setBorder(BorderFactory.createEmptyBorder(10,0,0,0)); 267 // add 'new' button 268 newB = new JButton("Insert Track"); 269 newB.addActionListener(new ActionListener() { 270 public void actionPerformed(ActionEvent e) { 271 QueryElement qElement = new QueryElement((String) trackL.getSelectedValue()); 272 updateQueryElement(qElement); 273 274 int[] selection = table.getSelectedRows(); 275 if (selection.length == 0) { // nothing selected, append to end 276 tableModel.addRow(qElement); 277 } else { // insert track after current selection 278 int index = selection[selection.length - 1]; 279 if (index == tableModel.getRowCount()) { 280 // last row selected so just append 281 tableModel.addRow(qElement); 282 } else { 283 tableModel.addRowAt(qElement, index + 1); 284 } 285 } 286 updateQuery(); 287 updateEditPanel(null); // reset the query panel 288 } 289 }); 290 buttonP.add(newB); 291 // add group button 292 groupB = new JButton("Group Selected Element(s)"); 293 groupB.setEnabled(false); 294 groupB.addActionListener(new ActionListener() { 295 public void actionPerformed(ActionEvent e) { 296 // get selected elements to put into group 297 int[] rows = table.getSelectedRows(); 298 299 // store where the new group goes 300 int row = rows[0]; 301 302 //make new group 303 QueryElement qElement = new QueryElement(); 304 updateQueryElement(qElement); 305 306 // remove operator from first element in group 307 QueryElement firstQE = tableModel.getRow(row); 308 // firstQE.operator = ""; 309 firstQE.operator = -1; 310 311 // build group element 312 for (int i = 0; i < rows.length; i++) { 313 qElement.addToGroup(tableModel.getRow(rows[i])); 314 } 315 316 // remove grouped elements from table 317 tableModel.removeRows(rows); 318 319 // add group element to table 320 tableModel.addRowAt(qElement, row); 321 322 updateQuery(); 323 updateEditPanel(null); // reset the query panel 324 } 325 }); 326 buttonP.add(groupB); 327 // add ungroup button 328 ungroupB = new JButton("Ungroup Selected Element(s)"); 329 ungroupB.setEnabled(false); 330 ungroupB.addActionListener(new ActionListener() { 331 public void actionPerformed(ActionEvent e) { 332 // get selected elements to ungroup 333 int[] rows = table.getSelectedRows(); 334 // store where the new elements go 335 int row = rows[0]; 336 337 // build an array containing all the selected 338 // items, removing the items from the table. This 339 // array is in reverse order and will be inverted 340 // when the items are ungrouped and added back to 341 // the table. 342 ArrayList selected = new ArrayList(); 343 int maxRow = rows[rows.length-1]; 344 while (maxRow >= row) { 345 selected.add(tableModel.getRow(maxRow)); 346 tableModel.removeRow(maxRow--); 347 } 348 349 // by adding all of the ungrouped items at the 350 // same index location 'row', we are effectively 351 // inverting 'selected' 352 for (Iterator i = selected.iterator(); i.hasNext();) { 353 QueryElement qElement = (QueryElement) i.next(); 354 // only 'ungroup' if actually a group 355 if (qElement.isGrouped()) { 356 // move operator from group to first element 357 QueryElement firstQE = (QueryElement) qElement.getGroup().get(0); 358 firstQE.operator = qElement.operator; 359 360 // add ungrouped items 361 int index = row; 362 for (Iterator it = qElement.groupIterator(); it.hasNext();) { 363 tableModel.addRowAt((QueryElement) it.next(), index++); 364 } 365 } else { 366 // not a group so just add the item 367 tableModel.addRowAt(qElement, row); 368 } 369 } 370 371 updateQuery(); 372 } 373 }); 374 buttonP.add(ungroupB); 375 // add update button 376 updateB = new JButton("Update Selected Element"); 377 updateB.setEnabled(false); 378 updateB.addActionListener(new ActionListener() { 379 public void actionPerformed(ActionEvent e) { 380 QueryElement qElement = tableModel.getRow(table.getSelectedRow()); 381 updateQueryElement(qElement); 382 if (! qElement.isGrouped()) { 383 qElement.track = (String) trackL.getSelectedValue(); 384 } 385 updateQuery(); 386 387 // need to tell table to redraw 388 tableModel.fireTableDataChanged(); 389 } 390 }); 391 buttonP.add(updateB); 392 393 // ************************************************* 394 // ************* OPTION PANELS 395 JPanel optionsP = new JPanel(new GridLayout(1,0,5,0)); 396 397 // create Operators panel with titled, etched, padded border 398 JPanel operatorP = new JPanel(new BorderLayout()); 399 // JPanel operatorP = new JPanel(new GridLayout(0,1,0,5)); 400 operatorP.setBorder(BorderFactory.createTitledBorder(etchedPadBorder0, "Operators")); 401 operatorL = new JList(OPERATORS); 402 operatorL.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 403 operatorL.setEnabled(false); 404 operatorL.clearSelection(); 405 operatorL.addListSelectionListener(new ListSelectionListener() { 406 public void valueChanged(ListSelectionEvent e) { 407 if (e.getValueIsAdjusting()) return; 408 if (operatorL.getSelectedIndex() == -1) { // nothing selected 409 minPosTF.setEnabled(false); 410 } else { 411 // test if order operator ('POS'/'.') 412 if (QueryElement.isOrderOperator(operatorL.getSelectedIndex())) { 413 minPosTF.setEnabled(true); 414 } else { 415 minPosTF.setEnabled(false); 416 maxPosTF.setEnabled(false); 417 } 418 } 419 } 420 }); 421 JScrollPane operatorSP = new JScrollPane(operatorL); 422 423 JPanel orderedP = new JPanel(new GridLayout(1,0)); 424 orderedP.setBorder(BorderFactory.createEmptyBorder(5,0,0,0)); 425 PlainDocument minPosPD = new PlainDocument(); 426 minPosPD.addDocumentListener(new DocumentListener() { 427 public void insertUpdate(DocumentEvent e) { 428 try { 429 Integer.parseInt(minPosTF.getText().trim()); 430 maxPosTF.setEnabled(minPosTF.getText().trim().length() > 0); 431 } catch (NumberFormatException exception) { 432 GloDBUtils.printError("Order lower bound must be an integer."); 433 maxPosTF.setEnabled(false); 434 } 435 } 436 public void removeUpdate(DocumentEvent e) { 437 try { 438 Integer.parseInt(minPosTF.getText().trim()); 439 maxPosTF.setEnabled(minPosTF.getText().trim().length() > 0); 440 } catch (NumberFormatException exception) { 441 // don't need to again show error message, 442 // just disable maxPosTF 443 maxPosTF.setEnabled(false); 444 } 445 } 446 public void changedUpdate(DocumentEvent e) { ; } 447 }); 448 minPosTF = new JTextField(minPosPD, "", 0); 449 minPosTF.setEnabled(false); 450 orderedP.add(new JLabel("Order (bp):")); 451 orderedP.add(minPosTF); 452 orderedP.add(new JLabel("to", JLabel.CENTER)); 453 PlainDocument maxPosPD = new PlainDocument(); 454 maxPosPD.addDocumentListener(new DocumentListener() { 455 public void insertUpdate(DocumentEvent e) { 456 try { 457 Integer.parseInt(maxPosTF.getText().trim()); 458 } catch (NumberFormatException exception) { 459 GloDBUtils.printError("Order upper bound must be an integer."); 460 } 461 } 462 public void removeUpdate(DocumentEvent e) { ; } 463 public void changedUpdate(DocumentEvent e) { ; } 464 }); 465 maxPosTF = new JTextField(maxPosPD, "", 0); 466 maxPosTF.setEnabled(false); 467 orderedP.add(maxPosTF); 468 469 operatorP.add(operatorSP, BorderLayout.CENTER); 470 operatorP.add(orderedP, BorderLayout.SOUTH); 471 /* 472 JPanel setOperatorP = new JPanel(new BorderLayout()); 473 setOperatorL = new JList(SET_OPERATORS); 474 setOperatorL.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 475 setOperatorL.setEnabled(false); 476 setOperatorL.clearSelection(); 477 JScrollPane setOperatorSP = new JScrollPane(setOperatorL); 478 setOperatorP.add(new JLabel("Set:"), BorderLayout.NORTH); 479 setOperatorP.add(setOperatorSP, BorderLayout.CENTER); 480 JPanel bitOperatorP = new JPanel(new BorderLayout()); 481 bitOperatorL = new JList(BIT_OPERATORS); 482 bitOperatorL.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 483 bitOperatorL.setEnabled(false); 484 bitOperatorL.clearSelection(); 485 JScrollPane bitOperatorSP = new JScrollPane(bitOperatorL); 486 bitOperatorP.add(new JLabel("Bitwise:"), BorderLayout.NORTH); 487 bitOperatorP.add(bitOperatorSP, BorderLayout.CENTER); 488 operatorP.add(setOperatorP); 489 operatorP.add(bitOperatorP); 490 */ 491 optionsP.add(operatorP); 492 493 // create panel to stack unitOperatorP and trackP 494 JPanel middleP = new JPanel(new BorderLayout()); 495 496 // create Unitary Operator panel with titled, etched, padded border 497 JPanel unitOperatorP = new JPanel(new BorderLayout()); 498 unitOperatorP.setBorder(BorderFactory.createTitledBorder(etchedPadBorder0, "Unitary Operators")); 499 JPanel unitOperatorP0 = new JPanel(new BorderLayout()); 500 negateChB = new JCheckBox(); 501 negateChB.setSelected(false); 502 unitOperatorP0.add(negateChB, BorderLayout.WEST); 503 unitOperatorP0.add(new JLabel(" Negate"), BorderLayout.CENTER); 504 unitOperatorP.add(unitOperatorP0, BorderLayout.NORTH); 505 middleP.add(unitOperatorP, BorderLayout.NORTH); 506 507 // create Track panel with titled, etched, padded border 508 JPanel trackP = new JPanel(new BorderLayout()); 509 trackP.setBorder(BorderFactory.createTitledBorder(etchedPadBorder0, "Track")); 510 JPanel trackP0 = new JPanel(new BorderLayout()); 511 trackL = new JList(ObjectHandles.getTrackList()); 512 trackL.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 513 trackL.addListSelectionListener(new ListSelectionListener() { 514 public void valueChanged(ListSelectionEvent e) { 515 if (e.getValueIsAdjusting()) return; 516 if (trackL.getSelectedIndex() == -1) { // nothing selected 517 newB.setEnabled(false); 518 } else { 519 newB.setEnabled(true); 520 } 521 } 522 }); 523 JScrollPane trackSP = new JScrollPane(trackL); 524 trackSP.setBorder(emptyBorder5); 525 trackP0.add(trackSP, BorderLayout.CENTER); 526 trackP.add(trackP0, BorderLayout.NORTH); 527 middleP.add(trackP, BorderLayout.CENTER); 528 optionsP.add(middleP); 529 530 // create Qualifier panel with titled, etched, padded border 531 JPanel qualifiersP = new JPanel(new GridLayout(8,1,5,5)); 532 qualifiersP.setBorder(BorderFactory.createTitledBorder(etchedPadBorder0, "Qualifiers")); 533 JPanel sequenceP = new JPanel(new BorderLayout()); 534 sequenceChB = new JCheckBox(); 535 sequenceChB.setSelected(false); 536 sequenceChB.addActionListener(new ActionListener() { 537 public void actionPerformed(ActionEvent e) { 538 sequenceCB.setEnabled(sequenceChB.isSelected()); 539 } 540 }); 541 JPanel sequenceP0 = new JPanel(new BorderLayout()); 542 sequenceP0.add(sequenceChB, BorderLayout.WEST); 543 sequenceP0.add(new JLabel(" Sequence:"), BorderLayout.CENTER); 544 sequenceP.add(sequenceP0, BorderLayout.WEST); 545 sequenceCB = new JComboBox(ObjectHandles.getSequenceList()); 546 sequenceCB.setEditable(false); 547 sequenceCB.setEnabled(false); 548 sequenceP.add(sequenceCB, BorderLayout.CENTER); 549 qualifiersP.add(sequenceP); 550 // qualifiersP.add(sequenceCB); 551 qualifiersP.add(new JLabel("")); 552 553 JPanel widthP = new JPanel(new GridLayout(1,0)); 554 PlainDocument minWidthPD = new PlainDocument(); 555 minWidthPD.addDocumentListener(new DocumentListener() { 556 public void insertUpdate(DocumentEvent e) { 557 try { 558 Integer.parseInt(minWidthTF.getText().trim()); 559 maxWidthTF.setEnabled(minWidthTF.getText().trim().length() > 0); 560 } catch (NumberFormatException exception) { 561 GloDBUtils.printError("Width lower bound must be an integer."); 562 maxWidthTF.setEnabled(false); 563 } 564 } 565 public void removeUpdate(DocumentEvent e) { 566 try { 567 Integer.parseInt(minWidthTF.getText().trim()); 568 maxWidthTF.setEnabled(minWidthTF.getText().trim().length() > 0); 569 } catch (NumberFormatException exception) { 570 // don't need to again show error message, 571 // just disable maxWidthTF 572 maxWidthTF.setEnabled(false); 573 } 574 } 575 public void changedUpdate(DocumentEvent e) { ; } 576 }); 577 minWidthTF = new JTextField(minWidthPD, "", 0); 578 widthP.add(new JLabel("Width (bp):")); 579 widthP.add(minWidthTF); 580 widthP.add(new JLabel("to", JLabel.CENTER)); 581 PlainDocument maxWidthPD = new PlainDocument(); 582 maxWidthPD.addDocumentListener(new DocumentListener() { 583 public void insertUpdate(DocumentEvent e) { 584 try { 585 Integer.parseInt(maxWidthTF.getText().trim()); 586 } catch (NumberFormatException exception) { 587 GloDBUtils.printError("Width upper bound must be an integer."); 588 } 589 } 590 public void removeUpdate(DocumentEvent e) { ; } 591 public void changedUpdate(DocumentEvent e) { ; } 592 }); 593 maxWidthTF = new JTextField(maxWidthPD, "", 0); 594 maxWidthTF.setEnabled(false); 595 widthP.add(maxWidthTF); 596 // qualifiersP.add(new JLabel("Width (bp):")); 597 qualifiersP.add(widthP); 598 qualifiersP.add(new JLabel("")); 599 600 JPanel seqPosP = new JPanel(new GridLayout(1,0)); 601 PlainDocument minSeqPosPD = new PlainDocument(); 602 minSeqPosPD.addDocumentListener(new DocumentListener() { 603 public void insertUpdate(DocumentEvent e) { 604 try { 605 Integer.parseInt(minSeqPosTF.getText().trim()); 606 maxSeqPosTF.setEnabled(minSeqPosTF.getText().trim().length() > 0); 607 } catch (NumberFormatException exception) { 608 GloDBUtils.printError("Location lower bound must be an integer."); 609 maxSeqPosTF.setEnabled(false); 610 } 611 } 612 public void removeUpdate(DocumentEvent e) { 613 try { 614 Integer.parseInt(minSeqPosTF.getText().trim()); 615 maxSeqPosTF.setEnabled(minSeqPosTF.getText().trim().length() > 0); 616 } catch (NumberFormatException exception) { 617 // don't need to again show error message, 618 // just disable maxSeqPosTF 619 maxSeqPosTF.setEnabled(false); 620 } 621 } 622 public void changedUpdate(DocumentEvent e) { ; } 623 }); 624 minSeqPosTF = new JTextField(minSeqPosPD, "", 0); 625 seqPosP.add(new JLabel("Location (bp):")); 626 seqPosP.add(minSeqPosTF); 627 seqPosP.add(new JLabel("and", JLabel.CENTER)); 628 PlainDocument maxSeqPosPD = new PlainDocument(); 629 maxSeqPosPD.addDocumentListener(new DocumentListener() { 630 public void insertUpdate(DocumentEvent e) { 631 try { 632 Integer.parseInt(maxSeqPosTF.getText().trim()); 633 } catch (NumberFormatException exception) { 634 GloDBUtils.printError("Location upper bound must be an integer."); 635 } 636 } 637 public void removeUpdate(DocumentEvent e) { ; } 638 public void changedUpdate(DocumentEvent e) { ; } 639 }); 640 maxSeqPosTF = new JTextField(maxSeqPosPD, "", 0); 641 maxSeqPosTF.setEnabled(false); 642 seqPosP.add(maxSeqPosTF); 643 // qualifiersP.add(new JLabel("Location (bp):")); 644 qualifiersP.add(seqPosP); 645 qualifiersP.add(new JLabel("")); 646 647 JPanel repeatP = new JPanel(new GridLayout(1,0)); 648 PlainDocument minRepeatPD = new PlainDocument(); 649 minRepeatPD.addDocumentListener(new DocumentListener() { 650 public void insertUpdate(DocumentEvent e) { 651 try { 652 Integer.parseInt(minRepeatTF.getText().trim()); 653 maxRepeatTF.setEnabled(minRepeatTF.getText().trim().length() > 0); 654 minWithinTF.setEnabled(minRepeatTF.getText().trim().length() > 0); 655 maxWithinTF.setEnabled(minWithinTF.getText().trim().length() > 0); 656 } catch (NumberFormatException exception) { 657 GloDBUtils.printError("Repeat lower bound must be an integer."); 658 maxRepeatTF.setEnabled(false); 659 minWithinTF.setEnabled(false); 660 maxWithinTF.setEnabled(false); 661 } 662 } 663 public void removeUpdate(DocumentEvent e) { 664 try { 665 Integer.parseInt(minRepeatTF.getText().trim()); 666 maxRepeatTF.setEnabled(minRepeatTF.getText().trim().length() > 0); 667 minWithinTF.setEnabled(minRepeatTF.getText().trim().length() > 0); 668 maxWithinTF.setEnabled(minWithinTF.getText().trim().length() > 0); 669 } catch (NumberFormatException exception) { 670 // don't need to again show error message, 671 // just disable maxRepeatTF 672 maxRepeatTF.setEnabled(false); 673 minWithinTF.setEnabled(false); 674 maxWithinTF.setEnabled(false); 675 } 676 } 677 public void changedUpdate(DocumentEvent e) { ; } 678 }); 679 minRepeatTF = new JTextField(minRepeatPD, "", 0); 680 repeatP.add(new JLabel("Repeat:")); 681 repeatP.add(minRepeatTF); 682 repeatP.add(new JLabel("to", JLabel.CENTER)); 683 PlainDocument maxRepeatPD = new PlainDocument(); 684 maxRepeatPD.addDocumentListener(new DocumentListener() { 685 public void insertUpdate(DocumentEvent e) { 686 try { 687 Integer.parseInt(maxRepeatTF.getText().trim()); 688 } catch (NumberFormatException exception) { 689 GloDBUtils.printError("Repeat upper bound must be an integer."); 690 } 691 } 692 public void removeUpdate(DocumentEvent e) { ; } 693 public void changedUpdate(DocumentEvent e) { ; } 694 }); 695 maxRepeatTF = new JTextField(maxRepeatPD, "", 0); 696 maxRepeatTF.setEnabled(false); 697 repeatP.add(maxRepeatTF); 698 // qualifiersP.add(new JLabel("Repeat:")); 699 qualifiersP.add(repeatP); 700 701 JPanel withinP = new JPanel(new GridLayout(1,0)); 702 PlainDocument minWithinPD = new PlainDocument(); 703 minWithinPD.addDocumentListener(new DocumentListener() { 704 public void insertUpdate(DocumentEvent e) { 705 try { 706 Integer.parseInt(minWithinTF.getText().trim()); 707 maxWithinTF.setEnabled(minWithinTF.getText().trim().length() > 0); 708 } catch (NumberFormatException exception) { 709 GloDBUtils.printError("Within lower bound must be an integer."); 710 maxWithinTF.setEnabled(false); 711 } 712 } 713 public void removeUpdate(DocumentEvent e) { 714 try { 715 Integer.parseInt(minWithinTF.getText().trim()); 716 maxWithinTF.setEnabled(minWithinTF.getText().trim().length() > 0); 717 } catch (NumberFormatException exception) { 718 // don't need to again show error message, 719 // just disable maxWithinTF 720 maxWithinTF.setEnabled(false); 721 } 722 } 723 public void changedUpdate(DocumentEvent e) { ; } 724 }); 725 minWithinTF = new JTextField(minWithinPD, "", 0); 726 minWithinTF.setEnabled(false); 727 withinP.add(new JLabel("Within:")); 728 withinP.add(minWithinTF); 729 withinP.add(new JLabel("to", JLabel.CENTER)); 730 PlainDocument maxWithinPD = new PlainDocument(); 731 maxWithinPD.addDocumentListener(new DocumentListener() { 732 public void insertUpdate(DocumentEvent e) { 733 try { 734 Integer.parseInt(maxWithinTF.getText().trim()); 735 } catch (NumberFormatException exception) { 736 GloDBUtils.printError("Within upper bound must be an integer."); 737 } 738 } 739 public void removeUpdate(DocumentEvent e) { ; } 740 public void changedUpdate(DocumentEvent e) { ; } 741 }); 742 maxWithinTF = new JTextField(maxWithinPD, "", 0); 743 maxWithinTF.setEnabled(false); 744 withinP.add(maxWithinTF); 745 // qualifiersP.add(new JLabel("Within:")); 746 qualifiersP.add(withinP); 747 748 // pad the remaining spaces 749 // qualifiersP.add(new JLabel("")); 750 // qualifiersP.add(new JLabel("")); 751 optionsP.add(qualifiersP); 752 753 editP.add(optionsP, BorderLayout.CENTER); 754 editP.add(buttonP, BorderLayout.SOUTH); 755 756 // ************************************************* 757 // create panel with overall control buttons 758 JPanel bottomButtonsP = new JPanel(new GridLayout(1,0,100,5)); 759 // JToolBar bottomButtonsP = new JToolBar(); 760 // bottomButtonsP.setFloatable(false); 761 bottomButtonsP.setBorder(emptyBorder5); 762 // add compute button 763 computeB = new JButton("Compute"); 764 // computeB = new JButton(new ImageIcon("icons/compute.png")); 765 // computeB.setToolTipText("Compute"); 766 computeB.setEnabled(false); 767 computeB.addActionListener(new ActionListener() { 768 public void actionPerformed(ActionEvent e) { 769 if (trackID.length() == 0) { 770 setID(getID()); 771 if (trackID.length() == 0) { 772 GloDBUtils.printError("Query Builder requires a target track."); 773 return; 774 } 775 } 776 String cmd = "compute(\"" + queryTF.getText() + "\")"; 777 Root.runCommand(cmd, true); 778 } 779 }); 780 bottomButtonsP.add(computeB); 781 // add copy button 782 copyB = new JButton("Copy"); 783 // copyB = new JButton(new ImageIcon("icons/copy.png")); 784 // copyB.setToolTipText("Copy"); 785 copyB.setEnabled(false); 786 copyB.addActionListener(new ActionListener() { 787 public void actionPerformed(ActionEvent e) { 788 queryTF.selectAll(); 789 queryTF.copy(); 790 } 791 }); 792 bottomButtonsP.add(copyB); 793 // add cancel button 794 JButton closeB = new JButton("Close"); 795 // JButton closeB = new JButton(new ImageIcon("icons/close.png")); 796 // closeB.setToolTipText("Close"); 797 closeB.addActionListener(new ActionListener() { 798 public void actionPerformed(ActionEvent e) { 799 thisFrame.dispose(); 800 } 801 }); 802 bottomButtonsP.add(closeB); 803 804 // get the target Track ID, if necessary. If user doesn't set 805 // id here, then the "compute" button will ask. 806 if (GloDBUtils.isEmpty(id)) { 807 id = getID(); 808 // if user exited ID dialog with the cancel button then 809 // don't launch QueryBuilder 810 if (id == null) { 811 thisFrame.dispose(); 812 return; 813 } 814 else setID(id); 815 } else { 816 setID(id); 817 } 818 819 getContentPane().add(editP, BorderLayout.NORTH); 820 getContentPane().add(queryP, BorderLayout.CENTER); 821 getContentPane().add(bottomButtonsP, BorderLayout.SOUTH); 822 pack(); 823 824 // set the default window size 825 setSize(getSize().width, getSize().height + 70); 826 827 // initialize the edit panel 828 updateEditPanel(null); 829 830 // display the window 831 // setVisible(true); 832 show(); 833 } 834 835 private void updateEditPanel(QueryElement qElement) { 836 if (qElement == null) { // reset values 837 // if empyt list then clear operatorL selection, else 838 // default to first item in list 839 if (tableModel.getRowCount() == 0) { 840 operatorL.clearSelection(); 841 } else { 842 operatorL.setSelectedIndex(0); 843 } 844 minPosTF.setText(""); 845 maxPosTF.setText(""); 846 negateChB.setSelected(false); 847 trackL.setSelectedIndex(0); 848 sequenceChB.setSelected(false); 849 sequenceCB.setSelectedIndex(0); 850 sequenceCB.setEnabled(false); 851 minWidthTF.setText(""); 852 maxWidthTF.setText(""); 853 minSeqPosTF.setText(""); 854 maxSeqPosTF.setText(""); 855 minRepeatTF.setText(""); 856 maxRepeatTF.setText(""); 857 minWithinTF.setText(""); 858 maxWithinTF.setText(""); 859 } else { 860 /* 861 if (table.getSelectedRow() == 0) { 862 // first row selected so unselect operatorL 863 operatorL.clearSelection(); 864 minPosTF.setText(""); 865 maxPosTF.setText(""); 866 } else { 867 */ 868 // operatorL.setSelectedValue(qElement.operator, true); 869 operatorL.setSelectedIndex(qElement.operator); 870 minPosTF.setText(qElement.getMinPos()); 871 maxPosTF.setText(qElement.getMaxPos()); 872 /* 873 } 874 */ 875 negateChB.setSelected(qElement.negate); 876 if (qElement.isGrouped()) { 877 trackL.clearSelection(); 878 } else { 879 trackL.setSelectedValue(qElement.track, true); 880 } 881 882 if (qElement.sequence.length() > 0) { 883 sequenceChB.setSelected(true); 884 sequenceCB.setSelectedItem(qElement.sequence); 885 sequenceCB.setEnabled(true); 886 } else { 887 sequenceChB.setSelected(false); 888 sequenceCB.setSelectedIndex(0); 889 sequenceCB.setEnabled(false); 890 } 891 minWidthTF.setText(qElement.getMinLength()); 892 maxWidthTF.setText(qElement.getMaxLength()); 893 minSeqPosTF.setText(qElement.getMinSeqPos()); 894 maxSeqPosTF.setText(qElement.getMaxSeqPos()); 895 minRepeatTF.setText(qElement.getMinRepeat()); 896 maxRepeatTF.setText(qElement.getMaxRepeat()); 897 minWithinTF.setText(qElement.getMinWithin()); 898 maxWithinTF.setText(qElement.getMaxWithin()); 899 } 900 } 901 902 private void updateQueryElement(QueryElement qElement) { 903 // don't allow operator if first item 904 if (tableModel.getRowCount() == 0) { 905 // qElement.operator = ""; 906 qElement.operator = -1; 907 qElement.setMinPos(""); 908 qElement.setMaxPos(""); 909 } else { 910 // qElement.operator = (String) operatorL.getSelectedValue(); 911 qElement.operator = operatorL.getSelectedIndex(); 912 913 // only add min/maxPos if order operator ('POS'/'.') 914 if (QueryElement.isOrderOperator(qElement.operator)) { 915 qElement.setMinPos(minPosTF.getText().trim()); 916 if (minPosTF.getText().trim().length() > 0) { 917 qElement.setMaxPos(maxPosTF.getText().trim()); 918 } else { 919 qElement.setMaxPos(""); 920 } 921 } else { // not order operator, so clear min/maxPos 922 qElement.setMinPos(""); 923 qElement.setMaxPos(""); 924 } 925 } 926 qElement.negate = negateChB.isSelected(); 927 if (sequenceChB.isSelected()) { 928 qElement.sequence = (String) sequenceCB.getSelectedItem(); 929 } else { 930 qElement.sequence = ""; 931 } 932 qElement.setMinLength(minWidthTF.getText().trim()); 933 if (minWidthTF.getText().trim().length() > 0) { 934 qElement.setMaxLength(maxWidthTF.getText()); 935 } else { 936 qElement.setMaxLength(""); 937 } 938 qElement.setMinSeqPos(minSeqPosTF.getText().trim()); 939 if (minSeqPosTF.getText().trim().length() > 0) { 940 qElement.setMaxSeqPos(maxSeqPosTF.getText().trim()); 941 } else { 942 qElement.setMaxSeqPos(""); 943 } 944 qElement.setMinRepeat(minRepeatTF.getText().trim()); 945 if (minRepeatTF.getText().trim().length() > 0) { 946 qElement.setMaxRepeat(maxRepeatTF.getText().trim()); 947 } else { 948 qElement.setMaxRepeat(""); 949 } 950 qElement.setMinWithin(minWithinTF.getText().trim()); 951 if (minWithinTF.getText().trim().length() > 0) { 952 qElement.setMaxWithin(maxWithinTF.getText().trim()); 953 } else { 954 qElement.setMaxWithin(""); 955 } 956 } 957 958 /** 959 * Updates the query text field based on the current list 960 * items. 961 */ 962 private void updateQuery() { 963 String text; 964 965 if (trackID.length() == 0) { 966 text = "<invalid track> = "; 967 } else { 968 text = trackID + " = "; 969 } 970 971 if (tableModel.getRowCount() == 0) { 972 // no items so stop here 973 queryTF.setText(text); 974 return; 975 } 976 977 // there is at least 1 item, so make sure the first item 978 // doesn't have an operator 979 QueryElement qElement = tableModel.getRow(0); 980 // qElement.operator = ""; 981 qElement.operator = -1; 982 text += qElement.toString(); 983 984 for (int i = 1; i < tableModel.getRowCount(); i++) { 985 text += tableModel.getRow(i).toString(); 986 } 987 queryTF.setText(text); 988 } 989 990 private void setID(String id) { 991 if (id == null) id = ""; 992 trackID = id; 993 updateQuery(); 994 } 995 996 private String getID() { 997 String[] labels = {" Output Track ID:"}; 998 // use this to get the return value from FieldEditDialog 999 ArrayList textFields = new ArrayList(); 1000 textFields.add(new JTextField(20)); 1001 new FieldEditDialog("Output Track ID", labels, textFields); 1002 1003 // check "exit code" for FieldEditDialog, if false then exit 1004 Boolean exitCode = (Boolean) textFields.get(textFields.size()-1); 1005 if (! exitCode.booleanValue()) return null; 1006 1007 JTextField tf = (JTextField) textFields.get(0); 1008 return tf.getText(); 1009 } 1010 1011 private class ListTableModel extends AbstractTableModel { 1012 /** Stores the QueryElements that are in the table. */ 1013 private ArrayList items = new ArrayList(); 1014 1015 private String[] columnNames = {"Operator", "Negate", "Track/Group", "Qualifiers"}; 1016 1017 ////////////////////////////////////////// 1018 // Methods added to handle items ArrayList 1019 1020 /** 1021 * Append a new row to the end of the table. 1022 */ 1023 public void addRow(QueryElement qElement) { addRowAt(qElement, -1); } 1024 1025 1026 /** 1027 * Add new row to table. If row index is '-1' then add to end 1028 * of table. 1029 */ 1030 public void addRowAt(QueryElement qElement, int row) { 1031 if (row == -1) items.add(qElement); 1032 else items.add(row, qElement); 1033 1034 // there is now at least 1 item 1035 deleteAllB.setEnabled(true); 1036 computeB.setEnabled(true); 1037 copyB.setEnabled(true); 1038 operatorL.setEnabled(true); 1039 1040 fireTableDataChanged(); 1041 } 1042 1043 /** 1044 * Returns the QueryElement that represents the specified row 1045 * in the table. 1046 */ 1047 public QueryElement getRow(int row) { 1048 if (items.isEmpty()) return null; 1049 else return (QueryElement) items.get(row); 1050 } 1051 1052 /** 1053 * Removes the QueryElement that represents the specified row 1054 * in the table. 1055 */ 1056 public void removeRow(int row) { removeRows(row, row); } 1057 1058 /** 1059 * Removes the QueryElements that represent the rows 1060 * specified. 1061 */ 1062 public void removeRows(int[] rows) { 1063 removeRows(rows[0], rows[rows.length-1]); 1064 } 1065 1066 /** 1067 * Removes the QueryElements that represent the specified row 1068 * in the table. If the row is -1 then will clear the entire 1069 * table. 1070 */ 1071 public void removeRows(int minRow, int maxRow) { 1072 if (items.isEmpty()) { 1073 // XXX this should throw an exception, as this 1074 // condition should never occur 1075 GloDBUtils.printWarning("No items in table"); 1076 return; 1077 } 1078 1079 if (minRow == maxRow) { 1080 if (minRow == -1) items.clear(); // remove all rows 1081 else items.remove(minRow); // remove one row 1082 } else { 1083 // can't use removeRange() because protected method 1084 /* 1085 if (minRow > maxRow) { // make sure minRow < maxRow 1086 int tmp = maxRow; 1087 maxRow = minRow; 1088 minRow = tmp; 1089 } 1090 */ 1091 while (maxRow >= minRow) { 1092 items.remove(maxRow--); 1093 } 1094 } 1095 1096 1097 // test if list is now empty 1098 if (items.isEmpty()) { 1099 deleteAllB.setEnabled(false); 1100 computeB.setEnabled(false); 1101 copyB.setEnabled(false); 1102 // don't allow operator if first item 1103 operatorL.setEnabled(false); 1104 operatorL.clearSelection(); 1105 } 1106 1107 fireTableDataChanged(); 1108 } 1109 1110 /** Removes all rows from the table. */ 1111 public void removeAllRows() { removeRow(-1); } 1112 1113 1114 ////////////////////////////////////////// 1115 // Standard AbstractTableModel methods 1116 1117 public int getColumnCount() { return columnNames.length; } 1118 1119 public int getRowCount() { 1120 if (items.isEmpty()) return 0; 1121 else return items.size(); 1122 } 1123 1124 public String getColumnName(int col) { return columnNames[col]; } 1125 1126 /** Operator, Negate, Track, Qualifiers */ 1127 public Object getValueAt(int row, int col) { 1128 QueryElement qElement = getRow(row); 1129 switch (col) { 1130 case 0: return " " + qElement.getOperatorVal(); 1131 case 1: return ((qElement.negate) ? " !" : ""); 1132 case 2: 1133 if (qElement.isGrouped()) { 1134 String out = " ("; 1135 for (Iterator i = qElement.groupIterator(); i.hasNext();) { 1136 out += ((QueryElement) i.next()).toString(); 1137 } 1138 return out + ")"; 1139 } else { 1140 return " " + qElement.track; 1141 } 1142 default: return qElement.toStringQualifiers(); 1143 } 1144 } 1145 } 1146 1147 } // QueryBuilder.java