1 19 20 package org.netbeans.modules.ruby.rubyproject.ui.customizer; 21 22 import java.awt.*; 23 import java.awt.event.ActionEvent ; 24 import java.awt.event.ActionListener ; 25 import java.io.File ; 26 import java.net.URI ; 27 import java.net.URL ; 28 import java.util.ArrayList ; 29 import java.util.Iterator ; 30 import java.util.HashSet ; 31 import java.util.Set ; 32 import java.util.Vector ; 33 import java.text.MessageFormat ; 34 import javax.swing.*; 35 import javax.swing.JButton ; 36 import javax.swing.event.ListSelectionEvent ; 37 import javax.swing.event.ListSelectionListener ; 38 import javax.swing.event.CellEditorListener ; 39 import javax.swing.event.ChangeEvent ; 40 import javax.swing.table.DefaultTableCellRenderer ; 41 import javax.swing.table.DefaultTableModel ; 42 import org.netbeans.api.project.ProjectUtils; 43 import org.netbeans.api.project.SourceGroup; 44 import org.netbeans.api.project.Sources; 45 import org.netbeans.modules.ruby.rubyproject.RubyProject; 46 import org.netbeans.modules.ruby.rubyproject.ui.FoldersListSettings; 47 import org.netbeans.api.project.FileOwnerQuery; 48 import org.netbeans.api.project.Project; 49 import org.netbeans.api.project.ProjectInformation; 50 import org.netbeans.modules.ruby.rubyproject.SourceRoots; 51 import org.openide.DialogDisplayer; 52 import org.openide.DialogDescriptor; 53 import org.openide.NotifyDescriptor; 54 import org.openide.filesystems.FileObject; 55 import org.openide.filesystems.FileUtil; 56 import org.openide.util.NbBundle; 57 import org.openide.util.HelpCtx; 58 59 63 public final class RubySourceRootsUi { 64 65 public static DefaultTableModel createModel( SourceRoots roots ) { 66 67 String [] rootLabels = roots.getRootNames(); 68 String [] rootProps = roots.getRootProperties(); 69 URL [] rootURLs = roots.getRootURLs(); 70 Object [][] data = new Object [rootURLs.length] [2]; 71 for (int i=0; i< rootURLs.length; i++) { 72 data[i][0] = new File (URI.create (rootURLs[i].toExternalForm())); 73 data[i][1] = roots.getRootDisplayName(rootLabels[i], rootProps[i]); 74 } 75 return new SourceRootsModel(data); 76 77 } 78 79 public static EditMediator registerEditMediator( RubyProject master, 80 SourceRoots sourceRoots, 81 JTable rootsList, 82 JButton addFolderButton, 83 JButton removeButton, 84 JButton upButton, 85 JButton downButton) { 86 87 EditMediator em = new EditMediator( master, 88 sourceRoots, 89 rootsList, 90 addFolderButton, 91 removeButton, 92 upButton, 93 downButton); 94 95 addFolderButton.addActionListener( em ); 98 removeButton.addActionListener( em ); 99 upButton.addActionListener( em ); 100 downButton.addActionListener( em ); 101 rootsList.getSelectionModel().addListSelectionListener( em ); 103 DefaultCellEditor editor = new DefaultCellEditor(new JTextField()); 104 editor.addCellEditorListener (em); 105 rootsList.setDefaultRenderer( File .class, new FileRenderer (FileUtil.toFile(master.getProjectDirectory()))); 106 rootsList.setDefaultEditor(String .class, editor); 107 em.valueChanged( null ); 109 110 DefaultTableModel model = (DefaultTableModel )rootsList.getModel(); 111 String [] columnNames = new String [2]; 112 columnNames[0] = NbBundle.getMessage( RubySourceRootsUi.class,"CTL_PackageFolders"); 113 columnNames[1] = NbBundle.getMessage( RubySourceRootsUi.class,"CTL_PackageLabels"); 114 model.setColumnIdentifiers(columnNames); 115 rootsList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 116 117 return em; 118 } 119 120 124 public static void showIllegalRootsDialog (Set roots) { 125 JButton closeOption = new JButton (NbBundle.getMessage(RubySourceRootsUi.class,"CTL_RubySourceRootsUi_Close")); 126 closeOption.getAccessibleContext ().setAccessibleDescription (NbBundle.getMessage(RubySourceRootsUi.class,"AD_RubySourceRootsUi_Close")); 127 JPanel warning = new WarningDlg (roots); 128 String message = NbBundle.getMessage(RubySourceRootsUi.class,"MSG_InvalidRoot"); 129 JOptionPane optionPane = new JOptionPane (new Object [] {message, warning}, 130 JOptionPane.WARNING_MESSAGE, 131 0, 132 null, 133 new Object [0], 134 null); 135 optionPane.getAccessibleContext().setAccessibleDescription (NbBundle.getMessage(RubySourceRootsUi.class,"AD_InvalidRootDlg")); 136 DialogDescriptor dd = new DialogDescriptor (optionPane, 137 NbBundle.getMessage(RubySourceRootsUi.class,"TITLE_InvalidRoot"), 138 true, 139 new Object [] { 140 closeOption, 141 }, 142 closeOption, 143 DialogDescriptor.DEFAULT_ALIGN, 144 null, 145 null); 146 DialogDisplayer.getDefault().notify(dd); 147 } 148 149 151 public static class EditMediator implements ActionListener , ListSelectionListener , CellEditorListener { 152 153 154 final JTable rootsList; 155 final JButton addFolderButton; 156 final JButton removeButton; 157 final JButton upButton; 158 final JButton downButton; 159 private final Project project; 160 private final SourceRoots sourceRoots; 161 private final Set ownedFolders; 162 private DefaultTableModel rootsModel; 163 private EditMediator relatedEditMediator; 164 private File lastUsedDir; 166 167 public EditMediator( RubyProject master, 168 SourceRoots sourceRoots, 169 JTable rootsList, 170 JButton addFolderButton, 171 JButton removeButton, 172 JButton upButton, 173 JButton downButton) { 174 175 if ( !( rootsList.getModel() instanceof DefaultTableModel ) ) { 176 throw new IllegalArgumentException ( "Jtable's model has to be of class DefaultTableModel" ); } 178 179 this.rootsList = rootsList; 180 this.addFolderButton = addFolderButton; 181 this.removeButton = removeButton; 182 this.upButton = upButton; 183 this.downButton = downButton; 184 this.ownedFolders = new HashSet (); 185 186 this.project = master; 187 this.sourceRoots = sourceRoots; 188 189 this.ownedFolders.clear(); 190 this.rootsModel = (DefaultTableModel )rootsList.getModel(); 191 Vector data = rootsModel.getDataVector(); 192 for (Iterator it = data.iterator(); it.hasNext();) { 193 Vector row = (Vector ) it.next (); 194 File f = (File ) row.elementAt(0); 195 this.ownedFolders.add (f); 196 } 197 } 198 199 public void setRelatedEditMediator(EditMediator rem) { 200 this.relatedEditMediator = rem; 201 } 202 203 205 207 public void actionPerformed( ActionEvent e ) { 208 209 Object source = e.getSource(); 210 211 if ( source == addFolderButton ) { 212 213 JFileChooser chooser = new JFileChooser(); 215 FileUtil.preventFileChooserSymlinkTraversal(chooser, null); 216 chooser.setFileSelectionMode( JFileChooser.DIRECTORIES_ONLY ); 217 chooser.setMultiSelectionEnabled( true ); 218 if (this.sourceRoots.isTest()) { 219 chooser.setDialogTitle( NbBundle.getMessage( RubySourceRootsUi.class, "LBL_TestFolder_DialogTitle" )); } 221 else { 222 chooser.setDialogTitle( NbBundle.getMessage( RubySourceRootsUi.class, "LBL_SourceFolder_DialogTitle" )); } 224 File curDir = this.lastUsedDir; 225 if (curDir == null) { 226 curDir = FileUtil.toFile(this.project.getProjectDirectory()); 227 } 228 if (curDir != null) { 229 chooser.setCurrentDirectory (curDir); 230 } 231 int option = chooser.showOpenDialog( SwingUtilities.getWindowAncestor( addFolderButton ) ); 233 if ( option == JFileChooser.APPROVE_OPTION ) { 234 curDir = chooser.getCurrentDirectory(); 235 if (curDir != null) { 236 this.lastUsedDir = curDir; 237 if (this.relatedEditMediator != null) { 238 this.relatedEditMediator.lastUsedDir = curDir; 239 } 240 } 241 File files[] = chooser.getSelectedFiles(); 242 addFolders( files ); 243 } 244 245 } 246 else if ( source == removeButton ) { 247 removeElements(); 248 } 249 else if ( source == upButton ) { 250 moveUp(); 251 } 252 else if ( source == downButton ) { 253 moveDown(); 254 } 255 } 256 257 259 261 public void valueChanged( ListSelectionEvent e ) { 262 263 int[] si = rootsList.getSelectedRows(); 264 265 267 269 271 boolean edit = si != null && si.length > 0; 273 274 boolean remove = si != null && si.length > 0; 276 278 boolean up = si != null && si.length > 0 && si[0] != 0; 281 282 boolean down = si != null && si.length > 0 && si[si.length-1] !=rootsList.getRowCount() - 1; 285 286 removeButton.setEnabled( remove ); 287 upButton.setEnabled( up ); 288 downButton.setEnabled( down ); 289 290 292 } 293 294 public void editingCanceled(ChangeEvent e) { 295 296 } 297 298 public void editingStopped(ChangeEvent e) { 299 } 301 302 private void addFolders( File files[] ) { 303 int[] si = rootsList.getSelectedRows(); 304 int lastIndex = si == null || si.length == 0 ? -1 : si[si.length - 1]; 305 ListSelectionModel selectionModel = this.rootsList.getSelectionModel(); 306 selectionModel.clearSelection(); 307 Set rootsFromOtherProjects = new HashSet (); 308 Set rootsFromRelatedSourceRoots = new HashSet (); 309 out: for( int i = 0; i < files.length; i++ ) { 310 File normalizedFile = FileUtil.normalizeFile(files[i]); 311 Project p; 312 if (ownedFolders.contains(normalizedFile)) { 313 Vector dataVector = rootsModel.getDataVector(); 314 for (int j=0; j<dataVector.size();j++) { 315 File f = (File )((Vector )dataVector.elementAt(j)).elementAt(0); 317 if (f.equals(normalizedFile)) { 318 selectionModel.addSelectionInterval(j,j); 319 } 320 } 321 } 322 else if (this.relatedEditMediator != null && this.relatedEditMediator.ownedFolders.contains(normalizedFile)) { 323 rootsFromRelatedSourceRoots.add (normalizedFile); 324 continue; 325 } 326 if ((p=FileOwnerQuery.getOwner(normalizedFile.toURI()))!=null && !p.getProjectDirectory().equals(project.getProjectDirectory())) { 327 final Sources sources = (Sources) p.getLookup().lookup (Sources.class); 328 if (sources == null) { 329 rootsFromOtherProjects.add (normalizedFile); 330 continue; 331 } 332 final SourceGroup[] sourceGroups = sources.getSourceGroups(Sources.TYPE_GENERIC); 333 final SourceGroup[] javaGroups = sources.getSourceGroups(RubyProject.SOURCES_TYPE_RUBY); 334 final SourceGroup[] groups = new SourceGroup [sourceGroups.length + javaGroups.length]; 335 System.arraycopy(sourceGroups,0,groups,0,sourceGroups.length); 336 System.arraycopy(javaGroups,0,groups,sourceGroups.length,javaGroups.length); 337 final FileObject projectDirectory = p.getProjectDirectory(); 338 final FileObject fileObject = FileUtil.toFileObject(normalizedFile); 339 if (projectDirectory == null || fileObject == null) { 340 rootsFromOtherProjects.add (normalizedFile); 341 continue; 342 } 343 for (int j=0; j<groups.length; j++) { 344 final FileObject sgRoot = groups[j].getRootFolder(); 345 if (fileObject.equals(sgRoot)) { 346 rootsFromOtherProjects.add (normalizedFile); 347 continue out; 348 } 349 if (!projectDirectory.equals(sgRoot) && FileUtil.isParentOf(sgRoot, fileObject)) { 350 rootsFromOtherProjects.add (normalizedFile); 351 continue out; 352 } 353 } 354 } 355 int current = lastIndex + 1 + i; 356 rootsModel.insertRow( current, new Object [] {normalizedFile, sourceRoots.createInitialDisplayName(normalizedFile)}); selectionModel.addSelectionInterval(current,current); 358 this.ownedFolders.add (normalizedFile); 359 } 360 if (rootsFromOtherProjects.size() > 0 || rootsFromRelatedSourceRoots.size() > 0) { 361 rootsFromOtherProjects.addAll(rootsFromRelatedSourceRoots); 362 showIllegalRootsDialog (rootsFromOtherProjects); 363 } 364 } 366 367 private void removeElements() { 368 369 int[] si = rootsList.getSelectedRows(); 370 371 if( si == null || si.length == 0 ) { 372 assert false : "Remove button should be disabled"; } 374 375 for( int i = si.length - 1 ; i >= 0 ; i-- ) { 377 this.ownedFolders.remove(((Vector )rootsModel.getDataVector().elementAt(si[i])).elementAt(0)); 378 rootsModel.removeRow( si[i] ); 379 } 380 381 382 if ( rootsModel.getRowCount() != 0) { 383 int selectedIndex = si[si.length - 1] - si.length + 1; 385 if ( selectedIndex > rootsModel.getRowCount() - 1) { 386 selectedIndex = rootsModel.getRowCount() - 1; 387 } 388 rootsList.setRowSelectionInterval( selectedIndex, selectedIndex ); 389 } 390 391 393 } 394 395 private void moveUp() { 396 397 int[] si = rootsList.getSelectedRows(); 398 399 if( si == null || si.length == 0 ) { 400 assert false : "MoveUp button should be disabled"; } 402 403 ListSelectionModel selectionModel = this.rootsList.getSelectionModel(); 405 selectionModel.clearSelection(); 406 for( int i = 0; i < si.length; i++ ) { 407 Vector item = (Vector ) rootsModel.getDataVector().elementAt(si[i]); 408 int newIndex = si[i]-1; 409 rootsModel.removeRow( si[i] ); 410 rootsModel.insertRow( newIndex, item ); 411 selectionModel.addSelectionInterval(newIndex,newIndex); 412 } 413 } 415 416 private void moveDown() { 417 418 int[] si = rootsList.getSelectedRows(); 419 420 if( si == null || si.length == 0 ) { 421 assert false : "MoveDown button should be disabled"; } 423 424 ListSelectionModel selectionModel = this.rootsList.getSelectionModel(); 426 selectionModel.clearSelection(); 427 for( int i = si.length -1 ; i >= 0 ; i-- ) { 428 Vector item = (Vector ) rootsModel.getDataVector().elementAt(si[i]); 429 int newIndex = si[i] + 1; 430 rootsModel.removeRow( si[i] ); 431 rootsModel.insertRow( newIndex, item ); 432 selectionModel.addSelectionInterval(newIndex,newIndex); 433 } 434 } 436 437 438 } 439 440 private static class SourceRootsModel extends DefaultTableModel { 441 442 public SourceRootsModel (Object [][] data) { 443 super (data,new Object []{"location","label"}); } 445 446 public boolean isCellEditable(int row, int column) { 447 return column == 1; 448 } 449 450 public Class getColumnClass(int columnIndex) { 451 switch (columnIndex) { 452 case 0: 453 return File .class; 454 case 1: 455 return String .class; 456 default: 457 return super.getColumnClass (columnIndex); 458 } 459 } 460 } 461 462 private static class FileRenderer extends DefaultTableCellRenderer { 463 464 private File projectFolder; 465 466 public FileRenderer (File projectFolder) { 467 this.projectFolder = projectFolder; 468 } 469 470 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,int row, int column) { 471 String displayName; 472 if (value instanceof File ) { 473 File root = (File ) value; 474 String pfPath = projectFolder.getAbsolutePath() + File.separatorChar; 475 String srPath = root.getAbsolutePath(); 476 if (srPath.startsWith(pfPath)) { 477 displayName = srPath.substring(pfPath.length()); 478 } 479 else { 480 displayName = srPath; 481 } 482 } 483 else { 484 displayName = null; 485 } 486 Component c = super.getTableCellRendererComponent(table, displayName, isSelected, hasFocus, row, column); 487 if (c instanceof JComponent) { 488 ((JComponent) c).setToolTipText (displayName); 489 } 490 return c; 491 } 492 493 } 494 495 private static class WarningDlg extends JPanel { 496 497 public WarningDlg (Set invalidRoots) { 498 this.initGui (invalidRoots); 499 } 500 501 private void initGui (Set invalidRoots) { 502 setLayout( new GridBagLayout ()); 503 JLabel label = new JLabel (); 504 label.setText (NbBundle.getMessage(RubySourceRootsUi.class,"LBL_InvalidRoot")); 505 label.setDisplayedMnemonic(NbBundle.getMessage(RubySourceRootsUi.class,"MNE_InvalidRoot").charAt(0)); 506 GridBagConstraints c = new GridBagConstraints(); 507 c.gridx = GridBagConstraints.RELATIVE; 508 c.gridy = GridBagConstraints.RELATIVE; 509 c.gridwidth = GridBagConstraints.REMAINDER; 510 c.fill = GridBagConstraints.HORIZONTAL; 511 c.anchor = GridBagConstraints.NORTHWEST; 512 c.weightx = 1.0; 513 c.insets = new Insets (12,0,6,0); 514 ((GridBagLayout)this.getLayout()).setConstraints(label,c); 515 this.add (label); 516 JList roots = new JList (invalidRoots.toArray()); 517 roots.setCellRenderer (new InvalidRootRenderer(true)); 518 JScrollPane p = new JScrollPane (roots); 519 c = new GridBagConstraints(); 520 c.gridx = GridBagConstraints.RELATIVE; 521 c.gridy = GridBagConstraints.RELATIVE; 522 c.gridwidth = GridBagConstraints.REMAINDER; 523 c.fill = GridBagConstraints.BOTH; 524 c.anchor = GridBagConstraints.NORTHWEST; 525 c.weightx = c.weighty = 1.0; 526 c.insets = new Insets (0,0,12,0); 527 ((GridBagLayout)this.getLayout()).setConstraints(p,c); 528 this.add (p); 529 label.setLabelFor(roots); 530 roots.getAccessibleContext().setAccessibleDescription (NbBundle.getMessage(RubySourceRootsUi.class,"AD_InvalidRoot")); 531 JLabel label2 = new JLabel (); 532 label2.setText (NbBundle.getMessage(RubySourceRootsUi.class,"MSG_InvalidRoot2")); 533 c = new GridBagConstraints(); 534 c.gridx = GridBagConstraints.RELATIVE; 535 c.gridy = GridBagConstraints.RELATIVE; 536 c.gridwidth = GridBagConstraints.REMAINDER; 537 c.fill = GridBagConstraints.HORIZONTAL; 538 c.anchor = GridBagConstraints.NORTHWEST; 539 c.weightx = 1.0; 540 c.insets = new Insets (0,0,0,0); 541 ((GridBagLayout)this.getLayout()).setConstraints(label2,c); 542 this.add (label2); 543 } 544 545 private static class InvalidRootRenderer extends DefaultListCellRenderer { 546 547 private boolean projectConflict; 548 549 public InvalidRootRenderer (boolean projectConflict) { 550 this.projectConflict = projectConflict; 551 } 552 553 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 554 File f = (File ) value; 555 String message = f.getAbsolutePath(); 556 if (projectConflict) { 557 Project p = FileOwnerQuery.getOwner(f.toURI()); 558 if (p!=null) { 559 ProjectInformation pi = ProjectUtils.getInformation(p); 560 String projectName = pi.getDisplayName(); 561 message = MessageFormat.format (NbBundle.getMessage(RubySourceRootsUi.class,"TXT_RootOwnedByProject"), new Object [] { 562 message, 563 projectName}); 564 } 565 } 566 return super.getListCellRendererComponent(list, message, index, isSelected, cellHasFocus); 567 } 568 } 569 } 570 571 } 572 | Popular Tags |