diff --git a/diylc/diylc-core/src/org/diylc/plugins/cloud/presenter/PagingProvider.java b/diylc/diylc-core/src/org/diylc/plugins/cloud/presenter/PagingProvider.java new file mode 100644 index 00000000..770b1f78 --- /dev/null +++ b/diylc/diylc-core/src/org/diylc/plugins/cloud/presenter/PagingProvider.java @@ -0,0 +1,40 @@ +package org.diylc.plugins.cloud.presenter; + +import java.util.List; + +import org.diylc.plugins.cloud.model.ProjectEntity; + +public class PagingProvider { + + private CloudPresenter cloudPresenter; + + private String searchFor; + private String category; + private String sort; + private int itemsPerPage = 10; + + private int currentPage; + private List currentResults; + + public PagingProvider(CloudPresenter cloudPresenter) { + this.cloudPresenter = cloudPresenter; + } + + public List startSession(String searchFor, String category, String sort) throws CloudException { + this.searchFor = searchFor; + this.category = category; + this.sort = sort; + this.currentPage = 1; + + return currentResults = cloudPresenter.search(searchFor, category, sort, currentPage, itemsPerPage); + } + + public boolean hasMoreData() { + return this.currentResults != null && this.currentResults.size() == itemsPerPage; + } + + public List requestMoreData() throws CloudException { + currentPage++; + return currentResults = cloudPresenter.search(searchFor, category, sort, currentPage, itemsPerPage); + } +} diff --git a/diylc/diylc-swing/src/org/diylc/images/IconLoader.java b/diylc/diylc-swing/src/org/diylc/images/IconLoader.java index a7a55879..6bf6f5f8 100644 --- a/diylc/diylc-swing/src/org/diylc/images/IconLoader.java +++ b/diylc/diylc-swing/src/org/diylc/images/IconLoader.java @@ -33,7 +33,7 @@ public enum IconLoader { "arrow.png"), Cloud("cloud.png"), CloudUp("cloud_up.png"), CloudGear("cloud_gear.png"), IdCardEdit( "id_card_edit.png"), KeyEdit("key_edit.png"), Find("find.png"), Dashboard("dashboard.png"), DataFind( "data_find.png"), CloudDownload("cloud-download.png"), NavLeftBlue("nav_left_blue.png"), NavRightBlue( - "nav_right_blue.png"), MissingImage("missing_image.png"), CloudBg("cloud_bg.png"); + "nav_right_blue.png"), MissingImage("missing_image.png"), CloudBg("cloud_bg.png"), CloudBig("cloud_big.png"); protected String name; diff --git a/diylc/diylc-swing/src/org/diylc/images/cloud_big.png b/diylc/diylc-swing/src/org/diylc/images/cloud_big.png new file mode 100644 index 00000000..fdbfdf46 Binary files /dev/null and b/diylc/diylc-swing/src/org/diylc/images/cloud_big.png differ diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/cloud/view/browser/CloudGlassPane.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/cloud/view/browser/AnimatedCloudGlassPane.java similarity index 92% rename from diylc/diylc-swing/src/org/diylc/swing/plugins/cloud/view/browser/CloudGlassPane.java rename to diylc/diylc-swing/src/org/diylc/swing/plugins/cloud/view/browser/AnimatedCloudGlassPane.java index c0efa715..6c178651 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/plugins/cloud/view/browser/CloudGlassPane.java +++ b/diylc/diylc-swing/src/org/diylc/swing/plugins/cloud/view/browser/AnimatedCloudGlassPane.java @@ -28,9 +28,9 @@ import org.diylc.images.IconLoader; * * @author bancika */ -public class CloudGlassPane extends JPanel implements MouseListener, KeyListener { +public class AnimatedCloudGlassPane extends JPanel implements MouseListener, KeyListener { - public static final CloudGlassPane GLASS_PANE = new CloudGlassPane(); + public static final AnimatedCloudGlassPane GLASS_PANE = new AnimatedCloudGlassPane(); private static final long serialVersionUID = -5344758920442881290L; @@ -42,7 +42,7 @@ public class CloudGlassPane extends JPanel implements MouseListener, KeyListener private VolatileImage volatileImg; - public CloudGlassPane() { + public AnimatedCloudGlassPane() { addKeyListener(this); addMouseListener(this); setCursor(new Cursor(Cursor.WAIT_CURSOR)); diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/cloud/view/browser/CloudBrowserFrame.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/cloud/view/browser/CloudBrowserFrame.java index f9f68ad4..9c322f9c 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/plugins/cloud/view/browser/CloudBrowserFrame.java +++ b/diylc/diylc-swing/src/org/diylc/swing/plugins/cloud/view/browser/CloudBrowserFrame.java @@ -27,6 +27,7 @@ import org.diylc.core.IView; import org.diylc.images.IconLoader; import org.diylc.plugins.cloud.model.ProjectEntity; import org.diylc.plugins.cloud.presenter.CloudPresenter; +import org.diylc.plugins.cloud.presenter.PagingProvider; import org.diylc.swing.ISwingUI; public class CloudBrowserFrame extends JFrame { @@ -45,15 +46,7 @@ public class CloudBrowserFrame extends JFrame { private IPlugInPort plugInPort; private CloudPresenter cloudPresenter; - - private List currentResults; - - // search criteria - private String searchFor; - private String category; - private String sort; - private int pageNumber; - private int itemsPerPage = 10; + private PagingProvider pagingProvider; private ISwingUI swingUI; @@ -64,11 +57,12 @@ public class CloudBrowserFrame extends JFrame { this.setPreferredSize(new Dimension(700, 640)); this.plugInPort = plugInPort; this.cloudPresenter = cloudPresenter; + this.pagingProvider = new PagingProvider(cloudPresenter); setContentPane(getSearchPanel()); this.pack(); this.setLocationRelativeTo(swingUI.getOwnerFrame()); - this.setGlassPane(new CloudGlassPane()); + this.setGlassPane(SimpleCloudGlassPane.GLASS_PANE); JRootPane rootPane = SwingUtilities.getRootPane(getSearchHeaderPanel().getGoButton()); rootPane.setDefaultButton(getSearchHeaderPanel().getGoButton()); @@ -144,11 +138,6 @@ public class CloudBrowserFrame extends JFrame { @Override public void actionPerformed(ActionEvent e) { - CloudBrowserFrame.this.pageNumber = 1; - CloudBrowserFrame.this.searchFor = getSearchHeaderPanel().getSearchText(); - CloudBrowserFrame.this.category = getSearchHeaderPanel().getCategory(); - CloudBrowserFrame.this.sort = getSearchHeaderPanel().getSorting(); - getResultsScrollPane().startOver(); search(); } }); @@ -158,29 +147,19 @@ public class CloudBrowserFrame extends JFrame { private ResultsScrollPanel getResultsScrollPane() { if (resultsScrollPane == null) { - resultsScrollPane = new ResultsScrollPanel(swingUI, this, plugInPort, new ILazyProvider() { - - @Override - public void requestMoreData() { - CloudBrowserFrame.this.pageNumber++; - CloudBrowserFrame.this.search(); - } - - @Override - public boolean hasMoreData() { - return currentResults.size() == itemsPerPage; - } - }); + resultsScrollPane = new ResultsScrollPanel(swingUI, this, plugInPort, pagingProvider); } return resultsScrollPane; } private void search() { + getResultsScrollPane().clearPrevious(); executeBackgroundTask(new ITask>() { @Override public List doInBackground() throws Exception { - return cloudPresenter.search(searchFor, category, sort, pageNumber, itemsPerPage); + return pagingProvider.startSession(getSearchHeaderPanel().getSearchText(), + getSearchHeaderPanel().getCategory(), getSearchHeaderPanel().getSorting()); } @Override @@ -191,8 +170,7 @@ public class CloudBrowserFrame extends JFrame { @Override public void complete(List result) { - CloudBrowserFrame.this.currentResults = result; - getResultsScrollPane().addData(result); + getResultsScrollPane().startSearch(result); } }); } diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/cloud/view/browser/ILazyProvider.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/cloud/view/browser/ILazyProvider.java deleted file mode 100644 index 341bb60e..00000000 --- a/diylc/diylc-swing/src/org/diylc/swing/plugins/cloud/view/browser/ILazyProvider.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.diylc.swing.plugins.cloud.view.browser; - -/*** - * Generic interface for dynamic data provider. - * - * @author Branislav Stojkovic - * - * @param - */ -public interface ILazyProvider { - - /** - * @return true if there's potentially more data at the source - */ - boolean hasMoreData(); - - /** - * call to request more data. New data batch should be sent asynchronously to this function call. - */ - void requestMoreData(); -} diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/cloud/view/browser/ResultsScrollPanel.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/cloud/view/browser/ResultsScrollPanel.java index 8b6ae4c2..ec248635 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/plugins/cloud/view/browser/ResultsScrollPanel.java +++ b/diylc/diylc-swing/src/org/diylc/swing/plugins/cloud/view/browser/ResultsScrollPanel.java @@ -6,7 +6,6 @@ import java.awt.Graphics; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; -import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; @@ -29,6 +28,7 @@ import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.JTextArea; import javax.swing.SwingConstants; +import javax.swing.SwingWorker; import org.apache.log4j.Logger; import org.diylc.common.IPlugInPort; @@ -36,6 +36,7 @@ import org.diylc.common.ITask; import org.diylc.core.IView; import org.diylc.images.IconLoader; import org.diylc.plugins.cloud.model.ProjectEntity; +import org.diylc.plugins.cloud.presenter.PagingProvider; import org.diylc.swing.ISwingUI; import org.diylc.swing.gui.DialogFactory; import org.diylc.swing.plugins.file.FileFilterEnum; @@ -63,10 +64,9 @@ public class ResultsScrollPanel extends JScrollPane { * This flag tells us whether the loadMoreLabel should invoke a new data pull or not. */ private boolean armed; - private ILazyProvider provider; + private PagingProvider provider; - public ResultsScrollPanel(ISwingUI mainUI, CloudBrowserFrame cloudUI, IPlugInPort plugInPort, - ILazyProvider provider) { + public ResultsScrollPanel(ISwingUI mainUI, CloudBrowserFrame cloudUI, IPlugInPort plugInPort, PagingProvider provider) { super(); this.mainUI = mainUI; this.cloudUI = cloudUI; @@ -78,18 +78,24 @@ public class ResultsScrollPanel extends JScrollPane { setViewportView(getResultsPanel()); } - public void startOver() { + public void clearPrevious() { currentLocation = 0; getResultsPanel().removeAll(); armed = false; + } + + public void startSearch(List projects) { GridBagConstraints gbc = new GridBagConstraints(); gbc.anchor = GridBagConstraints.NORTHWEST; gbc.gridx = 0; gbc.gridy = 10000; gbc.fill = GridBagConstraints.BOTH; - gbc.weightx = 0; + gbc.weightx = 1; gbc.weighty = 100; + gbc.gridwidth = 3; resultsPanel.add(getLoadMoreLabel(), gbc); + + addData(projects); } public void showNoMatches() { @@ -109,12 +115,15 @@ public class ResultsScrollPanel extends JScrollPane { if (projects == null || projects.isEmpty() && currentLocation == 0) { showNoMatches(); } else { + LOG.info("Adding " + projects.size() + " projects to display."); for (int i = 0; i < projects.size(); i++) { addProjectToDisplay(projects.get(i)); this.currentLocation++; } armed = true; - } + } + + getLoadMoreLabel().setText(provider.hasMoreData() ? "Loading more data..." : ""); } private JComponent addProjectToDisplay(final ProjectEntity project) { @@ -253,24 +262,50 @@ public class ResultsScrollPanel extends JScrollPane { gbc.insets = new Insets(2, 2, 2, 2); getResultsPanel().add(new JSeparator(), gbc); + getResultsPanel().invalidate(); + return thumbnailLabel; } private JLabel getLoadMoreLabel() { if (loadMoreLabel == null) { - loadMoreLabel = new JLabel("Load more...") { + loadMoreLabel = new JLabel("Loading more data...") { private static final long serialVersionUID = 1L; @Override public void paint(Graphics g) { + super.paint(g); if (armed && provider.hasMoreData()) { // disarm immediately so we don't trigger successive requests to the provider armed = false; - provider.requestMoreData(); + SwingWorker, Void> worker = new SwingWorker, Void>() { + + @Override + protected List doInBackground() throws Exception { + return provider.requestMoreData(); + } + + @Override + protected void done() { + try { + List newResults = get(); + if (newResults != null && !newResults.isEmpty()) + addData(newResults); + else + getLoadMoreLabel().setText(""); + } catch (Exception e) { + cloudUI.showMessage("Search failed! Detailed message is in the logs. Please report to the author.", + "Search Failed", IView.ERROR_MESSAGE); + } + } + }; + worker.execute(); } } }; + loadMoreLabel.setHorizontalAlignment(SwingConstants.CENTER); + loadMoreLabel.setFont(loadMoreLabel.getFont().deriveFont(10f)); } return loadMoreLabel; } diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/cloud/view/browser/SimpleCloudGlassPane.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/cloud/view/browser/SimpleCloudGlassPane.java new file mode 100644 index 00000000..c3413530 --- /dev/null +++ b/diylc/diylc-swing/src/org/diylc/swing/plugins/cloud/view/browser/SimpleCloudGlassPane.java @@ -0,0 +1,79 @@ +package org.diylc.swing.plugins.cloud.view.browser; + +import java.awt.Cursor; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingConstants; + +import org.diylc.images.IconLoader; + +/** + * JPanel that shows cloud animation while visible. + * + * @author bancika + */ +public class SimpleCloudGlassPane extends JPanel implements MouseListener, KeyListener { + + public static final SimpleCloudGlassPane GLASS_PANE = new SimpleCloudGlassPane(); + + private static final long serialVersionUID = -5344758920442881290L; + + + public SimpleCloudGlassPane() { + addKeyListener(this); + addMouseListener(this); + setCursor(new Cursor(Cursor.WAIT_CURSOR)); + setOpaque(false); + + setLayout(new GridBagLayout()); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.anchor = GridBagConstraints.LAST_LINE_START; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.weightx = 1; + gbc.weighty = 1; + JLabel cloudLabel = new JLabel(IconLoader.CloudBig.getIcon()); + cloudLabel.setHorizontalAlignment(SwingConstants.CENTER); + add(cloudLabel, gbc); + + gbc.gridy = 1; + gbc.weighty = 1.5; + gbc.anchor = GridBagConstraints.FIRST_LINE_START; + JLabel label = new JLabel("Querying the cloud..."); + label.setHorizontalAlignment(SwingConstants.CENTER); + add(label, gbc); + } + + @Override + public void mouseClicked(final MouseEvent pArg0) {} + + @Override + public void mouseEntered(final MouseEvent pArg0) {} + + @Override + public void mouseExited(final MouseEvent pArg0) {} + + @Override + public void mousePressed(final MouseEvent pArg0) {} + + @Override + public void mouseReleased(final MouseEvent pArg0) {} + + @Override + public void keyPressed(KeyEvent e) {} + + @Override + public void keyReleased(KeyEvent e) {} + + @Override + public void keyTyped(KeyEvent e) {} +}