Display sources in feed list; right-click menus

This commit is contained in:
Jaidyn Ann 2021-05-14 11:28:24 -05:00
parent 726d6346a1
commit c712468177
13 changed files with 232 additions and 562 deletions

View File

@ -35,6 +35,7 @@ SRCS = \
src/Feed.cpp \ src/Feed.cpp \
src/FeedEditWindow.cpp \ src/FeedEditWindow.cpp \
src/FeedListItem.cpp \ src/FeedListItem.cpp \
src/FeedListView.cpp \
src/FeedsView.cpp \ src/FeedsView.cpp \
src/MainWindow.cpp \ src/MainWindow.cpp \
src/Mimetypes.cpp \ src/Mimetypes.cpp \

View File

@ -59,13 +59,19 @@ FeedController::MessageReceived(BMessage* msg)
case kEnqueueFeed: case kEnqueueFeed:
{ {
int i = 0; int i = 0;
const void* data; BString feedID;
BString feedSource;
ssize_t size = sizeof(Feed); ssize_t size = sizeof(Feed);
while (msg->HasData("feeds", B_RAW_TYPE, i)) { while (msg->HasString("feed_identifiers", i)) {
msg->FindData("feeds", B_RAW_TYPE, i, &data, &size); msg->FindString("feed_identifiers", i, &feedID);
fDownloadQueue->AddItem((Feed*)data); msg->FindString("feed_sources", i, &feedSource);
Feed* feed = SourceManager::GetFeed(feedID.String(),
feedSource.String());
fDownloadQueue->AddItem(feed);
_SendProgress(); _SendProgress();
i++;
} }
fMessageRunner->SetCount(-1); fMessageRunner->SetCount(-1);
break; break;

View File

@ -152,10 +152,11 @@ FeedEditWindow::_SaveFeed()
SourceManager::EditFeed(fFeed); SourceManager::EditFeed(fFeed);
BMessage edited(kFeedsEdited); BMessage edited(kFeedsEdited);
// BMessage enqueueUpdated(kEnqueueFeed); BMessage enqueueUpdated(kEnqueueFeed);
// enqueueUpdated.AddData("feeds", B_RAW_TYPE, &fFeed, sizeof(Feed)); enqueueUpdated.AddString("feed_identifiers", fFeed->Identifier());
// enqueueUpdated.AddString("feed_sources", fFeed->Source());
// ((App*)be_app)->MessageReceived(&enqueueUpdated);
((App*)be_app)->MessageReceived(&enqueueUpdated);
((App*)be_app)->PostMessage(&edited); ((App*)be_app)->PostMessage(&edited);
Quit(); Quit();
} }

View File

@ -24,6 +24,7 @@ FeedListItem::FeedListItem(Feed* feed)
fFeedIdentifier(feed->Identifier()), fFeedIdentifier(feed->Identifier()),
fFeedSource(feed->Source()) fFeedSource(feed->Source())
{ {
fFeed = feed;
if (feed->Title().IsEmpty() == true) if (feed->Title().IsEmpty() == true)
SetText(B_TRANSLATE("Untitled Feed")); SetText(B_TRANSLATE("Untitled Feed"));
} }
@ -87,3 +88,10 @@ FeedListItem::SetStatus(int8 status)
} }
Feed*
FeedListItem::GetFeed()
{
return fFeed;
}

View File

@ -31,6 +31,7 @@ public:
BUrl FeedUrl(); BUrl FeedUrl();
const char* FeedIdentifier(); const char* FeedIdentifier();
const char* FeedSource(); const char* FeedSource();
Feed* GetFeed();
void SetStatus(int8 status); void SetStatus(int8 status);
@ -39,6 +40,7 @@ private:
BUrl fFeedUrl; BUrl fFeedUrl;
BString fFeedIdentifier; BString fFeedIdentifier;
BString fFeedSource; BString fFeedSource;
Feed* fFeed;
}; };

80
src/FeedListView.cpp Normal file
View File

@ -0,0 +1,80 @@
/*
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#include "FeedListView.h"
#include <Catalog.h>
#include <MenuItem.h>
#include <OutlineListView.h>
#include <PopUpMenu.h>
#include <Window.h>
#include "App.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "FeedListView"
FeedListView::FeedListView(const char* name)
: BOutlineListView(name)
{
}
void
FeedListView::MouseDown(BPoint where)
{
BOutlineListView::MouseDown(where);
uint32 buttons = 0;
Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
if (!(buttons & B_SECONDARY_MOUSE_BUTTON))
return;
BMenuItem* item;
int32 selIndex = CurrentSelection();
if (Superitem(ItemAt(selIndex)) == NULL)
item = _SourcePopUp()->Go(ConvertToScreen(where));
else
item = _FeedPopUp()->Go(ConvertToScreen(where));
if (item != NULL)
Window()->MessageReceived(item->Message());
return;
}
BPopUpMenu*
FeedListView::_FeedPopUp()
{
BPopUpMenu* menu = new BPopUpMenu("feedPopUp");
menu->SetTargetForItems(Window());
menu->AddItem(new BMenuItem(B_TRANSLATE("Update feed"),
new BMessage(kFeedsEnqueueSelected)));
menu->AddItem(new BMenuItem(B_TRANSLATE("Edit feed…"),
new BMessage(kFeedsEditSelected)));
menu->AddItem(new BMenuItem(B_TRANSLATE("Remove feed…"),
new BMessage(kFeedsRemoveSelected)));
return menu;
}
BPopUpMenu*
FeedListView::_SourcePopUp()
{
BMessage enqueue(kFeedsEnqueueSelected);
BPopUpMenu* menu = new BPopUpMenu("sourcePopUp");
menu->AddItem(new BMenuItem(B_TRANSLATE("Update source"),
new BMessage(kFeedsEnqueueSelected)));
// menu->AddItem(BMenuItem("Configure source…", BMessage('')));
// menu->AddItem(BMenuItem("Remove source…", BMessage('')));
return menu;
}

35
src/FeedListView.h Normal file
View File

@ -0,0 +1,35 @@
/*
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#ifndef FEEDLISTVIEW_H
#define FEEDLISTVIEW_H
#include <OutlineListView.h>
#include <Point.h>
class BPopUpMenu;
enum
{
kFeedsRemoveSelected = 'srem',
kFeedsEditSelected = 'sedt',
kFeedsEnqueueSelected = 'senq'
};
class FeedListView : public BOutlineListView {
public:
FeedListView(const char* name);
void MouseDown(BPoint where);
private:
BPopUpMenu* _FeedPopUp();
BPopUpMenu* _SourcePopUp();
};
#endif // FEEDLISTVIEW_H

View File

@ -1,25 +0,0 @@
/*
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#ifndef FEEDSOURCE_H
#define FEEDSOURCE_H
#include <StringList.h>
#include "Feed.h"
// Hierarchy: FeedSource → Feed → Entry
// TODO: RemoveFeed(), AddFeed(), etc.
class FeedSource {
public:
static BStringList FeedIdentifiers();
Feed RemoveFeed(Feed feed);
Feed RemoveFeed(const char* identifier);
};
#endif // FEEDSOURCE_H

View File

@ -21,6 +21,7 @@
#include "FeedController.h" #include "FeedController.h"
#include "FeedEditWindow.h" #include "FeedEditWindow.h"
#include "FeedListItem.h" #include "FeedListItem.h"
#include "FeedListView.h"
#include "Notifier.h" #include "Notifier.h"
#include "SourceListItem.h" #include "SourceListItem.h"
#include "SourceManager.h" #include "SourceManager.h"
@ -43,25 +44,30 @@ FeedsView::MessageReceived(BMessage* msg)
{ {
switch (msg->what) switch (msg->what)
{ {
case kFeedsAddButton: case kFeedsEnqueueSelected:
{
_UpdateSelected();
break;
}
case kFeedsRemoveSelected:
{
if (_RemoveSelected() == true)
_PopulateFeedList();
break;
}
case kFeedsEditSelected:
{
if (fFeedsListView->CurrentSelection() >= 0)
_EditSelected();
break;
}
case kFeedsAddNew:
{ {
FeedEditWindow* edit = new FeedEditWindow(); FeedEditWindow* edit = new FeedEditWindow();
edit->Show(); edit->Show();
edit->Activate(); edit->Activate();
break; break;
} }
case kFeedsRemoveButton:
{
_RemoveSelectedFeed();
_PopulateFeedList();
break;
}
case kFeedsEditButton:
{
if (fFeedsListView->CurrentSelection() >= 0)
_EditSelectedFeed();
break;
}
case kFeedsSelected: case kFeedsSelected:
{ {
bool enabled = msg->GetInt32("index", -1) >= 0; bool enabled = msg->GetInt32("index", -1) >= 0;
@ -116,23 +122,24 @@ void
FeedsView::_InitInterface() FeedsView::_InitInterface()
{ {
// Feeds list // Feeds list
fFeedsListView = new BOutlineListView("feedsList"); fFeedsListView = new FeedListView("feedsList");
fFeedsScrollView = new BScrollView("feedsScroll", fFeedsListView, fFeedsScrollView = new BScrollView("feedsScroll", fFeedsListView,
B_WILL_DRAW, false, true); B_WILL_DRAW, false, true);
fFeedsListView->SetSelectionMessage(new BMessage(kFeedsSelected)); fFeedsListView->SetSelectionMessage(new BMessage(kFeedsSelected));
fFeedsListView->SetInvocationMessage(new BMessage(kFeedsEditButton)); fFeedsListView->SetInvocationMessage(new BMessage(kFeedsEditSelected));
_PopulateFeedList(); _PopulateFeedList();
fProgressLabel = new BStringView("progressLabel", ""); fProgressLabel = new BStringView("progressLabel", "");
// Add, Remove, Edit // Add, Remove, Edit
fAddButton = new BButton("addFeed", "+", new BMessage(kFeedsAddButton)); fAddButton = new BButton("addFeed", "+", new BMessage(kFeedsAddNew));
fRemoveButton = new BButton("removeFeed", "-", new BMessage(kFeedsRemoveButton)); fRemoveButton = new BButton("removeFeed", "-",
new BMessage(kFeedsRemoveSelected));
fAddButton->SetToolTip(B_TRANSLATE("Add new feed")); fAddButton->SetToolTip(B_TRANSLATE("Add new feed"));
fRemoveButton->SetToolTip(B_TRANSLATE("Remove selected feed")); fRemoveButton->SetToolTip(B_TRANSLATE("Remove selected feed"));
fEditButton = new BButton("editFeed", B_TRANSLATE("Edit…"), fEditButton = new BButton("editFeed", B_TRANSLATE("Edit…"),
new BMessage(kFeedsEditButton)); new BMessage(kFeedsEditSelected));
font_height fontHeight; font_height fontHeight;
GetFontHeight(&fontHeight); GetFontHeight(&fontHeight);
@ -184,22 +191,62 @@ FeedsView::_InitInterface()
.End(); .End();
} }
void void
FeedsView::_EditSelectedFeed() FeedsView::_UpdateSelected()
{ {
int32 selIndex = fFeedsListView->CurrentSelection(); int32 selIndex = fFeedsListView->CurrentSelection();
// Enqueue single feed
if (fFeedsListView->Superitem(fFeedsListView->ItemAt(selIndex)) != NULL) {
FeedListItem* selected = (FeedListItem*)fFeedsListView->ItemAt(selIndex); FeedListItem* selected = (FeedListItem*)fFeedsListView->ItemAt(selIndex);
BMessage enqueue(kEnqueueFeed);
enqueue.AddString("feed_identifiers", selected->FeedIdentifier());
enqueue.AddString("feed_sources", selected->FeedSource());
((App*)be_app)->MessageReceived(&enqueue);
}
// Enqueue feeds from a source
else {
BMessage enqueue(kEnqueueFeed);
SourceListItem* selected = (SourceListItem*)fFeedsListView->ItemAt(selIndex);
BObjectList<Feed> feeds = selected->FeedSource()->Feeds();
for (int i = 0; i < feeds.CountItems(); i++) {
Feed* selected = feeds.ItemAt(i);
enqueue.AddString("feed_identifiers", selected->Identifier());
enqueue.AddString("feed_sources", selected->Source());
}
((App*)be_app)->MessageReceived(&enqueue);
}
}
void
FeedsView::_EditSelected()
{
int32 selIndex = fFeedsListView->CurrentSelection();
if (fFeedsListView->Superitem(fFeedsListView->ItemAt(selIndex)) != NULL) {
FeedListItem* selected = (FeedListItem*)fFeedsListView->ItemAt(selIndex);
FeedEditWindow* edit = new FeedEditWindow(selected->FeedIdentifier(), FeedEditWindow* edit = new FeedEditWindow(selected->FeedIdentifier(),
selected->FeedSource()); selected->FeedSource());
edit->Show(); edit->Show();
edit->Activate(); edit->Activate();
}
else {
SourceListItem* selected = (SourceListItem*)fFeedsListView->ItemAt(selIndex);
Source* source = selected->FeedSource();
// if (source->IsConfigurable() == true)
// … Do something about that
}
} }
void bool
FeedsView::_RemoveSelectedFeed() FeedsView::_RemoveSelected()
{ {
BAlert* alert = new BAlert(B_TRANSLATE("Confirm removal"), BAlert* alert = new BAlert(B_TRANSLATE("Confirm removal"),
B_TRANSLATE("Are you sure you want to remove the selected feed?"), B_TRANSLATE("Are you sure you want to remove the selected feed?"),
@ -209,13 +256,17 @@ FeedsView::_RemoveSelectedFeed()
alert->SetShortcut(1, B_ESCAPE); alert->SetShortcut(1, B_ESCAPE);
int32 button = alert->Go(); int32 button = alert->Go();
if (button != 0) if (button != 0)
return; return false;
int32 selIndex = fFeedsListView->CurrentSelection(); int32 selIndex = fFeedsListView->CurrentSelection();
FeedListItem* selected = (FeedListItem*)fFeedsListView->ItemAt(selIndex); FeedListItem* selected = (FeedListItem*)fFeedsListView->ItemAt(selIndex);
if (fFeedsListView->Superitem(selected) == NULL)
return false;
SourceManager::RemoveFeed(SourceManager::GetFeed(selected->FeedIdentifier(), SourceManager::RemoveFeed(SourceManager::GetFeed(selected->FeedIdentifier(),
selected->FeedSource())); selected->FeedSource()));
return true;
} }

View File

@ -10,16 +10,14 @@
#include <GroupView.h> #include <GroupView.h>
class BMessage; class BMessage;
class BOutlineListView; class FeedListView;
class BScrollView; class BScrollView;
class BStringView; class BStringView;
enum enum
{ {
kFeedsAddButton = 'sadd', kFeedsAddNew = 'sadd',
kFeedsRemoveButton = 'srem',
kFeedsEditButton = 'sedt',
kFeedsSelected = 'flsl', kFeedsSelected = 'flsl',
kFeedsEdited = 'fedd' kFeedsEdited = 'fedd'
}; };
@ -34,8 +32,9 @@ public:
private: private:
void _InitInterface(); void _InitInterface();
void _EditSelectedFeed(); void _UpdateSelected();
void _RemoveSelectedFeed(); void _EditSelected();
bool _RemoveSelected();
void _PopulateFeedList(); void _PopulateFeedList();
@ -45,7 +44,7 @@ private:
BButton* fRemoveButton; BButton* fRemoveButton;
BButton* fEditButton; BButton* fEditButton;
BStringView* fProgressLabel; BStringView* fProgressLabel;
BOutlineListView* fFeedsListView; FeedListView* fFeedsListView;
BScrollView* fFeedsScrollView; BScrollView* fFeedsScrollView;
}; };

View File

@ -1,436 +0,0 @@
/*
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#include "LocalSource.h"
#include <iostream>
#include <Catalog.h>
#include <Directory.h>
#include <Entry.h>
#include <File.h>
#include <FindDirectory.h>
#include <Path.h>
#include "Feed.h"
#include "Util.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "LocalSource"
LocalSource::LocalSource()
{
BDirectory subDir = BDirectory(_SubscriptionPath().Path());
if (subDir.InitCheck() == B_ENTRY_NOT_FOUND) {
// subDir.CreateDirectory(subPath.Path(), &subDir);
// subPath.Append("Haiku Project");
// Feed defaultSub(BUrl("https://www.haiku-os.org/blog/index.xml"),
// BEntry(subPath.Path()));
// defaultSub.SetTitle("Haiku Project");
// defaultSub.Filetize();
}
}
BObjectList<Feed>
LocalSource::Feeds()
{
BDirectory subDir = BDirectory(_SubscriptionPath().Path());
BEntry feedEntry;
BPath feedPath;
BObjectList<Feed> feeds;
while (subDir.GetNextEntry(&feedEntry) == B_OK
&& feedEntry.GetPath(&feedPath) == B_OK)
{
Feed* feed = GetFeed(feedPath.Path());
feeds.AddItem(feed);
}
return feeds;
}
Feed*
LocalSource::GetFeed(const char* identifier)
{
BFile file(identifier, B_READ_ONLY);
time_t tt_lastDate = 0;
BDateTime lastDate;
BString title;
BString url;
BString hash;
file.ReadAttrString("Feed:name", &title);
file.ReadAttrString("META:url", &url);
file.ReadAttrString("Feed:hash", &hash);
file.ReadAttr("Feed:when", B_TIME_TYPE, 0, &tt_lastDate, sizeof(time_t));
lastDate.SetTime_t(tt_lastDate);
Feed* feed = new Feed(identifier, title.String(), url.String());
feed->SetHash(hash);
feed->SetLastDate(lastDate);
return feed;
}
bool
LocalSource::Fetch(Feed* feed)
{
BFile cacheFile = BFile(feed->Identifier(), B_READ_WRITE | B_CREATE_FILE);
BString hash;
int32 result = fetch(feed->Url(), &cacheFile, &hash, 30);
feed->SetHash(hash);
if (result == 0)
return true;
return false;
}
bool
LocalSource::Parse(Feed* feed)
{
if (IsUpdated(feed) == false)
return true;
if (_IsAtom(feed) == true)
return _AtomParse(feed);
else if (_IsRss(feed) == true)
return _RssParse(feed);
return false;
}
void
LocalSource::AddFeed(Feed* newFeed)
{
if (newFeed->Identifier().IsEmpty() == true) {
BPath subPath = _SubscriptionPath();
subPath.Append(urlToFilename(newFeed->Url()));
newFeed->SetIdentifier(subPath.Path());
}
EditFeed(newFeed);
}
void
LocalSource::EditFeed(Feed* updated)
{
Feed* old = GetFeed(updated->Identifier());
BFile file(updated->Identifier(), B_READ_WRITE | B_CREATE_FILE);
if (old->Title() != updated->Title()) {
BString title = updated->Title();
file.WriteAttrString("Feed:name", &title);
}
if (old->Url() != updated->Url()) {
BString url = updated->Url().UrlString();
file.WriteAttrString("META:url", &url);
}
if (old->Hash() != updated->Hash()) {
BString hash = updated->Hash();
file.WriteAttrString("Feed:hash", &hash);
}
if (old->Date() != updated->Date()) {
time_t tt_date = updated->Date().Time_t();
file.WriteAttr("Feed:when", B_TIME_TYPE, 0, &tt_date, sizeof(time_t));
}
BString type("application/x-feed-source");
file.WriteAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, type.String(),
type.CountChars() + 1);
}
void
LocalSource::RemoveFeed(Feed* mortonta)
{
BEntry entry(mortonta->Identifier().String());
entry.Remove();
}
bool
LocalSource::IsUpdated(Feed* feed)
{
return (GetFeed(feed->Identifier())->Hash() != feed->Hash());
}
bool
LocalSource::_IsAtom(Feed* feed)
{
tinyxml2::XMLDocument xml;
xml.LoadFile(feed->Identifier().String());
if (xml.FirstChildElement("feed"))
return true;
return false;
}
bool
LocalSource::_IsRss(Feed* feed)
{
tinyxml2::XMLDocument xml;
xml.LoadFile(feed->Identifier().String());
if (xml.FirstChildElement("rss"))
return true;
return false;
}
bool
LocalSource::_AtomParse(Feed* atomFeed)
{
tinyxml2::XMLDocument xml;
xml.LoadFile(atomFeed->Identifier().String());
tinyxml2::XMLElement* xfeed = xml.FirstChildElement("feed");
_AtomRootParse(atomFeed, xfeed);
_AtomEntriesParse(atomFeed, xfeed);
return true;
}
void
LocalSource::_AtomRootParse(Feed* feed, tinyxml2::XMLElement* xfeed)
{
tinyxml2::XMLElement* xentry = xfeed->FirstChildElement("entry");
bool set = false;
_SetTitle(feed, xfeed->FirstChildElement("title"));
set = _SetDate(feed, xfeed->FirstChildElement("updated"));
if (!set)
set = _SetDate(feed, xfeed->FirstChildElement("published"));
if (!set && xentry)
set = _SetDate(feed, xentry->FirstChildElement("updated"));
if (!set && xentry)
set = _SetDate(feed, xentry->FirstChildElement("published"));
BString logString(B_TRANSLATE("Channel '%source%' at %url%:\n"));
logString.ReplaceAll("%source%", feed->Title().String());
logString.ReplaceAll("%url%", feed->Url().UrlString());
std::cout << logString.String();
}
void
LocalSource::_AtomEntriesParse(Feed* feed, tinyxml2::XMLElement* xfeed)
{
tinyxml2::XMLElement* xentry;
xentry = xfeed->FirstChildElement("entry");
int entryCount = _XmlCountSiblings(xentry, "entry");
BObjectList<Entry> entries(entryCount, true);
BString logString(B_TRANSLATE("\t-%count% entries-\n"));
BString entryStr;
entryStr << entryCount;
logString.ReplaceAll("%count%", entryStr);
std::cout << logString.String();
while (xentry) {
Entry* newEntry = _AtomEntryParse(feed, xentry);
if (newEntry != NULL)
entries.AddItem(newEntry);
xentry = xentry->NextSiblingElement("entry");
}
feed->SetEntries(entries);
}
Entry*
LocalSource::_AtomEntryParse(Feed* feed, tinyxml2::XMLElement* xentry)
{
Entry* newEntry = new Entry();
tinyxml2::XMLElement* xcontent = xentry->FirstChildElement("content");
tinyxml2::XMLElement* xmedia = xentry->FirstChildElement("media:group");
tinyxml2::XMLPrinter xprinter;
newEntry->SetTitle(xentry->FirstChildElement("title"));
newEntry->SetPostUrl(xentry->FirstChildElement("link")->Attribute("href"));
newEntry->SetFeedTitle(feed->Title());
bool set = false;
set = newEntry->SetDescription(xentry->FirstChildElement("summary"));
if (!set)
set = newEntry->SetDescription(xentry->FirstChildElement("description"));
if (!set && xmedia)
set = newEntry->SetDescription(
xmedia->FirstChildElement("media:description"));
set = _SetDate(newEntry, xentry->FirstChildElement("updated"));
if (!set)
set = _SetDate(newEntry, xentry->FirstChildElement("published"));
if (feed->Date() == NULL || feed->Date() < newEntry->Date())
feed->SetDate(newEntry->Date());
if (xcontent) {
xcontent->Accept(&xprinter);
newEntry->SetContent(xprinter.CStr());
}
return newEntry;
}
bool
LocalSource::_RssParse(Feed* feed)
{
tinyxml2::XMLDocument xml;
xml.LoadFile(feed->Identifier().String());
tinyxml2::XMLElement* xchan = xml.FirstChildElement("rss")->FirstChildElement("channel");
_RssRootParse(feed, xchan);
_RssEntriesParse(feed, xchan);
return true;
}
void
LocalSource::_RssRootParse(Feed* feed, tinyxml2::XMLElement* xchan)
{
_SetTitle(feed, xchan->FirstChildElement("title"));
_SetDate(feed, xchan->FirstChildElement("lastBuildDate"));
BString logString(B_TRANSLATE("Channel '%source%' at %url%:\n"));
logString.ReplaceAll("%source%", feed->Title().String());
logString.ReplaceAll("%url%", feed->Url().UrlString());
std::cout << logString.String();
}
void
LocalSource::_RssEntriesParse(Feed* feed, tinyxml2::XMLElement* xchan)
{
tinyxml2::XMLElement* xitem;
xitem = xchan->FirstChildElement("item");
int entryCount = _XmlCountSiblings(xitem, "item");
BObjectList<Entry> entries = BObjectList<Entry>(entryCount, true);
BString logString(B_TRANSLATE("\t-%count% entries-\n"));
BString entryStr;
entryStr << entryCount;
logString.ReplaceAll("%count%", entryStr);
std::cout << logString.String();
while (xitem) {
entries.AddItem(_RssEntryParse(feed, xitem));
xitem = xitem->NextSiblingElement("item");
}
feed->SetEntries(entries);
}
Entry*
LocalSource::_RssEntryParse(Feed* feed, tinyxml2::XMLElement* xitem)
{
Entry* newEntry = new Entry();
newEntry->SetTitle(xitem->FirstChildElement("title"));
newEntry->SetDescription(xitem->FirstChildElement("description"));
_SetDate(newEntry, xitem->FirstChildElement("pubDate"));
newEntry->SetPostUrl(xitem->FirstChildElement("link"));
newEntry->SetContent(xitem->FirstChildElement("content:encoded"));
newEntry->SetFeedTitle(feed->Title());
if (feed->Date() == NULL || feed->Date() < newEntry->Date())
feed->SetDate(newEntry->Date());
return newEntry;
}
bool
LocalSource::_SetTitle(Feed* feed, tinyxml2::XMLElement* elem)
{
if (elem != NULL && feed->Title().IsEmpty() == true)
return feed->SetTitle(elem->GetText());
return false;
}
bool
LocalSource::_SetDate(Feed* feed, const char* dateCStr)
{
if (dateCStr == NULL)
return false;
return feed->SetDate(feedDateToBDate(dateCStr));
}
bool
LocalSource::_SetDate(Feed* feed, tinyxml2::XMLElement* elem)
{
if (elem == NULL)
return false;
return _SetDate(feed, elem->GetText());
}
bool
LocalSource::_SetDate(Entry* entry, const char* dateStr)
{
if (dateStr == NULL)
return false;
return entry->SetDate(feedDateToBDate(dateStr));
}
bool
LocalSource::_SetDate(Entry* entry, tinyxml2::XMLElement* elem)
{
if (elem == NULL)
return false;
return _SetDate(entry, elem->GetText());
}
// Count the amount of siblings to an element of given type name
int
LocalSource::_XmlCountSiblings (tinyxml2::XMLElement* xsibling,
const char* sibling_name)
{
int count = 0;
while (xsibling) {
count++;
xsibling = xsibling->NextSiblingElement(sibling_name);
}
return count;
}
BPath
LocalSource::_SubscriptionPath()
{
BPath subPath;
find_directory(B_USER_SETTINGS_DIRECTORY, &subPath);
subPath.Append("Pogger");
subPath.Append("Subscriptions");
return subPath;
}

View File

@ -1,54 +0,0 @@
/*
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#ifndef LOCALSOURCE_H
#define LOCALSOURCE_H
#include "FeedSource.h"
class LocalSource : public FeedSource {
public:
LocalSource();
static BObjectList<Feed> Feeds();
static Feed* GetFeed(const char* identifier);
static bool Fetch(Feed* feed);
static bool Parse(Feed* feed);
static void AddFeed(Feed* newFeed);
static void EditFeed(Feed* updated);
static void RemoveFeed(Feed* mortonta);
static bool IsUpdated(Feed* feed);
private:
static bool _IsAtom(Feed* feed);
static bool _IsRss(Feed* feed);
static bool _AtomParse(Feed* feed);
static void _AtomRootParse(Feed* feed, tinyxml2::XMLElement*);
static void _AtomEntriesParse(Feed* feed, tinyxml2::XMLElement*);
static Entry* _AtomEntryParse(Feed* feed, tinyxml2::XMLElement*);
static bool _RssParse(Feed* feed);
static void _RssRootParse(Feed* feed, tinyxml2::XMLElement*);
static void _RssEntriesParse(Feed* feed, tinyxml2::XMLElement*);
static Entry* _RssEntryParse(Feed* feed, tinyxml2::XMLElement*);
static bool _SetTitle(Feed* feed, tinyxml2::XMLElement*);
static bool _SetDate(Feed* feed, const char*);
static bool _SetDate(Feed* feed, tinyxml2::XMLElement*);
static bool _SetDate(Entry* entry, const char*);
static bool _SetDate(Entry* entry, tinyxml2::XMLElement*);
static int _XmlCountSiblings(tinyxml2::XMLElement*, const char*);
static BPath _SubscriptionPath();
};
#endif // LOCALSOURCE_H

View File

@ -17,6 +17,7 @@
#include "App.h" #include "App.h"
#include "EntriesView.h" #include "EntriesView.h"
#include "FeedController.h" #include "FeedController.h"
#include "FeedListView.h"
#include "FeedsView.h" #include "FeedsView.h"
#include "Notifier.h" #include "Notifier.h"
#include "UpdatesView.h" #include "UpdatesView.h"
@ -49,9 +50,10 @@ MainWindow::MessageReceived(BMessage *msg)
{ {
switch (msg->what) switch (msg->what)
{ {
case kFeedsAddButton: case kFeedsAddNew:
case kFeedsRemoveButton: case kFeedsEnqueueSelected:
case kFeedsEditButton: case kFeedsRemoveSelected:
case kFeedsEditSelected:
case kFeedsSelected: case kFeedsSelected:
case kFeedsEdited: case kFeedsEdited:
case kDownloadStart: case kDownloadStart: