/* Copyright (c) 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package sample.spreadsheet.gui; import com.google.gdata.client.spreadsheet.SpreadsheetService; import com.google.gdata.data.spreadsheet.CellEntry; import com.google.gdata.data.spreadsheet.CellFeed; import com.google.gdata.util.ServiceException; import java.awt.BorderLayout; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.net.URL; import java.util.HashMap; import java.util.Map; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.table.AbstractTableModel; /** * Widget for displaying and editing a spreadsheet. * * This is just for demonstrative purposes, it is not very featureful. * * */ public class CellBasedSpreadsheetPanel extends JPanel { /** The underlying Google Spreadsheets service. */ private SpreadsheetService service; /** The URL of the cell feed. */ private URL cellFeedUrl; private SpreadsheetTableModel model; private JTable table; private JButton refreshButton; /** * Creates the cell-based spreadsheet widget. * * @param service the Google Spreadsheets service to connect to * @param cellFeedUrl the URL of the cell feed */ public CellBasedSpreadsheetPanel( SpreadsheetService service, URL cellFeedUrl) { this.service = service; this.cellFeedUrl = cellFeedUrl; model = new SpreadsheetTableModel(); initializeGui(); } /** * Sets a cell at the specified row and column. * * @return the newly set cell, returned back from the server */ private CellEntry actuallySetCell(int row, int col, String valueOrFormula) { try { // This method ignores collisions. Use update() if you are afraid // of overwriting other users' data. CellEntry entry = new CellEntry(row, col, valueOrFormula); return service.insert(cellFeedUrl, entry); } catch (IOException ioe) { SpreadsheetApiDemo.showErrorBox(ioe); return null; } catch (ServiceException se) { SpreadsheetApiDemo.showErrorBox(se); return null; } } /** * Gets a list of all cells. * * This just calls the method getAllCells in cellFellFeedHelper. */ private CellFeed getCellFeed() { try { return service.getFeed(cellFeedUrl, CellFeed.class); } catch (IOException ioe) { SpreadsheetApiDemo.showErrorBox(ioe); return null; } catch (ServiceException se) { SpreadsheetApiDemo.showErrorBox(se); return null; } } // -- Mostly GUI code from here on down -- /** * The Swing model for the spreadsheet. * * This is mostly GUI code. */ private class SpreadsheetTableModel extends AbstractTableModel { /** All the cells indexed for easy display purposes. */ private Map<Point, CellEntry> cells = new HashMap<Point, CellEntry>(); /** The last row to display (1-based). */ private int maxRow; /** The last column to display (1-based). */ private int maxCol; public SpreadsheetTableModel() { refresh(); } /** * Load all the cells from Google Spreadsheets. */ public synchronized void refresh() { cells.clear(); CellFeed cellFeed = getCellFeed(); if (cellFeed != null) { for (CellEntry entry : cellFeed.getEntries()) { doAddCell(entry); } } int oldMaxRow = maxRow; int oldMaxCol = maxCol; maxRow = cellFeed.getRowCount(); maxCol = cellFeed.getColCount(); fireTableDataChanged(); if (maxRow != oldMaxRow || maxCol != oldMaxCol) { fireTableStructureChanged(); } } /** * Implements the Swing method for handling cell edits. */ public void setValueAt(Object value, int screenRow, int screenCol) { int row = screenRow + 1; // account for the fact Swing is 0-indexed int col = screenCol + 1; // Pop up a little window to indicate that this is busy. JFrame statusIndicatorFrame = new JFrame(); statusIndicatorFrame.getContentPane().add(new JButton("Updating...")); statusIndicatorFrame.setVisible(true); statusIndicatorFrame.setSize(200, 100); CellEntry entry = actuallySetCell(row, col, value.toString()); if (entry != null) { doAddCell(entry); } statusIndicatorFrame.dispose(); fireTableDataChanged(); } /** Tells Swing how many rows are in this table. */ public int getRowCount() { return maxRow; // Allow user to insert up to two rows } /** Tells Swing how many columns are in this table. */ public int getColumnCount() { return maxCol; } /** Gets the cached version of the specified cell. */ public CellEntry getCell(int row, int col) { return cells.get(new Point(row, col)); } /** Tells Swing the value at a particular location. */ public Object getValueAt(int screenRow, int screenCol) { CellEntry cell = getCell(screenRow + 1, screenCol + 1); if (cell == null) { return null; } else { return cell.getCell().getValue(); } } public void addCell(CellEntry entry) { doAddCell(entry); } private void doAddCell(CellEntry entry) { int row = entry.getCell().getRow(); int col = entry.getCell().getCol(); cells.put(new Point(row, col), entry); } /** Tells Swing whether the cell is editable. */ public boolean isCellEditable(int screenRow, int screenCol) { CellEntry cell = getCell(screenRow + 1, screenCol + 1); if (cell == null) { // Although this can be wrong, assume editable unless told otherwise. return true; } else { return cell.getEditLink() != null; } } } public static JFrame createWindow( SpreadsheetService service, URL cellFeedUrl) { JFrame frame = new JFrame(); frame.setSize(600, 480); frame.getContentPane().add(new CellBasedSpreadsheetPanel( service, cellFeedUrl)); frame.setVisible(true); frame.setTitle("Cells Demo - Positional Editing"); return frame; } private void initializeGui() { table = new JTable(model); table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); JScrollPane scrollpane = new JScrollPane(table); setLayout(new BorderLayout()); add(scrollpane, BorderLayout.CENTER); refreshButton = new JButton("Refresh"); refreshButton.addActionListener(new ActionHandler()); add(refreshButton, BorderLayout.SOUTH); } private class ActionHandler implements ActionListener { public void actionPerformed(ActionEvent ae) { if (ae.getSource() == refreshButton) { model.refresh(); } } } }