Normalize line endings in already existing files

This commit is contained in:
Leon Richardt
2019-09-08 22:27:57 +02:00
parent 8064f8a49e
commit b06eb9df83
157 changed files with 15175 additions and 15175 deletions
+47 -47
View File
@@ -1,47 +1,47 @@
#pragma once
#include <boost/noncopyable.hpp>
#include <mutex>
namespace chatterino {
template <typename T>
class Atomic : boost::noncopyable
{
public:
Atomic()
{
}
Atomic(T &&val)
: value_(val)
{
}
T get() const
{
std::lock_guard<std::mutex> guard(this->mutex_);
return this->value_;
}
void set(const T &val)
{
std::lock_guard<std::mutex> guard(this->mutex_);
this->value_ = val;
}
void set(T &&val)
{
std::lock_guard<std::mutex> guard(this->mutex_);
this->value_ = std::move(val);
}
private:
mutable std::mutex mutex_;
T value_;
};
} // namespace chatterino
#pragma once
#include <boost/noncopyable.hpp>
#include <mutex>
namespace chatterino {
template <typename T>
class Atomic : boost::noncopyable
{
public:
Atomic()
{
}
Atomic(T &&val)
: value_(val)
{
}
T get() const
{
std::lock_guard<std::mutex> guard(this->mutex_);
return this->value_;
}
void set(const T &val)
{
std::lock_guard<std::mutex> guard(this->mutex_);
this->value_ = val;
}
void set(T &&val)
{
std::lock_guard<std::mutex> guard(this->mutex_);
this->value_ = std::move(val);
}
private:
mutable std::mutex mutex_;
T value_;
};
} // namespace chatterino
+49 -49
View File
@@ -1,49 +1,49 @@
#pragma once
#include "common/Aliases.hpp"
#include "common/Outcome.hpp"
#include "common/ProviderId.hpp"
#include <QString>
#include <QWidget>
#include <boost/optional.hpp>
#include <boost/preprocessor.hpp>
#include <string>
namespace chatterino {
enum class HighlightState {
None,
Highlighted,
NewMessage,
};
inline QString qS(const std::string &string)
{
return QString::fromStdString(string);
}
const Qt::KeyboardModifiers showSplitOverlayModifiers =
Qt::ControlModifier | Qt::AltModifier;
const Qt::KeyboardModifiers showAddSplitRegions =
Qt::ControlModifier | Qt::AltModifier;
const Qt::KeyboardModifiers showResizeHandlesModifiers = Qt::ControlModifier;
static const char *ANONYMOUS_USERNAME_LABEL ATTR_UNUSED = " - anonymous - ";
template <typename T>
std::weak_ptr<T> weakOf(T *element)
{
return element->shared_from_this();
}
struct Message;
using MessagePtr = std::shared_ptr<const Message>;
enum class CopyMode {
Everything,
OnlyTextAndEmotes,
};
} // namespace chatterino
#pragma once
#include "common/Aliases.hpp"
#include "common/Outcome.hpp"
#include "common/ProviderId.hpp"
#include <QString>
#include <QWidget>
#include <boost/optional.hpp>
#include <boost/preprocessor.hpp>
#include <string>
namespace chatterino {
enum class HighlightState {
None,
Highlighted,
NewMessage,
};
inline QString qS(const std::string &string)
{
return QString::fromStdString(string);
}
const Qt::KeyboardModifiers showSplitOverlayModifiers =
Qt::ControlModifier | Qt::AltModifier;
const Qt::KeyboardModifiers showAddSplitRegions =
Qt::ControlModifier | Qt::AltModifier;
const Qt::KeyboardModifiers showResizeHandlesModifiers = Qt::ControlModifier;
static const char *ANONYMOUS_USERNAME_LABEL ATTR_UNUSED = " - anonymous - ";
template <typename T>
std::weak_ptr<T> weakOf(T *element)
{
return element->shared_from_this();
}
struct Message;
using MessagePtr = std::shared_ptr<const Message>;
enum class CopyMode {
Everything,
OnlyTextAndEmotes,
};
} // namespace chatterino
+195 -195
View File
@@ -1,195 +1,195 @@
#include "common/CompletionModel.hpp"
#include "Application.hpp"
#include "common/Common.hpp"
#include "common/UsernameSet.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/commands/CommandController.hpp"
#include "debug/Benchmark.hpp"
#include "debug/Log.hpp"
#include "providers/twitch/TwitchChannel.hpp"
#include "providers/twitch/TwitchServer.hpp"
#include "singletons/Emotes.hpp"
#include "singletons/Settings.hpp"
#include <QtAlgorithms>
#include <utility>
namespace chatterino {
//
// TaggedString
//
CompletionModel::TaggedString::TaggedString(const QString &_string, Type _type)
: string(_string)
, type(_type)
{
}
bool CompletionModel::TaggedString::isEmote() const
{
return this->type > Type::EmoteStart && this->type < Type::EmoteEnd;
}
bool CompletionModel::TaggedString::operator<(const TaggedString &that) const
{
if (this->isEmote() != that.isEmote())
{
return this->isEmote();
}
// try comparing insensitively, if they are the same then senstively
// (fixes order of LuL and LUL)
int k = QString::compare(this->string, that.string, Qt::CaseInsensitive);
if (k == 0)
return this->string > that.string;
return k < 0;
}
//
// CompletionModel
//
CompletionModel::CompletionModel(Channel &channel)
: channel_(channel)
{
}
int CompletionModel::columnCount(const QModelIndex &) const
{
return 1;
}
QVariant CompletionModel::data(const QModelIndex &index, int) const
{
std::lock_guard<std::mutex> lock(this->itemsMutex_);
auto it = this->items_.begin();
std::advance(it, index.row());
return QVariant(it->string);
}
int CompletionModel::rowCount(const QModelIndex &) const
{
std::lock_guard<std::mutex> lock(this->itemsMutex_);
return this->items_.size();
}
void CompletionModel::refresh(const QString &prefix, bool isFirstWord)
{
std::function<void(const QString &, TaggedString::Type)> addString;
if (getSettings()->prefixOnlyEmoteCompletion)
{
addString = [&](const QString &str, TaggedString::Type type) {
if (str.startsWith(prefix, Qt::CaseInsensitive))
this->items_.emplace(str + " ", type);
};
}
else
{
addString = [&](const QString &str, TaggedString::Type type) {
if (str.contains(prefix, Qt::CaseInsensitive))
this->items_.emplace(str + " ", type);
};
}
std::lock_guard<std::mutex> guard(this->itemsMutex_);
this->items_.clear();
if (prefix.length() < 2)
return;
if (auto channel = dynamic_cast<TwitchChannel *>(&this->channel_))
{
// account emotes
if (auto account = getApp()->accounts->twitch.getCurrent())
{
for (const auto &emote : account->accessEmotes()->allEmoteNames)
{
// XXX: No way to discern between a twitch global emote and sub
// emote right now
addString(emote.string, TaggedString::Type::TwitchGlobalEmote);
}
}
// Usernames
if (prefix.length() >= UsernameSet::PrefixLength)
{
auto usernames = channel->accessChatters();
QString usernamePrefix = prefix;
QString usernamePostfix =
isFirstWord && getSettings()->mentionUsersWithComma ? ","
: QString();
if (usernamePrefix.startsWith("@"))
{
usernamePrefix.remove(0, 1);
for (const auto &name :
usernames->subrange(Prefix(usernamePrefix)))
{
addString("@" + name + usernamePostfix,
TaggedString::Type::Username);
}
}
else
{
for (const auto &name :
usernames->subrange(Prefix(usernamePrefix)))
{
addString(name + usernamePostfix,
TaggedString::Type::Username);
}
}
}
// Bttv Global
for (auto &emote : *channel->globalBttv().emotes())
{
addString(emote.first.string, TaggedString::Type::BTTVChannelEmote);
}
// Ffz Global
for (auto &emote : *channel->globalFfz().emotes())
{
addString(emote.first.string, TaggedString::Type::FFZChannelEmote);
}
// Bttv Channel
for (auto &emote : *channel->bttvEmotes())
{
addString(emote.first.string, TaggedString::Type::BTTVGlobalEmote);
}
// Ffz Channel
for (auto &emote : *channel->ffzEmotes())
{
addString(emote.first.string, TaggedString::Type::BTTVGlobalEmote);
}
// Emojis
if (prefix.startsWith(":"))
{
const auto &emojiShortCodes = getApp()->emotes->emojis.shortCodes;
for (auto &m : emojiShortCodes)
{
addString(":" + m + ":", TaggedString::Type::Emoji);
}
}
// Commands
for (auto &command : getApp()->commands->items_)
{
addString(command.name, TaggedString::Command);
}
for (auto &command : getApp()->commands->getDefaultTwitchCommandList())
{
addString(command, TaggedString::Command);
}
}
}
} // namespace chatterino
#include "common/CompletionModel.hpp"
#include "Application.hpp"
#include "common/Common.hpp"
#include "common/UsernameSet.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/commands/CommandController.hpp"
#include "debug/Benchmark.hpp"
#include "debug/Log.hpp"
#include "providers/twitch/TwitchChannel.hpp"
#include "providers/twitch/TwitchServer.hpp"
#include "singletons/Emotes.hpp"
#include "singletons/Settings.hpp"
#include <QtAlgorithms>
#include <utility>
namespace chatterino {
//
// TaggedString
//
CompletionModel::TaggedString::TaggedString(const QString &_string, Type _type)
: string(_string)
, type(_type)
{
}
bool CompletionModel::TaggedString::isEmote() const
{
return this->type > Type::EmoteStart && this->type < Type::EmoteEnd;
}
bool CompletionModel::TaggedString::operator<(const TaggedString &that) const
{
if (this->isEmote() != that.isEmote())
{
return this->isEmote();
}
// try comparing insensitively, if they are the same then senstively
// (fixes order of LuL and LUL)
int k = QString::compare(this->string, that.string, Qt::CaseInsensitive);
if (k == 0)
return this->string > that.string;
return k < 0;
}
//
// CompletionModel
//
CompletionModel::CompletionModel(Channel &channel)
: channel_(channel)
{
}
int CompletionModel::columnCount(const QModelIndex &) const
{
return 1;
}
QVariant CompletionModel::data(const QModelIndex &index, int) const
{
std::lock_guard<std::mutex> lock(this->itemsMutex_);
auto it = this->items_.begin();
std::advance(it, index.row());
return QVariant(it->string);
}
int CompletionModel::rowCount(const QModelIndex &) const
{
std::lock_guard<std::mutex> lock(this->itemsMutex_);
return this->items_.size();
}
void CompletionModel::refresh(const QString &prefix, bool isFirstWord)
{
std::function<void(const QString &, TaggedString::Type)> addString;
if (getSettings()->prefixOnlyEmoteCompletion)
{
addString = [&](const QString &str, TaggedString::Type type) {
if (str.startsWith(prefix, Qt::CaseInsensitive))
this->items_.emplace(str + " ", type);
};
}
else
{
addString = [&](const QString &str, TaggedString::Type type) {
if (str.contains(prefix, Qt::CaseInsensitive))
this->items_.emplace(str + " ", type);
};
}
std::lock_guard<std::mutex> guard(this->itemsMutex_);
this->items_.clear();
if (prefix.length() < 2)
return;
if (auto channel = dynamic_cast<TwitchChannel *>(&this->channel_))
{
// account emotes
if (auto account = getApp()->accounts->twitch.getCurrent())
{
for (const auto &emote : account->accessEmotes()->allEmoteNames)
{
// XXX: No way to discern between a twitch global emote and sub
// emote right now
addString(emote.string, TaggedString::Type::TwitchGlobalEmote);
}
}
// Usernames
if (prefix.length() >= UsernameSet::PrefixLength)
{
auto usernames = channel->accessChatters();
QString usernamePrefix = prefix;
QString usernamePostfix =
isFirstWord && getSettings()->mentionUsersWithComma ? ","
: QString();
if (usernamePrefix.startsWith("@"))
{
usernamePrefix.remove(0, 1);
for (const auto &name :
usernames->subrange(Prefix(usernamePrefix)))
{
addString("@" + name + usernamePostfix,
TaggedString::Type::Username);
}
}
else
{
for (const auto &name :
usernames->subrange(Prefix(usernamePrefix)))
{
addString(name + usernamePostfix,
TaggedString::Type::Username);
}
}
}
// Bttv Global
for (auto &emote : *channel->globalBttv().emotes())
{
addString(emote.first.string, TaggedString::Type::BTTVChannelEmote);
}
// Ffz Global
for (auto &emote : *channel->globalFfz().emotes())
{
addString(emote.first.string, TaggedString::Type::FFZChannelEmote);
}
// Bttv Channel
for (auto &emote : *channel->bttvEmotes())
{
addString(emote.first.string, TaggedString::Type::BTTVGlobalEmote);
}
// Ffz Channel
for (auto &emote : *channel->ffzEmotes())
{
addString(emote.first.string, TaggedString::Type::BTTVGlobalEmote);
}
// Emojis
if (prefix.startsWith(":"))
{
const auto &emojiShortCodes = getApp()->emotes->emojis.shortCodes;
for (auto &m : emojiShortCodes)
{
addString(":" + m + ":", TaggedString::Type::Emoji);
}
}
// Commands
for (auto &command : getApp()->commands->items_)
{
addString(command.name, TaggedString::Command);
}
for (auto &command : getApp()->commands->getDefaultTwitchCommandList())
{
addString(command, TaggedString::Command);
}
}
}
} // namespace chatterino
+60 -60
View File
@@ -1,60 +1,60 @@
#pragma once
#include <QAbstractListModel>
#include <chrono>
#include <mutex>
#include <set>
namespace chatterino {
class Channel;
class CompletionModel : public QAbstractListModel
{
struct TaggedString {
enum Type {
Username,
// emotes
EmoteStart,
FFZGlobalEmote,
FFZChannelEmote,
BTTVGlobalEmote,
BTTVChannelEmote,
TwitchGlobalEmote,
TwitchSubscriberEmote,
Emoji,
EmoteEnd,
// end emotes
Command,
};
TaggedString(const QString &string, Type type);
bool isEmote() const;
bool operator<(const TaggedString &that) const;
QString string;
Type type;
};
public:
CompletionModel(Channel &channel);
virtual int columnCount(const QModelIndex &) const override;
virtual QVariant data(const QModelIndex &index, int) const override;
virtual int rowCount(const QModelIndex &) const override;
void refresh(const QString &prefix, bool isFirstWord = false);
private:
TaggedString createUser(const QString &str);
std::set<TaggedString> items_;
mutable std::mutex itemsMutex_;
Channel &channel_;
};
} // namespace chatterino
#pragma once
#include <QAbstractListModel>
#include <chrono>
#include <mutex>
#include <set>
namespace chatterino {
class Channel;
class CompletionModel : public QAbstractListModel
{
struct TaggedString {
enum Type {
Username,
// emotes
EmoteStart,
FFZGlobalEmote,
FFZChannelEmote,
BTTVGlobalEmote,
BTTVChannelEmote,
TwitchGlobalEmote,
TwitchSubscriberEmote,
Emoji,
EmoteEnd,
// end emotes
Command,
};
TaggedString(const QString &string, Type type);
bool isEmote() const;
bool operator<(const TaggedString &that) const;
QString string;
Type type;
};
public:
CompletionModel(Channel &channel);
virtual int columnCount(const QModelIndex &) const override;
virtual QVariant data(const QModelIndex &index, int) const override;
virtual int rowCount(const QModelIndex &) const override;
void refresh(const QString &prefix, bool isFirstWord = false);
private:
TaggedString createUser(const QString &str);
std::set<TaggedString> items_;
mutable std::mutex itemsMutex_;
Channel &channel_;
};
} // namespace chatterino
+73 -73
View File
@@ -1,73 +1,73 @@
#pragma once
#include <type_traits>
namespace chatterino {
template <typename T>
class NullablePtr
{
public:
NullablePtr()
: element_(nullptr)
{
}
NullablePtr(T *element)
: element_(element)
{
}
T *operator->() const
{
assert(this->hasElement());
return element_;
}
typename std::add_lvalue_reference<T>::type operator*() const
{
assert(this->hasElement());
return *element_;
}
T *get() const
{
assert(this->hasElement());
return this->element_;
}
bool isNull() const
{
return this->element_ == nullptr;
}
bool hasElement() const
{
return this->element_ != nullptr;
}
operator bool() const
{
return this->hasElement();
}
bool operator!() const
{
return !this->hasElement();
}
template <typename X = T,
typename = std::enable_if_t<!std::is_const<X>::value>>
operator NullablePtr<const T>() const
{
return NullablePtr<const T>(this->element_);
}
private:
T *element_;
};
} // namespace chatterino
#pragma once
#include <type_traits>
namespace chatterino {
template <typename T>
class NullablePtr
{
public:
NullablePtr()
: element_(nullptr)
{
}
NullablePtr(T *element)
: element_(element)
{
}
T *operator->() const
{
assert(this->hasElement());
return element_;
}
typename std::add_lvalue_reference<T>::type operator*() const
{
assert(this->hasElement());
return *element_;
}
T *get() const
{
assert(this->hasElement());
return this->element_;
}
bool isNull() const
{
return this->element_ == nullptr;
}
bool hasElement() const
{
return this->element_ != nullptr;
}
operator bool() const
{
return this->hasElement();
}
bool operator!() const
{
return !this->hasElement();
}
template <typename X = T,
typename = std::enable_if_t<!std::is_const<X>::value>>
operator NullablePtr<const T>() const
{
return NullablePtr<const T>(this->element_);
}
private:
T *element_;
};
} // namespace chatterino
+7 -7
View File
@@ -1,7 +1,7 @@
#pragma once
namespace chatterino {
enum class ProviderId { Twitch, Irc };
//
} // namespace chatterino
#pragma once
namespace chatterino {
enum class ProviderId { Twitch, Irc };
//
} // namespace chatterino
+247 -247
View File
@@ -1,247 +1,247 @@
#pragma once
#include <QStandardItemModel>
#include <QTimer>
#include <boost/noncopyable.hpp>
#include <pajlada/signals/signal.hpp>
#include <shared_mutex>
#include <vector>
#include "debug/AssertInGuiThread.hpp"
namespace chatterino {
template <typename TVectorItem>
struct SignalVectorItemArgs {
const TVectorItem &item;
int index;
void *caller;
};
template <typename TVectorItem>
class ReadOnlySignalVector : boost::noncopyable
{
using VecIt = typename std::vector<TVectorItem>::iterator;
public:
struct Iterator
: public std::iterator<std::input_iterator_tag, TVectorItem> {
Iterator(VecIt &&it, std::shared_mutex &mutex)
: it_(std::move(it))
, lock_(mutex)
, mutex_(mutex)
{
}
Iterator(const Iterator &other)
: it_(other.it_)
, lock_(other.mutex_)
, mutex_(other.mutex_)
{
}
Iterator &operator=(const Iterator &other)
{
this->lock_ = std::shared_lock(other.mutex_.get());
this->mutex_ = other.mutex_;
return *this;
}
TVectorItem &operator*()
{
return it_.operator*();
}
Iterator &operator++()
{
++this->it_;
return *this;
}
bool operator==(const Iterator &other)
{
return this->it_ == other.it_;
}
bool operator!=(const Iterator &other)
{
return this->it_ != other.it_;
}
auto operator-(const Iterator &other)
{
return this->it_ - other.it_;
}
private:
VecIt it_;
std::shared_lock<std::shared_mutex> lock_;
std::reference_wrapper<std::shared_mutex> mutex_;
};
ReadOnlySignalVector()
{
QObject::connect(&this->itemsChangedTimer_, &QTimer::timeout,
[this] { this->delayedItemsChanged.invoke(); });
this->itemsChangedTimer_.setInterval(100);
this->itemsChangedTimer_.setSingleShot(true);
}
virtual ~ReadOnlySignalVector() = default;
pajlada::Signals::Signal<SignalVectorItemArgs<TVectorItem>> itemInserted;
pajlada::Signals::Signal<SignalVectorItemArgs<TVectorItem>> itemRemoved;
pajlada::Signals::NoArgSignal delayedItemsChanged;
Iterator begin() const
{
return Iterator(
const_cast<std::vector<TVectorItem> &>(this->vector_).begin(),
this->mutex_);
}
Iterator end() const
{
return Iterator(
const_cast<std::vector<TVectorItem> &>(this->vector_).end(),
this->mutex_);
}
bool empty() const
{
std::shared_lock lock(this->mutex_);
return this->vector_.empty();
}
const std::vector<TVectorItem> &getVector() const
{
assertInGuiThread();
return this->vector_;
}
std::vector<TVectorItem> cloneVector() const
{
std::shared_lock lock(this->mutex_);
return this->vector_;
}
void invokeDelayedItemsChanged()
{
assertInGuiThread();
if (!this->itemsChangedTimer_.isActive())
{
this->itemsChangedTimer_.start();
}
}
virtual bool isSorted() const = 0;
protected:
std::vector<TVectorItem> vector_;
QTimer itemsChangedTimer_;
mutable std::shared_mutex mutex_;
};
template <typename TVectorItem>
class BaseSignalVector : public ReadOnlySignalVector<TVectorItem>
{
public:
// returns the actual index of the inserted item
virtual int insertItem(const TVectorItem &item, int proposedIndex = -1,
void *caller = nullptr) = 0;
void removeItem(int index, void *caller = nullptr)
{
assertInGuiThread();
std::unique_lock lock(this->mutex_);
assert(index >= 0 && index < int(this->vector_.size()));
TVectorItem item = this->vector_[index];
this->vector_.erase(this->vector_.begin() + index);
lock.unlock(); // manual unlock
SignalVectorItemArgs<TVectorItem> args{item, index, caller};
this->itemRemoved.invoke(args);
this->invokeDelayedItemsChanged();
}
int appendItem(const TVectorItem &item, void *caller = nullptr)
{
return this->insertItem(item, -1, caller);
}
};
template <typename TVectorItem>
class UnsortedSignalVector : public BaseSignalVector<TVectorItem>
{
public:
virtual int insertItem(const TVectorItem &item, int index = -1,
void *caller = nullptr) override
{
assertInGuiThread();
{
std::unique_lock lock(this->mutex_);
if (index == -1)
{
index = this->vector_.size();
}
else
{
assert(index >= 0 && index <= this->vector_.size());
}
this->vector_.insert(this->vector_.begin() + index, item);
}
SignalVectorItemArgs<TVectorItem> args{item, index, caller};
this->itemInserted.invoke(args);
this->invokeDelayedItemsChanged();
return index;
}
virtual bool isSorted() const override
{
return false;
}
};
template <typename TVectorItem, typename Compare>
class SortedSignalVector : public BaseSignalVector<TVectorItem>
{
public:
virtual int insertItem(const TVectorItem &item, int = -1,
void *caller = nullptr) override
{
assertInGuiThread();
int index = -1;
{
std::unique_lock lock(this->mutex_);
auto it = std::lower_bound(this->vector_.begin(),
this->vector_.end(), item, Compare{});
index = it - this->vector_.begin();
this->vector_.insert(it, item);
}
SignalVectorItemArgs<TVectorItem> args{item, index, caller};
this->itemInserted.invoke(args);
this->invokeDelayedItemsChanged();
return index;
}
virtual bool isSorted() const override
{
return true;
}
};
} // namespace chatterino
#pragma once
#include <QStandardItemModel>
#include <QTimer>
#include <boost/noncopyable.hpp>
#include <pajlada/signals/signal.hpp>
#include <shared_mutex>
#include <vector>
#include "debug/AssertInGuiThread.hpp"
namespace chatterino {
template <typename TVectorItem>
struct SignalVectorItemArgs {
const TVectorItem &item;
int index;
void *caller;
};
template <typename TVectorItem>
class ReadOnlySignalVector : boost::noncopyable
{
using VecIt = typename std::vector<TVectorItem>::iterator;
public:
struct Iterator
: public std::iterator<std::input_iterator_tag, TVectorItem> {
Iterator(VecIt &&it, std::shared_mutex &mutex)
: it_(std::move(it))
, lock_(mutex)
, mutex_(mutex)
{
}
Iterator(const Iterator &other)
: it_(other.it_)
, lock_(other.mutex_)
, mutex_(other.mutex_)
{
}
Iterator &operator=(const Iterator &other)
{
this->lock_ = std::shared_lock(other.mutex_.get());
this->mutex_ = other.mutex_;
return *this;
}
TVectorItem &operator*()
{
return it_.operator*();
}
Iterator &operator++()
{
++this->it_;
return *this;
}
bool operator==(const Iterator &other)
{
return this->it_ == other.it_;
}
bool operator!=(const Iterator &other)
{
return this->it_ != other.it_;
}
auto operator-(const Iterator &other)
{
return this->it_ - other.it_;
}
private:
VecIt it_;
std::shared_lock<std::shared_mutex> lock_;
std::reference_wrapper<std::shared_mutex> mutex_;
};
ReadOnlySignalVector()
{
QObject::connect(&this->itemsChangedTimer_, &QTimer::timeout,
[this] { this->delayedItemsChanged.invoke(); });
this->itemsChangedTimer_.setInterval(100);
this->itemsChangedTimer_.setSingleShot(true);
}
virtual ~ReadOnlySignalVector() = default;
pajlada::Signals::Signal<SignalVectorItemArgs<TVectorItem>> itemInserted;
pajlada::Signals::Signal<SignalVectorItemArgs<TVectorItem>> itemRemoved;
pajlada::Signals::NoArgSignal delayedItemsChanged;
Iterator begin() const
{
return Iterator(
const_cast<std::vector<TVectorItem> &>(this->vector_).begin(),
this->mutex_);
}
Iterator end() const
{
return Iterator(
const_cast<std::vector<TVectorItem> &>(this->vector_).end(),
this->mutex_);
}
bool empty() const
{
std::shared_lock lock(this->mutex_);
return this->vector_.empty();
}
const std::vector<TVectorItem> &getVector() const
{
assertInGuiThread();
return this->vector_;
}
std::vector<TVectorItem> cloneVector() const
{
std::shared_lock lock(this->mutex_);
return this->vector_;
}
void invokeDelayedItemsChanged()
{
assertInGuiThread();
if (!this->itemsChangedTimer_.isActive())
{
this->itemsChangedTimer_.start();
}
}
virtual bool isSorted() const = 0;
protected:
std::vector<TVectorItem> vector_;
QTimer itemsChangedTimer_;
mutable std::shared_mutex mutex_;
};
template <typename TVectorItem>
class BaseSignalVector : public ReadOnlySignalVector<TVectorItem>
{
public:
// returns the actual index of the inserted item
virtual int insertItem(const TVectorItem &item, int proposedIndex = -1,
void *caller = nullptr) = 0;
void removeItem(int index, void *caller = nullptr)
{
assertInGuiThread();
std::unique_lock lock(this->mutex_);
assert(index >= 0 && index < int(this->vector_.size()));
TVectorItem item = this->vector_[index];
this->vector_.erase(this->vector_.begin() + index);
lock.unlock(); // manual unlock
SignalVectorItemArgs<TVectorItem> args{item, index, caller};
this->itemRemoved.invoke(args);
this->invokeDelayedItemsChanged();
}
int appendItem(const TVectorItem &item, void *caller = nullptr)
{
return this->insertItem(item, -1, caller);
}
};
template <typename TVectorItem>
class UnsortedSignalVector : public BaseSignalVector<TVectorItem>
{
public:
virtual int insertItem(const TVectorItem &item, int index = -1,
void *caller = nullptr) override
{
assertInGuiThread();
{
std::unique_lock lock(this->mutex_);
if (index == -1)
{
index = this->vector_.size();
}
else
{
assert(index >= 0 && index <= this->vector_.size());
}
this->vector_.insert(this->vector_.begin() + index, item);
}
SignalVectorItemArgs<TVectorItem> args{item, index, caller};
this->itemInserted.invoke(args);
this->invokeDelayedItemsChanged();
return index;
}
virtual bool isSorted() const override
{
return false;
}
};
template <typename TVectorItem, typename Compare>
class SortedSignalVector : public BaseSignalVector<TVectorItem>
{
public:
virtual int insertItem(const TVectorItem &item, int = -1,
void *caller = nullptr) override
{
assertInGuiThread();
int index = -1;
{
std::unique_lock lock(this->mutex_);
auto it = std::lower_bound(this->vector_.begin(),
this->vector_.end(), item, Compare{});
index = it - this->vector_.begin();
this->vector_.insert(it, item);
}
SignalVectorItemArgs<TVectorItem> args{item, index, caller};
this->itemInserted.invoke(args);
this->invokeDelayedItemsChanged();
return index;
}
virtual bool isSorted() const override
{
return true;
}
};
} // namespace chatterino
+357 -357
View File
@@ -1,357 +1,357 @@
#pragma once
#include "common/SignalVector.hpp"
#include <QAbstractTableModel>
#include <QStandardItem>
#include <boost/optional.hpp>
#include <pajlada/signals/signalholder.hpp>
namespace chatterino {
template <typename TVectorItem>
class SignalVectorModel : public QAbstractTableModel,
pajlada::Signals::SignalHolder
{
public:
SignalVectorModel(int columnCount, QObject *parent = nullptr)
: QAbstractTableModel(parent)
, columnCount_(columnCount)
{
for (int i = 0; i < columnCount; i++)
{
this->headerData_.emplace_back();
}
}
void init(BaseSignalVector<TVectorItem> *vec)
{
this->vector_ = vec;
auto insert = [this](const SignalVectorItemArgs<TVectorItem> &args) {
if (args.caller == this)
{
return;
}
// get row index
int index = this->getModelIndexFromVectorIndex(args.index);
assert(index >= 0 && index <= this->rows_.size());
// get row items
std::vector<QStandardItem *> row = this->createRow();
this->getRowFromItem(args.item, row);
// insert row
index = this->beforeInsert(args.item, row, index);
this->beginInsertRows(QModelIndex(), index, index);
this->rows_.insert(this->rows_.begin() + index,
Row(row, args.item));
this->endInsertRows();
};
int i = 0;
for (const TVectorItem &item : vec->getVector())
{
SignalVectorItemArgs<TVectorItem> args{item, i++, 0};
insert(args);
}
this->managedConnect(vec->itemInserted, insert);
this->managedConnect(vec->itemRemoved, [this](auto args) {
if (args.caller == this)
{
return;
}
int row = this->getModelIndexFromVectorIndex(args.index);
assert(row >= 0 && row <= this->rows_.size());
// remove row
std::vector<QStandardItem *> items =
std::move(this->rows_[row].items);
this->beginRemoveRows(QModelIndex(), row, row);
this->rows_.erase(this->rows_.begin() + row);
this->endRemoveRows();
this->afterRemoved(args.item, items, row);
for (QStandardItem *item : items)
{
delete item;
}
});
this->afterInit();
}
virtual ~SignalVectorModel()
{
for (Row &row : this->rows_)
{
for (QStandardItem *item : row.items)
{
delete item;
}
}
}
int rowCount(const QModelIndex &parent) const override
{
return this->rows_.size();
}
int columnCount(const QModelIndex &parent) const override
{
return this->columnCount_;
}
QVariant data(const QModelIndex &index, int role) const override
{
int row = index.row(), column = index.column();
assert(row >= 0 && row < this->rows_.size() && column >= 0 &&
column < this->columnCount_);
return rows_[row].items[column]->data(role);
}
bool setData(const QModelIndex &index, const QVariant &value,
int role) override
{
int row = index.row(), column = index.column();
assert(row >= 0 && row < this->rows_.size() && column >= 0 &&
column < this->columnCount_);
Row &rowItem = this->rows_[row];
rowItem.items[column]->setData(value, role);
if (rowItem.isCustomRow)
{
this->customRowSetData(rowItem.items, column, value, role, row);
}
else
{
int vecRow = this->getVectorIndexFromModelIndex(row);
this->vector_->removeItem(vecRow, this);
assert(this->rows_[row].original);
TVectorItem item = this->getItemFromRow(
this->rows_[row].items, this->rows_[row].original.get());
this->vector_->insertItem(item, vecRow, this);
}
return true;
}
QVariant headerData(int section, Qt::Orientation orientation,
int role) const override
{
if (orientation != Qt::Horizontal)
{
return QVariant();
}
auto it = this->headerData_[section].find(role);
if (it == this->headerData_[section].end())
{
return QVariant();
}
else
{
return it.value();
}
}
bool setHeaderData(int section, Qt::Orientation orientation,
const QVariant &value,
int role = Qt::DisplayRole) override
{
if (orientation != Qt::Horizontal)
{
return false;
}
this->headerData_[section][role] = value;
emit this->headerDataChanged(Qt::Horizontal, section, section);
return true;
}
Qt::ItemFlags flags(const QModelIndex &index) const override
{
int row = index.row(), column = index.column();
assert(row >= 0 && row < this->rows_.size() && column >= 0 &&
column < this->columnCount_);
return this->rows_[row].items[column]->flags();
}
QStandardItem *getItem(int row, int column)
{
assert(row >= 0 && row < this->rows_.size() && column >= 0 &&
column < this->columnCount_);
return rows_[row].items[column];
}
void deleteRow(int row)
{
int signalVectorRow = this->getVectorIndexFromModelIndex(row);
this->vector_->removeItem(signalVectorRow);
}
bool removeRows(int row, int count, const QModelIndex &parent) override
{
if (count != 1)
{
return false;
}
assert(row >= 0 && row < this->rows_.size());
int signalVectorRow = this->getVectorIndexFromModelIndex(row);
this->vector_->removeItem(signalVectorRow);
return true;
}
protected:
virtual void afterInit()
{
}
// turn a vector item into a model row
virtual TVectorItem getItemFromRow(std::vector<QStandardItem *> &row,
const TVectorItem &original) = 0;
// turns a row in the model into a vector item
virtual void getRowFromItem(const TVectorItem &item,
std::vector<QStandardItem *> &row) = 0;
virtual int beforeInsert(const TVectorItem &item,
std::vector<QStandardItem *> &row,
int proposedIndex)
{
return proposedIndex;
}
virtual void afterRemoved(const TVectorItem &item,
std::vector<QStandardItem *> &row, int index)
{
}
virtual void customRowSetData(const std::vector<QStandardItem *> &row,
int column, const QVariant &value, int role,
int rowIndex)
{
}
void insertCustomRow(std::vector<QStandardItem *> row, int index)
{
assert(index >= 0 && index <= this->rows_.size());
this->beginInsertRows(QModelIndex(), index, index);
this->rows_.insert(this->rows_.begin() + index,
Row(std::move(row), true));
this->endInsertRows();
}
void removeCustomRow(int index)
{
assert(index >= 0 && index <= this->rows_.size());
assert(this->rows_[index].isCustomRow);
this->beginRemoveRows(QModelIndex(), index, index);
this->rows_.erase(this->rows_.begin() + index);
this->endRemoveRows();
}
std::vector<QStandardItem *> createRow()
{
std::vector<QStandardItem *> row;
for (int i = 0; i < this->columnCount_; i++)
{
row.push_back(new QStandardItem());
}
return row;
}
struct Row {
std::vector<QStandardItem *> items;
boost::optional<TVectorItem> original;
bool isCustomRow;
Row(std::vector<QStandardItem *> _items, bool _isCustomRow = false)
: items(std::move(_items))
, isCustomRow(_isCustomRow)
{
}
Row(std::vector<QStandardItem *> _items, const TVectorItem &_original,
bool _isCustomRow = false)
: items(std::move(_items))
, original(_original)
, isCustomRow(_isCustomRow)
{
}
};
private:
std::vector<QMap<int, QVariant>> headerData_;
BaseSignalVector<TVectorItem> *vector_;
std::vector<Row> rows_;
int columnCount_;
// returns the related index of the SignalVector
int getVectorIndexFromModelIndex(int index)
{
int i = 0;
for (auto &row : this->rows_)
{
if (row.isCustomRow)
{
index--;
continue;
}
if (i == index)
{
return i;
}
i++;
}
return i;
}
// returns the related index of the model
int getModelIndexFromVectorIndex(int index)
{
int i = 0;
for (auto &row : this->rows_)
{
if (row.isCustomRow)
{
index++;
}
if (i == index)
{
return i;
}
i++;
}
return i;
}
};
} // namespace chatterino
#pragma once
#include "common/SignalVector.hpp"
#include <QAbstractTableModel>
#include <QStandardItem>
#include <boost/optional.hpp>
#include <pajlada/signals/signalholder.hpp>
namespace chatterino {
template <typename TVectorItem>
class SignalVectorModel : public QAbstractTableModel,
pajlada::Signals::SignalHolder
{
public:
SignalVectorModel(int columnCount, QObject *parent = nullptr)
: QAbstractTableModel(parent)
, columnCount_(columnCount)
{
for (int i = 0; i < columnCount; i++)
{
this->headerData_.emplace_back();
}
}
void init(BaseSignalVector<TVectorItem> *vec)
{
this->vector_ = vec;
auto insert = [this](const SignalVectorItemArgs<TVectorItem> &args) {
if (args.caller == this)
{
return;
}
// get row index
int index = this->getModelIndexFromVectorIndex(args.index);
assert(index >= 0 && index <= this->rows_.size());
// get row items
std::vector<QStandardItem *> row = this->createRow();
this->getRowFromItem(args.item, row);
// insert row
index = this->beforeInsert(args.item, row, index);
this->beginInsertRows(QModelIndex(), index, index);
this->rows_.insert(this->rows_.begin() + index,
Row(row, args.item));
this->endInsertRows();
};
int i = 0;
for (const TVectorItem &item : vec->getVector())
{
SignalVectorItemArgs<TVectorItem> args{item, i++, 0};
insert(args);
}
this->managedConnect(vec->itemInserted, insert);
this->managedConnect(vec->itemRemoved, [this](auto args) {
if (args.caller == this)
{
return;
}
int row = this->getModelIndexFromVectorIndex(args.index);
assert(row >= 0 && row <= this->rows_.size());
// remove row
std::vector<QStandardItem *> items =
std::move(this->rows_[row].items);
this->beginRemoveRows(QModelIndex(), row, row);
this->rows_.erase(this->rows_.begin() + row);
this->endRemoveRows();
this->afterRemoved(args.item, items, row);
for (QStandardItem *item : items)
{
delete item;
}
});
this->afterInit();
}
virtual ~SignalVectorModel()
{
for (Row &row : this->rows_)
{
for (QStandardItem *item : row.items)
{
delete item;
}
}
}
int rowCount(const QModelIndex &parent) const override
{
return this->rows_.size();
}
int columnCount(const QModelIndex &parent) const override
{
return this->columnCount_;
}
QVariant data(const QModelIndex &index, int role) const override
{
int row = index.row(), column = index.column();
assert(row >= 0 && row < this->rows_.size() && column >= 0 &&
column < this->columnCount_);
return rows_[row].items[column]->data(role);
}
bool setData(const QModelIndex &index, const QVariant &value,
int role) override
{
int row = index.row(), column = index.column();
assert(row >= 0 && row < this->rows_.size() && column >= 0 &&
column < this->columnCount_);
Row &rowItem = this->rows_[row];
rowItem.items[column]->setData(value, role);
if (rowItem.isCustomRow)
{
this->customRowSetData(rowItem.items, column, value, role, row);
}
else
{
int vecRow = this->getVectorIndexFromModelIndex(row);
this->vector_->removeItem(vecRow, this);
assert(this->rows_[row].original);
TVectorItem item = this->getItemFromRow(
this->rows_[row].items, this->rows_[row].original.get());
this->vector_->insertItem(item, vecRow, this);
}
return true;
}
QVariant headerData(int section, Qt::Orientation orientation,
int role) const override
{
if (orientation != Qt::Horizontal)
{
return QVariant();
}
auto it = this->headerData_[section].find(role);
if (it == this->headerData_[section].end())
{
return QVariant();
}
else
{
return it.value();
}
}
bool setHeaderData(int section, Qt::Orientation orientation,
const QVariant &value,
int role = Qt::DisplayRole) override
{
if (orientation != Qt::Horizontal)
{
return false;
}
this->headerData_[section][role] = value;
emit this->headerDataChanged(Qt::Horizontal, section, section);
return true;
}
Qt::ItemFlags flags(const QModelIndex &index) const override
{
int row = index.row(), column = index.column();
assert(row >= 0 && row < this->rows_.size() && column >= 0 &&
column < this->columnCount_);
return this->rows_[row].items[column]->flags();
}
QStandardItem *getItem(int row, int column)
{
assert(row >= 0 && row < this->rows_.size() && column >= 0 &&
column < this->columnCount_);
return rows_[row].items[column];
}
void deleteRow(int row)
{
int signalVectorRow = this->getVectorIndexFromModelIndex(row);
this->vector_->removeItem(signalVectorRow);
}
bool removeRows(int row, int count, const QModelIndex &parent) override
{
if (count != 1)
{
return false;
}
assert(row >= 0 && row < this->rows_.size());
int signalVectorRow = this->getVectorIndexFromModelIndex(row);
this->vector_->removeItem(signalVectorRow);
return true;
}
protected:
virtual void afterInit()
{
}
// turn a vector item into a model row
virtual TVectorItem getItemFromRow(std::vector<QStandardItem *> &row,
const TVectorItem &original) = 0;
// turns a row in the model into a vector item
virtual void getRowFromItem(const TVectorItem &item,
std::vector<QStandardItem *> &row) = 0;
virtual int beforeInsert(const TVectorItem &item,
std::vector<QStandardItem *> &row,
int proposedIndex)
{
return proposedIndex;
}
virtual void afterRemoved(const TVectorItem &item,
std::vector<QStandardItem *> &row, int index)
{
}
virtual void customRowSetData(const std::vector<QStandardItem *> &row,
int column, const QVariant &value, int role,
int rowIndex)
{
}
void insertCustomRow(std::vector<QStandardItem *> row, int index)
{
assert(index >= 0 && index <= this->rows_.size());
this->beginInsertRows(QModelIndex(), index, index);
this->rows_.insert(this->rows_.begin() + index,
Row(std::move(row), true));
this->endInsertRows();
}
void removeCustomRow(int index)
{
assert(index >= 0 && index <= this->rows_.size());
assert(this->rows_[index].isCustomRow);
this->beginRemoveRows(QModelIndex(), index, index);
this->rows_.erase(this->rows_.begin() + index);
this->endRemoveRows();
}
std::vector<QStandardItem *> createRow()
{
std::vector<QStandardItem *> row;
for (int i = 0; i < this->columnCount_; i++)
{
row.push_back(new QStandardItem());
}
return row;
}
struct Row {
std::vector<QStandardItem *> items;
boost::optional<TVectorItem> original;
bool isCustomRow;
Row(std::vector<QStandardItem *> _items, bool _isCustomRow = false)
: items(std::move(_items))
, isCustomRow(_isCustomRow)
{
}
Row(std::vector<QStandardItem *> _items, const TVectorItem &_original,
bool _isCustomRow = false)
: items(std::move(_items))
, original(_original)
, isCustomRow(_isCustomRow)
{
}
};
private:
std::vector<QMap<int, QVariant>> headerData_;
BaseSignalVector<TVectorItem> *vector_;
std::vector<Row> rows_;
int columnCount_;
// returns the related index of the SignalVector
int getVectorIndexFromModelIndex(int index)
{
int i = 0;
for (auto &row : this->rows_)
{
if (row.isCustomRow)
{
index--;
continue;
}
if (i == index)
{
return i;
}
i++;
}
return i;
}
// returns the related index of the model
int getModelIndexFromVectorIndex(int index)
{
int i = 0;
for (auto &row : this->rows_)
{
if (row.isCustomRow)
{
index++;
}
if (i == index)
{
return i;
}
i++;
}
return i;
}
};
} // namespace chatterino
+125 -125
View File
@@ -1,125 +1,125 @@
#include "UsernameSet.hpp"
#include <tuple>
namespace chatterino {
//
// UsernameSet
//
UsernameSet::ConstIterator UsernameSet::begin() const
{
return this->items.begin();
}
UsernameSet::ConstIterator UsernameSet::end() const
{
return this->items.end();
}
UsernameSet::Range UsernameSet::subrange(const Prefix &prefix) const
{
auto it = this->firstKeyForPrefix.find(prefix);
if (it != this->firstKeyForPrefix.end())
{
auto start = this->items.find(it->second);
auto end = start;
while (end != this->items.end() && prefix.isStartOf(*end))
{
end++;
}
return {start, end};
}
return {this->items.end(), this->items.end()};
}
std::set<QString>::size_type UsernameSet::size() const
{
return this->items.size();
}
std::pair<UsernameSet::Iterator, bool> UsernameSet::insert(const QString &value)
{
this->insertPrefix(value);
return this->items.insert(value);
}
std::pair<UsernameSet::Iterator, bool> UsernameSet::insert(QString &&value)
{
this->insertPrefix(value);
return this->items.insert(std::move(value));
}
void UsernameSet::insertPrefix(const QString &value)
{
auto &string = this->firstKeyForPrefix[Prefix(value)];
if (string.isNull() || value.compare(string, Qt::CaseInsensitive) < 0)
string = value;
}
//
// Range
//
UsernameSet::Range::Range(ConstIterator start, ConstIterator end)
: start_(start)
, end_(end)
{
}
UsernameSet::ConstIterator UsernameSet::Range::begin()
{
return this->start_;
}
UsernameSet::ConstIterator UsernameSet::Range::end()
{
return this->end_;
}
//
// Prefix
//
Prefix::Prefix(const QString &string)
: first(string.size() >= 1 ? string[0].toLower() : '\0')
, second(string.size() >= 2 ? string[1].toLower() : '\0')
{
}
bool Prefix::operator==(const Prefix &other) const
{
return std::tie(this->first, this->second) ==
std::tie(other.first, other.second);
}
bool Prefix::operator!=(const Prefix &other) const
{
return !(*this == other);
}
bool Prefix::isStartOf(const QString &string) const
{
if (string.size() == 0)
{
return this->first == QChar('\0') && this->second == QChar('\0');
}
else if (string.size() == 1)
{
return this->first == string[0].toLower() &&
this->second == QChar('\0');
}
else
{
return this->first == string[0].toLower() &&
this->second == string[1].toLower();
}
}
} // namespace chatterino
#include "UsernameSet.hpp"
#include <tuple>
namespace chatterino {
//
// UsernameSet
//
UsernameSet::ConstIterator UsernameSet::begin() const
{
return this->items.begin();
}
UsernameSet::ConstIterator UsernameSet::end() const
{
return this->items.end();
}
UsernameSet::Range UsernameSet::subrange(const Prefix &prefix) const
{
auto it = this->firstKeyForPrefix.find(prefix);
if (it != this->firstKeyForPrefix.end())
{
auto start = this->items.find(it->second);
auto end = start;
while (end != this->items.end() && prefix.isStartOf(*end))
{
end++;
}
return {start, end};
}
return {this->items.end(), this->items.end()};
}
std::set<QString>::size_type UsernameSet::size() const
{
return this->items.size();
}
std::pair<UsernameSet::Iterator, bool> UsernameSet::insert(const QString &value)
{
this->insertPrefix(value);
return this->items.insert(value);
}
std::pair<UsernameSet::Iterator, bool> UsernameSet::insert(QString &&value)
{
this->insertPrefix(value);
return this->items.insert(std::move(value));
}
void UsernameSet::insertPrefix(const QString &value)
{
auto &string = this->firstKeyForPrefix[Prefix(value)];
if (string.isNull() || value.compare(string, Qt::CaseInsensitive) < 0)
string = value;
}
//
// Range
//
UsernameSet::Range::Range(ConstIterator start, ConstIterator end)
: start_(start)
, end_(end)
{
}
UsernameSet::ConstIterator UsernameSet::Range::begin()
{
return this->start_;
}
UsernameSet::ConstIterator UsernameSet::Range::end()
{
return this->end_;
}
//
// Prefix
//
Prefix::Prefix(const QString &string)
: first(string.size() >= 1 ? string[0].toLower() : '\0')
, second(string.size() >= 2 ? string[1].toLower() : '\0')
{
}
bool Prefix::operator==(const Prefix &other) const
{
return std::tie(this->first, this->second) ==
std::tie(other.first, other.second);
}
bool Prefix::operator!=(const Prefix &other) const
{
return !(*this == other);
}
bool Prefix::isStartOf(const QString &string) const
{
if (string.size() == 0)
{
return this->first == QChar('\0') && this->second == QChar('\0');
}
else if (string.size() == 1)
{
return this->first == string[0].toLower() &&
this->second == QChar('\0');
}
else
{
return this->first == string[0].toLower() &&
this->second == string[1].toLower();
}
}
} // namespace chatterino
+86 -86
View File
@@ -1,86 +1,86 @@
#pragma once
#include <QString>
#include <functional>
#include <set>
#include <unordered_map>
namespace chatterino {
class Prefix
{
public:
Prefix(const QString &string);
bool operator==(const Prefix &other) const;
bool operator!=(const Prefix &other) const;
bool isStartOf(const QString &string) const;
private:
QChar first;
QChar second;
friend struct std::hash<Prefix>;
};
} // namespace chatterino
namespace std {
template <>
struct hash<chatterino::Prefix> {
size_t operator()(const chatterino::Prefix &prefix) const
{
return (size_t(prefix.first.unicode()) << 16) |
size_t(prefix.second.unicode());
}
};
} // namespace std
namespace chatterino {
struct CaseInsensitiveLess {
bool operator()(const QString &lhs, const QString &rhs) const
{
return lhs.compare(rhs, Qt::CaseInsensitive) < 0;
}
};
class UsernameSet
{
public:
static constexpr int PrefixLength = 2;
using Iterator = std::set<QString>::iterator;
using ConstIterator = std::set<QString>::const_iterator;
class Range
{
public:
Range(ConstIterator start, ConstIterator end);
ConstIterator begin();
ConstIterator end();
private:
ConstIterator start_;
ConstIterator end_;
};
ConstIterator begin() const;
ConstIterator end() const;
Range subrange(const Prefix &prefix) const;
std::set<QString>::size_type size() const;
std::pair<Iterator, bool> insert(const QString &value);
std::pair<Iterator, bool> insert(QString &&value);
private:
void insertPrefix(const QString &string);
std::set<QString, CaseInsensitiveLess> items;
std::unordered_map<Prefix, QString> firstKeyForPrefix;
};
} // namespace chatterino
#pragma once
#include <QString>
#include <functional>
#include <set>
#include <unordered_map>
namespace chatterino {
class Prefix
{
public:
Prefix(const QString &string);
bool operator==(const Prefix &other) const;
bool operator!=(const Prefix &other) const;
bool isStartOf(const QString &string) const;
private:
QChar first;
QChar second;
friend struct std::hash<Prefix>;
};
} // namespace chatterino
namespace std {
template <>
struct hash<chatterino::Prefix> {
size_t operator()(const chatterino::Prefix &prefix) const
{
return (size_t(prefix.first.unicode()) << 16) |
size_t(prefix.second.unicode());
}
};
} // namespace std
namespace chatterino {
struct CaseInsensitiveLess {
bool operator()(const QString &lhs, const QString &rhs) const
{
return lhs.compare(rhs, Qt::CaseInsensitive) < 0;
}
};
class UsernameSet
{
public:
static constexpr int PrefixLength = 2;
using Iterator = std::set<QString>::iterator;
using ConstIterator = std::set<QString>::const_iterator;
class Range
{
public:
Range(ConstIterator start, ConstIterator end);
ConstIterator begin();
ConstIterator end();
private:
ConstIterator start_;
ConstIterator end_;
};
ConstIterator begin() const;
ConstIterator end() const;
Range subrange(const Prefix &prefix) const;
std::set<QString>::size_type size() const;
std::pair<Iterator, bool> insert(const QString &value);
std::pair<Iterator, bool> insert(QString &&value);
private:
void insertPrefix(const QString &string);
std::set<QString, CaseInsensitiveLess> items;
std::unordered_map<Prefix, QString> firstKeyForPrefix;
};
} // namespace chatterino
+15 -15
View File
@@ -1,15 +1,15 @@
#pragma once
#include <QtGlobal>
#define CHATTERINO_VERSION "2.1.4-beta-2"
#if defined(Q_OS_WIN)
# define CHATTERINO_OS "win"
#elif defined(Q_OS_MACOS)
# define CHATTERINO_OS "macos"
#elif defined(Q_OS_LINUX)
# define CHATTERINO_OS "linux"
#else
# define CHATTERINO_OS "unknown"
#endif
#pragma once
#include <QtGlobal>
#define CHATTERINO_VERSION "2.1.4-beta-2"
#if defined(Q_OS_WIN)
# define CHATTERINO_OS "win"
#elif defined(Q_OS_MACOS)
# define CHATTERINO_OS "macos"
#elif defined(Q_OS_LINUX)
# define CHATTERINO_OS "linux"
#else
# define CHATTERINO_OS "unknown"
#endif