mirror of
https://github.com/clangen/musikcube.git
synced 2025-03-14 04:18:36 +00:00
Boring, necessary refactors to support more dynamic lists of content.
This commit is contained in:
parent
b718691553
commit
c179878e80
@ -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);
|
||||
}
|
@ -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;
|
||||
};
|
||||
|
||||
} }
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
};
|
20
src/musikbox/CategoryListView.cpp
Executable file
20
src/musikbox/CategoryListView.cpp
Executable 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
21
src/musikbox/CategoryListView.h
Executable 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;
|
||||
};
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
@ -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
30
src/musikbox/LibraryLayout.cpp
Executable 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
19
src/musikbox/LibraryLayout.h
Executable 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;
|
||||
};
|
@ -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;
|
||||
}
|
||||
|
@ -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
176
src/musikbox/MultiLineEntry.cpp
Executable 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
25
src/musikbox/MultiLineEntry.h
Executable 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;
|
||||
};
|
@ -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();
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
38
src/musikbox/SingleLineEntry.cpp
Executable file
38
src/musikbox/SingleLineEntry.cpp
Executable 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
23
src/musikbox/SingleLineEntry.h
Executable 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;
|
||||
};
|
@ -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" />
|
||||
|
@ -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">
|
||||
|
Loading…
x
Reference in New Issue
Block a user