tableview.java

来自「Azureus is a powerful, full-featured, cr」· Java 代码 · 共 1,316 行 · 第 1/3 页

JAVA
1,316
字号
/*
 * Created on 2004/Apr/18
 *
 * Copyright (C) 2004 Aelitis SARL, All rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details ( see the LICENSE file ).
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * AELITIS, SARL au capital de 30,000 euros,
 * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
 */
package org.gudy.azureus2.ui.swt.views;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.*;

import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.config.ParameterListener;
import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.util.*;
import org.gudy.azureus2.plugins.ui.tables.TableContextMenuItem;
import org.gudy.azureus2.pluginsimpl.local.ui.tables.TableContextMenuItemImpl;
import org.gudy.azureus2.ui.swt.ImageRepository;
import org.gudy.azureus2.ui.swt.Messages;
import org.gudy.azureus2.ui.swt.Utils;
import org.gudy.azureus2.ui.swt.mainwindow.Colors;
import org.gudy.azureus2.ui.swt.views.table.*;
import org.gudy.azureus2.ui.swt.views.table.impl.TableRowImpl;
import org.gudy.azureus2.ui.swt.views.table.utils.*;


/** An IView with a SortableTable in it.  Handles composite/menu/table creation
 * and management.
 *
 * @author Olivier (Original PeersView/MyTorrentsView/etc code)
 * @author TuxPaper
 *         2004/Apr/20: Remove need for tableItemToObject
 */
public class TableView 
  extends AbstractIView 
  implements SortableTable,
             ParameterListener,
             ITableStructureModificationListener
{
  /** TableID (from {@link TableManager}) of the table this class is
   * handling.  Config settings are stored with the prefix of 
   * "Table.<i>TableID</i>"
   */
  protected String sTableID;
  /** Prefix for retrieving text from the properties file (MessageText)
   * Typically <i>TableID</i> + "View"
   */
  protected String sPropertiesPrefix;
  /** Column name to sort on if user hasn't chosen one yet 
   */
  protected String sDefaultSortOn;
  /** 1st column gap problem (Eclipse Bug 43910).  Set to true when table is 
   * using TableItem.setImage 
   */
  protected boolean bSkipFirstColumn;
  /** Sets the table row's height.  0 for default height.  Do NOT use when
   * TableItem uses setImage
   */
  protected int iCellHeight = 0;
  /** Sets the icon size when the row is initialized.  Any TableItem.setImage
   * will use this size
   */
  protected Point ptIconSize = null;

  /** Basic (pre-defined) Column Definitions */
  private TableColumnCore[] basicItems;
  /** All Column Definitions.  The array is not necessarily in column order */
  private TableColumnCore[] tableColumns;

  /** Composite for IView implementation */
  private Composite panel;
  /** Table for SortableTable implementation */
  private Table table;
  /** SWT style options for the creation of the Table */
  private int iTableStyle;
  /** Context Menu */
  private Menu menu;
  /** Context Menu specific to the column the mouse was on */
  private Menu menuThisColumn;

  /** Link DataSource to their row in the table.
   * key = DataSource
   * value = TableRowCore
   */
  private Map 		objectToSortableItem;
  private AEMonitor objectToSortableItem_mon 	= new AEMonitor( "TableView:OTSI" );

  /** Sorting functions */
  protected TableSorter sorter;
  private boolean bSortScheduled = false;
  /* position of mouse in table.  Used for context menu. */
  private int iMouseX = -1;


  /** For updating GUI.  
   * Some UI objects get updating every X cycles (user configurable) 
   */
  private int loopFactor;
  /** How often graphic cells get updated
   */
  private int graphicsUpdate = COConfigurationManager.getIntParameter("Graphics Update");
  /** Check Column Widths every 10 seconds on Pre 3.0RC1 on OSX if view is active.  
   * Other OSes can capture column width changes automatically */
  private int checkColumnWidthsEvery = (Constants.isOSX && SWT.getVersion() < 3054) ?
                                       10000 / COConfigurationManager.getIntParameter("GUI Refresh") :
                                       0;


  /**
   * Main Initializer
   * @param _sTableID Which table to handle (see {@link TableManager}).  Config settings are stored with the prefix of  "Table.<i>TableID</i>"
   * @param _sPropertiesPrefix Prefix for retrieving text from the properties file (MessageText).  Typically <i>TableID</i> + "View"
   * @param _basicItems Column Definitions
   * @param _sDefaultSortOn Column name to sort on if user hasn't chosen one yet
   * @param _iTableStyle SWT style constants used when creating the table
   */  
  public TableView(String _sTableID, 
                   String _sPropertiesPrefix,
                   TableColumnCore[] _basicItems,
                   String _sDefaultSortOn,
                   int _iTableStyle) {
    sTableID = _sTableID;
    basicItems = _basicItems;
    sPropertiesPrefix = _sPropertiesPrefix;
    sDefaultSortOn = _sDefaultSortOn;
    iTableStyle = _iTableStyle;

    objectToSortableItem = new HashMap();
  }

  /**
   * Main Initializer. Table Style will be SWT.SINGLE | SWT.FULL_SELECTION
   *
   * @param _sTableID Which table to handle (see {@link TableManager}).  Config settings are stored with the prefix of  "Table.<i>TableID</i>"
   * @param _sPropertiesPrefix Prefix for retrieving text from the properties file (MessageText).  Typically <i>TableID</i> + "View"
   * @param _basicItems Column Definitions
   * @param _sDefaultSortOn Column name to sort on if user hasn't chosen one yet
   */  
  public TableView(String _sTableID, 
                   String _sPropertiesPrefix,
                   TableColumnCore[] _basicItems,
                   String _sDefaultSortOn) {
    this(_sTableID, _sPropertiesPrefix, _basicItems, _sDefaultSortOn, 
         SWT.SINGLE | SWT.FULL_SELECTION);
  }

  private void initializeColumnDefs() {
    // XXX Adding Columns only has to be done once per TableID.  
    // Doing it more than once won't harm anything, but it's a waste.
    TableColumnManager tcManager = TableColumnManager.getInstance();
    for (int i = 0; i < basicItems.length; i++) {
      tcManager.addColumn(basicItems[i]);
    }

    // fixup order
    tcManager.ensureIntegrety(sTableID);
    
    tableColumns = tcManager.getAllTableColumnCoreAsArray(sTableID);
  }

  /**
   * This method is called when the view is instanciated, it should initialize 
   * all GUI components. Must NOT be blocking, or it'll freeze the whole GUI.
   * Caller is the GUI Thread.
   *
   * @param composite the parent composite. Each view should create a child 
   *        composite, and then use this child composite to add all elements to.
   */
  public void initialize(Composite composite) {
    panel = createMainPanel(composite);

    menu = createMenu();
    fillMenu(menu);
    table = createTable();
    initializeTable(table);

    COConfigurationManager.addParameterListener("Graphics Update", this);
    Colors.getInstance().addColorsChangedListener(this);

    // So all TableView objects of the same TableID have the same columns, and column widths, etc
    TableStructureEventDispatcher.getInstance(sTableID).addListener(this);
  }
  
  
  /** Creates a composite within the specified composite and sets its layout
   * to a default FillLayout().
   *
   * @param composite to create your Composite under
   * @return The newly created composite
   */
  public Composite createMainPanel(Composite composite) {
    panel = new Composite(composite,SWT.NULL);
    GridLayout layout = new GridLayout();
    layout.marginHeight = 0;
    layout.marginWidth = 0;
    panel.setLayout(layout);

    return panel;
  }
    
  /** Creates the Table.
   *
   * @return The created Table.
   */
  public Table createTable() {
    table = new Table(panel, iTableStyle);
    table.setLayoutData(new GridData(GridData.FILL_BOTH));

    return table;
  }
  
  /** Creates the legend Composite
   * 
   * @return The created Legend Composite
   */
  public Composite createLegendComposite(Color[] colors,String[] keys) {
  	if(colors.length != keys.length) return null;
  	
  	Composite legend = new Composite(panel,SWT.NULL);
  	legend.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
  	
  	
    GridLayout layout = new GridLayout();
    int numColumns = colors.length * 2;
    if(numColumns > 10) numColumns = 10;
    layout.numColumns = numColumns;
    legend.setLayout(layout);
    GridData data;
    
    for(int i = 0 ; i < colors.length ; i++) {
    	Label lblColor = new Label(legend,SWT.BORDER);
    	lblColor.setBackground(colors[i]);
    	data = new GridData();
    	data.widthHint = 20;
    	data.heightHint = 10;
    	lblColor.setLayoutData(data);
    	
    	Label lblDesc = new Label(legend,SWT.NULL);
    	Messages.setLanguageText(lblDesc,keys[i]);
    	data = new GridData();
    	data.widthHint = 150;
    	lblDesc.setLayoutData(data);
    }
    
    return legend;
  	
  }

  /** Sets up the sorter, columns, and context menu.
   *
   * @param table Table to be initialized
   */
  public void initializeTable(final Table table) {
    initializeColumnDefs();

    table.setLinesVisible(false);
    table.setMenu(menu);
    table.setData("Name", sTableID);
    table.setData("TableView", this);

    sorter = new TableSorter(this, sTableID, sDefaultSortOn, COConfigurationManager.getBooleanParameter( "config.style.table.sortDefaultAscending" ));

    // Pre 3.0RC1 SWT on OSX doesn't call this!! :(
    ControlListener resizeListener = new ControlAdapter() {
      public void controlResized(ControlEvent e) {
        TableColumn column = (TableColumn) e.widget;
        if (column == null || column.isDisposed())
          return;

        TableColumnCore tc = (TableColumnCore)column.getData("TableColumnCore");
        if (tc != null)
          tc.setWidth(column.getWidth());

        int columnNumber = table.indexOf(column);
        locationChanged(columnNumber);
      }
    };

    // Setup table
    // -----------
    // Add 1 to position because we make a non resizable 0-sized 1st column
    // to fix the 1st column gap problem (Eclipse Bug 43910)
    if (bSkipFirstColumn) {
      TableColumn tc = new TableColumn(table, SWT.NULL);
      tc.setWidth(0);
      tc.setResizable(false);
    }

    //Create all columns
    for (int i = 0; i < tableColumns.length; i++) {
      int position = tableColumns[i].getPosition();
      if (position != -1) {
        new TableColumn(table, SWT.NULL);
      }
    }
    //Assign length and titles
    //We can only do it after ALL columns are created, as position (order)
    //may not be in the natural order (if the user re-order the columns).
    for (int i = 0; i < tableColumns.length; i++) {
      int position = tableColumns[i].getPosition();
      if (position == -1)
        continue;

      String sName = tableColumns[i].getName();
      // +1 for Eclipse Bug 43910 (see above)
      // user has reported a problem here with index-out-of-bounds - not sure why
      // but putting in a preventative check so that hopefully the view still opens
      // so they can fix it
      
      int	adjusted_position = position + (bSkipFirstColumn ? 1 : 0);
      
      if ( adjusted_position >= table.getColumnCount()){
      	
      	Debug.out( "Incorrect table column setup, skipping column '" + sName + "'" );
      	
      	continue;
      }
      
      TableColumn column = table.getColumn(adjusted_position);
      Messages.setLanguageText(column, tableColumns[i].getTitleLanguageKey());
      column.setAlignment(tableColumns[i].getSWTAlign());
      column.setWidth(tableColumns[i].getWidth());
      column.setData("TableColumnCore", tableColumns[i]);
      column.setData("configName", "Table." + sTableID + "." + sName);
      column.setData("Name", sName);

      column.addControlListener(resizeListener);
      // At the time of writing this SWT (3.0RC1) on OSX doesn't call the 
      // selection listener for tables
      column.addListener(SWT.Selection, new ColumnListener(tableColumns[i]));
    }

    table.addPaintListener(new PaintListener() {
      public void paintControl(PaintEvent event) {
        if(event.width == 0 || event.height == 0) return;
        doPaint(event.gc);
      }
    });
    
    // Deselect rows if user clicks on a black spot (a spot with no row)
    table.addMouseListener(new MouseAdapter() {
      public void mouseDown(MouseEvent e) {
        iMouseX = e.x;
        try {
          if (table.getItemCount() <= 0)
            return;

          // skip if outside client area (ie. scrollbars)
          Rectangle rTableArea = table.getClientArea();
          //System.out.println("Mouse="+iMouseX+"x"+e.y+";TableArea="+rTableArea);
          Point pMousePosition = new Point(e.x, e.y);
          if (rTableArea.contains(pMousePosition)) {
            TableItem ti = table.getItem(table.getItemCount() - 1);
            Rectangle cellBounds = ti.getBounds(table.getColumnCount() - 1);
            // OSX returns 0 size if the cell is not on screen (sometimes? all the time?)
            if (cellBounds.width <= 0 || cellBounds.height <= 0)
              return;
            //System.out.println("cellbounds="+cellBounds);
            if (e.x > cellBounds.x + cellBounds.width ||
                e.y > cellBounds.y + cellBounds.height) {
              table.deselectAll();
            }
/*        // This doesn't work because of OS inconsistencies when table is scrolled
          // Re-enable once SWT fixes the problem
            TableItem ti = table.getItem(pMousePosition);
            if (ti == null)
              table.deselectAll();
*/
          }
        } catch (Exception ex) {
          System.out.println("MouseDownError");
          Debug.printStackTrace( ex );
        }
      }
    });
    
    // XXX this may not be needed if all platforms process mouseDown
    //     before the menu
    table.addMouseMoveListener(new MouseMoveListener() {
      public void mouseMove(MouseEvent e) {
        iMouseX = e.x;
      }
    });

  	// Implement a "fake" tooltip
  	final Listener labelListener = new Listener () {
  		public void handleEvent (Event event) {
  		  Shell shell;
  		  if (event.widget instanceof Control)
  		    shell = ((Control)event.widget).getShell();
  		  else
  		    shell = (Shell)event.widget;
  			switch (event.type) {
  				case SWT.MouseDown:
  			  case SWT.MouseDoubleClick:
  					Event e = new Event ();
  					TableItem ti = (TableItem)shell.getData("_TABLEITEM");
  					if (!ti.isDisposed()) {
    					e.item = ti;
    					table.setSelection(table.indexOf(ti));
    					table.notifyListeners((event.type == SWT.MouseDown) ? SWT.Selection : SWT.DefaultSelection, e);
   					}
   					if (table != null && !table.isDisposed())
     					table.setFocus();
  					// fall through
  				case SWT.MouseMove:
  				case SWT.MouseExit:

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?