--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src/"/>
+ <classpathentry kind="lib" path="lib/xmlrpc-1.2-b1.jar"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null
+build.xml
+org.jnegre.allthenews_*.*.*.jar
+bin
+allthenews.jar
+allthenewssrc.zip
+patch_*.txt
+todo.txt
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>net.sourceforge.phpeclipse.news</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.team.cvs.core.cvsnature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ </natures>
+</projectDescription>
--- /dev/null
+#Tue Oct 05 18:40:07 CEST 2004\r
+eclipse.preferences.version=1\r
+encoding//src/net/sourceforge/phpeclipse/news/messages.properties=8859_1\r
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+ <head>
+ <title>Common Public License - v 1.0</title>
+ <meta http-equiv=Content-Type content="text/html; charset=windows-1252">
+ </head>
+<body vLink=#800000 bgColor=#ffffff>
+<p align=center><b>Common Public License - v 1.0</b>
+<p><b></b><font size=3></font>
+<p><font size=3></font><font size=2>THE ACCOMPANYING PROGRAM IS PROVIDED UNDER
+THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR
+DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS
+AGREEMENT.</font>
+<p><font size=2></font>
+<p><font size=2><b>1. DEFINITIONS</b></font>
+<p><font size=2>"Contribution" means:</font>
+
+<ul><font size=2>a) in the case of the initial Contributor, the initial code
+ and documentation distributed under this Agreement, and<br clear=left>b) in
+ the case of each subsequent Contributor:</font></ul>
+<ul><font size=2>i) changes to the Program, and</font></ul>
+<ul><font size=2>ii) additions to the Program;</font></ul>
+<ul><font size=2>where such changes and/or additions to the Program originate
+ from and are distributed by that particular Contributor. </font><font size=2>A
+ Contribution 'originates' from a Contributor if it was added to the Program by
+ such Contributor itself or anyone acting on such Contributor's behalf.
+ </font><font size=2>Contributions do not include additions to the Program
+ which: (i) are separate modules of software distributed in conjunction with
+ the Program under their own license agreement, and (ii) are not derivative
+ works of the Program. </font></ul>
+<p><font size=2></font>
+<p><font size=2>"Contributor" means any person or entity that distributes the
+Program.</font>
+<p><font size=2></font><font size=2></font>
+<p><font size=2>"Licensed Patents " mean patent claims licensable by a
+Contributor which are necessarily infringed by the use or sale of its
+Contribution alone or when combined with the Program. </font>
+
+<p><font size=2></font><font size=2></font>
+<p><font size=2></font><font size=2>"Program" means the Contributions
+distributed in accordance with this Agreement.</font>
+<p><font size=2></font>
+<p><font size=2>"Recipient" means anyone who receives the Program under this
+Agreement, including all Contributors.</font>
+<p><font size=2><b></b></font>
+<p><font size=2><b>2. GRANT OF RIGHTS</b></font>
+<ul><font size=2></font><font size=2>a) </font><font size=2>Subject to the
+ terms of this Agreement, each Contributor hereby grants</font><font size=2>
+ Recipient a non-exclusive, worldwide, royalty-free copyright license
+ to</font><font color=#ff0000 size=2> </font><font size=2>reproduce, prepare
+ derivative works of, publicly display, publicly perform, distribute and
+ sublicense the Contribution of such Contributor, if any, and such derivative
+ works, in source code and object code form.</font></ul>
+<ul><font size=2></font></ul>
+
+<ul><font size=2></font><font size=2>b) Subject to the terms of this
+ Agreement, each Contributor hereby grants </font><font size=2>Recipient a
+ non-exclusive, worldwide,</font><font color=#008000 size=2> </font><font
+ size=2>royalty-free patent license under Licensed Patents to make, use, sell,
+ offer to sell, import and otherwise transfer the Contribution of such
+ Contributor, if any, in source code and object code form. This patent license
+ shall apply to the combination of the Contribution and the Program if, at the
+ time the Contribution is added by the Contributor, such addition of the
+ Contribution causes such combination to be covered by the Licensed Patents.
+ The patent license shall not apply to any other combinations which include the
+ Contribution. No hardware per se is licensed hereunder. </font></ul>
+<ul><font size=2></font></ul>
+<ul><font size=2>c) Recipient understands that although each Contributor
+ grants the licenses to its Contributions set forth herein, no assurances are
+ provided by any Contributor that the Program does not infringe the patent or
+ other intellectual property rights of any other entity. Each Contributor
+ disclaims any liability to Recipient for claims brought by any other entity
+ based on infringement of intellectual property rights or otherwise. As a
+ condition to exercising the rights and licenses granted hereunder, each
+ Recipient hereby assumes sole responsibility to secure any other intellectual
+ property rights needed, if any. For example, if a third party patent license
+ is required to allow Recipient to distribute the Program, it is Recipient's
+ responsibility to acquire that license before distributing the
+Program.</font></ul>
+<ul><font size=2></font></ul>
+<ul><font size=2>d) Each Contributor represents that to its knowledge it has
+ sufficient copyright rights in its Contribution, if any, to grant the
+ copyright license set forth in this Agreement. </font></ul>
+<ul><font size=2></font></ul>
+<p><font size=2><b>3. REQUIREMENTS</b></font>
+<p><font size=2><b></b>A Contributor may choose to distribute the Program in
+object code form under its own license agreement, provided that:</font>
+<ul><font size=2>a) it complies with the terms and conditions of this
+ Agreement; and</font></ul>
+
+<ul><font size=2>b) its license agreement:</font></ul>
+<ul><font size=2>i) effectively disclaims</font><font face="Times New Roman"
+ size=2> on behalf of all Contributors all warranties and conditions, express
+ and implied, including warranties or conditions of title and non-infringement,
+ and implied warranties or conditions of merchantability and fitness for a
+ particular purpose; </font></ul>
+<ul><font face="Times New Roman" size=2>ii) effectively excludes on behalf of
+ all Contributors all liability for damages, including direct, indirect,
+ special, incidental and consequential damages, such as lost profits;
+</font></ul>
+<ul><font face="Times New Roman" size=2>iii)</font><font size=2> states that
+ any provisions which differ from this Agreement are offered by that
+ Contributor alone and not by any other party; and</font></ul>
+<ul><font size=2>iv) states that source code for the Program is available from
+ such Contributor, and informs licensees how to obtain it in a reasonable
+ manner on or through a medium customarily used for software
+ exchange.</font><font color=#0000ff size=2> </font><font color=#ff0000
+ size=2></font></ul>
+<ul><font color=#ff0000 size=2></font><font size=2></font></ul>
+<p><font size=2>When the Program is made available in source code form:</font>
+
+<ul><font size=2>a) it must be made available under this Agreement; and
+</font></ul>
+<ul><font size=2>b) a copy of this Agreement must be included with each copy
+ of the Program. </font></ul>
+<p><font size=2></font><font color=#0000ff size=2><strike></strike></font>
+<p><font color=#0000ff size=2><strike></strike></font><font size=2>Contributors
+may not remove or alter any copyright notices contained within the Program.
+</font>
+<p><font size=2></font>
+<p><font size=2>Each Contributor must identify itself as the originator of its
+Contribution, if any, in a manner that reasonably allows subsequent Recipients
+to identify the originator of the Contribution. </font>
+<p><font size=2></font>
+<p><font size=2><b>4. COMMERCIAL DISTRIBUTION</b></font>
+<p><font size=2>Commercial distributors of software may accept certain
+responsibilities with respect to end users, business partners and the like.
+While this license is intended to facilitate the commercial use of the Program,
+the Contributor who includes the Program in a commercial product offering should
+do so in a manner which does not create potential liability for other
+Contributors. Therefore, if a Contributor includes the Program in a commercial
+product offering, such Contributor ("Commercial Contributor") hereby agrees to
+defend and indemnify every other Contributor ("Indemnified Contributor") against
+any losses, damages and costs (collectively "Losses") arising from claims,
+lawsuits and other legal actions brought by a third party against the
+Indemnified Contributor to the extent caused by the acts or omissions of such
+Commercial Contributor in connection with its distribution of the Program in a
+commercial product offering. The obligations in this section do not apply to any
+claims or Losses relating to any actual or alleged intellectual property
+infringement. In order to qualify, an Indemnified Contributor must: a) promptly
+notify the Commercial Contributor in writing of such claim, and b) allow the
+Commercial Contributor to control, and cooperate with the Commercial Contributor
+in, the defense and any related settlement negotiations. The Indemnified
+Contributor may participate in any such claim at its own expense.</font>
+<p><font size=2></font>
+<p><font size=2>For example, a Contributor might include the Program in a
+commercial product offering, Product X. That Contributor is then a Commercial
+Contributor. If that Commercial Contributor then makes performance claims, or
+offers warranties related to Product X, those performance claims and warranties
+are such Commercial Contributor's responsibility alone. Under this section, the
+Commercial Contributor would have to defend claims against the other
+Contributors related to those performance claims and warranties, and if a court
+requires any other Contributor to pay any damages as a result, the Commercial
+Contributor must pay those damages.</font>
+
+<p><font size=2></font><font color=#0000ff size=2></font>
+<p><font color=#0000ff size=2></font><font size=2><b>5. NO WARRANTY</b></font>
+<p><font size=2>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
+PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR
+CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
+PARTICULAR PURPOSE. Each Recipient is</font><font size=2> solely responsible for
+determining the appropriateness of using and distributing </font><font
+size=2>the Program</font><font size=2> and assumes all risks associated with its
+exercise of rights under this Agreement</font><font size=2>, including but not
+limited to the risks and costs of program errors, compliance with applicable
+laws, damage to or loss of data, </font><font size=2>programs or equipment, and
+unavailability or interruption of operations</font><font size=2>. </font><font
+size=2></font>
+<p><font size=2></font>
+<p><font size=2></font><font size=2><b>6. DISCLAIMER OF LIABILITY</b></font>
+<p><font size=2></font><font size=2>EXCEPT AS EXPRESSLY SET FORTH IN THIS
+AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+
+</font><font face="Times New Roman" size=2>(INCLUDING WITHOUT LIMITATION LOST
+PROFITS),</font><font size=2> HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR
+THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY
+OF SUCH DAMAGES.</font>
+<p><font size=2></font><font size=2></font>
+<p><font size=2><b>7. GENERAL</b></font>
+<p><font size=2></font><font size=2>If any provision of this Agreement is
+invalid or unenforceable under applicable law, it shall not affect the validity
+or enforceability of the remainder of the terms of this Agreement, and without
+further action by the parties hereto, such provision shall be reformed to the
+minimum extent necessary to make such provision valid and enforceable.</font>
+<p><font size=2></font>
+<p><font size=2>If Recipient institutes patent litigation against a Contributor
+with respect to a patent applicable to software (including a cross-claim or
+counterclaim in a lawsuit), then any patent licenses granted by that Contributor
+to such Recipient under this Agreement shall terminate as of the date such
+litigation is filed. In addition, if Recipient institutes patent litigation
+against any entity (including a cross-claim or counterclaim in a lawsuit)
+alleging that the Program itself (excluding combinations of the Program with
+other software or hardware) infringes such Recipient's patent(s), then such
+Recipient's rights granted under Section 2(b) shall terminate as of the date
+such litigation is filed. </font><font size=2></font>
+<p><font size=2></font>
+<p><font size=2>All Recipient's rights under this Agreement shall terminate if
+it fails to comply with any of the material terms or conditions of this
+Agreement and does not cure such failure in a reasonable period of time after
+becoming aware of such noncompliance. If all Recipient's rights under this
+Agreement terminate, Recipient agrees to cease use and distribution of the
+Program as soon as reasonably practicable. However, Recipient's obligations
+under this Agreement and any licenses granted by Recipient relating to the
+Program shall continue and survive. </font><font size=2></font>
+<p><font size=2></font>
+<p><font size=2></font><font face="Times New Roman" size=2>Everyone is permitted
+to copy and distribute copies of this Agreement, but in order to avoid
+inconsistency the Agreement is copyrighted and may only be modified in the
+following manner. The Agreement Steward reserves the right to </font><font
+size=2>publish new versions (including revisions) of this Agreement from time to
+
+</font><font face="Times New Roman" size=2>time. No one other than the Agreement
+Steward has the right to modify this Agreement. IBM is the initial Agreement
+Steward. IBM may assign the responsibility to serve as the Agreement Steward to
+a suitable separate entity. </font><font size=2>Each new version of the
+Agreement will be given a distinguishing version number. The Program (including
+Contributions) may always be distributed subject to the version of the Agreement
+under which it was received. In addition, after a new version of the Agreement
+is published, Contributor may elect to distribute the Program (including its
+Contributions) under the new </font><font face="Times New Roman" size=2>version.
+</font><font size=2>Except as expressly stated in Sections 2(a) and 2(b) above,
+Recipient receives no rights or licenses to the intellectual property of any
+Contributor under this Agreement, whether expressly, </font><font size=2>by
+implication, estoppel or otherwise</font><font size=2>.</font><font size=2> All
+rights in the Program not expressly granted under this Agreement are
+reserved.</font>
+<p><font size=2></font>
+<p><font size=2>This Agreement is governed by the laws of the State of New York
+and the intellectual property laws of the United States of America. No party to
+this Agreement will bring a legal action under this Agreement more than one year
+after the cause of action arose. Each party waives its rights to a jury trial in
+any resulting litigation.</font>
+<p><font size=2></font><font size=2></font>
+<p><font size=2></font></p></body></html>
\ No newline at end of file
--- /dev/null
+bin.includes = icons/,\
+ plugin.xml,\
+ allthenewssrc.zip,\
+ about.html,\
+ lib/,\
+ allthenews.jar,\
+ default_feeds.properties,\
+ changes.txt
+src.includes = src/
+source.allthenews.jar = src/
+bin.excludes = icons/*.xcf
--- /dev/null
+#This is the list of default feeds.
+#The format is url=title
+#Beware that characters such as : or = must be escaped in urls
+
+http\://www.phpeclipse.de/tiki-forums_rss.php=PHPeclipse - User Forums
+http\://www.phpeclipse.de/tiki-forum_rss_de.php=PHPeclipse - deutsches Benutzer Forum
+http\://www.phpeclipse.de/tiki-wiki_rss.php=PHPeclipse - Wiki
+http\://www.eclipseproject.de/backend.php=EclipseProject.de - News
+http\://www.eclipseproject.de/backendforum1.php=EclipseProject.de - Eclipse als IDE nutzen
+http\://www.eclipseproject.de/backendforum4.php=EclipseProject.de - Eclipse Plugins benutzen
+http\://www.eclipseproject.de/backendforum2.php=EclipseProject.de - Eclipse Plugins entwickeln
+http\://www.eclipseproject.de/backendforum8.php=EclipseProject.de - SWT/JFace/RCP
+http\://www.php.net/news.rss=php.net News
+http\://www.jsurfer.org/backend.php=JSurfer.org
+http\://today.java.net/pub/q/java_today_rss?x-ver\=1.0=Java.net
+http\://eclipse-plugins.2y.net/eclipse/plugins_backend.jsp?what\=newandupdated=EclipsePlugins
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin
+ id="net.sourceforge.phpeclipse.news"
+ name="PHPeclipse RSS News"
+ version="3.0.3"
+ provider-name="phpeclipse.de"
+ class="net.sourceforge.phpeclipse.news.Plugin">
+
+ <runtime>
+ <library name="rssnews.jar"/>
+ <library name="lib/xmlrpc-1.2-b1.jar"/>
+ </runtime>
+ <requires>
+ <import plugin="org.eclipse.core.runtime.compatibility"/>
+ <import plugin="org.eclipse.ui"/>
+ <import plugin="net.sourceforge.phpeclipse.webbrowser"/>
+ </requires>
+
+
+ <extension
+ point="org.eclipse.ui.views">
+ <category
+ name="PHPeclipse RSS News"
+ id="net.sourceforge.phpeclipse.news.viewcategory">
+ </category>
+ <view
+ name="News Explorer"
+ icon="icons\news.gif"
+ category="net.sourceforge.phpeclipse.news.viewcategory"
+ class="net.sourceforge.phpeclipse.news.view.ExplorerView"
+ id="net.sourceforge.phpeclipse.news.view.explorer">
+ </view>
+ <view
+ name="Headlines"
+ icon="icons\headlines.png"
+ category="net.sourceforge.phpeclipse.news.viewcategory"
+ class="net.sourceforge.phpeclipse.news.view.HeadlineView"
+ id="net.sourceforge.phpeclipse.news.view.headline">
+ </view>
+ </extension>
+ <extension
+ point="org.eclipse.ui.perspectives">
+ <perspective
+ name="PHPeclipse RSS News"
+ icon="icons\news.gif"
+ class="net.sourceforge.phpeclipse.news.Perspective"
+ id="net.sourceforge.phpeclipse.news.perspective">
+ </perspective>
+ </extension>
+ <extension
+ point="org.eclipse.ui.preferencePages">
+ <page
+ name="PHPeclipse RSS News"
+ category="net.sourceforge.phpeclipse.preference.PHPEclipsePreferencePage"
+ class="net.sourceforge.phpeclipse.news.pref.PreferencePage"
+ id="net.sourceforge.phpeclipse.news.preference">
+ </page>
+ </extension>
+
+</plugin>
--- /dev/null
+package net.sourceforge.phpeclipse.news;
+
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashSet;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.eclipse.core.runtime.Preferences;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+/**
+ * @author jnegre - http://www.jnegre.org/
+ *
+ * (c)Copyright 2002 Jérôme Nègre
+ *
+ */
+public class Channel {
+
+ private final String url;
+ private final String title;
+
+ private boolean refreshing = false;
+ private String errorMessage = null;
+ private boolean unread = false;
+
+ private ArrayList items = new ArrayList();
+ private HashSet readUids = null;
+
+ /**
+ * Constructor for Channel.
+ */
+ public Channel(String title, String url) {
+ this(title, url, null);
+ }
+
+ public Channel(String title, String url, HashSet readUids) {
+ this.title = title;
+ this.url = url;
+ this.readUids = readUids;
+ }
+
+
+ public void update() {
+ update(Plugin.getDefault().getPluginPreferences());
+ }
+
+
+ public void update(Preferences prefs) {
+ ArrayList newItems = new ArrayList();
+ String newErrorMessage = null;
+ try {
+
+ URLConnection conn = new URL(url).openConnection();
+ conn.setRequestProperty("User-Agent", Plugin.userAgent);
+ if(prefs.getBoolean(Plugin.FORCE_CACHE_PREFERENCE)) {
+ conn.setRequestProperty("Pragma", "no-cache");
+ conn.setRequestProperty("Cache-Control", "no-cache");
+ }
+ InputStream stream = conn.getInputStream();
+
+ /* workaround a bug of crimson (it seems to ignore the encoding
+ * if it does not get it the first time it reads bytes from
+ * the stream. We use a PushbackInputStream to be sure that the
+ * encoding declaration is in the buffer)
+ */
+ PushbackInputStream pbStream = new PushbackInputStream(stream,64);
+ byte[] buffer = new byte[64];
+ int pos = 0;
+ while(pos != 64) {
+ pos += pbStream.read(buffer, pos, 64-pos);
+ }
+ pbStream.unread(buffer);
+ //end workaround
+
+ DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ InputSource inputSource = new InputSource(pbStream);
+ Document doc = parser.parse(inputSource);
+ pbStream.close();
+ NodeList itemNodes = doc.getElementsByTagName("item");
+ for (int i = 0; i < itemNodes.getLength(); i++) {
+ Item aNewItem = new Item(this, (Element) itemNodes.item(i));
+ if(aNewItem.isBanned()) continue;
+ if(readUids!=null && readUids.remove(aNewItem.getUID())) {
+ aNewItem.setReadFlag(true);
+ }
+ int indexOld = items.indexOf(aNewItem);
+ if(indexOld != -1) {
+ newItems.add(items.get(indexOld));
+ } else {
+ newItems.add(aNewItem);
+ }
+
+ }
+ this.readUids = null;
+ } catch(UnknownHostException e) {
+ // no connection to internet
+ } catch(Exception e) {
+ newErrorMessage = e.toString();
+ Plugin.logInfo("Error in channel update",e);
+ }
+
+ synchronized(this) {
+ this.errorMessage = newErrorMessage;
+ if(newErrorMessage == null) {
+ this.items = newItems;
+ computeUnRead();
+ }
+ }
+ }
+
+ /**
+ * Returns the url.
+ * @return String
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * Returns the errorMessage.
+ * @return String
+ */
+ public synchronized String getErrorMessage() {
+ return errorMessage;
+ }
+
+ /**
+ * Returns the items.
+ * @return ArrayList
+ */
+ public synchronized ArrayList getItems() {
+ return new ArrayList(items);
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ return "Channel at "+url;
+ }
+
+ /**
+ * Returns the title.
+ * @return String
+ */
+ public String getTitle() {
+ return title;
+ }
+
+ /**
+ * Returns the refreshing.
+ * @return boolean
+ */
+ public boolean isRefreshing() {
+ return refreshing;
+ }
+
+ /**
+ * Sets the refreshing.
+ * @param refreshing The refreshing to set
+ */
+ public void setRefreshing(boolean refreshing) {
+ this.refreshing = refreshing;
+ }
+
+ /**
+ * Returns the unread.
+ * @return boolean
+ */
+ public boolean isUnread() {
+ return unread;
+ }
+
+ public synchronized void computeUnRead() {
+ this.unread = false;
+ for (int i = 0; i < items.size(); i++) {
+ this.unread = this.unread || !((Item)items.get(i)).isReadFlag();
+ }
+ }
+
+ public String getUID() {
+ return "CHA" + url;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2004 Jérôme Nègre.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.jnegre.org/cpl1_0.html
+ *
+ * Contributors:
+ * Jérôme Nègre - initial API and implementation
+ *******************************************************************************/
+
+/*
+ * Created on 12 juin 2004
+ */
+package net.sourceforge.phpeclipse.news;
+
+import java.net.URL;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * @author Jérôme Nègre
+ */
+public class IconManager {
+ //the root folder containing the icons
+ private static final String ICON_FOLDER = "icons/";
+
+ //real file locations
+ private static final String LOC_LED_DARK_GREEN = "led_dark_green.gif";
+ private static final String LOC_LED_LIGHT_GREEN = "led_light_green.gif";
+ private static final String LOC_LED_RED = "led_red.gif";
+ private static final String LOC_LED_YELLOW = "led_yellow.gif";
+ private static final String LOC_LINK = "link.gif";
+ private static final String LOC_REFRESH = "refresh.gif";
+ private static final String LOC_EXTERNAL_BROWSER = "external_browser.gif";
+
+ //list of all icon files to put in the ImageRegistry
+ private static final String[] LOCATIONS = new String[]{
+ LOC_LED_DARK_GREEN,
+ LOC_LED_LIGHT_GREEN,
+ LOC_LED_RED,
+ LOC_LED_YELLOW,
+ LOC_LINK,
+ LOC_REFRESH,
+ LOC_EXTERNAL_BROWSER
+ };
+
+ //public names
+ public static final String ICON_STATUS_ERROR = LOC_LED_RED;
+ public static final String ICON_STATUS_UNREAD = LOC_LED_LIGHT_GREEN;
+ public static final String ICON_STATUS_READ = LOC_LED_DARK_GREEN;
+ public static final String ICON_STATUS_REFRESH = LOC_LED_YELLOW;
+
+ public static final String ICON_ACTION_REFRESH = LOC_REFRESH;
+ public static final String ICON_ACTION_LINK = LOC_LINK;
+ public static final String ICON_ACTION_EXTERNAL_BROWSER = LOC_EXTERNAL_BROWSER;
+
+ /**
+ * Populates an image registry with all the locations
+ * @param registry
+ */
+ protected static void populateImageRegistry(ImageRegistry registry) {
+ for(int i=0; i<LOCATIONS.length; i++) {
+ registry.put(LOCATIONS[i],createImageDescriptor(LOCATIONS[i]));
+ }
+ }
+
+ /**
+ * Creates the ImageDescriptor of a file given its path in the
+ * ICON_FOLDER.
+ * @param relativePath
+ * @return the ImageDescriptor
+ */
+ private static ImageDescriptor createImageDescriptor(String relativePath) {
+ try {
+ URL url = new URL(Plugin.getDefault().getDescriptor().getInstallURL(),
+ ICON_FOLDER + relativePath);
+ return ImageDescriptor.createFromURL(url);
+ } catch (java.net.MalformedURLException e) {
+ return ImageDescriptor.getMissingImageDescriptor();
+ }
+ }
+
+ public static ImageDescriptor getImageDescriptor(String key) {
+ return Plugin.getDefault().getImageRegistry().getDescriptor(key);
+ }
+
+ public static Image getImage(String key) {
+ return Plugin.getDefault().getImageRegistry().get(key);
+ }
+
+ /**
+ * This class should not be instanciated
+ */
+ private IconManager() {
+ //NOP
+ }
+}
--- /dev/null
+package net.sourceforge.phpeclipse.news;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+
+/**
+ * @author jnegre - http://www.jnegre.org/
+ *
+ * (c)Copyright 2002 Jérôme Nègre
+ *
+ */
+public class Item {
+
+ protected static DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.SHORT);
+ protected static SimpleDateFormat pubDateParser = new SimpleDateFormat("EEE, d MMM yy hh:mm:ss z", new Locale("en","US"));
+
+ protected Channel channel;
+
+ protected String title;
+ protected String link;
+ protected String description;
+ protected String author;
+ protected String guid;
+ protected boolean isPermaLink = true;
+ protected String date;
+
+ protected boolean readFlag = false;
+
+ /**
+ * Constructor for Item.
+ */
+ public Item(Channel channel, Element itemElement) {
+ this.channel = channel;
+ this.title = readValue("title", itemElement, 0);
+ this.link = readValue("link", itemElement, 0);
+ this.description = readValue("description", itemElement, 0);
+ this.author = readValue("author", itemElement, 0);
+ this.guid = readValue("guid", itemElement, 1);
+ String pubDate = readValue("pubDate", itemElement, 0);
+ String dcDate = readValue("dc:date", itemElement, 0);
+
+ try {
+ Date theDate;
+ if(pubDate != null) {
+ theDate = pubDateParser.parse(pubDate);
+ } else if(dcDate != null) {
+ theDate = decodeDCDate(dcDate);
+ } else {
+ theDate = new Date();
+ }
+ this.date = dateFormat.format(theDate);
+ } catch(Exception e) {
+ if(pubDate != null) {
+ this.date = pubDate;
+ } else if(dcDate != null) {
+ this.date = dcDate;
+ } else {
+ this.date = e.toString();
+ }
+ Plugin.logInfo("Unable to parse date",e);
+ }
+ }
+
+ protected String readValue(String elementName, Element parent, int type) {
+ Element element = (Element)parent.getElementsByTagName(elementName).item(0);
+ if(element != null) {
+
+ switch(type) {
+ case 1:
+ if(element.hasAttribute("isPermaLink") && element.getAttribute("isPermaLink").equals("false")) {
+ this.isPermaLink = false;
+ }
+ }
+
+ NodeList children = element.getChildNodes();
+ StringBuffer buffer = new StringBuffer();
+ for(int i=0; i<children.getLength(); i++) {
+ Node node = children.item(i);
+ if(node.getNodeType()==Node.TEXT_NODE || node.getNodeType()==Node.CDATA_SECTION_NODE) {
+ buffer.append(((Text)node).getData());
+ }
+ }
+ return buffer.toString().trim();
+ } else {
+ return null;
+ }
+ }
+
+ public String getUsableTitle() {
+ if(title != null) {
+ return title;
+ } else if (description != null) {
+ return description;
+ } else {
+ return "!! No title in feed !!";
+ }
+ }
+
+ public String getUsableLink() {
+ if(link != null) {
+ return link;
+ } else if (guid != null && isPermaLink) {
+ return guid;
+ } else {
+ return "about:blank";
+ }
+ }
+
+ public boolean isBanned() {
+ return Plugin.getDefault().isBannedTitle(title);
+ }
+
+ public TableItem toTableItem(Table table) {
+ TableItem tableItem = new TableItem(table, SWT.NONE);
+ fillTableItem(tableItem);
+ return tableItem;
+ }
+
+ public void fillTableItem(TableItem tableItem) {
+ tableItem.setText(new String[] {date,readFlag?"":"*",getUsableTitle()});
+ tableItem.setData(this);
+ }
+
+ /**
+ * Sets the readFlag and notifies the listeners
+ * that the status changed.
+ * @param readFlag The readFlag to set
+ */
+ public void setReadFlag(boolean readFlag) {
+ if(readFlag != this.readFlag) {
+ this.readFlag = readFlag;
+ channel.computeUnRead();
+ }
+ }
+
+ /**
+ * @see java.lang.Object#equals(Object)
+ */
+ public boolean equals(Object obj) {
+ return (obj instanceof Item)
+ && ((Item)obj).getUID().equals(this.getUID());
+ }
+
+ protected static Date decodeDCDate(String string) throws Exception {
+ GregorianCalendar calendar = new GregorianCalendar(readInt(string,0,4),0,1,0,0,0);
+ calendar.set(Calendar.MILLISECOND,0);
+ calendar.set(Calendar.DST_OFFSET,0);
+ if(checkChar(string,4,'-')) {
+ calendar.set(Calendar.MONTH,readInt(string,5,2)-1);
+ if(checkChar(string,7,'-')) {
+ calendar.set(Calendar.DATE,readInt(string,8,2));
+ if(checkChar(string,10,'T')) {
+ calendar.set(Calendar.HOUR_OF_DAY,readInt(string,11,2));
+ calendar.set(Calendar.MINUTE,readInt(string,14,2));
+ int length = string.length();
+ int position = 16;
+
+ //les secondes + millisecondes
+ if(checkChar(string,16,':')) {
+ calendar.set(Calendar.SECOND,readInt(string,17,2));
+ position = 19;
+ if(checkChar(string,position,'.')) {
+ position += 1;
+ StringBuffer millisecondBuffer = new StringBuffer("0.");
+ while(position<length && Character.isDigit(string.charAt(position))) {
+ millisecondBuffer.append(string.charAt(position));
+ position += 1;
+ }
+ calendar.set(Calendar.MILLISECOND,(int)(Double.parseDouble(millisecondBuffer.toString())*1000));
+ }
+
+ }
+
+
+ //TZD
+ if(string.charAt(position) == 'Z') {
+ calendar.set(Calendar.ZONE_OFFSET,0);
+ if(length != position +1) {
+ //trop de caractères
+ throw new Exception("Invalid format of dc:date (extra tokens)");
+ }
+ } else if(string.charAt(position) == '+' || string.charAt(position) == '-') {
+ int sign = 0;
+ sign = string.charAt(position) == '+'?1:-1;
+ int hour = readInt(string,position+1,2);
+ int minute = readInt(string,position+4,2);
+ calendar.set(Calendar.ZONE_OFFSET,sign*(hour*60*60*1000+minute*60*1000));
+ if(length != position +6) {
+ //trop de caractères
+ throw new Exception("Invalid format of dc:date (extra tokens)");
+ }
+ } else {
+ throw new Exception("Invalid format of dc:date (invalid TZD)");
+ }
+
+ }
+ }
+ }
+ return calendar.getTime();
+ }
+
+ private static int readInt(String buffer, int position, int length) {
+ int result = Integer.parseInt(buffer.substring(position,position+length));
+ return result;
+ }
+
+ private static boolean checkChar(String buffer, int position, char expectedChar) {
+ if(buffer.length() > position && buffer.charAt(position) == expectedChar) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * @return the description of this item
+ */
+ public String getDescription() {
+ return description;
+ }
+ /**
+ * @return the author of this item
+ */
+ public String getAuthor() {
+ return author;
+ }
+
+ /**
+ * Returns the date.
+ * @return String
+ */
+ public String getDate() {
+ return date;
+ }
+
+ /**
+ * Returns the readFlag.
+ * @return boolean
+ */
+ public boolean isReadFlag() {
+ return readFlag;
+ }
+
+ /**
+ * Returns the channel.
+ * @return Channel
+ */
+ public Channel getChannel() {
+ return channel;
+ }
+
+ /**
+ * Returns a unique ID used to remember which
+ * items were read in the ChannelStore
+ * @return
+ */
+ public String getUID() {
+ return getUsableLink() + ") ~ (" + getUsableTitle();
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2004 Jérôme Nègre.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.jnegre.org/cpl1_0.html
+ *
+ * Contributors:
+ * Jérôme Nègre - initial API and implementation
+ *******************************************************************************/
+
+/*
+ * Created on 27 juil. 2004
+ */
+package net.sourceforge.phpeclipse.news;
+
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * @author Jérôme Nègre
+ */
+public class Messages {
+ private static final String BUNDLE_NAME = "net.sourceforge.phpeclipse.news.messages";//$NON-NLS-1$
+
+ private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle
+ .getBundle(BUNDLE_NAME);
+
+ private Messages() {
+ }
+
+ public static String getString(String key) {
+ try {
+ return RESOURCE_BUNDLE.getString(key);
+ } catch (MissingResourceException e) {
+ return '!' + key + '!';
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+package net.sourceforge.phpeclipse.news;
+
+import net.sourceforge.phpeclipse.webbrowser.views.BrowserView;
+
+import org.eclipse.ui.IFolderLayout;
+import org.eclipse.ui.IPageLayout;
+import org.eclipse.ui.IPerspectiveFactory;
+
+//import org.eclipse.update.internal.ui.UpdatePerspective;
+/**
+ * @author Laurent Fourrier, Jérôme Nègre
+ *
+ */
+public class Perspective implements IPerspectiveFactory {
+ /**
+ * @see org.eclipse.ui.IPerspectiveFactory#createInitialLayout(IPageLayout)
+ */
+ public void createInitialLayout(IPageLayout layout) {
+ String editorArea = layout.getEditorArea();
+ layout.setEditorAreaVisible(false);
+ // IFolderLayout bottom = layout.createFolder("bottom",
+ // IPageLayout.BOTTOM, 0.70f, editorArea);
+ // bottom.addView("net.sourceforge.phpeclipse.news.view.headline");
+ IFolderLayout bottom = layout.createFolder("bottom", IPageLayout.BOTTOM, 0.50f, editorArea);
+ bottom.addView(BrowserView.ID_BROWSER);
+ IFolderLayout left = layout.createFolder("left", IPageLayout.LEFT, 0.30f, editorArea);
+ left.addView("net.sourceforge.phpeclipse.news.view.explorer");
+ IFolderLayout right = layout.createFolder("right", IPageLayout.RIGHT, 0.70f, editorArea);
+ right.addView("net.sourceforge.phpeclipse.news.view.headline");
+ }
+}
\ No newline at end of file
--- /dev/null
+package net.sourceforge.phpeclipse.news;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import net.sourceforge.phpeclipse.news.pref.ChannelStore;
+import net.sourceforge.phpeclipse.news.pref.ListEncoder;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPluginDescriptor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+public class Plugin extends AbstractUIPlugin {
+
+ public static final String BACKENDS_SECTION = "backends";
+
+ public static final String REFRESH_INTERVAL_PREFERENCE = "net.sourceforge.phpeclipse.news.refreshinterval";
+
+ /** @deprecated */
+ public static final String BACKENDS_PREFERENCE = "net.sourceforge.phpeclipse.news.backends";
+
+ public static final String BROWSER_PREFERENCE = "net.sourceforge.phpeclipse.news.browser";
+
+ public static final String BANNED_ITEMS_PREFERENCE = "net.sourceforge.phpeclipse.news.banneditems";
+
+ public static final String FORCE_CACHE_PREFERENCE = "net.sourceforge.phpeclipse.news.forcecache";
+
+ //Default values
+ public static final int DEFAULT_REFRESH_INTERVAL = 60;
+
+ public static final String DEFAULT_BROWSER = "C:\\Program Files\\Internet Explorer\\IEXPLORE.EXE";
+
+ public static final String DEFAULT_BANNED_ITEMS = "";
+
+ public static final boolean DEFAULT_FORCE_CACHE = false;
+
+ //User-Agent
+ public static String userAgent;
+
+ protected UpdateThread updateThread;
+
+ protected ArrayList views = new ArrayList();
+
+ protected Timer timer;
+
+ protected ArrayList channelList;
+
+ protected Object channelLock = new Object();
+
+ protected ArrayList banList = new ArrayList();
+
+ /**
+ * List of RssListeners to notify
+ */
+ private ArrayList rssListeners = new ArrayList();
+
+ /**
+ * Constructor for Plugin
+ */
+ public Plugin(IPluginDescriptor descriptor) {
+ super(descriptor);
+
+ //set the user-agent ID
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("AllTheNews/").append(descriptor.getVersionIdentifier()).append(" (").append(System.getProperty("os.name"))
+ .append("; ").append(System.getProperty("os.arch")).append("; http://www.jnegre.org/)");
+ userAgent = buffer.toString();
+
+ singleton = this;
+
+ //init the channel store
+ ChannelStore.init(this);
+
+ updateBanList();
+ updateChannelList();
+ setTimer();
+ }
+
+ protected static Plugin singleton;
+
+ public static Plugin getDefault() {
+ return singleton;
+ }
+
+ public static void logError(String message, Throwable t) {
+ getDefault().getLog()
+ .log(new Status(IStatus.ERROR, getDefault().getDescriptor().getUniqueIdentifier(), IStatus.OK, message, t));
+ }
+
+ public static void logInfo(String message, Throwable t) {
+ getDefault().getLog().log(new Status(IStatus.INFO, getDefault().getDescriptor().getUniqueIdentifier(), IStatus.OK, message, t));
+ }
+
+ /*
+ * @see AbstractUIPlugin#initializeDefaultPreferences(IPreferenceStore)
+ */
+ protected void initializeDefaultPreferences(IPreferenceStore store) {
+ store.setDefault(REFRESH_INTERVAL_PREFERENCE, DEFAULT_REFRESH_INTERVAL);
+ store.setDefault(BROWSER_PREFERENCE, DEFAULT_BROWSER);
+ store.setDefault(BANNED_ITEMS_PREFERENCE, DEFAULT_BANNED_ITEMS);
+ store.setDefault(FORCE_CACHE_PREFERENCE, DEFAULT_FORCE_CACHE);
+ }
+
+ protected ImageRegistry createImageRegistry() {
+ ImageRegistry registry = super.createImageRegistry();
+ IconManager.populateImageRegistry(registry);
+ return registry;
+ }
+
+ public void addRssListener(RssListener listener) {
+ synchronized (rssListeners) {
+ rssListeners.add(listener);
+ }
+ }
+
+ public void removeRssListener(RssListener listener) {
+ synchronized (rssListeners) {
+ rssListeners.remove(listener);
+ }
+ }
+
+ public void notifyChannelListChanged(RssListener source) {
+ Iterator iterator = rssListeners.iterator();
+ ArrayList channels = getChannelList();
+ while (iterator.hasNext()) {
+ RssListener listener = (RssListener) iterator.next();
+ if (listener != source) {
+ listener.onChannelListChanged(channels);
+ }
+ }
+ }
+
+ public void notifyChannelStatusChanged(Channel channel, RssListener source) {
+ Iterator iterator = rssListeners.iterator();
+ while (iterator.hasNext()) {
+ RssListener listener = (RssListener) iterator.next();
+ if (listener != source) {
+ listener.onChannelStatusChanged(channel);
+ }
+ }
+ }
+
+ public void notifyChannelSelected(Channel channel, RssListener source) {
+ Iterator iterator = rssListeners.iterator();
+ while (iterator.hasNext()) {
+ RssListener listener = (RssListener) iterator.next();
+ if (listener != source) {
+ listener.onChannelSelected(channel);
+ }
+ }
+ }
+
+ public void notifyItemSelected(Item item, RssListener source) {
+ Iterator iterator = rssListeners.iterator();
+ while (iterator.hasNext()) {
+ RssListener listener = (RssListener) iterator.next();
+ if (listener != source) {
+ listener.onItemSelected(item);
+ }
+ }
+ }
+
+ public void notifyItemStatusChanged(Item item, RssListener source) {
+ Iterator iterator = rssListeners.iterator();
+ while (iterator.hasNext()) {
+ RssListener listener = (RssListener) iterator.next();
+ if (listener != source) {
+ listener.onItemStatusChanged(item);
+ }
+ }
+ }
+
+ public void setTimer() {
+ if (timer != null) {
+ timer.cancel();
+ }
+ long period = getPreferenceStore().getInt(Plugin.REFRESH_INTERVAL_PREFERENCE) * 60000l;
+ if (period != 0) {
+ timer = new Timer(true);
+ timer.schedule(new UpdateTimer(), 0, period);
+ }
+ }
+
+ public void updateBanList() {
+ synchronized (banList) {
+ banList.clear();
+
+ String banned = this.getPreferenceStore().getString(Plugin.BANNED_ITEMS_PREFERENCE);
+ String[] bannedTitles = ListEncoder.decode(banned);
+ for (int i = 0; i < bannedTitles.length; i++) {
+ banList.add(bannedTitles[i]);
+ }
+ }
+ }
+
+ public boolean isBannedTitle(String title) {
+ synchronized (banList) {
+ return banList.contains(title);
+ }
+ }
+
+ public void updateChannelList() {
+ synchronized (channelLock) {
+ channelList = ChannelStore.getChannels();
+ }
+ notifyChannelListChanged(null);
+ }
+
+ public ArrayList getChannelList() {
+ synchronized (channelLock) {
+ return new ArrayList(channelList);
+ }
+ }
+
+ public void update() {
+ if (updateThread == null) {
+ updateThread = new UpdateThread();
+ updateThread.start();
+ }
+ }
+
+ protected class UpdateTimer extends TimerTask {
+ public void run() {
+ update();
+ }
+ }
+
+ public void shutdown() throws CoreException {
+ ChannelStore.saveReadStatus(getChannelList());
+ super.shutdown();
+ }
+
+ public IWorkbenchPage getActivePage() {
+ IWorkbenchWindow window = getWorkbench().getActiveWorkbenchWindow();
+ if (window != null)
+ return window.getActivePage();
+ return null;
+ }
+}
+
--- /dev/null
+/*
+ * Created on 15 mai 2004
+ * Copyright 2004 Jérôme Nègre
+ */
+package net.sourceforge.phpeclipse.news;
+
+import java.util.ArrayList;
+
+/**
+ * @author Jérôme Nègre
+ */
+public interface RssListener {
+
+ public void onChannelListChanged(ArrayList channels);
+
+ public void onChannelStatusChanged(Channel channel);
+
+ public void onChannelSelected(Channel channel);
+
+ public void onItemStatusChanged(Item tiem);
+
+ public void onItemSelected(Item tiem);
+}
--- /dev/null
+package net.sourceforge.phpeclipse.news;
+
+import java.util.Iterator;
+
+public class UpdateThread extends Thread {
+
+
+ /**
+ * Constructor for UpdateThread
+ */
+ public UpdateThread() {
+ super();
+ this.setDaemon(true);
+ }
+
+ /**
+ * @see Runnable#run()
+ */
+ public void run() {
+ try {
+ Plugin plugin = Plugin.getDefault();
+ Iterator iterator = Plugin.getDefault().getChannelList().iterator();
+ while(iterator.hasNext()) {
+ Channel channel = (Channel)iterator.next();
+ channel.setRefreshing(true);
+ plugin.notifyChannelStatusChanged(channel, null);
+ channel.update();
+ channel.setRefreshing(false);
+ plugin.notifyChannelStatusChanged(channel, null);
+ }
+ } finally {
+ Plugin.getDefault().updateThread = null;
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+#0 = description; 1=description with BR instead of \n; 2=url; 3=title
+BrowserView.DescriptionTemplate=<html><head><title>{3}</title></head><body><h2>{3}</h2><div>{1}</div><p><a href="{2}">Read more...</a></p></body></html>
+BrowserView.NoDescription=<i>No description</i>
--- /dev/null
+package net.sourceforge.phpeclipse.news.pref;
+
+import net.sourceforge.phpeclipse.news.Plugin;
+
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.preference.ListEditor;
+import org.eclipse.swt.widgets.Composite;
+
+public class BanListEditor extends ListEditor {
+
+ /**
+ * Constructor for SiteListEditor
+ */
+ protected BanListEditor(String name, String labelText, Composite parent) {
+ super(name, labelText, parent);
+ }
+
+ /**
+ * @see ListEditor#parseString(String)
+ */
+ protected String[] parseString(String stringList) {
+ return ListEncoder.decode(stringList);
+ }
+
+ /**
+ * @see ListEditor#getNewInputObject()
+ */
+ protected String getNewInputObject() {
+ InputDialog dialog;
+ dialog = new InputDialog(this.getShell(),"All The News","Enter item title to ban","",null);
+ dialog.open();
+ if("".equals(dialog.getValue()) || dialog.getValue()==null) {
+ return null;
+ } else {
+ return dialog.getValue();
+ }
+ }
+
+ /**
+ * @see ListEditor#createList(String[])
+ */
+ protected String createList(String[] items) {
+ return ListEncoder.encode(items);
+ }
+
+ /**
+ * @see org.eclipse.jface.preference.FieldEditor#doStore()
+ */
+ protected void doStore() {
+ super.doStore();
+ Plugin.getDefault().updateBanList();
+ }
+
+
+ /**
+ * @see org.eclipse.jface.preference.FieldEditor#doLoadDefault()
+ */
+ protected void doLoadDefault() {
+ super.doLoadDefault();
+ Plugin.getDefault().updateBanList();
+ }
+}
+
--- /dev/null
+/*
+ * Created on 9 juin 2004
+ * Copyright 2004 Jérôme Nègre
+ */
+package net.sourceforge.phpeclipse.news.pref;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Properties;
+
+import net.sourceforge.phpeclipse.news.Channel;
+import net.sourceforge.phpeclipse.news.Item;
+import net.sourceforge.phpeclipse.news.Plugin;
+
+import org.eclipse.jface.dialogs.IDialogSettings;
+
+/**
+ * @author Jérôme Nègre
+ */
+public class ChannelStore {
+
+ private final static String DEFAULT_CHANNELS_FILE = "default_feeds.properties";
+
+ private final static String BACKENDS_SECTION = "backends";
+
+ private final static String CHANNELS_ORDER_KEY = "order";
+ private final static String TITLE_KEY = "title";
+ private final static String URL_KEY = "url";
+ private final static String TYPE_KEY = "type";
+ private final static String READ_KEY = "read";
+
+ private final static int TYPE_CHANNEL = 1;
+
+ private static Plugin plugin = null;
+
+ public static void init(Plugin plugin) {
+ ChannelStore.plugin = plugin;
+ }
+
+ public static synchronized ArrayList getChannels() {
+ IDialogSettings section = getChannelsSection();
+ String[] uids = section.getArray(CHANNELS_ORDER_KEY);
+ ArrayList result = new ArrayList();
+ for(int i=0; i<uids.length; i++) {
+ String uid = uids[i];
+ IDialogSettings channelSection = section.getSection(uid);
+ String title = channelSection.get(TITLE_KEY);
+ String url = channelSection.get(URL_KEY);
+
+ String[] readUids = channelSection.getArray(READ_KEY);
+ HashSet set = new HashSet();
+ if(readUids != null) {
+ for(int k=0; k<readUids.length; k++) {
+ set.add(readUids[k]);
+ }
+ }
+
+ result.add(new Channel(title, url, set));
+ }
+ return result;
+ }
+
+ public static synchronized void setChannels(ArrayList channels) {
+ IDialogSettings section = getChannelsSection();
+ section.put(CHANNELS_ORDER_KEY,new String[0]);
+ int newSize = channels.size();
+ for(int i=0; i<newSize; i++) {
+ Channel channel = (Channel)channels.get(i);
+ addChannel(section,channel);
+ }
+ }
+
+ public static synchronized void saveReadStatus(ArrayList channels) {
+ IDialogSettings channelsSection = getChannelsSection();
+ Iterator channelIter = channels.iterator();
+ while(channelIter.hasNext()) {
+ Channel channel = (Channel)channelIter.next();
+ IDialogSettings section = channelsSection.getSection(channel.getUID());
+ Iterator itemIter = channel.getItems().iterator();
+ ArrayList readItems = new ArrayList();
+ while(itemIter.hasNext()) {
+ Item item = (Item)itemIter.next();
+ if(item.isReadFlag()) {
+ readItems.add(item.getUID());
+ }
+ }
+ section.put(READ_KEY,(String[])readItems.toArray(new String[0]));
+ }
+ }
+
+ public static synchronized ArrayList getDefaultChannels() {
+ ArrayList result = new ArrayList();
+ try {
+ Properties prop = new Properties();
+ URL propLocation = new URL(Plugin.getDefault().getDescriptor().getInstallURL(), DEFAULT_CHANNELS_FILE);
+ prop.load(propLocation.openStream());
+ Enumeration e = prop.propertyNames();
+ while(e.hasMoreElements()) {
+ String url = (String)e.nextElement();
+ String title = prop.getProperty(url);
+ result.add(new Channel(title, url));
+ }
+ } catch(Exception e) {
+ Plugin.logError("Error while getting default feed list", e);
+ }
+ return result;
+
+ }
+
+ /**
+ * Returns a non null Channels Section,
+ * creating it if needed.
+ * @return
+ */
+ private static IDialogSettings getChannelsSection() {
+ IDialogSettings section = plugin.getDialogSettings().getSection(BACKENDS_SECTION);
+ if(section == null) {
+ section = createDefaultChannelsSection();
+ }
+ return section;
+ }
+
+ private static IDialogSettings createDefaultChannelsSection() {
+ IDialogSettings section = plugin.getDialogSettings().addNewSection(BACKENDS_SECTION);
+ section.put(CHANNELS_ORDER_KEY,new String[0]);
+ //add some default channels from config file
+ Iterator iterator = getDefaultChannels().iterator();
+ while(iterator.hasNext()) {
+ addChannel(section, (Channel)iterator.next());
+ }
+ return section;
+ }
+
+ private static void addChannel(IDialogSettings backendSection, Channel channel) {
+ String title = channel.getTitle();
+ String url = channel.getUrl();
+ String uid = channel.getUID();
+ //check that section does not already exist before
+ //creating it, and if it exists, add it to the order key
+ //only if it's not already in it.
+ IDialogSettings section = backendSection.getSection(uid);
+ boolean addInOrder = true;
+ if(section == null) {
+ //create section
+ section = backendSection.addNewSection(uid);
+ } else {
+ //check if the section is already in the order key
+ String[] orders = backendSection.getArray(CHANNELS_ORDER_KEY);
+ for(int i=0; i<orders.length; i++) {
+ if(orders[i].equals(uid)) {
+ addInOrder = false;
+ break;
+ }
+ }
+ }
+ //set data
+ section.put(TITLE_KEY, title);
+ section.put(URL_KEY, url);
+ section.put(TYPE_KEY, TYPE_CHANNEL);
+ //set order key if needed
+ if(addInOrder) {
+ String[] oldOrder = backendSection.getArray(CHANNELS_ORDER_KEY);
+ String[] newOrder = new String[oldOrder.length+1];
+ System.arraycopy(oldOrder, 0, newOrder, 0, oldOrder.length);
+ newOrder[oldOrder.length] = uid;
+ backendSection.put(CHANNELS_ORDER_KEY,newOrder);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Created on 14 juil. 2003
+ * (c)2003 Jérôme Nègre - http://www.jnegre.org/
+ *
+ */
+package net.sourceforge.phpeclipse.news.pref;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.StringTokenizer;
+
+import net.sourceforge.phpeclipse.news.Plugin;
+
+/**
+ * @author jerome
+ *
+ */
+public class ListEncoder {
+
+ public static String[] decode(String stringList) {
+ StringTokenizer tokenizer = new StringTokenizer(stringList, " ");
+ int countTokens = tokenizer.countTokens();
+ String[] result = new String[countTokens];
+ try {
+ for (int i = 0; i < countTokens; i++) {
+ result[i] = URLDecoder.decode(tokenizer.nextToken(), "UTF-8");
+ }
+ } catch (UnsupportedEncodingException e) {
+ Plugin.logError("Internal Error", e);
+ }
+ return result;
+ }
+
+ public static String encode(String[] items) {
+ StringBuffer result = new StringBuffer();
+ try {
+ for (int i = 0; i < items.length; i++) {
+ result.append(URLEncoder.encode(items[i], "UTF-8")).append(' ');
+ }
+ } catch (UnsupportedEncodingException e) {
+ Plugin.logError("Internal Error", e);
+ }
+ return result.toString();
+ }
+
+}
--- /dev/null
+package net.sourceforge.phpeclipse.news.pref;
+
+import net.sourceforge.phpeclipse.news.Plugin;
+
+import org.eclipse.jface.preference.BooleanFieldEditor;
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.jface.preference.FileFieldEditor;
+import org.eclipse.jface.preference.IntegerFieldEditor;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class PreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage {
+
+ protected SiteListEditor siteListFE;
+ protected BanListEditor banListFE;
+ protected FileFieldEditor browserAppFE;
+ protected IntegerFieldEditor refreshFE;
+ protected BooleanFieldEditor forceCacheFE;
+
+ public PreferencePage() {
+ super("All The News", FieldEditorPreferencePage.GRID);
+ }
+
+ /**
+ * @see FieldEditorPreferencePage#createFieldEditors()
+ */
+ protected void createFieldEditors() {
+ siteListFE = new SiteListEditor(Plugin.BACKENDS_PREFERENCE, "Sites", getFieldEditorParent());
+ banListFE = new BanListEditor(Plugin.BANNED_ITEMS_PREFERENCE, "Banned items", getFieldEditorParent());
+ browserAppFE = new FileFieldEditor(Plugin.BROWSER_PREFERENCE, "External Browser", getFieldEditorParent());
+ refreshFE = new IntegerFieldEditor(Plugin.REFRESH_INTERVAL_PREFERENCE,"Refresh interval (minutes)", getFieldEditorParent());
+ refreshFE.setValidRange(0,10000);
+
+ forceCacheFE = new BooleanFieldEditor(Plugin.FORCE_CACHE_PREFERENCE,"Force refresh from proxy", getFieldEditorParent());
+
+ addField(siteListFE);
+ addField(banListFE);
+ addField(browserAppFE);
+ addField(refreshFE);
+ addField(forceCacheFE);
+ }
+
+ /**
+ * @see IWorkbenchPreferencePage#init(IWorkbench)
+ */
+ public void init(IWorkbench workbench) {
+ setPreferenceStore(Plugin.getDefault().getPreferenceStore());
+ }
+
+ /**
+ * @see org.eclipse.jface.preference.IPreferencePage#performOk()
+ */
+ public boolean performOk() {
+ boolean result = super.performOk();
+ Plugin.getDefault().setTimer();
+ return result;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Jérôme Nègre - adaptation of ListEditor to add the search button
+ *******************************************************************************/
+package net.sourceforge.phpeclipse.news.pref;
+
+import java.util.ArrayList;
+
+import net.sourceforge.phpeclipse.news.Channel;
+import net.sourceforge.phpeclipse.news.Plugin;
+import net.sourceforge.phpeclipse.news.search.SearchDialog;
+
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.preference.FieldEditor;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.Assert;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Widget;
+
+public class SiteListEditor extends FieldEditor {
+ /**
+ * The list widget; <code>null</code> if none
+ * (before creation or after disposal).
+ */
+ private List list;
+ private ArrayList channels;
+ /**
+ * The button box containing the Add, Remove, Up, and Down buttons;
+ * <code>null</code> if none (before creation or after disposal).
+ */
+ private Composite buttonBox;
+ /**
+ * The Add button.
+ */
+ private Button addButton;
+ /**
+ * The Search button.
+ */
+ private Button searchButton;
+ /**
+ * The Remove button.
+ */
+ private Button removeButton;
+ /**
+ * The Up button.
+ */
+ private Button upButton;
+ /**
+ * The Down button.
+ */
+ private Button downButton;
+ /**
+ * The selection listener.
+ */
+ private SelectionListener selectionListener;
+ /**
+ * Notifies that the Add button has been pressed.
+ */
+ private void addPressed() {
+ setPresentsDefaultValue(false);
+ Channel input = getNewInputChannel();
+ if (input != null) {
+ int index = list.getSelectionIndex();
+ if (index >= 0) {
+ list.add(input.getTitle(), index + 1);
+ channels.add(index + 1, input);
+ } else {
+ list.add(input.getTitle(), 0);
+ channels.add(0, input);
+ }
+ selectionChanged();
+ }
+ }
+
+ /**
+ * Notifies that the Search button has been pressed.
+ */
+ private void searchPressed() {
+ setPresentsDefaultValue(false);
+ SearchDialog sd = new SearchDialog(SiteListEditor.this
+ .getShell());
+ sd.open();
+ Channel[] inputs = sd.getChannels();
+ for(int i=0; i<inputs.length; i++) {
+ int index = list.getSelectionIndex();
+ if (index >= 0) {
+ list.add(inputs[i].getTitle(), index + 1);
+ channels.add(index + 1, inputs[i]);
+ } else {
+ list.add(inputs[i].getTitle(), 0);
+ channels.add(0, inputs[i]);
+ }
+ }
+ selectionChanged();
+ }
+
+
+ /* (non-Javadoc)
+ * Method declared on FieldEditor.
+ */
+ protected void adjustForNumColumns(int numColumns) {
+ Control control = getLabelControl();
+ ((GridData) control.getLayoutData()).horizontalSpan = numColumns;
+ ((GridData) list.getLayoutData()).horizontalSpan = numColumns - 1;
+ }
+ /**
+ * Creates the Add, Remove, Up, and Down button in the given button box.
+ *
+ * @param buttonBox the box for the buttons
+ */
+ private void createButtons(Composite buttonBox) {
+ addButton = createPushButton(buttonBox, "ListEditor.add");//$NON-NLS-1$
+ //TODO use my bundle ?
+ searchButton = createPushButton(buttonBox, "Search (experimental)");
+ removeButton = createPushButton(buttonBox, "ListEditor.remove");//$NON-NLS-1$
+ upButton = createPushButton(buttonBox, "ListEditor.up");//$NON-NLS-1$
+ downButton = createPushButton(buttonBox, "ListEditor.down");//$NON-NLS-1$
+ }
+ /**
+ * Helper method to create a push button.
+ *
+ * @param parent the parent control
+ * @param key the resource name used to supply the button's label text
+ */
+ private Button createPushButton(Composite parent, String key) {
+ Button button = new Button(parent, SWT.PUSH);
+ button.setText(JFaceResources.getString(key));
+ button.setFont(parent.getFont());
+ GridData data = new GridData(GridData.FILL_HORIZONTAL);
+ data.heightHint = convertVerticalDLUsToPixels(button,
+ IDialogConstants.BUTTON_HEIGHT);
+ int widthHint = convertHorizontalDLUsToPixels(button,
+ IDialogConstants.BUTTON_WIDTH);
+ data.widthHint = Math.max(widthHint, button.computeSize(SWT.DEFAULT,
+ SWT.DEFAULT, true).x);
+ button.setLayoutData(data);
+ button.addSelectionListener(getSelectionListener());
+ return button;
+ }
+ /**
+ * Creates a selection listener.
+ */
+ public void createSelectionListener() {
+ selectionListener = new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent event) {
+ Widget widget = event.widget;
+ if (widget == addButton) {
+ addPressed();
+ } else if (widget == searchButton) {
+ searchPressed();
+ } else if (widget == removeButton) {
+ removePressed();
+ } else if (widget == upButton) {
+ upPressed();
+ } else if (widget == downButton) {
+ downPressed();
+ } else if (widget == list) {
+ selectionChanged();
+ }
+ }
+ };
+ }
+ /* (non-Javadoc)
+ * Method declared on FieldEditor.
+ */
+ protected void doFillIntoGrid(Composite parent, int numColumns) {
+ Control control = getLabelControl(parent);
+ GridData gd = new GridData();
+ gd.horizontalSpan = numColumns;
+ control.setLayoutData(gd);
+ list = getListControl(parent);
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.verticalAlignment = GridData.FILL;
+ gd.horizontalSpan = numColumns - 1;
+ gd.grabExcessHorizontalSpace = true;
+ list.setLayoutData(gd);
+ buttonBox = getButtonBoxControl(parent);
+ gd = new GridData();
+ gd.verticalAlignment = GridData.BEGINNING;
+ buttonBox.setLayoutData(gd);
+ }
+ /* (non-Javadoc)
+ * Method declared on FieldEditor.
+ */
+ protected void doLoad() {
+ if (list != null) {
+ channels = ChannelStore.getChannels();
+ for (int i = 0; i < channels.size(); i++) {
+ list.add(((Channel)channels.get(i)).getTitle());
+ }
+ }
+ }
+ /* (non-Javadoc)
+ * Method declared on FieldEditor.
+ */
+ protected void doLoadDefault() {
+ if (list != null) {
+ list.removeAll();
+ channels = ChannelStore.getDefaultChannels();
+ for (int i = 0; i < channels.size(); i++) {
+ list.add(((Channel)channels.get(i)).getTitle());
+ }
+ setPresentsDefaultValue(false);
+ }
+ }
+ /* (non-Javadoc)
+ * Method declared on FieldEditor.
+ */
+ protected void doStore() {
+ ChannelStore.saveReadStatus(Plugin.getDefault().getChannelList());
+ ChannelStore.setChannels(channels);
+ Plugin.getDefault().updateChannelList();
+ }
+ /**
+ * Notifies that the Down button has been pressed.
+ */
+ private void downPressed() {
+ swap(false);
+ }
+ /**
+ * Returns this field editor's button box containing the Add, Remove,
+ * Up, and Down button.
+ *
+ * @param parent the parent control
+ * @return the button box
+ */
+ public Composite getButtonBoxControl(Composite parent) {
+ if (buttonBox == null) {
+ buttonBox = new Composite(parent, SWT.NULL);
+ GridLayout layout = new GridLayout();
+ layout.marginWidth = 0;
+ buttonBox.setLayout(layout);
+ createButtons(buttonBox);
+ buttonBox.addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent event) {
+ addButton = null;
+ searchButton = null;
+ removeButton = null;
+ upButton = null;
+ downButton = null;
+ buttonBox = null;
+ }
+ });
+ } else {
+ checkParent(buttonBox, parent);
+ }
+ selectionChanged();
+ return buttonBox;
+ }
+ /**
+ * Returns this field editor's list control.
+ *
+ * @param parent the parent control
+ * @return the list control
+ */
+ public List getListControl(Composite parent) {
+ if (list == null) {
+ list = new List(parent, SWT.BORDER | SWT.SINGLE | SWT.V_SCROLL
+ | SWT.H_SCROLL);
+ list.setFont(parent.getFont());
+ list.addSelectionListener(getSelectionListener());
+ list.addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent event) {
+ list = null;
+ }
+ });
+ } else {
+ checkParent(list, parent);
+ }
+ return list;
+ }
+ /* (non-Javadoc)
+ * Method declared on FieldEditor.
+ */
+ public int getNumberOfControls() {
+ return 2;
+ }
+ /**
+ * Returns this field editor's selection listener.
+ * The listener is created if nessessary.
+ *
+ * @return the selection listener
+ */
+ private SelectionListener getSelectionListener() {
+ if (selectionListener == null)
+ createSelectionListener();
+ return selectionListener;
+ }
+ /**
+ * Returns this field editor's shell.
+ * <p>
+ * This method is internal to the framework; subclassers should not call
+ * this method.
+ * </p>
+ *
+ * @return the shell
+ */
+ protected Shell getShell() {
+ if (addButton == null)
+ return null;
+ return addButton.getShell();
+ }
+ /**
+ * Notifies that the Remove button has been pressed.
+ */
+ private void removePressed() {
+ setPresentsDefaultValue(false);
+ int index = list.getSelectionIndex();
+ if (index >= 0) {
+ list.remove(index);
+ channels.remove(index);
+ selectionChanged();
+ }
+ }
+ /**
+ * Notifies that the list selection has changed.
+ */
+ private void selectionChanged() {
+ int index = list.getSelectionIndex();
+ int size = list.getItemCount();
+ removeButton.setEnabled(index >= 0);
+ upButton.setEnabled(size > 1 && index > 0);
+ downButton.setEnabled(size > 1 && index >= 0 && index < size - 1);
+ }
+ /* (non-Javadoc)
+ * Method declared on FieldEditor.
+ */
+ public void setFocus() {
+ if (list != null) {
+ list.setFocus();
+ }
+ }
+ /**
+ * Moves the currently selected item up or down.
+ *
+ * @param up <code>true</code> if the item should move up,
+ * and <code>false</code> if it should move down
+ */
+ private void swap(boolean up) {
+ setPresentsDefaultValue(false);
+ int index = list.getSelectionIndex();
+ int target = up ? index - 1 : index + 1;
+ if (index >= 0) {
+ //list widget
+ String[] selection = list.getSelection();
+ Assert.isTrue(selection.length == 1);
+ list.remove(index);
+ list.add(selection[0], target);
+ list.setSelection(target);
+ //channels arrayList
+ Object obj = channels.remove(index);
+ channels.add(target, obj);
+ }
+ selectionChanged();
+ }
+ /**
+ * Notifies that the Up button has been pressed.
+ */
+ private void upPressed() {
+ swap(true);
+ }
+ /*
+ * @see FieldEditor.setEnabled(boolean,Composite).
+ */
+ public void setEnabled(boolean enabled, Composite parent) {
+ super.setEnabled(enabled, parent);
+ getListControl(parent).setEnabled(enabled);
+ addButton.setEnabled(enabled);
+ removeButton.setEnabled(enabled);
+ upButton.setEnabled(enabled);
+ downButton.setEnabled(enabled);
+ }
+
+ /*
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+
+ /**
+ * Creates a site list field editor.
+ *
+ * @param name the name of the preference this field editor works on
+ * @param labelText the label text of the field editor
+ * @param parent the parent of the field editor's control
+ */
+ protected SiteListEditor(String name, String labelText, Composite parent) {
+ init(name, labelText);
+ createControl(parent);
+ }
+ /**
+ * Creates and returns a new item for the list.
+ *
+ * @return a new item
+ */
+ protected Channel getNewInputChannel() {
+ InputDialog dialog;
+ dialog = new InputDialog(this.getShell(), "All The News",
+ "Enter new site name", "", null);
+ dialog.open();
+ if ("".equals(dialog.getValue()) || dialog.getValue() == null)
+ return null;
+ String title = dialog.getValue();
+ dialog = new InputDialog(this.getShell(), "All The News",
+ "Enter new site URL", "", null);
+ dialog.open();
+ if ("".equals(dialog.getValue()) || dialog.getValue() == null)
+ return null;
+ String url = dialog.getValue();
+ return new Channel(title, url);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Created on 25 janv. 2004
+ *
+ * (c)2004 Jérôme Nègre - http://www.jnegre.org/
+ */
+
+package net.sourceforge.phpeclipse.news.search;
+
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Vector;
+
+import net.sourceforge.phpeclipse.news.Channel;
+
+import org.apache.xmlrpc.XmlRpcClient;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @author Jérôme Nègre
+ *
+ */
+public class SearchDialog extends Dialog {
+
+ private Label statusBar;
+ private Text searchText;
+ private List list;
+ private Text name;
+ private Text siteUrl;
+ private Text feedUrl;
+ private Text version;
+ private Text description;
+
+ private ArrayList resultChannels = new ArrayList();
+
+ private XmlRpcClient xmlRpcClient;
+
+ /**
+ * @param parentShell
+ */
+ public SearchDialog(Shell parentShell) {
+ super(parentShell);
+ this.setShellStyle(SWT.DIALOG_TRIM | SWT.RESIZE | SWT.MODELESS);
+ }
+
+ protected void configureShell(Shell newShell) {
+ super.configureShell(newShell);
+ newShell.setText("Search using http://www.syndic8.com/");
+ }
+
+ /**
+ * Adds the controls to the dialog
+ * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
+ */
+ protected Control createDialogArea(Composite parent) {
+ Composite composite = (Composite)super.createDialogArea(parent);
+ GridLayout gl = (GridLayout)composite.getLayout();
+ gl.numColumns = 4;
+
+ //Text to enter the searched words
+ searchText = new Text(composite,SWT.BORDER);
+ GridData gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.horizontalSpan = 3;
+ searchText.setLayoutData(gd);
+ //Button "search!"
+ Button searchButton = new Button(composite,0);
+ searchButton.setText("Search!");
+ searchButton.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ //TODO use a worker
+ Cursor waitCursor = new Cursor(SearchDialog.this.getContents().getDisplay(), SWT.CURSOR_WAIT);
+ try {
+ SearchDialog.this.getContents().setCursor(waitCursor);
+ SearchDialog.this.searchNow();
+ } catch (Exception x) {
+ setStatusMessage("Error: "+x.getMessage());
+ } finally {
+ SearchDialog.this.getContents().setCursor(null);
+ waitCursor.dispose();
+ }
+ }
+ });
+ //List for the titles of the feeds
+ list = new List(composite,SWT.BORDER|SWT.H_SCROLL|SWT.V_SCROLL|SWT.SINGLE);
+ gd = new GridData(GridData.FILL_BOTH);
+ list.setLayoutData(gd);
+ list.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ SearchDialog.this.showFieldDetails((Hashtable)((ArrayList)list.getData()).get(list.getSelectionIndex()));
+ }
+ });
+
+ //Description of the selected feed
+ Group group = new Group(composite,0);
+ group.setText("Selected Feed");
+ gd = new GridData(GridData.FILL_BOTH);
+ gd.horizontalSpan = 3;
+ group.setLayoutData(gd);
+ group.setLayout(new GridLayout(2,false));
+ //name
+ new Label(group,0).setText("Name:");
+ name = new Text(group,SWT.BORDER|SWT.READ_ONLY);
+ name.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ //site url
+ new Label(group,0).setText("Site URL:");
+ siteUrl = new Text(group,SWT.BORDER|SWT.READ_ONLY);
+ siteUrl.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ //feed url
+ new Label(group,0).setText("Feed URL:");
+ feedUrl = new Text(group,SWT.BORDER|SWT.READ_ONLY);
+ feedUrl.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ //RSS version
+ new Label(group,0).setText("RSS version:");
+ version = new Text(group,SWT.BORDER|SWT.READ_ONLY);
+ version.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ //description
+ new Label(group,0).setText("Description:");
+ description = new Text(group,SWT.BORDER|SWT.READ_ONLY|SWT.MULTI|SWT.H_SCROLL|SWT.V_SCROLL|SWT.WRAP);
+ description.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ //status bar
+ statusBar = new Label(composite,SWT.NONE);
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.horizontalSpan = 4;
+ statusBar.setLayoutData(gd);
+ setStatusMessage("Ready.");
+ return composite;
+ }
+
+ private XmlRpcClient getXmlRpcClient() throws MalformedURLException {
+ if(this.xmlRpcClient == null) {
+ this.xmlRpcClient = new XmlRpcClient("http://www.syndic8.com/xmlrpc.php");
+ }
+ return this.xmlRpcClient;
+ }
+
+ /**
+ *
+ */
+ protected void searchNow() throws Exception {
+ clearFeedList();
+ setStatusMessage("Connecting...");
+ XmlRpcClient client = getXmlRpcClient();
+ //Get the list of ids
+ Vector args = new Vector();
+ args.add(searchText.getText());
+ args.add("sitename");
+ //args.add(new Integer(30));
+ Vector ids = (Vector)client.execute("syndic8.FindFeeds",args);
+ setStatusMessage("Found "+ids.size()+" result(s), asking for details...");
+ //Get the descriptions of the feeds
+ Vector fields = new Vector();
+ fields.add("sitename");
+ fields.add("siteurl");
+ fields.add("dataurl");
+ fields.add("rss_version");
+ fields.add("description");
+ args.clear();
+ args.add(ids);
+ args.add(fields);
+ Vector infos = (Vector)client.execute("syndic8.GetFeedInfo",args);
+ setStatusMessage("Showing details...");
+ Iterator iterator = infos.iterator();
+ while(iterator.hasNext()) {
+ Hashtable info = (Hashtable)iterator.next();
+ addFeedInList(info);
+ }
+ setStatusMessage("Ready.");
+ }
+
+ protected void clearFeedList() {
+ list.removeAll();
+ showFieldDetails(null);
+ list.setData(new ArrayList());
+ }
+
+ protected void showFieldDetails(Hashtable info) {
+ name.setText(info==null?"":(String)info.get("sitename"));
+ siteUrl.setText(info==null?"":(String)info.get("siteurl"));
+ feedUrl.setText(info==null?"":(String)info.get("dataurl"));
+ version.setText(info==null?"":(String)info.get("rss_version"));
+ description.setText(info==null?"":(String)info.get("description"));
+ }
+
+ protected void addFeedInList(Hashtable info) {
+ String name = (String)info.get("sitename");
+ String dataurl = (String)info.get("dataurl");
+ if("".equals(name) || "".equals(dataurl)) {
+ //skip it
+ return;
+ }
+ ArrayList al = (ArrayList)list.getData();
+ al.add(info);
+ list.add(name);
+ }
+
+ protected void setStatusMessage(String message) {
+ statusBar.setText(message);
+ //TODO remove next line
+ System.out.println(message);
+ }
+
+ protected void createButtonsForButtonBar(Composite parent) {
+ createButton(parent,IDialogConstants.OPEN_ID,"Add Selected",false);
+ createButton(parent,IDialogConstants.OK_ID,IDialogConstants.OK_LABEL,false);
+ }
+
+ protected void buttonPressed(int buttonId) {
+ super.buttonPressed(buttonId);
+ if(buttonId == IDialogConstants.OPEN_ID) {
+ String name = this.name.getText();
+ String url = this.feedUrl.getText();
+ if(!"".equals(name) && !"".equals(url)) {
+ resultChannels.add(new Channel(name, url));
+ }
+ }
+ }
+
+ public Channel[] getChannels() {
+ return (Channel[])resultChannels.toArray(new Channel[]{});
+ }
+}
--- /dev/null
+/*
+ * Created on 15 mai 2004
+ * Copyright 2004 Jérôme Nègre
+ */
+package net.sourceforge.phpeclipse.news.view;
+
+import java.util.ArrayList;
+
+import net.sourceforge.phpeclipse.news.Channel;
+import net.sourceforge.phpeclipse.news.IconManager;
+import net.sourceforge.phpeclipse.news.Item;
+import net.sourceforge.phpeclipse.news.Plugin;
+import net.sourceforge.phpeclipse.news.RssListener;
+import net.sourceforge.phpeclipse.webbrowser.views.BrowserView;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.part.ViewPart;
+
+/**
+ * @author Jérôme Nègre
+ */
+public class ExplorerView extends ViewPart implements RssListener {
+
+ private TreeViewer treeViewer;
+
+ private NewsTreeViewerProvider provider;
+
+ private Action refreshAction;
+
+ public ExplorerView() {
+ super();
+ }
+
+ public void dispose() {
+ Plugin.getDefault().removeRssListener(this);
+ super.dispose();
+ }
+
+ public void createPartControl(Composite parent) {
+ treeViewer = new TreeViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
+ provider = new NewsTreeViewerProvider();
+ treeViewer.setContentProvider(provider);
+ treeViewer.setLabelProvider(provider);
+ treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ Object selected = ((StructuredSelection) event.getSelection()).getFirstElement();
+ if (selected != null) {
+ if (selected instanceof Channel) {
+ Plugin.getDefault().notifyChannelSelected((Channel) selected, ExplorerView.this);
+ } else if (selected instanceof Item) {
+ Plugin.getDefault().notifyItemSelected((Item) selected, ExplorerView.this);
+ }
+ }
+ }
+ });
+
+ createActions();
+ createMenu();
+ createToolBar();
+
+ Plugin.getDefault().addRssListener(this);
+ treeViewer.setInput(Plugin.getDefault());
+ }
+
+ public void setFocus() {
+ treeViewer.getControl().setFocus();
+ }
+
+ public void onChannelListChanged(ArrayList channels) {
+ treeViewer.setInput(Plugin.getDefault());
+ }
+
+ public void onChannelStatusChanged(final Channel channel) {
+ treeViewer.getControl().getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ treeViewer.refresh(channel);
+ }
+ });
+ }
+
+ public void onChannelSelected(Channel channel) {
+ System.out.println("Explorer.onChannelSelected -> " + channel);
+ }
+
+ public void onItemStatusChanged(final Item item) {
+ treeViewer.getControl().getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ treeViewer.refresh(item);
+ }
+ });
+ }
+
+ private void createActions() {
+ //refresh
+ refreshAction = new Action("Refresh", IconManager.getImageDescriptor(IconManager.ICON_ACTION_REFRESH)) {
+ public void run() {
+ Plugin.getDefault().update();
+ }
+ };
+ refreshAction.setToolTipText("Refresh");
+ }
+
+ private void createMenu() {
+ //IMenuManager mgr = getViewSite().getActionBars().getMenuManager();
+ //mgr.add(clearAction);
+ }
+
+ private void createToolBar() {
+ IToolBarManager mgr = getViewSite().getActionBars().getToolBarManager();
+ mgr.add(refreshAction);
+ }
+
+ private void setUrl(String url) {
+ IWorkbenchPage page = Plugin.getDefault().getActivePage();
+ try {
+ IViewPart part = page.findView(BrowserView.ID_BROWSER);
+ if (part == null) {
+ part = page.showView(BrowserView.ID_BROWSER);
+ } else {
+ page.bringToTop(part);
+ }
+ ((BrowserView) part).setUrl(url);
+ } catch (Exception e) {
+ }
+ }
+
+ // public void onItemSelected(Item tiem) {
+ // // NOP
+ // }
+ public void onItemSelected(Item item) {
+ if (item != null) { // && uiReady && linkAction.isChecked()) {
+ // if(showDescritionAction.isChecked()) {
+ // String desc = item.getDescription();
+ // if(desc == null)
+ // desc = HTML_NO_DESCRIPTION;
+ // browser.setText(MessageFormat.format(HTML,new String[]{desc, encodeNewLine(desc), item.getUsableLink(),
+ // item.getUsableTitle()}));
+ // } else {
+ setUrl(item.getUsableLink());
+ // }
+ //XXX this is a hack, should be done otherwise
+ boolean channelStatus = item.getChannel().isUnread();
+ item.setReadFlag(true);
+ Plugin.getDefault().notifyItemStatusChanged(item, this);
+ if (channelStatus != item.getChannel().isUnread()) {
+ Plugin.getDefault().notifyChannelStatusChanged(item.getChannel(), this);
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Created on 15 mai 2004
+ * Copyright 2004 Jérôme Nègre
+ */
+package net.sourceforge.phpeclipse.news.view;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import net.sourceforge.phpeclipse.news.Channel;
+import net.sourceforge.phpeclipse.news.IconManager;
+import net.sourceforge.phpeclipse.news.Item;
+import net.sourceforge.phpeclipse.news.Plugin;
+import net.sourceforge.phpeclipse.news.RssListener;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.ui.part.ViewPart;
+
+/**
+ * @author Jérôme Nègre
+ */
+public class HeadlineView extends ViewPart implements RssListener {
+
+ Table table;
+
+ public HeadlineView() {
+ super();
+ }
+
+ public void dispose() {
+ Plugin.getDefault().removeRssListener(this);
+ super.dispose();
+ }
+
+ private TableColumn createColumn(int style, int width, String text) {
+ TableColumn col = new TableColumn(table, style);
+ col.setWidth(width);
+ col.setText(text);
+ return col;
+ }
+
+ public void createPartControl(Composite parent) {
+ table = new Table(parent, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION);
+ //TODO remember the width of the columns
+ createColumn(SWT.LEFT, 120, "Publication Date");
+ createColumn(SWT.CENTER, 20, "");
+ createColumn(SWT.LEFT, 600, "Title");
+ table.setHeaderVisible(true);
+ table.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ Item item = (Item) e.item.getData();
+ Plugin.getDefault().notifyItemSelected(item,HeadlineView.this);
+ }
+ });
+ Plugin.getDefault().addRssListener(this);
+ }
+
+ public void setFocus() {
+ table.setFocus();
+ }
+
+ public void onChannelListChanged(ArrayList channels) {
+ // NOP
+ }
+
+ public void onChannelStatusChanged(Channel channel) {
+ // NOP
+ }
+
+ public void onChannelSelected(Channel channel) {
+ fillTable(channel);
+ }
+
+ public void onItemSelected(Item item) {
+ fillTable(item.getChannel());
+ int index = item.getChannel().getItems().indexOf(item);
+ table.setSelection(index);
+ }
+
+ public void onItemStatusChanged(Item item) {
+ fillTable(item.getChannel());
+ int index = item.getChannel().getItems().indexOf(item);
+ table.setSelection(index);
+ }
+
+ private void fillTable(Channel channel) {
+ Iterator items = channel.getItems().iterator();
+ table.removeAll();
+ while(items.hasNext()) {
+ Item item = (Item)items.next();
+ TableItem tableItem = new TableItem(table,SWT.NONE);
+ tableItem.setText(0,item.getDate());
+ String image = item.isReadFlag()? IconManager.ICON_STATUS_READ : IconManager.ICON_STATUS_UNREAD;
+ tableItem.setImage(1,IconManager.getImage(image));
+ tableItem.setText(2,item.getUsableTitle());
+ tableItem.setData(item);
+ }
+ }
+}
--- /dev/null
+/*
+ * Created on 16 mai 2004
+ * Copyright 2004 Jérôme Nègre
+ */
+package net.sourceforge.phpeclipse.news.view;
+
+import net.sourceforge.phpeclipse.news.Channel;
+import net.sourceforge.phpeclipse.news.IconManager;
+import net.sourceforge.phpeclipse.news.Item;
+import net.sourceforge.phpeclipse.news.Plugin;
+
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * @author Jérôme Nègre
+ */
+public class NewsTreeViewerProvider
+ implements
+ ITreeContentProvider,
+ ILabelProvider {
+
+ public Object[] getChildren(Object parentElement) {
+ if(parentElement instanceof Channel) {
+ return ((Channel)parentElement).getItems().toArray();
+ } else {
+ return null;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
+ */
+ public Object getParent(Object element) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public boolean hasChildren(Object element) {
+ if(element instanceof Channel) {
+ return !((Channel)element).getItems().isEmpty();
+ } else {
+ return false;
+ }
+ }
+
+ public Object[] getElements(Object inputElement) {
+ return ((Plugin)inputElement).getChannelList().toArray();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.IContentProvider#dispose()
+ */
+ public void dispose() {
+ // TODO Auto-generated method stub
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
+ */
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ // TODO Auto-generated method stub
+ }
+
+ public Image getImage(Object element) {
+
+ String iconId;
+
+ if(element instanceof Channel) {
+ Channel channel = (Channel)element;
+ if(channel.isRefreshing()) {
+ iconId = IconManager.ICON_STATUS_REFRESH;
+ } else if(channel.getErrorMessage()!=null) {
+ iconId = IconManager.ICON_STATUS_ERROR;
+ } else if(channel.isUnread()) {
+ iconId = IconManager.ICON_STATUS_UNREAD;
+ } else {
+ iconId = IconManager.ICON_STATUS_READ;
+ }
+ } else if(element instanceof Item) {
+ Item item = (Item)element;
+ if(item.isReadFlag()) {
+ iconId = IconManager.ICON_STATUS_READ;
+ } else {
+ iconId = IconManager.ICON_STATUS_UNREAD;
+ }
+ } else {
+ return null;
+ }
+ return Plugin.getDefault().getImageRegistry().getDescriptor(iconId).createImage();
+ }
+
+ public String getText(Object element) {
+ if(element instanceof Channel) {
+ return ((Channel)element).getTitle();
+ } else if(element instanceof Item) {
+ return ((Item)element).getUsableTitle();
+ } else {
+ return "Unexpected object: "+element;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
+ */
+ public void addListener(ILabelProviderListener listener) {
+ // TODO Auto-generated method stub
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object, java.lang.String)
+ */
+ public boolean isLabelProperty(Object element, String property) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
+ */
+ public void removeListener(ILabelProviderListener listener) {
+ // TODO Auto-generated method stub
+
+ }
+}