Boring, necessary refactors to support more dynamic lists of content.

This commit is contained in:
casey 2016-05-11 01:15:24 -07:00
parent b718691553
commit c179878e80
23 changed files with 501 additions and 225 deletions

View File

@ -64,6 +64,24 @@ inline std::string u16to8(const std::wstring& u16) {
return result;
}
inline size_t u8len(const std::string& u8) {
return utf8::distance(u8.begin(), u8.end());
inline static int u8len(const std::string& str) {
try {
return utf8::distance(str.begin(), str.end());
}
catch (...) {
return str.length();
}
}
inline static std::string u8substr(const std::string& in, int offset, int len) {
std::string::const_iterator begin = in.begin() + offset;
std::string::const_iterator it = begin;
int count = 0;
while (count < len && it != in.end()) {
utf8::unchecked::next(it);
++count;
}
return std::string(begin, it);
}

View File

@ -28,6 +28,7 @@ namespace musik { namespace core {
virtual int GetStatus() = 0;
virtual int GetId() = 0;
virtual int GetOptions() = 0;
virtual std::string Name() = 0;
};
} }

View File

@ -42,6 +42,9 @@
#include <core/support/Common.h>
#include <core/support/Preferences.h>
#include <core/library/Indexer.h>
#include <core/debug.h>
static const std::string TAG = "LocalLibrary";
using namespace musik::core;
using namespace musik::core::library;
@ -137,6 +140,8 @@ int LocalLibrary::Enqueue(QueryPtr query, unsigned int options) {
queryQueue.push_back(query);
queueCondition.notify_all();
musik::debug::info(TAG, "query '" + query->Name() + "' enqueued");
return query->GetId();
}
@ -180,8 +185,14 @@ void LocalLibrary::ThreadProc() {
}
if (query) {
musik::debug::info(TAG, "query '" + query->Name() + "' running");
query->Run(this->db);
this->QueryCompleted(query);
musik::debug::info(TAG, boost::str(boost::format(
"query '%1%' finished with status=%2%") % query->Name() % query->GetStatus()));
query.reset();
}
}

View File

@ -3,14 +3,38 @@
#include "stdafx.h"
#include "CategoryListQuery.h"
CategoryListQuery::CategoryListQuery() {
#include <core/db/Statement.h>
using musik::core::db::Statement;
using musik::core::db::Row;
CategoryListQuery::CategoryListQuery() {
result.reset(new std::vector<std::string>());
}
CategoryListQuery::~CategoryListQuery() {
}
CategoryListQuery::Result CategoryListQuery::GetResult() {
return this->result;
}
bool CategoryListQuery::OnRun(Connection& db) {
if (result) {
result.reset(new std::vector<std::string>());
}
std::string query =
"SELECT DISTINCT artists.name "
"FROM artists, tracks "
"WHERE artists.id = tracks.visual_artist_id;";
Statement stmt(query.c_str(), db);
while (stmt.Step() == Row) {
result->push_back(stmt.ColumnText(0));
}
return false;
}

View File

@ -8,6 +8,8 @@ using musik::core::db::Connection;
class CategoryListQuery : public QueryBase {
public:
typedef boost::shared_ptr<std::vector<std::string>> Result;
CategoryListQuery();
~CategoryListQuery();
@ -15,6 +17,10 @@ class CategoryListQuery : public QueryBase {
return "CategoryListQuery";
}
virtual Result GetResult();
protected:
virtual bool OnRun(Connection &db);
Result result;
};

View File

@ -0,0 +1,20 @@
#pragma once
#include "stdafx.h"
#include "CategoryListView.h"
CategoryListView::CategoryListView() {
}
CategoryListView::~CategoryListView() {
}
IScrollAdapter& CategoryListView::GetScrollAdapter() {
return adapter;
}
void CategoryListView::OnQueryCompleted(QueryPtr query) {
}

21
src/musikbox/CategoryListView.h Executable file
View File

@ -0,0 +1,21 @@
#pragma once
#include "ScrollableWindow.h"
#include "SimpleScrollAdapter.h"
#include <core/library/IQuery.h>
using musik::core::QueryPtr;
class CategoryListView : public ScrollableWindow {
public:
CategoryListView();
~CategoryListView(); /* non-virtual for now*/
protected:
virtual IScrollAdapter& GetScrollAdapter();
private:
void OnQueryCompleted(QueryPtr query);
SimpleScrollAdapter adapter;
};

View File

@ -41,7 +41,8 @@ CommandWindow::CommandWindow(Transport& transport, OutputWindow& output)
this->paused = false;
this->library = LibraryFactory::Libraries().at(0);
this->output->WriteLine("type 'h' or 'help'\n", BOX_COLOR_BLACK_ON_GREY);
this->library->Enqueue(QueryPtr(new CategoryListQuery()));
this->library->QueryCompleted.connect(this, &CommandWindow::OnQueryCompleted);
}
CommandWindow::~CommandWindow() {
@ -110,6 +111,11 @@ void CommandWindow::SetVolume(float volume) {
transport->SetVolume(volume);
}
void CommandWindow::OnQueryCompleted(QueryPtr query) {
CategoryListQuery *result = (CategoryListQuery *) query.get();
CategoryListQuery::Result data = result->GetResult();
}
void CommandWindow::Help() {
int64 s = -1;
this->output->WriteLine("help:\n", s);
@ -138,6 +144,9 @@ bool CommandWindow::ProcessCommand(const std::string& cmd) {
if (name == "plugins") {
this->ListPlugins();
}
if (name == "artists") {
this->artistQueryId = this->library->Enqueue(QueryPtr(new CategoryListQuery()));
}
else if (name == "play" || name == "pl" || name == "p") {
return this->PlayFile(args);
}
@ -232,7 +241,6 @@ void CommandWindow::ListPlaying() {
cout << transport.NumOfStreams() << " playing" << std::std::endl;*/
}
void CommandWindow::ListPlugins() const {
using musik::core::IPlugin;
using musik::core::PluginFactory;

View File

@ -8,9 +8,10 @@
#include <core/library/LibraryFactory.h>
using musik::core::LibraryPtr;
using musik::core::QueryPtr;
using namespace musik::core::audio;
class CommandWindow : public Window, public IInput {
class CommandWindow : public Window, public IInput, public sigslot::has_slots<> {
public:
CommandWindow(Transport& transport, OutputWindow& output);
~CommandWindow();
@ -30,10 +31,13 @@ class CommandWindow : public Window, public IInput {
void SetVolume(float volume);
void Help();
void OnQueryCompleted(QueryPtr query);
char* buffer;
int bufferPosition;
OutputWindow* output;
Transport* transport;
LibraryPtr library;
bool paused;
int artistQueryId;
};

View File

@ -8,5 +8,17 @@ class IScrollAdapter {
virtual size_t GetLineCount() = 0;
virtual size_t GetEntryCount() = 0;
virtual void DrawPage(WINDOW* window, size_t index) = 0;
class IEntry { /* can we slim this down? */
public:
virtual size_t GetIndex() = 0;
virtual void SetIndex(size_t index) = 0;
virtual size_t GetLineCount() = 0;
virtual std::string GetLine(size_t line) = 0;
virtual std::string GetValue() = 0;
virtual void SetWidth(size_t width) = 0;
virtual void SetAttrs(int64 attrs) = 0;
virtual int64 GetAttrs() = 0;
};
};

30
src/musikbox/LibraryLayout.cpp Executable file
View File

@ -0,0 +1,30 @@
#include "stdafx.h"
#include "LibraryLayout.h"
LibraryLayout::LibraryLayout() {
}
LibraryLayout::~LibraryLayout() {
}
IWindow* LibraryLayout::FocusNext() {
return NULL;
}
IWindow* LibraryLayout::FocusPrev() {
return NULL;
}
IWindow* LibraryLayout::GetFocus() {
return NULL;
}
void LibraryLayout::Layout() {
}
void LibraryLayout::OnIdle() {
}

19
src/musikbox/LibraryLayout.h Executable file
View File

@ -0,0 +1,19 @@
#pragma once
#include "ILayout.h"
#include "CategoryListView.h"
class LibraryLayout : public ILayout {
public:
LibraryLayout();
~LibraryLayout(); /* not virtual */
virtual IWindow* FocusNext();
virtual IWindow* FocusPrev();
virtual IWindow* GetFocus();
virtual void Layout();
virtual void OnIdle();
private:
CategoryListView albumList;
};

View File

@ -2,10 +2,13 @@
#include "stdafx.h"
#include "LogWindow.h"
#include "MultiLineEntry.h"
#include "Colors.h"
#include "Screen.h"
#include <curses.h>
typedef IScrollAdapter::IEntry IEntry;
LogWindow::LogWindow()
: ScrollableWindow() {
this->SetContentColor(BOX_COLOR_WHITE_ON_BLUE);
@ -53,7 +56,7 @@ void LogWindow::Update() {
std::string s = boost::str(boost::format(
"[%1%] %2%") % entry->tag % entry->message);
this->adapter->AddLine(s, attrs);
this->adapter->AddEntry(boost::shared_ptr<IEntry>(new MultiLineEntry(s, attrs)));
delete entry;
}

View File

@ -40,6 +40,7 @@
#include "TransportWindow.h"
#include "ResourcesWindow.h"
#include "MainLayout.h"
#include "LibraryLayout.h"
#include "IInput.h"
#include <boost/locale.hpp>
@ -93,13 +94,15 @@ int main(int argc, char* argv[])
Transport tp;
tp.SetVolume(0.01);
MainLayout layout(tp);
MainLayout mainLayout(tp);
LibraryLayout library;
int ch;
timeout(IDLE_TIMEOUT_MS);
bool quit = false;
IWindow* focused = layout.GetFocus();
ILayout* layout = &mainLayout;
IWindow* focused = layout->GetFocus();
IInput* input = dynamic_cast<IInput*>(focused);
IScrollable* scrollable = dynamic_cast<IScrollable*>(focused);
@ -114,7 +117,7 @@ int main(int argc, char* argv[])
ch = (input != NULL) ? wgetch(focused->GetContent()) : getch();
if (ch == -1) { /* timeout */
layout.OnIdle();
layout->OnIdle();
}
else if (ch == 9) { /* tab */
if (input != NULL) {
@ -122,14 +125,17 @@ int main(int argc, char* argv[])
wtimeout(focused->GetContent(), 0);
}
focused = layout.FocusNext();
focused = layout->FocusNext();
scrollable = dynamic_cast<ScrollableWindow*>(focused);
input = dynamic_cast<IInput*>(focused);
if (input != NULL) {
curs_set(1);
input->Focus();
wtimeout(focused->GetContent(), IDLE_TIMEOUT_MS);
if (focused) {
wtimeout(focused->GetContent(), IDLE_TIMEOUT_MS);
}
}
else {
curs_set(0);

176
src/musikbox/MultiLineEntry.cpp Executable file
View File

@ -0,0 +1,176 @@
#include "stdafx.h"
#include "MultiLineEntry.h"
#include <boost/algorithm/string.hpp>
MultiLineEntry::MultiLineEntry(const std::string& value, int64 attrs) {
this->value = value;
this->charCount = value.size();
this->width = -1;
this->attrs = attrs;
}
size_t MultiLineEntry::GetLineCount() {
return max(1, this->lines.size());
}
std::string MultiLineEntry::GetLine(size_t n) {
return this->lines.at(n);
}
std::string MultiLineEntry::GetValue() {
return value;
}
size_t MultiLineEntry::GetIndex() {
return this->index;
}
void MultiLineEntry::SetIndex(size_t index) {
this->index = index;
}
int64 MultiLineEntry::GetAttrs() {
return this->attrs;
}
void MultiLineEntry::SetAttrs(int64 attrs) {
this->attrs = attrs;
}
inline static void breakIntoSubLines(
std::string& line,
size_t width,
std::vector<std::string>& output)
{
size_t len = u8len(line);
size_t count = (int)ceil((float)len / (float)width);
/* easy case: the line fits on a single line! */
if (count <= 1) {
output.push_back(line);
}
/* difficult case: the line needs to be split multiple sub-lines to fit
the output display */
else {
/* split by whitespace */
std::vector<std::string> words;
boost::algorithm::split(words, line, boost::is_any_of(" \t\v\f\r"));
/* this isn't super efficient, but let's find all words that are greater
than the width and break them into more sublines... it's possible to
do this more efficiently in the loop below this with a bunch of additional
logic, but let's keep things simple unless we run into performance
problems! */
std::vector<std::string> sanitizedWords;
for (size_t i = 0; i < words.size(); i++) {
std::string word = words.at(i);
size_t len = u8len(word);
/* this word is fine, it'll easily fit on its own line of necessary */
if (width >= len) {
sanitizedWords.push_back(word);
}
/* otherwise, the word needs to be broken into multiple lines. */
else {
std::string accum;
/* ugh, we gotta split on UTF8 characters, not actual characters.
this makes things a bit more difficult... we iterate over the string
one displayable character at a time, and break it apart into separate
lines as necessary. */
std::string::iterator begin = word.begin();
std::string::iterator end = word.begin();
int count = 0;
bool done = false;
while (end != word.end()) {
utf8::unchecked::next(end);
++count;
if (count == width || end == word.end()) {
sanitizedWords.push_back(std::string(begin, end));
begin = end;
count = 0;
}
}
}
}
words.clear();
/* now we have a bunch of tokenized words. let's string them together
into sequences that fit in the output window's width */
std::string accum;
size_t accumLength = 0;
for (size_t i = 0; i < sanitizedWords.size(); i++) {
std::string word = sanitizedWords.at(i);
size_t wordLength = u8len(word);
size_t extra = (i != 0) && (i != sanitizedWords.size() - 1);
/* we have enough space for this new word. accumulate it. the
+1 here is to take the space into account */
if (accumLength + extra + wordLength < width) {
if (extra) {
accum += " ";
}
accum += word;
accumLength += wordLength + extra;
}
/* otherwise, flush the current line, and start a new one... */
else {
if (accum.size()) {
output.push_back(accum);
}
/* special case -- if the word is the exactly length of the
width, just add it as a new line and reset... */
if (wordLength == width) {
output.push_back(word);
accum = "";
accumLength = 0;
}
/* otherwise, let's start accumulating a new line! */
else {
accum = word;
accumLength = wordLength;
}
}
}
if (accum.size()) {
output.push_back(accum);
}
}
}
void MultiLineEntry::SetWidth(size_t width) {
if (this->width != width) {
this->width = width;
this->lines.clear();
std::vector<std::string> split;
boost::algorithm::split(split, this->value, boost::is_any_of("\n"));
for (size_t i = 0; i < split.size(); i++) {
breakIntoSubLines(split.at(i), this->width, this->lines);
}
}
}

25
src/musikbox/MultiLineEntry.h Executable file
View File

@ -0,0 +1,25 @@
#pragma once
#include "IScrollAdapter.h"
class MultiLineEntry : public IScrollAdapter::IEntry {
public:
MultiLineEntry(const std::string& value, int64 attrs = -1);
size_t GetIndex();
void SetIndex(size_t index);
size_t GetLineCount();
std::string GetLine(size_t line);
std::string GetValue();
void SetWidth(size_t width);
void SetAttrs(int64 attrs);
int64 GetAttrs();
private:
size_t index;
std::string value;
std::vector<std::string> lines;
size_t charCount;
int64 attrs;
size_t width;
};

View File

@ -4,6 +4,9 @@
#include "OutputWindow.h"
#include "Screen.h"
#include "Colors.h"
#include "MultiLineEntry.h"
typedef IScrollAdapter::IEntry IEntry;
OutputWindow::OutputWindow()
: ScrollableWindow()
@ -24,6 +27,6 @@ IScrollAdapter& OutputWindow::GetScrollAdapter() {
}
void OutputWindow::WriteLine(const std::string& text, int64 attrs) {
this->adapter->AddLine(text, attrs);
this->adapter->AddEntry(boost::shared_ptr<IEntry>(new MultiLineEntry(text, attrs)));
this->OnAdapterChanged();
}

View File

@ -1,20 +1,13 @@
#include "stdafx.h"
#include "SimpleScrollAdapter.h"
#include "SingleLineEntry.h"
#include "MultiLineEntry.h"
#include <boost/algorithm/string.hpp>
#include <utf8/utf8/unchecked.h>
#define MAX_ENTRY_COUNT 0xffffffff
inline static int utf8Length(const std::string& str) {
try {
return utf8::distance(str.begin(), str.end());
}
catch (...) {
return str.length();
}
}
SimpleScrollAdapter::SimpleScrollAdapter() {
this->lineCount = 0;
@ -87,7 +80,7 @@ void SimpleScrollAdapter::DrawPage(WINDOW* window, size_t lineNumber) {
for (size_t i = c; i < count && remaining != 0; i++) {
std::string line = (*it)->GetLine(i).c_str();
size_t len = utf8Length(line);
size_t len = u8len(line);
/* don't add a newline if we're going to hit the end of the line, the
newline will be added automatically. */
@ -106,16 +99,14 @@ void SimpleScrollAdapter::DrawPage(WINDOW* window, size_t lineNumber) {
}
void SimpleScrollAdapter::AddLine(const std::string& str, int64 attrs) {
boost::shared_ptr<Entry> entry(new Entry(str));
void SimpleScrollAdapter::AddEntry(boost::shared_ptr<IEntry> entry) {
entry->SetWidth(this->width);
entry->SetIndex(this->lineCount + this->removedOffset);
entry->SetAttrs(attrs);
entries.push_back(entry);
this->lineCount += entry->GetLineCount();
while (entries.size() > this->maxEntries) {
boost::shared_ptr<Entry> e = entries.front();
boost::shared_ptr<IEntry> e = entries.front();
size_t lineCount = e->GetLineCount();
this->removedOffset += lineCount;
this->lineCount -= lineCount;
@ -133,7 +124,7 @@ size_t SimpleScrollAdapter::FindEntryIndex(size_t lineNumber) {
while (true) {
size_t guess = (min + max) / 2;
Entry* entry = this->entries.at(guess).get();
IEntry* entry = this->entries.at(guess).get();
size_t first = entry->GetIndex() - this->removedOffset;
size_t last = first + entry->GetLineCount();
if (lineNumber >= first && lineNumber <= last) {
@ -160,175 +151,3 @@ void SimpleScrollAdapter::Reindex() {
this->removedOffset = 0;
this->lineCount = index;
}
SimpleScrollAdapter::Entry::Entry(const std::string& value) {
this->value = value;
this->charCount = value.size();
this->width = -1;
}
size_t SimpleScrollAdapter::Entry::GetLineCount() {
return max(1, this->lines.size());
}
std::string SimpleScrollAdapter::Entry::GetLine(size_t n) {
return this->lines.at(n);
}
std::string SimpleScrollAdapter::Entry::GetValue() {
return value;
}
size_t SimpleScrollAdapter::Entry::GetIndex() {
return this->index;
}
void SimpleScrollAdapter::Entry::SetIndex(size_t index) {
this->index = index;
}
int64 SimpleScrollAdapter::Entry::GetAttrs() {
return this->attrs;
}
void SimpleScrollAdapter::Entry::SetAttrs(int64 attrs) {
this->attrs = attrs;
}
inline static void breakIntoSubLines(
std::string& line,
size_t width,
std::vector<std::string>& output)
{
size_t len = utf8Length(line);
size_t count = (int) ceil((float) len / (float) width);
/* easy case: the line fits on a single line! */
if (count <= 1) {
output.push_back(line);
}
/* difficult case: the line needs to be split multiple sub-lines to fit
the output display */
else {
/* split by whitespace */
std::vector<std::string> words;
boost::algorithm::split(words, line, boost::is_any_of(" \t\v\f\r"));
/* this isn't super efficient, but let's find all words that are greater
than the width and break them into more sublines... it's possible to
do this more efficiently in the loop below this with a bunch of additional
logic, but let's keep things simple unless we run into performance
problems! */
std::vector<std::string> sanitizedWords;
for (size_t i = 0; i < words.size(); i++) {
std::string word = words.at(i);
size_t len = utf8Length(word);
/* this word is fine, it'll easily fit on its own line of necessary */
if (width >= len) {
sanitizedWords.push_back(word);
}
/* otherwise, the word needs to be broken into multiple lines. */
else {
std::string accum;
/* ugh, we gotta split on UTF8 characters, not actual characters.
this makes things a bit more difficult... we iterate over the string
one displayable character at a time, and break it apart into separate
lines as necessary. */
std::string::iterator begin = word.begin();
std::string::iterator end = word.begin();
int count = 0;
bool done = false;
while (end != word.end()) {
utf8::unchecked::next(end);
++count;
if (count == width || end == word.end()) {
sanitizedWords.push_back(std::string(begin, end));
begin = end;
count = 0;
}
}
}
}
words.clear();
/* now we have a bunch of tokenized words. let's string them together
into sequences that fit in the output window's width */
std::string accum;
size_t accumLength = 0;
for (size_t i = 0; i < sanitizedWords.size(); i++) {
std::string word = sanitizedWords.at(i);
size_t wordLength = utf8Length(word);
size_t extra = (i != 0) && (i != sanitizedWords.size() - 1);
/* we have enough space for this new word. accumulate it. the
+1 here is to take the space into account */
if (accumLength + extra + wordLength < width) {
if (extra) {
accum += " ";
}
accum += word;
accumLength += wordLength + extra;
}
/* otherwise, flush the current line, and start a new one... */
else {
if (accum.size()) {
output.push_back(accum);
}
/* special case -- if the word is the exactly length of the
width, just add it as a new line and reset... */
if (wordLength == width) {
output.push_back(word);
accum = "";
accumLength = 0;
}
/* otherwise, let's start accumulating a new line! */
else {
accum = word;
accumLength = wordLength;
}
}
}
if (accum.size()) {
output.push_back(accum);
}
}
}
void SimpleScrollAdapter::Entry::SetWidth(size_t width) {
if (this->width != width) {
this->width = width;
this->lines.clear();
std::vector<std::string> split;
boost::algorithm::split(split, this->value, boost::is_any_of("\n"));
for (size_t i = 0; i < split.size(); i++) {
breakIntoSubLines(split.at(i), this->width, this->lines);
}
}
}

View File

@ -14,34 +14,11 @@ class SimpleScrollAdapter : public IScrollAdapter {
virtual size_t GetEntryCount();
virtual void DrawPage(WINDOW* window, size_t index);
virtual void AddLine(const std::string& str, int64 attrs = -1);
virtual void AddEntry(boost::shared_ptr<IEntry> entry);
virtual void SetMaxEntries(const size_t size = 500);
private:
class Entry {
public:
Entry(const std::string& value);
size_t GetIndex();
void SetIndex(size_t index);
size_t GetLineCount();
std::string GetLine(size_t line);
std::string GetValue();
void SetWidth(size_t width);
void SetAttrs(int64 attrs);
int64 GetAttrs();
private:
size_t index;
std::string value;
std::vector<std::string> lines;
size_t charCount;
int64 attrs;
size_t width;
};
private:
typedef std::deque<boost::shared_ptr<Entry>> EntryList;
typedef std::deque<boost::shared_ptr<IEntry>> EntryList;
typedef EntryList::iterator Iterator;
void Reindex();

View File

@ -0,0 +1,38 @@
#include "stdafx.h"
#include "SingleLineEntry.h"
#include <boost/algorithm/string.hpp>
SingleLineEntry::SingleLineEntry(const std::string& value) {
this->value = value;
}
size_t SingleLineEntry::GetIndex() {
return this->index;
}
void SingleLineEntry::SetWidth(size_t width) {
this->width = width;
}
void SingleLineEntry::SetIndex(size_t index) {
this->index = index;
}
int64 SingleLineEntry::GetAttrs() {
return this->attrs;
}
void SingleLineEntry::SetAttrs(int64 attrs) {
this->attrs = attrs;
}
size_t SingleLineEntry::GetLineCount() {
return 1;
}
std::string SingleLineEntry::GetLine(size_t line) {
return u8substr(this->value, 0, this->width > 0 ? this->width : 0);
}
std::string SingleLineEntry::GetValue() {
return this->value;
}

23
src/musikbox/SingleLineEntry.h Executable file
View File

@ -0,0 +1,23 @@
#pragma once
#include "IScrollAdapter.h"
class SingleLineEntry : public IScrollAdapter::IEntry {
public:
SingleLineEntry(const std::string& value);
size_t GetIndex();
void SetIndex(size_t index);
void SetWidth(size_t width);
void SetAttrs(int64 attrs);
int64 GetAttrs();
size_t GetLineCount();
std::string GetLine(size_t line);
std::string GetValue();
private:
size_t index, width;
std::string value;
int64 attrs;
};

View File

@ -116,7 +116,11 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="CategoryListQuery.cpp" />
<ClCompile Include="CategoryListView.cpp" />
<ClCompile Include="LibraryLayout.cpp" />
<ClCompile Include="MainLayout.cpp" />
<ClCompile Include="MultiLineEntry.cpp" />
<ClCompile Include="SingleLineEntry.cpp" />
<ClCompile Include="Window.cpp" />
<ClCompile Include="Colors.cpp" />
<ClCompile Include="CommandWindow.cpp" />
@ -138,8 +142,12 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="CategoryListQuery.h" />
<ClInclude Include="CategoryListView.h" />
<ClInclude Include="IScrollable.h" />
<ClInclude Include="LibraryLayout.h" />
<ClInclude Include="MainLayout.h" />
<ClInclude Include="MultiLineEntry.h" />
<ClInclude Include="SingleLineEntry.h" />
<ClInclude Include="Window.h" />
<ClInclude Include="Colors.h" />
<ClInclude Include="CommandWindow.h" />

View File

@ -48,6 +48,18 @@
<ClCompile Include="MainLayout.cpp">
<Filter>view</Filter>
</ClCompile>
<ClCompile Include="LibraryLayout.cpp">
<Filter>view</Filter>
</ClCompile>
<ClCompile Include="CategoryListView.cpp">
<Filter>view</Filter>
</ClCompile>
<ClCompile Include="SingleLineEntry.cpp">
<Filter>curses</Filter>
</ClCompile>
<ClCompile Include="MultiLineEntry.cpp">
<Filter>curses</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h" />
@ -102,6 +114,18 @@
<ClInclude Include="IScrollable.h">
<Filter>curses</Filter>
</ClInclude>
<ClInclude Include="LibraryLayout.h">
<Filter>view</Filter>
</ClInclude>
<ClInclude Include="CategoryListView.h">
<Filter>view</Filter>
</ClInclude>
<ClInclude Include="SingleLineEntry.h">
<Filter>curses</Filter>
</ClInclude>
<ClInclude Include="MultiLineEntry.h">
<Filter>curses</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="curses">