You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by gb...@apache.org on 2010/03/06 02:33:57 UTC

svn commit: r919683 - in /pivot/trunk/tutorials: .settings/ src/org/apache/pivot/tutorials/stocktracker/

Author: gbrown
Date: Sat Mar  6 01:33:57 2010
New Revision: 919683

URL: http://svn.apache.org/viewvc?rev=919683&view=rev
Log:
Update Stock Tracker to use Bindable.

Added:
    pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/DetailPane.java
    pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTrackerWindow.java
    pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTrackerWindow.json
      - copied, changed from r918971, pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTracker.json
    pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTrackerWindow_fr.json
      - copied, changed from r918971, pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTracker_fr.json
    pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTrackerWindow_it.json
      - copied, changed from r918971, pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTracker_it.json
    pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/detail_pane.wtkx
      - copied, changed from r918971, pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/stocktracker.detail.wtkx
    pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/stocktracker_window.wtkx
      - copied, changed from r918971, pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/stocktracker.wtkx
Removed:
    pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockQuoteView.java
    pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTracker.json
    pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTracker_fr.json
    pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTracker_it.json
    pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/stocktracker.detail.wtkx
    pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/stocktracker.wtkx
Modified:
    pivot/trunk/tutorials/.settings/org.eclipse.core.resources.prefs
    pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTracker.java

Modified: pivot/trunk/tutorials/.settings/org.eclipse.core.resources.prefs
URL: http://svn.apache.org/viewvc/pivot/trunk/tutorials/.settings/org.eclipse.core.resources.prefs?rev=919683&r1=919682&r2=919683&view=diff
==============================================================================
--- pivot/trunk/tutorials/.settings/org.eclipse.core.resources.prefs (original)
+++ pivot/trunk/tutorials/.settings/org.eclipse.core.resources.prefs Sat Mar  6 01:33:57 2010
@@ -1,7 +1,7 @@
-#Sat Feb 13 10:53:04 EST 2010
+#Fri Mar 05 20:12:02 EST 2010
 eclipse.preferences.version=1
 encoding//src/org/apache/pivot/tutorials/localization/Localization.json=UTF-8
 encoding//src/org/apache/pivot/tutorials/localization/Localization_de.json=UTF-8
-encoding//src/org/apache/pivot/tutorials/stocktracker/StockTracker.json=UTF-8
-encoding//src/org/apache/pivot/tutorials/stocktracker/StockTracker_fr.json=UTF-8
+encoding//src/org/apache/pivot/tutorials/stocktracker/StockTrackerWindow.json=UTF-8
+encoding//src/org/apache/pivot/tutorials/stocktracker/StockTrackerWindow_fr.json=UTF-8
 encoding/<project>=UTF-8

Added: pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/DetailPane.java
URL: http://svn.apache.org/viewvc/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/DetailPane.java?rev=919683&view=auto
==============================================================================
--- pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/DetailPane.java (added)
+++ pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/DetailPane.java Sat Mar  6 01:33:57 2010
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.pivot.tutorials.stocktracker;
+
+import java.text.DecimalFormat;
+
+import org.apache.pivot.collections.Dictionary;
+import org.apache.pivot.serialization.JSONSerializer;
+import org.apache.pivot.util.Resources;
+import org.apache.pivot.wtk.BoxPane;
+import org.apache.pivot.wtk.Label;
+import org.apache.pivot.wtkx.Bindable;
+import org.apache.pivot.wtkx.WTKX;
+
+public class DetailPane extends BoxPane implements Bindable {
+    @WTKX private Label valueLabel = null;
+    @WTKX private Label changeLabel = null;
+    @WTKX private Label openingValueLabel = null;
+    @WTKX private Label highValueLabel = null;
+    @WTKX private Label lowValueLabel = null;
+    @WTKX private Label volumeLabel = null;
+
+    private Resources resources = null;
+
+    private DecimalFormat valueFormat = new DecimalFormat("$0.00");
+    private DecimalFormat changeFormat = new DecimalFormat("+0.00;-0.00");
+    private DecimalFormat volumeFormat = new DecimalFormat();
+
+    @Override
+    public void initialize(Resources resources) {
+        this.resources = resources;
+    }
+
+    @Override
+    public void load(Dictionary<String, ?> context) {
+        super.load(context);
+
+        String notApplicable = resources.getString("notApplicable");
+
+        float value = JSONSerializer.getFloat(context, "value");
+        valueLabel.setText(Float.isNaN(value) ? notApplicable : valueFormat.format(value));
+
+        float openingValue = JSONSerializer.getFloat(context, "openingValue");
+        openingValueLabel.setText(Float.isNaN(openingValue) ? notApplicable : valueFormat.format(openingValue));
+
+        float highValue = JSONSerializer.getFloat(context, "highValue");
+        highValueLabel.setText(Float.isNaN(highValue) ? notApplicable : valueFormat.format(highValue));
+
+        float lowValue = JSONSerializer.getFloat(context, "lowValue");
+        lowValueLabel.setText(Float.isNaN(lowValue) ? notApplicable : valueFormat.format(lowValue));
+
+        float change = JSONSerializer.getFloat(context, "change");
+        changeLabel.setText(changeFormat.format(change));
+
+        int volume = JSONSerializer.getInteger(context, "volume");
+        volumeLabel.setText(volumeFormat.format(volume));
+    }
+}

Modified: pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTracker.java
URL: http://svn.apache.org/viewvc/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTracker.java?rev=919683&r1=919682&r2=919683&view=diff
==============================================================================
--- pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTracker.java (original)
+++ pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTracker.java Sat Mar  6 01:33:57 2010
@@ -16,208 +16,31 @@
  */
 package org.apache.pivot.tutorials.stocktracker;
 
-import java.awt.Desktop;
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.text.DateFormat;
-import java.util.Comparator;
-import java.util.Date;
 import java.util.Locale;
 
-import org.apache.pivot.web.GetQuery;
-
-import org.apache.pivot.collections.ArrayList;
-import org.apache.pivot.collections.List;
 import org.apache.pivot.collections.Map;
-import org.apache.pivot.collections.Sequence;
-import org.apache.pivot.serialization.CSVSerializer;
 import org.apache.pivot.util.Resources;
-import org.apache.pivot.util.concurrent.Task;
-import org.apache.pivot.util.concurrent.TaskListener;
 import org.apache.pivot.wtk.Application;
-import org.apache.pivot.wtk.ApplicationContext;
-import org.apache.pivot.wtk.Button;
-import org.apache.pivot.wtk.ButtonPressListener;
-import org.apache.pivot.wtk.Component;
-import org.apache.pivot.wtk.ComponentKeyListener;
-import org.apache.pivot.wtk.Container;
 import org.apache.pivot.wtk.DesktopApplicationContext;
 import org.apache.pivot.wtk.Display;
-import org.apache.pivot.wtk.Form;
-import org.apache.pivot.wtk.Keyboard;
-import org.apache.pivot.wtk.Label;
-import org.apache.pivot.wtk.MessageType;
-import org.apache.pivot.wtk.Span;
-import org.apache.pivot.wtk.TableView;
-import org.apache.pivot.wtk.TableViewRowListener;
-import org.apache.pivot.wtk.TableViewSelectionListener;
-import org.apache.pivot.wtk.TableViewSortListener;
-import org.apache.pivot.wtk.TaskAdapter;
-import org.apache.pivot.wtk.TextInput;
-import org.apache.pivot.wtk.TextInputTextListener;
-import org.apache.pivot.wtk.Window;
-import org.apache.pivot.wtk.content.TableViewRowComparator;
-import org.apache.pivot.wtk.text.TextNode;
-import org.apache.pivot.wtkx.WTKX;
 import org.apache.pivot.wtkx.WTKXSerializer;
 
 public class StockTracker implements Application {
-    private ArrayList<String> symbols = new ArrayList<String>();
-
-    private Window window = null;
-
-    @WTKX private TableView stocksTableView;
-    @WTKX private TextInput symbolTextInput;
-    @WTKX private Button addSymbolButton;
-    @WTKX private Button removeSymbolsButton;
-    @WTKX private Label lastUpdateLabel;
-    @WTKX private Button yahooFinanceButton;
-    @WTKX(id="detail.rootPane") private Container detailRootPane;
-    @WTKX(id="detail.changeLabel") private Label detailChangeLabel;
-
-    private GetQuery getQuery = null;
+    private StockTrackerWindow window = null;
 
     public static final String LANGUAGE_PROPERTY_NAME = "language";
-    public static final String SERVICE_HOSTNAME = "download.finance.yahoo.com";
-    public static final String SERVICE_PATH = "/d/quotes.csv";
-    public static final long REFRESH_INTERVAL = 15000;
-    public static final String YAHOO_FINANCE_HOME = "http://finance.yahoo.com";
-
-    public StockTracker() {
-        symbols.setComparator(new Comparator<String>() {
-            @Override
-            public int compare(String s1, String s2) {
-                return s1.compareTo(s2);
-            }
-        });
-
-        symbols.add("EBAY");
-        symbols.add("AAPL");
-        symbols.add("MSFT");
-        symbols.add("AMZN");
-        symbols.add("GOOG");
-        symbols.add("ORCL");
-        symbols.add("IBM");
-    }
 
     @Override
-    public void startup(Display display, Map<String, String> properties)
-        throws Exception {
-        // Set the locale
+    public void startup(Display display, Map<String, String> properties) throws Exception {
         String language = properties.get(LANGUAGE_PROPERTY_NAME);
         if (language != null) {
             Locale.setDefault(new Locale(language));
         }
 
-        // Load and bind to the WTKX source
-        Resources resources = new Resources(getClass().getName());
+        Resources resources = new Resources(StockTrackerWindow.class.getName());
         WTKXSerializer wtkxSerializer = new WTKXSerializer(resources);
-        window = (Window)wtkxSerializer.readObject(this, "stocktracker.wtkx");
-        wtkxSerializer.bind(this, StockTracker.class);
-
-        // Wire up event handlers
-        stocksTableView.getTableViewRowListeners().add(new TableViewRowListener.Adapter() {
-            @Override
-            public void rowsSorted(TableView tableView) {
-                List<?> tableData = stocksTableView.getTableData();
-                if (tableData.getLength() > 0) {
-                    stocksTableView.setSelectedIndex(0);
-                }
-            }
-        });
-
-        stocksTableView.getTableViewSelectionListeners().add(new TableViewSelectionListener.Adapter() {
-            @Override
-            public void selectedRangesChanged(TableView tableView, Sequence<Span> previousSelectedRanges) {
-                refreshDetail();
-            }
-        });
-
-        stocksTableView.getTableViewSortListeners().add(new TableViewSortListener.Adapter() {
-            @Override
-            @SuppressWarnings("unchecked")
-            public void sortChanged(TableView tableView) {
-                List<Object> tableData = (List<Object>)tableView.getTableData();
-                tableData.setComparator(new TableViewRowComparator(tableView));
-            }
-        });
-
-        stocksTableView.getComponentKeyListeners().add(new ComponentKeyListener.Adapter() {
-            @Override
-            public boolean keyPressed(Component component, int keyCode, Keyboard.KeyLocation keyLocation) {
-                if (keyCode == Keyboard.KeyCode.DELETE) {
-                    removeSelectedSymbols();
-                }
-
-                return false;
-            }
-        });
-
-        symbolTextInput.getTextInputTextListeners().add(new TextInputTextListener() {
-            @Override
-            public void textChanged(TextInput textInput) {
-                TextNode textNode = textInput.getTextNode();
-                addSymbolButton.setEnabled(textNode.getCharacterCount() > 0);
-            }
-        });
-
-        symbolTextInput.getComponentKeyListeners().add(new ComponentKeyListener.Adapter() {
-            @Override
-            public boolean keyPressed(Component component, int keyCode, Keyboard.KeyLocation keyLocation) {
-                if (keyCode == Keyboard.KeyCode.ENTER) {
-                    addSymbol();
-                }
-
-                return false;
-            }
-        });
-
-        addSymbolButton.getButtonPressListeners().add(new ButtonPressListener() {
-            @Override
-            public void buttonPressed(Button button) {
-                addSymbol();
-            }
-        });
-
-        removeSymbolsButton.getButtonPressListeners().add(new ButtonPressListener() {
-            @Override
-            public void buttonPressed(Button button) {
-                removeSelectedSymbols();
-            }
-        });
-
-        yahooFinanceButton.getButtonPressListeners().add(new ButtonPressListener() {
-            @Override
-            public void buttonPressed(Button button) {
-                Desktop desktop = Desktop.getDesktop();
-
-                try {
-                    desktop.browse(new URL(YAHOO_FINANCE_HOME).toURI());
-                } catch(MalformedURLException exception) {
-                    throw new RuntimeException(exception);
-                } catch(URISyntaxException exception) {
-                    throw new RuntimeException(exception);
-                } catch(IOException exception) {
-                    System.out.println("Unable to open "
-                        + YAHOO_FINANCE_HOME + " in default browser.");
-                }
-            }
-        });
-
+        window = (StockTrackerWindow)wtkxSerializer.readObject(this, "stocktracker_window.wtkx");
         window.open(display);
-
-        refreshTable();
-
-        ApplicationContext.scheduleRecurringCallback(new Runnable() {
-            @Override
-            public void run() {
-                refreshTable();
-            }
-        }, REFRESH_INTERVAL);
-
-        symbolTextInput.requestFocus();
     }
 
     @Override
@@ -237,168 +60,6 @@
     public void resume() {
     }
 
-    @SuppressWarnings("unchecked")
-    private void refreshTable() {
-        getQuery = new GetQuery(SERVICE_HOSTNAME, SERVICE_PATH);
-
-        StringBuilder symbolsArgumentBuilder = new StringBuilder();
-        for (int i = 0, n = symbols.getLength(); i < n; i++) {
-            if (i > 0) {
-                symbolsArgumentBuilder.append(",");
-            }
-
-            symbolsArgumentBuilder.append(symbols.get(i));
-        }
-
-        // Format:
-        // s - symbol
-        // n - company name
-        // l1 - most recent value
-        // o - opening value
-        // h - high value
-        // g - low value
-        // c1 - change percentage
-        // v - volume
-        String symbolsArgument = symbolsArgumentBuilder.toString();
-        getQuery.getParameters().put("s", symbolsArgument);
-        getQuery.getParameters().put("f", "snl1ohgc1v");
-
-        CSVSerializer quoteSerializer = new CSVSerializer();
-        quoteSerializer.getKeys().add("symbol");
-        quoteSerializer.getKeys().add("companyName");
-        quoteSerializer.getKeys().add("value");
-        quoteSerializer.getKeys().add("openingValue");
-        quoteSerializer.getKeys().add("highValue");
-        quoteSerializer.getKeys().add("lowValue");
-        quoteSerializer.getKeys().add("change");
-        quoteSerializer.getKeys().add("volume");
-
-        quoteSerializer.setItemClass(StockQuote.class);
-        getQuery.setSerializer(quoteSerializer);
-
-        getQuery.execute(new TaskAdapter<Object>(new TaskListener<Object>() {
-            @Override
-            public void taskExecuted(Task<Object> task) {
-                if (task == getQuery) {
-                    List<Object> quotes = (List<Object>)task.getResult();
-
-                    // Preserve any existing sort and selection
-                    Sequence<?> selectedStocks = stocksTableView.getSelectedRows();
-
-                    List<Object> tableData = (List<Object>)stocksTableView.getTableData();
-                    Comparator<Object> comparator = tableData.getComparator();
-                    quotes.setComparator(comparator);
-
-                    stocksTableView.setTableData(quotes);
-
-                    if (selectedStocks.getLength() > 0) {
-                        // Select current indexes of selected stocks
-                        for (int i = 0, n = selectedStocks.getLength(); i < n; i++) {
-                            StockQuote selectedStock = (StockQuote)selectedStocks.get(i);
-
-                            int index = 0;
-                            for (StockQuote stock : (List<StockQuote>)stocksTableView.getTableData()) {
-                                if (stock.getSymbol().equals(selectedStock.getSymbol())) {
-                                    stocksTableView.addSelectedIndex(index);
-                                    break;
-                                }
-
-                                index++;
-                            }
-                        }
-                    } else {
-                        if (quotes.getLength() > 0) {
-                            stocksTableView.setSelectedIndex(0);
-                        }
-                    }
-
-                    refreshDetail();
-
-                    DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG,
-                        DateFormat.MEDIUM, Locale.getDefault());
-                    lastUpdateLabel.setText(dateFormat.format(new Date()));
-
-                    getQuery = null;
-                }
-            }
-
-            @Override
-            public void executeFailed(Task<Object> task) {
-                if (task == getQuery) {
-                    System.err.println(task.getFault());
-                    getQuery = null;
-                }
-            }
-        }));
-    }
-
-    @SuppressWarnings("unchecked")
-    private void refreshDetail() {
-        int firstSelectedIndex = stocksTableView.getFirstSelectedIndex();
-        removeSymbolsButton.setEnabled(firstSelectedIndex != -1);
-
-        StockQuote stockQuote = null;
-
-        if (firstSelectedIndex != -1) {
-            int lastSelectedIndex = stocksTableView.getLastSelectedIndex();
-
-            if (firstSelectedIndex == lastSelectedIndex) {
-                List<StockQuote> tableData = (List<StockQuote>)stocksTableView.getTableData();
-                stockQuote = tableData.get(firstSelectedIndex);
-            } else {
-                stockQuote = new StockQuote();
-            }
-        } else {
-            stockQuote = new StockQuote();
-        }
-
-        StockQuoteView stockQuoteView = new StockQuoteView(stockQuote);
-        detailRootPane.load(stockQuoteView);
-
-        float change = stockQuote.getChange();
-        if (!Float.isNaN(change)
-            && change < 0) {
-            Form.setFlag(detailChangeLabel, new Form.Flag(MessageType.ERROR));
-        } else {
-            Form.setFlag(detailChangeLabel, (Form.Flag)null);
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    private void addSymbol() {
-        String symbol = symbolTextInput.getText().toUpperCase();
-        if (symbols.indexOf(symbol) == -1) {
-            symbols.add(symbol);
-
-            List<StockQuote> tableData = (List<StockQuote>)stocksTableView.getTableData();
-            StockQuote stockQuote = new StockQuote();
-            stockQuote.setSymbol(symbol);
-            int index = tableData.add(stockQuote);
-
-            stocksTableView.setSelectedIndex(index);
-        }
-
-        symbolTextInput.setText("");
-        refreshTable();
-    }
-
-    private void removeSelectedSymbols() {
-        int selectedIndex = stocksTableView.getFirstSelectedIndex();
-        int selectionLength = stocksTableView.getLastSelectedIndex() - selectedIndex + 1;
-        stocksTableView.getTableData().remove(selectedIndex, selectionLength);
-        symbols.remove(selectedIndex, selectionLength);
-
-        if (selectedIndex >= symbols.getLength()) {
-            selectedIndex = symbols.getLength() - 1;
-        }
-
-        stocksTableView.setSelectedIndex(selectedIndex);
-
-        if (selectedIndex == -1) {
-            refreshDetail();
-        }
-    }
-
     public static void main(String[] args) {
         DesktopApplicationContext.main(StockTracker.class, args);
     }

Added: pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTrackerWindow.java
URL: http://svn.apache.org/viewvc/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTrackerWindow.java?rev=919683&view=auto
==============================================================================
--- pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTrackerWindow.java (added)
+++ pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTrackerWindow.java Sat Mar  6 01:33:57 2010
@@ -0,0 +1,356 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.pivot.tutorials.stocktracker;
+
+import java.awt.Desktop;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.text.DateFormat;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.Locale;
+
+import org.apache.pivot.collections.ArrayList;
+import org.apache.pivot.collections.List;
+import org.apache.pivot.collections.Sequence;
+import org.apache.pivot.serialization.CSVSerializer;
+import org.apache.pivot.serialization.JSONSerializer;
+import org.apache.pivot.util.Resources;
+import org.apache.pivot.util.concurrent.Task;
+import org.apache.pivot.util.concurrent.TaskListener;
+import org.apache.pivot.web.GetQuery;
+import org.apache.pivot.wtk.ApplicationContext;
+import org.apache.pivot.wtk.Button;
+import org.apache.pivot.wtk.ButtonPressListener;
+import org.apache.pivot.wtk.Component;
+import org.apache.pivot.wtk.ComponentKeyListener;
+import org.apache.pivot.wtk.Display;
+import org.apache.pivot.wtk.Keyboard;
+import org.apache.pivot.wtk.Label;
+import org.apache.pivot.wtk.Span;
+import org.apache.pivot.wtk.TableView;
+import org.apache.pivot.wtk.TableViewRowListener;
+import org.apache.pivot.wtk.TableViewSelectionListener;
+import org.apache.pivot.wtk.TableViewSortListener;
+import org.apache.pivot.wtk.TaskAdapter;
+import org.apache.pivot.wtk.TextInput;
+import org.apache.pivot.wtk.TextInputTextListener;
+import org.apache.pivot.wtk.Window;
+import org.apache.pivot.wtk.content.TableViewRowComparator;
+import org.apache.pivot.wtkx.Bindable;
+import org.apache.pivot.wtkx.WTKX;
+
+public class StockTrackerWindow extends Window implements Bindable {
+    @WTKX private TableView stocksTableView = null;
+    @WTKX private TextInput symbolTextInput = null;
+    @WTKX private Button addSymbolButton = null;
+    @WTKX private Button removeSymbolsButton = null;
+    @WTKX private DetailPane detailPane = null;
+    @WTKX private Label lastUpdateLabel = null;
+    @WTKX private Button yahooFinanceButton = null;
+
+    private ArrayList<String> symbols = new ArrayList<String>();
+    private GetQuery getQuery = null;
+
+    public static final String SERVICE_HOSTNAME = "download.finance.yahoo.com";
+    public static final String SERVICE_PATH = "/d/quotes.csv";
+    public static final long REFRESH_INTERVAL = 15000;
+    public static final String YAHOO_FINANCE_HOME = "http://finance.yahoo.com";
+
+    public StockTrackerWindow() {
+        symbols.setComparator(new Comparator<String>() {
+            @Override
+            public int compare(String s1, String s2) {
+                return s1.compareTo(s2);
+            }
+        });
+
+        symbols.add("EBAY");
+        symbols.add("AAPL");
+        symbols.add("MSFT");
+        symbols.add("AMZN");
+        symbols.add("GOOG");
+        symbols.add("ORCL");
+        symbols.add("IBM");
+    }
+
+    @Override
+    public void initialize(Resources resources) {
+        // Wire up event handlers
+        stocksTableView.getTableViewRowListeners().add(new TableViewRowListener.Adapter() {
+            @Override
+            public void rowsSorted(TableView tableView) {
+                List<?> tableData = stocksTableView.getTableData();
+                if (tableData.getLength() > 0) {
+                    stocksTableView.setSelectedIndex(0);
+                }
+            }
+        });
+
+        stocksTableView.getTableViewSelectionListeners().add(new TableViewSelectionListener.Adapter() {
+            @Override
+            public void selectedRangesChanged(TableView tableView, Sequence<Span> previousSelectedRanges) {
+                refreshDetail();
+            }
+        });
+
+        stocksTableView.getTableViewSortListeners().add(new TableViewSortListener.Adapter() {
+            @Override
+            @SuppressWarnings("unchecked")
+            public void sortChanged(TableView tableView) {
+                List<Object> tableData = (List<Object>)tableView.getTableData();
+                tableData.setComparator(new TableViewRowComparator(tableView));
+            }
+        });
+
+        stocksTableView.getComponentKeyListeners().add(new ComponentKeyListener.Adapter() {
+            @Override
+            public boolean keyPressed(Component component, int keyCode, Keyboard.KeyLocation keyLocation) {
+                if (keyCode == Keyboard.KeyCode.DELETE) {
+                    removeSelectedSymbols();
+                }
+
+                return false;
+            }
+        });
+
+        symbolTextInput.getTextInputTextListeners().add(new TextInputTextListener() {
+            @Override
+            public void textChanged(TextInput textInput) {
+                addSymbolButton.setEnabled(textInput.getTextLength() > 0);
+            }
+        });
+
+        symbolTextInput.getComponentKeyListeners().add(new ComponentKeyListener.Adapter() {
+            @Override
+            public boolean keyPressed(Component component, int keyCode, Keyboard.KeyLocation keyLocation) {
+                if (keyCode == Keyboard.KeyCode.ENTER) {
+                    addSymbol();
+                }
+
+                return false;
+            }
+        });
+
+        addSymbolButton.getButtonPressListeners().add(new ButtonPressListener() {
+            @Override
+            public void buttonPressed(Button button) {
+                addSymbol();
+            }
+        });
+
+        removeSymbolsButton.getButtonPressListeners().add(new ButtonPressListener() {
+            @Override
+            public void buttonPressed(Button button) {
+                removeSelectedSymbols();
+            }
+        });
+
+        yahooFinanceButton.getButtonPressListeners().add(new ButtonPressListener() {
+            @Override
+            public void buttonPressed(Button button) {
+                Desktop desktop = Desktop.getDesktop();
+
+                try {
+                    desktop.browse(new URL(YAHOO_FINANCE_HOME).toURI());
+                } catch(MalformedURLException exception) {
+                    throw new RuntimeException(exception);
+                } catch(URISyntaxException exception) {
+                    throw new RuntimeException(exception);
+                } catch(IOException exception) {
+                    System.out.println("Unable to open "
+                        + YAHOO_FINANCE_HOME + " in default browser.");
+                }
+            }
+        });
+    }
+
+    @Override
+    public void open(Display display, Window owner) {
+        super.open(display, owner);
+
+        refreshTable();
+
+        ApplicationContext.scheduleRecurringCallback(new Runnable() {
+            @Override
+            public void run() {
+                refreshTable();
+            }
+        }, REFRESH_INTERVAL);
+
+        symbolTextInput.requestFocus();
+    }
+
+    @SuppressWarnings("unchecked")
+    private void refreshTable() {
+        getQuery = new GetQuery(SERVICE_HOSTNAME, SERVICE_PATH);
+
+        StringBuilder symbolsArgumentBuilder = new StringBuilder();
+        for (int i = 0, n = symbols.getLength(); i < n; i++) {
+            if (i > 0) {
+                symbolsArgumentBuilder.append(",");
+            }
+
+            symbolsArgumentBuilder.append(symbols.get(i));
+        }
+
+        // Format:
+        // s - symbol
+        // n - company name
+        // l1 - most recent value
+        // o - opening value
+        // h - high value
+        // g - low value
+        // c1 - change percentage
+        // v - volume
+        String symbolsArgument = symbolsArgumentBuilder.toString();
+        getQuery.getParameters().put("s", symbolsArgument);
+        getQuery.getParameters().put("f", "snl1ohgc1v");
+
+        CSVSerializer quoteSerializer = new CSVSerializer();
+        quoteSerializer.setItemClass(StockQuote.class);
+
+        quoteSerializer.getKeys().add("symbol");
+        quoteSerializer.getKeys().add("companyName");
+        quoteSerializer.getKeys().add("value");
+        quoteSerializer.getKeys().add("openingValue");
+        quoteSerializer.getKeys().add("highValue");
+        quoteSerializer.getKeys().add("lowValue");
+        quoteSerializer.getKeys().add("change");
+        quoteSerializer.getKeys().add("volume");
+
+        getQuery.setSerializer(quoteSerializer);
+
+        getQuery.execute(new TaskAdapter<Object>(new TaskListener<Object>() {
+            @Override
+            public void taskExecuted(Task<Object> task) {
+                if (task == getQuery) {
+                    List<Object> quotes = (List<Object>)task.getResult();
+
+                    // Preserve any existing sort and selection
+                    Sequence<?> selectedStocks = stocksTableView.getSelectedRows();
+
+                    List<Object> tableData = (List<Object>)stocksTableView.getTableData();
+                    Comparator<Object> comparator = tableData.getComparator();
+                    quotes.setComparator(comparator);
+
+                    stocksTableView.setTableData(quotes);
+
+                    if (selectedStocks.getLength() > 0) {
+                        // Select current indexes of selected stocks
+                        for (int i = 0, n = selectedStocks.getLength(); i < n; i++) {
+                            Object selectedStock = selectedStocks.get(i);
+
+                            int index = 0;
+                            for (Object stock : stocksTableView.getTableData()) {
+                                String symbol = JSONSerializer.getString(stock, "symbol");
+                                String selectedSymbol = JSONSerializer.getString(selectedStock, "symbol");
+
+                                if (symbol.equals(selectedSymbol)) {
+                                    stocksTableView.addSelectedIndex(index);
+                                    break;
+                                }
+
+                                index++;
+                            }
+                        }
+                    } else {
+                        if (quotes.getLength() > 0) {
+                            stocksTableView.setSelectedIndex(0);
+                        }
+                    }
+
+                    refreshDetail();
+
+                    DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG,
+                        DateFormat.MEDIUM, Locale.getDefault());
+                    lastUpdateLabel.setText(dateFormat.format(new Date()));
+
+                    getQuery = null;
+                }
+            }
+
+            @Override
+            public void executeFailed(Task<Object> task) {
+                if (task == getQuery) {
+                    System.err.println(task.getFault());
+                    getQuery = null;
+                }
+            }
+        }));
+    }
+
+    @SuppressWarnings("unchecked")
+    private void refreshDetail() {
+        int firstSelectedIndex = stocksTableView.getFirstSelectedIndex();
+        removeSymbolsButton.setEnabled(firstSelectedIndex != -1);
+
+        StockQuote stockQuote = null;
+
+        if (firstSelectedIndex != -1) {
+            int lastSelectedIndex = stocksTableView.getLastSelectedIndex();
+
+            if (firstSelectedIndex == lastSelectedIndex) {
+                List<StockQuote> tableData = (List<StockQuote>)stocksTableView.getTableData();
+                stockQuote = tableData.get(firstSelectedIndex);
+            } else {
+                stockQuote = new StockQuote();
+            }
+        } else {
+            stockQuote = new StockQuote();
+        }
+
+        detailPane.load(stockQuote);
+    }
+
+    @SuppressWarnings("unchecked")
+    private void addSymbol() {
+        String symbol = symbolTextInput.getText().toUpperCase();
+        if (symbols.indexOf(symbol) == -1) {
+            symbols.add(symbol);
+
+            List<StockQuote> tableData = (List<StockQuote>)stocksTableView.getTableData();
+            StockQuote stockQuote = new StockQuote();
+            stockQuote.setSymbol(symbol);
+            int index = tableData.add(stockQuote);
+
+            stocksTableView.setSelectedIndex(index);
+        }
+
+        symbolTextInput.setText("");
+        refreshTable();
+    }
+
+    private void removeSelectedSymbols() {
+        int selectedIndex = stocksTableView.getFirstSelectedIndex();
+        int selectionLength = stocksTableView.getLastSelectedIndex() - selectedIndex + 1;
+        stocksTableView.getTableData().remove(selectedIndex, selectionLength);
+        symbols.remove(selectedIndex, selectionLength);
+
+        if (selectedIndex >= symbols.getLength()) {
+            selectedIndex = symbols.getLength() - 1;
+        }
+
+        stocksTableView.setSelectedIndex(selectedIndex);
+
+        if (selectedIndex == -1) {
+            refreshDetail();
+        }
+    }
+}

Copied: pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTrackerWindow.json (from r918971, pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTracker.json)
URL: http://svn.apache.org/viewvc/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTrackerWindow.json?p2=pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTrackerWindow.json&p1=pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTracker.json&r1=918971&r2=919683&rev=919683&view=diff
==============================================================================
Binary files - no diff available.

Copied: pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTrackerWindow_fr.json (from r918971, pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTracker_fr.json)
URL: http://svn.apache.org/viewvc/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTrackerWindow_fr.json?p2=pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTrackerWindow_fr.json&p1=pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTracker_fr.json&r1=918971&r2=919683&rev=919683&view=diff
==============================================================================
Binary files - no diff available.

Copied: pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTrackerWindow_it.json (from r918971, pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTracker_it.json)
URL: http://svn.apache.org/viewvc/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTrackerWindow_it.json?p2=pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTrackerWindow_it.json&p1=pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/StockTracker_it.json&r1=918971&r2=919683&rev=919683&view=diff
==============================================================================
Binary files - no diff available.

Copied: pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/detail_pane.wtkx (from r918971, pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/stocktracker.detail.wtkx)
URL: http://svn.apache.org/viewvc/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/detail_pane.wtkx?p2=pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/detail_pane.wtkx&p1=pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/stocktracker.detail.wtkx&r1=918971&r2=919683&rev=919683&view=diff
==============================================================================
--- pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/stocktracker.detail.wtkx (original)
+++ pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/detail_pane.wtkx Sat Mar  6 01:33:57 2010
@@ -16,8 +16,9 @@
 limitations under the License.
 -->
 
-<BoxPane wtkx:id="rootPane" orientation="vertical" styles="{fill:true}"
+<stocktracker:DetailPane orientation="vertical" styles="{fill:true}"
     xmlns:wtkx="http://pivot.apache.org/wtkx"
+    xmlns:stocktracker="org.apache.pivot.tutorials.stocktracker"
     xmlns="org.apache.pivot.wtk">
     <Label textKey="companyName" styles="{font:{size:12, bold:true}}"/>
     <Separator/>
@@ -25,19 +26,19 @@
         leftAlignLabels:true}">
         <sections>
             <Form.Section>
-                <Label Form.label="%value" textKey="value"
+                <Label wtkx:id="valueLabel" Form.label="%value"
                     styles="{horizontalAlignment:'right'}"/>
-                <Label wtkx:id="changeLabel" Form.label="%change" textKey="change"
+                <Label wtkx:id="changeLabel" Form.label="%change"
                     styles="{horizontalAlignment:'right'}"/>
-                <Label Form.label="%openingValue" textKey="openingValue"
+                <Label wtkx:id="openingValueLabel" Form.label="%openingValue"
                     styles="{horizontalAlignment:'right'}"/>
-                <Label Form.label="%highValue" textKey="highValue"
+                <Label wtkx:id="highValueLabel" Form.label="%highValue"
                     styles="{horizontalAlignment:'right'}"/>
-                <Label Form.label="%lowValue" textKey="lowValue"
+                <Label wtkx:id="lowValueLabel" Form.label="%lowValue"
                     styles="{horizontalAlignment:'right'}"/>
-                <Label Form.label="%volume" textKey="volume"
+                <Label wtkx:id="volumeLabel" Form.label="%volume"
                     styles="{horizontalAlignment:'right'}"/>
             </Form.Section>
         </sections>
     </Form>
-</BoxPane>
+</stocktracker:DetailPane>

Copied: pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/stocktracker_window.wtkx (from r918971, pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/stocktracker.wtkx)
URL: http://svn.apache.org/viewvc/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/stocktracker_window.wtkx?p2=pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/stocktracker_window.wtkx&p1=pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/stocktracker.wtkx&r1=918971&r2=919683&rev=919683&view=diff
==============================================================================
--- pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/stocktracker.wtkx (original)
+++ pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/stocktracker_window.wtkx Sat Mar  6 01:33:57 2010
@@ -16,7 +16,7 @@
 limitations under the License.
 -->
 
-<Window title="%stockTracker" maximized="true"
+<stocktracker:StockTrackerWindow title="%stockTracker" maximized="true"
     xmlns:wtkx="http://pivot.apache.org/wtkx"
     xmlns:content="org.apache.pivot.wtk.content"
     xmlns:stocktracker="org.apache.pivot.tutorials.stocktracker"
@@ -72,7 +72,7 @@
                         <right>
                             <Border styles="{padding:6, color:10}">
                                 <content>
-                                    <wtkx:include wtkx:id="detail" src="stocktracker.detail.wtkx"/>
+                                    <wtkx:include wtkx:id="detailPane" src="detail_pane.wtkx"/>
                                 </content>
                             </Border>
                         </right>
@@ -124,4 +124,4 @@
             </rows>
         </TablePane>
     </content>
-</Window>
+</stocktracker:StockTrackerWindow>