diff --git a/Makefile b/Makefile index 64805e0..165c148 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,7 @@ SRCS = \ src/EntriesView.cpp, \ src/Feed.cpp, \ src/FeedController.cpp \ + src/FeedEditWindow.cpp \ src/FeedListItem.cpp \ src/FeedsView.cpp, \ src/MainWindow.cpp, \ diff --git a/TODO.txt b/TODO.txt index 49c1584..1c3b883 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,6 +1,9 @@ -* Add subscriptions - * Adding/editing from GUI - * Do this for file refs when Feeds, too +* Feeds list + * Update when feeds are edited +* Store feeds in kEnqueueFeeds/etc messages as paths? hmm + * This way, if the user edits the feed after it is enqueued, the changes + will be applied. +* Support for clearing queue * Revamp configuration * Fix saving, etc. * Show progress diff --git a/src/App.cpp b/src/App.cpp index 07af227..ff0879f 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -46,7 +46,7 @@ App::App() : BApplication("application/x-vnd.Pogger") fMainWindow->Show(); BMessage* updateMessage = new BMessage(kUpdateSubscribed); - MessageReceived(updateMessage); +// MessageReceived(updateMessage); fUpdateRunner = new BMessageRunner(this, updateMessage, cfg->updateInterval); } @@ -58,11 +58,8 @@ App::MessageReceived(BMessage* msg) switch (msg->what) { case kEnqueueFeed: - { - fFeedController->MessageReceived(msg); - break; - } case kUpdateSubscribed: + case kDownloadComplete: { fFeedController->MessageReceived(msg); break; @@ -71,23 +68,18 @@ App::MessageReceived(BMessage* msg) { break; } - case kParseFail: + case kParseComplete: { fNotifier->MessageReceived(msg); - fFeedController->MessageReceived(msg); break; } + case kParseFail: case kDownloadFail: { fNotifier->MessageReceived(msg); fFeedController->MessageReceived(msg); break; } - case kDownloadComplete: - { - fFeedController->MessageReceived(msg); - break; - } default: { // BApplication::MessageReceived(msg); diff --git a/src/AtomFeed.cpp b/src/AtomFeed.cpp index db3f6c9..49f084e 100644 --- a/src/AtomFeed.cpp +++ b/src/AtomFeed.cpp @@ -42,7 +42,7 @@ AtomFeed::Parse () RootParse(xfeed); ParseEntries(xfeed); - _PostParse(); + Filetize(); } diff --git a/src/Feed.cpp b/src/Feed.cpp index 52d96b2..922328f 100644 --- a/src/Feed.cpp +++ b/src/Feed.cpp @@ -98,24 +98,6 @@ Feed::Parse() } -void -Feed::_PostParse() -{ - BFile feedFile(cachePath, B_WRITE_ONLY); - time_t tt_date = date.Time_t(); - BString url = xmlUrl.UrlString(); - BString name = GetTitle(); - BString type("application/x-feed-source"); - - feedFile.WriteAttrString("Feed:name", &name); - feedFile.WriteAttrString("META:url", &url); - feedFile.WriteAttrString("BEOS:TYPE", &type); - feedFile.WriteAttr("Feed:when", B_TIME_TYPE, 0, &tt_date, sizeof(time_t)); - feedFile.WriteAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, type.String(), - type.CountChars() + 1); -} - - // Download a remote feed's XML to the cache path. bool Feed::Fetch() @@ -133,6 +115,32 @@ Feed::Fetch() } +void +Feed::Filetize() +{ + BFile feedFile(cachePath, B_WRITE_ONLY | B_CREATE_FILE); + time_t tt_date = date.Time_t(); + BString url = xmlUrl.UrlString(); + BString name = GetTitle(); + BString type("application/x-feed-source"); + + feedFile.WriteAttrString("Feed:name", &name); + feedFile.WriteAttrString("META:url", &url); + feedFile.WriteAttrString("BEOS:TYPE", &type); + feedFile.WriteAttr("Feed:when", B_TIME_TYPE, 0, &tt_date, sizeof(time_t)); + feedFile.WriteAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, type.String(), + type.CountChars() + 1); +} + + +void +Feed::Unfiletize() +{ + BEntry entry(GetCachePath().String()); + entry.Remove(); +} + + bool Feed::IsRss() { diff --git a/src/Feed.h b/src/Feed.h index afb7fce..f1cd13c 100644 --- a/src/Feed.h +++ b/src/Feed.h @@ -34,28 +34,32 @@ public: bool Fetch(); + void Filetize(); + void Unfiletize(); + bool IsRss(); bool IsAtom(); bool IsUpdated(); BString GetTitle(); + bool SetDate(BDateTime); + BUrl GetXmlUrl(); BDateTime GetDate(); + bool SetTitle(const char*); + bool SetXmlUrl(BUrl newUrl); + bool SetCachePath(BString path); + BString GetCachePath(); protected: - bool SetTitle(const char*); bool SetTitle(tinyxml2::XMLElement*); - bool SetDate(BDateTime); bool SetDate(const char*); bool SetDate(tinyxml2::XMLElement*); - bool SetXmlUrl(BUrl newUrl); - bool SetCachePath(BString path); bool AddEntry(Entry*); - void _PostParse(); int xmlCountSiblings(tinyxml2::XMLElement*, const char*); BString title; diff --git a/src/FeedEditWindow.cpp b/src/FeedEditWindow.cpp new file mode 100644 index 0000000..eacf399 --- /dev/null +++ b/src/FeedEditWindow.cpp @@ -0,0 +1,153 @@ +/* + * Copyright 2020, Jaidyn Levesque + * All rights reserved. Distributed under the terms of the MIT license. + */ + +#include "FeedEditWindow.h" + +#include +#include +#include +#include +#include +#include + +#include "App.h" +#include "Feed.h" +#include "FeedController.h" +#include "FeedListItem.h" + + +FeedEditWindow::FeedEditWindow() + : + BWindow(BRect(BPoint(-1000.0, -1000.0), BSize(500, 150)), "New Feed", + B_TITLED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE) +{ + _InitInterface(); + MoveOnScreen(); + fDeleteButton->SetEnabled(false); + + fFeed = new Feed(); +} + + +FeedEditWindow::FeedEditWindow(BEntry feedEntry) + : + FeedEditWindow() +{ + SetTitle("Edit Feed"); + fFeed = new Feed(feedEntry); + + fFeedNameText->SetText(fFeed->GetTitle().String()); + fFeedUrlText->SetText(fFeed->GetXmlUrl().UrlString().String()); + + fDeleteButton->SetEnabled(true); +} + + +FeedEditWindow::FeedEditWindow(FeedListItem* feedItem) + : + FeedEditWindow(BEntry(feedItem->GetFeedPath())) +{ +} + + +void +FeedEditWindow::MessageReceived(BMessage* msg) +{ + switch (msg->what) + { + case kSaveButton: + { + _SaveFeed(); + break; + } + case kDeleteButton: + { + _DeleteFeed(); + break; + } + } +} + + +void +FeedEditWindow::_InitInterface() +{ + // Name and URL + fFeedNameLabel = new BStringView("feedNameLabel", "Feed name:"); + fFeedNameText = new BTextControl("feedName", "", "", NULL); + fFeedUrlLabel = new BStringView("feedUrlLabel", "Feed URL:"); + fFeedUrlText = new BTextControl("feedUrl", "", "", NULL); + + // Save/Delete + fSaveButton = new BButton("save", "Save", + new BMessage(kSaveButton)); + fDeleteButton = new BButton("delete", "Delete", + new BMessage(kDeleteButton)); + + BLayoutBuilder::Group<>(this, B_VERTICAL, 0) + .SetInsets(0, B_USE_DEFAULT_SPACING, 0, 0) + + .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING) + .AddGroup(B_VERTICAL, B_USE_DEFAULT_SPACING) + .SetInsets(B_USE_ITEM_INSETS, 0, 0, B_USE_ITEM_INSETS) + .Add(fFeedNameLabel) + .Add(fFeedUrlLabel) + .End() + .AddGroup(B_VERTICAL, B_USE_DEFAULT_SPACING) + .SetInsets(B_USE_ITEM_INSETS, 0, B_USE_ITEM_INSETS, + B_USE_ITEM_INSETS) + .Add(fFeedNameText) + .Add(fFeedUrlText) + .End() + .End() + + .Add(new BSeparatorView(B_HORIZONTAL)) + .AddGroup(B_HORIZONTAL) + .AddGlue() + .Add(fDeleteButton) + .Add(fSaveButton) + .SetInsets(B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING, + B_USE_DEFAULT_SPACING, B_USE_WINDOW_SPACING) + .End() + .End(); +} + + +void +FeedEditWindow::_SaveFeed() +{ + BString subLocation("/boot/home/config/settings/Pogger/Subscriptions/"); + BString title(fFeedNameText->Text()); + const char* urlString = fFeedUrlText->Text(); + BString filename; + if (title.IsEmpty()) + filename = BString(urlString); + else + filename = BString(title); + subLocation.Append(filename); + + if (fFeed->GetCachePath().IsEmpty()) + fFeed->SetCachePath(subLocation); + + if (!title.IsEmpty()) + fFeed->SetTitle(title.String()); + fFeed->SetXmlUrl(BUrl(urlString)); + fFeed->Filetize(); + + BMessage* firstUpdate = new BMessage(kEnqueueFeed); + firstUpdate->AddData("feeds", B_RAW_TYPE, (void*)fFeed, sizeof(Feed)); + ((App*)be_app)->MessageReceived(firstUpdate); + Quit(); +} + + +void +FeedEditWindow::_DeleteFeed() +{ + fFeed->Unfiletize(); + Quit(); +} + + diff --git a/src/FeedListItem.cpp b/src/FeedListItem.cpp index 709769d..45422a6 100644 --- a/src/FeedListItem.cpp +++ b/src/FeedListItem.cpp @@ -17,3 +17,15 @@ FeedListItem::FeedListItem(Feed* feed) } +BString +FeedListItem::GetFeedPath() +{ + return fFeedPath; +} + + +BUrl +FeedListItem::GetFeedUrl() +{ + return fFeedUrl; +} diff --git a/src/FeedListItem.h b/src/FeedListItem.h index 8679e51..8c30b2a 100644 --- a/src/FeedListItem.h +++ b/src/FeedListItem.h @@ -19,6 +19,9 @@ class FeedListItem : public BStringItem public: FeedListItem(Feed* feed); + BUrl GetFeedUrl(); + BString GetFeedPath(); + private: BUrl fFeedUrl; BString fFeedPath; diff --git a/src/FeedsView.cpp b/src/FeedsView.cpp index b865d40..0a6828e 100644 --- a/src/FeedsView.cpp +++ b/src/FeedsView.cpp @@ -14,7 +14,10 @@ #include +#include "App.h" +#include "Feed.h" #include "FeedController.h" +#include "FeedEditWindow.h" #include "FeedListItem.h" @@ -31,6 +34,33 @@ FeedsView::MessageReceived(BMessage* msg) { switch (msg->what) { + case kFeedsAddButton: + { + FeedEditWindow* edit = new FeedEditWindow(); + edit->Show(); + edit->Activate(); + break; + } + case kFeedsRemoveButton: + { + _RemoveSelectedFeed(); + break; + } + case kFeedsEditButton: + { + _EditSelectedFeed(); + break; + } + case kFeedsSelected: + { + fEditButton->SetEnabled(true); + fRemoveButton->SetEnabled(true); + break; + } + case kFeedsEdited: + { + break; + } default: { // BWindow::MessageReceived(msg); @@ -47,6 +77,8 @@ FeedsView::_InitInterface() fFeedsListView = new BListView("feedsList"); fFeedsScrollView = new BScrollView("feedsScroll", fFeedsListView, B_WILL_DRAW, false, true); + fFeedsListView->SetSelectionMessage(new BMessage(kFeedsSelected)); + fFeedsListView->SetInvocationMessage(new BMessage(kFeedsEditButton)); BList feeds = FeedController::SubscribedFeeds(); for (int i = 0; i < feeds.CountItems(); i++) { @@ -55,28 +87,32 @@ FeedsView::_InitInterface() } // Add, Remove, Edit - fAddButton = new BButton("addFeed", "+", - new BMessage('fadd')); - fRemoveButton = new BButton("removeFeed", "-", - new BMessage('frem')); + fAddButton = new BButton("addFeed", "+", new BMessage(kFeedsAddButton)); + fRemoveButton = new BButton("removeFeed", "-", new BMessage(kFeedsRemoveButton)); + fEditButton = new BButton("editFeed", "Edit…", new BMessage(kFeedsEditButton)); font_height fontHeight; GetFontHeight(&fontHeight); int16 buttonHeight = int16(fontHeight.ascent + fontHeight.descent + 12); BSize charButtonSize(buttonHeight, buttonHeight); - fAddButton->SetTarget(this); fAddButton->SetExplicitSize(charButtonSize); - fRemoveButton->SetTarget(this); + fAddButton->SetEnabled(true); fRemoveButton->SetExplicitSize(charButtonSize); + fRemoveButton->SetEnabled(false); + fEditButton->SetExplicitSize( + BSize(fEditButton->ExplicitPreferredSize().Width(), + charButtonSize.Height())); + fEditButton->SetEnabled(false); BLayoutBuilder::Group<>(this, B_VERTICAL, 0) .SetInsets(B_USE_DEFAULT_SPACING) .Add(fFeedsScrollView) - // Add, Remove, and Edit buttons .AddGroup(B_HORIZONTAL, 0, 0.0) + + // Add and Remove buttons .Add(new BSeparatorView(B_VERTICAL)) .AddGroup(B_VERTICAL, 0, 0.0) .AddGroup(B_HORIZONTAL, 1, 0.0) @@ -88,7 +124,41 @@ FeedsView::_InitInterface() .End() .Add(new BSeparatorView(B_VERTICAL)) .AddGlue() + + // Edit button + .Add(new BSeparatorView(B_VERTICAL)) + .AddGroup(B_VERTICAL, 0, 0.0) + .AddGroup(B_HORIZONTAL, 1, 0.0) + .SetInsets(1) + .Add(fEditButton) + .End() + .Add(new BSeparatorView(B_HORIZONTAL)) + .End() + .Add(new BSeparatorView(B_VERTICAL)) .End(); } +void +FeedsView::_EditSelectedFeed() +{ + int32 selIndex = fFeedsListView->CurrentSelection(); + FeedListItem* selected = (FeedListItem*)fFeedsListView->ItemAt(selIndex); + FeedEditWindow* edit = new FeedEditWindow(selected); + + edit->Show(); + edit->Activate(); +} + + +void +FeedsView::_RemoveSelectedFeed() +{ + int32 selIndex = fFeedsListView->CurrentSelection(); + FeedListItem* selected = (FeedListItem*)fFeedsListView->ItemAt(selIndex); + Feed delFeed = Feed(BEntry(selected->GetFeedPath())); + + delFeed.Unfiletize(); +} + + diff --git a/src/FeedsView.h b/src/FeedsView.h index 315f187..d1fc675 100644 --- a/src/FeedsView.h +++ b/src/FeedsView.h @@ -14,6 +14,16 @@ class BListView; class BScrollView; +enum +{ + kFeedsAddButton = 'sadd', + kFeedsRemoveButton = 'srem', + kFeedsEditButton = 'sedt', + kFeedsSelected = 'flsl', + kFeedsEdited = 'fedd' +}; + + class FeedsView : public BGroupView { public: FeedsView(const char* name); @@ -23,8 +33,12 @@ public: private: void _InitInterface(); + void _EditSelectedFeed(); + void _RemoveSelectedFeed(); + BButton* fAddButton; BButton* fRemoveButton; + BButton* fEditButton; BListView* fFeedsListView; BScrollView* fFeedsScrollView; }; diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 9a4a22b..08e509e 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -21,10 +21,7 @@ #include "UpdatesView.h" -enum { M_BUTTON_CLICKED = 'btcl' }; - - -MainWindow::MainWindow (void) +MainWindow::MainWindow() : BWindow(BRect(BPoint(-1000.0, -1000.0), BSize(520, 380)), "Pogger", B_TITLED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE) @@ -39,6 +36,14 @@ MainWindow::MessageReceived(BMessage *msg) { switch (msg->what) { + case kFeedsAddButton: + case kFeedsRemoveButton: + case kFeedsEditButton: + case kFeedsSelected: + { + fFeedsView->MessageReceived(msg); + break; + } default: { BWindow::MessageReceived(msg); diff --git a/src/Notifier.cpp b/src/Notifier.cpp index c785c42..076aeb4 100644 --- a/src/Notifier.cpp +++ b/src/Notifier.cpp @@ -24,7 +24,8 @@ Notifier::MessageReceived(BMessage* msg) if (msg->FindString("feed_name", &feedName) == B_OK && msg->FindInt32("entry_count", &entryCount) == B_OK) { - _NewEntryNotification(feedName, entryCount); + if (entryCount > 0) + _NewEntryNotification(feedName, entryCount); } break; } diff --git a/src/RssFeed.cpp b/src/RssFeed.cpp index 1004cd7..7cc5592 100644 --- a/src/RssFeed.cpp +++ b/src/RssFeed.cpp @@ -40,7 +40,7 @@ RssFeed::Parse() RootParse(xchan); ParseEntries(xchan); - _PostParse(); + Filetize(); }